GNU make strange behavior - gnu-make

I have a simple Makefile:
VAR := aaa
include a.inc
VAR += bbb
and a.inc
some_target: $(VAR)
#echo "refer to automatic var $^"
#echo "refer to VAR $(VAR)"
aaa:
bbb:
and when I run make I see a strange print:
refer to automatic var aaa
refer to VAR aaa bbb
Seems that GNU make substitutes $(VAR) in prerequisites at the inclusion moment but $(VAR) in debug print in some later moment. Is this correct behavior ? Why GNU make acts in such non-intuitive way?

It is correct and it isn't unintuitive (though perhaps not immediately obvious).
It works the same way as variables in every other language.
Try this shell example.
$ var=aaa
$ echo "${var}"
aaa
$ ev() { echo "${var}"; }
$ ev
aaa
$ var+=bbb
$ ev
aaabbb
You get the value of a variable as it exists at the time that you use it.
The difference between tgt: prereq and #recipe line in make is when the lines are evaluated.
The GNU Make Manual goes into about this in 3.7 How make Reads a Makefile. Specifically, for this situation, you want the very last sub-section Rule Definition.

Make variables set with = (rather than :=) are expanded when they are used.
When you run make first it parses the makefile and determines the targets and prerequisites. At that point VAR gets expanded to its current value.
Then it runs the first target, and executes the recipe for that target, at which point VAR is expanded again, but to its new value.

GNU make evaluates makefile in 2 passes. Everything is evaluated at first pass, except recipes. Recipes are evaluated at the second pass. So, your reference to $(VAR) in some_target's recipe is evaluated at the second pass, i.e. after the += bbb was appended to VAR, which happened at the end of the first pass.
To achieve the behavior you wanted you can use the following techinique for defining your rule:
define rule
some_target: $(VAR)
#echo "refer to automatic var $^"
#echo "refer to VAR $(VAR)"
endef
$(eval $(rule))
Don't forget to use tabs for recipe lines.

Related

In GNU Make, output of function call is being treated as a separate line to be executed by the shell

I have defined the following function in a GNU Make file:
define comma-seperated
$(shell printf '$(foreach name,$(1),$(name),)' | head -c -1)
end
And you would use it like this:
names := John Paul George Ringo
.PHONY: test
test:
#echo Hey $(call comma-sepperated, "$(names)")
but It's outputting as:
Hey
"John,Paul,George,Ringo"
and generating an error:
/bin/sh: John,Paul,George,Ringo: command not found
make: *** [Makefile: test] Error 127
Why is the output of the function call being moved to a separate line, and why is the shell trying to then execute it as a command?
As I said, I can't reproduce this. Also, your makefile adds spaces after the commas while your example output doesn't have these, so something is clearly different. Please try to ensure that your questions use the exact example code and show the exact output.
However, a define variable is allowed to contain newlines and if it does, those newlines are preserved and interpreted as newlines in any recipe it is expanded in. For example you can create an entire recipe inside a define, including multiple lines, then use it in a rule and it will work as expected.
So, my suspicion is that in your real version there's an initial newline in the expansion.
I don't see any good reason to use such a complex method for this, though: a difficult-to-understand combination of call, shell, printf, head, etc. It's much simpler to do all of this within make.
Here's one way to do it:
EMPTY :=
SPACE := $(EMPTY) $(EMPTY)
COMMA := ,
comma-separated = $(subst $(SPACE),$(COMMA),$(strip $(1)))
names := John Paul George Ringo
.PHONY: test
test:
#echo Hey $(call comma-separated,$(names))

Make: run common pre/post rules for a set of targets

