0

This is the Makefile content:

sgr0 := $(shell tput sgr0)
bold := $(shell tput bold)
orange := $(shell tput setaf 166)

test:
    @echo "$(bold)$(orange)ENV_VARIABLE:(sgr0) ${ENV_VARIABLE}"

You run the following command in bash:

export ENV_VARIABLE=value

Then you call call make test and get the following output:

ENV_VARIABLE: value

Instead, if you modify the Makefile test target like this:

test:
        @echo "$(bold)$(orange)ENV_VARIABLE:(sgr0) $${ENV_VARIABLE}"

The result of calling make test would be:

ENV_VARIABLE: $ENV_VARIABLE

So I'm pretty confused, because the GNU make official doc says to use $$ for shell variables:

Variable and function references in recipes have identical syntax and semantics to references elsewhere in the makefile. They also have the same quoting rules: if you want a dollar sign to appear in your recipe, you must double it (‘$$’). For shells like the default shell, that use dollar signs to introduce variables, it’s important to keep clear in your mind whether the variable you want to reference is a make variable (use a single dollar sign) or a shell variable (use two dollar signs).

But if I use $$, then I don't see how my variable gets expanded. So, in order to follow the best practice of using $$ for shell variables and $ for make variables, I need to give up on being able to read how the shell variable gets expanded?

UPDATE:
You execute export ENV=test.txt
Makefile content:

test:
    touch $(ENV)

this works as it returns touch test.txt

Instead, if you change to $$(ENV), it will throw an error.

Berstigma
  • 313
  • 1
  • 2
  • 9
  • Make transforms environment variables into make variables. So `${ENV_VARIABLE}` (make variable) and `$${ENV_VARIABLE}` (shell variable) should do the same in your case. If you get `ENV_VARIABLE: $ENV_VARIABLE` there must be something else that you don't show us (backslashes?). – Renaud Pacalet Apr 17 '23 at 04:49
  • I updated the original post with the actual code I was running. – Berstigma Apr 17 '23 at 05:42
  • 1
    Downvoted for the disruptive edit. I'll be happy to remove the downvote if you roll back. Maybe post a new question with _your actual question_ if you still need help. – tripleee Apr 17 '23 at 05:54
  • 1
    I did the rollback – Berstigma Apr 17 '23 at 06:00

2 Answers2

0

In very brief, the variables you created are make variables. They are substituted by make before the shell runs at all.

If you wanted to defer expansion to be performed by the shell, then yes, you would have to use a different syntax (with the dollar signs escaped by doubling them, and without parentheses, possibly replacing them with braces).

export bold sgr0

demo:
    @echo "Make expanded $(bold)bold$(sgr0) before the shell ran"
    @echo "The shell can do this too: $${bold}bold$${sgr0}"

For the latter echo to work, you have to include the export line.

Braces are unnecessary when the variable is already a separate token. So in the above, $$sgr0 would work too. See also When do we need curly braces around shell variables?

If you were to try $$(bold), that's a command substitution, where the shell will try to run the text inside the parentheses as a command, and replace the command substitution with the output from that command. If the text inside the parentheses is not a valid command, then of course, you will get an error.

weird:
    @echo "Today is $$(date)"
    @echo "You don't want a $$(syntax error)"
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • In fact, I updated the answer with an explanation of that case just slightly earlier. – tripleee Apr 17 '23 at 05:43
  • So since both $$ and $ work, I can use the one I want? Or is it like bad practice? Currently I use single dollar because I want to see how the variable gets expanded, but theoretically i should use double dollar because it's a shell variable? – Berstigma Apr 17 '23 at 05:48
  • Theoretically yes, but those cases tend to get gnarly. Probably prefer `make` variables whenever they make sense. However, a variable which is defined outside of `make` makes sense to keep as strictly a shell variable, even though it is technically available to `make` too. – tripleee Apr 17 '23 at 05:51
  • Actually now that I look at it, I understand what's going on... The Makefile subprocess resolves the env variable and passes it to the shell, so you can see it already expanded. Or you can use double dollar so that the Makefile subprocess doesn't resolve it, instead the shell would expand it. Of course the result is the same... – Berstigma Apr 17 '23 at 05:54
  • Now I actually understand the difference. In the specific case where you put the env variable inside of `echo` command, the `echo` command itself will perform an expansion, in addition to the Makefile subprocess. So it gets double expanded and that's why it gets resolved in both cases. – Berstigma Apr 17 '23 at 05:56
  • `echo` simply prints out its arguments; the expansion is performed by the shell before `echo` runs, or by `make` before the shell runs if you use a `make` variable. – tripleee Apr 17 '23 at 06:01
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/253183/discussion-between-cholmugod-and-tripleee). – Berstigma Apr 17 '23 at 06:06
0

Let's illustrate with a few examples :

test:
    @echo "ENV_VARIABLE: ${ENV_VARIABLE}"
# Calling with: ENV_VARIABLE=value make
# prints : ENV_VARIABLE: value

Now

test:
    @echo "ENV_VARIABLE: $${ENV_VARIABLE}" # Two dollars
# Calling with: ENV_VARIABLE=value make
# prints the same: ENV_VARIABLE: value

So you would be tempted to say $ and $$ are the same, but they aren't.

test:
    @ENV_VARIABLE=value2; echo "ENV_VARIABLE: ${ENV_VARIABLE}"
# Calling with: ENV_VARIABLE=value make
# prints : ENV_VARIABLE: value

but

test:
    @ENV_VARIABLE=value2; echo "ENV_VARIABLE: $${ENV_VARIABLE}"
# Calling with: ENV_VARIABLE=value make
# prints : ENV_VARIABLE: value2
Philippe
  • 20,025
  • 2
  • 23
  • 32