Proper way to define recursive makefile for subfolders - makefile

I have a project like this:
main --> main.c uses tests/test.h
|
|_ tests (main/tests) --> test.h
--> test1.c implementation of test.h
--> test2.c implementation of test.h
--> test3.c implementation of test.h
I currently have a makefile like this:
all: main.o tests/test1.o tests/test2.o tests/test3.o
gcc -o main main.o tests/test1.o tests/test2.o tests3.o
%.o: %.c test.h
gcc -c $< -o $#
But I feel that it's repetitive (say if I have multiple directories in main). Is there a better way to do this using recursive make definitions?
I'm a beginner, and I just wanted to explore different ways.

Not sure what you mean by recursive, but I'd put all testx.o and testx.c into 2 lists(actually your default rule will pick up all .c files and compile them to .o files) this will probably do the work:
OBJECTS=tests/test1.o tests/test2.o tests/test3.o ...
SOURCES=test1.c test2.c test3.c ...
all: main
main: main.o $(OBJECTS)
gcc -o $# $(OBJECTS)
%.o: %.c test.h
gcc -c $< -o $#

Related

Makefile header dependencies

I am new to using make and have been learning the basics through this tutorial. Here is the final example makefile example from the tutorial:
IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)
ODIR=obj
LDIR =../lib
LIBS=-lm
_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = hellomake.o hellofunc.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
hellomake: $(OBJ)
gcc -o $# $^ $(CFLAGS) $(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
This should work fine assuming all .c files are only including hellomake.h, but it wouldn't work if each .c file was including different headers. Is it possible to write a makefile that knows what each .c file is including, so I don't have to go in and do it manually like:
foo.o: foo.c something.h
...
bar.o: bar.c somethingelse.h
...
because that seems like it would be a big waste of time.
Suppose foo.c has a line:
#include "something.h"
You'd like a line in the makefile:
foo.o: foo.c something.h
The gcc compiler can construct that line for you. The command
gcc -MMD -c -o foo.o foo.c
will build foo.o and foo.d which contains the line. (Try it.)
So just modify your makefile to produce these *.d files and include them, and you're done:
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -MMD -c -o $# $< $(CFLAGS)
-include $(ODIR)/*.d
(Further refinements are possible, like specifying where the *.d files should go.)
Traditional makes are rather limited and force you to do all that basic stuff yourself. If you rightly expect a build tool to find dependencies and know what to link, try makepp. You may not need a makefile at all, or just a minimal one like
CFLAGS = -O3
myprog: # just a default target to know what to build
The linking part would require a little help on your side, in that it is based on source-header pairs. If myprog.cpp includes a.h and b.hpp it'll look if it can build a.o and/or b.o, and if so, will link them and recursively check what their sources include.
You will only need to learn more make syntax, if you have more complex requirements. But if you do, there is no limit. Besides doing almost all that GNU make can, there are lots more useful things, and you can even extend your makefiles with some Perl programming.
Yes, the "MMD" flag will help you to generate ".d" file (dependency) files. If you include at end of your Makefile( -include *.d ) and then if you make any change in .h file, the respective .o file, will rebuild.
Take this as reference:
https://github.com/saanvijay/makefile-skeleton
There's a minor limitation to #Beta's answer which can be fixed pretty easily.
Say you have a file main.c which includes header.h. You build this, and your main.d file looks like this:
main.o: main.c header.h
Then you delete header.h and its corresponding include in main.c. The program is valid and should compile fine, but make fails due to the above rule, which has a dependency on a now-nonexistent file.
To fix this, you need main.o to depend on main.d, and a rule to create main.d.
main.d: main.c
$(CC) -MM -o main.d main.c
include main.d
This splits the generation of the .d file into a separate step, and make is smart enough to know that since it includes main.d, it should be rebuilt before it is included. This would fix the above issue. More info in the docs.
One issue with this approach is that make will rebuild the .d file when it's not needed, e.g. when running make clean. In these cases, you can just disable the include like in this answer. I'm interested to know if there is a smarter way to do this.
The complete Makefile would look something like this:
main.d: main.c
$(CC) -MM -o main.d main.c
main.o: main.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o main.o main.c
main: main.o:
$(CC) main.o -o main $(LDLIBS)
.PHONY: clean
clean:
rm main.o main.d
ifneq ($(MAKECMDGOALS),clean)
include main.d
endif

Apply the same compilation command to several source files

Let's say I have several .cpp files which are built by the same way:
main.o : main.cpp
$(CC) -c main.cpp $(COMPILATION_FLAGS)
file1.o : file1.cpp
$(CC) -c file1.cpp $(COMPILATION_FLAGS)
file2.o : file2.cpp
$(CC) -c file2.cpp $(COMPILATION_FLAGS)
How can I write this command only once, and then apply it to main, file1 and file2, to exclude code duplication?
http://www.gnu.org/software/make/manual/html_node/Pattern-Rules.html
http://www.gnu.org/software/make/manual/html_node/Pattern-Examples.html#Pattern-Examples
Something like
%.o : %.cpp
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $#
should do what you want.
Why don't you use CFLAGS and CPPFLAGS rather than COMPILATION_FLAGS?

Compiling for Multiple Targets

I want to compile my program both to linux and windows using g++ and mingw respectively. The only difference between the compilations is the compiler to use and output file name.
A single make command should produce both output files. What is the best way to achieve this with as little duplications in the makefile as possible?
How about this:
linux-name: CC:=g++
windows-name: CC:=mingw
linux-name windows-name:
$(CC) whatever -o $#
EDIT:
What I wrote above is only the new part of the makefile; I assumed that the rest of the makefile was implied. To be more explicit:
all: linux-name windows-name
linux-name: CC:=g++
windows-name: CC:=mingw
linux-name windows-name: foo.o bar.o baz.o SomethingElse
$(CC) $(CCFLAGS) whatever $^ -o $#
%.o: %.cc
$(CC) $(CFLAGS) -I$(INC_DIR) whatever -c $< -o $#
SomethingElse:
build somehow

Makefile include file for same targets

I have a large project using recursive Make. Almost all the Makefiles are the same though. I'm basically building all the object files into the same directory like this:
$(OBJ)/%.o: %.c
$(COMPILE) ${INCLUDES} -c $< -o $#
$(OBJ)/%.o: %.cpp
${CXX} ${INCLUDES} ${FLAGS} -c -fPIC $< -o $#
Is it possible to put these targets in an include file so I don't have to put the same lines in every Makefile?
include I've only used for shared variables and when I tested this using include it did not work.

VPATH not working with Makefile Rule

I have added another rule to a Makefile to attempt to build a C shared library that uses SWIG to wrap the functions for Java using JNI
The additional rule looks like this (basically lifted from one of the SWIG java examples)
java: $(program_C_SRCS)
$(SWIG) -java $(SWIGOPT) $(INTERFACEPATH)
$(CC) -c $(CFLAGS) $(JAVACFLAGS) $(program_C_SRCS) $(ISRCS) $(CPPFLAGS) $(JAVA_INCLUDE)
$(JAVALDSHARED) $(CFLAGS) $(program_C_OBJS) $(IOBJS) $(JAVA_DLNK) $(LDFLAGS) -o $(JAVA_LIBPREFIX)$(TARGET)$(JAVASO)
javac *.java
problem I have is that my VPATH doesn't seem to work with the *.c files anymore
I noticed that this rule causes all the .c files to compiled as one call to gcc rather than a separate call to gcc for the compilation of each .o file
my previous rules for compilation without any JNI stuff look like this:
.PHONY: all clean
all: $(program_DEBUG_NAME) $(program_RELEASE_NAME)
# original rule to build library in src dir (no longer inc. in all)
$(program_NAME): $(program_C_OBJS)
$(LINK.c) -shared -Wl,-soname,$# $^ -o $#
# new rules to build debug/release libraries and place them in relevant build
# dirs
$(program_DEBUG_NAME): $(DEBUG_OBJS)
$(DEBUG_LINK.c) -shared -Wl,-soname,$# $^ -o $(BUILD_DIR)/debug/$#
$(program_RELEASE_NAME): $(RELEASE_OBJS)
$(RELEASE_LINK.c) -shared -Wl,-soname,$# $^ -o $(BUILD_DIR)/release/$#
# rule to build object files (replaces implicit rule)
$(BUILD_DIR)/debug/%.o: %.c
$(DEBUG_LINK.c) $< -c -o $#
$(BUILD_DIR)/release/%.o: %.c
$(RELEASE_LINK.c) $< -c -o $#
and these work with VPATH no problem
my VPATH statement looks like this:
VPATH = ../../pulse_IO/src ../../../g2/src
Look at your rule:
java: $(program_C_SRCS)
...
$(CC) -c $(CFLAGS) $(JAVACFLAGS) $(program_C_SRCS) ...
...
Suppose program_C_SRCS is foo.c, and the path is somewhere/foo.c. Without VPATH, this rule doesn't run at all because Make can't find foo.c. With VPATH, Make finds it, and knows that the real prereq is somewhere/foo.c. But you have $(program_C_SRCS) in your rule:
java: somewhere/foo.c
...
$(CC) -c $(CFLAGS) $(JAVACFLAGS) foo.c ...
...
This fails because there is no foo.c (locally).
Try this:
java: $(program_C_SRCS)
...
$(CC) -c $(CFLAGS) $(JAVACFLAGS) $^ ...
...
(The use of automatic variables like $^ is the reason your previous rules worked.)

Resources