I want to build the following dependency graph, but with pre and post being artifact free:
Before creating/updating any of a, b, or c the command pre should run once and afterwards post should run once. Both do not and preferably should not produce artifacts. And of course, these should only be run if any of a b c have changed. This should all be triggered by a phony all target, i.e. a is never run independently.
Using order-only prerequisites a: | pre does not help because these are always run. Making post depend on a b c won't work because then it is also run all the time because post does not create an artifact.
If this is impossible and artifacts are required after all, how would pre (the more interesting of the two) only run if any of the targets which depend on it have changed?
Note: a etc. are normal makefile targets (which could be called independently), e.g.:
a: a.in a.dependency
#echo Creating a
#mkabc a.in > a
There is only one way in make to force a command to execute before target X is built, but only if target X needs to be built, and that's put the command as the first thing in the recipe for target X. There's no way to manipulate the dependency graph in GNU make so that make determines if a target needs to be built and, if so, first builds some other target before the recipe runs.
So you will definitely have to use recursive make here, putting the command to build the pre target into the recipe of the other targets. However, of course that will cause it to be built multiple times which you don't want.
One way to get around that is to play a trick using eval. Try this (untested):
BUILD_PRE = $(shell $(MAKE) -j1 pre >/dev/null)
post: a b c
echo $#
pre:
echo $#
a b c:
$(BUILD_PRE)$(eval BUILD_PRE =)
touch $#
.PHONY: pre post
In the rule for a, b, and c we first expand the BUILD_PRE variable which results in a recursive make invocation via the shell call. Then the eval expansion will reset the value of BUILD_PRE so that it's now empty; this means in subsequent rules for b and c this first line will expand to the empty string and pre will not be run again.
You may ask, why do we need to use shell here? Can't we just use:
BUILD_PRE = $(MAKE) -j1 pre
so that the first recipe contained a recursive make? The problem with this is that it won't work with parallel make. Suppose the first target make attempts to build is a (it will always be of course). That recipe will contain a recursive make invocation and make will start it. But if you are using -j make does not wait for that recipe to complete: it will go try to start b and c. Since BUILD_PRE is now empty you only get one build of pre, but b and c are not waiting for pre to be completed.
By using a shell function the recursive invocation is forced to complete when the recipe is expanded, before any other recipe is started.
I should say, I suspect there may be a few odd things about this. In particular when make normally invokes a recursive build it does some setup etc. that won't happen when a recursive build is invoked through shell. But, it should work.
Edit: Final Makefile with '+' prefix to mark recursive make calls:
all: allabc
BUILD_PRE = $(shell $(MAKE) pre)
BUILD_POST =
pre:
#echo PRE abc >&2
post:
#echo POST abc >&2
allabc: a b c
#+$(BUILD_POST) > /dev/null
a:
+$(BUILD_PRE)$(eval BUILD_PRE = )
touch "$#"
$(eval BUILD_POST = $$(MAKE) post)
b:
+$(BUILD_PRE)$(eval BUILD_PRE = )
touch "$#"
$(eval BUILD_POST = $$(MAKE) post)
c:
+$(BUILD_PRE)$(eval BUILD_PRE = )
touch "$#"
$(eval BUILD_POST = $$(MAKE) post)
clean:
rm -f a b c
Not sure I understand all the details but assuming you want to build your 5 targets when invoking make all, with the dependencies you show (and maybe a, b and c in parallel), you can, for instance:
.PHONY: all pre post
all:
$(MAKE) pre
$(MAKE) a b c
$(MAKE) post
pre:
<pre-recipe>
post:
<post-recipe>
a:
<a-recipe>
...

history expansion in zsh

according to the doc, !! should expand to the last command when hit <tab> after it. However, this is not the case for my setup (I used default oh-my-zsh). Here is an example:
$ echo 1
1
$ echo 2
2
$ !!<tab>
$ echo 1
Moreover, !# does not expand to what are input in the current line. Instead, it expands to the last command
$ echo 1
1
$ echo 2
2
$ echo 3 !#<tab>
$ echo 3 echo 2
Is there any option controls this?
I had a look at the default oh-my-zsh completion settings on github and it look like parameter expansion is not enabled out of the box. According to oh-my-zsh documentation any overrides should go into custom/ directory, in files ending with *.zsh. Getting parameter expansion to work should be as simple as dropping there a file with something like this:
zstyle ':completion:*' completer _expand _complete
The completer function you're looking for is called _expand, here is what man zshcompsys has to say about it:
_expand
This completer function does not really perform completion, but instead
checks if the word on the command line is eligible for
expansion and, if it is, gives detailed control over how this
expansion is done. For this to happen, the completion system needs to
be invoked with complete-word, not expand-or-complete (the default
binding for TAB), as otherwise the string will be expanded by the
shell's internal mechanism before the completion system is started.
Note also this completer should be called before the _complete
completer function.
The tags used when generating expansions are all-expansions for the
string containing all possible expansions, expansions when adding the
possible expansions as single matches and original when adding the
original string from the line. The order in which these strings are
generated, if at all, can be controlled by the group-order and
tag-order styles, as usual.
The format string for all-expansions and for expansions may contain
the sequence '%o' which will be replaced by the original string from
the line.
The kind of expansion to be tried is controlled by the substitute,
glob and subst-globs-only styles.
It is also possible to call _expand as a function, in which case the
different modes may be selected with options: -s for substitute, -g
for glob and -o for subst-globs-only.

