Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I properly escape data for a Makefile?

I'm dynamically generating config.mk with a bash script which will be used by a Makefile. The file is constructed with:

cat > config.mk <<CFG SOMEVAR := $value_from_bash1 ANOTHER := $value_from_bash2 CFG 

How do I ensure that the generated file really contains the contents of $value_from_bash*, and not something expanded / interpreted? I probably need to escape $ to $$ and \ to \\, but are there other characters that needs to be escaped? Perhaps there is a special literal assignment I've not heard of?

Spaces seems to be troublesome too:

$ ls -1 a b a $ cat Makefile f := a b default_target:     echo "$(firstword $(wildcard ${f}))" $ make a 

If I use f := a\ b it works (using quotes like f := 'a b' did not work either, makefile just treats it as a regular character)

like image 278
Lekensteyn Avatar asked Oct 04 '11 21:10

Lekensteyn


People also ask

How do you escape a Makefile?

I just learned that if you want to use a dollar sign inside a Makefile you need to escape $ with an extra $ , so double it. Otherwise make will think that you are accessing a make variable, not a shell one.

How do you escape in Linux?

Escape characters. Escape characters are used to remove the special meaning from a single character. A non-quoted backslash, \, is used as an escape character in Bash. It preserves the literal value of the next character that follows, with the exception of newline.

What does escape data mean?

Escaping data is the process of securing output by stripping any unwanted data such as script tags, incorrectly formed HTML and other unwanted data. It therefore prevents of this data being seen or executed as code.

What is an escaped string?

Escaping a string means to reduce ambiguity in quotes (and other characters) used in that string. For instance, when you're defining a string, you typically surround it in either double quotes or single quotes: "Hello World."


2 Answers

Okay, it turned out that Makefiles need little escaping for itself, but the commands which are executed by the shell interpreter need to be escaped.

Characters which have a special meaning in Makefile and that need to be escaped are:

  • sharp (#, comment) becomes \#
  • dollar ($, begin of variable) becomes $$

Newlines cannot be inserted in a variable, but to avoid breaking the rest of the Makefile, prepend it with a backslash so the line break will be ignored.

Too bad a backslash itself cannot be escaped (\\ will still be \\ and not \ as you might expect). This makes it not possible to put a literal slash on the end of a string as it will either eat the newline or the hash of a following comment. A space can be put on the end of the line, but that'll also be put in the variable itself.

The recipe itself is interpreted as a shell command, without any fancy escaping, so you've to escape data yourself, just imagine that you're writing a shellscript and inserting the variables from other files. The strategy here would be putting the variables between single quotes and escape only ' with '\'' (close the string, insert a literal ' and start a new string). Example: mornin' all becomes 'morning'\'' all' which is equivalent to "morning' all".

The firstword+wildcard issue is caused by the fact that filenames with spaces in them are treated as separate filenames by firstword. Furthermore, wildcard expands escapes using \ so x\ y is matches as one word, x y and not two words.

like image 142
Lekensteyn Avatar answered Sep 22 '22 01:09

Lekensteyn


It seems that the full answer to this question is found nowhere on the internet, so I finally sat down and figured it out for the Windows case.

Specifically, the "Windows case" refers to file names that are valid in Windows, meaning that they do not contain the characters \, /, *, ?, ", ^, <, >, |, or line breaks. It also means \ and / are both considered valid directory separators for the purposes of Make.

An example will clear it up better than I can explain. Basically, if you are trying to match this file path:

Child\a$b  {'}(a.o#$@,&+=~`),[].c 

Then you have to write these rules:

all: Child\\a$$b\\\ \\\ {'}(a.o\#$$@,&+=~`),[].o  %.o: %.c     $(CC) '$(subst ','"'"',$(subst \,,$(subst \\,/,$+)))' 

Stare at it for a long time and it'll sort of start making some remote sense.

This works in my MSYS2 environment, so I presume it is correct.

like image 38
user541686 Avatar answered Sep 20 '22 01:09

user541686