can't use variable in Makefile - makefile

My main Makefile call config.mk
include $(TOPDIR)/config.mk
then config.mk include some sentences like this:
ifdef CPU
sinclude $(TOPDIR)/cpu/$(CPU)/config.mk
endif
ifdef SOC
sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk
endif
I have prepared these two tree and necessary config.mks. But for "SOC", whose value is "versatile", there is a problem. If I put "versatile" directly here, it could find the file and everything is fine; but when I use $(SOC), il will meet an error, and say
/../../../cpu/arm926ejs/versatile: is a folder, stop
Anyone know what the problem is ??

Are you sure you gave the exact error message? What version of make are you using? That error doesn't look like anything GNU make would print.
Anyway, I'll bet the problem is that your assignment of the SOC variable has trailing whitespace. According to the POSIX definition of make, leading whitespace before a variable value is removed, but trailing whitespace is preserved. That means, for example, if you write your makefile like this:
SOC = versatile # this is the versatile SOC
then make will remove the comment, but keep the space, so the value will be 'versatile' (space at the end). This means when the value is expanded in the sinclude line you get:
sinclude $(TOPDIR)/cpu/$(CPU)/versatile /config.mk
which make interprets as trying to include two different values, the first of which is a directory.
Even if you don't have a comment there, any trailing whitespace will be preserved. When editing makefiles you should try to put your editor into a mode where it flags trailing whitespace, or even better removes it automatically. GNU Emacs, for example, can do this.

Related

GNU Makefile "preprocessor"?

