conditional check against list of strings in GNU makefile command - makefile

I am trying to check a file against a list before I try to compile it in a GNU makefile. Will the conditional ifneq below be evaluated every time the rule is invoked or just once? The condition seems to always evaluate the same way.
If not is the only way to do this to put the conditional in the shell command? I realize it may seem weird that the target list could be "not OK" ... the Make system could certainly be fixed to eliminate that weirdness but the pain will be greater.
Any suggestions?
Eli
OKSRC := realfile1.cpp realfile2.cpp
%.o: %.cpp
ifneq ($(findstring $<,$(OKSRC),),)
... do the compile
else
#skip the file
endif

Quoting from the Make documentation:
Conditional directives are parsed immediately. This means, for example, that automatic variables cannot be used in conditional directives, as automatic variables are not set until the recipe for that rule is invoked. If you need to use automatic variables in a conditional directive you must move the condition into the recipe and use shell conditional syntax instead.
ifneq is a conditional directive, and $< is an automatic variable. So in short, your above code will not work, so you would have to use the shell-based conditional.
But I would strongly suggest that you fix the root cause (i.e. the erroneous dependency generation), rather than trying to hack around it.

I think you can use if function to defer the expansion of automatic variables until the condition is evaluated.
OKSRC = realfile1.cpp realfile2.cpp
.cpp.o:
$(if $(findstring $<,$(OKSRC)),$(CC) $(CFLAGS) -c $<,#echo skip $<)
And the result shows:
$ make realfile1.o realfile2.o realfile3.o
cc -c realfile1.cpp
cc -c realfile2.cpp
skip realfile3.cpp

Related

GNU make target-specific variables, want single assignment, getting many

Given the following GNU Makefile:
OBJS = a.o b.o
LIB = libX.a
$(LIB): $(OBJS)
$(AR) cr $# $^
$(LIB): CPPFLAGS = $(shell P)
When I build $(LIB), I can see that external program P is called twice, once each to build a.o and b.o (I have it just printing getpid() to stderr).
In my case, P will always produce the same result, so it's wasted cycles/time having P called for creation of every .o . LIB could be made of MANY .o's of course, and the problem worse.
Is there a way to get target-specific variables to only be evaluated once, i.e evaluated for the target $(LIB) and that VALUE, verbatim, be passed to the prerequisite recipes (.o from .c)? Or am I misunderstanding their usage (I suspect that I am!)
Tried various variable assignment syntaxes, e.g. = and :=, even ::=, all by trial and error. Read the manual over and over.
Is there a way to get target-specific variables to only be evaluated once, i.e evaluated for the target $(LIB) and that VALUE, verbatim, be passed to the prerequisite recipes (.o from .c)? Or am I misunderstanding their usage (I suspect that I am!)
The docs don't seem to specify the exact semantics of when and how target-specific variables are set for building the affected target's prerequisites. Your best bet for the behavior you want was to use simple variable assignment (:= or ::=), but you say that doesn't work. make seems to be behaving as if the variable assignment were included, separately, in the rule for each prerequisite, and that makes sense because in general, there is no guarantee that the prerequisites will all be built one after another or immediately before the main target, and where they aren't all built one right after another, the variable must resume its global value between.
Really, I'd like to encourage you to minimize your use of features specific to GNU make. Every one of them is a convenience, not a necessity, though occasionally, some of them are very convenient indeed. You might consider instead deploying Autoconf +/- Automake to (semi-)dynamically insert flags into your makefile.
But if you must use $(shell) in your makefile, and you want to be certain that the command is executed only once per make run, then your best bet is probably to run it outside any rule. If you don't want to modify the global CPPFLAGS then store the result instead in some other variable:
OBJS = a.o b.o
LIB = libX.a
X_CPPFLAGS := $(shell P)
$(LIB): $(OBJS)
$(AR) cr $# $^
$(LIB): CPPFLAGS = $(X_CPPFLAGS)

How can I add a directory to the search path of GNU Make?

I have a makefile that looks something like this:
include anotherFile.mk
all:
someStuff
The file anotherFile.mk is like this:
include yetAnotherFile.mk
export SOME_VAR = 93
The problem is that anotherFile.mk and yetAnotherFile.mk are in a different directory from my Makefile. So my makefile can't just be changed to this:
include $(OTHER_PROJECT_PATH)/anotherFile.mk
all:
someStuff
The problem with this approach is that the include statement in anotherFile.mk will fail because it will be searching in the current directory.
A partial solution that I found is to pass the --include-dir=$OTHER_PROJECT_PATH flag to the invocation of make, but that's a bit user-unfriendly.
So my question is: Is there something I can put inside my makefile that will add to the directories that make searches for when executing an include? Something like MAKE_INCLUDE_DIRS += $(OTHER_PROJECT_PATH)
Surprisingly there doesn't seem to be a good answer to that question. Forcing .INCLUDE_DIR doesn't help and there doesn't seem to be any way around invoking make with --include-dir=$OTHER_PROJECT_PATH.
It is however possible to put the appropriate recursive make invocation inside the makefile but, in order to get it to work for all reasonable cases it quickly becomes too complicated to be worth it. In summary it requires:
a top level condition to check if the OTHER_PROJECT_PATH is in .INCLUDE_DIR
the appropriate target with the recipe invoking make recursively
possibly additional targets if there are multiple command goals
the real make file enclosed in the else part of the conditional
You Makefile would look like this:
OTHER_PROJECT_PATH := other
ifeq (,$(filter $(OTHER_PROJECT_PATH), $(.INCLUDE_DIRS)))
# this is the mechanism to add the include dir in a recursive make
$(or $(firstword $(MAKECMDGOALS)),all):
$(MAKE) -I$(OTHER_PROJECT_PATH) $(MAKECMDGOALS)
# add empty targets for additional goals if needed
ifneq (,$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)))
$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)):
endif
else
# this is where the real makefile starts
all more:
echo $#: $< $^
include a.mak
endif
It still does not seem possible from a makefile, but if you have a script that sets up environment variables, you can use MAKEFLAGS (e.g. export MAKEFLAGS=I/your/path ordentlich on Linux, or SET on Windows)

