Makefile: What does 'make $* clean' mean? - makefile

I know what make clean does.
But what does make $* clean do?
I'm not able to find a clear explanation anywhere.

As Ross says, we can't help because you haven't provided enough context. You need to provide at least the rule in which the make $* clean appears.
However, I'll guess it looks something like this:
%.xyz:
make $* clean
Here, $* is an automatic variable which will expand to the stem of the target (the text matching the % in the pattern). So, if you invoke make foobar.xyz, this rule would invoke make foobar clean: it would run a sub-make, build the foobar target, then build the clean target.
I've not seen anything quite like the above, although I can think of reasons for doing it. Far more common would be if you mistyped the command and it really said make -C $* clean, giving a rule like this:
%.xyz:
make -C $* clean
(note you should never use the static string make when invoking a sub-make; you should always use $(MAKE) or ${MAKE}). In this example running make foobar.xyz would run make -C foobar clean, which means change to the directory foobar and run the clean target there.

If it is invoked from a shell script, then $* is expanded to the arguments passed to the shell script.
#!/usr/bin/env bash
# filename clean.sh
make $* clean
The above shell script can be invoked like,
#./clean.sh --silent
Which will eventually pass the --silent to the make application and execute the following command.
#make --silent clean
Finally, $* in a shell-script is expanded by the shell, not by the make application.

Related

How to unhide commands in Makefile?

I want to troubleshoot Makefile. A lot of commands are hidden using the # prefix. e.g.
all:
#echo "building..."
how can I tell make to show all the commands? I tried the -d option and it does not show hidden commands.
Maybe try running make with V=1 or VERBOSE=1
I'm not exactly sure this is what you are looking for, but here is a related technique:
Prefixing commands with # in a makefile recipe can be convenient, so output does not get too much cluttered, but, as you may have discovered, in some situations it can be useful to actually see what command is executed.
One solution is to avoid using the # character in a makefile but use instead a variable. Say, a one-letter one. Say L, for "log output" (but its your choice).
Then, prefix all your commands in your makefile with that variable:
target: prerequ
$(L)do_this $< $#
$(L)do_that $< $#
An simply define the variable, using some command-line switch:
ifeq "$(LOG)" ""
LOG=no
endif
ifeq "$(LOG)" "Y"
L=#
endif
Then, you can launch make from the shell like this $make target (no logging) or like this:
$ make target LOG=Y
And all the executed commands will show up!

Export environment variables from child bash script in GNU Make

I have a script "set_env.py" that outputs the following uppon execution:
export MY_VAR_1=some_value
export MY_VAR_2=some_other_value
I cannot change this script, it is supplied in my current environment.
Further I have a Makefile that looks like this:
SHELL := /bin/bash
all: set_env
env | grep MY_VAR
set_env:
eval ./set_env.py
With this makefile I expected the grep to list my two variables, however it seems the environment is not set.
I suspect this is because make creates a sub-environment for each line and so the variables set on the first line will not be available in the second.
So the question is, how would I go about exporting the environment from the untouchable script in my makefile?
Actually, the output of the python is valid make.
One option then is to read the output of the python directly into the makefile.
The only fly in the ointment is that $(shell) doesn't cut the mustard.
include Environment.mk
PHONY: test
test:
env | grep MY_VAR
Environment.mk:
./set_env.py >$#-tmp
mv $#-tmp $#
How does this work?
The first thing that make tries to do is to ensure the makefile itself is up-to-date.
Since we have told it to include Environment.mk,
make must ensure that is up-to-date too.
Make finds a rule for Environment.mk
The python is run, creating Environment.mk
Environment.mk is read in, creating two make variables with the export attribute
The makefile is now up-to-date, so make proceeds on to the target (test in this case)
Make runs test's recipe, exporting any variables with the export attribute.
No recursion, but you should ensure the python always spits out make compatible syntax.
EDIT
As #raspy points out, this is not the whole story.
As it stands,
once Environment.mk has been created,
it will never be regenerated.
If set_env.py ever generates different output,
you should tell make what conditions these are by adding dependencies.
If set_env.py takes a trivial time to run,
I advise a simple .PHONY.
That way it will run every time you run make,
and Environment.mk will never be stale.
.PHONY: Environment.mk
Recursive make is your friend I'm afraid.
.PHONY: all
all:
eval $$(./set_env.py) && ${MAKE} test
.PHONY: test
test:
env | grep MY_VAR
There are a few moving parts here.
make all executes the shell command eval $(./set_env.py) && make test
The shell first does the command substitution
$(./set_env.py) is replaced by the export commands
The export commands are passed to the (shell) eval command
The environment variables are defined, but only for this shell invocation
The eval succeeds, so the execution passes to the command after the &&
Make runs recursively, but this second make has an augmented environment

