Makefile: difference between := and ::= assignment operators - makefile

Gnu Makefile shows two symbols for immediate expansion, as follows:
immediate := immediate
immediate ::= immediate
Is there a difference between these two?
reference: gnu makefile manual section 3.7

From section 6.2:
Simply expanded variables are defined by lines using ‘:=’ or ‘::=’
(see Setting Variables). Both forms are equivalent in GNU make;
however only the ‘::=’ form is described by the POSIX standard
(support for ‘::=’ was added to the POSIX standard in 2012, so older
versions of make won’t accept this form either).

Related

Can we use NMAKE.EXE as a substitute for GNU Make?

I was curious to see if I can compile this repo, with the Microsoft Program Maintenance Utility (NMAKE.EXE) (location) where I got the U1001 error:
makefile(4) : fatal error U1001: syntax error : illegal character '.' in macro
Stop.
so I am wondering if nmake.exe is by any means a substitute for GNU Make?
my questions are:
Can we have cross-compatible Makefiles by using a subset of shared syntax between the two?
Can we use some sort of NMake or GNU Make macros to have compatible MakeFiles?
What are the major syntax differences between NMake and GNU Make?
P.S. It seems that this discussion is very relevant to my question, if not a duplicate.
No, you cannot use nmake as a replacement for GNU make.
If your GNU make makefile is written strictly to POSIX specification and doesn't use any GNU-specific enhancements, then you could use any POSIX-conforming instance of make with that makefile.
However, (a) it's unlikely the makefile is really POSIX-conforming, and (b) nmake (as far as I'm aware) isn't a POSIX-conforming implementation of make anyway.

Universally escape dollar sign

I'm working on embedded project, which consists of my own code as well as 3rd party libraries and executables. To build all the parts consistently, I've written a script, which sets environment variables for cross-compilation (CC, CXX, CFLAGS, etc.). Among others it sets LDFLAGS to pass the rpath flag to linker. The rpath value contains $ORIGIN token, which must not be expanded and must be seen by linker and written to output binary as is. I then build several needed 3rd party projects using the environment set by the script. The projects uses different build systems (make, CMake, others maybe). Because of this and maybe because of the build scripts written in different ways, the dollar sign is expanded differently. I.e., whatever escaping I try, I get different results in different projects (e.g., $$ORIGIN, RIGIN, empty string), but never I managed to get the same $ORIGIN value in all the binaries. Is there a universal way to escape dollar sign so that it will work the same in at least make and shell, but in any combination?
This is how I've finally solved this problem.
In addition to the previous environment variables, needed to build for my platform, I've added two more:
ORIGIN=$ORIGIN
O=$$O
The former is to workaround shell expansion, and the latter is to workaround makefile expansion. With this fix, variables are resolved to themselves.
Yes, this does not look like an ideal solution, looks more like a hack, but it works so far allowing me to avoid adapting my build environment for every third party project I use.
I've hit the same expansion problem, and here is the adapted version for a bash script.
LDFLAGS="-Wl,-rpath=\$ORIGIN"
LDFLAGS="-lfoo $LDFLAGS"
LDFLAGS="-L. $LDFLAGS"
echo $LDFLAGS
# -L. -lfoo -Wl,-rpath=$ORIGIN # <== correct ORIGIN
ORIGIN='$ORIGIN'
eval echo $LDFLAGS
# -L. -lfoo -Wl,-rpath=$ORIGIN # <== correct ORIGIN

The make on FreeBSD doesn't support "ifdef" directives

My FreeBSD is 11.0, and find the make can't process the ifdef directives. For example:
ifdef VERBOSE
Q :=
else
Q := #
endif
The make will complain:
make: "/root/Project/powermon/Makefile" line 13: Need an operator
make: "/root/Project/powermon/Makefile" line 15: Need an operator
make: "/root/Project/powermon/Makefile" line 17: Need an operator
My current solution is using gmake instead. So does any make port on FreeBSD support processing ifdef?
BSD make uses different syntax and has different features than GNU make. The snippet you show should look like the following for BSD make:
.ifdef VERBOSE
Q :=
.else
Q := #
.endif
You have basically three options:
If your software targets specifically BSD, write your Makefile in BSD make syntax. man make(1) has the complete manual for FreeBSD's make.
Write a portable Makefile. This would only use the most basic features of make that every known make tool implements (e.g. don't use any pattern rules etc). This can be tedious and there are other tools helping to manage this by generating the Makefile like cmake or the GNU autotools.
write a GNU make Makefile (might be a good idea to name it GNUmakefile, so it is never interpreted by any other make than GNU make) and rely on the fact that GNU make is available nearly everywhere. For FreeBSD, this would mean installing the appropriate port.
If you go with the third option, you can add a "wrapper" Makefile like e.g. this:
GNUMAKE?= gmake
all:
${GNUMAKE} $#
.DEFAULT:
${GNUMAKE} $#
.PHONY: all
Typing make on BSD will cause the BSD make to read this file and accordingly call gmake. On a GNU system (where make is GNU make), this file will be ignored when there is a GNUmakefile -- GNU make prefers this over just Makefile.
A more portable way to write something like that is:
Q$(VERBOSE) := #

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.

Computing Makefile variable on assignment

In a Makefile, I'm trying to assign the result of a shell command to a variable:
TMP=`mktemp -d /tmp/.XXXXX`
all:
echo $(TMP)
echo $(TMP)
but
$ make Makefile all
is echoing 2 different values, eg:
/tmp/.gLpm1T
/tmp/.aR4cDi
What is the syntax for mktemp to be computed on variable assignment?
Thank you.
It depends on the flavour of make. With GNU Make, you can use := instead of = as in
TMP:=$(shell mktemp -d /tmp/.XXXXX)
Edit As pointed out by Novelocrat, the = assignment differs from := assignment in that values assigned using = will be evaluated during substitution (and thus, each time, the variable is used), whereas := assigned variables will have their values evaluated only once (during assignment), and hence, the values are fixed after that. See the documentation of GNU Make for a more detailed explanation.
In order for the value to be truly constant after assignment, though, it should not contain any parts, which might be special to the shell (which make calls in order to actually run the update rules, etc.) In particular, backticks are best avoided. Instead, use GNU make's built-in shell function and similar to achieve your goals.
If you’re using GNU Make, instead of using backticks, use $(shell ...). For example,
TMP=$(shell mktemp -d /tmp/.XXXXX)

Resources