Recursive calling make file with variable assignment not working - makefile

I am working on few modifications in GNUmakefile of our build system.
Right now we have some part of processing in shell script where we wanted to move that part of code to GNUmakefile.
Shell snippet :-
args="TEST=1 PROJECTS=\"\""
make $args test_target
Note:- PROJECTS variable should be defined and it's empty.
Now we wanted to move that code (running make) in GNUmakefile where i am trying to call the make itself.
GNUmakefile snippet
ifdef PROJECTS
DIRS := ...
endif
....
$(MAKE) DISABLE_TEST=1 PROJECTS=""
I want to do some processing inside if condition above. But while running make i notice that its not even going inside if check. When i was calling via shell script , it is working fine as expected. Can you please help how we can resolve this issue?

When you use
Ifdef PROJECTS
DIRS := ...
endif
DIRS := ... is run only if PROJECTS has non-empty value.
When you run shell script :
args="TEST=1 PROJECTS=\"\""
make $args test_target
PROJECTS is not empty but contains ""

Related

Basename of xml files in Makefile

Here is a simplified use case of my Makefile. What I want to do is use the names of these files and create the respective .d and .hpp files. However, for some reason, I am unable to do it.
XML_DATA_FILES := a.xml b.xml c.xml d.xml
build:
#for var in $(XML_DATA_FILES); do \
echo "$(basename $$var)";\
$(eval var1 = $(basename $$var)) \
echo "$(join var1,.hpp)"; \
echo "$(join var1,.d)"; \
done
The output that I get when I run make is as follows
a.xml
var1.hpp
var1.d
b.xml
var1.hpp
var1.d
c.xml
var1.hpp
var1.d
d.xml
var1.hpp
var1.d
But what I want is a.d, a.hpp and so on for all the four xml files input.
I have already referred to this question and GNU Manual but it hasnt helped so far.How can I achieve this?
There're a number of problems here :). But, fundamentally you cannot combine make functions like basename and eval inside a shell loop like for and expect it to do anything useful. Make always expands the entire recipe for all make variables and function FIRST, then it passes the entire expanded string to the shell to run, then it waits for the shell to finish.
Consider: how would the shell, running its for loop, communicate the current value of var back up to make each time through the shell's loop so that make could run the proper functions etc.? It's just not possible.
You need to write your entire loop using only shell constructs, plus simple make variables that have the same value throughout the recipe.
However, this is useless as a makefile since you just have one target that does everything. Why not just write a shell script? There's no point to using make for this. If you want to write it the make way, you'll need to declare the targets and prerequisites and create pattern rules, like this:
XML_DATA_FILES := a.xml b.xml c.xml d.xml
OUTPUTS := $(foreach X,$(XML_DATA_FILES:.xml=),$X.d $X.hpp)
build: $(OUTPUTS)
%.d %.hpp: %.xml
echo "$*.d $*.hpp"
Of course since you don't say exactly what the real commands do I can't be sure this is correct; if you actually have two different commands, one that builds the .d file and one that builds the .hpp file, you should create two different pattern rules.

Using Condition statements with Makefile targets

Hello i'm trying to use condition statements in my makefile to have it execute different make targets, but it skips over the condition and goes right to the else.
He's a general example of what i'm trying to do
ifdef ($(RUN_TEST))
all: install run uninstall
else
all: install uninstall
endif
You did not indicate on what system you are running the Makefile, as there are slightly differing make programs available. Make can be run on linux and windows and comes in different variants.
However, I have worked with most variants, and there is a common way you can resolve your problem. You have to realise that the makefile is not a program executed in the conventional sequential manner. It is a series of declarations or definitions of actions to be performed at some future time. You cannot read through it in a sequential manner like a program. In particular the definitions of dependencies are not executed and cannot be embedded within statements, even pre-processed statements.
The best way to achieve what you want it to put the dependencies in a variable and set that variable conditionally, like this:
if ($(RUN_TEST))
ALL=install run uninstall
else
ALL=install uninstall
endif
all : $(ALL)
This should work on most implementations.
The GNU make ifdef operation takes the name of a variable to test; you are providing it the expansion of a variable. You want to write it like this:
ifdef RUN_TEST
ALL = install run uninstall
else
ALL = install uninstall
endif
By using $(RUN_TEST) you're actually testing a variable named by the expansion of RUN_TEST. So for example if RUN_TEST is set to true, then ifdef $(RUN_TEST) actually tests to see if the variable true is defined or not.

Changing value of a variable according to a condition inside a target in Makefile

