How to define AUTOMAKE_OPTIONS conditionally? - automake

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.)

Related

How to check whether GNU Make supports Guile

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))

makefile use variable defined in target [duplicate]

This question already has answers here:
Define make variable at rule execution time
(4 answers)
Closed 4 years ago.
How can one use the variable defined inside a make target
.PHONY: foo
VAR_GLOBAL=$(shell cat /tmp/global)
foo:
echo "local" > /tmp/local
VAR_LOCAL=$(shell cat /tmp/local)
echo ${VAR_GLOBAL}
echo ${VAR_LOCAL}
here is the execution output:
$ echo global > /tmp/global
$ make foo
echo "local" > /tmp/local
VAR_LOCAL=local
echo global
global
echo
As #KelvinSherlock pointed out this is a duplicate of another question
here is the specific solution for my question:
.PHONY: foo
VAR_GLOBAL=$(shell cat /tmp/global)
foo:
echo "local" > /tmp/local
$(eval VAR_LOCAL := $(shell cat /tmp/local))
echo ${VAR_GLOBAL}
echo ${VAR_LOCAL}
You probably want to use the override directive in a target-specific variable assignment, so try:
foo: override LS_LOCAL=$(shell ls /var | tail -1)
echo ${LS_GLOBAL}
echo ${LS_LOCAL}
If LS_LOCAL is never defined (even by builtin-rules) you might not need the override keyword.
BTW, you might avoid $(shell ls /var | tail -1) by using the wildcard function combined with the lastword function (perhaps combined with notdir function), so you might code $(lastword $(wildcard /var/*)) or $(notdir $(lastword $(wildcard /var/*))) instead . However, beware of the order of expansion, and of filenames with spaces. At last the shell function probably uses your $PATH variable (so strange things could happen if some weird ls program appears there before /bin/ls). Perhaps using $(shell /bin/ls /var | /usr/bin/tail -1) might be better.
Look also into Guile-extended make; consider perhaps some other build-automation tool like ninja and/or generating your Makefile (or other build configuration) with something like a configure script generated via autoconf or cmake.
Notice also that a command in recipe can be made of several physical backslashed lines (hence executed in the same shell). Maybe you might consider something like
export MY_VAR=$$(ls /var | tail); \
dosomething; \
use $$MY_VAR
inside some recipe.

Transitioning from shell to Makefile

I want to know how I can convert this shell script to a Makefile and use make all to perform all the script's operations. I have two directories pictures and thumbs, where thumbs is empty before running the script and pictures contains some .jpg files. Finally, this is the shell script that I want to convert to a Makefile:
#!/bin/bash
DIR="thumbs"
if [ "$(ls -A $DIR)" ]; then
p=$(find pictures/|grep "jpg"|cut -d"/" -f2)
for i in $p
do
m=$(ls -l pictures/$i | cut -d" " -f7)
n=$(ls -l thumbs/$i | cut -d" " -f7)
if [ "${m//':'}" -gt "${n//':'}" ] ;then
rm thumbs/$i
convert -thumbnail 100 pictures/$i thumbs/$i
fi
done
else
find pictures/ |cut -d"/" -f2 | grep "jpg"| \
awk '{system("convert-thumbnail 100 pictures/" $0 " thumbs/" $0)}'
fi
From Makefile you can call a program.
Example
$ cat a.sh
echo From Makefile
$ cat Makefile
all:
./a.sh
Test :
$ make
./a.sh
From Makefile
You can implement the behavior of your shell code much more simply as a Makefile, at least if you are willing to rely on GNU make. I interpret this to be what you're asking (modulo dependency on GNU make in particular). This is a pretty functional rough cut:
THUMBS = $(patsubst pictures/%,thumbs/%,$(wildcard pictures/*jpg*))
all: $(THUMBS)
thumbs/%: pictures/% thumbs
convert -thumbnail 100 '$<' '$#'
thumbs:
mkdir -p '$#'
.PHONY: all
Notes:
the THUMBS make variable gets set to a list of the thumbnail images you want to manage, based on expanding the shell glob pictures/*jpg* and replacing each occurrence of pictures/ with thumbs/.
The pattern is chosen to match your shell code, but perhaps you really want something more like $(wildcard pictures/*.jpg)
File names with whitespace in them are going to present a tricky problem if you need to worry about them; file names with certain other special characters too, albeit a bit less so
the patsub and wildcard functions are GNU extensions
You could also merge the definition of THUMBS into the rule for all, and avoid a separate variable
The rule for thumbs/%: pictures/% thumbs uses GNU-specific pattern rule syntax; this particular form is hard to express to POSIX make.
The thumbs directory is created if absent, but errors will occur if there is an ordinary file of that name in the way
make all (or just make) will update all out-of-date thumbnails; it does not rely on the same date comparison logic as the original script (which is a good thing)
The .PHONY rule is just to be careful. It prevents the existence of an actual file named "all" from interfering with make's operation.

Prevent a command being executed from a source'd file in Bash

For security purposes, how can I prevent a command being executed in a file that is source'd?
For example:
#!/bin/sh
source file.cfg
Wanted:
get="value"
Unintended:
command
You could use a mechanism like in Python. Define variables and/or functions and put executable commands into a conditional block:
#!/bin/bash
# Variables and functions comes here
a=1
b=2
function foo() {
echo "bar"
}
# Put executable commands here
if [ "$0" = "$BASH_SOURCE" ] ; then
foo
fi
If you chmod +x the file and run it or run it through bash file.sh the executable commands in the conditional statement will get executed. If you source the file only variables and functions will get imported.
Long story short, you can't. We could debate how to try to prevent some commands from being executed but if security is the major concern here, source is a no-go. You are looking for a proper configuration facility — while source is intended to execute code.
For example, the following code provides a trivial key-value configuration file parsing:
while read -r x; do
declare +x -- "${x}"
done < file.cfg
But this is both far from the flexibility source gives you, and it is far from perfectly secure solution either. It doesn't handle any specific escaping, multi-line variables, comments… and it also doesn't filter the assigned variables, so the config can override your precious variables. The extra +x argument to declare ensures that the config file at least won't modify environment exported to programs.
If you really want to go this route, you can try to improve this. But if you are really worried about security, you should think twice before using shell script at all. Writing proper shell script is not trivial at all, and it is full of pitfalls.
Something basic, might work:
name="$(sed -n 1p < source_file | grep -o 'name="[^"]*' | grep -o '[^"]*$')"
lastname="$(sed -n 2p < source_file | grep -o 'lastname="[^"]*' | grep -o '[^"]*$')"
age="$(sed -n 3p < source_file | grep -o 'age="[^"]*' | grep -o '[^"]*$')"
Next, check the parameters if they meet certain standards for example if it matches a name of a database ($LIST_NAMES) or if you have a certain amount of character string, ect.
if ! grep -Fox "$name" <<<"$LIST_NAMES"; then exit 1; fi
if [ $(wc -c <<<"$age") -gt 3 ]; then exit 1; fi
then taken only the lines useful to prevent the rest.
head -n3 < source_file > source_file.tmp
source 'source_file.tmp'

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