Makeifle: if condition block in define block - if-statement

I create a define block
I'd like to check the program whether in the $PATH env.var,
i.e. whether program exists.
define CheckProgExist
#$(eval exist=$(shell ${1} ${2} 2> /dev/null))
#$(info ${1} ${2})
#$(info ${exist})
ifneq (${exist},"")
#$(error "${1} does not exist in the $${PATH} of env.var")
endif
endef
First Line: #$(eval exist=$(shell ${1} ${2} 2> /dev/null))
Check command exists,
${1} is command I specify
${2} flags of program which I can check if program exists, generally:
return version message (to stdout) if program exists
return null(maybe) if program doesn't exist, because I've redirected stderr to null, output should be null
Second line: #$(info ${1} ${2})
Display argument I input.
Third line: #$(info ${exist})
Display content of variable, if
${1} exists, ${exist} should be message of version and program
${2} does not exists, ${exist} should be empty (or null)
Fourth Line: ifneq (${exist},"")
If variable does not exist, then abort makefile
For example, I'd like to check whether ls exist:
chk_env_hw:
#$(call CheckProgExist,ls,--version)
If program ls does not exist, then error message will display.
But the result seems strange:
>$ make
ls --version
ls (GNU coreutils) 8.32 Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Written by Richard M. Stallman and David MacKenzie.
Makefile:11: *** "ls does not exist in the ${PATH} of env.var". Stop.
ls is exist but the result shows not.
fully code
define CheckProgExist
#$(eval exist=$(shell ${1} ${2} 2> /dev/null))
#$(info ${1} ${2})
#$(info ${exist})
ifneq (${exist},"")
#$(error "${1} does not exist in the $${PATH} of env.var")
endif
endef
chk_env_hw:
$(call CheckProgExist,ls,--version)

You have several problems here:
ifneq (${exist},"") will fire the error if ${exist} is not equal to "", which is the case. Try ifneq (${exist},).
ls --version sends its output to the standard output, not the standard error, so in your case, with ls --version, exist will not be the empty string. It will be something like ls (GNU coreutils) 8.32...
What you're doing is a strange mixture of make and shell constructs. Make recipes are shell scripts. There is no need to call the shell make function in a recipe. You should maybe try to write a make macro with 100% shell content (including shell variables if you need some). Do not forget to escape the make expansion if needed.
You could try something like (not tested):
define CheckProgExist
#command -v '$(1)' > /dev/null || { echo "$(1) does not exist"; exit 1; }
endef
.PHONY: chk_env_hw
chk_env_hw:
$(call CheckProgExist,ls)
(edited to use command -v as suggested by MadScientist instead of the non-POSIX which).

Related

GNU Make: expand pattern before passing to Shell

