114

How can I create a variable on-the-fly from makefile, the value of which would be the entire contents of another data file.

0

8 Answers 8

107

Assuming GNU make:

file := whatever.txt variable := $(shell cat ${file}) 
Sign up to request clarification or add additional context in comments.

5 Comments

@ceving Use the standard GNU make. Solaris make is a non-portable application.
Gnu make is neither standard nor more portable than solaris make. "Popular" is your word. If we seriously consider portability and standard conformation, stick with posix.
I'd go with "Popular" in this case - non-GNU Make is just too primitive. If you're aiming to continue supporting 1990s operating systems, then @bb-generation's answer is probably what you need (though it will get you in trouble in many cases). In all other cases, GNU Make is either easily available or - more likely - the default, so please make use of it.
GNU make runs anywhere so yes it's much more portable than any other make. "standard" can be subjective, granted.
the braces around the file variable are new for me. is this standard make? I would use $(file) instead of ${file} - but both work. does anyone know the difference?
58

I'm guessing that you like to set a variable in your Makefile to the contents of another file:

FILE=test.txt VARIABLE=`cat $(FILE)` target: echo $(VARIABLE) 

10 Comments

Actually, I cannot put the VARIABLE definition above all: because PATH_TO_MY_DATA_FILE does not exist until some commands have been run in the all: target. As an alternative, what I have done is that I have split things up into two makefiles (created a sub-make) and declared the VARIABLE at the top of the sub-make and it now works like a charm.
This is a wrong answer. The value of VARIABLE is a string cat $(FILE) (in quotes) which only gets expanded in a recipe by the shell. Try printing the value of it like $(info ${VARIABLE}).
@MaximEgorushkin It is not wrong. You just have to consider VARIABLE as a promise. This is the only version which works with older versions of make. Both := and $(shell ...) are GNU extensions.
Downvoted. This will execute cat every time the rule is executed. This is usually not be intended and should be warned against, because it's slow. Besides, if the file is a pipe, it has unintended side-effects. The assignment should be with := so that the rule would not get evaluated when the variable is replaced but when the variable is defined. The replacement value should be $(shell cat $(FILE)) so that the cat command is executed in-place by make, not later by the recipe rules.
@antred , make is part of the standard UNIX/POSIX toolset which also includes cat. If you install make it's normal to install all the other basic tools and there are very rare circumstances where that will cause any difficulties. Relying on cat rather than complex alternate rules will make your makefiles much simpler to understand and almost as portable or sometimes more portable.
|
50

GNU make version 4.2 supports file reading operation, so with respect to Maxim Egorushkin's great answer there is an optional way to solve this problem now:

FILE := test.txt variable :=$(file < $(FILE)) 

4 Comments

Fixed an error. Variable names are case-sensitive. The names ‘foo’, ‘FOO’, and ‘Foo’ all refer to different variables. (gnu.org/software/make/manual/html_node/Using-Variables.html)
quotes are also not needed, $(file < "$(FILE)") tries to open "test.txt".
This is superior to the "shell cat" answers in the case where the file contains newlines and you want them to be preserved
This is a better solution because it is much faster!
17

cat doesn't exist on Windows. Solution that works for Linux and Windows:

cat := $(if $(filter $(OS),Windows_NT),type,cat) variable := $(shell $(cat) filename) 

Explanation: Seems like On Windows there is always OS environment variable defined to be equal to 'Windows_NT'. This way, for Windows type command is used, for non-Windows cat is used.

Comments

15

Much simpler since $(file op filename) was added:

VARIABLE = $(file < my_file.txt) 

Manual page here: https://www.gnu.org/software/make/manual/html_node/File-Function.html#index-file_002c-reading-from

6 Comments

This worked for me under GNU Make 4.2.1, unlike the other suggested solutions :)
This was introduced in GNU Make 4, and macOS still ships with 3.81 in 2019 :-(
macOS Big Sur is also using 3.81 :-(
macOS Ventura checking in (3.81) :(
Will it work on windows?
Yes. This was the first answer to this question that worked for me on Windows 11 Enterprise with make 4.4.1 installed via chocolatey.
4

If you are using GNU make, another way to get this effect is to use a make "include" of another makefile:

From Makefile:

include "./MyFile.mak" 

Create a file "MyFile.mak" with content:

FILE := "my file content" FILE += "more content 1" FILE += "more content 2" 

5 Comments

this doesn't seem to work, I get Makefile:1: "./MyFile.mak": No such file or directory
that probably means you are using a none GNU version of make.... or a really old version of GNU make... try: "make -v"
using make in mac, not sure if bundled or installed by homebrew, make -v gives GNU Make 3.81
do you make sure you have not accidently converted TAB characters into SPACE characters in the Makefile? Make is almost annoying as python in this regards.
I got this to work by on my Mac by removing the quotes, e.g.: include ./MyFile
3

As the platform has not been specified, this is the way how it works on Solaris:

VERSION:sh = cat VERSION all: echo $(VERSION) 

Comments

1

Here's a more portable solution, which works with MAKE version 3, where file directive isn't available. The downside is that it requires creating a temporary file in the process.

$(shell echo define my_variable > file.tmp) $(shell cat my_file.txt >> file.tmp) $(shell echo endef >> file.tmp) include file.tmp 

The main idea is to use define directive, which is specifically designed to declare multiline variables. Of course, you can avoid using shell and a temporary file if you can explicitly write file contents for Makefile usage.

Keep in mind that, if your file contains $ signs, MAKE will try to expand them as variables/directives when my_variable is expanded (or when assigned, of you define it with :=). If you want to avoid it, you need to escape them before including file contents. For example, instead of cat you can do this:

$(shell sed 's/\$$/$$$$/g' my_file.txt >> file.tmp) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.