How to set a Makefile variable from external shell call from the target

I need to read a list of the files from some text file, line by line, then translate it to the sequence of -a filename1 -a filename2 -a filename3. I've tried to do this, but looks like the syntax of the setting FILES variable is wrong in that context:

files_pack: @while read -r file; do \ FILES += "-a $$file"; \ done <$(LIST_FILE) some_util $(FILES)

4 Answers

Inspired by Anton Kochkov's answer, a simple solution which will work with any shell, but fails if filenames contain whitespace or metacharacters:

files_pack: some_util $(shell printf " -a %s" $$(cat $(FILES)))

This can be improved to handle filenames with whitespace or special characters other than apostrophes:

files_pack: some_util $(shell IFS="$$(printf '\n+')"; IFS="$${IFS%+}"; printf " -a '%s'" $$(cat $(FILES)))

The peculiar sequence of setting IFS to newline+ and then removing the + is the only Posix-compatible way I know of to set IFS to a single newline which does not involve a command-line with a physical newline. (Simple command substitution deletes trailing newlines regardless of the setting of IFS.)

With bash, this can be simplified and made more robust, using the %q printf format (and some other bashisms to simplify):

files_pack: SHELL := /bin/bash some_util $(shell IFS=$$'\n'; printf " -a %q" $$(<$(FILES)))
2

If the LIST_FILE has one file per line, and all you need to do is prepend each line with -a, then what you want is

files_pack: some_util $$(sed 's/^/-a /' $(LIST_FILE))
1

Shell assignments cannot have spaces around the =/+=.

Also $(FILES) is going to be expanded by make. You want "$${FILES[@]}" there.

I also modified your code to use an array instead of a string so that files with spaces/etc. in the name can work correctly.

files_pack: @while read -r file; do \ FILES+=(-a "$$file"); \ done <$(LIST_FILE) some_util "$${FILES[@]}"

Though, as Jens pointed out this will not with with /bin/sh on many systems and would require telling make to use /bin/bash as the shell for this target:

files_pack: SHELL := /bin/bash

or globally:

SHELL := /bin/bash
3

This works too:

FILES=$(shell cat ${LIST_FILE) FILES_OPT=$(addprefix -a,${FILES})

And this solution doesn't depend from bash, works with every generic shell, and doesn't require 'sed' to be installed.

1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

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

You Might Also Like