I want to use an awk script to figure out the which modules have to be compiled before I can compile a FORTRAN source file. My project is structured in a way that I can obtain the filenames that provide the modules by running
awk '$1=/use/{print gensub(",","","g", $2) ".o"}' file.f90
on the file I want to compile.
However, my make command
%.o: $(shell awk '$$1=/use/{print gensub(",","","g", $$2) ".o"}' /path/to/%.f90)
fails with
awk: fatal: cannot open file `/path/to/%.f90' for reading: No such file or directory
So %.f90 does not get expanded. Why is that the case and how can I solve that issue?
Variables and functions in targets and prerequisites are expanded when the makefile is parsed, not when make is running the makefile. But, pattern rules are only expanded when make is running the makefile, trying to build a target that matches the pattern. So at the time these variables and functions are expanded, you only have the literal pattern string not its expansion into a real filename.
See How make reads a makefile in the docs.
There are a number of ways to do this. One option is using secondary expansion. However note you'll have to double-escape the $ you are escaping!!
.SECONDEXPANSION:
%.o: $$(shell awk '$$$$1=/use/{print gensub(",","","g", $$$$2) ".o"}' /path/to/$$*.f90)
ETA
You could alternatively not use .SECONDEXPANSION at all and instead use eval like this:
%.o:
...
SRCS := $(wildcard *.f90)
OBJS := $(SRCS:%.f90=%.o)
$(foreach O,$(OBJS),\
$(eval $O: $(shell awk '$$1=/use/{print gensub(",","","g", $$2) ".o"}' $(O:%.o=%.f90))))
Since you didn't give an actual example I just made up SRCS and OBJS variables. Maybe you have similar variables already.

How to compare variable (from command line) and string in Makefile?

I trying to compare variable and string in Makefile for defining different actions depending on which value user appointed to variable from the command line. But every time I get an unexpected result.
Example:
Makefile
print:
ifeq ("minikube", $(env))
#echo "yes"
else
#echo "no"
endif
Execution from shell
$ make print env=minikube
no
Additional info
GNU Make version 4.2.1
GNU bash version 5.0.17
Ubuntu 20.04

Make uses old Makefile

I'm using make to build a C++ project. During the course of the project, I wanted to make some changes to the Makefile. Unfortunately, ever since I executed make once, it keeps using that particular version of the Makefile and just doesn't do anything with the changes at all.
I have run make clean, I have renamed the makefile, I've searched for other Makefiles which might be used instead, all to no avail. There is no mention of any caching mechanism in the man pages for make, nor anywhere on Google.
Does anyone have any idea why make isn't using the new version and what I can do about it? I'm compiling on a Ubuntu 12.04.2 LTS (x86_64) box, with (GNU) make version 3.81.
Update:
Some additional information. It seems make is using the current version of the makefile after all. If I change something in the main target, it's working just fine. But if I change something in the obj/%.o target, it just keeps running the same command, no matter what changes I make to that target.
Full Makefile an be found here: http://pastebin.com/WK43NRcL
CC_FILES = $(shell find -name "*.cc" -exec echo "{}" +;)
That find command is incorrect, shouldn't it be looking in the src directory? And why use echo to print the name when that's what find does anyway?
That means your list of CC_FILES and so also list of OBJ_FILES is empty.
I think you want:
CC_FILES := $(shell find src -name "*.cc")
Note that this uses := not = because otherwise the shell function gets run every time you reference the CC_FILES variable. Using := means it is run and evaluated only once.
However, since it seems all your .cc files are in the same directory you don't need a recursive find, just do:
CC_FILES := $(wildcard src/*.cc)
As you've realised, your patsubst is broken, you can just do:
OBJ_FILES := $(patsubst src/%.cc,obj/%.o,$(CC_FILES))
(Again, use := here)
Also:
obj/%.o: obj src/%.cc
$(CXX) $(CFLAGS) -c -o $# $<
I think you need to read what the $< variable expands to, because the rule above isn't going to do what you expect.
This makefile is full of errors, you need to use echo in pattern rules to print out the values of variables, so you can verify they have the values you expect. (As another option for debugging, set SHELL=bash -x so every shell command is echoed)
make does not somehow magically keep track of your old makefile; it will use whatever file is first in the list of files it looks for, in the current directory.
To find out which Makefile is actually used, see this question: Getting the name of the makefile from the makefile
Since you're using GNU make, check its excellent manual on what filenames it looks for, and in which order.

Autotools naming bug

I have following configure.ac:
AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_PROG_CXX
AC_OUTPUT
And it generates configure, that contains following lines:(grep core configure )
1572: rm -f core *.core core.conftest.* &&
2143:rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
2210:rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
2212:rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
2214:rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
Fun begins with the fact, that I have folder, named core. So ./configure yield
checking for g++... g++
checking whether the C++ compiler works... yes
checking for C++ compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C++ compiler... rm: cannot remove 'core': Is a directory
yes
checking whether g++ accepts -g... rm: cannot remove 'core': Is a directory
yes
configure: creating ./config.status
rm: cannot remove 'core': Is a directory
I found in google, that it is done in case of compiler crash. But is it any nice way to disable this check(I really do not care about compilers that can core dump on autoconf tests).
Renaming folder is not what I want to do.
But is it any nice way to disable this check
No, there isn't.
Renaming folder is not what I want to do.
In that case, I suggest patching 'configure' after somewhere in an autoconf 'bootstrap.sh' script or after whatever runs autoreconf:
#!/bin/sh
autoreconf -fvi # or whatever autoreconf needs
sed -i 's/rm -f core/rm -f/g' configure
Note that sed -i is not a universal solution, as it relies on GNU sed, but it's not too hard to come up with a portable solution.
As show in the question most projects have a configure.ac file with rules for generating the configure script. The last line is almost always AC_OUTPUT (as it is in your case). You can add this after that line (usually as the very end of the file) to manipulate the generated configure file and scrub the offending commands:
m4_esyscmd_s([test -f configure && sed -i -e '/rm -f/s/ core / /' configure])

Exiting from a Makefile based on the state of two shell variables

During make checksource, $CROSS_COMPILE should be "whatever". If $CROSS_COMPILE is not set, make should throw an error and exit.
I have the following rule in my Makefile:
.PHONY: checksource
all: checksource default
checksource:
$(if $(and $(ifeq ($(CROSS_COMPILE), whatever)), $(ifeq ($(VARIABLE),))), \
($(shell echo "Error! VARIABLE not defined!") \
$(shell exit 2)))
Right now if $CROSS_COMPILE is set to whatever and $VARIABLE is undefined, the make doesn't exit.
$CROSS_COMPILE:
$> echo $CROSS_COMPILE
whatever
$>
$VARIABLE is not defined:
$> echo $VARIABLE
$>
I could use a nested ifeq, but I want to make it pretty (and learn a bit more about Makefile operations).
There is no such thing as $(ifeq). I still think you should do the check in the makefile itself, not as one of the targets:
ifeq ($(CROSS_COMPILE),whatever)
ifeq ($(VARIABLE),)
$(error Variables not set correctly.)
endif
endif
And if you're set on avoiding nested ifeq:
ifeq ($(or $(subst whatever,,$(CROSS_COMPILE)),$(VARIABLE)),)
$(error Variables not set correctly.)
endif
But I fail to see how that's an improvement. If you want to do it in a target, just use the shell and don't bother with make functions:
checksource:
#if [ "$(CROSS_COMPILE)" = whatever -a -z "$(VARIABLE)" ]; then \
echo "Error: Variables not set correctly"; exit 2; \
else true; fi
I'd still go with the first option, because you can stop make before it stat s all the files names in Makefile and decides to start executing checksource.
Doing it in make is always better than using the shell (whether via $(shell) or a recipe). If you do do the check in a recipe, then it means that the Makefile can contain other targets that do not need this particular assert.
assert = $(if $(filter whatever,${CROSS_COMPILE}),$(if ${VARIABLE},,$(error Urk! Variable problem)))
checksource:
${assert}some shell commands...
P.S. If you ran your original make with --warn-undefined-variables you may have got some clue why your macros were not expanding properly:
$ make -f 1.mak CROSS_COMPILE=whatever --warn-undefined-variables
1.mak:6: warning: undefined variable `ifeq (whatever, whatever)'
make: *** No rule to make target `default', needed by `all'. Stop.