开发者

Writing contents of makefile (>131000 chars) variable to a file?

开发者 https://www.devze.com 2023-04-01 08:56 出处:网络
How can I write the contents of a makefile variable to file, without invoking a shell command? The problem is that the contents of the variable is possible longer than the shell allows for a command (

How can I write the contents of a makefile variable to file, without invoking a shell command? The problem is that the contents of the variable is possible longer than the shell allows for a command (i.e. longer than MAX_ARG_STRLEN (131072) characters).

In particular, in a makefile I have a variable containing a long list of filenames to process (including their absolute pathes for out-of-source builds). Now I need to write those filenames to a (temporary) file, which I can then pass to another command.

So far, we had a rule like ($COLLATED_FILES is the variable containing the paths):

$(outdir)/collated-files.tely: $(COLLATED_FILES)
    $(LYS_TO_TELY) --name=$(outdir)/collated-files.tely --title="$(TITLE)" \
    --author="$(AUTHOR)" $^

This breaks if COLLATED_FILES is longer than about 130000 characters, we get the error message:

make[2]: execvp: /bin/sh: Argument list too long

As a solution, we are now trying to write the contents of the variable to a file and use that file in the $(LYS_TO_TELY) command. Unfortunately, I have not yet found a way to do this without invoking the shell. My attempts include:

$(outdir)/collated-files.list: $(COLLATED_FILES)开发者_JAVA百科
    echo "" > $@
    $(foreach f,$^,echo $f >> $@;)

But this also invokes all echo commands at once in a shell, so the shell command is just as long.

Is there any way to write the contents of $(COLLATED_FILES) to a file on disk without passing them on the command line to a shell command?

I also searched whether I could pipe the contents of the variable to the shell, but I couldn't find anything in that direction, either...


Assuming you are using GNU Make, there is the file function!

https://www.gnu.org/software/make/manual/html_node/File-Function.html

$(file op filename,text)

where op is either > or >>.

This requires GNU Make 4.0+


You could move whatever makefile code you use to build up the value of COLLATED_FILES to a trivial helper makefile, then invoke make recursively from your original makefile and use trivial shell redirection to capture the stdout of the recursive make invocation -- basically using make as a rudimentary text-processing tool in that context. For example, create a makefile called get_collated_files.mk with these contents:

COLLATED_FILES=abc
COLLATED_FILES+=def
COLLATED_FILES+=ghi
# ... etc ...

# Use $(info) to print the list to stdout.  If you want each filename on a 
# separate line, use this instead:
#
# $(foreach name,$(COLLATED_FILES),$(info $(name)))

$(info $(COLLATED_FILES))

all: ;@#shell no-op to quell make complaints

Then, in your original makefile:

collated-files.list:
        $(MAKE) -f get_collated_files.mk > $@

$(outdir)/collated-files.tely: collated-files.list
    $(LYS_TO_TELY) --name=$(outdir)/collated-files.tely --title="$(TITLE)" \
    --author="$(AUTHOR)" --filelist=collated-files.list

This will be quite a lot more efficient than using hundreds or thousands of individual echo invocations to append to the file one path at a time.

EDIT: One final option, if you really want to have each filename on a separate line, and you have a lot of control over how COLLATED_FILES is defined:

define NL


endef
COLLATED_FILES=abc
COLLATED_FILES+=$(NL)def
COLLATED_FILES+=$(NL)ghi
$(info $(COLLATED_FILES))
all: ;@#no-op

This approach allows you to again use just one call to $(info), if that's important to you for some reason.


Here's a patch to gnu make that lets you directly write a variable into a file. It creates a new 'writefile' function, similar to the existing 'info' function, except it takes a filename argument and writes to the file:

https://savannah.gnu.org/bugs/?35384


It looks to me as if you should rethink your build design-- surely there's a better way than letting a variable get this big. But here's a way to do it:

# Make sure this doesn't collide with any of your other targets.    
NAMES_TO_WRITE = $(addprefix write_,$(COLLATED_FILES)) 

collated-files.list: $(NAMES_TO_WRITE)

write_blank:
        echo "" > collated-files.list

.PHONY: $(NAMES_TO_WRITE)
$(NAMES_TO_WRITE) : write_% : write_blank
        echo $* >> collated-files.list
0

精彩评论

暂无评论...
验证码 换一张
取 消