1

I'm trying to write a custom Makefile in which I collect all source files to compile against the main. First I collect their names:

# other sources to compile against (without extensions, separated by spaces)
DEPENDENT_FILES = greet farewell

Then I'd like to append .o to each word. In a shell, I can do this in many ways, e.g.

echo '$DEPENDENT_FILES' | sed 's/\>/\.o/g' # but I know, it's UNIX dependant
# or ...
echo '$DEPENDENT_FILES' | sed 's/[^ ]*/&\.o/g'

both result in greet.o farewell.o on the terminal, but fail (resulting in empty strings) during the make process. I write them as

DEPENDENCIES := $(echo '$DEPENDENT_FILES' | sed 's/\>/\.o/g')
# or ...
DEPENDENCIES := $(echo '$DEPENDENT_FILES' | sed 's/[^ ]*/&\.o/g')
# but they result in
$(info DEPENDENCIES is $(DEPENDENCIES)) # "DEPENDENCIES is "

I don't know what I'm doing wrong here, I suspect it could be something related to echos or pipes in Makefiles but I can't figure it out.

Please, I'd prefer answers related to working sed and echo solutions, without proposing different approaches: it's not the first time that I'm facing problems with echo and pipes in Makefiles and I still don't know what is wrong in my approach.

3
  • 1
    a note for anyone interested: if there is no dependencies, /^$/! can be written as prefix in the sed rule to avoid that an empty $DEPENDENT_FILES is translated to .o (e.g. sed '/^$/!s/[^ ]*/&\.o/g')
    – Maik93
    Dec 29 '20 at 9:50
  • 3
    “Then I'd like to append .o to each word.” – You can do that directly in make using the addsuffix function. E. g. DEPENDENCIES = $(addsuffix .o, $(DEPENDENT_FILES)).
    – phg
    Dec 29 '20 at 10:08
  • Guess: Some of the quoting trips of because of make interpretation. Debug in the usual way by divide and conquer: first replace the whole sed ... part with cat, see if the pipe works. If it does, use echo instead of sed and no pipes to see what quotation make processes.
    – dirkt
    Dec 29 '20 at 10:24
0

You need to realize that for all the similarities in their syntaxes, make and shell are quite apart and have to be used with abundant caution. To make matters worse, make is very stingy with its warnings.

You erred in two places in your make code:

## bad code 
DEPENDENCIES := $(echo '$DEPENDENT_FILES' | sed 's/\>/\.o/g')
  • There is no GNU make built-in function named echo l, coz that is how make is understanding it to mean.
  • What you need to do when you need to run an external utility is to call the gnu make built-in $(shell ...) function.
  • Now there's an insidious issue lurking in the way you wrote the make code, and that is the variable $DEPENDENT_FILES. make will parse it as a make variable $D followed by a string literal EPENDENT ! This is coz make vars when not enclosed parentheses or braces are single letter only, rest being taken as literal string.
  • Note shell vars vegin with $$ unless the eval is in play.
2
  • This is actually the answer that I've requested: very clarifying on my current mistakes
    – Maik93
    Dec 30 '20 at 15:29
  • Please also note that := is a make extension for target secific macro assignments, introduced by SunPro Make in January 1986 and that later, GNU make introduced something completely incompatible, so be careful not to use := in portable makefiles.
    – schily
    Jan 31 at 14:45
2

Then I'd like to append .o to each word.

There is no need for the round trip through the shell. Make is powerful enough to provide helpers for common operations on file names like this. In your case you want the function with the self-explanatory name addsuffix:

DEPENDENCIES = $(addsuffix .o, $(DEPENDENT_FILES))

Which gets you a.o b.o c.o for an input list a b c. A more complete example:

units = a b c

inc = $(addsuffix .h,$(units))
obj = $(addsuffix .o,$(units))

myprog: $(obj)
    gcc -o $@ $(obj)

header-bundle.h: $(inc)
    cat $< >$@

If you need more intricate substitutions, there’s also a variety of generic string operations but where they are sufficient, the file ops are more convenient and IMO easier to understand by a humble human.

2
  • Thank you, very clarifying and stylish. One only thing: what does make header-bundle.h should produce in this way? A single header with all the included dependencies?
    – Maik93
    Dec 30 '20 at 15:27
  • 1
    The header-bundle.h was just an example of generating another file from the $(inc) generated list of header names. There are some projects that create a bundle of all public API headers compiled from individual files for convenience (or as a workaround for crappy compilers).
    – phg
    Jan 5 at 8:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.