using makefile targets to set build options - makefile

This is either trivial or runs counter to the philosophy of how make should be used, but I'd like to have a command line that reads as "make debug" rather than "make DEBUG=1". I tried creating a phony target called debug that did nothing except set the DEBUG variable, but then there was a difference between "make debug build" and "make build debug"--namely that in one case, the variable got set after the build happened.
Is there a way to give certain targets precedence?
Thanks for your help.

See 'Target-specific variable values' in this page.

You can also do it by looking at the MAKECMDGOALS variable
ifneq "$(findstring debug, $(MAKECMDGOALS))" ""
DEBUG = 1
endif
build:
#echo build and DEBUG is [$(DEBUG)]
debug:
This is what happens when you call it:
$ make build
build and DEBUG is []
$ make build debug
build and DEBUG is [1]
make: Nothing to be done for `debug'.
$ make debug build
make: Nothing to be done for `debug'.
build and DEBUG is [1]

you can write the following:
.PHONY: debug
debug:
$(MAKE) -$(MAKEFLAGS) build DEBUG=1
build:
echo makeflags='$(MAKEFLAGS)' debug=${DEBUG}
This will at least work with GNU Make, BSD Make and Interix Make. I didn't try all the other implementations.

One thing you can do with GnuMake is use macros that expand to rules with the foreach builtin. Something like:
TARGETS := build all foo bar baz
define DEBUG_TARGET_RULE
$(1).debug:
$$(MAKE) DEBUG=1 $(1)
debug.$(1):
$$(MAKE) DEBUG=1 $(1)
endef
$(foreach target,$(TARGETS),$(eval $(call DEBUG_TARGET_RULE,$(target))))
This will allow you to type make debug.foo or make foo.debug and it will automatically turn into make DEBUG=1 foo, and it works for any target you put in $(TARGETS)

If your debug will only be used with the build target, you might as well make debug call build so you can just type make debug or make build, where make build would be non-debug.
But as for your actual question, I'm not familiar enough with makefiles to answer it.

One approach is to set up the dependencies and build rules in both the build and debug targets, but add the your debugging options to the debug target. A simple example:
Makefile
program: program.c
gcc -o program program.c
debug: program.c
gcc -D DEBUG -o program program.c
Program.c
#include <stdio.h>
int main(void) {
#ifdef DEBUG
printf("DEBUG on!\n");
#endif
printf("in the program\n");
return 0;
}

Related

How to build a programs that's output will be used in a Make variable name

I want to create a Makefile which will build a program which when run's output will be used as a value for a variable in the same Makefile.
Consider the program:
print_name.c:
#include <stdio.h>
int main() {
printf("foo\n");
return 0;
}
I'd like to use the standard output of this program to then determine a directory name to be stored in a variable used in the makefile and used for other rules, so far I've tried:
all: $(MY_DIRECTORY_NAME)/my_program
print_name: print_name.c | print_name
gcc $^ -o $#
MY_DIRECTORY_NAME:=$(shell ./print_name)
$(MY_DIRECTORY_NAME)/my_program: my_program.c
mkdir -p $(MY_DIRECTORY_NAME)
gcc $^ -o $#
However when I run:
make all
I get:
make: *** No rule to make target '/my_program', needed by 'all'. Stop
Whereas I'd like:
mkdir -p foo
gcc my_program.c -o foo/my_program
So basically, I want to compile print_name before the assignment to MY_DIRECTORY_NAME, and then that variable in then used in the following rules. Is this possible, and if not are there any workarounds for this?
The basic problem is that you need to build (and run) print_name before you can parse the rules that use it -- but all rules are read and parsed before anything is built. So you need to run make twice -- first to build print_name and second to build everything that depends on it. Something like
all: print_name
$(MAKE) `./print_name`/my_program
should do the trick

Including another Makefile results in a failed build, why?

Problem
Including an external Makefile makes my previously stable build fail with the following error :
make: *** No rule to make target 'build/main.o', needed by 'all'. Stop.
Context
The context is that I currently manage the build of each of my projects with one Makefile per project. Since the projects Makefiles have a lot of redundancy, I want to put the common things in an external Makefile that will be included in each project's Makefile.
Minimal example to reproduce the issue
In this simple and minimalist example I try to build srcs/main.c into build/main.o and I also try to display what is inside the variable FOO which in is tools.mk :
Folder structure :
| Makefile
|
+---build
| (main.o)
+---mkf
| tools.mk
|
\---srcs
main.c
Content of Makefile :
include ./mkf/tools.mk
mkfile_path :=$(realpath $(lastword $(MAKEFILE_LIST)))
current_dir :=$(dir $(mkfile_path))
VPATH = $(current_dir)/srcs
./build/%.o: %.c
#echo $< $#
gcc $< -o $#
all: ./build/main.o test
#echo Done !
test:
#echo Testing include : $(FOO)
.PHONY: all test
Content of tools.mk :
FOO = 42
Content of main.c (basic hello world) :
# include <stdio.h>
# include <stdlib.h>
int main(void)
{
printf("Hello, world!");
return EXIT_SUCCESS;
} /*main*/
Now basically my problem is that if I place myself in the root folder and type make all, the build will fail with the error mentioned above. However if I comment the line include ./mkf/tools.mk the build succeeds. Thus I guess it fails because of the include line, but I cannot figure out why.
Can someone enlighten me on this ?
The build is performed with GNU Make 4.2 on Windows 7 64-bits.
In
mkfile_path :=$(realpath $(lastword $(MAKEFILE_LIST)))
That yields the path of the last included makefile, which is ./mkf/tools.mk. See Other Special Variables for details.
A fix:
mkfile_path :=$(realpath $(lastword $(MAKEFILE_LIST)))
current_dir :=$(dir $(mkfile_path))
include ./mkf/tools.mk

