Using wildcard with ifneq in Makefile - bash

Here's the piece of my Makefile:
MY_CONFIG ?= .config.yaml
...
.PHONY: %/foo
%/foo: %/bar.yaml
ifneq ($(wildcard $*/$(MY_CONFIG)),)
$(info **FOUND CONFIG FILE** )
else
$(info **DIDNT FIND CONFIG FILE** )
endif
I call this target like make myfolder/foo and there's bar.yaml and .config.yaml files under that myfolder directory. However, that ifneq doesn't seem to work and it always prints DIDN'T FIND CONFIG FILE so I'm wondering how can I fix my condition (wildcard?) in ifneq to make it find a config file.
File structure:
-- Makefile
-- myfolder
---- bar.yaml
---- .config.yaml

The problem is that this kind of statement:
ifneq (...)
$(info ... )
else
$(info ... )
endif
is in Make syntax. It is not really part of the rule. Make will evaluate it before executing any rule. In this case:
ifneq ($(wildcard $*/$(MY_CONFIG)),)
...
before Make matches the pattern rule to anything, $* has no value and expands to nothing, so unless you have a config file in your root directory, the search will always come up negative.
There are a couple of ways to get the result you want. The simplest is probably to divide the rule into two rules:
.PHONY: %/foo
%/foo: %/bar.yaml %/$(MY_CONFIG)
#echo **FOUND CONFIG FILE**
%/foo: %/bar.yaml
#echo **DIDNT FIND CONFIG FILE**
Alternatively, you could put a shell conditional inside the rule, in the syntax of the shell you use.

Related

Ensure that make is invoked from a specific directory

I would like all my recipes to be executed from a specific directory, the directory where the Makefile is located.
This is the default behaviour when invoking make without options, but an user could always run :
(cd /somewhere; make -f /path/to/directory/Makefile)
To ensure that make working directory is the same as the directory where the Makefile is located, there are multiple solutions :
run make without options (default), from this specific directory (cd /path/to/directory; make)
use make -C /path/to/directory
cd to /path/to/directory for each recipe, like this :
MAKEFILE_DIR_LOCATION := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
a:
cd ${MAKEFILE_DIR_LOCATION} && do_something_from_makefile_folder
b:
cd ${MAKEFILE_DIR_LOCATION} && do_another_thing_from_makefile_folder
The problem is that the first two solutions requires actions from the user invoking the Makefile, while the last one clutters the Makefile.
Is there a prettier way to ensure that all recipes are executed from the directory where the Makefile is located?
Extra solution (does not work)
I also thought comparing the working directory ($(shell pwd)) to ${MAKEFILE_DIR_LOCATION}, and exit if it does not match (at least to warn the user that make is not correctly invoked), but I can't find how to do this. I tried :
MAKEFILE_DIR_LOCATION := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
WORKING_DIR := $(shell pwd)
ifneq (${MAKEFILE_DIR_LOCATION}, ${WORKING_DIR})
#error "Please run make from the directory of the Makefile, or use make -C"
endif
a:
do_something_from_makefile_folder
b:
do_another_thing_from_makefile_folder
But I got a missing separator error (line #error), or a recipe commences before first target if #error line is indented.
Answering the question you asked without commenting on whether it's a good idea or not, I'm not sure where you found this syntax:
#error "Please run make from the directory of the Makefile, or use make -C"
but it's definitely wrong. error is a make function, so you want this:
$(error Please run make from the directory of the Makefile, or use make -C)
A variant on your last attempt would re-invoke Make in the correct directory, with the same target:
ifneq (${MAKEFILE_DIR_LOCATION},${WORKING_DIR})
%:
$(MAKE) -C ${MAKEFILE_DIR_LOCATION} $#
.PHONY: %
else
## rest of Makefile rules
endif

Export preprocessor macro in makefile

I want to export preprocessor macro to an internal makefile from the main makefile in a particular target.
example:
Main_Makefile
target1 :
CXXFLAGS+=-DNewFlag=NewFlag
cd some_directory; make
Here I want to use value of CXXFLAGS which is -DNewFlag=NewFlag and is only defined under target1 in some_directory/make
Please let me know how can I achieve this.
There is no way to append to a variable from the command line, unless you've made arrangements for it in the makefile of the subdirectory.
The simplest thing to do is use a different variable, like this:
target1:
cd some_directory && $(MAKE) EXTRA_CXXFLAGS=-DNewFlag=NewFlag
(note you should always use $(MAKE) or ${MAKE} when invoking a sub-make, never make directly.)
Then in the subdirectory makefile you do something like this:
CXXFLAGS += $(EXTRA_CXXFLAGS)

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 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.

Using ifeq and ifndef in GNU Make

I've written a fairly simple test Makefile where I define two targets, all & clean. I've got two different conditional statements. One checks for the existence of the $(MAKECMDGOALS) special variable and the other detects whether any of the command line targets matched those listed in a variable (NODEPS). The problem I'm having is that none of the branches within my conditionals get executed. Ultimately I want to use a conditional to decide whether the target I'm supplying should include some autogenerated dependency files but at the moment I'm struggling to get either expression to even evaluate. I'm running GNU make version 3.81 and I've tried it under Ubuntu and Mac OS X to no avail.
NODEPS := clean
INCLUDE = $(filter $(NODEPS),$(MAKECMDGOALS))
.PHONY : all clean
ifndef $(MAKECMDGOALS)
#echo "$$(MAKECMDGOALS) is not defined"
else
#echo "$(MAKECMDGOALS) is defined"
endif
ifneq (0, $(words $(INCLUDE)))
#echo "INCLUDE = $(INCLUDE) != 0"
else
#echo "INCLUDE = $(INCLUDE) == 0"
endif
all :
#echo "all : $(MAKECMDGOALS)"
clean :
#echo "clean : $(MAKECMDGOALS)"
I eventually managed to work out what was wrong. #eriktous was right, pointing out that I should be using $(info) rather than #echo. More subtly though, part of the problem was that I'd indented the #echos with a tab. It seems that tabs are mandatory for rules but not allowed in conditionals. The other mistake was I'd expanded the $(MAKECMDGOALS) variable in the test condition when it should have been written as just ifndef MAKECMDGOALS.
https://www.gnu.org/software/make/manual/html_node/Make-Control-Functions.html
A makefile is not a shell script. You can not "randomly" place executable statements anywhere you like and expect them to be executed.
There are various ways of communicating with the outside world from within a makefile: $(info ...), $(warning ...), $(error ...) and $(shell #echo ...) (some or all of these may be GNU make extensions).
Ps: you misspelled PHONY.

Resources