separate builds in separate directories - makefile

I'm sure this is a totally normal thing to do, but I can't figure out how to get make to do this.
I have a compiler that generates make dependencies of the usual form:
M/A.o : M/A.hs
M/B.o : M/A.o
So I write a rule to compile %.hs into %.o, add a rule to link the binary, include the dependencies file, and all is well. But I want to have several binary targets with different flags. E.g. I want build/test built with -DTESTING and build/profile built with -prof. So I need to keep the .o files in a separate tree, where they will be compiled with special flags.
The straightforward way I can think of would be to have dependencies that look something like this:
build/test/M/A.o : M/A.hs
build/test/M/B.o : build/test/M/A.o
build/profile/M/A.o : M/A.hs
... etc.
And then rules so that %.hs to build/test/%.o compiles with -DTESTING, etc. I think this would work, but it's clumsy, means preprocessing the deps file to add all that build/whatever/ prefix stuff, and would multiply its size by however many kinds of builds.
VPATH appears to be designed for this sort of thing and my idea was that I could set the VPATH and compiler flags depending on the target, and it almost works, but:
%.o: %.hs
#mkdir -p build/M
cp $< build/$#
VPATH = build
main: M/A.o M/B.o
cat $^ >$#
M/A.o : M/A.hs
M/B.o : M/B.hs
The first time the main target wants to run 'cat M/A.o M/B.o >main' which seems contrary to the gnu make documentation that says $^ should include the include the VPATH directory in which the dependency was found. Curiously, if I remove 'main' and make again, this time it uses the correct path. This is GNU make, 3.81.
What's going on here? Is there a better way to build with different flags? VPATH seems like a clumsy tool, surely there is a better way?

