51

I'm trying to assign the output of this command ( that is in my makefile ) to the makefile HEADER var like in this following line of code:

HEADER = $(shell for file in `find . -name *.h`;do echo $file; done)

The problem is that if I print HEADER in my makefile using:

print:
    @echo $(HEADER)

I get

ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile ile

And if I run this command directly in the console, and directly where my makefile is:

myaccount$ for file in `find . -name *.h`;do echo $file; done
./engine/helper/crypto/tomcrypt/headers/._tomcrypt_pk.h
./engine/helper/crypto/tomcrypt/headers/tomcrypt.h
./engine/helper/crypto/tomcrypt/headers/tomcrypt_argchk.h
./engine/helper/crypto/tomcrypt/headers/tomcrypt_cfg.h
./engine/helper/crypto/tomcrypt/headers/tomcrypt_cipher.h
./engine/helper/crypto/tomcrypt/headers/tomcrypt_custom.h
./engine/helper/crypto/tomcrypt/headers/tomcrypt_hash.h
./engine/helper/crypto/tomcrypt/headers/tomcrypt_mac.h
....

So I get all my header files. I'm doing this to avoid manually specifying all my .h files manually in my makefile.

Any ideas ?

Goles
  • 11,599
  • 22
  • 79
  • 140
  • I don't think it's worthy of an answer because it's not really what you're asking but I can only presume you're doing this for dependencies; if a header file is modified the respective source files need to be recompiled? If so you might find the -MM option to cc (well technically cpp) of use. You can even make such a Makefile for a directory that has separate (unrelated) source files. This is useful for tab completion. But the value of it is that it also generates dependencies for the source files (just like you would if it is a single project). – Pryftan Jan 22 '19 at 19:03

3 Answers3

102

You will need to double-escape the $ character within the shell command:

HEADER = $(shell for file in `find . -name *.h`;do echo $$file; done)

The problem here is that make will try to expand $f as a variable, and since it doesn't find anything, it simply replaces it with "". That leaves your shell command with nothing but echo ile, which it faithfully does.

Adding $$ tells make to place a single $ at that position, which results in the shell command looking exactly the way you want it to.

e.James
  • 116,942
  • 41
  • 177
  • 214
  • Very nice response :), I do it this way because in my for loop I actually perform more complex stuff, so I just posted a small snippet here. Thank you very much @e.James. – Goles Mar 03 '10 at 17:01
  • It might be noted that you also have to do this outside of the shell builtin when doing shell commands and referring to a a variable e.g. in a for loop (it's not always needed to use the shell builtin, after all). – Pryftan Jan 22 '19 at 18:58
  • How do I escape the ")" character? – luckydonald Dec 07 '19 at 20:13
  • 1
    Beware: `$(shell ...)` syntax is GNU Makefile extension which may not be supported by locally available `make`., Also note the difference with `HEADER = $(shell ...)` vs `HEADER := $(shell ...)` where the former is expanded on every use and latter only once. If your shell command takes a lot of time, this difference may cause huge performance difference, too. The `:=` syntax is also GNU Makefile extension. – Mikko Rantalainen May 21 '21 at 11:54
20

Why not simply do

HEADER = $(shell find . -name '*.h')
sorpigal
  • 25,504
  • 8
  • 57
  • 75
  • 3
    This is a correct workaround for this specific issue but doesn't explain *why* the original command fails (that is, the need for encoding `$` with `$$`). – Mikko Rantalainen May 21 '21 at 11:49
8

The makefile tutorial suggested to use wildcard to get list of files in a directory. In your case, it means this :

HEADERS=$(wildcard *.h)
BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • 11
    This is not really equivalent to the code in the question. The wildcard substitution takes place while the Makefile is parsed, whereas for instance the shell commands can take place only once a Makefile rule is executed. This makes a difference when testing for existence of files generated by the Makefile. – Étienne Sep 10 '14 at 09:24
  • `HEADERS=$(wildcard ...)` should be executed multiple times but `HEADERS:=$(wildcard ...)` will be executed only once. As far as I know, the `wildcard` function is not special here. – Mikko Rantalainen May 21 '21 at 11:56