How to handle filenames with equals signs in GNU Make? - makefile

I am trying to automate the processing of files that sometimes contain an "equal sign" with GNU Make. For example, say the filename is called hello=world.txt and the Makefile is the following:
default: hello=world.txt.gz
hello=world.txt.gz : hello=world.txt
gzip hello=world.txt
echo done
You get the following error:
test.make:5: *** commands commence before first target. Stop.
How can you escape the equal signs? I have tried backslashes, double quotes and single quotes. Any ideas?

This is one solution:
equal := =
default: hello$(equal)world.txt.gz
hello$(equal)world.txt.gz : hello$(equal)world.txt
gzip hello$(equal)world.txt
echo done

Use %=
in makefiles special character must be escaped by %

Related

bash: Unable to run build commands via bash scripts

I am trying to run 'make' command from my bash script to build the code.
I can see that all parameters got assigned and able to display the command that i am trying to run. I could not see any issue with the command. But the issue is when it tries to run the command via bash script it fails.
My command is :- ./build_script LIC=1 DOCUMNETS=1 PROJECTS="cuda bfm"
Script Snippet of parsing all the arguments and constructing make command:-
makeargs=""
for build_flag do
if [[ "$build_flag " = "PROJECTS="* ]]; then
apps =`echo $build_flag |sed "s/PROJECTS=//" `
makeargs="$makeargs PROJECTS=\"$apps \""
else
makeargs="$makeargs $build_flag"
fi
done
echo "make DCOV=1 $makeargs $maketest"
make DCOV=1 $makeargs $maketest
When i run the script, I can see the build command has constructed properly.
Output :-
make DCOV=1 LIC=1 DOCUMNETS=1 PROJECTS="cuda bfm" run_all
GNUmakefile:16: warning: overriding commands for target `/'
GNUmakefile:19: warning: ignoring old commands for target `/'
make: *** No rule to make target `bfm"'. Stop.
I try to print PROJECTS variable in my 'GNUmakefile' present in build_main folder. I can see the output : PROJECTS is "bfm . It is not taking whole "cuda bfm" as a whole string.
Note:- When i try to run the same build command :- make DCOV=1 LIC=1 DOCUMNETS=1 PROJECTS="cuda bfm" run_all explicitly it works fine.
Seems like issue with Interpreting variables with makefile.
Any solution for this ? Please help.
Thanks!
Change makeargs string to array before passing it as an arguments group.
eval makeargs_array=( $makeargs )
make UVC=1 "${makeargs_array[#]}" $maketest
Without converting to array, if you enable debug, it shows last line interpretation as
make DCOV=1 LIC=1 DOCUMNETS=1 'PROJECTS="cuda' bfm '"'
Which is clearly ignoring double-quote and considering space as separator.
Even double-quote is getting passed as a separate argument in this case.
Explanation:
Word-splitting
It says,
The shell scans the results of parameter expansion, command
substitution, and arithmetic expansion that did not occur within
double quotes for word splitting.
If we use "$makeargs" i.e. surrounded by double-quote, it is not considered by word-splitting and results in LIC=1 DOCUMNETS=1 "PROJECTS=cuda bfm"
But again its a complete string, while requirement is to split the string to use as arguments.
So now using $makeargs.
Word-splitting gets in action as per the default IFS (space, tab, newline), we get result as LIC=1 DOCUMNETS=1 PROJECTS="cuda bfm "
Double-quoted part of string didn't affect the word-splitting since, subject to splitting is complete string here.
Why array worked here?
Array itself expands each element as separate word when using # and here no further word-splitting requires after expansion.
Arrays

I am trying to define a string X="either 'this|that'" but GNU make won't accept it

I am trying to write a Makefile for GNU make. I can't figure out what the problem is here:
foo := this|works
bar := "I lost my 'single quotes'"
baz := 'make|will|not|accept|this|with|the|single|quotes'
whatIWant := "A string with its 'single|quoted|regex|alternatives'"
this-almost-works: #But the single quotes are lost.
#printf '%s ' "$(whatIWant)"
this-fails-horribly:
#printf '$(whatIWant)'
I get the following error message
/bin/sh: 1: quoted: not found
/bin/sh: 1: /bin/sh: 1: regex: not foundalternatives": not found
blah blah Error 127
Why is it trying to run parts of this string in the shell?
How can I define a variable to contain exactly the contents of whatIWant?
Might be worth looking in detail at the expansion.
When defining variables,
just about the only character that has an effect is $.
Everything else is taken literally.
It's worth nothing that white space around the assignment operator (= or :=) is ignored.
foo := this|works
foo is assigned the literal text this|works.
Similarly,
baz := 'make|will|not|accept|this|with|the|single|quotes'
assigns the literal text 'make|will|not|accept|this|with|the|single|quotes' to baz.
Fine and dandy.
Now, when make decides to build this-fails-horribly
(possibly because you said to the shell make this-fails-horribly)
it expands the block of commands before doing anything.
Not unreasonably,
$(whatIWant) is replaced by "A string with its 'single|quoted|regex|alternatives'".
Again, fine and dandy.
What is left is passed verbatim, one line at a time, to the shell.
The shell sees
printf '"A string with its 'single|quoted|regex|alternatives'"'
(which make would have helpfully echoed to you if you had left off the # prefix).
Now we are in the land of shell quoting.
The printf command is passed one parameter: "A string with its single:
'"A string with its ' is a single quoted string. The shell strips the 's and is left with the text "A string with its.
single has no metacharacters in it, so the shell leaves this alone.
The output is piped to the quoted command
The output is piped to the regex command
The output is piped to the alternatives" command
The shell sees the single quoted string '=', strips the quotes leaving you with a literal = which it appends to the word alternatives
No syntax error.
When the shell attempts to set up the pipeline it looks for the alternatives" command.
It doesn't find one in the directories it its $PATH, so it stops with the message /bin/sh: 1: /bin/sh: 1: regex: not foundalternatives": not found.
One possible encoding:
.PHONY: this-workes-nicely
this-workes-nicely:
echo $(whatIWant)
though you'll probably find it's cleaner to leave the quotes outside the variable definition in the first place.

How I can add Quotes " " to Makefile variable using substitute method

I have a Makefile variable as follows
L_PATH_INCLUDE = c:\dir1\include C:\dir2\include C:\dir3\include
I need to handover this to other tool which need this as
tool.exe add-include-dir "c:\dir1\include" "C:\dir2\include" "C:\dir3\include"
I tried following way but tool reporting error that there should not be variable with single character and I have used $(space)
$(subst $(space)," ",$(strip $(L_PATH_INCLUDE)))
Could it be possible in any other way without using $(space) or single character variable
There is no problem with placing quotes, it is not an interpreted symbol. But you need to place quotes around all the paths in L_PATH_INCLUDE and to achieve this I'd do that :
L_PATH_INCLUDE = c:\dir1\include C:\dir2\include C:\dir3\include
all:
echo $(addprefix ",$(addsuffix ",$(L_PATH_INCLUDE)))
Which outputs this with make all :
echo "c:\dir1\include" "C:\dir2\include" "C:\dir3\include"
c:\dir1\include C:\dir2\include C:\dir3\include
You can just replace echo with your command and it'll do it.
Basically the idea is to use addprefix and addsuffix to add quotes at the beginning and at the end of each word in L_PATH_INCLUDE.
If you copy/paste my answer in your Makefile, please be aware of replacing the four spaces by a proper tabulation.
L_PATH_INCLUDE = c:\dir1\include C:\dir2\include C:\dir3\include
L_PATH_INCLUDE_QUOTED := $(patsubst %,"%",$(L_PATH_INCLUDE))
$(info L_PATH_INCLUDE_QUOTED=$(L_PATH_INCLUDE_QUOTED))

How to break a string across lines in a makefile without spaces?

In a makefile, escaping a new-line with \ allows to split a single-line long string content across multiple source lines. However, the new-line is replaced with a space. Is there a transparent line break in the source that does not affect the string content?
VAR=w\
o\
r\
d
all:
echo $(VAR)
The desired output is 'word', but the actual output is 'w o r d'.
The simplest solution is to use $\<newline> to split the
line (at least if you are using GNU Make):
VAR = w$\
o$\
r$\
d
all:
echo $(VAR)
The output will be "word" with no spaces. This is because GNU
Make will replace backslash-newline-whitespace with a single
space, making the assignment to VAR be equivalent to:
VAR = w$ o$ r$ d
From
https://www.gnu.org/software/make/manual/html_node/Reference.html#Reference:
"A dollar sign followed by a character other than a dollar sign,
open-parenthesis or open-brace treats that single character as
the variable name." So the $<space> pairs are expansions of
the variable whose name is a single space character. Since this
variable is not defined by default, it will expand to the empty
string.
Note that the variable VAR will still contain the
$<space> pairs until it is expanded. Most of the time, this
doesn't matter, but if your makefile depends on using
$(value VAR) to process the underlying (unexpanded) value,
the above technique may provide surprising results.
Also, the recently released GNU Make 4.3 now explicitly documents this
technique for splitting lines (https://www.gnu.org/software/make/manual/make.html#Splitting-Lines):
Splitting Without Adding Whitespace
If you need to split a line but do not want any whitespace added, you can utilize a subtle trick: replace your backslash/newline pairs with the three characters dollar sign/backslash/newline:
var := one$\
word
After make removes the backslash/newline and condenses the following line into a single space, this is equivalent to:
var := one$ word
Then make will perform variable expansion. The variable reference ‘$ ’ refers to a variable with the one-character name “ ” (space) which does not exist, and so expands to the empty string, giving a final assignment which is the equivalent of:
var := oneword
For other ideas, see my answer to a similar question here:
How can I break a variable definition across multiple lines in a Makefile without spaces?
A longer treatment of line continuation options can be found in
my article "GNU Make line continuations":
http://drmikehenry.com/gnu-make-line-continuations/
This was just asked yesterday: How can I break a variable definition across multiple lines in a Makefile without spaces?
The short answer is no, there's no way to do that. This behavior is required by the POSIX standard for make.
All you can do is try postprocessing the string to remove the whitespaces using $(subst ...) or similar.

make exits with error due to a special character in the filename

We're generating make files for our source files and one of these files happens to have a '#' in its name.
The dependency statement in the make file looks like this:
./obj/abc/def#ghi.o: ./src/abc/def#ghi.pli
...
Which results in error:
Zeile 15: make: 1254-055 Abhängigkeitszeilen erfordern einen Doppelpunkt oder doppelten Doppelpunkt als Operator.
I tried to escape the '#' but neither single quotes, double quotes, one backslash or 2 backslashes work. Is there a way to fix this without renaming the file?
I'm afraid you're out of luck, especially with the AIX version of make.
The AIX Make documentation is here, and doesn't mention any way of escaping the # character.
The GNU Make documentation here mentions that it's possible:
If you want a literal #, escape it with a backslash (e.g., \#).
Comments may appear on any line in the makefile, although they
are treated specially in certain situations.
Is there any reason that you can't build GNU Make for AIX and use it instead of the AIX make?

Resources