Gnu make: Add a flag in a command for certain targets - makefile

I have the following make script (excerpt):
debug/abc-%.txt:
python buildtxt.py --firstFlag <second flag>
where <second flag> should be --debug if % is debug and empty (i.e. no flag) otherwise. This behaviour could simply be implemented in buildtxt.py but I think doing it this way is more modular.
How can I easily implement this?

You want $(if $(filter debug,$*),--debug).
$* expands to the string that matched %. There's no built-in function to compare strings for equality, so we use filter for that.

Related

How do you use (GNU) make's "simply expanded" variables in build rules and not get the last definition?

I have a complicated set of rules I need to write to generate a rather large number of "parameterised" output files and thought that, rather than expand them all out by hand, I could repeatedly "include" a template file with sets of rules and use (GNU)make's facility for allowing "simply expanded" variables to avoid the pain.
(In the past I've always been using the "recursively expanded" variable approach, so this is new to me)
As a trivial example of what I thought would work, I tried putting the following in a Makefile
Targ:=A
Param1:=Pa
Param2:=Qa
$(Targ):
#echo expect A, get $(Targ), Target is $#. Params are $(Param1) and $(Param2)
Targ:=B
Param1:=Pb
Param2:=Qb
$(Targ):
#echo expect B, get $(Targ), Target is $#. Params are $(Param1) and $(Param2)
Targ:=C
Param1:=Pc
Param2:=Qc
$(Targ):
#echo expect C, get $(Targ), Target is $#. Params are $(Param1) and $(Param2)
The eventual plan was to replace the rules with an include file containing dozens of different rules, each referencing the various "parameter" variables.
However, what I get is...
prompt> make A
expect A, get C, Target is A. Params are Pc and Qc
prompt> make B
expect B, get C, Target is B. Params are Pc and Qc
Essentially, unlike each rule's target, which is picking up the intended definition, the $(Targ), $(Param1), and $(Param2) in each rule's command is instead being run with the final definition.
Does anyone know how to prevent this, i.e. how do you force the command to use the definition at the time it is encountered in the Makefile?
Simple vs recursive expansion makes no difference here; regardless of which you use you'll see the same behavior. A GNU make variable is global and obviously can have only one value.
You have to understand when variables are expanded. The documentation provides a detailed description of this. Targets and prerequisites are expanded when the makefile is read in, so the value of Targ as the makefile is being parsed is used.
Recipes are expanded when the recipe is to be invoked, which is not until after all makefiles are parsed and make starts to build targets. At that time of course the variable Targ has its last set value.
Without knowing what your makefile really does it's hard to suggest an alternative. One option is to use target-specific variables:
Targ := A
$(Targ): LocalTarg := $(Targ)
$(Targ):
#echo expect A, get $(LocalTarg), Target is $#
Another option is to use constructed variable names:
Targ := A
Targ_$(Targ) := $(Targ)
$(Targ):
#echo expect A, get $(Targ_$#), Target is $#
Apologies for answering my own question, but I now realised it is possible to solve the issue I was having by running make recursively.
E.g. if the parameter variables for the rules are Targ, Param1 and Param2 then
#Set up "default" values for the parameters (As #madscientist points out,
#these will safely be overridden by the defs on the #(make) commands below
Targ=XXXXXXXXXX
Param=XXXXXXXXXX
Param2=XXXXXXXXXX
Recursing=
#
# define N (==3) templated rule(s)
#
$(Targ)%a:
#echo Run Combo_a $(Targ) $(Param1) $(Param2) $#
$(Targ)%b:
#echo Run Combo_b $(Targ) $(Param2) $(Param1) reversed $#
$(Targ)%c:
#echo Run Combo_c $(Param1) $(Targ) $(Param2) mixed again $#
#
#Enumerate "M" (==2) sets of parameters,
# (Except if we are already recursing because unrecognised targets may cause
# it to descend forever)
#
ifneq ($(Recursing), Yes)
Set1%:
#$(MAKE) Targ=Set1 Param1=foo Param2=bar Recursing=Yes $#
Set2%:
#$(MAKE) Targ=Set2 Param1=ray Param2=tracing Recursing=Yes $#
endif
This then allows N*M different combos for N+M typing cost.
eg. (removing messages from make re recursion)
>make Set1.a
Run Combo_a Set1 foo bar Set1.a
>make Set2.c
Run Combo_c ray Set2 tracing mixed again Set2.c

Makefile passing additional arguments to targets command from make command

I have a Makefile that defines docker-compose project.
It essentially assembles me a command:
COMMAND := docker-compose --project-name=$(PREFIX) --file=$(FILE_PATH)
up:
$(COMMAND) up -d
I would like to add a target named dc to which I would be able to pass any arguments I want.
I know there is one solution:
target:
$(COMMAND) $(ARGS)
And then call it with make target ARGS="--help" for example.
But isn't there an easier way like in bash $# ? I would like to skip the ARGS=... part and send everything to the command after target name.
Not really. The make program interprets all arguments (that don't contain =) as target names to be built and there's no way you can override that. So even though you can obtain the list of arguments given on the command line (via the GNU make-specific $(MAKECMDGOALS) variable) you can't prevent those arguments from being considered targets.
You could do something like this, which is incredibly hacky:
KNOWN_TARGETS = target
ARGS := $(filter-out $(KNOWN_TARGETS),$(MAKECMDGOALS))
.DEFAULT: ;: do nothing
.SUFFIXES:
target:
$(COMMAND) $(ARGS)
(untested). The problem here is you have to keep KNOWN_TARGETS up to date with all the "real" targets so you can remove them from the list of targets given on the command line. Then add the .DEFAULT target which will be run for any target make doesn't know how to build, which does nothing. Reset the .SUFFIXES meta-target to remove built-in rules.
I suspect this still will have weird edge-cases where it doesn't work.
Also note you can't just add options like --help to the make command line, because make will interpret them itself. You'll have to prefix them with -- to force make to ignore them:
make target -- --help
Another option would be to add a target like this:
target%:
$(COMMAND) $*
Then you can run this:
make "target --help"
But you have to include the quotes.
In general I just recommend you reconsider what you want to do.
You could write a bash wrapper script to do what you'd like:
#/bin/bash
make target ARGS=\"$#\"
The reason you don't want to do it in make, is that make parses the command line parameters before it parse the makefile itself, so by the time you read the makefile, the targets, variables, etc have already been set. This means that make will have already interpreted the extra parameters as new targets, variables etc.
A target that re-run make containerized
.PHONY: all containerized
ifeq ($(filter containerized,$(MAKECMDGOALS)),containerized)
.NOTPARALLEL: containerized
MAKEOVERRIDES ?=
containerized: ## Build inside a container
#docker run image_with_make make $(MAKEOVERRIDES) $(filter-out containerized,$(MAKECMDGOALS))
else
# other targets here
all: xxxx
endif
Executing
make containerized all runs make all in container
The first answer is correct, no passthru of args. However, here is a plausible path for experimentation, use of branch by include selection:
# Makefile:
COMMAND := $(PYTHON) this_shit_got_real.py
LOCAL_MK ?= local.mk
# '-' important, absence of LOCAL_MK is not cause for error, just run with no overrides
- include $(LOCAL_MK)
target:
$(COMMAND) $(ARGS)
Now see how you add branching with env:
echo "ARGS=--help">>local.mk
# make target
And the other cli controlled branch
echo "ARGS=--doit">>runner.mk
# LOCAL_MK=runner.mk make target

Suppressing First Part of Output in Makefile

DEPRECATED_CHECK := $(shell grep "test454" tex/*.tex)
ifneq ($(DEPRECATED_CHECK), )
$(warning \test454 is deprecated. Use \test2 instead)
endif
When I run this I get:
../common/Makefile.include:133: \test454 is deprecated. Use \test2 instead
That's fine, but I'd quite like to have only:
\test454 is deprecated. Use \test2 instead
Is this possible? Some sort of awk function? I think I need something with:
#echo \text454 is deprecated ...
But I don't know how to get this working with the basic purpose of my MWE, as it keeps complaining about missing separators.
Many thanks
You could use $(info ...) instead of $(warning ...). info doesn't prepend the file and line number.
just an aside -- I usually try to do those sort of checks as part of a sanity rule, and make everything depend on that rule instead of doing it at the top level. It gives you more flexibility that way. For example, if you didn't want to run the check when building clean, it becomes simple, or if you wanted to fail the build if a check failed, it becomes simple as well.
EDIT (adding more detail on aside)
Instead of doing an ifneq at the top level of make, you could add a target as so:
sanity_check:
# ! grep -q "test454" tex/*.txt || echo "test454 is depricated"
.PHONY: sanity check
The add dependencies of your main targets to sanity check:
all maintarg1 maintarg2: sanity_check
This way the sanity check will be run before any of your main targets, and will output as desired. This is in my opinion, a cleaner way of doing the test. This way the test is only run if you are building any of your targets, and will not be run, if for example you are making clean, or if your makefile was included by a parent makefile, or in a bunch of other corner cases that might pop up in the future.
Just a quick note on the recipe syntax: the # is a make directive that tells make not to echo the command as it's run. The ! is bash syntax to inverse the return of grep (so ! grep returns false if the text is found, thereby causing the || part of the statement to be evaluated.). The .PHONY: sanity_check tells make to run the rule, even if a file called sanity_check already exists

How to define a variable which is every file except this one

I am using gnu make.
I have a rule like this:
SOURCES=*.in
output:$(SOURCES)
$(PROCESSOR) -i $(SOURCES)
This works well. Now I need to exclude one file, test.in, from SOURCES, how should I update the rule above?
[UPDATE]
Actually the problem changes a bit, I need to read an environment variable, based on its value, I either take all files with ".in" as postfix or exclude test.in.
Could you show me how to do this?
Using some of the built-in functions:
SOURCES := $(filter-out $(if $(filter val,$(env_var)),test.in),$(wildcard *.in))
If you only want this to apply to that single rule:
output: $(filter-out $(if $(filter val,$(env_var)),test.in),$(SOURCES))
where env_var is the environment value to check, and val the value. If you want a partial match then replace filter with findstr. You can split the definition into multiple statements and / or ifeq if the one-liner is too cryptic.

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

Resources