Make: Variable assignment - makefile

I am facing the issue while accessing a variable from other makefile which is included.
i have test.mak which has variable LIBS32 := $(TESTLIBS)/$(NEW_PLAT32)
i have included test.mak in other makefile and trying to assign that variable in one of the target.
extlib32: EXTLIBS = $(LIBS32)
extlib64: EXTLIBS = $(LIBS64)
The expected value of EXTLIBS should be '/home/testlib/extlibs/Linux' . But here when i print EXTLIBS the value which i am seeing is '/home/testlib/extlibs/'
Note:- When i jut print LIBS i can see the content as expected. But when i assigned to EXTLIBS and try to use it.. I can see word 'Linux' is missing.
Thanks!

You set EXTLIBS as a target-specific variable for target extlib32. Such variables are non-global and their value is only available in the target recipe and target's prerequisites, rather than globally (this is why $(info $(EXTLIBS)) doesn't print the expected value).
To print its value you need to print it from the recipe of target extlib32, e.g.:
extlib32:
#echo "EXTLIBS=${EXTLIBS}"
If extlib32 and extlib64 are .PHONY targets to build something then your original target-specific assignments should propagate to the dependencies and be available in their recipes. You just cannot print its value from the global makefile scope.
To have one makefile build both in 32 and 64-bit mode (as well as release and debug) you need to structure it differently and invoke make separately for each build mode. Example:
# User can overrided capitalized variables. E.g.
# make BUILD=release MODE=32 LDLIBS=-lrt
BUILD := debug
MODE := 64
build_dir := ${BUILD}/${MODE}
ldlibs.32 := my-32-bit-libs
ldlibs.64 := my-64-bit-libs
ldlibs := ${ldlibs.${MODE}} ${LDLIBS}
all :
#echo "build_dir=${build_dir}"
#echo "ldlibs=${ldlibs}"
Usage examples:
$ make
build_dir=debug/64
ldlibs=my-64-bit-libs
$ make BUILD=release MODE=32
build_dir=release/32
ldlibs=my-32-bit-libs
Another example for debug/release compiler options.

Related

Makefile target name compared to string

