"heritage" in Makefile - makefile

I'm looking a way to overload parts of a Makefile A to another one B, hence extending A.
For instance we have the following makefile A:
TEXT="AHAHA"
default: after-default
before-default:
echo "BEFORE DEFAULT"
default: before-default
echo ${TEXT}
after-default: default
echo "AFTER DEFAULT"
I want to reuse it in a new Makefile B like this:
TEXT="HIHIHI"
before-default:
echo "NEW BEFORE DEFAULT"
The new makefile will print:
NEW BEFORE DEFAULT
HIHIHI
AFTER DEFAULT
This example is a bit absurd it is not possible like this way, but I want to know if it is possible to make such Makefile composition close to this idea.

You can do this:
File Makefile:
# Define default TEXT
TEXT := HAHAHA
# Define default target
after:
#echo AFTER
run:
#echo $(TEXT)
before:
#echo BEFORE
# Define dependencies
run: before
after: run
# Include the new makefile
include inheritance.mk
File inheritance.mk:
# Redefine TEXT
TEXT := HIHIHI
# Redefine before target
before:
# echo NEW BEFORE
When you run make you'll have some warning but it'll work as expected:
inheritance.mk:4: warning: overriding commands for target `before'
Makefile:10: warning: ignoring old commands for target `before'
NEW BEFORE
HIHIHI
AFTER

Your example will be trivially fulfilled by adding include A at the start of B. The new before-default target will override the old one.
vnix$ tail *
==> A <==
TEXT="AHAHA"
before-default: default
echo "BEFORE DEFAULT"
default: after-default
echo ${TEXT}
after-default:
echo "AFTER DEFAULT"
==> B <==
include A
TEXT="HIHIHI"
before-default: default
echo "NEW BEFORE DEFAULT"
vnix$ make -sf A
AFTER DEFAULT
AHAHA
BEFORE DEFAULT
vnix$ make -sf B
B:6: warning: overriding commands for target `before-default'
A:4: warning: ignoring old commands for target `before-default'
AFTER DEFAULT
HIHIHI
NEW BEFORE DEFAULT
This isn't a very good design, though; parametrizing things like you already do with TEXT comes with fewer surprises than having code that is being overridden elsewhere.
(See my comment above re: why the output is in the opposite order from what you were hoping.)

Related

Allow Makefile both append and override target

