Rewrite rule printout in make file - makefile

I am currently using a makefile for cocotb, similar to this.
That consists of a bunch of variable definitions followed by and
include $(shell cocotb-config --makefiles)/Makefile.sim
Where the final commands will be executed.
I run it in Visual studio code, the simulator output message format is (%file,%line|%column). I want them to be shown as (%file:%line:%col). I can easily do it piping the output to sed.
make MODULE=x TESTCASE=y | sed -e 's/,\([0-9]\+\)|\([0-9]\+\)):/:\1:\2):/g'
I was expecting to be able to change the make file so that the output will be edited before shown
Something like
...
.DEFAULT:
make -C $PWD -f $(shell cocotb-config --makefiles)/Makefile.sim \
(all variables/environment) (rule) \
| sed -e 's/,\([0-9]\+\)|\([0-9]\+\)):/:\1:\2):/g
Where "magic rule" would apply the specified rule and pipe its output to sed.
How can I achieve this?
Thank you.

Given your example, it seems like the .DEFAULT target would do what you want:
.DEFAULT:
$(MAKE) -f $$(cocotb-config --makefiles)/Makefile.sim $# \
| sed -e 's/,\([0-9]\+\)|\([0-9]\+\)):/:\1:\2):/g
Edit (from the OP)
The default works when the rule is given, e.g. if I invoke make sim instead of make. For this I can create a default-delegate rule.
More importantly, I have to pass all the variables to the sub make, that can be achieved by using export by itself.
Then, replacing the include command but the following snippets, works for the more common cases.
export
default-delegate: sim
.DEFAULT:
$(MAKE) -f $$(cocotb-config --makefiles)/Makefile.sim $# \
| sed -e 's/,\([0-9]\+\)|\([0-9]\+\)):/:\1:\2):/g'

Related

Print the prerequisites to a target

This is easily a duplicate of this question, but it has not been answered, for what I can see.
Here is my goal: to be able to print the prerequisites to a target.
I have some kind of a solution but it feels like a hack to me.
Say the target is all, and it only has prerequisites, in a file named makefile-1:
all: foo
all: bar baz
I can use another makefile named makefile-2:
all:
ifeq ($(SHOW),yes)
echo $^
else
cat $^
endif
This kind of gives me what I need, when invoked properly:
$ make -f makefile-1 -f makefile-2
cat foo bar baz
$ make -s SHOW=yes -f makefile-1 -f makefile-2
foo bar baz
But I am not sure what to do if the prerequisite actually has a recipe in the original makefile, or if there is a better/cleaner way.
An easier way to show the prerequisites to a target without building the target would probably be to use the -p option (as suggested here) and -q options together, as suggested on the GNU-Make manual page (long name for the option is --print-data-base):
make -qp | grep '^all:'
This will print the database and select only the line which has the target all and its prerequisites. If there is a rule for that target, it would be at the end of the same paragraph (if I am understanding the format of the output correctly).
If multiple makefiles are used, one can specify all of them with the -f option:
make -f makefile-1 -f makefile-2 -qp
This will of course collect prerequisites for the same target from all makefiles, but the target cannot have a rule specified in more than one file.

GNU make loop variable strange behavior