How to call top-level make with same arguments?

I want always call top-level makefile with same command line. I tried:
.PHONY: %
%:
$(MAKE) -C ${CURDIR}/.. ${CURDIR}/$*
but its does not work :(
I'm not sure what you're trying to do. Are you saying that for any invocation of make in this directory you want to run make in a different directory, with the same arguments?
You can't quite do this, but this will get you close:
.DEFAULT:
$(MAKE) -C ${CURDIR}/.. $#
I don't know why you were prefixing the $* with $(CURDIR) in the target name...?
The big difference between this and the original invocation is that it will run a separate make process for each target; that is if you run make foo bar it will invoke the "top-level makefile" twice: once with a target of foo and then again with a target of bar. There's no way to avoid this, that I can think of.

Makefile that passes its command-line parameters to another script

In a project, I have a script called make.sh that builds the project and does some other stuff too. It is working well so far.
Then, just out of curiosity I've tried to create a Makefile that just passed its command-line parameters to this script so I could call it
make snapshot
instead of
./make.sh snapshot
this is the Makefile that I'm using right now
.PHONY: snapshot
%:
./make.sh $#
snapshot:
./make.sh snapshot
But this approach have some problems, I can't pass "build" as a parameter, because I have a "Build" directory (used by SCons), and I can't pass a second parameter to be passed to the script, like:
make upload 192.168.1.10
as make interprets it as two different targets...
Is there a way I can do this with the Makefile?
Yes, you can do it:
%:all
#true
all:
./make.sh $(MAKECMDGOALS)
but this is an abuse of Make. The idea is that Make should interpret its arguments as a set of targets, not an ordered list of general arguments. You should probably use a different tool.

Is there a configuration file for gnu make?

I want to tell make that it shall always use -j4 option even if I didn't specify it vie command line. Normally i would do this in some configuration file (i.e. ~/.makerc).
Does such file exist for gnu make?
Have a read about the $(MAKEFLAGS) variable:
export MAKEFLAGS=j4
However this will likely interfere with recursive-make-based builds (not that sensible people are using recursive make anyway!), by interfering with GNU make's ability to communicate with its sub-makes.
So the more sensible approach is probably a wrapper script or an alias or shell function.
Well, yes and no --- normally you would use an include file. Put your common configuration items together in a file, say common.mk and add
include common.mk
at the top of your makefile. If the flag doesn't have a matching way to configure it from inside the make file, you can use a function
function mk {
make -j4 $*
}
It doesn't exist, but you can do this by having a recursive call into make.
For example:
Makefile:
-include $(HOME)/.makerc
.DEFAULT_GOAL: all
# This will handle a default goal if make is just called without any target
all:
$(MAKE) $(MAKE_OPTIONS) -f Makefile.real $(MAKECMDGOALS)
# This handles all targets and passes it through
%:
$(MAKE) $(MAKE_OPTIONS) -f Makefile.real $(MAKECMDGOALS)
$(HOME)/.makerc:
MAKE_OPTIONS := -j4
I would like to expand a bit on the solution hinted in John Marshall's answer.
You can simply put a one-line wrapper script somewhere earlier in the $PATH with the following contents:
#!/bin/bash
$(type -ap make | sed -n 2p) -j4 "$#"
(The script doesn't have to be named make, and that would make it simpler, but I find it convenient if it is.)
I would argue that this is better than the other approaches for the following reasons:
Unlike MAKEFLAGS approach, it does not break recursive builds (which are actually quite common in my experience).
Unlike include .makerc approach, it can be applied locally without changing any existing makefiles or your workflow in any way.
Unlike shell alias or function approach, it is shell-agnostic (doesn't tie you to any particular shell) and works in any additional build scripts that you might have to use, too, as long as you launch them in the same environment.
I like the MAKEFLAGS approach suggested by John Marshall in lieu of make supporting something like an automatic .makerc project config file. However, I didn't want to have to remember to source a .env or similar environment variables beforehand (and unsetting them afterward).
A solution to this is to put the MAKEFLAGS assignment at the top of the Makefile itself:
#!/usr/bin/env make
MAKEFLAGS=s
.PHONY: foo
foo:
echo "hello, make"
Run it:
$ make foo
hello, make
Compared to running without the MAKEFLAGS=... line:
$ make foo
echo "hello, make"
hello, make

Resources