tags:

views:

72

answers:

3

make syntax is newline-delimited, but $(shell ...) replaces newlines with whitespace. So what's the least ugly way to do

$(eval $(shell program-that-emits-makefile-fragment))

which doesn't work the way one might like.

+5  A: 

This will probably do for what you have in mind:

makefile_fragment:
  program-that-emits-makefile-fragment > $@

include makefile_fragment

Is that enough? There are additional tricks you can use if you need them, such as having the rule add context around what the program produces, or piping the output through sed to glom it into one line, then parsing it out with backslashes.

Beta
+10 if I could. That's an elegant and fairly portable solution.
Dan Moulding
+1  A: 

The issue has already been reported to the GNU make maintainers:

http://savannah.gnu.org/bugs/?28230

It was closed with the following comment:

As Philip mentions, this is the intended/documented behavior.
I recommend using "include" rather than eval; have your script write out a file, then include that file.

ADDED: There's a workaround! Here's a sample GNUmakefile (replace 8 spaces by tabs; yes, there are two empty lines after define newline):

define newline


endef

$(eval $(subst #,$(newline),$(shell { echo 'war:'; echo '        echo "not love?"'; echo '        echo "Give peace a chance!"'; } | tr '\n' '#')))

More generally, that's $(eval $(subst #,$(newline),$(shell myscript | tr '\n' '#')))

You have to pick a character that won't appear in the output of the script; # seems a good candidate.

Gilles
Writing out files is a bit ugly---have to use tempfile to prevent clobbering something, clean up, etc.The following works as long as the lines emitted by the script contain no whitespace.$(foreach line, $(shell make-frag-maker), $(eval $(line)))
Bryan
@colmode: If you don't like the include trick, here's another. I'm not sure I like it: it feels very hacky, but on the other hand it's make so what else did I expect. A good point is that it's fairly transparent; a point in favor of includes is that they should be a little easier to debug.
Gilles
@Gilles: Personally, I'd prefer the "include" approach. Portability may not matter in the OP's specific case, but "include" avoids use of non-portable constructs (such as `$(shell) and `$(eval)`) and is therefore, IMHO, the better solution.
Dan Moulding
@Dan: `include` isn't completely portable either. It's not in POSIX, for instance. And even where it exists, some make implementations read all included files before processing any rules, so you have to run `make` twice (at least). My preference for portability is a single script that generates a Makefile (i.e., `configure`, which doesn't have to be itself generated by autotools!).
Gilles
@Gilles: True, `include` isn't completely portable, but with Makefiles there's little that *is* completely portable, and `include` is one of the more commonly supported features. `shell` and `eval`, as far as I'm aware, are both GNU-only. And yeah, a shell script that generates a (very simple) Makefile is really the only hope of having a moderately complicated Makefile that is truly portable, but that doesn't seem to be an option here. :)
Dan Moulding
A: 

Here's a way to do it by hijacking the % substitution character, so we don't need to rely on a char that doesn't appear in the output of myscript.

define nl


enddef
$(foreach line, $(shell myscript | sed -e 's/%/\\%/' -e 's/$$/%/'), \
  $(eval $(patsubst %, $(line), $(nl))))
Bryan
Doesn't work for me, even after s/enddef/endef/: `GNUmakefile:5: *** missing separator. Stop.` (GNU Make 3.81)
Gilles
Curious. I just pasted it back into the makefile verbatim from this post and it works for me. Same version of make, too. Is it possible this parsing error is coming from the execution of the eval, i.e. it's an error in the output of myscript?
Bryan