Printing a sentence, only if a Makefile rule is executed - c

I am customizing my Makefile for a school project.
I would like to print the following sentence but only when the .c files from my SRCS_DIR have been actually compiled.
All the .c files have been compiled successfully !
If I move the printf that you see in line 3 of the code block below to the last line, it prints the message after each .c file being compiled...
Thus, I created a COMPILED variable which I set to 0 at the beginning of my Makefile, and then I change its value to 1 during compilation (line 11 in the code block below). I tried to use the ifeq condition (line 2 in the code block below), but the sentence does not print when I do that.
$(NAME): $(LIBFT_AR) $(OBJS)
ifeq ($(COMPILED), 1)
printf "$(GREEN)> All the .c files have been compiled successfully !$(END)\n"
endif
printf "$(BLUE)> Creating the executable file :$(END) $#\n"
$(CC) $(OBJS) $(LIBFT_AR) -lreadline -o $(NAME)
printf "$(GREEN)> Executable file has been created successfully !$(END)\n"
$(OBJS_DIR):
mkdir -p $(addprefix $(OBJS_DIR)/, $(SUBDIRS_LST))
$(OBJS_DIR)/%.o: $(SRCS_DIR)/%.c $(INCS) Makefile | $(OBJS_DIR)
override COMPILED=1
$(CC) $(CFLAGS) -I $(INCS_DIR) -c $< -o $#
printf "$(BLUE)> Compiling :$(END) $<\n"
Do you have any explanation regarding this issue and/or a solution that could help me to solve the problem ?
Thank you !

Makefiles are not scripting languages. Make doesn't read the makefile and run each rule as it's read. Make will (1) parse the entire makefile (and any included files) and build an internal graph of all the prerequisites, than (2) run recipes for targets that are outdated. Content that is NOT IN A RECIPE is always evaluated during the first step. Content that IS IN A RECIPE is always evaluated during the second step.
Lines that are not indented with TABs, are not in recipes (and so are evaluated during the first step). Lines that are indented with TABs, are in recipes (and so are evaluated--which means, given to the shell to execute--during the second step).
Maybe you can now see why your attempts cannot work: the if-statements and variable assignment of COMPILED are always evaluated, during the first step, before make has decided whether or not any targets should be built.
I'm not really sure I understand your goal. If the recipe of your executable is being invoked then it means that all your source files have been compiled: that's what a makefile does. Maybe you are trying to make a distinction between a build where at least one source file was compiled, and a build where no source files had to be recompiled but the target (the executable) was out of date?
If that's what you want the simple way to solve your problem is with automatic variables; for example the $? automatic variable expands to the list of prerequisites that were out of date. You can do something like:
$(NAME): $(LIBFT_AR) $(OBJS)
test -z '$(filter %.o,$?)' || printf "$(GREEN)> All the .c files have been compiled successfully !$(END)\n"
printf "$(BLUE)> Creating the executable file :$(END) $#\n"
$(CC) $(OBJS) $(LIBFT_AR) -lreadline -o $(NAME)
printf "$(GREEN)> Executable file has been created successfully !$(END)\n"
The $(filter ...) function will expand to the list of .o files in the $? variable; if that's empty then no .o files were rebuilt.

Related

Including input statements and conditions in the make file

