8

I'm compiling a large list of files in a Makefile.

my.list : ${deps}
    rm -f $@
    $(foreach F,$^,echo "${F}" >> $@;)

but ${deps} can be large and the generated command-line could be too large for one SHELL call. Is it possible to replace ';' by a newline '\n' ?

Pierre
  • 34,472
  • 31
  • 113
  • 192
  • I'm assuming your real target is more complicated than `echo` in the loop? Because there's no reason to use `foreach` for this example at all. You can just `echo $^` directly. – Etan Reisner Apr 15 '15 at 13:23
  • 1
    @EtanReisner, won't that put everything on a single line? – Jonathan Wakely Apr 15 '15 at 13:25
  • 1
    @JonathanWakely Ah, true, `printf '%s\n' then. – Etan Reisner Apr 15 '15 at 13:29
  • @EtanReisner yes, it's complicated: I'm generating a Makefile on the fly and I've got more than 10000 files in deps. – Pierre Apr 15 '15 at 13:33
  • updated: changed title to CR->newline – Pierre Apr 15 '15 at 13:34
  • Then maybe doing it in Make isn't the right approach. N.B. you still say "carriage-return" in the question and tags. – Jonathan Wakely Apr 15 '15 at 13:36
  • Are you actually hitting command-line length limits? Or is this theoretical? Is the limiting factor the length of `DEPS` contents or the repeated command string? Have you thought about not using a single target for this and instead having individual targets per-dep (and then possibly one `my.list` target that `cat`s the individual files together)? – Etan Reisner Apr 15 '15 at 13:44
  • "Are you actually hitting command-line length limits?" no currently, but my number of ${deps} could be large (>10000). – Pierre Apr 15 '15 at 13:48
  • OK, I've solved the problem by putting the content of ${deps} in the memory of my Makefile-generator. I'd prefer a pure-makefile solution though. – Pierre Apr 15 '15 at 13:55
  • example usage: https://gist.github.com/lindenb/0bbb814a62ab68f47ca0 – Pierre Apr 15 '15 at 13:59
  • As Etan Reisner says, you need to figure out which is lower, the maximum length of the content of a `make` variable or the maximum length of a `bash` command line. This may depend on the versions used. – reinierpost Apr 15 '15 at 14:25
  • 1
    An alternative approach is to split the rules: generate one rule per, say, 1000 `${deps}` elements, and another rule to combine the results. – reinierpost Apr 15 '15 at 14:27
  • Possible duplicate of [Add a newline in Makefile 'foreach' loop](https://stackoverflow.com/questions/4788276/add-a-newline-in-makefile-foreach-loop) – imz -- Ivan Zakharyaschev Mar 03 '19 at 21:24

5 Answers5

9

As already mentioned in Jonathan Wakely's answer, the straightforward answer is

define newline


endef

Interestingly enough, for all chars except newline there is an easier way to get non-printable characters into variables with some help from the shell, e.g.:

tab := $(shell printf '\011')

It won't work here, though, because the builtin shell function replaces all newlines by spaces.

The version above is still a bit fragile though, in particular when combining with automake which will silently remove the second consecutive newline from the input file, turning newline into an empty variable. To force it to keep the newline, we can extend the snippet:

blank :=
define newline

$(blank)
endef

Finally, to actually get a separate shell command for each dependency, you need to run the generated string through eval:

define generate-rule =
my.list : $(1)
    rm -f $$@
    $(foreach F,$$^,$(newline)$(tab)echo "${F}" >> $@)

endef

$(eval $(call generate-rule,$(deps)))
Benno
  • 5,288
  • 5
  • 42
  • 60
8

You can define a variable that expands to a newline like so:

define NEWLINE

endef

Now you can use $(NEWLINE) to expand to a newline.

This won't change anything though, the foreach will still generate a single command with embedded newlines between the statements, rather than a single command with ; between the statements.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
3

Possibly the most straight-forward answer is to use:

@printf "\n"

You can also use this, as I commonly do, to show a description when each new make target executes. An example target:

.PHONY: clean-version
clean-version:    ## Reset version back to committed version number
    @printf "\n## Reset version to last committed version number ##\n\n"; \
    $(GIT) checkout $(VERSION_FILE);
t-stark
  • 31
  • 1
0

Replacing ';' by a carriage-return will produce a string of the same size, subject to the same problem.

"foreach" is simply a string expansion. If you want to execute a separate command for each item, you can use a for loop.

my.list : ${deps}
    rm -f $@
    (for F in $^; do echo $$F >> $@ ; done)
Michel Billaud
  • 1,758
  • 11
  • 14
  • 1
    Why use () to start a subshell? This will still create a single shell command that includes the whole list of `deps`, although at least it won't repeat the command for every item – Jonathan Wakely Apr 15 '15 at 13:38
  • see Etan Reisner's comment "if it is a large command line and the list in DEPS really is huge it might be possible to exceed the length limits of a single command line this way" – Pierre Apr 15 '15 at 13:39
  • This helps (barring the sub-shell bit) if the command is the limiting factor and not the contents of `DEPS` itself. – Etan Reisner Apr 15 '15 at 13:43
  • 2
    Well, I tried the command echo $(seq 1000000) which first expands to a one million word command, and executes without a problem (bash 4.2.37) Are Makefile variables subject to the same limitations ? Same for L=$(seq 1000000) echo $L so, is there a real limitation ? – Michel Billaud Apr 15 '15 at 13:43
  • The `ARG_MAX` limit applies to how long a command line you can pass from one process to another, but if the shell evaluates the arguments internally, this particular limitation does not affect that. – tripleee Jan 05 '18 at 13:25
0

Edit -- after some revisions, it looks like the only problem with my original was not due to the whitespaces, but with MAKE_O. I've fixed it in my version, but I'll mostly be removing them below.

By the way the original post was written, I'm not sure if my solution will be relevant. However, I found myself in the middle of a define already inside a foreach, and couldn't figure out how to insert a newline using any of the other answers as given.

Solution:

define BR
$(1)

endef

define MAKE_O
$(1): $(wildcard $(1:obj/%.o=src/%.cpp)); \
        $(CXX) ... -c $$< \$(call BR)-o $1 ... \
                \$(call BR)&& touch $$@
endef
$(foreach N,main.o,$(eval $(call MAKE_O,$(N))))

Desired output (compilation is truncated from auto-dependency generation, hence the touch):

> make obj/main.o
g++ ... -c src/main.cpp \
-o obj/main.o ... \
&& touch obj/main.o

I changed BR to perform the indentation but leave the end of the line up to you:

define BR
$(1)
$(1:%= )        #<remove this comment
endef
define MAKE_O
$(1): $(wildcard $(1:obj/%.o=src/%.cpp)); \
        $(CXX) ... -c $$< $(call BR,\)-o $1 ... \
                $(call BR,\)&& touch $$@
endef
$(foreach N,main.o,$(eval $(call MAKE_O,$(N))))

The markup won't help to show this, but line 2 of BR is $(1:%=_space_)_tab_ (the comment itself is not allowed.) Result:

> make -n obj/main.o
g++ obj/main.o -c \
        -o obj/main.o && \
        echo statement on new line

I used $(call BR,\) so that the newline was not parsed as an escape of the new line, and $(1:%=space)tab so that the tab is forced (many similar rules have been defined, like SPACE:=$(SPACE) $(SPACE) without a prior value.) The variable left of the whitespace must evaluate to something. To be clear, removing the whitespace before and after the call: ...lastword$(call BR,\)firstword... yields ...lastword\n\tfirstword..., or written out,

[^]...lastword \[$]
[^]$(call BR,\)firstword...[$]

...yields...

[^]...lastword \[$]
[^]        firstword...[$]

to achieve the same (using ^,$ to denote the beginning and end of the line. Someone else will probably know how to format/annotate this better.)

My syntax highlighter is decidedly unimpressed with the 'escaped' parentheses and trailing whitespace, but the result is decent.

John P
  • 1,463
  • 3
  • 19
  • 39