unwanted space in Makefile - makefile

This will work
PREFIX=/home/tim/program_files/
# PREFIX=/opt/
PATH=$(PREFIX)mylib/
This will not work
PREFIX=/home/tim/program_files/ # PREFIX=/opt/
PATH=$(PREFIX)mylib/
because there is an unwanted space between $(PREFIX) and mylib/.
What is the related syntax rule of Makefile? Thanks.

I can't point to a syntax rule as such but this detail is specifically called out in the makefile in the `The Two Flavors of Variables section.
You can also use them to introduce controlled leading whitespace into variable values. Leading whitespace characters are discarded from your input before substitution of variable references and function calls; this means you can include leading spaces in a variable value by protecting them with variable references, like this:
nullstring :=
space := $(nullstring) # end of the line
Here the value of the variable space is precisely one space. The comment ‘# end of the line’ is included here just for clarity. Since trailing space characters are not stripped from variable values, just a space at the end of the line would have the same effect (but be rather hard to read). If you put whitespace at the end of a variable value, it is a good idea to put a comment like that at the end of the line to make your intent clear. Conversely, if you do not want any whitespace characters at the end of your variable value, you must remember not to put a random comment on the end of the line after some whitespace, such as this:
dir := /foo/bar # directory to put the frobs in
< Here the value of the variable dir is ‘/foo/bar ’ (with four trailing spaces), which was probably not the intention. (Imagine something like ‘$(dir)/file’ with this definition!)

Related

How to encode a string with a space at the end in a protected manner in a makefile?

Suppose I define a runners.mk file:
run=./
valgrind=valgrind --this --or --that-option
And intentionally put a space at the end of the valgrind line.
The idea is to use any "runner" in the same way:
valgrind-foo-bar-executable: foo-bar-executable
$(valgrind)$<
run-foo-bar-executable: foo-bar-executable
$(run)$<
So that I can eventually abstract everything into a runner pattern with matching (once I get a few more examples).
However, this is a public repo and it is standard practice to clear out all blanks at the end of lines.
In order to do this pattern, it would be ideal to put some kind of protected blank at the end of the valgrind command. However, in a makefile quotes are interpreted literally.
Is there some way to protect this suffixed blank in the makefile?
You don't even have to make a variable for this. In make, all trailing spaces are preserved automatically. So, just adding a comment is sufficient:
valgrind = valgrind --this --or --that-option # Leave one blank space
But, this is really kind asking for confusion IMO. There's no reason you can't include the space in the recipe like this:
run-it: foo-bar-executable
$(valgrind) ./$<
then valgrind can be empty or not.
You can add an empty var on the end of the valgrind line to "protect" the space before it:
empty=
run=./
valgrind=valgrind --this --or --that-option $(empty)
Now the space between --that-option and $(empty) will be clearly visible and not removed as a trailing space.

Simple bash function to find/replace string variable (no files)

I simply want a function (or just a 1-liner) to find/replace a string inside a variable, and not worry if the variables contain crazy characters.
Pseudo-code:
findReplace () {
#what goes here?
}
myLongVar="some long \crazy/ text my_placeholder bla"
replace="my_placeholder"
replaceWith="I like hamburgers/fries"
myFinalVar=$(findReplace $myLongVar $replace $replaceWith)
All similar questions seem complicated and use files
You can define the function like this:
findReplace1() {
printf "%s" "${1/"$2"/$3}"
}
And then run it like this:
myFinalVar=$(findReplace "$myLongVar" "$replace" "$replaceWith")
Note the double-quotes -- they're very important, because without them bash will split the variables' values into separate words (e.g. "some long \crazy/ text..." -> "some" "long" "\crazy/" "text...") and also try to expand anything that looks like a wildcard into a list of matching filenames. It's ok to leave them off on the right side of an assignment (myFinalVar=...), but that's one of the few places where it's ok. Also, note that within the function I put double-quotes around $2 -- in that case again it's to keep it from being treated as a wildcard pattern, but here it'd a string-match wildcard rather than filenames. Oh, and I used printf "%s" instead of echo because some versions of echo do weird things with strings that contain backslashes and/or start with "-".
And, of course, you can just skip the function and do the replacement directly:
myFinalVar=${myLongVar/"$replace"/$replaceWith}
Try:
myFinalVar=${myLongVar/$replace/$replaceWith}
If your want to replace all occurrences of $replace, not just the first, use:
myFinalVar=${myLongVar//$replace/$replaceWith}
Documentation
From man bash:
${parameter/pattern/string}
Pattern substitution. The pattern is expanded to produce a pattern
just as in pathname expansion. Parameter is expanded and the longest
match of pattern against its value is replaced with
string. If pattern begins with /, all matches of pattern are
replaced with string. Normally only the first match is replaced. If
pattern begins with #, it must match at the beginning of
the expanded value of parameter. If pattern begins with %, it must
match at the end of the expanded value of parameter. If string is
null, matches of pattern are deleted and the / following pattern may
be omitted. If the nocasematch shell option is enabled, the match is
performed without regard to the case of alphabetic
characters. If parameter is # or *, the substitution operation is
applied to each positional parameter in turn, and the
expansion is the resultant list. If parameter is an array variable
subscripted with # or *, the substitution operation is applied to each
member of the array in turn, and the expansion is the
resultant list.

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.

Strip all spaces from a variable and append to another variable if not blank

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.

Makefile syntax $(A,B,C)?

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)

Resources