I have base Makefile for all my services, in some cases I want to use my default "test" target, in other cases I want to override\add to it. These are the files I have so far (and obviously its not working as expect..).
MakefileBase
test:
./.../run-tests.sh
Makefile
BASE_FILE := /path/to/MakefileBase
include ${BASE_FILE}
test:
#$(MAKE) -f $(BASE_FILE) test # un/comment this line in order to run the default tests.
# echo "custom test"
When I run the test with the first line commented out I get the following
Makefile:10: warning: overriding commands for target `test'
/.../MakefileBase:63: warning: ignoring old commands for target `test'
echo "no tests"
no tests
except of the warning it works as expected, the problem is when I try to use the parent function then I get the following errors:
Makefile:9: warning: overriding commands for target `test'
/.../MakefileBase:63: warning: ignoring old commands for target `test'
make[1]: test: No such file or directory
make[1]: *** No rule to make target `test'. Stop.
make: *** [test] Error 2
Actually, both answers so far are wrong or incomplete:
exit 0 in a rule will just exit the current shell (which runs only the exit 0 command, so it is a no-op in this case). So this won't override.
It's not true that you cannot override a command without warning. If it is not necessary that both targets have the same name, you can do:
MakefileBase
.PHONY: test-base
test-base:
echo base
%: %-base # handles cases where you don't want to override
Makefile1
include MakefileBase
.PHONY: test
test:
echo override
Makefile
include MakefileBase
.PHONY: test
test: test-base
echo append
As with double colon rules, the effects of each targets (on each other) have to be considered, especially if you move away from .PHONY (for example, files considered up-to-date because the other rule just updated them).
BTW, I don't see the problem with your approach (aside from the warning). For me it worked fine.
This is what double-colon rules are for:
test::
./.../run-tests.sh
and:
BASE_FILE := /path/to/MakefileBase
include ${BASE_FILE}
test::
#$(MAKE) -f $(BASE_FILE) test
This will "add to" an existing target. There is no way to override a target with a different recipe without incurring a warning.
If you want to do that the only way is to use variables to hold the recipe then override the variable value. For example:
test_recipe = ./.../run-tests.sh
test:
$(test_recipe)
and:
BASE_FILE := /path/to/MakefileBase
include ${BASE_FILE}
test_recipe = #$(MAKE) -f $(BASE_FILE) test
Hacky, but you can get add, and a limited form of override that can never be deeper than one override. Both use double colon rules.
add: use double colons on both rules
override: use double colons on both rules, appending command exit 0 to the last rule
# "addcmd" echoes "BA", "overridecmd" echoes "B"
addcmd ::
echo "A"
addcmd ::
echo "B"
overridecmd ::
echo "A"
overridecmd ::
echo "B"
exit 0

Pass file argument through to child make

Conditions:
a makefile is passed to the make: make -f ../makefile hi
the hi target also calls make
I need a way to propagate the value from -f to the sub-make command running inside hi.
bye:
echo 'whatever'
hi:
${MAKE} bye
Assuming there is no default makefile in the current directory
(presumably the reason you are passing -f ../makefile) then
your example ../makefile will work as it is if you invoke
make like:
$ make MAKEFILES=../makefile hi
make bye
make[1]: Entering directory '/home/imk/develop/scrap'
echo 'whatever'
whatever
make[1]: Leaving directory '/home/imk/develop/scrap'
See 3.4 The Variable MAKEFILES
If there is a default makefile in the current directory then this
approach will cause make to read the default makefile after it has
read ../makefile, with untoward results. In that case prefer #Dummy00001's answer.
The only possibility I'm aware of is to use the MAKEFILE_LIST variable:
CURR_MAKEFILE:=$(lastword $(MAKEFILE_LIST))
bye:
echo 'whatever'
hi:
+$(MAKE) -f $(CURR_MAKEFILE) bye
But note, as it is written in the official documentation, that the variable records all parsed makefiles, including the includeed ones. The value of the variable is only reliable when queried at the very top of the makefile, before any includes.

Using conditional rules in a makefile

I capture the intent of the Makefile in pseudo code, then indicate the issues I have. I'm looking for a Makefile which is more user friendly in a test environment. The correct usage of the Makefile is one of the below.
make CATEGORY=parser TEST=basic.
make ALL
If a user gives "JUST" the commands as indicated below, it should print a message saying "CATEGORY defined TEST undefined" and vice-versa
make CATEGORY=parser
make TEST=basic
I tried writing the Makefile in following ways, but it errors out:
help:
echo"Usage: make CATEGORY=<advanced|basic> TEST=<test-case>
echo" make ALL
ifdef CATEGORY
ifdef TEST
CATEGORY_TEST_DEFINED = 1
else
echo "TEST not defined"
else
echo "CATEGORY not defined"
endif
ifeq ($(CATEGORY_TEST_DEFINED), 1)
$(CATEGORY):
cd $(PROJ)/$(CATEGORY)
make -f test.mk $(TEST)
endif
ifdef ALL
$(ALL):
for i in `ls`
cd $$(i)
make all
endif
The questions I have are:
Whether the rules in a Makefile can be selective (using ifdef to select the rules and targets).
echo doesn't work. echo should help the user with correct usage.
The problem is that echo belongs to the shell; Make can pass it to the shell in a command, but Make cannot execute it. Use info instead:
ifdef CATEGORY
$(info CATEGORY defined)
else
$(info CATEGORY undefined)
endif
If you want the rules to be conditional:
ifdef CATEGORY
ifdef TEST
$(CATEGORY):
whatever
else
$(info TEST not defined)
else
$(info CATEGORY not defined)
endif
The biggest issue here is that all ifdef/ifndef/ifeq/... statements must be at column 0 or they it will results in an error. The echo is a minor issue compared with the indentation issue.
These lines are dubious:
help:
echo"Usage: make CATEGORY=<advanced|basic> TEST=<test-case>
echo" make ALL
You need a space between echo and the string, and the string needs to be terminated:
help:
echo "Usage: make CATEGORY=<advanced|basic> TEST=<test-case>"
echo " make ALL"
These lines are dubious:
ifdef CATEGORY
ifdef TEST
CATEGORY_TEST_DEFINED = 1
else
echo "TEST not defined"
else
echo "CATEGORY not defined"
endif
Surely you need an endif before the second else? (Even if it is not syntactically mandatory, I'd recommend it.)
ifdef CATEGORY
ifdef TEST
CATEGORY_TEST_DEFINED = 1
else
echo "TEST not defined"
endif
else
echo "CATEGORY not defined"
endif
Additionally, make only executes commands such as echo is supposed to be when processing a target (rule). It won't execute echo there; it will simply object that you cannot define commands without them being actions for a target. Despite everything that GNU Make adds to a makefile, the language in a makefile is a declarative language and not a procedural language.
Another way of handling this is to define default values for the macros:
CATEGORY = advanced
TEST = all
Define default values that do something semi-reasonable; let the user override the default if they want to. You can have a rule such as:
${CATEGORY}/${TEST}: ...dependencies...
...actions...
You can leave help as the first rule. I have some directories where the first rule is:
null:
#echo "You must specify a target with this makefile!"
This is equivalent to what you have (except that make does not echo the command before running it, so I only see the message instead of the echo command line and the message; that's the # at work). The makefile this comes from also has a rule all which is otherwise usually the most sensible first (default) rule.

Check if a makefile exists before including it

I have a makefile that includes a Rules.mak file that holds includes to tools I want to use. Problem is that the tools folder has free options if they want to extract a version or use the "native" installation. So I want to include the tools extracted rules if it exists otherwise I want to include the native file.
something like this is the goal:
if Tool/Rules.mak exists then
include Tool/Rules.mak
else
include common/Rules-Tool.mak
fi
I have tried either the bash way or the make way but as this is preincludes to setup the enviroment I don't have a specifik target but make calls out wrong due to the check fails.
if [ -f Tool/Rules.mak ]
then
echo testfile exists!
fi
also
if [ -d ./Tool ]
then
echo testfile exists!
fi
as well as versions with quotes and similar. Problem is that almost all the time when I type make I get the following error:
Rules.mak:14: *** missing separator. Stop.
You could do it like that (no if or else)
-include Tool/Rules.mak
include common/Rules-Tool
like this you won't get an error if Tool/Rules.mak does not exists. (The '-' does the trick)
In common/Rules-Tool you then use the ?= operator ("conditional variable assignment operator") to assign values to the variable. This operator will assign the value only if the variable does not exists yet. IOW, it will not overwrite a pre-existing value. If Tool/Rules.mak does not exist or only partially fills in variable common/Rules-Tool will complete them.
If for some reason you don't want to use the ?= operator, (perhaps you have more action than just setting the variable) then you can do the if..then..else..fi this way:
ifneq ("$(wildcard Tool/Rules.mak)","")
$(info using Tools/Rules.mak)
include Tool/Rules.mak
else
$(info using common/Rules-Tool.mak)
include common/Rules-Tool.mak
endif

Conditional inclusion/execution of Makefile commands in a variable?

There is a Makefile that I am using, which I got from somewhere, and which is quite big. I have also found some things that I'd like changed occasionally in the makefile - and the easiest way to do that for me is to define (or not) a (switch) variable (say, OVWRCHOICE) at the start of the makefile; and then later on in the makefile code, do something like:
ifdef OVWRCHOICE
MYOPT = override
....
endif
... which is all dandy and fine.
The thing is, eventually I also need to change parts in the "override" part as well, so I'd like to have it at the start of the file. So, as this "override" part contains several make commands -- I tried to use define, to have a variable which will contain the commands (which would be executed at the ifdef OVWRCHOICE... part).
So I arrived at this simple example:
# uncomment as needed;
OVWRCHOICE = YES
define SET_OVWRCHOICE
MYOPT = override
endef
export SET_OVWRCHOICE
# ... many lines of code ...
MYOPT = default
# ... many lines of code...
# without indent: Makefile:18: *** missing separator. Stop.
# with tab indent: Makefile:18: *** commands commence before first target. Stop.
ifdef OVWRCHOICE
$(SET_OVWRCHOICE)
endif
all:
#echo $(MYOPT)
... which fails with the errors noted. Of course, if I use the first snippet in the post instead, all runs fine, and make prints out the expected result: "override".
How would I go about in achieving something like this? Not sure if "inclusion" or "execution" of "Makefile commands" are even the right terms in this context; so I have a hard time in finding a starting point for a search :)
Got it - it is described in Eval Function - GNU `make'; the right construct is:
ifdef OVWRCHOICE
$(eval $(call SET_OVWRCHOICE))
endif
Hope this helps someone,
Cheers!
Oh well, didn't really know where to archive this snippet, so back to this old question of mine :) this is off topic for OP; but here goes:
To test how environment variables are processed by a makefile, here is a simple example:
Foo=something
all :
ifdef DEBUG
#echo "Debug defined"
else
#echo "Debug NOT defined"
endif
... and here is the test for it:
$ make
Debug NOT defined
$ make DEBUG
make: *** No rule to make target `DEBUG'. Stop.
$ DEBUG make
DEBUG: command not found
$ DEBUG= make
Debug NOT defined
$ DEBUG=1 make
Debug defined
... so obviously, the right syntax to set that variable inside the makefile from the command line is: "DEBUG=1 make"

Resources