Makefile, conditional and wildcard with variables - makefile

I'm having troubles with my makefile. I want to have a "master" target which automatically processes a variety of dirs and copies them in a specific location. But somehow the ifneq condition with the wildcard is not working properly, giving me always the same result regardless of the condition being satisfied or not.
I'm trying with:
# Basically the same thing for all targets
% :
ifeq ($(wildcard $(TOP)/flow/$#),)
$(error Directory $(TOP)/flow/$# not found. Aborting. Debug: $(wildcard $(TOP)/flow/$#))
else
$(error Directory $(TOP)/flow/$# found!. Debug: $(wildcard $(TOP)/flow/$#))
endif
As an example, in $(TOP)/flow exists a directory core but not pwr. The Make target proceed as if they both exist.
➜ work ✗ make core
Makefile:20: *** Directory TOPFOLDER/flow/core found!. Debug: TOPFOLDER/flow/core. Stop.
➜ work ✗ make pwr
Makefile:20: *** Directory TOPFOLDER/flow/pwr found!. Debug: . Stop.
Why is this so? Can't see what I'm doing wrong here.
Thank you for the help!

The variable $# is empty at the time the ifeq is evaluated.
Probably use a run-time check instead:
% :
test -d $(TOP)/flow/$#/

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

Using Make with force snippet to override other file

I've been trying to get a makefile, a, to include another makefile, b, if the target specified is not found in file a. I'm using this snippet to try and achieve this, but from echos I've put into the file I can see that makefile b is being accessed even when the target is found in a and run.
The snippet I'm using from the link above is:
foo:
frobnicate > foo
%: force
#echo "No target found locally, running default makefile"
#$(MAKE) -f Makefile $#
force: ;
Specifically I'm getting "Nothing to be done" outputs when makefile b is being used, and makefile a is behaving as expected. This is shown below:
$ make all # all target appears in both make files
No target found locally, running default makefile
make[1]: Entering directory `/home/user/currdir' # (b)
make[1]: Nothing to be done for `Makefile'.
make[1]: Leaving directory `/home/user/currdir'
Local all # (a)
Is there a better way to be doing this?
addition: After adding another echo to the % rule, I've found that $# is "Makefile", when it should be the target trying to be built.
I don't really understand your question based on the example you gave; there is no "a" or "b" in that example, just one Makefile.
However, the behavior you're seeing is due to GNU make's re-making makefiles capability. When you create match-anything pattern rules as you've done, you have to consider that every single target or prerequisite that make wants to build will match that rule. That's a large burden.
You can avoid having remade makefiles match by creating explicit rules for them, such as:
Makefile: ;

Is there a way to tell my makefile to output a custom error message if a certain an include file is not found?

I have a configure script that generates a config.inc file containing some variable definitions and a makefile that imports those configurations using
include config.inc
The thing that bothers me is that if the user tries to run the makefile directly without first running configure they get an unhelpful error message:
makefile:2: config.inc: No such file or directory
make: *** No rule to make target 'config.inc'. Stop.
Is there a way for me to produce a better error message, instructing the user to first run the configure script, without resorting to the autoconf strategy of generating the full makefile from inside configure?
Sure, no problem; just do something like this:
atarget:
echo here is a target
ifeq ($(wildcard config.inc),)
$(error Please run configure first!)
endif
another:
echo here is another target
include config.inc
final:
echo here is a final target
Note this is definitely specific to GNU make; there's no portable way to do this.
EDIT: the above example will work fine. If the file config.inc exists, then it will be included. If the file config.inc does not exist, then make will exit as it reads the makefile (as a result of the error function) and never get to the include line so there will be no obscure error about missing include files. That's what the original poster asked for.
EDIT2: Here's an example run:
$ cat Makefile
all:
#echo hello world
ifeq ($(wildcard config.inc),)
$(error Please run configure first!)
endif
include config.inc
$ touch config.inc
$ make
hello world
$ rm config.inc
$ make
Makefile:5: *** Please run configure first!. Stop.
I gave up and decided to use autoconf and automake to handle my makefile-generation needs.

how to implement makefile shared variable

I need a variable in makefile that can be shared across subdir makefiles ( recursive build strategy), and finally use it for some decision making. I had earlier thought export is the way to do it, but I am not getting desired results.
following are my makefiles:-
Target.mk (common include for both Makefile.mk & Subdir.mk)
Makefile.mk
Subdir.mk
FILE : Target.mk
export TARGET_PROPERTIES :=
FILE : Makefile.mk
-include Target.mk
.PHONY : all
all :
$(MAKE) -C $(PWD) -f Subdir.mk all
#echo "#------------------------------------------------------------#"
#echo "Target build properties"
#echo $(TARGET_PROPERTIES)
#echo "#------------------------------------------------------------#"
FILE : Subdir.mk
-include Target.mk
TARGET_PROPERTIES+=alpha
TARGET_PROPERTIES+=beta
$(warning $(TARGET_PROPERTIES))
all:
#echo "Subdir.mk......[OK]"
PROBLEM:-
I want TARGET_PROPERTIES, to be updated from Subdir.mk and the use the results in Makefile.mk
following is my output
$ make -f Makefile.mk
make -C /cygdrive/c/make_pf -f Subdir.mk all
make[1]: Entering directory `/cygdrive/c/make_pf'
Subdir.mk:8: alpha beta
Subdir.mk......[OK]
make[1]: Leaving directory `/cygdrive/c/make_pf'
#------------------------------------------------------------#
Target build properties
#------------------------------------------------------------#
in Subdir.mk TARGET_PROPERTIE updates, fine until here.
Subdir.mk:8: alpha beta
in Makefile.mk after return from "Subdir.mk - all target" it resets to NULL.
Not sure what I am doing wrong
P.S. I am using cygwin environment.
Exported environment variables are copied to child processes, so changes there do not propagate back into the parent process.
Your best bet would probably to avoid recursive makefiles and instead include everything from the main Makefile.mk. Google for Recursive Make considered harmful for pointers how to do this.

Makefile calling other makefile with target, gives wrong target

I have a makefile with a target clean:
clean:
$(MAKE) -f <other makefile location> clean
When I call make clean in this makefile, it tells me that in the other makefile there is no rule 'clean-linux'. Specifically here is the output.
make -f /root/ovaldi-5.5.25-src/project/linux/Makefile clean
make[1]: Entering directory '/root/ovaldi-5.5.25-src'
make[2]: Entering directory '/root/ovaldi-5.5.25-src'
make[2]: *** No rule to make target 'clean-linux'. Stop.
make[2]: Leaving directory '/root/ovaldi-5.5.25-src'
make[1]: Leaving directory '/root/ovaldi-5.5.25-src'
Why is it giving it the clean-linux target and not just clean like I specified?
When you make (or $(MAKE)), by default you use whatever makefile is there. So here's what I think is happening.
You cd to some location.
You 'make -f Makefile_A clean'.
make runs with Makefile_A, and does '$(MAKE) -f Makefile_B clean'.
make[1] runs with Makefile_B, and does '$(MAKE) clean-linux'.
make[2] runs with whatever makefile is here which might be anything (I suspect it's Makefile_A) but whatever it is it has no rule for clean-linux.
The solution: rewrite your second makefile (the one that has clean-linux) so that clean-linux becomes a prerequisite of clean (if/when you're on a linux system). That way it won't run make[2].
ifeq ($(PLATFORM), LINUX)
clean: clean-linux
endif
ifeq ($(PLATFORM), SUNOS)
clean: clean-sunos
endif
clean:;
Just a guess but maybe the 'clean' target in the second makefile calls 'clean-linux'?
Can you post the clean target of the second makefile?
Edit:
In light of your posted clean target it seems you're just calling the clean-linux target incorrectly.
Beta has posted the correct way of dealing with your problem in their answer so I'm going to +1 that.

Resources