Does Makefile "-B" get passed to "child" Makefiles - unix

If I have a Makefile that calls another Makefile:
my_target:
$(MAKE) -C path/to/Makefile all
And I run:
make -B my_target
Will the -B flag be passed to the other Makefile when it is called $(MAKE) -C path/to/Makefile all?

Related

GNU Make: always build pre-targets but don't rebuild target

Imagine my Makefile has something like:
CXXFLAGS = -O3 ${INCLUDES} --std=c++17 -g ${AUTO_ARGUMENT}
COMPILE.cc = $(CXX) $(DEPFLAGS) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
.PHONY: all directories
all: directories programs
directories: obj
obj:
mkdir obj
programs: Foo
Foo: obj/Foo.o
${CXX} obj/Foo.o ${LDFLAGS} -o Foo
obj/%.o : %.cpp
$(COMPILE.cc) $(OUTPUT_OPTION) $<
clean:
rm -rf Foo obj
I can execute make and it will create the obj subdirectory then do a nice compile and link. Works great. But if I do make clean Foo, it's going to fail. The clean removed the subdir and because I bypassed all to just make a single target, it doesn't recreate obj.
So I can do this:
Foo: directories obj/Foo.o
${CXX} obj/Foo.o ${LDFLAGS} -o Foo
But then it ALWAYS does the link:
$ make
g++ obj/Foo.o -o Foo
$ make
g++ obj/Foo.o -o Foo
But if I remove the directories part from Foo:
$ make
make: Nothing to be done for 'all'.
This is even worse:
obj/%.o : directories %.cpp
$(COMPILE.cc) $(OUTPUT_OPTION) $<
So, my question... Is there some way that I can tell an individual target to do some of the pre-setup without that target then always being rebuilt? I could probably make fake targets like this:
makeFoo: directories Foo
But that's annoying. I could also have all be:
all: setup programs
setup: directories
And then do make setup Foo. That's only moderately annoying. What I'd really like is the rule for the objects to ensure the directory exists without adding any spam or unnecessary rebuilds. I suppose I could add something to that particular rule to ensure the directory exists:
obj/Foo.o: Foo.cpp
if [ -d obj ]; then \
mkdir obj \
fi
$(COMPILE.cc) $(OUTPUT_OPTION) $<
Is there a cleaner way?
Thanks to Andreas, I made some adjustments. Here's my entire sample Makefile:
CXXFLAGS = -O3 ${INCLUDES} --std=c++17 -g ${AUTO_ARGUMENT}
COMPILE.cc = $(CXX) $(DEPFLAGS) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
.PHONY: all directories
all: directories programs
directories: | obj
obj:
mkdir obj
programs: Foo
Foo: obj/Foo.o
${CXX} obj/Foo.o ${LDFLAGS} -o Foo
obj/%.o : %.cpp | obj
$(COMPILE.cc) $(OUTPUT_OPTION) $<
clean:
rm -rf obj Foo
And here are my runs:
$ make clean Foo
rm -rf obj Foo
mkdir obj
g++ -O3 --std=c++17 -g -c -o obj/Foo.o Foo.cpp
g++ obj/Foo.o -o Foo
$ make Foo
make: 'Foo' is up to date.
The trick was order-only dependencies -- the pipe thing. See the rule for obj/%.o and directories. Note that it works the same if I do it this way or if my obj/%.o rule used directories instead.

Makefile | How to check only once whether an output dir already exists?

Currently the shell script checks for the existence of intended object directory right before each compiler call. How do I modify my Makefile so that the code checks only once before it moves on to compiling all the prerequisites?
Here is my Makefile:
#########################################################
## BUILD TASKS ##
#########################################################
HDIR := hdr
SDIR := src
ODIR := obj
EXET := a
OBJS := main.o mainhdr.o testcode.o
OSRC := $(addprefix $(ODIR)/, $(OBJS))
CXX := g++
CXXFLAGS := -I$(HDIR) -g -Wall -std=c++17
## BUILD DIRECTIVE:
all: $(EXET)
$(EXET): $(OSRC)
$(CXX) $(CXXFLAGS) $^ -o $#
$(ODIR)/%.o: $(SDIR)/%.cpp
if [ ! -d "$(ODIR)" ]; then mkdir $(ODIR); fi
$(CXX) -c $(CXXFLAGS) $^ -o $#
## foo.bak: foo.bar
## if [ ! -d "$(ODIR)" ]; then mkdir $(ODIR); fi
#########################################################
## CLEAN TASK ##
#########################################################
.PHONY: clean
clean:
rm -r $(EXET) $(ODIR)
I have tried putting:
foo.bak: foo.bar
if [ ! -d "$(ODIR)" ]; then mkdir $(ODIR); fi
as suggested here right before the clean task, but that doesn't seem to work. I could be understanding it wrong, but isn't that the Makefile is executed recursively so by putting that block of code at the end it should be at the tip of recursion and thus executed before everything else?
On newer gnu-make, you can use 'order-only-prerequisites`. This eliminate the timestamp checking, and only required that the prerequisite will exists. This works well to ensure that directories will be created before files are stored into them, and can significantly speedup build jobs
$(ODIR):
mkdir $(ODIR)
# Note pipe '|' to separate order only prereq.
$(ODIR)/%.o: $(SDIR)/%.cpp | $(ODIR)
$(CXX) -c $(CXXFLAGS) $^ -o $#
See: https://www.gnu.org/software/make/manual/make.html#Prerequisite-Types