Calling make with two targets

I have C++ project that I build using a Makefile with two targets as so
debug: FLAGS += -g3 -DDEBUG -DSOCKET_LOG_COMMUNICATION
#printf ""
test: some_other_target
$(COMPILER) ...
I wanted to call make as so
make debug test
to define a macro and also build the test target. Is this possible?
Target-specific variables apply only to the named target and its dependencies (unless the variable is declared private), so the only way you would be able to have test inherit debug's variables would be debug: test which probably isn't what you're looking for.
One way to do this is use conditional statements:
ifdef debug
FLAGS += -g3 -DDEBUG -DSOCKET_LOG_COMMUNICATION
$(info whatever)
endif
test: some_other_target
$(COMPILER) ...
and invoke make test debug=1.

How to preprocess makefiles

How to show the makefile after it's been preprocessed? For example, if we have two makefiles:
# Makefile
include Makefile2
# Makefile2
a:a.c
gcc -o a a.c
Then <preprocessor> Makefile should give:
a:a.c
gcc -o a a.c
It's similar to what a C preprocessor does (gcc -E). Is there such a makefile preprocessor?
You didn't specify for which make tool you are writing makefile. Assuming that it is GNU make, you can try running makefile with -n (--just-print) option See Command-Line Options chapter here. That will show what make is going to execute without execution (however, the commands needed for evaluation of variables will be executed). This is probably the closest to what you want to see.
This causes make to read the makefile and print every command it would
normally execute to update the target but without executing them.
Apart from that there is $(warning ) function to debug makefiles. You can place it almost to any part in makefile and the following will show you the values of all defined variables in that place:
$(warning Variables HERE: .VARIABLES)

How do you implement a Makefile that remembers the last build target?

Let's say you have a Makefile with two pseudo-targets, 'all' and 'debug'. The 'debug' target is meant to build the same project as 'all', except with some different compile switches (like -ggdb, for example). Since the targets use different compile switches, you obviously need to rebuild the entire project if you switch between the two. But GNUmake doesn't naturally recognize this.
So if you type make all you'll get
Building ...
...
Then if you type make debug, you get
make: Nothing to be done for `debug'.
So my question is: how do you implement a clean solution in the Makefile to notice that the last build used a different pseudo-target, or different compile switches, than the one you want currently? If they are different, the Makefile would rebuild everything.
Put the build products into different directory trees (whilst keeping one copy of the source of course). That way you are always just a short compile from an up-to-date build, be it debug or release (or even others). No possibility of confusion either.
EDIT
Sketch of the above.
src := 1.c 2.c 3.c
bare-objs := ${src:%.c=%.o}
release-objs := ${bare-objs:%=Release/%}
debug-objs := ${bare-objs:%=Debug/%}
Release/prog: ${release-objs}
Debug/prog: ${debug-objs}
${release-objs}: Release/%.o: %.c # You gotta lurve static pattern rules
gcc -c $< -o $#
${debug-objs}: Debug/%.o: %.c
gcc -c $< -o $#
Release/prog Debug/prog:
gcc $^ -o $#
.PHONY: all
all: Release/prog ; echo $# Success
.PHONY: debug
debug: Debug/prog ; echo $# Success
(Disclaimer: not tested, nor even run through make.)
There you go. It's even -j safe so you can do make -j5 all debug. There is a lot of obvious boiler plate just crying out for tidying up.
Keeping variant sets of object files (as in bobbogo's solution) is probably the best way, but if for some reason you don't want to do that, you can use empty files as markers, to indicate which way you last built the executable:
%-marker:
#rm -f $(OBJECTS) *-marker
#touch $#
debug: GCCFLAGS += -ggdb
debug: SOMEOTHERFLAG = WHATEVER
all debug: % : %-marker
#echo making $#
#$(MAKE) -S GCCFLAGS='$(GCCFLAGS)' SOMEOTHERFLAG='$(SOMEOTHERFLAG)' main
There are other variants on this idea; you could have a small file containing the flag settings, which the makefile would build and include. That would be clever, but not really any cleaner than this.
The only clean solution is to incorporate the difference into the target names.
E.g. you can define a variable $(DEBUG) and consistently use it in all targets that depend on the compile step.

Resources