Makefile if conditional function inside foreach - if-statement

I am trying to run in makefile something like this, and I am a newbie in writing makefiles.
EXECUTABLES = emcc em++ emcmake emconfigure emmake
K := $(foreach exec,$(EXECUTABLES),\
$(if $(shell which $(exec)), \
#echo "Emsdk binaries are already added to path", \
$(if [! -d "${HOME}/.wasm/emsdk"]), \
#echo "Emsdk is not installed consider running bash install_requirements first",\
#source "${HOME}/.wasm/emsdk/emsdk_env.sh"))
But it throws error saying something like this insufficient number of arguments (1) to function `if'. Stop. . Am I missing something?

I'm not sure what this part of your assignment is intended to mean:
$(if [! -d "${HOME}/.wasm/emsdk"]), \
but it's definitely incorrect syntax. You have closed the if function with only one argument, where it needs at least two.
Maybe you really wanted to use $(shell ...) here? But I don't understand what this would do; make's if-function doesn't look at exit codes.

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.

Cannot compile Makefile using make command on Windows

Problem summary
I am trying to install an open-source parallel finite-element code called TACS and available at this github repository. To comply with the indicated prerequisites, I followed the instructions at this github repository, which allowed me to install SuiteSparse and METIS on Windows with precompiled BLAS/LAPACK DLLs. For the MPI, I installed both the Intel MPI Library and Open MPI through Cygwin. The final step should be to compile running make, however this command is not directly available in Windows 10. As a consequence, I explored the options suggested in this question, unfortunately without success. I feel at a dead end, any help will be appreciated.
What I've tried
Please have a look below at my attempts. I am mainly a Windows user and I don't know much of compiling programs using Makefile. My current understanding is that the Makefile that I am trying to compile is written for Linux and whatever GNU compiler for Windows I use will not work because of the different syntax needed. Please correct me if I am wrong. What I can't understand is why I get errors also when I try to compile with Ubuntu Bash for Windows 10 (last attempt of the list below).
Visual Studio nmake
Running the Developer Command Prompt for VS 2019 as administrator, I typed nmake -f Makefile in TACS base directory and I got Makefile.in(28) : fatal error U1001: syntax error : illegal character '{' in macro Stop.
Chocolatey make
Running Windows Command Prompt as administrator with C:\ProgramData\chocolatey\bin at the top of PATH environment variable, I typed make in TACS base directory and I got
"" was unexpected at this time.
make: *** [Makefile:23: default] Error 255
Cygwin make
Running Windows Command Prompt as administrator with C:\cygwin64\bin at the top of PATH environment variables, I typed make in TACS base directory and I got three types of error:
error: expected ',' or '...' before numeric constant
error: cannot convert 'int*' to 'idx_t*' {aka 'long int*'}
error: no matching function for call to 'TACSSchurMat::getBCSRMat(BCSRMat**, NULL, NULL, NULL)'
I tried to change some variable names in the affected scripts (like N_ in place of _N) and I got rid of the first and the third type of error, but not of the second one.
GnuWin make
Running Windows Command Prompt as administrator with C:\Program Files (x86)\GnuWin32\bin at the top of PATH environment variables, I typed make in TACS base directory and I got
"" was unexpected at this time.
make: *** [Makefile:23: default] Error 255
MinGW mingw32-make
Running Windows Command Prompt as administrator with C:\MinGW\bin at the top of PATH environment variables, I typed mingw32-make in TACS base directory and I got
"" was unexpected at this time.
Makefile:23: recipe for target 'default' failed
mingw32-make: *** [default] Error 255
MSYS MinGW 64-bit make
Running Windows Command Prompt as administrator with C:\msys64\usr\bin at the top of PATH environment variables, I typed make in TACS base directory and I got
make[1]: mpicxx: No such file or directory
make[1]: *** [../TACS_Common.mk:28: C:/Users/qa21944/git/tacs/src/TACSAssembler.o] Error 127
make[1]: *** Waiting for unfinished jobs....
make[1]: mpicxx: No such file or directory
make[1]: *** [../TACS_Common.mk:28: C:/Users/qa21944/git/tacs/src/TACSCreator.o] Error 127
make[1]: Leaving directory '/c/Users/qa21944/git/tacs/src'
make: *** [Makefile:23: default] Error 1
This is quite difficult for me to understand, as the mpicxx file is C:\Program Files (x86)\Intel\oneAPI\mpi\latest\bin, which in turn is in the PATH environment variable. When I tried to add C:\cygwin64\bin to PATH (below C:\msys64\usr\bin) and to rerun make, I got
0 [main] opal_wrapper (14432) C:\cygwin64\bin\opal_wrapper.exe: *** fatal error - cygheap base mismatch detected - 0x180352408/0x180357408.
This problem is probably due to using incompatible versions of the cygwin DLL.
Search for cygwin1.dll using the Windows Start->Find/Search facility
and delete all but the most recent version. The most recent version *should*
reside in x:\cygwin\bin, where 'x' is the drive on which you have
installed the cygwin distribution. Rebooting is also suggested if you
are unable to find another cygwin DLL.
I tried to follow these instructions and then rebooted my computer, but nothing changed.
Ubuntu Bash for Windows 10 make
This attempt was inspired by this answer. I downloaded Ubuntu from Microsoft Store and installed make. From the Ubuntu Bash I typed make in TACS base directory and I got
make[1]: Entering directory '/mnt/c/Users/qa21944/git/tacs/src'
Makefile:26: *** target pattern contains no '%'. Stop.
make[1]: Leaving directory '/mnt/c/Users/qa21944/git/tacs/src'
make: *** [Makefile:23: default] Error 1
I do not understand why I should get this error. I also made sure that all lines started with a tab rather than white spaces, but nothing changed.
Codes
Below you can find the Makefile.in and Makefile that I am using.
Makefile.in
# Do not modify this file. Copy this file to Makefile.in and then modify it.
# In order to get TACS to compile, you'll need to fill in the
# following path information. Some of the items below are required
# only if you're going to use the python interface.
# the full path to the root TACS directory
TACS_DIR = C:/Users/qa21944/git/tacs
CXX = mpicxx
RM = rm -f
PYTHON = python
PYTHON_CONFIG = python-config
# Set up for parallel make
MAKE = make -j 8
# Set the ar flags
AR_FLAGS = rcs
# Flags for debugging and regular compilation versions
EXTRA_DEBUG_CC_FLAGS = -fPIC -g
EXTRA_CC_FLAGS = -fPIC -O3
# Use this if you have problems with mpich
# TACS_DEF = -DMPICH_IGNORE_CXX_SEEK
# Defines whether to use static or dynamic linking
# TACS_LD_CMD=${TACS_DIR}/lib/libtacs.a
TACS_LD_CMD=-L${TACS_DIR}/lib/ -Wl,-rpath,${TACS_DIR}/lib -ltacs
# For linux systems, use the following settings:
SO_EXT=so
SO_LINK_FLAGS=-fPIC -shared
# For MAC OS X, use the following settings:
# SO_EXT=so
# SO_LINK_FLAGS=-fPIC -dynamiclib
# This uses the default installation of LAPACK.
# Use an optimized version of LAPACK if available.
# You may also have to include -lblas as well.
LAPACK_LIBS = -LC:/SP_ROOT/lapack_windows/x64 -llapack -lpthread -lblas
# For MAC OSX use the accelerate framework
# LAPACK_LIBS=-framework accelerate
# METIS is handy for partitioning graphs, but can be problematic for
# compilation. If you compile METIS using a C++ compiler you must add
# -DTACS_CPLUSPLUS_METIS to the TACS_DEF arguments below. If you
# compile METIS using a C compiler, there should be no issues.
METIS_INCLUDE = -IC:/SP_ROOT/build/install/include
METIS_LIB = -LC:/SP_ROOT/build/install/lib -lmetis
# AMD is a set of routines for ordering matrices. It is not required by default.
AMD_INCLUDE = -IC:/SP_ROOT/build/install/include/suitesparse
AMD_LIBS = -LC:/SP_ROOT/build/install/lib -llibamd
Makefile
# ============================================
#
# Make file for TACS_DIR/
#
# ============================================
include Makefile.in
include TACS_Common.mk
TACS_SUBDIRS = src \
src/bpmat \
src/elements \
src/elements/dynamics \
src/elements/basis \
src/elements/shell \
src/constitutive \
src/functions \
src/io
TACS_OBJS := $(addsuffix /*.o, ${TACS_SUBDIRS})
default:
#if [ "${TACS_IS_COMPLEX}" = "true" ]; then \
echo "Building Complex TACS"; \
for subdir in $(TACS_SUBDIRS) ; do \
echo "making $# in $$subdir"; \
echo; (cd $$subdir && $(MAKE) TACS_DIR=${TACS_DIR} TACS_DEF="${TACS_DEF} -DTACS_USE_COMPLEX") || exit 1; \
done \
else \
echo "Building Real TACS"; \
for subdir in $(TACS_SUBDIRS) ; do \
echo "making $# in $$subdir"; \
echo; (cd $$subdir && $(MAKE) TACS_DIR=${TACS_DIR}) || exit 1; \
done \
fi
${CXX} ${SO_LINK_FLAGS} ${TACS_OBJS} ${TACS_EXTERN_LIBS} -o ${TACS_DIR}/lib/libtacs.${SO_EXT}
#if [ "${TACS_IS_COMPLEX}" = "true" ]; then \
echo "ctypedef complex TacsScalar" > tacs/TacsTypedefs.pxi; \
echo "TACS_NPY_SCALAR = np.NPY_CDOUBLE" > tacs/TacsDefs.pxi; \
echo "dtype = complex" >> tacs/TacsDefs.pxi; \
else \
echo "ctypedef double TacsScalar" > tacs/TacsTypedefs.pxi; \
echo "TACS_NPY_SCALAR = np.NPY_DOUBLE" > tacs/TacsDefs.pxi; \
echo "dtype = np.double" >> tacs/TacsDefs.pxi; \
fi
debug:
#if [ "${TACS_IS_COMPLEX}" = "true" ]; then \
echo "Building Complex TACS"; \
for subdir in $(TACS_SUBDIRS) ; do \
echo "making $# in $$subdir"; \
echo; (cd $$subdir && $(MAKE) debug TACS_DIR=${TACS_DIR} TACS_DEF="${TACS_DEF} -DTACS_USE_COMPLEX") || exit 1; \
done \
else \
echo "Building Real TACS"; \
for subdir in $(TACS_SUBDIRS) ; do \
echo "making $# in $$subdir"; \
echo; (cd $$subdir && $(MAKE) debug TACS_DIR=${TACS_DIR}) || exit 1; \
done \
fi
${CXX} ${SO_LINK_FLAGS} ${TACS_OBJS} ${TACS_EXTERN_LIBS} -o ${TACS_DIR}/lib/libtacs.${SO_EXT}
#if [ "${TACS_IS_COMPLEX}" = "true" ]; then \
echo "ctypedef complex TacsScalar" > tacs/TacsTypedefs.pxi; \
echo "TACS_NPY_SCALAR = np.NPY_CDOUBLE" > tacs/TacsDefs.pxi; \
echo "dtype = complex" >> tacs/TacsDefs.pxi; \
else \
echo "ctypedef double TacsScalar" > tacs/TacsTypedefs.pxi; \
echo "TACS_NPY_SCALAR = np.NPY_DOUBLE" > tacs/TacsDefs.pxi; \
echo "dtype = np.double" >> tacs/TacsDefs.pxi; \
fi
interface:
${PYTHON} setup.py build_ext --inplace
complex_interface:
${PYTHON} setup.py build_ext --inplace --define TACS_USE_COMPLEX
complex: TACS_IS_COMPLEX=true
complex: default
complex_debug: TACS_IS_COMPLEX=true
complex_debug: debug
clean:
${RM} lib/libtacs.a lib/libtacs.so
${RM} tacs/*.so tacs/*.cpp
#for subdir in $(TACS_SUBDIRS) ; do \
echo "making $# in $$subdir"; \
echo; \
(cd $$subdir && $(MAKE) $# TACS_DIR=${TACS_DIR}) || exit 1; \
done
Edit: I am adding a snippet of TACS_Common.mk as requested in the comments.
TACS_Common.mk
TACS_LIB = ${TACS_DIR}/lib/libtacs.a
TACS_INCLUDE = -I${TACS_DIR}/src \
-I${TACS_DIR}/src/bpmat \
-I${TACS_DIR}/src/elements \
-I${TACS_DIR}/src/elements/dynamics \
-I${TACS_DIR}/src/elements/basis \
-I${TACS_DIR}/src/elements/shell \
-I${TACS_DIR}/src/constitutive \
-I${TACS_DIR}/src/functions \
-I${TACS_DIR}/src/io
# Set the command line flags to use for compilation
TACS_OPT_CC_FLAGS = ${TACS_DEF} ${EXTRA_CC_FLAGS} ${METIS_INCLUDE} ${AMD_INCLUDE} ${TACS_INCLUDE}
TACS_DEBUG_CC_FLAGS = ${TACS_DEF} ${EXTRA_DEBUG_CC_FLAGS} ${METIS_INCLUDE} ${AMD_INCLUDE} ${TACS_INCLUDE}
# By default, use the optimized flags
TACS_CC_FLAGS = ${TACS_OPT_CC_FLAGS}
# Set the linking flags to use
TACS_EXTERN_LIBS = ${AMD_LIBS} ${METIS_LIB} ${LAPACK_LIBS}
TACS_LD_FLAGS = ${EXTRA_LD_FLAGS} ${TACS_LD_CMD} ${TACS_EXTERN_LIBS}
# This is the one rule that is used to compile all the
# source code in TACS
%.o: %.cpp
${CXX} ${TACS_CC_FLAGS} -c $< -o $*.o
#echo
#echo " --- Compiled $*.cpp successfully ---"
#echo
I can't answer but maybe I can orient you.
First nmake is not make. It will not work with any makefile not written specifically as an nmake makefile. And it's only available on Windows. So, best to just forget it exists.
Second, it's important to understand how make works: rules in makefiles are a combination of targets/prerequisites, and a recipe. The recipe is not in "makefile" syntax, it's a shell script (batch file). So make works in tandem with the shell, to run commands. Which shell? On POSIX systems like GNU/Linux and MacOS it's very simple: a POSIX shell; by default /bin/sh.
On Windows systems it's much less simple: there are a lot of options. It could be cmd.exe. It could be PowerShell. It could be a POSIX shell, that was installed by the user. Which one is chosen by default, depends on how your version of make was compiled. That's why you see different behaviors for different "ports" of make to Windows.
So, if you look at the makefiles you are trying to use you can see they are unquestionably written specifically for a POSIX system and expect a POSIX shell and a POSIX environment. Any attempt to use a version of make that invokes cmd.exe as its default shell will fail immediately with syntax errors ("" was unexpected at this time.).
OK, so you find a version of make that invokes a POSIX shell, and you don't get that error anymore.
But then you have to contend with another difference: directory separators. In Windows they use backslash. In POSIX systems, they use forward slash and backslash is an escape character (so it's not just passed through the shell untouched). If you are going to use paths in a POSIX shell, you need to make sure your paths use forward slashes else the shell will remove them as escape characters. Luckily, most Windows programs accept forward slashes as well as backslashes as directory separators (but not all: for example cmd.exe built-in tools do not).
Then you have to contend with the Windows abomination known as drive letters. This is highly problematic for make because to make, the : character is special in various places. So when make sees a line like C:/foo:C:/bar its parser will get confused, and you get errors. Some versions of make compiled for Windows enable a heuristic which tries to see if a path looks like a drive letter or not. Some just assume POSIX-style paths. They can also be a problem for the POSIX shell: many POSIX environments on Windows map drive letters to standard POSIX paths, so C:\foo is written as /c/foo or /mnt/c/foo or something else. If you are adding paths to your makefile you need to figure out what the right mapping, if any, is and use that.
That's not even to start discussing the other differences between POSIX and Windows... there are so many.
From what you've shown above, this project was not written with any sort of portability to Windows in mind. Given the complexity of this, that's not surprising: it takes a huge amount of work. So you have these options that I can see:
Port it yourself to be Windows-compatible
Try to get it working inside cygwin (cygwin is intended to be a POSIX-style environment that runs on Windows)
Try to get it working in WSL
Install a virtual machine using VMWare, VirtualBox, etc. running a Linux distribution and build and run it there
Unfortunately I don't know much about the pros and cons of these approaches so I can't advise you as to the best course.
The route I chose, long long ago, was to get rid of Windows entirely and just use GNU/Linux. But of course that won't be possible for everyone :).
You need a real answer: you can't compile using the Windows Command Prompt.
Get yourself MSYS2 -- which will also install MinGW-w64 -- and follow the setup instructions. Then launch the MSYS shell to get a unix-y terminal prompt (zsh, IIRC), and change directory to the head of the project folder.
To access the Windows filesystem the root directory will be either /c or /mnt/c (sorry, on my mobile ATM, I can improve this in a day or two). For example,
C:\Users\qa21944\git\tacs
becomes
/c/Users/qa21944/git/tacs
From there the GNU/Windows make command should work:
mingw32-make
There is also this post to use more modern *nix environments under Windows:
How to install and use "make" in Windows?
Thanks may be overkill for what you need, though.
When I finish traveling and can sit down to look at this properly I can improve this answer's details, but getting a Linux shell terminal is what you need.

GNU Make Exits Due to Syntax Error in If Statement

I'm building a C++ project using GNU Make (version 3.80). The makefile is auto-generated from the tool I'm using (IBM Rational Rhapsody). An example of this makefile is at the end of this post.
This makefile has a mechanism that allows me to specify a directory for object files (the OBJ_DIR variable). If this is set, the variable CREATE_OBJ_DIR is set up with the command if not exist $(OBJ_DIR) mkdir $(OBJ_DIR). This is then called for each object file in the project.
Running this makefile without setting an object file directory works as expected; the code is compiled without issues. But running it with OBJ_DIR set to 'build' causes the following error:
C:\Users\XXX\AppData\Local\Temp\make52963.sh: C:\Users\XXX\AppData\Local\Temp\make52963.sh: line 2: syntax error: unexpected end of file
C:\Tools\XXX\x86-win32\bin\make.exe: *** [build/Example.o] Error 2
I'm certain the issue is within the rule for '/build/Example.o', when $(CREATE_OBJ_DIR) is called. If I manually edit the rule and replace $(CREATE_OBJ_DIR) with mkdir $(OBJ_DIR), the command is executed correctly. If I then replace it with if not exist build mkdir build directly, to eliminate any issues due to variable expansion, the same error appears.
Other things I have tried:
Run a cmd shell with the same environment variables set as when the makefile is called, and attempted to run the if not exist build mkdir build command. No issues with this.
Ensure that no trailing characters are present in the command run within the makefile. None appear to be present.
My only conclusion at this point is that something about if statements causes the makefile to fail, but I'm not sure what. Is there anything else I should try to track down the source of this problem? Am I missing something obvious.
Let me know if more details are required.
Note: I've edited this makefile pretty heavily, so it's just to give an idea of what I'm using, and probably won't execute. Some of the environment variables below are set up in a batch file prior to calling make, but I'm confident they're not part of the issue I'm seeing, as the makefile works correctly except in the situation described above.
CPU = XXX
TOOL = gnu
INCLUDE_QUALIFIER=-I
LIB_CMD=$(AR)
LINK_CMD=$(LD)
CPP_EXT=.cpp
H_EXT=.h
OBJ_EXT=.o
EXE_EXT=.out
LIB_EXT=.a
TARGET_NAME=Example
all : $(TARGET_NAME)$(EXE_EXT) Example.mak
TARGET_MAIN=Example
LIBS=
INCLUDE_PATH=
ADDITIONAL_OBJS=
OBJS= \
build/Example.o \
OBJ_DIR=build
ifeq ($(OBJ_DIR),)
CREATE_OBJ_DIR=
else
CREATE_OBJ_DIR= if not exist $(OBJ_DIR) mkdir $(OBJ_DIR)
endif
build/Example.o : src/Example.cpp
#echo Compiling src/Example.cpp
$(CREATE_OBJ_DIR)
#$(CXX) $(C++FLAGS) -o build/Example.o src/Example.cpp
You are thinking to complex. A far simpler solution here is to use:
mkdir -p $(OBJ_DIR)
This will also make it work if OBJ_DIR=my/little/obj/dir/deep/down/the/rabit/hole.
Look at the following Makefile:
OBJ_DIR=foo
CREATE_OBJ_DIR= if not exist $(OBJ_DIR) mkdir $(OBJ_DIR)
$(info CREATE_OBJ_DIR=$(CREATE_OBJ_DIR))
all:
$(CREATE_OBJ_DIR)
and it's output:
% make
CREATE_OBJ_DIR=if not exist foo mkdir foo
if not exist foo mkdir foo
/bin/sh: 1: Syntax error: end of file unexpected (expecting "then")
Makefile:8: recipe for target 'all' failed
make: *** [all] Error 2
Your "if" statement is simply not valid shell syntax. On the other hand if OBJ_DIR is empty then CREATE_OBJ_DIR is empty and that is valid.

Automated make system with regex pattern matching

I use GNU make for building reports (LaTeX for source, python for figures, etc.). For targets, I use extensively pattern matching, for example:
all : sample1_test1.png sample2_test1.png sample2_test.png
sample%_test1.png : sample%.dat
python gen_figure.py $< $# --test 1
sample%_test2.png : sample%.dat
python gen_figure.py $< $# --test 2
Now, to simplify the rules I would like to use multiple pattern groups (like regex groups) and use the matches separately in the build rule, for example:
all : sample1_test1.png sample2_test1.png sample2_test.png
sample(?P<Sample>[0-9]+)_test(?P<Test>[0-9]+).png : sample$(<Sample>).dat
python gen_figure.py $< $# --test $(<Test>)
(the syntax is arbitrary, but the point is that I can define two different match groups called Sample and Test and use them as parameters to my script).
How would I achieve this in make or another build system (waf, scons etc.)?
To do it in GNU make, you can use one of two different "metaprogramming" models supported by GNU make:
Auto-generated include files. In your main makefile, add -include generated.mk then write a makefile rule with the target generated.mk (probably listing Makefile as a prerequisite), where the recipe generates the appropriate targets based on the list of targets. You have the full power of the shell to construct your target lists however you want. Every time you modify the makefile, the included file will be rebuilt then GNU make will automatically re-exec itself so you don't have to do anything extra.
Use GNU make's $(eval ...) function, probably combined with $(call ...) and $(foreach ...), to automatically evaluate rules. To do this you define a "template" for the rule using define ... enddef, with variables installed where you want to provide arguments, then use $(call ...) to instantiate them, use $(eval ...) on the result of the call, and do it in a loop for each target. Something like: $(foreach T,$(TARGETS),$(eval $(call DEFINERULE,$(T))))
Here's an example of method 1. Suppose you have this predefined content in your makefile:
TESTS := sample1_test1.png sample2_test1.png sample2_test.png
Then you can use this makefile to get something like the above:
all: $(TESTS)
-include generated.mk
generated.mk : Makefile
#rm -f '$#'
#for t in $(TESTS); do \
eval `echo "$$t" | sed 's/^sample\([0-9]*\)_test\([0-9]*\).*/sample=\1 test=\2/'`; \
echo "$$t : sample$$sample.dat ; python gen_figure.py \$$< \$$# --test $$test" >> '$#'; \
done
Note I just wrote this off the top of my head but I think it will work.

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.