Working with multiple source file extensions in a makefile - makefile

I have a c++ project with various extensions for the source files (.cpp, .c, .cc) and various extensions for the header files (.hpp, .h, .hh). The source files are located in a directory called SRC, and the header files are predictably in a directory called INC.
I would like to compile the source with a rule like
vpath %.c $(SRC)
%.o: %.c
$(COMPILER) $(FLAGS) $< $(INCFLAG)$(INC)
This of course works if I know the source file will be of the form %.c, but in the case of multiple possible file extensions, I would need to build a similar rule for %.cpp and %.cc as well. Of course three rules isn't a big deal to write, but it would be nice to be able to use this makefile as a drag and drop for any project, even in a different language, without having to re-write the rules.
So how can I write a rule (or some other construct that accomplishes the same goal) that works like:
SRC_EXT = cpp c cc
vpath %.$(SRC_EXT) $(SRC)
%.o: %.$(SRC_EXT)
$(COMPILER) $(FLAGS) $< $(INCFLAG)$(INC)
Thanks for your help.

You can't in standard POSIX make. However since you mention vpath I'll assume you're using GNU make. If you have a sufficiently new version (3.81 or newer), you can do this easily enough with call and eval:
SRC_EXT = cpp c cc
define compile_rule
%.o : %.$1
$$(COMPILER) $$(FLAGS) $$< $$(INCFLAG)$$(INC)
endef
$(foreach EXT,$(SRC_EXT),$(eval $(call compile_rule,$(EXT))))
If you don't have sufficiently new GNU make, or would prefer an alternate solution, you can do the same thing with generated makefiles.

Related

Using dependencies in a GNU makefile