In a makefile which I have ,I want to assign value to a variable based on a condition.
I have:
CMAKE=cmake ../
I tried doing:
if test condition;
then $(eval CMAKE := $(cmake -DCMAKE_BUILD_TYPE=Release ../));\
fi;
But this does not work.Is there any other way to do this?
P.S The error which is getting reported is :
Syntax error: ";" unexpected
When I removed ";" ,it showed another error :
Syntax error: "fi" unexpected
This kind of "back and forth" between the shell and make is not possible. It's important to understand the relationship between them: make does not implement a shell parser: it just runs the shell for that. All make gets back from the shell is a single exit code that determines whether the command succeeded or not.
Make runs a recipe by first expanding all the variables and functions in the recipe, then passing the resulting command to the shell to be invoked.
Thus it should be clear why your example doesn't work.
Before we can give you good advice we'd need higher-level information about exactly what you're trying to do and why. One way to do what you want is to use $(shell ...) to compute the conditional, all outside of any rule:
ifeq ($(shell test condition && echo true),true)
CMAKE := cmake -DCMAKE_BUILD_TYPE=Release ../
endif
However, this looks pretty strange to me. If you described what you really want to do we can probably give you better help.
ETA:
Based on your description below, the best option is target-specific variables:
CMAKE_BUILD_FLAG = -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE)
debug: CMAKE_BUILD_TYPE = Debug
release: CMAKE_BUILD_TYPE = Release
setup: CMAKE_BUILD_FLAG =
debug release setup: all
all:
cmake $(CMAKE_BUILD_FLAG) ../

How to include makefiles dynamically?

Is it possible to include Makefiles dynamically? For example depending on some environment variable? I have the following Makefiles:
makefile
app1.1.mak
app1.2.mak
And there is an environment variable APP_VER which could be set to 1.1.0.1, 1.1.0.2, 1.2.0.1, 1.2.0.2.
But there will be only two different makefiles for 1.1 and 1.2 lines.
I have tried to write the following Makefile:
MAK_VER=$$(echo $(APP_VER) | sed -e 's/^\([0-9]*\.[0-9]*\).*$$/\1/')
include makefile$(MAK_VER).mak
all: PROD
echo MAK_VER=$(MAK_VER)
But it does not work:
$ make all
"makefile$(echo", line 0: make: Cannot open makefile$(echo
make: Fatal errors encountered -- cannot continue.
UPDATE:
As far as I understand make includes files before it calculates macros.
That's why it tries to execute the following statement
include makefile.mak
instead of
include makefile1.1.mak
You have two problems: your method of obtaining the version is too complicated, and your include line has a flaw. Try this:
include app$(APP_VER).mak
If APP_VER is an environmental variable, then this will work. If you also want to include the makefile called makefile (that is, if makefile is not the one we're writing), then try this:
include makefile app$(APP_VER).mak
Please note that this is considered a bad idea. If the makefile depends on environmental variables, it will work for some users and not others, which is considered bad behavior.
EDIT:
This should do it:
MAK_VER := $(subst ., ,$(APP_VER))
MAK_VER := $(word 1, $(MAK_VER)).$(word 2, $(MAK_VER))
include makefile app$(MAK_VER).mak
Try this:
MAK_VER=$(shell echo $(APP_VER) | sed -e 's/^\([0-9]*\.[0-9]*\).*$$/\1/')
MAK_FILE=makefile$(MAK_VER).mak
include $(MAK_FILE)
all:
echo $(MAK_VER)
echo $(MAK_FILE)
Modifying the outline solution
Have four makefiles:
makefile
app1.1.mak
app1.2.mak
appdummy.mak
The app.dummy.mak makefile can be empty - a symlink to /dev/null if you like. Both app.1.1.mak and app.1.2.mak are unchanged from their current content.
The main makefile changes a little:
MAK_VER = dummy
include makefile$(MAK_VER).mak
dummy:
${MAKE} MAK_VER=$$(echo $(APP_VER) | sed -e 's/^\([0-9]*\.[0-9]*\).*$$/\1/') all
all: PROD
...as now...
If you type make, it will read the (empty) dummy makefile, and then try to build the dummy target because it appears first. To build the dummy target, it will run make again, with APP_VER=1.1 or APP_VER=1.2 on the command line:
make APP_VER=1.1 all
Macros set on the command line cannot be changed within the makefile, so this overrides the line in the makefile. The second invocation of make, therefore, will read the correct version-specific makefile, and then build all.
This technique has limitations, most noticeably that it is fiddly to arrange for each and every target to be treated like this. There are ways around it, but usually not worth it.
Project organization
More seriously, I think you need to review what you're doing altogether. You are, presumably, using a version control system (VCS) to manage the source code. Also, presumably, there are some (significant) differences between the version 1.1 and 1.2 source code. So, to be able to do a build for version 1.1, you have to switch from the version 1.1 maintenance branch to the version 1.2 development branch, or something along those lines. So, why isn't the makefile just versioned for 1.1 or 1.2? If you switch between versions, you need to clean out all the derived files (object files, libraries, executables, etc) that may have been built with the wrong source. You have to change the source code over. So why not change the makefile too?
A build script to invoke make
I also observe that since you have the environment variable APP_VER driving your process, that you can finesse the problem by requiring a standardized 'make invoker' that sorts out the APP_VER value and invokes make correctly. Imagine that the script is called build:
#!/bin/sh
: ${APP_VER:=1.2.0.1} # Latest version is default
case $APP_VER in
[0-9].[0-9].*)
MAK_VER=`echo $APP_VER | sed -e 's/^\(...\).*/\1/'`
;;
*) echo "`basename $0 .sh`: APP_VER ($APP_VER) should start with two digits followed by dots" 1>&2;
exit 1;;
esac
exec make MAK_VER=$MAK_VER "$#"
This script validates that APP_VER is set, giving an appropriate default if it is not. It then processes that value to derive the MAK_VER (or errors out if it is incorrect). You'd need to modify that test after you reach version 10, of course, since you are planning to be so successful that you will reach double-digit version numbers in due course.
Given the correct version information, you can now invoke your makefile with any command line arguments.
The makefile can be quite simple:
MAK_VER = dummy
include app$(MAK_VER).mak
all: PROD
...as now...
The appdummy.mak file now contains a rule:
error:
echo "You must invoke this makefile via the build script" 1>&2
exit 1
It simply points out the correct way to do the build.
Note that you can avoid the APP_VER environment variable if you keep the product version number under the VCS in a file, and the script then reads the version number from the file. And there could be all sorts of other work done by the script, ensuring that correct tools are installed, other environment variables are set, and so on.