Make zsh complete arguments from a file

zsh is great but its completion system is very diverse. And the documentation lacks good examples. Is there a template for completing for a specific application. The completion would get its match data from a file, separated by newlines?
I tried modifying an older example of mine that takes match data "live":
~ % cat .zsh/completers/_jazzup
#compdef jazz_up
_arguments "2: :(`mpc lsplaylists|sed -e 's# #\\\\ #g'`)"
I could supply cat my_file there instead of mpc invocation and so on but would there be a more elegant way to do this simple task? And that completion there is placement-specific: can you provide an example where zsh would attempt to complete at any point after the program name is recognized?
The match data will have whitespaces and so on, the completion should escape the WS. Example of that:
Foo bar
Barbaric
Get it (42)
Now if that completion would be configured for a command Say, we should get this kind of behaviour out of zsh:
$ Say Fo<TAB>
$ Say Foo\ bar
$ Say Ge<TAB>
$ Say Get\ it\ \(42\)
Simple completion needs are better addressed with _describe, it pairs an array holding completion options and a description for them (you can use multiple array/description pairs, check the manual).
(_arguments is great but too complex.)
[...]
First create a file
echo "foo\nbar\nbaz\nwith spac e s\noh:noes\noh\:yes" >! ~/simple-complete
Then create a file _simple somewhere in your $fpath:
#compdef simple
# you may wish to modify the expansion options here
# PS: 'f' is the flag making one entry per line
cmds=( ${(uf)"$(< ~/simple-complete)"} )
# main advantage here is that it is easy to understand, see alternative below
_describe 'a description of the completion options' cmds
# this is the equivalent _arguments command... too complex for what it does
## _arguments '*:foo:(${cmds})'
then
function simple() { echo $* }
autoload _simple # do not forget BEFORE the next cmd!
compdef _simple simple # binds the completion function to a command
simple [TAB]
it works. Just make sure the completion file _simple is placed somewhere in your fpath.
Notice that : in the option list is supposed to be used for separating an option from their (individual) description (oh:noes). So that won't work with _describe unless you quote it (oh\:yes). The commented out _arguments example will not use the : as a separator.
Without changing anything further in .zshrc (I already have autoload -Uz compinit
compinit) I added the following as /usr/local/share/zsh/site-functions/_drush
#compdef drush
_arguments "1: :($(/usr/local/bin/aliases-drush.php))"
Where /usr/local/bin/aliases-drush.php just prints a list of strings, each string being a potential first argument for the command drush. You could use ($(< filename)) to complete from filename.
I based this on https://unix.stackexchange.com/a/458850/9452 -- it's surprising how simple this is at the end of the day.

Makefile: conversion from recursive make calls to a sequential flow

I have a Makefile that is executed by "gmake -f Makefile foo" and looks like the following.
foo:
#set var = 1
#$(MAKE) bar var=1
bar:
#hello.mk is included
#echo “success”
ifeq ($(var), 1)
include test\hello.mk
endif
I'd like to convert this to a sequential flow, like something in the following as it has less overhead and does not need to step back in this Makefile.
foo:
$(eval var=1)
#$(bar)
define bar
include test\hello.mk #this doesn’t work
#echo “success”
endef
I cannot have an include statement inside a function or target, so how should I go around this problem to make this Makefile sequential with no make calls?
Short answer: you can't do this.
You want to make 'foo' and 'bar' with what are in effect different makefiles, which you can't do without a recursive call to Make.
If you can put some restrictions on what hello.mk can do, you might be able to get this into one pass.

Resources