I am having a doubt on how to use dependencies in my GNU makefile.
Consider the following make file:
// Building all my dependency files
$(OUTDIR)/%.dep: %.c
$(DPP) $(CPPFLAGS) $(DPPFLAGS) $(#:dep=o) $< $#
Now, I have these dependency files containing lists or "pointer" to files/dependencies.
I then want to create my object files based on those dependencies (when they change).
Can I do the following thing:
$(OUTDIR)/%.o $(OUTDIR)/%.orc: %.c $(OUTDIR)/%.dep
$(AS) $(COMMONFLAGS) $(CPPFLAGS) $(ASFLAGS) -o $# $<
Does that not however mean that I will be rebuilding my objects only when my *.dep files actually change (not the files listed within those dependency files).
Is that the correct way of doing it? If not, what is?
I feel like I am not using/understanding how these dependency files are being interpreted by the tools.
The dependency files are themselves in makefile format, and is input to make, not gcc. To use them, you will need to include them in your makefile (ignoring the file if it hasn't been created yet):
-include $(wildcard *.dep)
The only caveat is that it will then build the dep files whenever it needs to build anything, including a "make clean", so you need to make the include conditional on the target:
ifneq ($(MAKECMDGOALS),clean)
-include $(wildcard *.dep)
endif

Makefile -- compile only modified C++ files

This is my current makefile
CFLAGS = -Iheaders/
CC = g++
PROGRAM_NAME = sportsmanager
rwildcard = $(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
SOURCES = $(call rwildcard,sources/,*.cpp)
OFILES = $(call rwildcard,obj-tmp/,*.o)
OBJDIR = obj-tmp/
compileAndRun:
make -s compile && make -s $(PROGRAM_NAME)
./$(PROGRAM_NAME)
compile: $(SOURCES)
mkdir -p $(OBJDIR)
$(CC) $(CFLAGS) -c $(SOURCES) && mv *.o $(OBJDIR)
$(PROGRAM_NAME): $(OFILES)
$(CC) $(CFLAGS) $(OFILES) -o $(PROGRAM_NAME)
Whenever I run $ make, target compile is triggered which compiles all .cpp files in directory sources/ to .o files which are then moved to obj-tmp/. Then the target $(PROGRAM_NAME) is triggered, which links all the .o files and outputs the executable file.
The problem is that all files are compiled each time I run make. What ideally should happen if I run 'make' twice in succession is that make should know that the program is up to date the second time. If I modify only one file, only that file should be compiled.
Heads up: I know that there exists similar questions regarding this, but I've yet to see a solution which works in conjunction with the above makefile.
Any input is greatly appreciated.
The whole point of make is to compile only those files which have been modified since the last build. The problem in your makefile is that your compile recipe has the $(SOURCES) variable as a dependency. As in, all the source files.
I would use vpath to organize the project folder like so:
vpath %.cpp src
vpath %.h include
This will tell make to look for c++ files in ./src and header files in ./include. Then, you can simplify your recipe for individual files like this:
%.o: %.cpp
$(CC) $(CFLAGS) -c -o $# $<
Having done this, you can now define an $(OBJECTS) variable with a wildcard that matches .o files and continue from there. As an aside, moving your object files into a separate folder is considered bad practice and I agree; it really adds nothing substantial of value but complicates recipes.
Remember that object files represent a dependency for the $(PROGRAM) recipe. So naturally, make looks for the necessary object files to see if they need to be rebuilt. If they've been moved, one of two things happens. Either make will determine that they don't exist and will rebuild all the object files again from scratch, thereby invalidating the very reason we use make in the first place, or you'll have to define a folder where the object files will live, and every time you handle wildcards, searches, etc., literally anything that has to do with the object files, you'll have to take this added complexity into account.
I agree that having a ton of object files in the project folder can be a little annoying, but it definitely beats waiting forever for the project to compile. Just remember to add *.o to your .gitignore or whatever source control platform you use and they'll be nothing more than an eyesore, while make will be that much easier to use.
To answer your question on handling subdirectories in the source folder, the answer is a little more complicated.
Rather than using the specific vpath <pattern> <folder> directive as above, you could just outright use the VPATH variable like this:
VPATH = include src src/sub
This would handle the job, but the first method is usually preferred because when using VPATH, make searches every directory every time when looking for a file, rather than being location-constrained by file extension.
It is possible to use make to conveniently manage large projects though, and it involves calling make itself recursively, writing makefiles for each module in the build process. This process is obviously much more complicated, and I would strongly recommend considering whether the project genuinely necessitates this, as any potential gains in build-process modularization may not be recuperated due to the complexity involved in implementation.
I'd like to point you to this and this, both of which are phenomenal resources on makefiles.
Change the dependency of compile to be the object files.
Add a pattern rule for the object files.
compile: $(OFILES)
$(OBJDIR)/%.o: sources/%.cpp
mkdir -p $(OBJDIR)
$(CC) $(CFLAGS) -c $< -o $#
Ok, a lot of good input in this thread! Here's a follow up. I've now updated the script to the following:
CC = g++
CFLAGS = -Iheaders/
PROGRAM_NAME = sportsmanager
OFILES = $(patsubst %.cpp,%.o,$(wildcard sources/*.cpp))
vpath %.cpp sources
compileAndRun:
#make -s $(PROGRAM_NAME)
#./$(PROGRAM_NAME)
$(PROGRAM_NAME): $(OFILES)
$(CC) $(CFLAGS) -o $(PROGRAM_NAME) $(OFILES)
%.o: %.cpp
$(CC) $(CFLAGS) -c -o $# $<
clean:
rm -rf $(PROGRAM_NAME) $(OFILES)
Any suggestions for further improvements are very welcome!

generic make rule assitance when placing files into different folders

I have a make rule like this. I want it to define a generic rule that describes transformation of any C file into compiled Object file. It works fine, but i want to keep my C files in one folder and output files in another.
Here is the relevant snippet from Makefile itself:
.SUFFIXES .c .o
.c.o :
$(GCC) -c $(CFLAGS) $< -o $#
I want to modify this makefile rule to tell make to find the source (C) files in one folder, let's say $(C_DIR), run GCC and then and put OBJ files into $(O_DIR) ?
You cannot do that with suffix rules. In order to do that you'll have to use non-POSIX-standard make features. GNU make (the standard make on GNU/Linux systems for example, and available for pretty much any other platform) provides pattern rules that can do this:
SRCS = foo.c bar.c baz.c
OBJS = $(addprefix $(O_DIR)/,$(SRCS))
all: $(OBJS)
$(O_DIR)/%.o : $(C_DIR)/%.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $# $<

.cu file Makefile issue

I am new to linux development.
I wrote a project using MPI and cuda. When
it gets bigger and bigger, I realize that I
need a Makefile now. So I learned how to write
one. The Makefile works, but will only compile
cpp files even if I have both of the following
lines in my Makefile:
.cpp.o:
$(CC) $(CCFLAGS) $<
.cu.o:
$(NVCC) $(CCFLAGS) $<
Any idea why this is happening? Thanks.
UNDERSTANDING MAKE
Make is all about generating missing files.
If you have TWO rules that generate the SAME file upon existence of a source then the first one in make's list that actually has a source file present will get invoked. So for instance if you have the rules:
.c.o:
$(CC) -o $# -c $<
.cpp.o:
$(CXX) -o $# -c $<
and you have two files, foo.c and bar.cpp then you can type:
$ make foo.o
it will use the first rule... and when you type
$ make bar.o
it will use the second rule.
Now suppose you have TWO files foo.c and foo.cpp
Here make has to make a choice as to which takes precedence. Make uses suffixes of files intimately for its build rules. What is considered a suffix is controlled by the .SUFFIXES directive.
The .SUFFIXES directive has a default built-in value that defines common suffixes such as .c .cpp .cc .o etc. in a particular order. If we want to change the order of precedence we clear that out with a blank line in Makefile i.e.:
.SUFFIXES:
and then follow it with our definition:
.SUFFIXES: .cpp .c .o
if you don't blank the line out, then make just appends the listed suffixes to its current list, that way multiple makefiles can simply add new suffixes without worrying about breaking each other.
Now since the .cpp is before .c the .cpp.o rule will take precedence (in case foo.cpp and foo.c are both present)
NOTE: Yes there is a "." before the words SUFFIXES and yes it is all capital letters.
Try to play with this Makefile to see the effects:
.SUFFIXES:
.SUFFIXES: .cpp .c .o
.c.o:
echo Compiling C
.cpp.o:
echo Compiling CPP
Make is very very powerful, and quite well documented so well worth the read. GNU make, which is probably the strongest implementation with amazing extensions has made me a lot of money in the past :-) enjoy the experience.
Your rule is wrong, you want something like this:
%.o : %.cu
$(NVCC) $(CCFLAGS) $< -o $#
That's assuming the command line you need to execute is something like
nvcc foo.cu -o foo.o
Otherwise, edit to suit.

How to write different implicit rules for different file names for GNU Make

I have a directory in which I keep adding different C++ source files, and generic Makefile to compile them. This is the content of the Makefile:
.PHONY: all clean
CXXFLAGS = -pipe -Wall -Wextra -Weffc++ -pedantic -ggdb
SRCS = $(wildcard *.cxx)
OBJS = $(patsubst %.cxx,%.out,$(SRCS))
all: $(OBJS)
clean:
rm -fv $(OBJS)
%.out: %.cxx
$(CXX) $(CXXFLAGS) $^ -o $#
NOTE: As is obvious from above, I am using *.out for executable file extensions (and not for object file).
Also, there are some files which are compiled together:
g++ file_main.cxx file.cxx -o file_main.out
To compile such files, until now I have been adding explicit rules in the Makefile:
file_main.out: file_main.cxx file.cxx
file.out: file_main.out
#echo "Skipping $#"
But now my Makefile has a lot of explicit rules, and I would like to replace them with a simpler implicit rule.
Any idea how to do it?
First, this method of compiling several source files directly into an executable is not a terribly good idea. The more common compile-then-link approach will save a lot of unnecessary compilation.
That said, the way to replace many explicit rules with a simpler rule depends on what the explicit rules have in common. You already have a pattern rule:
%.out: %.cxx
$(CXX) $(CXXFLAGS) $^ -o $#
and if all you want to do is add another source file to a particular target, you don't have to do this:
g++ file_main.cxx file.cxx -o file_main.out
you can get the effect just by adding a prerequisite (in a line by itself):
file_main.out: file.cxx
If you have several targets with that pattern, you can use a pattern rule:
file_main.out another_main.out a_third_main.out: %_main.out : %.cxx
If you have many such targets, you can use a variable:
MAIN_THINGS = file another a_third a_fourth and_yet_another
MAIN_TARGETS = $(addsuffix _main.out, $(MAIN_THINGS))
$(MAIN_TARGETS): %_main.out : %.cxx
And you can add other patterns for other target sets, even overlapping sets. Does that cover your situation?
It seems that you are putting the source code for multiple different programs in the same folder, and this is really the source of your problems. If you separate the source code for your libraries and programs into separate folders (or, better yet, separate projects), then you can skirt this issue by depending on all source files in the given folder. When you have everything intermixed, it is necessary to be explicit.
That said, if your dependencies have consistent, predictable names, then it is possible to eliminate this redundancy by using the eval function. For example, based on the example above:
#
# I'm going to use standard file extensions here,
# slightly deviating from your conventions. I am also
# assuming that there is a variable named PROGNAMES,
# which gives a list of all the programs to be built.
#
define ADD_EXECUTABLE
$(1): $(1).o $(1)_main.o
$(LINK.cc) $(1).o $(1)_main.o -o $(1)
endef
$(foreach progname,$(PROGNAMES),$(eval $(call ADD_EXECUTABLE,$(progname))))
Also, just a few suggestions... you should append to CXXFLAGS rather than overwrite it and you would be better off using standard file extensions (".cpp" for C++ source files, ".o" for object files, no extension for executables). See my Makefile tutorial for tips on making things easier with Make (no pun intended).

Resources