In my Makefile I am trying to copy a list of files from location1 to location2, then to location2 to location3. I got the following strange behavior:
FILES_LIST=dir1/file1 dir2/file2 dir3/file3 ........
mytarget:
for file in $(FILES_LIST) ; do \
#this works
cp -vf location1/$$file location2/$(shell $$file##*/) ; \
#this does not work
cp -vf location2/$(shell $$(file)##*/) location3/ ; \
done
I am using "$(shell $$(file)##/)" to strip out "dir1/" part of each item in FILES_LIST.
The first cp works (from location1 to 2), however, the send does not, build log shows "$(shell $$(file)##/)" is evaluated to empty.
I am using GNU Make 3.81
The problem is $$(file). That's not a variable evaluation. That's a Command Substitution. You meant $${file} (well not quite but we'll get to that).
There is also absolutely no reason to be using $(shell) here at all as you are already in a shell context when those lines run.
Not to mention that those $(shell) calls aren't doing anything even remotely like what you want (they aren't operating at the right time to do that).
You want this:
FILES_LIST=dir1/file1 dir2/file2 dir3/file3 ........
mytarget:
for file in $(FILES_LIST) ; do \
#this works
cp -vf location1/$$file location2/$${file##*/} ; \
#this does not work
cp -vf location2/$${file)##*/} location3/ ; \
done
Your $file variable is a shell variable not a make one. The call to $(shell) does not see it. You are effectively running $(shell $file##*/) which runs the $file##*/ command through the shell. That shell has no $file variable so that becomes ##*/ which is a comment and the whole thing returns nothing. (Actually I think the comment may be stripped first but that doesn't change anything.)
Use $(notdir $file) command.
notdir will strip out the directories and return the filename. For example, $(notdir dir1/file.h) will return file.h
Reference link for more detailed info:
https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html

multiple stems in makefile rule

I am trying to write a makefile that does something like the following:
%-foo-(k).out : %-foo-(k-1).out
# do something, e.g.
cat $< $#
i.e. there are files with arbitrary stems, then -foo-, then an integer, followed by .out. Each file depends on the one with the same name, with integer one smaller.
For instance, if the file blah/bleh-foo-1.out exists, then
make blah/bleh-foo-2.out
would work.
I could do this with multiple stems if there were such a thing... what's another way to do this sort of thing in (gnu) make?
There is no easy way to do something like this. You basically have two options: you can use auto-generated makefiles, or you can use $(eval ...). To me auto-generated makefiles are easier, so here's a solution:
SOURCELIST = blah/bleh-foo-1.out
all:
-include generated.mk
generated.mk: Makefile
for f in $(SOURCELIST); do \
n=`echo "$$f" | sed -n 's/.*-\([0-9]*\)\.out$/\1/p'`; \
echo "$${f%-foo-[0-9]*.out}-foo-`expr $$n + 1`.out: $$f ; cat $$< > $$#"; \
done > $#

Multi-line bash commands in makefile

Considering that every command is run in its own shell, what is the best way to run a multi-line bash command in a makefile? For example, like this:
for i in `find`
do
all="$all $i"
done
gcc $all
You can use backslash for line continuation. However note that the shell receives the whole command concatenated into a single line, so you also need to terminate some of the lines with a semicolon:
foo:
for i in `find`; \
do \
all="$$all $$i"; \
done; \
gcc $$all
But if you just want to take the whole list returned by the find invocation and pass it to gcc, you actually don't necessarily need a multiline command:
foo:
gcc `find`
Or, using a more shell-conventional $(command) approach (notice the $ escaping though):
foo:
gcc $$(find)
As indicated in the question, every sub-command is run in its own shell. This makes writing non-trivial shell scripts a little bit messy -- but it is possible! The solution is to consolidate your script into what make will consider a single sub-command (a single line).
Tips for writing shell scripts within makefiles:
Escape the script's use of $ by replacing with $$
Convert the script to work as a single line by inserting ; between commands
If you want to write the script on multiple lines, escape end-of-line with \
Optionally start with set -e to match make's provision to abort on sub-command failure
This is totally optional, but you could bracket the script with () or {} to emphasize the cohesiveness of a multiple line sequence -- that this is not a typical makefile command sequence
Here's an example inspired by the OP:
mytarget:
{ \
set -e ;\
msg="header:" ;\
for i in $$(seq 1 3) ; do msg="$$msg pre_$${i}_post" ; done ;\
msg="$$msg :footer" ;\
echo msg=$$msg ;\
}
The ONESHELL directive allows to write multiple line recipes to be executed in the same shell invocation.
all: foo
SOURCE_FILES = $(shell find . -name '*.c')
.ONESHELL:
foo: ${SOURCE_FILES}
FILES=()
for F in $^; do
FILES+=($${F})
done
gcc "$${FILES[#]}" -o $#
There is a drawback though : special prefix characters (‘#’, ‘-’, and ‘+’) are interpreted differently.
https://www.gnu.org/software/make/manual/html_node/One-Shell.html
Of course, the proper way to write a Makefile is to actually document which targets depend on which sources. In the trivial case, the proposed solution will make foo depend on itself, but of course, make is smart enough to drop a circular dependency. But if you add a temporary file to your directory, it will "magically" become part of the dependency chain. Better to create an explicit list of dependencies once and for all, perhaps via a script.
GNU make knows how to run gcc to produce an executable out of a set of .c and .h files, so maybe all you really need amounts to
foo: $(wildcard *.h) $(wildcard *.c)
What's wrong with just invoking the commands?
foo:
echo line1
echo line2
....
And for your second question, you need to escape the $ by using $$ instead, i.e. bash -c '... echo $$a ...'.
EDIT: Your example could be rewritten to a single line script like this:
gcc $(for i in `find`; do echo $i; done)

Makefile : assigning function variable in target command line

I need the xpi_hash variable to be assigned only when update target's command is decided to execute. Then I'm using this variable as environment, exporting, etc..
If I put it outside of rule, it will be expanded firstly, before $(xpi) target is called, hence will not find that file.
substitute := perl -p -e 's/#([^#]+)#/$$ENV{$$1} bla bla...
export xpi_hash
.PHONY: dirs substitute update
update: $(xpi) $(target_update_rdf)
xpi_hash := $(shell sha1sum $(xpi) | grep -Eow '^[^ ]+')
#echo "Updating..."
$(target_update_rdf): $(update_rdf)
$(substitute) $< > $#
and above of course is not correct, because for command part the shell is represented. So maybe another way to put this question is - how to bring variable as command output?
I'm not sure exactly what you're looking for here, how are you planning to use xpi_hash? If you want to get the current hash every time you use the variable use = to assign the variable instead of :=, e.g.
xpi_hash=$(shell sha1sum $(xpi) | grep -Eow '^[^ ]+')
update:$(xpi) $(target_update_rdf)
#echo $(xpi_hash)
will print the hash of xpi after it has been updated.
For variables in make, see section 6.2 of the manual. Briefly ':=' will expand variables on the right hand side, '=' will leave them to be expanded later.
The altered command in my comment (substitute = xpi_hash="$(xpi_hash)" perl -p -e 's/#([^#]+)#/$$ENV{$$1}...') will expand to be equivalent to
$(substitute)
xpi_hash="$(xpi_hash)" perl -p -e 's/#([^#]+)#/$$ENV{$$1}...'
xpi_hash="`sha1sum $(xpi) | grep -Eow '^[^ ]+'`" perl -p -e 's/#([^#]+)#/$$ENV{$$1}...'
xpi_hash="`sha1sum xpi_expansion | grep -Eow '^[^ ]+'`" perl -p -e 's/#([^#]+)#/$$ENV{$$1}...'
The xpi_hash="..." syntax is defining a variable in the bash subshell, rather than using the variable in make.
If only substitute must use xpi_hash, make xpi_hash a target-specific variable:
$(target_update_rdf): xpi_hash = $(shell ...)
$(target_update_rdf): $(update_rdf)
$(substitute) $< > $#
If other Perl scripts will need xpi_hash, and you want to export it, you have a problem, because the variables assigned in the subshells of a rule cannot (easily) be communicated to Make. But you can store it in a file and include it:
xpi_hash_file: $(xpi)
rm -f $#
echo xpi_hash = $(shell...) > $#
-include xpi_hash_file

Resources