How can I create a variable on-the-fly from makefile, the value of which would be the entire contents of another data file.
8 Answers
Assuming GNU make:
file := whatever.txt variable := $(shell cat ${file}) 5 Comments
$(file) instead of ${file} - but both work. does anyone know the difference?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
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}).VARIABLE as a promise. This is the only version which works with older versions of make. Both := and $(shell ...) are GNU extensions.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.cat rather than complex alternate rules will make your makefiles much simpler to understand and almost as portable or sometimes more portable.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
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
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
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
Makefile:1: "./MyFile.mak": No such file or directorymake -v gives GNU Make 3.81include ./MyFileHere'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)