How can I ignore command line variable assignment in a recursive build?

I'm trying to glue two build systems together. Both are recursive (rules in the makefile use make to call other makefiles to build components of the project).
I'll call them 'A' and 'B' where 'A' builds the application and 'B' builds libraries used by 'A'.
The top level makefile in A calls 'make TARGET=whatever' which means that all the recursively-invoked bits of the build inherit the value of TARGET as a read-only variable, including the build system from B, which is called as part of the recursive build.
I don't want this to happen in the build system for 'B' (which come from a different project) as the makefiles there use TARGET for their own purposes and the build fails since TARGET has the wrong value and is read-only.
I can only see two solutions to this, neither of which is palettable;
1) Rename TARGET to something else in the makefile in A that sets it and in the makefiles in A that use it, to avoid the clash with the lower levels of the build system.
2) Use the 'override' directive everywhere in the makefiles in B where the TARGET variable is set, to override its read-only status.
Anyone got any better ideas? - ideally, I want nothing to be inherited by the B's build system from A's, except those options I explicitly pass to the B build system from A.
Incidentally, I'm using GNU Make v3.80.
You could set MAKEOVERRIDES to nothing in the second-level makefile in A.
callb:
cd subdir && $(MAKE) MAKEOVERRIDES=
This passes down the normal commandline parameters like -k and -s but not commandline variable definitions.
Or you use the historical MFLAGS which is the same as MAKEFLAGS except MFLAGS doesn't contain the commandline variable definitions.
callb:
cd subdir && $(MAKE) $(MFLAGS)
Details about this two options can be read here: The GNU Make Manual
Perhaps you can use the "unexport" directive to prevent TARGET from being propagated to B's makefile?
At the point where build system A invokes build system B, do not use '${MAKE}' directly; invoke a shell script that invokes build system B (possibly after sanitizing the environment).
To achieve the behaviour where the commands are executed by 'make -n', prefix the command line in the makefile with '+' (similar to prefixing the line with '#' or '-').
It sounds like you have modified the A makefile to recursively invoke the B makefile, and thus your problem. Why not instead introduce a new toplevel makefile which recursively invokes the B makefile, and then recursively invokes the A makefile? For example, combined.mk:
all:
$(MAKE) -f Makefile.B
$(MAKE) -f Makefile.A
That way the B makefile inherits nothing from the A makefile.

Resources