How to check whether GNU Make supports Guile - command-line-arguments

How to check from the command line whether GNU Make is built with support of Guile?
Inside Makefile it can be determined via analyzing .FEATURES variable (see documentation).

One possible way is a quasi makefile in stdin.
So, .FEATURES variable can be printed in the following way:
echo '$(info $(.FEATURES))' | make -f -
The following command outputs guile string if it is supported or nothing in otherwise:
echo '$(info $(filter guile,$(.FEATURES)))' | make -f - 2>/dev/null
A variation using grep:
echo '$(info $(.FEATURES))' | make -f - 2>/dev/null | grep -wo guile
The solution
As #bobbogo mentioned, we can avoid the pipe at all, using --eval option:
make --eval '$(info $(filter guile,$(.FEATURES)))' 2>/dev/null
This command will print 'guile' or nothing.

As #ruvim points out, the manual says
You can determine whether GNU Guile support is available by checking the .FEATURES variable for the word guile.
$(if $(filter guile,${.FEATURES}) \
,$(info Guile suppoerted, yay!) \
,$(error Guile not supported - update your make))

Related

Rewrite rule printout in make file

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'

/bin/sh: -c: line 1: syntax error: unexpected end of file in bash [duplicate]

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)

How to define AUTOMAKE_OPTIONS conditionally?

In an autotools-based project, I currently have the following line in my Makefile.am:
AUTOMAKE_OPTIONS = serial-tests
I would like to make this option apply if and only if my automake version is 1.12 or greater. The reason is the need to support the serial test harness with both 1.11 and 1.13 automake. What is the best way to do this?
I have already tried this:
AM_VER = $(shell $(AUTOMAKE) --version | head -n1 | sed -e 's|[^0-9.]||g')
AM_VER_MAJOR = $(shell echo $(AM_VER) | cut -d. -f1)
AM_VER_MINOR = $(shell echo $(AM_VER) | cut -d. -f2)
AM_VER_PATCH = $(shell echo $(AM_VER) | cut -d. -f3)
$(info $(AM_VER_MAJOR) $(AM_VER_MINOR) $(AM_VER_PATCH))
supports_serial_tests_opt = $(shell if [ "$(AM_VER_MAJOR)" -gt 1 ] || { [ "$(AM_VER_MAJOR)" -eq 1 ] && [ "$(AM_VER_MINOR)" -ge 12 ]; }; then echo true; fi)
$(info $(supports_serial_tests_opt))
$(if $(supports_serial_tests_opt), $(eval AUTOMAKE_OPTIONS=serial-opts))
$(info $(AUTOMAKE_OPTIONS))
and it doesn't work, because AUTOMAKE_OPTIONS need to be set at automake execution time, and the function conditionals are executed at make time. Even if it worked, I would have found it ridiculously verbose and bloated; is there a better way? My gut tells me I should use my configure.ac to set a variable which I will then simply let expand in the Makefile.am, like this:
AUTOMAKE_OPTIONS = $(SERIAL_TESTS)
The philosophy behind autoconfiguration is to check for features -- can I somehow skip the version checking and check for the serial-tests option availability and use it if given?
Your attempt does not work because $(shell ...) is only interpreted by GNU Make, and Automake reads Makefile.am by itself and knows nothing about GNU Make's features.
Instead of trying to use the AUTOMAKE_OPTIONS variables, you should try to use the AM_INIT_AUTOMAKE arguments. Because configure.ac is always read using M4, you can use shell expansions there. Besides, the options you set there will apply to the entire project and do not need to be repeated in each Makefile.am.
For instance:
...
AM_INIT_AUTOMAKE(m4_esyscmd([case `automake --version | head -n 1` in
*1.11*);;
*) echo serial-tests;;
esac]))
...
If you need to debug that script, use autoconf -t AM_INIT_AUTOMAKE to trace the arguments that are being passed to AM_INIT_AUTOMAKE.
(That said, I have to agree with William's comment. Requiring 1.12 seems a better move to me.)

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