How to print leading whitespace with $(info) - makefile

I want to print indented output with $(info ...)
Make ignores whitespace after info, so the following doesn't work:
$(info There is a space and then a tab before this text)
It will print:
There is a space and then a tab before this text
So right now I'm doing this (which isn't exactly what I want, but close enough):
$(info - Here there is a space, a dash, and then a tab)
It will print:
- Here there is a space, a dash, and then a tab
Is it possible to print the following?:
Is it possible to print me?
I know I can echo(1), but I've seen considerable performance loss by doing that, so I'd prefer to use $(info), even if I have to print a leading dash.

You can put something that expands as the empty string on front of your spaces:
NULL :=
$(info $(NULL) X)
The $(NULL) token plays the role of delimiter between info and the string parameter.
Instead of NULL you could even use a macro that would expand as the specified number of spaces. It would play the role of the NULL variable in the previous example, plus add the requested number of spaces.
$ cat Makefile
info-spaces = $(subst -, ,$(subst $(eval) ,,$(wordlist 1,$(1),\
$(foreach n,0 1 2 3 4 5 6 7 8 9,- - - - - - - - - -))))
.PHONY: all
N ?= 0
all:
$(info 0123456789)
$(info $(call info-spaces,$(N))X)
#:
$ make
0123456789
X
$ make N=10
0123456789
X
$ make N=5
0123456789
X
Note: you can add up to 100 spaces, no more. Modify the foreach call to increase or reduce this maximum.
Explanations:
The $(foreach ...) call creates a list of 100 words, all equal to -.
$(wordlist ...) returns the N first words where N is the parameter passed to the info-spaces macro (If N=0 it returns the empty string, and if N>100 it returns 100 words).
$(subst $(eval) ,,...) removes the spaces from the list of words, leaving one single word with min(N,100) times the - character. Here $(eval) does absolutely nothing, it's just a way to use a space as first parameter of subst. It is the same as $(NULL) in the first solution. We could probably use something else as long as it expands to the empty string.
Finally, the outermost subst substitutes each - character by one space and returns a string of min(N,100) spaces.
Note: We could also use $(shell printf '%$(1)s' "") instead of this complicated stuff but if you have performance issues spawning a new shell for each info call is probably not a good idea.

If $(info)'s text parameter is empty it will output nothing and
expand to nothing, making it usable as a zero-width character, e.g.
$(info $(info) indented text)
\t := $(info) $(info)
which outputs indented text and defines \t as a tab character.

Related

How can i find the first letter in a line[A to Z] of a file when the line is full of spaces