In a makefile I'm trying to compare the target name with a string, and depending on this set a variable with a string or another.
This example illustrates what I'm trying to do:
ifeq ($#,"Target_A")
THE_PATH="Path_a"
THE_TARGET=$#
else
THE_PATH="Path_b"
THE_TARGET=$#
endif
Target_A:
#echo $(THE_PATH)
#echo $(THE_TARGET)
Target_B:
#echo $(THE_PATH)
#echo $(THE_TARGET)
This is the output when I call make passing Target_A and when I call it passing Target_B:
$ make Target_A
Path_b
Target_A
$ make Target_B
Path_b
Target_B
The fact that I always get "Path_b" indicates the ifeq always evaluates to false, but you can see that $# contained the right string.
Why doesn't this work?
You probably want target-specific variables:
Target_A: THE_PATH="Path_a"
Target_A:
#echo $(THE_PATH)
Since contents of a (regular) variable are expanded each time it's used, THE_TARGET=$# can be made global.
Target-specific variables are only accesible in a target they belong to, and its dependencies.
Normally this is enough, but if you need to have global variables, you can use the same code you have in the question, with the condition changed to this:
ifneq ($(filter Target_A,$(MAKECMDGOALS)),)
$# (which you tried to use) only works inside of a recipe, and expands to a target name that the recipe builds.
$(MAKECMDGOALS) is a global variable that contains all targets specified (as command-line parameters) when invoking make.
This option will only work if the target you're looking for was specified as a command-line parameter.

Make: extract the target path segment that follows a known one

I am trying to optimize our build targets. In our current process we have separate targets for 32bit & 64bit build. Due to separate targets we have hundreds of targets in our build flow. Where we try to make it global i.e. one target for both 32-bit and 64-bit build. I'm trying to achieve this with the code below.
Target/Output directory for 32-bit build should be like:
/test/scratch/<client_name>/Linux/...
Target/Output directory for 64-bit build should be like:
/test/scratch/<client_name>/Linux-64bit/...
So based on above Target directory paths I am searching the string Linux using $(findstring) function and proceed with 32-bit command to run. Else it will run 64bit command as shown below.
RELEASE_FILES := $(TARGET_DIR)/build/test/csimtime.c
$(RELEASE_FILES): $(TGTDIRFILES)/%: %
ifneq (Linux,$(findstring $(OS),$#))
$(test_lib_32)
else
$(test_lib_64)
endif
$(TARGET_DIR) variable is passed as parameter to make command
make TARGET_DIR=$(TGT32) all32
For 64-bit we will pass TARGET_DIR=$(TGT64) instead.
Note: test_lib_32/64 above are macro definition in other make file which we are including in current make file.
It works fine as expected, But I am not sure whether this is the best way? And I notice one problem here, Generally TGT32/TGT64 variable which we are passing has values either:
/test/scratch/<client_name>/Linux/ (for 32bit)
or
/test/scratch/<client_name>/Linux-64bit/... (for 64bit)
If someone creates a client with client_name 'Linux' string it wont work. It will always go to if block and try to run 32-bit command even when we run 64-bit build. How can I handle this?
I am looking for getting the string after 'client_name' in above path.. Could please help?
If your target directory name is always something like:
/test/scratch/<client_name>/Linux/...
and there is no / character and no spaces in <client-name>, you can base your test on the exact position of Linux (or Linux-64bit) in the path:
TYPE := $(word 4,$(subst /, ,$(TARGET_DIR)))
ifeq ($(TYPE),Linux)
<something>
else ifeq ($(TYPE),Linux-64bit)
<something-else>
else
$(error Unknown type: $(TYPE))
endif
EDIT if the position is not constant but you know what <client-name> is, and there is no / character and no spaces in <client-name>, you can extract the name of the directory that follows <client-name> in $(TARGET_DIR) like this:
TAIL := $(subst /, ,$(TARGET_DIR))
$(foreach w,$(TAIL),$(eval TAIL := $(wordlist 2,$(words $(TAIL)),$(TAIL)))$(if $(patsubst $(CLIENT_NAME),,$(w)),,$(eval TYPE := $(word 1,$(TAIL)))))
all:
#printf 'TYPE = $(TYPE)\n'
Demo:
$ make TARGET_DIR=/a/b/test-client/Linux/c/d CLIENT_NAME=test-client
TYPE = Linux
$ make TARGET_DIR=/a/b/c/d/test-client/Linux-64bit/e/f/g CLIENT_NAME=test-client
TYPE = Linux-64bit
Explanation:
TAIL := $(subst /, ,$(TARGET_DIR)) replaces the / characters in $(TARGET_DIR) by one space, such that it becomes a list of words (one per directory in the path), and assigns the result to variable TAIL.
$(foreach w,$(TAIL),...) evaluates ... with variable w set successively to each word of $(TAIL).
$(eval TAIL := $(wordlist 2,$(words $(TAIL)),$(TAIL))) re-assigns variable TAIL by removing the leading word.
$(if $(patsubst $(CLIENT_NAME),,$(w)),,...) evaluates ... if $(w) equals $(CLIENT_NAME).
$(eval TYPE := $(word 1,$(TAIL))) assigns the first word of $(TAIL) to variable TYPE.
There may be simpler ways to do the same...
If I understand your question correctly then you get problems with the following $(TARGET_DIR) value:
/.../Linux/.../Linux-64bit
The problem is that you are testing on the value of $#:
/.../Linux/.../Linux-64bit/some/other/file
As you can see that makes it impossible to decide which Linux in the path should be used for the decision.
I would suggest to try this instead:
# if $(TARGET_DIR) ends with /Linux -> 32-bit build
$(RELEASE_FILES): $(TGTDIRFILES)/%: %
ifneq ($(filter %/Linux,$(TARGET_DIR)),)
$(test_lib_32)
else
$(test_lib_64)
endif

Makefile set global variable in target body

I want to set a Global variable through a recipe then reference that variable in another independent recipe
The below code is an example code that sets the variable within the recipe but the variable stays with the initial value if referenced outside the recipe
ACTIVE = a
switch:
ifeq ($(ACTIVE),b)
ACTIVE=$(shell echo 'a')
else
ACTIVE=$(shell echo 'b')
endif
print:
$(info acitve = $(ACTIVE))
I know there are ways to broadcast the value of a target-specific variable to dependent targets, but that's not what I want.
You can use $(eval ...) for this, although it's almost always a bad idea. I have to assume that your real situation is much more complicated because there are many better ways to accomplish what you've actually provided in the sample makefile.
switch:
$(eval ACTIVE=$(if $(filter-out a,$(ACTIVE)),a,b))

Makefile - use the same variable twice in a single row

I have a recipe:
define postmake
ZZZ=$(strip $(1))
echo $(ZZZ) $(ZZZ)
endef
$(1) is the first argument passed to it. When the recipe is called the echo command prints only the first expansion of the variable. The second one is lost. How can I expand/use the same variable twice in the same row?
I need it for objcopy something.elf -O binary something.bin (something is the variable, in my case the second file becomes just .bin and is obviously wrong).
UPDATE: I use Boilermake as the template. Everything else besides this recipe works perfectly fine. I invoke the postmake recipe like this:
TARGET :=main.elf
TGT_POSTMAKE := ${postmake}$(TARGET)
It turns out that in Boilermake when specifying TGT_POSTMAKE only the recipe name can be defined.
Correct way:
TGT_POSTMAKE := $(postmake)
Incorrect way:
TGT_POSTMAKE := $(postmake) $(TARGET)
Then in the postmake recipe instead of arguments like $(1) simply $(TARGET) can be used.

GNU make - how to determine name + path of makefile from --print-data-base output

When you do make all --print-data-base, you get this kind of output after a given makefile is done being executed:
...
# default
LINK.o = $(CC) $(LDFLAGS) $(TARGET_ARCH)
# default
OUTPUT_OPTION = -o $#
# environment
QT_INSTALL_PLUGINS = C:/Users/mureadr/Desktop/A/HMI_FORGF/qt5binaries/plugins
# default
COMPILE.cpp = $(COMPILE.cc)
# environment
PATHEXT = .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW
# makefile (from 'Makefile', line 1)
MAKEFILE_LIST := C:/Users/mureadr/Desktop/A/ImpTarget.mk Makefile
# environment
TMP = C:/Temp
...
The current directory is simply equal to CURDIR - not shown - but the name of makefile is not so straightforward.
Looking at 6.14 Other Special Variables: MAKEFILE_LIST, it seems the answer is: look at the first entry of MAKEFILE_LIST and that's the name of the makefile. However, if you pass in a makefile to make via make all --print-data-base MAKEFILES=MyMake.mk, then the first entry will be MyMake.mk.
Therefore, the rule appears to be: look at the variable -*-command-variables-*- and determine if a makefile has been passed in. If yes, discount it from the variable MAKEFILE_LIST and the first string from the remaining entries is the name of the makefile.
QUESTION
Are there any other 'gotchas' that would insert something in var MAKEFILE_LIST in front of the real makefile?
Look for -*-command-variables-*- in the --print-data-base output and if it exists and sets the var MAKEFILES - as in make all MAKEFILES=thing.mk - then you know that whatever it is set to will appear in every MAKEFILE_LIST first i.e. before the name of the actual makefile.
When you cross-reference with
MAKEFILE_LIST := thing.mk Makefile cfg.mk
You'll know that the name of the current makefile is Makefile and everything appearing after are makefiles read in because they were included at the top.

Resources