1

I need to recursively find all the header files in a list of directories. I can't figure out how to escape the command properly. I have searched around and found various information on escaping in makefiles but I have not been able to solve this issue.

In bash the following does what I want:

find path1 path2 path3 -type f \( -name *.hpp -o -name *.h -o *.hxx \)

In my make file I have tried a few combinations of foreach, etc. Currently I have this:

INCLUDE_PATHS ?= path1 path2 path3
MY_HEADERS := $(shell find $(INCLUDE_PATHS) -type f \( -name *.h -o -name *.hpp -o -name *.hxx \))

This produces:

find: paths must precede expression
Usage: find [-H] [-L] [-P] [path...] [expression]

If I just look for one extension such as "*.hpp" it works fine (I assume because the \(...\) is not needed).

I have tried various combinations of $, ', ". \ to escape the '\' characters in the shell command without success.

Any help would be greatly appreciated.

sjacobs
  • 13
  • 5

3 Answers3

1

Your problem doesn't have anything to do with make or the value of INCLUDE_PATHS or how make interprets backslash characters. The problem is that you're not escaping your globbing, and it's matching some local files. Rewrite your function to escape your glob statements, like this:

MY_HEADERS := $(shell find $(INCLUDE_PATHS) -type f \( -name \*.h -o -name \*.hpp -o -name \*.hxx \))

I would be very surprised if the original command works in bash without quoting those characters, if you run it from the same directory containing the same contents as make.

MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • I have a related problem: in a recipe I need to use the sort command with the -t option to specify the field delimiter as a TAB. But `sort -t'\t'` fails when it's passed to sort ("sort: multi-character tab ‘\\t’". How do I escape a backslash in a shell command option? – Peter Flynn Jan 31 '21 at 14:46
  • This isn't related to make either: if you try it you'll get that same error if you run the command from the shell prompt, not in a recipe. There's nothing in the `sort` man page that suggests it knows how to convert a two-character string `\t` into a TAB character, and in fact the error you receive shows that it doesn't. It thinks you're trying to set a delimiter as the two-character sequence backslash-`t`. Using Google for how to use TAB as a delimiter with `sort` I found this: https://stackoverflow.com/questions/1037365/sorting-a-tab-delimited-file which seems on-point. – MadScientist Jan 31 '21 at 16:39
0

The variable MY_HEADERS becomes correct, by calling $($INCLUDE_PATHS) -- not $(INCLUDE_PATHS). So your Makefile would be:

INCLUDE_PATHS ?= path1 path2 path3
MY_HEADERS := $(shell find $($INCLUDE_PATHS) -type f \( -name *.h -o -name *.hpp -o -name *.hxx \))

You can continue to check the variable's value:

all: printme

printme:
    @echo $(MY_HEADERS)

Running this Makefile by make will show your desired answer.

popuptoast
  • 1
  • 1
  • 2
  • My earlier comment was regarding a typo in my original question which I have corrected. – sjacobs Sep 07 '17 at 20:15
  • Your work on the find path is not the solution or relevant. If I put a single path there rather then reference a make variable I still get the same result. The issue is in passing the multiple -name options which require the \(...\) characters be passed to the shell correctly. In other words this `MY_HEADERS := $(shell find $(INCLUDE_PATHS) -type f -name *.hpp))` works fine. – sjacobs Sep 07 '17 at 20:25
  • Whether "escaping backslash" or not was not the reason why you got the initial results. In Makefile, we embed the same find command EXCEPT there is different variable calling convention. – popuptoast Sep 07 '17 at 20:28
  • BTW you should do like this: `INCLUDE_PATHS ?= 'path1 path2 path3' `(single quote added) – popuptoast Sep 07 '17 at 20:33
  • Uh... eh? `$($INCLUDE_PATHS)` is _definitely_ wrong. What is that supposed to do? – MadScientist Sep 07 '17 at 21:22
0

Although MadScientist already answered the question perfectly, you could use the following to avoid the shell altogether:

INCLUDE_PATHS ?= path1 path2 path3
EXTENSIONS := .h .hpp .hxx
MY_HEADERS := $(shell find $(INCLUDE_PATHS) -type f \( -name \*.h -o -name \*.hpp -o -name \*.hxx \))
$(info $(MY_HEADERS))
MY_HEADERS := $(foreach p,$(INCLUDE_PATHS),$(foreach e,$(EXTENSIONS),$(wildcard $(p)/*$(e))))
$(info $(MY_HEADERS))
Vroomfondel
  • 2,704
  • 1
  • 15
  • 29