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.
Related
I have a program parsing two files and comparing them looking for conflicts between the two and allowing the user to decide what action to take. As a result, I need to be able to parse the lines below. If a string contains { or } when using pattern replacement parameter expansion it will cause an error.
I was looking for a potential work around for the following lines
F=TSM_CLASS="Test text {class}"
newstring=${F//{class}/\\{class\\}}
Results:
echo $newstring
TSM_CLASS="Test text }/\{class\}}"
${F//{class} is a complete parameter expansion which replaces every instance of {class in F's value with empty string. To embed braces in the pattern and/or the replacement string, you need to quote them.
$ F=TSM_CLASS="Test text {class}"
$
$ echo "${F//{class\}/\\{class\\\}}"
TSM_CLASS=Test text \{class\}
How and when do I quote a string in a make file? What is best practice?
Is the following the way to quote?
$(warning $(shell ls -ld "$(CURDIR)" ) )
I'm familiar with Bash where you usually quote variables to allow for embedded spaces. Do you do such in a makefile?
How should I do assignment statements with a string?
vara := "$(CURDIR)"
varb := $(CURDIR)
varc := /home/me/source
vard := "/home/me/source"
What about the space after the equal?
You should never quote anything because of make. Make doesn't understand or parse single- or double-quote characters in any way. Every quoting character you write in a makefile will be kept as a literal quote and passed along as-is to the commands that make invokes.
So, if the shell expects and can interpret a quoted string, then you should use quotes. Where the shell doesn't expect or won't correctly interpret a quoted string, you should not use quotes.
In your examples, whether the quotes are acceptable or not depends on how those variables are used. As above, make won't do anything special with quotes, which means that vard (for example) contains the literal string "/home/me/source" (including the quotes).
If you use that value in a way where the shell will handle the quotes for you, then it's fine:
all: ; echo $(vard)
will print /home/me/source (no quotes) because the shell interprets them. But if you use the variable in a make context, for example as a target or a prerequisite:
all: $(vard)
$(vard): ; echo $#
then this is not right, because the target and prerequisite are the literal strings "/home/me/source" (including the quotes).
In general it's best to not use quotes around filenames in variables, and instead add the quotes in the recipe around the make variable. Of course if the variable contains an entire shell script, not just a filename, then you should add appropriate quoting to the script.
I'm using make in Windows and I need to manipulate some variables to create a filename. I have a base filename and an optional part to append. If the optional part is appended an underscore should be inserted between two parts. The optional part is being read in from a file and contains 5 characters (spaces or alphanumeric).
This is what I expected to work:
OPTIONAL_NAME=" "
BASE_NAME=myfile
STRIPPED_OPTIONAL_NAME=$(strip $(OPTIONAL_NAME))
ifeq ("$(STRIPPED_OPTIONAL_NAME)","")
FULL_NAME=$(BASE_NAME)
else
FULL_NAME=$(BASE_NAME)_$(STRIPPED_OPTIONAL_NAME)
endif
OPTIONAL_NAME could have the following values:
OPTIONAL_NAME=" "
OPTIONAL_NAME="ABCDE"
OPTIONAL_NAME="A "
OPTIONAL_NAME=" A "
OPTIONAL_NAME=" A"
The value of OPTIONAL_NAME has quotes around it due to the way it is being imported (see my previous question - Read a value from a file into a variable).
The problem with the result is that strip seems to reduce multiple spaces to single spaces, but not actually remove leading or trailing spaces.
I've tried using this to strip the spaces:
space:=
space+=
STRIPPED_OPTIONAL_NAME=$(subst $(space),,$(OPTIONAL_NAME))
This does remove the spaces, but I struggle to check for an empty string.
Thanks
Stephen
You should not put the variable in the comparison in quotation marks. Try this instead:
ifeq ($(STRIPPED_OPTIONAL_NAME),"")
The above checks for empty strings.
Or
ifeq ($(STRIPPED_OPTIONAL_NAME)," ")
to check for single space.
Consider the following code:
$ANIMAL = COW PIG CHICKEN VAMPIRE
all:
#echo $(ANIMAL, F, >.txt)
I strove to find a section in GNU make manual that mentions the above syntax, but I couldn't find anything related to it. What does it print and how is the syntax structured for the functionality?
Added: When a line starts with "#--" what does it mean?
#-- $(GEN_ENV); ...
To answer your addition: In regular Makefiles (read: POSIX, GNU, ...)
a leading '#' supresses echoing of the command.
a leading '-' says to ignore a non-zero exit status
both can be combined, and repetitions are okay, so #---###-#---echo foo is the same as #-echo foo
This is called "macro modifiers". This is not a GNU make feature. Take a look at this chapter of OPUS make tutorial. The general syntax of these modifiers:
$(name,modifier[,modifier]...)
name is macro expanded, then each modifier is applied in succession to the elements of the expanded value.
Take a look then at the list of modifiers and it becomes clear that it forms a list of file names (truncates paths of each variable in ANIMAL) with .txt added. So, in your case it shoud output:
COW.txt PIG.txt CHICKEN.txt VAMPIRE.txt
PS
I looked through the reference mentioned above and don't think the first line ($ANIMAL = ) is correct since macro definition should start without $.
Based on your comments it seems you are actually using OpusMake, rather than GNU make. You can find more information about it on the Opus Software, Inc. website, and also in this handy reference guide. From those sources you can see that you have an example of a macro employing macro modifiers in its expansion.
Generally speaking $(FOO) is expanded to the unmodified value of the variable FOO, while $(FOO,mod1[,mod2[,...]]]) expands to the value of FOO, modified according to the modifiers you specify. Note that you can string together any number of modifiers, and they will be applied in left-to-right order.
There's a ton of possible modifiers, but your example specifically uses two:
The F modifier, which means "use just the final path component of each pathname in the variable value"
The >str modifier, which means "append the text str to each space-separated word in the value".
Here's a quick example:
FOO=abc/def ghi/jkl
BAR=$(FOO,F)
BAZ=$(FOO,>.txt)
BOO=$(FOO,F,>.txt)
BAR will have the value def jkl (ie, just the filename portion of each path).
BAZ will have the value abc/def.txt ghi/jkl.txt (ie, append .txt to each space-separated word in the value)
BOO will have the value def.txt jkl.txt (ie, first take just the filename portion of each path, then append .txt to each)
I have inherited a shell script. One of the things it does is parsing of a list of filenames. For every filename in the list it does following command:
fs_item="`echo ${fs_item%/}`"
This command (a part from doing it's job which in this case, I think, is to remove everything after last slash) replaces spaces in filename with one space:
in: aa bbbb ccc
out: aa bbbb ccc
From this point filename is broken.
So, the question is: can I somehow tell bash not to replace spaces?
Get rid of the backticks and the echo command. It is worse than useless in this situation because it adds nothing, and causes the problem you are trying to solve here.
fs_item="${fs_item%/}"
Is the echo really necessary? You could simply remove it:
fs_item="${fs_item%/}"
If your actual problem is something different, and you cannot get rid of the echo (or some other command invocation), adding some quotes should work:
fs_item="`echo \"${fs_item%/}\"`"
The spaces vanish when running the backticked echo command. The internal field separator includes the space character, so words separated by a sequence of one or more spaces will be passed as separate arguments to echo. Then, echo just prints it's arguments separated by a single space.
Since we're on the internal field separator subject, changing the IFS should also work (but usually has other possibly undesirable effects elsewhere in your script):
IFS=$'\n'
This sets the internal field separator to the newline character. After this, the spaces are no longer considered to be separators for lists. The echo command will receive just one argument (unless you have file names with the newline character in them) and spaces will stay intact.
Try setting IFS to something else, e.g. IFS=","