Makefile: different variable's value based on rule - makefile

I've got the following situation in my makefile:
SDLINC_NOVA = -I/usr/local/lib/sdl_2.0.5/include/SDL2 -D_REENTRANT
SDLLIB_NOVA = -L/usr/local/lib/sdl_2.0.5/lib -Wl,-rpath,/usr/local/lib/sdl_2.0.5/lib -Wl,--enable-new-dtags -lSDL2 -lSDL2main
SDLINC_MAC = -I/usr/local/SDL/include -D_REENTRANT
SDLLIB_MAC = -L/usr/local/SDL/lib -Wl,-rpath,/usr/local/SDL/lib -Wl,-install_name,--enable-new-dtags -lSDL2 -lSDL2main
....
.PHONY: all nova mac clean
all: nova
nova: SDLINC = $(SDLINC_NOVA)
nova: SDLLIB = $(SDLLIB_NOVA)
nova: build
mac: SDLINC = $(SDLINC_MAC)
mac: SDLLIB = $(SDLLIB_MAC)
mac: build
build: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(OBJECTS) $(SDLLIB) -o $#
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
$(CC) $(CFLAGS) $(SDLINC) -c $< -o $#
It feels like I'm doing something wrong.. Basically the SDLINC and SDLLIB variables should contain different value based on the rule that is called, and then the build rule should be called.
What is the right convention to achieve that in a makefile?

Basically, the right approach would be not to have two different rules for Mac and Linux. This is because both of those are Unix-based systems and are quite common.
The approach is to automatically detect the system you are running on, this can be done using uname shell command:
OS := $(shell uname)
ifeq ($(OS), Darwin)
OS := mac
else ifeq ($(OS), Linux)
OS := linux
else
$(info Warning: unknown OS, assuming linux)
OS := linux
endif
And then just add ifeq conditions to assign your variables:
ifeq ($(OS), linux)
SDLINC = $(SDLINC_NOVA)
SDLLIB = $(SDLLIB_NOVA)
else ifeq ($(OS), mac)
SDLINC = $(SDLINC_MAC)
SDLLIB = $(SDLLIB_MAC)
endif
and remove your rules for mac and nova, now you only need one build rule.
You can check what is the output of uname command on your Nova machine and adjust it accordingly in your makefile, but I'm pretty sure it will be Linux.

The problem is that the build target is a common prerequisite of both nova and mac. Therefore, the rule for the target build is only matched once. That is, it is matched for the first target that requires it, then it remains updated for the second target that requires it.
You can set up your makefile, so that those targets above depend on different prerequisites:
nova: SDLINC = $(SDLINC_NOVA)
nova: SDLLIB = $(SDLLIB_NOVA)
nova: build-nova
mac: SDLINC = $(SDLINC_MAC)
mac: SDLLIB = $(SDLLIB_MAC)
mac: build-mac
With this new setup, mac depends on build-mac and nova depends on build-nova.
Then, by adding the following rule:
build-nova build-mac:
$(MAKE) --no-print-directory $(TARGET)
its recipe will be executed for each target (i.e.: build-nova and build-mac) separately.
What the recipe of that rule does is to call make recursively for the target specified (i.e.: the result of expanding TARGET). You need however to export both SDLINC and SDLLIB variables in your makefile, so that the new process which runs make obtains them:
export SDLINC
export SDLLIB

Related

How can I run two makefile targets such that the second uses the first's variables?