Make is working correctly. It tries cat M/A.o M/B.o >main the first time because it can't find the prerequisites it needs, but it knows a rule for M/A.o' andM/B.o(<em>not</em>build/M/A.o' and build/M/B.o) and expects that that is what the rule will produce. If you remove main and try again, it will find build/M/A.o' andbuild/M/B.o` via VPATH.
Let's modify this makefile in stages. First we change the VPATH so that it can find the .hs files (Make is good at using things there to build things here, not vise-versa, and that's what VPATH is good for), and change the rules slightly:
build/%.o: %.hs
cp $< $#
VPATH = M
main: build/A.o build/B.o
cat $^ > $#
Now for the different object directories.
build/test/%.o build/project/%.o: %.hs
cp $< $#
VPATH = M
test: build/test/A.o build/test/B.o
cat $^ > $#
project: build/project/A.o build/project/B.o
cat $^ > $#
Then we simplify those last two rules, so that it's easy to add more object files and binary targets:
OBJECTS = A.o B.o
test: $(addprefix build/test/,$(OBJECTS))
project: $(addprefix build/project/,$(OBJECTS))
test project:
cat $^ > $#
Now for the different compiler flags:
build/test/%.o: FLAGS += test_flags
build/project/%.o: FLAGS += proj_flags
build/test/%.o build/project/%.o: %.hs
#echo building $# from $^ using flags $(FLAGS)
cp $< $#
Finally the dependencies. This is a little tricky. Suppose you want the dependency B.o : A.hs to apply to however many object you have. This is one approach:
OBJECT_PATHS = build/test/ build/project/
# The following is from the included file generated by the compiler
$(addsuffix B.o,$(OBJECT_PATHS)) : A.hs
To generate lines like that, I'd pipe the raw lines (e.g. B.o: A.hs) through sed 's/\(.*\):\(.*\)/\1:\2/', and note that if you want to put this in a makefile command, don't forget to double the $ signs to preserve them for the shell.
I know that's a lot to absorb. Take it one step at a time and let us know how it works out.

If you haven't solved your problem by now or are experiencing further problems, best give the autotools (automake and autoconf) a chance. They'll quickly build you a Makefile that supports more configurable and flexible out-of-tree builds.

Related

GNU make generate assembly first, them compile them to .o and link

SOURCE=a.c b.c c.c
ASM=$(patsubst %.c,%.s, $(SOURCE))
all:%.o
gcc -o test $^
$(ASM):%.c
gcc -S -o $# $<
%.o:%.s
gcc -c -o$# $<
I want to generate assembly code (.s) first, then compile the assembly code to object (.o), then link them.
But it seems above makefile code does not work. What is the correct code?
When asking questions, does not work is never very useful... if it worked you probably wouldn't be asking a question! :-) Instead you should always show the command you ran and the output you received (or at least the failing part of the output if it's long). Please cut and paste the actual text rather than paraphrasing messages. Also, including the version of the make program you're using (make --version) and the platform you're running on is often helpful.
Luckily this time we can figure out the problem without this information:
This:
$(ASM):%.c
gcc -S -o $# $<
where ASM is a.s b.s c.s, is not a pattern rule because the targets don't contain a pattern character %. That means the prerequisite %.c is not treated as a pattern, but as an actual file name, literally %.c which obviously doesn't exist.
Similarly, this:
all: %.o
has the same problem: all is a target, so this depends on the literal file named %.o which doesn't exist, and can't be created.
Also as a general rule every recipe that creates a target must create the actual target you told make it would, so this all rule is wrong because the target name is all but the recipe creates the target test.
Finally, it's a very bad idea to name your program test because test is a common UNIX program and a shell built-in, so if you run test it won't do the right thing (if you run ./test it will work).
You want to have all depend on the program you want to build, say mytest, and mytest should depend on the actual .o files:
all: mytest
mytest: $(SOURCE:.c=.o)
gcc -o $# $^
Next, you need to define a pattern rule that knows how to create an assembly file from a source file:
%.s : %.c
gcc -S -o $# $<
That, along with your other pattern rules, is all you need: make will figure it all out from that.
Finally, make has a built-in rule that tells it how to build object files directly from source files. It's best to get rid of this to force make to use your rules; add this to your makefile to delete it:
%.o : %.c

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!

Is this a robust solution to creating output directories in a Makefile?

I've seen a few approaches to making output directories in Make.
These include making all directories ahead of time outside of any rule, and
making an object's destination directory as part of the object's rule.
Both of these approaches involve making directories that likely already exist.
Am I missing any gotchas or drawbacks that explain why I haven't seen the below approach?
.SECONDEXPANSION:
$(OBJDIR)%.o: %.c | $$(#D)/
# Compile command
.PRECIOUS: %/
%/:
# mkdir Command
make is very good at dealing with files. make is not very good at dealing with directories.
So treating directories as implementation detail internal to the target rule makes sense, because then make never has to consider the directory at all:
MKDIR_P = mkdir -p
$(objdir)%.o: %.c
#$(MKDIR_P) $(#D)
$(COMPILE.c) -o $# -c $<
Note that the processing and IO required for the mkdir -p can be neglected next to the processing and IO required for the compilation.
The problem with directories is that (contrary to any other target) you don't care for their timestamp, you only need them to exist. Many Makefiles get directories somehow wrong, and creating them over and over again is what you observe, so make will never detect "Nothing to be done for ...".
In fact, the only thing you need for correct handling of directories with GNU make is an "order only dependency", like shown in your example. The trailing slash normally isn't needed (you seem to use it in order to have a pattern rule, I'm not sure whether this works), and you don't need .PRECIOUS either. Your trick with .SECONDEXPANSION looks quite neat, I guess this will work, given the pattern rule indeed works that way (didn't try).
For an alternative, most Makefiles that handle directories correctly take a simpler approach by concatenating all needed output directories for a rule in a single variable and use this variable as a target for another rule, e.g. like in this simplified example:
MODULES:=src/main
OBJDIR?=obj
OBJS:=$(addprefix $(OBJDIR)/,$(addsuffix .c,$(MODULES)))
DIRS:=$(sort $(addprefix $(OBJDIR)/,$(dir $(OBJS))))
TARGET:= myprogram
all: $(TARGET)
myprogram: $(OBJS)
$(CC) -o$# $^
$(DIRS):
mkdir -p $(DIRS)
$(OBJDIR)/%.o: %.c Makefile | $(DIRS)
$(CC) -c -o$# $<
clean:
rm -fr $(OBJDIR)
.PHONY: all clean

How to force make to always rebuild a file

I have a version.c file in my project that contains current revision of the project and some other stuff that is passed as a definition (-D compiler option) from makefile.
I know that to force make to compile version.c always regardless of modification date I can touch version.c.
Is there a makefile only way to achieve this? If I write .PHONY : version.o the object file doesn't get build at all.
EDIT:
Here is my makefile:
export CC = gcc
export MODULES = $(sort \
sys \
cim \
version \
)
export FILES = $(sort \
main.c \
cim.c \
version.c \
)
VPATH = $(MODULES)
OBJS = $(FILES:.c=.o)
INCLUDES = $(addprefix -I,$(MODULES))
all:$(OBJS)
$(CC) $(INCLUDES) $(OBJS) -o main.exe
clean:
rm -rf *.o *.exe
cim.o: cim.c
main.o: main.c cim.o
version.o: version.c
.PHONY: version.o
.c.o :
$(CC) $(CFLAGS) $(INCLUDES) -c $<
The classic way to do it is:
version.o: .FORCE
.FORCE:
(and you might add .PHONY: .FORCE). The file '.FORCE' is presumed not to exist, so it is always 'created', so version.o is always out of date w.r.t it, so version.o is always compiled.
I'm not sure that making version.o into a phony file is correct; it is actually a real file, not a phony one.
Not a makefile way, but easier than touch:
make -B
‘-B’ ‘--always-make’
Consider all targets out-of-date. GNU make proceeds to consider targets and their prerequisites using the normal algorithms; however,
all targets so considered are always remade regardless of the status
of their prerequisites. To avoid infinite recursion, if MAKE_RESTARTS
(see Other Special Variables) is set to a number greater than 0 this
option is disabled when considering whether to remake makefiles (see
How Makefiles Are Remade).
If you want to do this using the FORCE mechanism the correct solution looks like this:
version.o: FORCE
.PHONY: FORCE
FORCE:
By explicitly declaring FORCE to be phony we make sure things will work right even if .SECONDARY: is used (.SECONDARY: will cause FORCE to be considered an intermediate file, and make doesn't rebuilt intermediate files unless they have prerequisites newer than the ultimate target, and FORCE doesn't have any prerequisites, so .PHONY: FORCE is needed).
The other solution (using $(shell touch version.c)) also has a problem: it may cause your editor to think version.c has been updated, and prompt for a reload of the file, which might end up being destructive if you've been editing the file's buffer but haven't yet saved it. If you don't mind this, it can be made even simpler by observing that the touch command is silent, so the assignment to the hack dummy variable isn't needed:
$(shell touch version.c) # This is enough, but will likely confuse your editor
The .PHONY "trick" referred to in the comments on the question generally DOES NOT work. It may look like it does because it will force a relink iff version.o already exists, but the actual object file won't get rebuilt if the .o file rule is an implicit rule (which it usually is). The problem is that make doesn't do the implicit rule search for explicitly phony targets. This make file shows the failure:
fooprog: test.o
cp $< $#
%.o: %.c
cp $< $#
.PHONY: test.o # WRONG
clean:
rm test.o fooprog
If a static pattern rule is used instead of an implicit rule the .PHONY: version.o trick will work. In general using static pattern rules instead of implicit rules cuts out most of the more confusing Make behaviors. But most make files use implicit rules.
The quick hack version when you just need it to work and you don't want to play Make games:
# Hack to get main.c rebuilt
hack := $(shell touch main.c)
Basically just make Make run touch for you.

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