3

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
vinni_f
  • 622
  • 3
  • 15
  • 25
  • 2
    https://stackoverflow.com/questions/1909188/define-make-variable-at-rule-execution-time – Kelvin Sherlock Nov 15 '17 at 14:44
  • thanks @KelvinSherlock you are right it's a dupe. The funny thing is that I overlooked it when I found it earlier... Thanks for your time. – vinni_f Nov 15 '17 at 15:38
  • 2
    When looking at that question please use the SECOND answer, it's better than the accepted answer in virtually every situation. This is kind of a flaw in SO: sometimes a less-good answer is accepted and then bumped up over and over and nothing can be done about it, and the less-good method propagates to makefiles across the world :sad:. – MadScientist Nov 15 '17 at 16:54

2 Answers2

7

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}
vinni_f
  • 622
  • 3
  • 15
  • 25
  • This is unnecessarily complex with odd side-effects. I recommend using a single command line with backslashes, as in Basile's last example (although the export is not needed in that example) or the second answer in the referenced question above. – MadScientist Nov 15 '17 at 16:57
  • Pardon the confusion, I know that the example is utterly stupid but the spirit is to put the result of a command in a variable and use that variable later on. – vinni_f Nov 15 '17 at 17:01
  • By "later on" do you mean later in the same recipe, or later in some other recipe? That still sounds pretty dangerous. What happens if this recipe is never invoked? If it is always invoked, then why do you need to set the variable in the recipe in the first place? This sounds like maybe you're asking for a solution to a problem you don't need to have. Can't say for sure of course. – MadScientist Nov 15 '17 at 17:11
  • @MadScientist I understand what is bugging you and I know that with the context I gave it's impossible to understand why I do such a thing and why for my problem the `eval` solution works and not the other. To add a little context, the first operation I need to do is to call a cli and collect some info in the output. The second command use the latter and does a second cli command. I also do a `test` on the variable before doing the second command to make sure it is set or else it fails. With that said, makefile is not the right tool to do what I do here, but I gotta comply with some standards. – vinni_f Nov 17 '17 at 14:11
2

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.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 1
    Hi thanks for your great response. It made me realize that my example was not reflecting my reality. I've modified my original code. It's important that VAR_LOCAL is defined at a certain point in the target and not systematically at the beginning since it's value will be impacted by other operations in the target – vinni_f Nov 15 '17 at 15:02