Is there an option to output the "preprocessed" makefile, something equivalent to the GCC's -E option?
I have a project comprised of an hierarchy of dozens of modules, each with its makefile. The build is invoked from a master makefile. That master makefile contains includes, variable definitions, command line option dependent variables, etc.
So, essentially, I am looking for the processed makefile, including all substitutions.
Not that I'm aware of. The closest thing you can get to this is the output from make -qp (or similar) which will dump the make database out at you.
Part of the problem with this request is that many of the substitutions/etc. happen as targets are processed and the list of targets isn't necessarily known without actually attempting a build (at least to an extent) so it isn't necessarily possible to fully expand/etc. a makefile in-place.
The make -d output is also useful for certain incidental information related to how make has processed the makefiles but doesn't contain makefile contents directly.
Remake might also be able to provide some extra useful information.
If you are looking for the computed value of some assembled/etc. global make variable then this blog post by Eric Melski is likely to be very helpful.
tl;dr It adds a target like this to the Makefile (though there's more magic in the blog post so I suggest reading it).
print-%:
#echo '$*=$($*)'
#echo ' origin = $(origin $*)'
#echo ' flavor = $(flavor $*)'
#echo ' value = $(value $*)'
Though in personal use I replaced that first line with something more like this
#echo '$*=$(subst ','\'',$($*))'
to keep the quoting of the result correct.

What does $(#:.h=.h.d) mean in GNU make?

I'm maintaining a (horrendously complicated) Makefile, and in some recipes I saw the following:
$(#:.h=.h.d)
I have absolutely no clue as to how to interpret this, or whether there's any documentation on those characters. Obviously, Google won't work because it thinks I'm typing gibberish.
I saw a related question about #:H, but this is GNU make instead of BSD make.
This is a variable reference with a substitution: $(VAR:FROM=TO). It means the value of the variable VAR, but for each whitespace-separated word in the value, if the word ends with the suffix FROM, it is replaced by the suffix TO.
In this case, the variable is #, the filename of the target of the rule (with special handling for archive members). If the target of the rule ends with .h, then .d is added at the end.
A common file naming convention is to use .d for a list of dependencies. The file foo.h.d presumably contains dependencies for rules to compile source files that include foo.h (so, in practice, foo.d.h would contains foo.h and the headers that it includes).
By the way, this is portable syntax. There is another slightly more wordy syntax which is common (supported by both GNU and BSD make) but not POSIX: $(#:%.h=%.h.d) where the % acts as a wildcard; this syntax allows a prefix to be substituted in addition to a suffix. There is yet another syntax to do the same thing in GNU make: call the function patsubst, written $(patsubst %.h,%.h.d,$#) — it's arguably less cryptic, but because the portable syntax has existed for decades, it's commonly used even in makefiles that otherwise require GNU make.

How to comment sh scripts in Make

Make's semantics force complex sh scripts to be broken up using \. Make turns those recipes into (arbitrarily long) one-liners. Therefore, I can't use # for comments. The Make function $(info my comment goes here) works, but the comment is printed even when the recipe itself isn't invoked.
I would like to have something like rem "string" in sh.
What I have been using so far is $(call rem, my comment goes here) in Make,
which I define as:
rem = $(if,$(1))
But I'd like to not reinvent the wheel if something already exists and I just missed it. In particular, in sh I'd like something that preserves pipes, or at the very least stdout.
You can use the shell's do-nothing operator, which is :. So like:
all:
#echo "hi" ; : this is a comment ; echo "there"
Just be aware that the shell does expand these, so if you want to use special characters like quotes, backticks, etc. you should escape them from the shell if you're worried they'll cause problems.
ETA:
If you want something that won't interfere with shell behaviors like pipelines, you can't do that with any shell construct; all possible methods will cause syntax errors in the shell.
You'll have to use a make construct to force make to get rid of the text before it invokes the shell. The example you have kind of works, but not for the reason you think. If you really wanted to use a user-defined function rem then to run it you'd need to invoke call, as in:
$(call rem, my comment goes here)
What your method is doing is, since there is no function rem defined, it's looking up the variable named rem my comment goes here and that variable doesn't exist, so it expands to the empty string. You can see this for yourself by having your rem function actually invoke $(info ...) or something: it will never be printed.
So you can continue to use the $(rem ...) syntax but you might as well remove the rem = $(if,$(1)) since it's not used anyway, and will just confuse people. Or you could use something smaller like $(: my comment goes here) or whatever. Really you can put anything in there as long as it doesn't expand to a valid variable name.
I should point out that this is not necessarily future-proof; since variable names containing whitespace are not allowed anymore in make, it's possible that make will decide to handle a variable reference containing whitespace in a different way, sometime in the future.

Trailing comments after variable assignment subvert comparison

In GNU make, trailing comments appended to variable assignments prevent subsequent comparison (via ifeq) from working correctly.
Here's the Makefile...
A = a
B = b ## trailing comment
C = c
RESULT :=
ifeq "$(A)" "a"
RESULT += a
endif
ifeq "$(B)" "b"
RESULT += b
endif
ifeq "$(C)" "c"
RESULT += c
endif
rule:
#echo RESULT=\"$(RESULT)\"
#echo A=\"$(A)\"
#echo B=\"$(B)\"
#echo C=\"$(C)\"
Here's the output...
$ make
RESULT=" a c"
A="a"
B="b "
C="c"
As you can see from the displayed value of RESULT, the ifeq was affected by the presence of the comment in the assignment of B. Echoing the variable B, shows that the problem is not the comment, but the intervening space.
The obvious solution is to explicitly strip the whitespace prior to comparison like so...
ifeq "$(strip $(B))" "b"
RESULT += b
endif
However this seems error prone. Since the strip operation is not needed unless/until a comment is used, you can leave out the strip and everything will initially work just fine -- so chances are you won't always remember to add the strip. Later, if someone adds a comment when setting the variable, the Makefile no longer works as expected.
Note: There is a closely related issue, as demonstrated in this question, that trailing whitespace can break string compares even if there is no comment.
Question: Is there a more fool-proof way to deal with this issue?
This is not something particular to GNU Make; rather, make is defined by POSIX to work this way:
string1 = [string2]
The macro named string1 is defined as having the value of string2, where string2 is defined as all characters, if any, after the <equals-sign>, up to a comment character (#) or an unescaped <newline>. Any <blank> characters immediately before or after the <equals-sign> shall be ignored.
This can be construed as a feature allowing you to clearly create variables with trailing whitespace:
FOO = stuff # this macro has two trailing spaces
BAR = something else# and this one has none
though probably usually it would be clearer to reorganise the places you use $(FOO) rather than depend on it having obscure whitespace.
Probably the best way to deal with this is just to avoid it: have a convention that you do not put comments on variable definition lines (except very occasionally to make intentional whitespace explicit). Instead of writing this:
A = a # list of apples
B = b # list of bananas
C = c # list of carrots
write this:
# list of apples
A = a
# list of bananas
B = b
# list of carrots
C = c
This tends to be the style in GNU projects (see for example the bottom of this page), though I don't recall whether this is documented anywhere.
Incidentally, when examining whitespace you probably want to quote your variables in your echo command more:
rule:
#echo 'RESULT="$(RESULT)"'
In your echo RESULT=\"$(RESULT)\" version, $(RESULT) is not quoted from the shell, so tabs and multiple spaces are being misleadingly displayed as single spaces.
Here are some raw ideas that I have:
Make it a policy to always use strip with ifeq
Not using strip would be a rare exception and would require an explanation in the comments.
Don't manually set configuration variables inside of a Makefile
Find or create some other tool to do that.
maybe the POSIX shell will suffice (although I think the nuances of shell variables may be worse than those of make).
I suspect that the GNU build system (Autoconf/Automake/etc.) addresses this, but my feeling is that this is overkill for most purposes.
Use some kind of "lint" tool to find these kind of problems
I'm not aware of the existence of any such tool.
Modify GNU make to fix this problem.
Preferably minimizing the impact on existing Makefiles.
Modify the make language so that by default the trailing-spaces are stripped
Use a more modern build tool instead of GNU make
Ugly, but perhaps more foolproof. Anyone who edits this in the future might at least notice that you, perhaps, made it ugly on purpose.
A = $(strip a )##
B = $(strip b )## trailing comment
C = $(strip c )##

patsubst and dir usage

I am trying to figure out what the following two lines in a .mk file mean
include $(ROOTDIRECT)/target/$(MYSUBDIR)/defs.mk
include $(ROOTDIRECT)/target/$(dir $(patsubst %/,%,$(MYSUBDIR)))/defs.mk
For clarity let ROOTDIRECT be "/home/me" and MYSUBDIR be "platform"
The first line I guess is straight forward and includes "/home/me/target/platform/defs.mk"
The second line I dont understand and my guess from my environment is that it includes "/home/me/target/defs.mk"
Am I right/wrong and could could someone help me to understand the second line
$(patsubst %/,%,$(MYSUBDIR)) will substitute anything matching the pattern %/ by %, where % can be anything.
In other words, it will remove the trailing / of $(MYSUBDIR).
See GNU Make Manual 8.2 Functions for String Substitution and Analysis

Resources