Using a shell variable within Makefile

I have a line line in make file for compiling a c program, which goes like this
$(CC) -c -o $# $< $(CFLAGS). I have to modify a particular line in code every time and compile again. Modification is just an argument to a function. I have the argument in one file and i use sed utility to modify my c source and then compile. I want to see which of the arguements leads to successful compilation. I tried to use this $(CC) -c -o $# $< $(CFLAGS) ; echo $? >status where I was hoping if compilation was successful status file would have an entry 0. But i see the source file name in status file. I came to know that $? is also a make automatic variable. How can i read the shell variable $? within makefile ? I have tried using $(CC) -c -o $# $< $(CFLAGS) ; echo $$? >status and $(CC) -c -o $# $< $(CFLAGS) ; echo $(shell echo $?) >status without getting correct results.
The version
echo $$? > status
definitely works for me. What OS are you using? What are the incorrect results when using $$??
If you are on Windows, there is no $?, you might want ot use %errorlevel% instead.

How to include clean target in Makefile?

I have a Makefile that looks like this
CXX = g++ -O2 -Wall
all: code1 code2
code1: code1.cc utilities.cc
$(CXX) $^ -o $#
code2: code2.cc utilities.cc
$(CXX) $^ -o $#
What I want to do next is to include clean target so that every time
I run make it will automatically delete the existing binary files of code1 and code2 before creating the new ones.
I tried to put these lines at the very end of the makefile, but it doesn't work
clean:
rm -f $#
echo Clean done
What's the right way to do it?
The best thing is probably to create a variable that holds your binaries:
binaries=code1 code2
Then use that in the all-target, to avoid repeating:
all: clean $(binaries)
Now, you can use this with the clean-target, too, and just add some globs to catch object files and stuff:
.PHONY: clean
clean:
rm -f $(binaries) *.o
Note use of the .PHONY to make clean a pseudo-target. This is a GNU make feature, so if you need to be portable to other make implementations, don't use it.
In makefile language $# means "name of the target", so rm -f $# translates to rm -f clean.
You need to specify to rm what exactly you want to delete, like rm -f *.o code1 code2
By the way it is written, clean rule is invoked only if it is explicitly called:
make clean
I think it is better, than make clean every time. If you want to do this by your way, try this:
CXX = g++ -O2 -Wall
all: clean code1 code2
code1: code1.cc utilities.cc
$(CXX) $^ -o $#
code2: code2.cc utilities.cc
$(CXX) $^ -o $#
clean:
rm ...
echo Clean done

make: hierarchical make file

(disclaimer: I am used to scons ... I am somewhat unexperienced with make)
Context: I am using Eclipse CDT which generates makefiles.
Let's say I have a project directory 'lib' and 2 build configurations 'Debug' and 'Release'. Eclipse CDT gracefully generates a makefile for each build configuration. The said makefiles end-up residing in 'Debug' and 'Release' folders.
Now, what I want to do is have a makefile in the folder 'lib' which calls the makefiles 'Debug/makefile' and 'Release/makefile'.
How do I do that?
I want to be able to launch 'make' in the folder 'lib' and both configurations would be called with the specified target(s).
Solution
Based on all the great input gathered here, I devised the following:
MAKE=make
BUILDS=Release Debug
TARGETS=all clean
$(TARGETS):
#for b in $(BUILDS) ; do $(MAKE) -C $$b $# ; done
$(BUILDS):
#for t in $(TARGETS) ; do $(MAKE) -C $# $$t ; done
%:
#for b in $(BUILDS) ; do $(MAKE) -C $$b $# ; done
depends on what is "calls". You want to either
include $(BUILD)/Makefile
or
$(MAKE) -C $(BUILD) $#
or some such. I'd guess you want the latter. Maybe something like
release debug:
$(MAKE) -C $#
You get the idea.
More examples:
BUILDS=release debug
TARGETS=all clean
$(TARGETS):
for b in $(BUILDS) ; do $(MAKE) -C $$b $# ; done
$(BUILDS):
for t in $(TARGETS) ; do $(MAKE) -C $# $$t ; done
Since you mention "the specified target(s)", I suggest:
%:
$(MAKE) -C Debug $#
$(MAKE) -C Release $#
If that's too general, you can replace the % with $(TARGETS), where TARGETS is something you define, a list of all the things you'd ever want to do this with.
all: release debug
release:
$(MAKE) -C ../Release
debug:
$(MAKE) -C ../Debug
I'm assuming they're all on the same level. The path must be from where you call Make.
Have different targets that invoke the makefile in the two directories.
all: debug product
debug:
$(MAKE) -f debug/Makefile
product:
$(MAKE) -f product/Makefile

Resources