findstring in MAKEFLAGS don't working with ifeq - makefile

I'm trying to use find_j=$(findstring j,$(filter-out --%,$(MAKEFLAGS))) to find if there is -j option, so when I echo $(find_j) the value is j
but when I compare it ifeq (j, $(find_j)) this returnes false
I cant understand where is the problem
my version of make is make-3.99.90
find_j=$(findstring -j,$(filter-out --%,$(MAKEFLAGS)))
ifneq ( , $(find_j))
PARALLEL_ENABLED=true
endif
.PHONY: PRINT
PRINT:
$(info $(PARALLEL_ENABLED))
$(info $(MAKEFLAGS))
$(info $(find_j))
---empty line---
--warn-undefined-variables -ws --jobserver-fds=5,6 -j
-j

One thing to note is that the release of GNU make you're using is a beta release of GNU make 4.0, which itself was released in 2013... so you're using a beta of a release that itself is 8.5 years old.
However, that's not related to this problem.
The issue is that the MAKEFLAGS variable's final value is not set until after all makefiles are parsed. If you try to examine it before all makefiles are parsed, it will contain only a subset of the total set of options.
When you expand that variable as part of an ifeq or ifneq statement, that happens as the makefile is being parsed and so (as per the above) only the simple options (ones that don't take an argument: -j accepts an argument so is not "simple") are available.
When you expand the variable as part of a recipe, that happens after all makefiles are parsed: at that time the final value of MAKEFLAGS is set. So your $(info ...) functions inside the recipe do the right thing.
This is easy to see:
$(info no recipe MAKEFLAGS is '$(MAKEFLAGS)')
all: ; $(info in recipe MAKEFLAGS is '$(MAKEFLAGS)')
If you run with -j10 you'll get:
no recipe MAKEFLAGS is ''
in recipe MAKEFLAGS is '-j10 --jobserver-auth=3,4'
(your "in recipe" flags might look different because you're using such an old version of GNU make).
In the next release of GNU make, the value of MAKEFLAGS is kept up-to-date constantly so you can check it at any time and it will be accurate. But that release is not available yet.

This seems to be imprecisely documented. While MAKEFLAGS has the flags like e.g. -s and -k as ks in it, the -j flag gets processed in another way: it is not stripped of the leading dash - AND it is not visible in the first pass of processing the makefile. Only when rules are executed, MAKEFLAGS receives a value, albeit a processed form of the one you gave. -j3 elicits a -j3 --jobserver-auth=3,4 response from the command line transcriber of make, while -j stays -j. So what does this mean for us? Obviously the feature to detect the requested parallelism at runtime is not stable or there are some good reasons not to access them (which is the case most of the time when you encounter exceptional behaviour in GNU tools), so maybe you can give us more information on what you are trying to achieve - maybe there is a way to circumvent accessing the command line.

Related

Recursive make with nonstandard makefile name on old make version

I have a makefile which calls itself, in order to obtain a license for the compiler before compiling anything, and release the license even when compilation fails, like this:
.PHONY main_target
main_target:
#license_grab &
#sleep 2
-#$(MAKE) real_target
#license_release
This works great if the makefile is named "makefile". But if I make a copy of the makefile to experiment with something, and invoke it with make -f makefile_copy, then the wrong makefile gets used in the recursive call. How do I prevent this without hard-coding the makefile name in the makefile itself?
Edit: Unfortunately I'm stuck using GNU Make version 3.79.1, so I cannot use MAKEFILE_LIST, which was apparently introduced in version 3.80. Therefore none of the answers in this question will work for me.
You can use the MAKEFILE_LIST variable:
THIS_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
.PHONY main_target
main_target:
#license_grab &
#sleep 2
-#$(MAKE) -f $(THIS_MAKEFILE) real_target
#license_release
You can set the MAKE variable outside the makefile, to include the makefile name (unless of course, it gets overridden). Something like this (for bash):
MAKE="make -f makefile_copy" make -e -f makefile_copy
or this (in pretty much any shell):
make MAKE="make -f makefile_copy" -f makefile_copy

Can I tell if --jobs is used inside a Makefile?

I want to set some variables based on whether or not parallel builds are enabled, so I tried this:
jobs:
»·echo "executing jobs job"
ifneq (,$(findstring -j,$(MAKEFLAGS)))
»·$(warning "parallel!")
else
»·$(warning "not parallel!")
endif
And this is what happens:
$ make -j2
Makefile:2: "not parallel!"
echo "executing jobs job"
executing jobs job
I also tried testing $(JOBS), but no luck.
Is there a way for me to tell inside a Makefile that the --jobs parameter was used?
Additional info:
$ 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 x86_64-pc-linux-gnu
Surprisingly, ${MAKEFLAGS} will only gain the -j when it is expanded at recipe expansion time.
Makefile:
$(warning [${MAKEFLAGS}])
.PHONY: all
all:
$(warning [${MAKEFLAGS}])
echo Now do something useful
Run:
$ make -j5
1:1: []
1:5: [ -j --jobserver-fds=3,4]
echo Now do something useful
Now do something useful
About the MAKEFLAGS expansion in #bobbogo's answer: If we look at the code I think I can explain the behavior:
Looking at the code, main function of make calls the define_makeflags function several times.
/* Define the MAKEFLAGS and MFLAGS variables to reflect the settings of the
command switches. Include options with args if ALL is nonzero.
Don't include options with the 'no_makefile' flag set if MAKEFILE. */
static struct variable *
define_makeflags (int all, int makefile)
{
......
Call locations in main:
1)
/* Set up the MAKEFLAGS and MFLAGS variables for makefiles to see.
Initialize it to be exported but allow the makefile to reset it. */
define_makeflags (0, 0)->export = v_export;
2)
/* Set up MAKEFLAGS and MFLAGS again, so they will be right. */
define_makeflags (1, 0);
3)
/* Set up 'MAKEFLAGS' specially while remaking makefiles. */
define_makeflags (1, 1);
There are other calls in sub-functions but this should be enough to explain.
The first call sets all parameter to false. The others set to true. With all set to false, the define_makeflags function only parses "simple flags" and j is not one of them. In order to understand the parsing one needs to look into this switch statement and the definition of command line params.
My SWAG is like the following:
I presume the parsing of ifneq statements happen after the first call to define_makeflags but before the subsequent calls. I can guess the reason of keeping the MAKEFLAGS simple at the start is to continue to support documented Makefile patterns like the following.
From doc1, doc2:
archive.a: ...
ifneq (,$(findstring t,$(MAKEFLAGS)))
+touch archive.a
+ranlib -t archive.a
else
ranlib archive.a
endif
If MAKEFLAGS contained long options or options that take parameters, then searching for single char flags in MAKEFLAGS would not be possible.
There is some guesstimate in my answer. Maybe someone who was involved in the design decision can also weigh in. Given this change Paul Smith may have an idea.

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)

conditional check against list of strings in GNU makefile command

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

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