Makefile that passes its command-line parameters to another script - makefile

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.

Related

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

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.

Calling make from within a makefile

I have a Makefile which works perfectly called from a new shell, i.e.:
make -C /dir/
However, if I call this Makefile from another Makefile, it fails due to some complicated dependency issues. Make clearly has knowledge of the nested calls, evident by the print of make[1]: etc, and I suspect make is somehow sharing variables with its child process.
Is there anyway to call a clean make from within a Makefile? If my build works from a clean shell, it should be possible to call it from another Makefile without addressing the horrors inside the script! :)
Thanks!
make indeed shares some of its environment when it is recursively called. As suggested in https://www.gnu.org/software/make/manual/html_node/Options_002fRecursion.html#Options_002fRecursion, you might want to write your recursive call that way:
sub-make:
$(MAKE) -C /dir/ MAKEFLAGS=
and see if it helps. You can also control the variables that are exported to the sub-make by using export and unexport directives (https://www.gnu.org/software/make/manual/html_node/Variables_002fRecursion.html#Variables_002fRecursion)
It was a few environment variables in the caller make that broke the callee make (CFLAGS etc...)
My solution was to diff the environment at a clean shell and from the point of call. I then manually added the problem variables to a list and created some save_env/restore_env scripts.
Thanks!

Strange behaviour after recursively calling another makefile

I have a main makefile in the root directory of my project. There is another makefile inside the include directory. The second makefile uses the include keyword to call some other makefiles in other projects which I have no control over it. I cannot directly include this makefile as it has some targets which have the same name as the ones that I have. As a workaround, I decided to use recursive calling. When I run this makefile through the shell, using the following command, it works nicely:
my_project$ make -C include -f Second_Makefile
But when I call it through the main Makefile as follows, it does not behave normally meaning that it reports some project specific errors rooted from the included files inside the second makefile which are very hard to locate.
all:
#$(MAKE) -C include -f Second_Makefile
I also tried the following line, but it did not help:
cd include; #$(MAKE) -f Second_Makefile
I think there should be difference between recursive calling and direct calling but I don't know what it is.

Measure (profile) time spent in each target of a Makefile

Is there a way to echo the (system, user, real) time spent in each target of a Makefile recursively when I do make all?
I'd like to benchmark the compilation of a project in a more granular way than just time make all. Ideally, it would echo a tree of the executed target, each one with the time spent in all its dependencies. It'd be great also if it could work with -j (parallel make). And by the way my Makefile is non-recursive (doesn't spawn another make instance for each main targets).
Thanks!
Gnu Make uses the $(SHELL) variable to execute commands in the targets.
By default it is set to /bin/sh.
You can set this variable to a script that will execute the command given with the "time" command. Something like this:
In your makefile specify the SHELL variable, somewhere at the top:
SHELL = ./report_time.sh
and in the file ./report_time.sh:
#!/bin/sh
shift # get rid of the '-c' supplied by make.
time sh -c "$*"
The replace the 'sh' command with the original SHELL specified in the Makefile if any.
This will report the timings.
However This will not tell you what target the report_time.sh script is running. One solution for this is to prepend the target name ($#) in each target entry in the makefile so that it will be passed to the report_time.sh script as well.
remake --profile is a drop-in replacement for make. It generates a target call tree in a callgrind format.

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