ifndef include guard in Gnu Make breaks on nested conditional

I’m trying to implement include guards in Gnu Make. In this Makefile, the first inclusion is OK, while the second one fails with an error.
ifndef INCLUDED
INCLUDED = 1
$(info Including)
define macro
ifneq ($(1),)
define inner_macro
macro content...
endef
else
define inner_macro
endef
endif
endef
endif
The same effect can be simulated by explicitly giving INCLUDED = 1 before the inclusion, e.g. on command line.
Gnu Make 4.1 under Gentoo says Makefile:14: *** missing separator. Stop., while Gnu Make 3.81 under Debian Wheezy says Makefile:14: *** extraneous `endef'. Stop.. On the first inclusion, they both say:
Including
make: *** No targets. Stop.
If I try $(eval $(call macro,whatever)) after the first inclusion, it defines inner_macro as expected.
I used make INCLUDED=1 and make commands respectively to get the described behavior.
The same happens when I clear the environment and disable built-in rules and variables: env -i make -rR INCLUDE=1. When I use -p to dump the database, without INCLUDED=1, the macro is defined as it should be, but with INCLUDED=1, empty inner_macro is defined. This is consistent across both the versions of Make. This hints me that when the condition is false, Make parses the Makefile differently and thinks the else inside macro’s definition belongs to the ifndef. Other condition types behave all the same.
If I remove both the definitions of inner_macro, the problem goes away.
I read the manual pages info make conditional\ syntax and info make multi-line (formerly defining), but I found no caveat there and I still think I am doing nothing wrong.
Am I correct with my conclusions?
Is this a bug in Make, or am I invoking undefined behavior?
How should I implement include guards in Gnu Make?
That's a bug. Report it on Savannah.
There's something wrong with the tracking of nested define/endef inside a not-taken ifdef/ifndef condition. If you don't use nested define/endef then it works; for example (obviously you may not be able to do this in your environment):
ifndef INCLUDED
INCLUDED = 1
$(info Including)
define macro
ifneq ($(1),)
inner_macro = macro content...
else
inner_macro =
endif
endef
endif

Using ifeq and ifndef in GNU Make

I've written a fairly simple test Makefile where I define two targets, all & clean. I've got two different conditional statements. One checks for the existence of the $(MAKECMDGOALS) special variable and the other detects whether any of the command line targets matched those listed in a variable (NODEPS). The problem I'm having is that none of the branches within my conditionals get executed. Ultimately I want to use a conditional to decide whether the target I'm supplying should include some autogenerated dependency files but at the moment I'm struggling to get either expression to even evaluate. I'm running GNU make version 3.81 and I've tried it under Ubuntu and Mac OS X to no avail.
NODEPS := clean
INCLUDE = $(filter $(NODEPS),$(MAKECMDGOALS))
.PHONY : all clean
ifndef $(MAKECMDGOALS)
#echo "$$(MAKECMDGOALS) is not defined"
else
#echo "$(MAKECMDGOALS) is defined"
endif
ifneq (0, $(words $(INCLUDE)))
#echo "INCLUDE = $(INCLUDE) != 0"
else
#echo "INCLUDE = $(INCLUDE) == 0"
endif
all :
#echo "all : $(MAKECMDGOALS)"
clean :
#echo "clean : $(MAKECMDGOALS)"
I eventually managed to work out what was wrong. #eriktous was right, pointing out that I should be using $(info) rather than #echo. More subtly though, part of the problem was that I'd indented the #echos with a tab. It seems that tabs are mandatory for rules but not allowed in conditionals. The other mistake was I'd expanded the $(MAKECMDGOALS) variable in the test condition when it should have been written as just ifndef MAKECMDGOALS.
https://www.gnu.org/software/make/manual/html_node/Make-Control-Functions.html
A makefile is not a shell script. You can not "randomly" place executable statements anywhere you like and expect them to be executed.
There are various ways of communicating with the outside world from within a makefile: $(info ...), $(warning ...), $(error ...) and $(shell #echo ...) (some or all of these may be GNU make extensions).
Ps: you misspelled PHONY.

Passing additional variables from command line to make

Can I pass variables to a GNU Makefile as command line arguments? In other words, I want to pass some arguments which will eventually become variables in the Makefile.
You have several options to set up variables from outside your makefile:
From environment - each environment variable is transformed into a makefile variable with the same name and value.
You may also want to set -e option (aka --environments-override) on, and your environment variables will override assignments made into makefile (unless these assignments themselves use the override directive . However, it's not recommended, and it's much better and flexible to use ?= assignment (the conditional variable assignment operator, it only has an effect if the variable is not yet defined):
FOO?=default_value_if_not_set_in_environment
Note that certain variables are not inherited from environment:
MAKE is gotten from name of the script
SHELL is either set within a makefile, or defaults to /bin/sh (rationale: commands are specified within the makefile, and they're shell-specific).
From command line - make can take variable assignments as part of his command line, mingled with targets:
make target FOO=bar
But then all assignments to FOO variable within the makefile will be ignored unless you use the override directive in assignment. (The effect is the same as with -e option for environment variables).
Exporting from the parent Make - if you call Make from a Makefile, you usually shouldn't explicitly write variable assignments like this:
# Don't do this!
target:
$(MAKE) -C target CC=$(CC) CFLAGS=$(CFLAGS)
Instead, better solution might be to export these variables. Exporting a variable makes it into the environment of every shell invocation, and Make calls from these commands pick these environment variable as specified above.
# Do like this
CFLAGS=-g
export CFLAGS
target:
$(MAKE) -C target
You can also export all variables by using export without arguments.
The simplest way is:
make foo=bar target
Then in your makefile you can refer to $(foo). Note that this won't propagate to sub-makes automatically.
If you are using sub-makes, see this article: Communicating Variables to a Sub-make
Say you have a makefile like this:
action:
echo argument is $(argument)
You would then call it make action argument=something
From the manual:
Variables in make can come from the environment in which make is run. Every environment variable that make sees when it starts up is transformed into a make variable with the same name and value. However, an explicit assignment in the makefile, or with a command argument, overrides the environment.
So you can do (from bash):
FOOBAR=1 make
resulting in a variable FOOBAR in your Makefile.
It seems command args overwrite environment variable.
Makefile:
send:
echo $(MESSAGE1) $(MESSAGE2)
Example run:
$ MESSAGE1=YES MESSAGE2=NG make send MESSAGE2=OK
echo YES OK
YES OK
There's another option not cited here which is included in the GNU Make book by Stallman and McGrath (see http://www.chemie.fu-berlin.de/chemnet/use/info/make/make_7.html). It provides the example:
archive.a: ...
ifneq (,$(findstring t,$(MAKEFLAGS)))
+touch archive.a
+ranlib -t archive.a
else
ranlib archive.a
endif
It involves verifying if a given parameter appears in MAKEFLAGS. For example .. suppose that you're studying about threads in c++11 and you've divided your study across multiple files (class01, ... , classNM) and you want to: compile then all and run individually or compile one at a time and run it if a flag is specified (-r, for instance). So, you could come up with the following Makefile:
CXX=clang++-3.5
CXXFLAGS = -Wall -Werror -std=c++11
LDLIBS = -lpthread
SOURCES = class01 class02 class03
%: %.cxx
$(CXX) $(CXXFLAGS) -o $#.out $^ $(LDLIBS)
ifneq (,$(findstring r, $(MAKEFLAGS)))
./$#.out
endif
all: $(SOURCES)
.PHONY: clean
clean:
find . -name "*.out" -delete
Having that, you'd:
build and run a file w/ make -r class02;
build all w/ make or make all;
build and run all w/ make -r (suppose that all of them contain some certain kind of assert stuff and you just want to test them all)
If you make a file called Makefile and add a variable like this $(unittest)
then you will be able to use this variable inside the Makefile even with wildcards
example :
make unittest=*
I use BOOST_TEST and by giving a wildcard to parameter --run_test=$(unittest)
then I will be able to use regular expression to filter out the test I want my Makefile
to run
export ROOT_DIR=<path/value>
Then use the variable, $(ROOT_DIR) in the Makefile.

Resources