I am writing a makefile that has multiple targets for architectures and build types. When I call make I would like to be able to pass it two targets such as:
make x86 debug
Where the architecture target sets variables to be used by the build target. An architecture target might look like this:
.PHONY: x86
x86: NAME_PREFIX = x86_64_Linux
x86: TARGET_TRIPLE = x86_64-pc-linux-elf
x86: CC = gcc
x86: CFLAGS += -fomit-frame-pointer
First issue: If I run the two targets, make skips the first target since it does nothing but set variables.
$ make x86 debug
make: Nothing to be done for `x86'.
Second issue: Even if it would run the first target, my understanding is that the values won't be retained when make starts running the second target anyway.
For now, I'm using targets that set variables and add the build type target as a prerequisite at the end of the target body, but I would end up needing a target for every combination of architecture and build type.
.PHONY: x86_debug
x86_debug: NAME_PREFIX = x86_64_Linux
x86_debug: TARGET_TRIPLE = x86_64-pc-linux-elf
x86_debug: CC = gcc
x86_debug: CFLAGS += -fomit-frame-pointer
x86_debug: debug
Your second approach is better. But you don't need a target for every combination, if you use a consistent naming scheme, and use wildcard targets:
.PHONY: x86_% arm_% # etc
.PHONY: %_debug %_optimised # etc
x86_%: NAME_PREFIX = x86_64_Linux
x86_%: TARGET_TRIPLE = x86_64-pc-linux-elf
x86_%: CC = gcc
x86_%: CFLAGS += -fomit-frame-pointer
arm_%: NAME_PREFIX = ARM_LINUX
arm_%: TARGET_TRIPLE = arm-linux-eabi
arm_%: CC = arm-linux-gcc
%_debug: CFLAGS += -g
%_optimised: CFLAGS += -O3

Default link script in GNU Make

I have this very simple makefile:
P = hello_world.exe
OBJECTS = main.o
CFLAGS = -g -Wall -O3
LDLIBS =
CC = clang
$(P): $(OBJECTS)
When I run make it will compile main.c but it will not link to hello_world.exe. Shouldn't that be happening automatically?
My environment is cygwin 64bit.
The output of make -p is here: http://pastebin.com/qbr0sRXL
There's no default rule for .exe files that I'm aware of (or can find in that output).
You'll need to write one yourself.
If your output was hello_world and you had a hello_world.c/hello_world.cpp source file and also a main.c/main.cpp file then your makefile as written would work I believe (since the default %: %.o rule would apply and your added prerequisite would be added to the hello_world prerequisite list).

How can I make a target "private" in GNU make for internal use only? OR: how to best enforce target-specific variable-values?

I have some ancillary targets in a makefile that I want to restrict for internal or "private" use (only) inside the makefile. That is, I want to be able to specify these targets as dependencies from within the makefile, but I want to prevent the target from being specified as a build goal from the command line. Somewhat analogous to a private function from OOP: the target is harmful (or simply doesn't make sense) to build separately.
I wish there were a special-target .HIDDEN or .PRIVATE or something that did this, akin to what .PHONY does for non-file targets, but I don't think this exists. The private keyword is only for variables.
What is a good/general/elegant way to protect a target for internal/private use only?
The best workaround that I could come up with is to check $(MAKECMDGOALS) for "unacceptable" targets, then error-out if specified; this seems inelegant. I'm sure the makefile could be rewritten to avoid this situation -- perhaps a superior solution -- but that's not practical here.
Below the cut-line... here's a contrived example for illustration.
Though I'm looking for a general solution, one example of targets that are harmful as individual/primary goal is with inheriting of target-specific variable values:
override CFLAGS += -Wall
all : debug
%.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $# $<
debug : CFLAGS += -g3 -O0
release : CFLAGS += -O3
debug : CPPFLAGS += -DDEBUG
release : CPPFLAGS += -DRELEASE
debug release : foo.o bar.o main.o
$(CC) -o $# $^ $(LDFLAGS) $(LDLIBS)
clean:
-rm -f *.o debug release
.PHONY: all clean
Implicit rule duplicated (unnecessary) for illustration. With the goal of debug or release, foo.o and others will inherit respective CFLAGS and CPPFLAGS -- If one does make clean debug all objects will be consistent. But for example if someone builds foo.o separately, it will fail to inherit the appropriate flags; e.g., make clean foo.o debug you'll get foo.o built with default CFLAGS; then it doesn't need to be updated when building debug, so it will be linked with other objects with different optimizations or different macro settings. It will probably work in this case, but it's not what was intended. Marking foo.o, etc. as illegal goals would prevent this.
EDIT:
It's very clear that my example (above) was not a good choice for my more-general question: hiding targets was not the best way to fix an issue with my example. Here's a modified example that illustrates the modified question "How to enforce target-specific values?" -- it builds on commentary from #Michael, #Beta, #Ross below, and allows posing and answering this more limited scenario.
As described in previous responses below, it's a much better idea in this case to create objects that have different build flags in separate locations. e.g.,
bin_debug/%.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $# $<
bin_release/%.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $# $<
OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c)
DEBUG_OBJS = $(addprefix bin_debug/,$OBJS)
RELEASE_OBJS = $(addprefix bin_release/,$OBJS)
debug : $(DEBUG_OBJS)
release : $(RELEASE_OBJS)
debug release :
$(CC) -o $# $^ $(LDFLAGS) $(LDLIBS)
Pattern rule duplicated because I think it has to be (multiple "pattern targets" (%) convince make all targets are built at once with one recipe; see SO questions this and this).
So now, add in target-specific flags:
debug : CPPFLAGS += -DDEBUG
release : CPPFLAGS += -DRELEASE
But this still suffers:
make bin_debug/foo.o
will not get the CPPFLAGS from debug. I've accepted #Michael's answer below as it got me thinking about the problem in a more helpful way, but also answered some of my own rhetorical questions below.
You kind of can define private targets by starting their name with two hyphens.
--private-target:
#echo private
public-target: --private-target
#echo public
You can call make public-target but make --private-target will complain about an unknown option:
$ make public-target
private
public
$ make --private-target
/Library/Developer/CommandLineTools/usr/bin/make: unrecognized option `--private-target'
This is not a feature of make, but takes advantage of the fact that command line options are passed with two hyphens and as a result make will complain about an unknown option. This also can be easily bypassed by signaling the end of options:
$ make -- --private-target
private
$ make --version
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
This program built for i386-apple-darwin11.3.0
The problem you are trying to solve is legitimate but you are heading on the worse possible path to solve it.
Declaring private targets does not make any sense
When we write a Makefile, we are describing a compilation job in terms of targets, sources and recipes. The advancement of this job is described by the set of targets which are already built. Now you are accurately observing that the sequence
make clean
make foo.o
make debug
will produce objects whose format is inconsistent with foo.o thus leaving your build directory in an inconsistent state. But it is very wrong to deduce that the user should not be able to construct foo.o explicitly. Consider the following sequence:
make clean
# Wait for foo.o being compiles and
# interrupt the build job with a signal
make debug
Since make sees that foo.o it will resume its task where it was at and left foo.o untouched while compiling subsequent units with different flags, leaving the build directory the same inconsistent state as in the first scenario.
Hence, if we could implement private targets in Makefiles, this would be ineffective and could convey a false sense of security, which is even worse than insecurity by itself. Also the solution you imagined annihilates one of the most important advantages of using Makefiles over shell scripts: Make makes it easy to continue an interrupted task where it was at.
I documented some other aspects of using Makefiles in relation to the set of targets already built in my answer to the question “What is the purpose of linking object files separately in a Makefile?”.
Another solution to your problem
To address the issue of compilation flags inconsistency, we can arrange to store built targets into a special directory, depending on the compilation flags used. Implementing this would fix the issue without forcing us to resign upon the ease of resuming an interrupted compilation job.
Here is an implementation roadmap:
Identify build profiles, here you have release and build.
Choose which compilation to use for each build profile.
Choose in which directory to store built targets for each build profile.
Write your Makefile so that built targets are stored in the directories you choosed. Please refer Gnu make - how to get object files in separate subdirectory.
Note. In my opinion, the BSD variant of make has a much nicer support for writing targets in a special directory, see my answer to the question “How to write a Makefile using different directories for targets and sources”. Generally I prefer the BSD variant of make because its documentation is short and to the point and it enjoys a lot of useful advanced examples, since operating system build and ports build in the BSD world are orchestrated by this program.
One solution to the problem is to migrate the CPPFLAGS to the pattern rules (e.g., bin_debug/%.o: CPPFLAGS...) instead of the regular rule (debug: CPPFLAGS...), final result:
bin_debug/%.o : CPPFLAGS += -DDEBUG
bin_debug/%.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $# $<
bin_release/%.o : CPPFLAGS += -DRELEASE
bin_release/%.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $# $<
OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c)
DEBUG_OBJS = $(addprefix bin_debug/,$OBJS)
RELEASE_OBJS = $(addprefix bin_release/,$OBJS)
debug : $(DEBUG_OBJS)
release : $(RELEASE_OBJS)
debug release :
$(CC) -o $# $^ $(LDFLAGS) $(LDLIBS)
so make bin_debug/foo.o will get CPPFLAGS including -DDEBUG.
Now, lets say you have >>2 rules: debug, release, config01, config02, config03, ... each with their own CPPFLAGS.
One way might be to continue reduplicating all of the pattern rules, but that gets annoying if anything has to change. Furthermore it's not really possible to use in a foreach. This seems handy:
debug : CPPFLAGS+=-DDEBUG
release : CPPFLAGS+=-DRELEASE
config01 : CPPFLAGS+=-DSOMETHING
config02 : CPPFLAGS+=-DSOMETHINGELSE
TARGETS = debug release config01 config02
OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c)
define TARGET_template
bin_$(1)/%.o : %.c
$$(CC) $$(CFLAGS) $$(CPPFLAGS) -c -o $# $<
$(1): $(addprefix bin_$(1)/,$(OBJS))
# other TARGET-specific stuff here
endef
$(foreach tgt,$(TARGETS),$(eval $(call TARGET_template,$(tgt))))
But still doesn't fix the situation of make bin_debug/foo.o -- still doesn't get CPPFLAGS.
So, instead of making target-specific variable-value like debug: CPPFLAGS+=... you could have a variable that is specific to the target, like CPPFLAGS_debug, then add to each rule:
CPPFLAGS_debug = -DDEBUG
CPPFLAGS_release = -DRELEASE
CPPFLAGS_config01 = -DSOMETHING
CPPFLAGS_config02 = -DSOMETHINGELSE
TARGETS = debug release config01 config02
OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c)
define TARGET_template
bin_$(1)/%.o : CPPFLAGS+=$$(CPPFLAGS_$(1))
bin_$(1)/%.o : %.c
$$(CC) $$(CFLAGS) $$(CPPFLAGS) -c -o $$# $$<
$(1): $(addprefix bin_$(1)/,$(OBJS))
# other TARGET-specific stuff here
endef
$(foreach tgt,$(TARGETS),$(eval $(call TARGET_template,$(tgt))))
Beware; above may need more $$(...)s, untested.
Problems? Better way?
Thinking about this and tried the following:
TEST := $(shell echo $$RANDOM)
test : $(TEST)
$(TEST):
<tab>#echo tada $(TEST)
then doing a make test on command line seems to work and I think it would be difficult to get the result without using the test target. Maybe this path can help?
I don't think there's any "elegant" way to have targets somehow made private. I think the only solution that could be called elegant would be to rewrite your makefile so that it doesn't matter what target users invoke, as Beta suggests. It would also have the advantage of making your makefile more maintainable and easier to understand.
A not so elegant but fairly simple way to make targets "private" would be to rename the makefile to something other than one of the default names. Then put a new makefile in it's place that invokes the "private" makefile to do it's work. Something like:
.SUFFIXES:
PUBLIC_TARGETS = all debug release clean
REAL_MAKEFILE = private.mak
define invoke_make
$(1): $(REAL_MAKEFILE)
$(MAKE) -f $(REAL_MAKEFILE) $(1)
endef
$(foreach target, $(PUBLIC_TARGETS), $(eval $(call invoke_make,$(target))))
.PHONY: $(PUBLIC_TARGETS)
Obviously this doesn't prevent a determined user from invoking "private" targets, but hopefully it makes it clear that they shouldn't be doing this. That's all making things private in object-oriented languages does anyways. It's always possible for a sufficiently determined user to bypass it.
Even if previous speakers called this a bad idea, I was very interested in the concept of having a custom special target like .PRIVATE to more or less protect some targets from beeing called straigt.
And for everyone interested in it... this is what I came up with:
ifeq ($(strip $(filter .PRIVATE,$(MAKECMDGOALS))),)
__PRIVATEGOALS = $(shell make -f $(firstword $(MAKEFILE_LIST)) -n .PRIVATE | tail -n 1)
$(foreach __privgoal,$(__PRIVATEGOALS),$(eval __PRIVATECMDGOALS += $(filter $(__privgoal),$(MAKECMDGOALS))))
endif
ifneq ($(strip $(__PRIVATECMDGOALS)),)
$(error tried to call *private* goal(s) $(strip $(__PRIVATECMDGOALS)))
endif
.PHONY: .PRIVATE
.SILENT: .PRIVATE
.PRIVATE:
##
$^
Put it at the top of your makefile, or at least in front of the first target declared as private. You could as well put it into a separate file, like private.mk and include it in your main makefile.
You should be able to use the .PRIVATE target in the same way as you use the .SILENT or the .PHONY targets. An error is triggered in case a "private" target is called and make stops.

