Check File Existence with Makefile Conditional - makefile

I am trying to check the existence of a file using Makefile conditionals.
I've tried the following syntax which don't seem to work:
Path = /usr/local/myFileVer1
ifeq ($(wildcard $(Path)),)
version = 1
else
version = 2
endif
I thought the wildcard statement would evaluate to an empty string if the file did not exist so it would fall into the else statement. That isn't happening.
Any idea what else I can try?

You're close to the syntax. You can try something like this:
File = /usr/local/myFileVer1
ifeq ($(wildcard $(File)),)
all:
echo 1
else
all:
echo 2
endif
Or better, you can write two separate makefiles and include them at the right places:
File = /usr/local/myFileVer1
ifeq ($(wildcard $(File)),)
include Makefile1.mk
else
include Makefile2.mk
endif

Make, like all UNIX utilities, is case-sensitive. PATH is not the same as Path.
Also, you should not set the variable PATH as this will change the PATH when commands are invoked, and then your recipes will all fail.

Related

How can I add a directory to the search path of GNU Make?

I have a makefile that looks something like this:
include anotherFile.mk
all:
someStuff
The file anotherFile.mk is like this:
include yetAnotherFile.mk
export SOME_VAR = 93
The problem is that anotherFile.mk and yetAnotherFile.mk are in a different directory from my Makefile. So my makefile can't just be changed to this:
include $(OTHER_PROJECT_PATH)/anotherFile.mk
all:
someStuff
The problem with this approach is that the include statement in anotherFile.mk will fail because it will be searching in the current directory.
A partial solution that I found is to pass the --include-dir=$OTHER_PROJECT_PATH flag to the invocation of make, but that's a bit user-unfriendly.
So my question is: Is there something I can put inside my makefile that will add to the directories that make searches for when executing an include? Something like MAKE_INCLUDE_DIRS += $(OTHER_PROJECT_PATH)
Surprisingly there doesn't seem to be a good answer to that question. Forcing .INCLUDE_DIR doesn't help and there doesn't seem to be any way around invoking make with --include-dir=$OTHER_PROJECT_PATH.
It is however possible to put the appropriate recursive make invocation inside the makefile but, in order to get it to work for all reasonable cases it quickly becomes too complicated to be worth it. In summary it requires:
a top level condition to check if the OTHER_PROJECT_PATH is in .INCLUDE_DIR
the appropriate target with the recipe invoking make recursively
possibly additional targets if there are multiple command goals
the real make file enclosed in the else part of the conditional
You Makefile would look like this:
OTHER_PROJECT_PATH := other
ifeq (,$(filter $(OTHER_PROJECT_PATH), $(.INCLUDE_DIRS)))
# this is the mechanism to add the include dir in a recursive make
$(or $(firstword $(MAKECMDGOALS)),all):
$(MAKE) -I$(OTHER_PROJECT_PATH) $(MAKECMDGOALS)
# add empty targets for additional goals if needed
ifneq (,$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)))
$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)):
endif
else
# this is where the real makefile starts
all more:
echo $#: $< $^
include a.mak
endif
It still does not seem possible from a makefile, but if you have a script that sets up environment variables, you can use MAKEFLAGS (e.g. export MAKEFLAGS=I/your/path ordentlich on Linux, or SET on Windows)

How to check whether a file exists inside of a "foreach" in Makefile?

So I have a variable with file names, however I am not sure whether these file names exist. I want to put the ones that exist into some other variable.
All this is happening in a Makefile.
Here is one of my many tries to accomplish it:
FILES is the preset variable containing the set of the files.
OUTPUT += $(foreach file, $(FILES), \
ifneq (,$(wildcard $(file))
$(file)
endif
Obviously it doesn't work for many reasons, just trying to make clear what I want to achieve.
Also I would like to avoid using $(shell *) if possible.
Thanks!
The wildcard function takes multiple targets, and unlike shell globs non-matching patterns are omitted from the output, so I would expect this to work:
OUTPUT = $(wildcard $(FILES))

How to check if a file exists in Makefile

I am using makefile to build my program in multiple system. Some system have installed colorgcc script. In my Makefile i want to check, if script exists
and depending on it i setting up CC variable. But my Makefile don't work correctly - in system, that haven't colorgcc, make always set $(CC) as colorgcc. Here's part of Makefile:
ifneq ("$(wildchar /usr/bin/colorgcc)","")
CC=colorgcc
else
CC=gcc
endif
I also tried to use this variant:
ifeq ( $(shell test -e /usr/bin/colorgcc), )
CC=colorgcc
else
CC=gcc
endif
In both case $(CC) doesn't depend of existence file /usr/bin/colorgcc
How can i solve my problem?
In the first case, you mistyped the function $(wildcard ...) so you get nothing, always.
In the second case, the output of test is always the empty string. It will set its exit code depending on whether the condition is true or not, but you are not examining its exit code, just the output it prints, which will always be nothing at all.

Get exit code 1 on Makefile if statement

I'm trying to get the exit code on the ifdef statement if the statement is not true, but I tried by using exit 1 and $(call exit 1)
when using the first on the following code I get "Makefile:11: * missing separator. Stop."
...
ifdef PACKAGE
PACKAGEDIR = $(HOME)/$(PACKAGE)
else
exit 1
endif
...
By using $(call exit 1) I get no error but the makefile still keeps executing.
What I'm trying to accomplish is to exit the Makefile on the else with the error code 1
Thanks
As geekosaur says you can't put a shell command like exit 1 as a makefile operation. Makefiles are not shell scripts, although they can contain shell scripts. Shell commands can only appear within a target recipe, and nowhere else.
If you have a sufficiently new version of GNU make you can use the $(error ...) function, like this:
ifdef PACKAGE
PACKAGEDIR = $(HOME)/$(PACKAGE)
else
$(error You must define the PACKAGE variable)
endif
Also note that ifdef will be true if the variable is defined, even if it's defined to be the empty string. You may prefer:
ifneq ($(PACKAGE),)
PACKAGEDIR = $(HOME)/$(PACKAGE)
else
$(error You must define the PACKAGE variable)
endif
to ensure the variable is set to a non-empty value.
And, it's possible your version of GNU make is too old to support the $(error ...) function, although it's been around for a long time now.
You can't simply have bare code sticking somewhere in a Makefile; code (such as exit 1) is always associated with a build rule of some kind.
In this case you want the $(error) function. It may not be sufficient to drop it in the else, though, for the same reason that exit 1 itself won't work there; you may need to rephrase the whole thing as
PACKAGEDIR := $(if $(flavor PACKAGE),undefined,$(error PACKAGE must be defined!),$(HOME)/$(PACKAGE))

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.

Resources