I am trying to create a makefile, for the first time. I went through some tutorials and I managed to create one, but I am having trouble with a couple of things. Below are the details.
Below are the files in the order of execution:
CSV_to_txt.c - no dependency on any other files.
I want to include CSV_files/Equilibrium_trajectories.csv, which is my input, in the make file. Further, I run the command tac Chemical_Equilibrium.txt in the terminal. Can I include this in the make file as well?
fluid_profile.c - depends on pdfutil.h and beta_util.h.
I have the same problem of reading the inputs, for ex:
Enter the number of points
1000 --> to be included in the make file.
This file creates a text file called fluid_points.txt. What I want to include in the makefile is if this file already exists don't execute the command gcc fluid_points.c -o fluid_points.o -lm.
Structure of the make file:
all:
gcc CSV_to_txt.c -o CSV_to_txt.o -lm
./CSV_to_txt.o
#Include the file path and name when asked for it
#ubuntu terminal command --> tac filename.txt > filename_changed.txt
gcc fluid_profile.c -o fluid_profile.o -lm
./fluid_profile.o
#Enter the number of points when prompted to do so
#If fluid_points.txt file is already existing don't execute the above command, instead execute the below one
gcc blah.c -o blah.o -lm
./blah.o
clean:
$(RM) *.o *~
Any sort of help or even a link to a tutorial would be helpful.
A suggested makefile:
run:
.PHONY: run
CSV_to_txt: CSV_to_txt.c
gcc CSV_to_txt.c -o CSV_to_txt -lm
fluid_profile: fluid_profile.c
gcc fluid_profile.c -o fluid_profile -lm
blah: blah.c
gcc blah -o blah.c -lm
run: CSV_to_txt fluid_profile blah
echo "CSV_files/Equilibrium_trajectories.csv" | ./CSV_to_txt.o
tac Chemical_Equilibrium.txt
echo "1000" | ./fluid_profile.o
./blah.o
clean:
$(RM) *.o *~
So, a break down -- first line, predeclare target run, such that it becomes the default target (if you do make, it will run the first target ). Declare this as a phony target (This means there's no actual file called run being produced. You can look up .PHONY for more details)
Then create some rules to generate the executables. Each executable has its own rule to generate it. Typically you would use automatic variables for these like $# and $<, but I wanted to keep it simple for now.
Then the rule for run. This is dependent on the executables (so executables will finish building before this rule runs).
Then, to pass the filename into the executable, you can simply echo the filename, and then pipe that into the executable.
You have a common newbie error... this is to think that a source file depends on other source files (a .c file depends on some .h files) This is an error and probably the cause you are not getting your result.
The objective of a Makefile is to describe file dependencies in order to do the minimum set of commands to build the final target you specify. For this you need to think that a target is something you are goint to create.
Is a source .c file something you create during the build proces? Not, so it cannot be a target of a rule. The target, indeed is the result of the compilation. The source file doesn't depend on a header file... it just includes it to make the compilation of the .o target (this is, actually the target).
Let's say you have a program hello.c that includes modA.h and modB.h. (and even modB.h includes modB2.h) If you modify any of them, you need to recompile hello.c, so your rule will be:
# (1)
hello.o: hello.c modA.h modB.h modB2.h
cc -c hello.c # (2) see below.
(1) a rule line starts at column 1 and has a left hand side (the target file) and a list of sources (dependencies). Each time make sees that the target doesn't exist or has a last change date earlier than the change dates of any of the dependencies, the command lines below are executed, one after the other.
(2) a command rule starts with a <tab> char in the first column of the line. It represents a command (or a list of commands, each in it's command line) that are required to generate the target file from the sources.
a line starting with # is a comment line (also valid to start in the middle of a rule or a command line)
There is anothe type of line (a macro definition) but you need to learn first how to create dependencies and get used to them, before starting learning how to create macros. Read the make(1) doc first.
you see that we only compile hello.c, but we have to do it every time we change any of the other files above. There are two modules, modA.o and modB.o, each of them with their .c file and the includes needed in hello.c. So:
modA.o: modA.c modA.h
cc -c modA.c
modB.o: modB.c modB.h modB2.h
cc -c modB.c
so when we change any of modA.c or modA.h then modA.o will be created. And as modB.h we said above that included modB2.h, then if we modify it, it should be compiled.
Now the dependency of the program to be linked: As the program is compiled, it has just three modules: hello.o, modA.o and modB.o. To create hello all these three modules must be given to the linker.... so the Makefile needs also:
hello: hello.o modA.o modB.o
cc -o hello hello.o modA.o modB.o
and so, the complete Makefile is:
# this rule is put first to become the default target.
# the default target is the final program.
hello: hello.o modA.o modB.o
cc -o hello hello.o modA.o modB.o
# this rule is for the hello.o target only.
hello.o: hello.c modA.h modB.h modB2.h
cc -c hello.c
# this rule is for modA.o
modA.o: modA.c modA.h
cc -c modA.c
# and this one for modB.o
modB.o: modB.c modB.h modB2.h
cc -c modB.c
and with this Makefile you'll enjoy, because you can touch any file, but the compiler will compile only the correct dependencies to generate the final executable program.
Make has a lot of more functionality, but that requires you to know at least the most basic of it. Once after you have succeeded on the creation of the correct dependencies, you can start to study the other facilities of make, that are only there to abbreviate/avoid rewritting the same thing several times. But read at least the make manual page.

What is this variable in the Makefile?

So here is a Makefile I have been given, with comment I have added.
MF= Makefile_c #name of the makefile
CC= cc #compiler to use
CFLAGS= -g #flags to use
LFLAGS= -lm #flags to use after the thingy
EXE= hello #name to give the executable
INC= \ # ??? What's this for ???
# No user-defined include files at present - list here if required.
# name of the source file
SRC= \
hello.c
#delete default suffix
.SUFFIXES:
#define the suffixes we are interested in
.SUFFIXES: .c .o
OBJ= $(SRC:.c=.o) # names to give the object files
#The .o files depend on the .c files. Compile the object files.
.c.o:
$(CC) $(CFLAGS) -c $<
all: $(EXE) #The output is the executable
$(OBJ): $(INC) #The objects depend on whatever INC is
# The executable depends on the object files. build it from the object files.
$(EXE): $(OBJ)
$(CC) $(CFLAGS) -o $# $(OBJ) $(LFLAGS)
# ??? the object files depend on the makefile???
$(OBJ): $(MF)
# remove any old executables or object files.
clean:
rm -f $(OBJ) $(EXE) core
I am still learning about makefiles so please correct me if I have misidentified anything. The makefile works fine But I want to adapt it to work with my program that has multiple files and header files. I suspect that the variable $INC will somehow make this possible, but so far my attempts to use it have not worked.
For now I would like to understand what this makefile is trying to do, cn you tell me what $INC is for?
The makefile works fine But I want to adapt it to work with my program that has multiple files and header files. I suspect that the variable $INC will somehow make this possible
Unfortunately, no. The \ is just a line continuation, so you can write content for the variable in the next line. It's empty here. This is just a very simple (and ancient!) approach for dependencies: List them yourself. The intention is to list all files your C source file #includes there, so make will rebuild when any of these included files change.
There are much advanced patterns, gcc (and other compilers) allow to do automatic dependency information for make, but this is out of scope for this question. (*)
As for building with multiple source files, this Makefile already supports it, again using an "ancient" way, a suffix rule. It automatically considers all .c files in your directory part of your final program.
(*) As mentioned by Tormund Giantsbane in the comments, this document has nice information on the topic auf automatic dependencies

Issues with makefile producing fatal error "Don't know how to make target" using C source files

I am having an issue with this makefile giving the fatal error: "Don't know how to make target calc.o". The naming is correct along with being in the working directory, and the other issue is that when I switch the order of the object files for default target file it will say it does not know how to make that target either, so the order does not seem to change anything or be one specific file. I am running the make command on a sun sparc unix server as well. The makefile is pasted below:
#The following rule tells make about possible suffixes
#(extensions) of file names.
.SUFFIXES: .c .o
#The following definition of CC ensures that
#gcc will be used to compile the C source files.
CC = gcc
#The following definition of CFLAGS ensures that
#the debugger can be used with the executable file (p1)
#created by running make.
CFLAGS = -g
#The following rule tells make how a ".o" file should
#be created from the corresponding ".c" file.
#Note that the "-c" option must be used here since we are
#compiling source files separately. (Note that the line
#following the ".c.o:" line begins with the "tab" character.)
.c.o:
$(CC) $(CFLAGS) -c $<
#Dependency rule for the default target and how the
#default target is to be created. (Note that the line
#following the dependency rule begins with the "tab"
#character.)
p2: main.o textToBin.o binToText.o calc.o
gcc main.o textToBin.o binToText.o calc.o -o p2
#Dependency rules for other targets. (We don't need to
#specify how these targets are created since we have already
#given a general rule for creating a ".o" file from the
#corresponding ".c" file.)
#NO HEADER FILES
#Target for removing unnecessary files.
clean:
rm -f *.o core

Makefile, "nothing to be done for all" error

So I have a make file, stored in a directory called "temp" the following directory has a src folder, with 2 .c files "file1.c" and "file2.c". The temp directory also holds a include folder (which is empty), and a bin folder (which is empty until the make command is so posed to be run). I'm currently to trying get a single .c file to compile (get it working),but a single file doesn't even seem to work here.
This is how the directories look:
temp
cd into temp..
bin include Makefile src
Here is my makefile:
all:
gcc -Wall -pedantic -std=c99 src/file1.c -Iinclude -o bin/runMe -lncurses
And yes, there is a tab before the gcc. Any help on this frustrating issue, would be much appreciated. Also, if possible any input on compiling the second .c file, would also be very helpful!
Nothing to be done for TARGET means that a target has no commands which, in this case, almost certainly means that you do not have a tab on that gcc line.
That being said that's only the immediate problem. This makefile is also not following good practices and will unnecessarily recompile your program (as well as ceasing to work entirely should an all file be created).
DrC had, in a currently deleted answer, very good suggestions for how to improve your makefile to avoid both of those latter issues.
Specically, your makefile should look more like this:
.PHONY: all
all: bin/runMe
bin/runMe: src/file1.c
gcc -Wall -pedantic -std=c99 $^ -Iinclude -o $# -lncurses
Which marks the all target as a .PHONY so that an all file or directory getting created won't confuse make as well as setting up a prerequisite on the source file for your built binary so that make can tell when it does (and doesn't) need to rebuild the binary.

How does Redis Makefile include header file prerequisites

I was teaching myself GNU Make and thought a look at the Redis Makefile would teach me a thing or two about the tool.
The rule that compiles the source file to the object file is here:
%.o: %.c .make-prerequisites
$(REDIS_CC) -c $<
Notice that the suffix rule just mentions the C source file (with %.c) as a prerequisite.
But if I add a echo in the middle and run make:
%.o: %.c .make-prerequisites
echo $^
$(REDIS_CC) -c $<
Then the first few lines of the output from make is like below:
cd src && make all
make[1]: Entering directory `/home/cltpadmin/code/redis/src'
echo adlist.c .make-prerequisites adlist.h zmalloc.h
adlist.c .make-prerequisites adlist.h zmalloc.h
CC adlist.o
How did make know that adlist.c depends on adlist.h and zmalloc.h?
The prerequisites in question come from line one of the Makefile.dep included makefile (included on line 134).
The dep target on line 136 generates that file.
This was a fairly common (though entirely avoidable) step for using the compiler to generate the necessary header file includes. This static method also has issues with conditional header includes I believe.
To clarify, the "avoidable" part of this is that it need not be a separate step and a static dependency file at all. See Advanced Auto-Dependency Generation for details about this idea.

Resources