Setting OS variable from rule

I am trying to build a shared library with one set of code, and everything works, except for this issue with my Makefile. Here's my (simplified) Makefile thus far:
OBJS = bar.o
libfoo.so: OS = LINUX # These don't seem to happen
libfoo.dll: OS = WINDOWS
# Linux
ifeq ($(OS), LINUX)
CC = gcc
...
# Windows
else ifeq ($(OS), WINDOWS)
CC = i686-pc-mingw32-gcc
...
endif
all: libfoo.so libfoo.dll
libfoo.so: clean $(OBJS)
...
libfoo.dll: clean $(OBJS)
...
bar.o: bar_$(OS).c bar.h
...
So, when you type make libfoo.so, I expect it to set OS = LINUX first. Then, when it gets to bar.o (it is a dependency of libfoo) it should know which bar_$(OS).c to use. However, I get the error:
make: *** No rule to make target `bar_.c', needed by bar.o. Stop.
Which tells me that when it tries to make bar.o, $(OS) is not set. But shouldn't that be the first thing that happens when I try to make libfoo.so, and that rule is evaluated?
Target-specific variables are available in the body of the rule, not in its prerequisites. But even if you could get this to work, you'd be asking for trouble: if you build one library and then the other, there's no way for Make to know that the bar.o that was made for the first is wrong for the second and should not be used.
There are several ways to get the effect you want, but none is perfect. I'd suggest using two different object file names, like bar_unix.o and bar_windows.o.
If you want to set a target-specific variable, and then have that variable available outside the body of that rule, you can recursively call the Makefile, after exporting the variable:
OBJS ?= foo.o # Use ? so it isn't blown away on recursive call
libfoo.so: OS = LINUX
libfoo.so: OBJS += linux_only.o
libfoo.so:
$(MAKE) -s build_libfoo_linux
build_libfoo_linux: $(OBJS)
#echo "OS = $(OS)" # Should print "OS = LINUX"
export OS # Can be anywhere
You have to remember to export the variables you want to "persist" after the recursive make call. And also, as shown above, if you append to any variables before the call, you'll want to make their initial assignment with ?= so they aren't set the second time.
You might want to detect the OS using uname and then conditionally compile. This explains

Adding a directory for the headers in a Makefile

Hello I would like to ask you, If someone knows how can I add a directory for the header files in the Makefile to avoid the error *.h not found, I have tried this option but does not work:
INC_PATH := -I /directory/to/add
At least for GNU make, try the implicit variable CFLAGS, as in:
CFLAGS=-I/directory/to/add
Although the goal is ultimately to affect the value of CFLAGS (as suggested by #unwind), it is often not a good idea to simply set the value of CFLAGS as it is often built out of many pieces. You have to understand the structure of the makefile, and the set of macros used.
[Added:
Eduardo asked: Can you post macros to do the same?
Yes, but whether they are helpful depends on how your makefiles are structured. Here's a moderately complex example from one of my makefiles.
CC = gcc -g
XFLAGS = -Wall -Wshadow -Wstrict-prototypes -Wmissing-prototypes \
-DDEBUG -Wredundant-decls
#CC = cc -g
#XFLAGS =
UFLAGS = # Always overrideable on the command line
DEPEND.mk = sqlcmd-depend.mk
INSTALL.mk = sqlcmd-install.mk
ESQLC_VERSION = `esqlcver`
OFLAGS = # -DDEBUG_MALLOC -g
OFLAGS = -g -DDEBUG -O4
PFLAGS = -DHAVE_CONFIG_H
OFILES.o = # rfnmanip.o # malloc.o # strdup.o # memmove.o
VERSION = -DESQLC_VERSION=${ESQLC_VERSION}
#INC1 = <defined in sqlcmd-depend.mk>
#INC2 = <defined in sqlcmd-depend.mk>
INC3 = /usr/gnu/include
INC4 = ${INFORMIXDIR}/incl/esql
INC5 = . #${INFORMIXDIR}/incl
INCDIRS = -I${INC3} -I${INC1} -I${INC2} -I${INC4} -I${INC5}
LIBSQLCMD = libsqlcmd.a
STRIP = #-s
LIBC = #-lc_s
LIBMALLOC = #-lefence
LIBRDLN = -lreadline
LIBCURSES = -lcurses
LIBPOSIX4 = -lposix4
LIBG = #-lg
LIBDIR1 = ${HOME}/lib
LIBDIR2 = /usr/gnu/lib
LIBJL1 = ${LIBDIR1}/libjl.a
LIBJL2 = ${LIBDIR1}/libjlss-${ESQLC_VERSION}.a
LIBTOOLS = ${LIBJL2} ${LIBJL1}
LDFLAGS = ${LIBSQLCMD} ${LIBTOOLS} -L${LIBDIR2} ${LIBG} ${LIBMALLOC} \
${LIBPOSIX4} ${LIBC} ${STRIP}
CFLAGS = ${VERSION} ${INCDIRS} ${OFLAGS} ${XFLAGS} ${PFLAGS} ${UFLAGS}
This a makefile for a program of mine called sqlcmd (a name chosen a decade and more before Microsoft created a command of the same name). I assume that the make program has a rule for compiling C code to object like:
${CC} ${CFLAGS} -c $*.c
and that the rule for linking a program from a set of object files listed in the macro OBJECTS looks like:
${CC} ${CFLAGS} -o $# ${OBJECTS} ${LDFLAGS}
As you can see, there are separately settable macros for the ESQLC_VERSION (the version of Informix ESQL/C in use, derived by default by runing a script esqlcver), then the include directories via INC1 to INC5 and INCFLAGS (there can be quite a lot of these, depending on platform), and optimizer flags (OFLAGS), extra flags (CFLAGS), user-defined flags (UFLAGS - an idiom I use in most of my makefiles; it allows the user to set UFLAGS on the make command line and add an extra flag to the build), and a bunch of library-related macros. This is what it takes for my development makefile to be tunable with minimal fuss to my development platform, which can be Linux, Solaris or MacOS X. For consumers of the program, there is a configure script generated by autoconf, so they don't have to worry about getting those bits right. However, that has a strong genetic resemblance to this code, including the UFLAGS option.
Note that many systems for makefile building have a mechanism for setting CFLAGS faintly similar to this - and simply assigning to CFLAGS undoes the good work done by the system. But you have to understand your makefile to be able to modify it sanely.
]

Resources