Interactive input of a Makefile variable - makefile

I want to run makefile with input variable. What I want is that if I write down the project name, a folder with that name will be created.
So I write read command:
CC = gcc
CFLAGS = -W -Wall
FILE := hi
src = $(wildcard *.c)
OBJ = $(src:.c=.o)
all : $(FILE)
$(FILE) : $(OBJ)
$(CC) $(CFLAGS) -o $# $^
.PHONY: clean
clean :
rm *.o $(FILE)
move :
mkdir -p ../../bin/$(FILE);
mkdir -p ../../exe/$(FILE);
mv *.o ../../bin/$(FILE);
mv $(FILE) ../../exe/$(FILE)
afterclean :
rm ../../bin/$(FILE)/*.o;
rm ../../exe/$(FILE)/$(FILE)
execute :
./../../exe/$(FILE)/$(FILE)
read :
#read -p "Enter : " enter; \
$(FILE) := enter; \
echo $FILE
What I wanna do is if I get FILE name through read I want to change FILE variable, but I can't change it. How can I do that?

Well in short, you cannot easily do that (and you should likely not want to, scroll down for rationale). If you have a closer look at your Makefile you'd notice that you're mixing make and shell syntax... and their contexts.
In your case, it literally passes the following string to shell (value of SHELL, likely defaults to /bin/sh) with -c:
read -p "Enter : " enter; \
hi := enter; \
echo ILE
Which shows the effects of the intermixed syntax. $(FILE) (value hi) and $F (unset -> empty) are make variables substituted by make before invoking shell. (while read into enter variable is not used at all and instead literal string enter is used in attempted make variable assignment inside that running shell.)
If you wanted to run a shell command and assign a value from what it has done / learned to a make variable, you would have to do so using shell function (or generate a (temporary) file you would include, but that's even messier):
FILE := $(shell read -p "Enter: " enter ; echo $${enter})
That however always asks... unless you use conditional assignment (?=) in which case you could choose already from the command line (make FILE=something, at which point we're about to close the circle). I am generally unsure what your intentions were how to tell make when to ask and when to use default value of hi.
That leads me to why this notion sounds suspect to me to start with and why suggestion made by #HolyBlackCat is a superior way of customizing invocation of make.
Also any runtime user interactions generally break automation (which is what we have make for) and also make builds non-reproducible. So, they better are to be avoided.
In other words, if you really had to, I'd say write an interactive_make_call.sh around it for this type of invocation:
#!/bin/bash
read -p "Enter : " enter
make FILE="${enter}" "$#"
Or even:
#!/bin/bash
read -p "Enter : " enter
if [[ -n "${enter}" ]] ; then
make FILE="${enter}" "$#"
else
make "$#"
fi
To fallback on the default value of FILE from the Makefile if you just press enter.

Related

run before and after each target in a makefile

I would like to run a command before and after each target in a makefile.
so something like this
pre:
#echo pre
#echo running | mailx -s "Start {target}" user#foo.com
post:
#echo post
#echo post | mailx -s "Finish {target}" user#foo.com
j:
long_running_command && echo $# > $#
k: j
long_running_command2 && echo $# > $#
I would like to run pre and post for j and k. Ideally, I would like to get an email for each task that starts and stops.
One way to do it is to modify all the recipes in your makefile to invoke some command. You can put it into a variable so it doesn't look too gross:
START = mailto --subject 'Started target $#' me#my.host
END = mailto --subject 'Finished target $#' me#my.host
j:
$(START)
long_running_command && echo $# > $#
$(END)
k: j
$(START)
long_running_command2 && echo $# > $#
$(END)
The nice thing about this is you can pick and choose which targets you want it for; maybe some of them don't need it. The disadvantage is if the command fails you won't get any "end" email at all.
If you really want to do it for every single target, then you can write a shell script that mimics the shell's behavior but also sends mail, while running the command.
$ cat mailshell
#!/bin/sh
# get rid of the -c flag
shift
mailto --subject "started $*" me#my.host
/bin/sh -c "$#"
r=$?
mailto --subject "ended $* with exit code $r" me#my.host
exit $r
(note this is totally untested but you get the idea I hope). Then in your makefile, set SHELL to that shell:
SHELL := mailshell
j:
long_running_command && echo $# > $#
k: j
long_running_command2 && echo $# > $#
I guess you could still pick and choose by setting SHELL as a target-specific variable only for those targets you wanted to use the shell.
One downside of this is that if you have recipes that have multiple lines you'll get an email for each line individually. You can work around this by enabling .ONESHELL: to force the entire recipe to be passed to a single shell. However, I believe that this may require your mailshell tool to be more sophisticated.
If you only have one command per recipe, you can do this by changing the configuration for the shell the commands are run in.
Have the config file run the pre commands directly and trap EXIT to run the after commands in.
For example:
$ cat Makefile
SHELL := BASH_ENV=/dev/fd/3 3<<<'echo before; trap "echo after" EXIT' /bin/bash
default:
echo default
other:
echo first
echo second
$ make default
echo default
before
default
after
However this may not be what you want if a recipe runs several commands, as the before and after code will run each time.
$ make other
echo first
before
first
after
echo second
before
second
after
And I don't know of a way (outside of recursive Makefiles) to have different recipes use different shells, so this won't work if you only want to set before/after for several recipes.

make with directories as pattern drops intermediate files?

I have the following Makefile:
cells.csv:
echo cellA > cells.csv
echo cellB >> cells.csv
echo cellC >> cells.csv
echo cellD >> cells.csv
mkdir -p cellA
mkdir -p cellB
mkdir -p cellC
mkdir -p cellD
%/cell_gen: cells.csv
echo '$# generated' > $#
%/cell_gds: %/cell_gen
cat $(#D)/cell_gen > $#
echo $#_GDS >> $#
The idea is to generate 'cells' in two step (called [cell]_gen and [cell]_gds) while the cells list is
not known at the beginning of make.
Here: the target 'cells.csv' is human readable (just echo) , but in
the general case, I expect something complexe, itselft resulting of previous steps ...etc..: not readable.
Each step of 'cell' should be stored in the directory named [cell] .
I don't understand why in this case, if I ask for "make cellA/cell_gds" then it looks like
the steps are all executed: I get the csv file and I get cellA/cell_gds.
...but I can't explain why I don't get cellA/cell_gen ??
... Despite I can see "echo 'cellA/cell_gen generated' > cellA/cell_gen" during make execution , and i really get "cellA/cell_gen generated" instide the cellA/cell_gds
Does anybody knows why there is no file cellA/cell_gen ??
thanks !
The file cellA/cell_gen is an intermediate file; you didn't explicitly ask for it, Make deduced that it was necessary as part of a chain of pattern rules. So by default, Make will delete it once the "real" target, cellA/cell_gds, is complete.
To prevent this, just add the line
.PRECIOUS: %/cell_gen

Getting directory portion of a list of source files in a for loop

I have the following gnu make script:
for hdrfile in $(_PUBLIC_HEADERS) ; do \
echo $(dir $$hdrfile) ; \
done
The _PUBLIC_HEADERS variable has a list of relative paths, like so:
./subdir/myheader1.h
./subdir/myheader2.h
The output I get from the for loop above is:
./
./
I expect to see:
./subdir/
./subdir/
What am I doing wrong? Note that if I change the code to:
echo $(dir ./subdir/myheader1.h)
it works in this case. I think maybe it has something to do with the for loop but I'm not sure.
You are confusing make variables (or functions) with shell variables when executing the for-loop. Note that $(dir ...) is a make construct that gets expanded by make before the command is executed by the shell. However, you want the shell to execute that command inside the loop.
What you could do is replace $(dir) with the corresponding command dirname which gets executed by the shell. So it becomes:
for hdrfile in $(_PUBLIC_HEADERS) ; do \
dirname $$hdrfile ; \
done
This should give the desired result.

Define make variable at rule execution time

In my GNUmakefile, I would like to have a rule that uses a temporary directory. For example:
out.tar: TMP := $(shell mktemp -d)
echo hi $(TMP)/hi.txt
tar -C $(TMP) cf $# .
rm -rf $(TMP)
As written, the above rule creates the temporary directory at the time that the rule is parsed. This means that, even I don't make out.tar all the time, many temporary directories get created. I would like to avoid my /tmp being littered with unused temporary directories.
Is there a way to cause the variable to only be defined when the rule is fired, as opposed to whenever it is defined?
My main thought is to dump the mktemp and tar into a shell script but that seems somewhat unsightly.
In your example, the TMP variable is set (and the temporary directory created) whenever the rules for out.tar are evaluated. In order to create the directory only when out.tar is actually fired, you need to move the directory creation down into the steps:
out.tar :
$(eval TMP := $(shell mktemp -d))
#echo hi $(TMP)/hi.txt
tar -C $(TMP) cf $# .
rm -rf $(TMP)
The eval function evaluates a string as if it had been typed into the makefile manually. In this case, it sets the TMP variable to the result of the shell function call.
edit (in response to comments):
To create a unique variable, you could do the following:
out.tar :
$(eval $#_TMP := $(shell mktemp -d))
#echo hi $($#_TMP)/hi.txt
tar -C $($#_TMP) cf $# .
rm -rf $($#_TMP)
This would prepend the name of the target (out.tar, in this case) to the variable, producing a variable with the name out.tar_TMP. Hopefully, that is enough to prevent conflicts.
A relatively easy way of doing this is to write the entire sequence as a shell script.
out.tar:
set -e ;\
TMP=$$(mktemp -d) ;\
echo hi $$TMP/hi.txt ;\
tar -C $$TMP cf $# . ;\
rm -rf $$TMP ;\
I have consolidated some related tips here: Multi-line bash commands in makefile
Another possibility is to use separate lines to set up Make variables when a rule fires.
For example, here is a makefile with two rules. If a rule fires, it creates a temp dir and sets TMP to the temp dir name.
PHONY = ruleA ruleB display
all: ruleA
ruleA: TMP = $(shell mktemp -d testruleA_XXXX)
ruleA: display
ruleB: TMP = $(shell mktemp -d testruleB_XXXX)
ruleB: display
display:
echo ${TMP}
Running the code produces the expected result:
$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile testruleB_Y4Ow
I dislike "Don't" answers, but... don't.
make's variables are global and are supposed to be evaluated during makefile's "parsing" stage, not during execution stage.
In this case, as long as the variable local to a single target, follow #nobar's answer and make it a shell variable.
Target-specific variables, too, are considered harmful by other make implementations: kati, Mozilla pymake. Because of them, a target can be built differently depending on if it's built standalone, or as a dependency of a parent target with a target-specific variable. And you won't know which way it was, because you don't know what is already built.

Getting the name of the makefile from the makefile

How to get the name of the makefile in the makefile?
Thanks.
Note:
I would need that because I would like my makefile to call itself, but the makefile is not called Makefile, so I'd like to write something like this:
target:
($MAKE) -f ($MAKEFILENAME) other_target
location = $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
WHERE_ART_THOU := $(location)
$(warning $(WHERE_ART_THOU))
I also believe this is GNU make-specific, but I'm not too sure.
(Should you have any questions, refer to amazingly written GNU make manual. But remember, that, just like Makefile, this manual should be read completely before putting the concepts into practice).
I couldn't figure out how it is done easily. As far as I understand, you'll have to do some manual job.
Later I will describe how it could be done and show scripts that introduce current_makefile variable. But I would like to stress an important concept at the first place.
You should understand that if we had some kind of variable current_makefile, that expands to the current makefile name, then it will have to change during the process of reading makefiles. That means that it should be used withinin "immediate" expansion context -- i.e. within commands that are executed during reading the makefile. Most commands, however, are executed after makefiles are read. Therefore, some commands will print the correct value smoothly, while in certain places, where "deferred" expansion is used, it will always expand to the root makefile name.
If you would want to use this variable within rule text, for example, you'll have to do tricks, because rule text always has deferred expansion. So, if your have the rule
rule:
echo In makefile $(current_makefile):
echo Making target $#
it will always print the name of the root makefile. Instead, to force immediate expansion, you will have to create another variable with makefile-specific name (i.e. names of such variables should be different in each makefile):
this_makefile_unique_name := $(current_makefile)
rule:
echo In makefile $(current_makefile):
echo Making target $#
or use eval:.
define make_rule
rule:
echo In makefile $(1):
echo Making target $$#
$(eval $(call make_rule,$(current_makefile)))
If you want to use the name of current makefile for debug purpose only, consider special debugging functions, like warning or info:.
$(warning We're in makefile $(current_makefile))
These functions use "immediate" expansion and will print the correct value.
How to define such a $(current_makefile)?
You have to manually maintain stack of makefile inclusions. When you include a makefile, its name is placed to the top of the stack; when you return from included makefile to the outer one, the topmost name is popped out of stack. This is achieved by inserting special calls to the beginning and the end of makefile:
# Beginning of makefile
$(eval $(makefile_names_push))
#... makefile text
$(warning $(current_makefile))
#...
$(eval $(makefile_names_pop))
#End of file
Now define the functions at the beginning of your root makefile.
lastword=$(word $(words $(1)),$(1))
define makefile_names_push
current_makefile := $$(CURDIR)/$$(call lastword,$$(MAKEFILE_LIST))
makefile_stack :=$$(makefile_stack) $$(current_makefile)
endef
define makefile_names_pop
makefile_stack := $$(filter-out $$(current_makefile),$$(makefile_stack))
current_makefile := $$(call lastword,$$(makefile_stack))
endef
If you're sure your make is new enough (version 3.81+), replace lastword call with builtin function:.
#inctead of $$(call lastword,$$(MAKEFILE_LIST))
$$(lastword $$(MAKEFILE_LIST))
Is it useful?
Totally useless. An only use that might be useful here is to make 100 makefiles that are symlinks to one makefile, the rules in these makefiles depending on their names. But it can be achieved within one makefile and foreach-eval technique described in the manual. So my post was a complete waste of time, though I had some fun :-)
This returns the name of the first Makefile called, i.e. the one at the bottom of the call stack:
MAKEFILE_JUSTNAME := $(firstword $(MAKEFILE_LIST))
MAKEFILE_COMPLETE := $(CURDIR)/$(MAKEFILE_JUSTNAME)
When used in non-cross-recursive situations (e.g. for makedepend), it is just the name of the current makefile.
I wanted to do something similar (for echoing the contents of the Makefile) for when I use Make for managing simple repetitive tasks. I came across this page and found it was exactly what I was after and really useful for my limited understanding of make.
My result after reading this page:
# Makefile - 'make' and 'make help' now echo the makefile.
help:
cat $(lastword $(MAKEFILE_LIST))
start:
sudo -u www /path/to/webapp/myhttpd restart
stop:
sudo kill `cat /path/to/webapp/data/httpd.pid`
A quick excursion to Google suggests this site has the answer.
G'day,
If you make a copy of your original makefile, say makefile_test, and then enter the command:
make -np -f makefile_test 2>&1 | tee output
That will evaluate the makefile and your make environment but not execute any of the commands. Looking through the output file for references to makefile_test will show you what is set in make's environment and where that value is being set.
N.B. This can generate a lot of info! And don't add the -d (debug) switch which will generate tons of additional output about make's decision process but minimal additional info about make's env.
HTH
The solutions here addresses 1) POSIX make with 2) Invoked, non included, makefile in 3) A Unix alike platform.
What the OP asked for:
target:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME other_target
The targets depends on the makefile, kind of bloated:
TARGET1_MAKEFILENAME = target1_preamble
all: target1 target2...
target1: $(TARGET1_MAKEFILENAME) other_dependencies...
#test $(TARGET1_MAKEFILENAME) == target1_preamble && exit 0; \
built_instructions_for_target1;
target1_preamble:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME target1;
Can be a bit simplified if make is invoked only for all targets.
MAKEFILENAME = invoked_makefile_placeholder
all: target1 target2...
target1: $(MAKEFILENAME) other_dependencies...
#test $(MAKEFILENAME) == invoked_makefile_placeholder && exit 0; \
built_instructions_for_target1;
invoked_makefile_placeholder:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME
With the previous approach is trivial to implement a solution for included makefiles based in grep and a unique pattern contained in the makefile.
I never answer when I feel the question got a proper solution.

Resources