I have a file with 5 empty spaces and then a letter, how can i show the position of the first letter in a line?
Something like
" version"
And it should return me 5 since it starts on the 5th position
In bash you can do it relatively easily with parameter expansion that trims all non-space characters from the right, leaving only the spaces which you store in a variable and then use the length parameter expansion to get the count, e.g.
$ line=" version"; nspaces="${line%%[![:space:]]*}"; echo "${#nspaces} spaces"
5 spaces
Explanation
nspaces="${line%%[![:space:]]*}" which uses the parameter expansion form ${parameter%%word} with the inverted POSIX character-class [:space:] to trim all non-space characters from the right leaving only leading spaces;
echo "${#nspaces} spaces" simply uses the ${#parameter} form to obtain the length of spaces in nspaces.
Let me know if that is what you were looking for and if you have any questions.

Using tab character in Makefile $(info ...)

I would like to add tabs specifically to the stdout text in a makefile. I've tried placing tabs directly into the text as well as using a \t command and neither work.
I could always achieve the same with spaces but it is just annoying.
$(info test text with tabs)
tabs are completely removed on output
$(info \ttest text)
becomes : "\ttest text" on output
Is there a reasonable way to do this? I am aware I can do this with
#echo -e "\tNow the tabs work!"
However I was trying to get rid of using echos.
Thanks
edit :
I was wrong about using spaces instead -- they are just deleted on the output just like inserted tabs are.
make strips strings before using them as arguments of various commands or statements. So, let's first define a variable containing... nothing, use it to define a variable containing a tab and use that variable when needed:
$ cat Makefile
NULL :=
TAB := $(NULL)<tab>$(NULL)
all:
$(info X$(TAB)X)
$ make
X X
make: 'all' is up to date.
I used <tab> to show where the tab character must go. Use the real tab character instead, of course. Note that NULL alone is enough for what you want:
$ cat Makefile
NULL :=
all:
$(info $(NULL)<tab>X)
$ make
X
But having a TAB variable is maybe more convenient.
Note also that make variables can have very strange names. If you prefer naming the variable \t instead of TAB, you can:
$ cat Makefile
NULL :=
\t := $(NULL)<tab>$(NULL)
all:
$(info X$(\t)X)
$ make
X X
make: 'all' is up to date.
You can even define \n for end-of-line:
$ cat Makefile
NULL :=
\t := $(NULL)<tab>$(NULL)
define \n
endef
all:
$(info X$(\t)X$(\n)Y$(\t)Y)
$ make
X X
Y Y
make: 'all' is up to date.
The reason why we need two empty lines in the define-endef can be found in the GNU make manual:
The value in an ordinary assignment cannot contain a newline; but the
newlines that separate the lines of the value in a define become part
of the variable’s value (except for the final newline which precedes
the endef and is not considered part of the value).

In Makefile, ifeq seems to see a variable as combined with comments following the set value

I have this Makefile.
HFLOAT = 1 # comment
$(info $(HFLOAT))
ifeq ($(HFLOAT), 1)
$(info HFLOAT is 1)
endif
If I run 'make', it gives me
id#host:~/test] make
1
make: *** No targets. Stop.
Oh, the ifeq (..) result was a false! so "HFLOAT is 1" is not printed.
But if I remove the # comment above in the Makefile, and rut make, I get
id#host:~/test] make
1
HFLOAT is 1
make: *** No targets. Stop.
The $(info ..) printed HFLOAT value correct in both cases, but ifeq ($(HFLOAT), 1) seems to take the # comment as part of $(HFLOAT). What is wrong?
From GNU make manual:
...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...
Because of your comment, the value of HFLOAT is 1 followed by one space character. This is because the assigned value is everything right to the = sign, leading spaces stripped, and up to # (excluded). Your ifeq test thus fails. Without the comment and, more important, without the trailing space, the value of HFLOAT is 1 (just one) and the test passes.
Note that with:
HFLOAT = 1 # comment
the value of HFLOAT is 1 followed by two spaces. Note also that without the comment the trailing spaces can be difficult to spot but still there. With (use your mouse to select the line and see the trailing spaces):
HFLOAT = 1
the value of HFLOAT is also 1 followed by two spaces. When using conditionals like ifeq it is frequently wise to also use strip to remove leading and trailing spaces:
ifeq ($(strip $(HFLOAT)),1)

make subst not working as I want it to be

I am trying to use makefile "subst" function to split the text. Here is my code.
$(subst :,\nvalue:,peter:value:2)
what I want to do is that is to split from the first of occurrence of the ':'. but it also splits on the second occurrence. Can someone help me how to solve that issue. the result she be similar to below.
peter\nvalue:value:2
As the documentation states, the subst function replaces every occurrence of the original text with the new text.
If you want only the first one you'll have to get a lot more fancy, if you want to do it completely within make and you don't know anything about the text before or after the first colon. Something like this should work:
VAL := peter:value:2
NEW := $(patsubst $(firstword $(subst :, ,$(VAL)))%,$(firstword $(subst :, ,$(VAL)))\nvalue%,$(VAL))
There might be a simpler way to do it; I'll have to think about it.
If you don't mind having make invoke a shell to do this, you could do:
VAL := peter:value:2
NEW := $(shell echo "$(VAL)" | sed -e 's/:/\\nvalue:/')
I'm not sure if your \n is a literal "\n" or a newline. I'm assuming the former. This means the \n in the sed expression needs to be escaped as \\n.
Another way of doing it completely within make (without any shell invocations):
empty:=
space:= $(empty) $(empty)
VAL := peter:value:2
VL := $(subst :, ,$(VAL))
NEW := $(firstword $(VL))\nvalue:$(subst $(space),:,$(wordlist 2,$(words $(VL)),$(VL)))
Caveat: This method will give incorrect results if the original value contains whitespace.

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)
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.
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.
I don't see how that makefile can work as you say. A pattern rule cannot be the default.
You're missing a `$` in `$(wildcard ...)`, so I think you haven't posted what you're really testing.
You should escape newlines too.

Resources