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.
Related
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.
I am taking a Coursera course and I am totally stuck in one of the project. Project demands us to create a makefile but I could not.
Here is my files and folders:
project
|---include
|---CMSIS
|---cmsis_gcc.h
|---core_cm4.h
|---core_cmFunc.h
|---core_cmInstr.h
|---ore_cmSimd.h
|------common
|---memory.h
|---platform.h
|------msp432
|---cmsis_gcc.h
|---msp_compatibility.h
|---syste_msp432.h
|---src
|---interr.c
|---main.c
|---memory.c
|---str.h
|---system_msp.h
|---makefile
|---msp432p401r.lds
|---sources.mk
Here is my sources.mk and makefile.
For now, I just want to check whether makefile succesfully find related source and include files but it does not.
I have checked several example makefiles and could not find where is the fault of mine? What I have got here is:
What I understand from error is it can not find related memory.h file. What can I do?
Any help will be appreciated.
Thanks.
Make searches for prerequisites in the working directory. It doesn't know that you have memory.h somewhere else, and will not go looking for without instructions.
One way to solve this is with the vpath directive:
vpath %.h common
You can use the same trick when you run into problems finding other prerequisites:
vpath %h common CMSIS
vpath %.c src
That will get echo "HI" to work, but you will have more trouble with a real rule that actually uses the prerequisites. To take a contrived example:
memory.o: memory.c memory.h
$(CC) -c $(CFLAGS) memory.c -o memory.o
This will fail because although Make can find the prerequisites, the compiler still doesn't know where they are.
We tell the compiler where memory.c is by referring it to the list of prerequisites:
memory.o: memory.c memory.h
$(CC) -c $(CFLAGS) $< -o memory.o
($< means the first prerequisite; Make will provide the path.) We could use the same trick (with some effort) to tell the compiler where to search for the header file, but it's usually simpler to put that in by hand:
memory.o: memory.c memory.h
$(CC) -c $(CFLAGS) -Icommon $< -o memory.o
It's important to understand that make and the compiler are two completely different programs, that do different things and work different ways. Make can run a compiler, but it can also do all sorts of other things: it could build documentation, or an entire website, or copy files, etc. Basically make is a general-purpose tool that can investigate when files are out of date and run any command you give it to make that file "up to date", whatever that may mean.
So, when you create a variable like INCLUDES = -I... that is setting a make variable to a flag that can tell the compiler where your header files are.
That means nothing to make.
Make sees this rule:
ysk: memory.h memory.c
and says "ok to build the file ysk I need to have memory.h and memory.c". It looks in the current directory and those files don't exist (because they are in src/memory.h and src/memory.c) so it fails.
You need to put the paths into your makefile, so make can find them.
We can add pattern rules such as %.c or %.o which mimics *.c or *.o in bash (it searches for all files that have extension .c or .o. This is very useful if you have several targets, so you don't have to write all rules. I would like to know how to use this trick if your target files (.c or .o) are in the previous directory. In bash, one can write ../*.c, but ../%.c does not work in makefile as I tested. How do you do such thing in makefile?
My second question: sometimes one would like to add header dependencies like this:
HEADER=factorial.h
%.o: %.c $(HEADERS)
gcc -o program $%
It is a good idea to add a header dependency because sometimes you don't know whether or not the included libraries have some change.
Here we have to manually type the file names for HEADER.
How do I make it so it can scan the target file's included headers?
For example: my main.c has #include "dog.h"
How do I make it so it detects main.c has included dog.h.
This is my absolute first time ever making a makefile, and I'm really trying to understand the process.
I'm trying to create a very simple makefile for a C++ project whose structure is as follows:
root folder
makefile
readme
src folder
...source files all here...
include folder
...header files for external libraries here...
lib folder
...external lib files all here...
bin folder
...output directory for built executable...
obj folder
...object files all here...
I followed the tutorial here.
Here's my makefile:
IDIR=include .
CC=g++
CFLAGS=-I$(IDIR)
ODIR=bin/obj
LDIR=lib
LIBS=none
SRC=src
_DEPS=hello.h
DEPS=$(patsubst %,$(IDIR)/,%(_DEPS))
_OBJ=file1.o file2.o
OBJ=$(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: $(SRC)/%.cpp $(DEPS)
$(CC) -c -o $# $< $(CFLAGS) # $(LIBS)
test_proj: $(OBJ)
$(CC) -o $# $^ $(CFLAGS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
When I run make on this, I get the following error:
g++ -o .o
g++: fatal error: no input files
compilation terminated.
<builtin>: recipe for target '.o' failed
mingw32-make.exe: *** [.o] Error 1
I'm using GNU Make 3.82.90 built for i686-pc-mingw32, if that matters at all.
Can anyone point out whatever ridiculous error I'm making?
IDIR=include .
is the first problem. Replace it by:
IDIR=include
With your code CFLAGS is expanded as:
-Iinclude .
It does not make sense, I'm afraid. The second problem is:
DEPS=$(patsubst %,$(IDIR)/,%(_DEPS))
which should probably be:
DEPS=$(patsubst %,$(IDIR)/%,$(_DEPS))
and would expand as:
DEPS=include/hello.h
if you fix the first problem, else as:
DEPS=include ./hello.h
which does not make sense neither. The cumulated effect of these two errors are strange recipes (I didn't try to expand them by hand) that probably trigger a make implicit rule with wrong parameters.
IDIR=include .
CC=g++
CFLAGS=-I$(IDIR)
This is wrong. First, for C++ code, use CXX not CC and CXXFLAGS not CFLAGS. Run make -p to understand the builtin rules of your make.
Then -I$(IDIR) does not "distribute" the -I, and IDIR is never used elsewhere. So I suggest to start your Makefile with:
CXX=g++
MY_CXX_LANG_FLAGS= -std=c++11
MY_CXX_WARN_FLAGS= -Wall -Wextra
MY_CXX_INCL_FLAGS= -I. -Iinclude
MY_CXX_MACRO_FLAGS= -DMYFOO=32
### replace with -O2 for a release build below
MY_CXX_OPTIM_FLAGS= -g
CXXFLAGS= $(MY_CXX_LANG_FLAGS) $(MY_CXX_WARN_FLAGS) \
$(MY_CXX_INCL_FLAGS) $(MY_CXX_MACRO_FLAGS)
I won't improve your Makefile, but I do suggest to upgrade to GNU make version 4 if possible (and compiling make 4.1 from its source code is worthwhile in 2015) for that purpose. If possible enable GUILE scripting in it.
If you are forced to use make 3.82 debug your Makefile using remake (with -x); if you can afford a make version 4 use its --trace option
BTW, you might consider using automatic dependencies, that is generating dependencies by passing -M or -MG (etc) flags of g++, see that.
At last, a simple project for a small program (less than a hundred thousands of source lines) might just put all (a few dozens of) its files in the current directory (then the Makefile could be simpler); your proposed directory structure might be arcane for a simple project (but could worth the pain if you have millions of C++ source lines of code). I've given several simple examples of Makefile, e.g. this & that. And GNU make source code itself has a less complex file tree that what you want.
BTW, I strongly disagree with the opinions of that answer (which I did upvote, since it is helpful). I don't feel that GNU make is senile, but I regret that, instead of using recent features available on recent versions (4.x) of make, many people prefer to use complex and arcane Makefile generators (like cmake) instead of coding a clever Makefile (for make version 4 specifically).
At last, you could use other builders, e.g. omake, icmake, ....
I have implemented a binary tree program which includes the tree.c with the functions, the tree.h with the declarations of them and a main.c for testing.
Also, I have a makefile which is:
CC=gcc
CFLAGS=-g -Wall
DEPS = tree.h
OBJ = main.o tree.o
%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
tree: $(OBJ)
$(CC) -o $# $^ $(CFLAGS)
clean:
rm -f *.o tree
Now I want to make it generate a library not only an object file for the binary trees functions and afterwards to generate the documentation of doxygen inside the makefile. Any help would be helpful.
I know that my answer comes in a bit late, but i hope someone will benefit from this.
I have a makefile that generates Doxygen doc.
You have to twist Doxygen a tiny bit
Create the Doxygen setup file that fits Your need, then open that in an editor and remove the lines containg the following two settings (they will be added by the make file later)
INPUT
FILE_PATTERNS
add this line
#INCLUDE = doxyfile.inc
Save this file under a different name I use Doxyfile.mk
in You makefile You need a list of sources and the directories where they are located
example
SRCS = $(OBJS:.o=.c)
SRCDIRS = ./src
SRCDIRS += ./other_src
Now You can put this rule in the Makefile, it will create the file doxyfile.inc that contains the settings You removed from Doxyfile.mk.
.PHONY: all clean distclean doxy
# If makefile changes, maybe the list of sources has changed, so update doxygens list
doxyfile.inc: Makefile.mk
echo INPUT = $(SRCDIRS) > doxyfile.inc
echo FILE_PATTERNS = *.h $(SRCS) >> doxyfile.inc
doxy: doxyfile.inc $(SRCS)
doxygen.exe doxyfile.mk
Bonus: If run from inside an IDE like Eclipse the errors that Doxygen spits out becomes clickable and will jump to the bad comment.
Well, I don't really know the syntax for the doxygen command, so I'll make a generic answer:
in your Makefile, each
term: [dep]
action
is a target.
So if you add something like:
doc: $(OBJ)
doxygen with-correct-options
You will be able to generate the documentation using:
make doc
(doc being here the name of the target)
Now, if you add:
all: tree doc
#echo "Generating program and doc."
you will have the program and the documentation generated with simply invoking
make
In the end, there is an additional statment your Makefile could have use of: .PHONY. It's "A way to mark one of many targets as not directly producing files, and ensure their execution even if a file having the same name as the target exists". In other terms, it's to make sure doc, clean or all will always be executed even if files named doc, clean or all exist.
Its syntax is the following:
.PHONY: all clean doc
And is usually put at the end of the Makefile.