recipe commences before first target - makefile

Error : Makefile:12: *** recipe commences before first target. Stop.
My makefile:
objDir := obj
incDir := include
srcDir := src
binDir := bin
files := matrix palindrome encryption
define generateObject
#nasm -f elf32 -o $(objDir)/$(1).o $(srcDir)/$(1).asm
endef
object: $(addprefix $(srcDir)/,$(addsuffix .asm,$(files)))
#echo -n "Generating object files... "
$(foreach file,$(files),$(eval $(call generateObject,$(file))))
#echo "Done"
I read in a post that this could be due to unwanted whitespace/tab but i could not find any.
I tried cat -e -t -v Makefile and the output was :
objDir := obj$
incDir := include$
srcDir := src$
binDir := bin$
files := matrix palindrome encryption$
$
define generateObject$
^I#nasm -f elf32 -o $(objDir)/$(1).o $(srcDir)/$(1).asm$
endef$
$
object: $(addprefix $(srcDir)/,$(addsuffix .asm,$(files)))$
^I#echo -n "Generating object files... "$
^I$(foreach file,$(files),$(eval $(call generateObject,$(file))))$
^I#echo "Done"$

Your problem is use of the eval function. eval is used to parse make constructs, but you're passing it a shell command. Consider this line:
$(foreach file,$(files),$(eval $(call generateObject,$(file))))
Each time through the list you'll call generateObject with a filename. That will expand to a shell command; for example if file is matrix then call will expand to:
^I#nasm -f elf32 -o obj/matrix.o src/matrix.asm
Then you take that text string and pass it to eval which tries to read that as a makefile. Note that the text passed to eval must be a complete and valid makefile in itself; it's like you invoked make recursively and gave it this string as a makefile, except that the result of parsing are applied to the current makefile. You can't give eval just a part of a valid makefile (like one command line in a recipe) and have it insert that into the current makefile. Because that line by itself isn't valid, you get this error.
Instead of running eval on the results you want to concatenate them into one shell command. Try this:
define generateObject
nasm -f elf32 -o $(objDir)/$(1).o $(srcDir)/$(1).asm
endef
object: $(addprefix $(srcDir)/,$(addsuffix .asm,$(files)))
#echo -n "Generating object files... "
#$(foreach file,$(files),$(call generateObject,$(file)) && ) true
#echo "Done"
However, that's really not "the make way". You don't want to build multiple targets within a single rule: that defeats the main point of make which is that it only rebuilds the files that are out of date.
You should write your makefile like this:
object: $(files:%=$(objDir)/%.o)
$(objDir)/%.o : $(srcDir)/%.asm
#nasm -f elf32 -o $# $<
You don't need the generateObject variable, or call, or eval, or even foreach.

Related

How to defer foreach variable expansion in makefile prerequisites?

I have the following makefile that creates a config.json file from terraform output and then parses that config.json file and creates a makefile list variable, I then loop over that list and trigger targets accordingly. The targets copies the directories from output/%/csv to current/%/csv by looping over the list abc xyz
CONFIG = config.json
tf:
terraform output -json > $(CONFIG)
config_file: tf
$(eval obj := $(shell jq -c '.objects.value[]' $(CONFIG)))
output/%/csv : current/%/csv
mkdir -p $(#D)
cp -r $< $#
apply: $(foreach X, $(obj), output/$X/csv)
The above makefile gives an empty output. I expected this result since the prerequisites are expanded immediately and have empty value, to resolve this I added .SECONDEXPANSION: to have the prerequisites expanded in the deferred phase. But then got an error No rule to make target output//csv', needed by apply'. Stop. This means the variables are still assigned an empty value.
.SECONDEXPANSION:
CONFIG = config.json
tf:
terraform output -json > $(CONFIG)
config_file: tf
$(eval obj := $(shell jq -c '.objects.value[]' $(CONFIG)))
output/%/csv : current/%/csv
mkdir -p $(#D)
cp -r $< $#
apply: $(foreach $X, $$(obj), output/$$(X)/csv)
Is there something that I am missing here or is there any better way to do this?
There is no reason to set the obj make variable in a recipe with eval. In almost all examples I saw where eval or shell make functions were used in a recipe it was an error.
What about the following?
OBJ := $(shell terraform output -json | jq -c '.objects.value[]')
TARGETS := $(patsubst %,output/%/csv,$(OBJ))
output/%/csv : current/%/csv
mkdir -p $(#D)
cp -r $< $#
.PHONY: apply
apply: $(TARGETS)

Makefile wildcard and variable expansion

I have this Makefile (abbreviated):
COMPILE = armcc
LINK = armlink
SRCDIR := ./src1 \
./src2
INCLUDES := -I ./inc
CSRC := $(wildcard $(SRCDIR)/*.c)
# CSRC := ./src1/*.c ./src2/*.c
OBJS := $(CSRC:.c=.o)
.PHONY: clean
clean:
#echo "Clean Finished"
%.o: %.c
#echo Compiling $<
#$(COMPILE) $(INCLUDES) $< -o $#
mktest: $(OBJS) Makefile
#echo $(CSRC)
#echo $(OBJS)
#echo building mktest
#$(LINK) -o mktest.axf
When I run it the wildcard only expanded for the last entry in the SRCDIR variable, which is ./src2. The output shown below.
c:> make mktest
./src1 ./src2/file2.c
./src1 ./src2/file2.o
building mktest
If I replace the line where CSRC defined, with the line below it. It works fine, and the output shown below.
c:> make mktest
./src1/*.c ./src2/*.c
./src1/*.o ./src2/*.o
building mktest
This is OK if I only have a few sub-directories I want to include. But if I want to include more, the Makefile will become ugly. Am I not using the wildcard function properly here?
What you would need your CSRC definition to be is:
CSRC:= $(foreach dir,$(SRCDIR),$(wildcard $(dir)/*))
If you look at the documentation:
$(wildcard pattern…)
This string, used anywhere in a makefile, is replaced by a space-separated list of names of existing files that match one of the given file name patterns…
This means your original line actually reads as:
CSRC := $(wildcard src1/ src2/*.c)
That is files whose names are matching against src1/ or src2/*.c.

Makefile For Loop Pattern Substitution

In the following makefile for loop, how can I edit the string, that the i variable represents, with a pattern substitution in the middle of the string? In my case, I wish to replace any / character in the string with a _ character.
for i in $(MODULES:%.cpp=%); do \
g++ -c Sources/$$i.cpp -o Build/$$i.o; \
done
For example if MODULES = Directory/File.cpp then the inner line should expand to
g++ -c Sources/Directory/File.cpp -o Build/Directory_File.o
This answer is valid only with GNU make and bash.
Simple bash substitution (${parameter/pattern/string}) in the context of a make recipe (double $):
for i in $(MODULES:%.cpp=%); do \
g++ -c Sources/$$i.cpp -o Build/$${i//\//_}.o; \
done
Warning: this works only if the shell used by make is bash. So, add maybe a:
SHELL := bash
at the beginning of your Makefile.
Explanation:
${i/X/_} expands as the value of variable i in which the first occurrence of X is replaced by _.
${i//X/_} expands as the value of variable i in which all occurrences of X are replaced by _.
In your case X is the / character and it must be escaped (\/): ${i//\//_}.
Note that there is probably a less bash and more make way to do the same. Something like:
SRCS := $(shell find Sources -type f -name *.cpp)
OBJS :=
define OBJ_rule
obj := Build/$$(subst /,_,$$(patsubst Sources/%.cpp,%,$(1))).o
OBJS += $$(obj)
$$(obj): $(1)
g++ -c $$< -o $$#
endef
$(foreach s,$(SRCS),$(eval $(call OBJ_rule,$(s))))
.PHONY: objs
objs: $(OBJS)
Which instantiates one rule per module and should do the same... with the significant advantage that, when you type make objs, only the outdated object files are rebuilt. But it's a bit more tricky.

Target name in Makefile prerequisite

I have some rules like this
foo_%: $(BIN_DIR) $(LIB_DIR) $(OBJ_DIR)
gcc stuff
Directory variables like this:
BIN_DIR := $(BUILD_DIR)/$#/$(TARGET)/bin
And a rule to make the directories:
$(BIN_DIR) $(LIB_DIR) $(OBJ_DIR):
mkdir -p $#
I want the (expanded) foo_% part to replace the $#in the directory name, but right now $# gets replaced with nothing.
Now I could just replace $# with % in the BIN_DIR var:
BIN_DIR := $(BUILD_DIR)/%/$(TARGET)/bin
but this omits the foo_ part, which I do want to include.
Last resort is including three mkdir -p statements in each rule, but I'd rather not!
The expansion cannot work because the lists of prerequisites and targets in rules are expanded when the syntax is processed. In other words, statically, at the "compile time" of the makefile rules. Whereas the $# parameter is dynamic; it takes on values in the makefile "run time", when the rule tree is evaluated. At that time, it cannot be substituted into targets and prerequisites.
Since this is for just ensuring that some directories exist, you can move the make -p into the recipe, and do it like this. Note that we still have to change the BIN_DIR := assignment into the non-expanding BIN_DIR = assignment:
TARGET := target
LIB_DIR := lib_dir
OBJ_DIR := obj_dir
BUILD_DIR := build_dir
BIN_DIR = $(BUILD_DIR)/$#/$(TARGET)/bin
foo: | $(LIB_DIR) $(OBJ_DIR) # see text below for explanation of | symbol
mkdir -p $(BIN_DIR)
echo other steps
$(LIB_DIR) $(OBJ_DIR):
mkdir -p $#
Here, we are using the make rule processing to create $(LIB_DIR) and $(OBJ_DIR). But $(BIN_DIR) is handled in a pedestrian way, just by running a mkdir -p in the recipe. The expansion of $# works here because BIN_DIR is an traditional unexpanded-style make variable which undergoes expansion when it is substituted. It is still statically expanded when the rule syntax is processed, but the point is that it holds the unexpanded $# which is inserted into the make -p recipe line. Because that $# is in the recipe line, it works. It just doesn't work in the list of targets or prerequisites.
I used the | symbol to designate $(LIB_DIR) $(OBJ_DIR) as order-only prerequisites. Read about this in the GNU Make manual. Order-only prerequisites are only updated if they are missing. If they already exist, they are disregarded.
If we don't make these directories order-only, then they will trigger a rebuild of the target each time their time-stamp changes such that it is newer than the target.
It's probably best not to name directories as prerequisites anyway, and just put in the just-in-case mkdir -p into the recipe. As in, simply:
TARGET := target
LIB_DIR := lib_dir
OBJ_DIR := obj_dir
BUILD_DIR := build_dir
BIN_DIR = $(BUILD_DIR)/$#/$(TARGET)/bin
foo:
mkdir -p $(LIB_DIR) $(OBJ_DIR) $(BIN_DIR)
echo other step
Run:
$ make
mkdir -p lib_dir obj_dir build_dir/foo/target/bin
echo other steps
other steps

Iterate to next prerequisite file when matching when given variables for statement

I'm trying to do something like this with make:
SRC := $(src/*.md)
DIST := $(subst -,/,$(patsubst src/%.md, dist/%/index.html, $(SRC)))
all: $(DIST)
$(DIST): $(SRC)
mkdir -p $(#D) && pandoc $< -o $#
E.g., the prerequisite src/2014-04-myfile.md is put into target dist/2014/04/myfile/index.html with the transform pandoc
But when I use $< it only refers to the first argument in the $(SRC) variable.
I know normally we would do something like:
dist/%.html: src/%.md
but since I changed the file name in the output to just index.html for all files and used the original file name to create a new path I'm not sure how to go about iterating over the prerequisites.
Here's one way it could be done. The way this works is that it iterates over $(SRC) to create one rule per source file. The $$ in MAKE_DEP are necessary to prevent make from interpreting the functions when it first reads the contents of MAKE_DEP. The documentation on call and eval are also useful.
SRC := $(wildcard src/*.md)
# Set the default goal if no goal has been specified...
.DEFAULT_GOAL:=all
#
# This is a macro that we use to create the rules.
#
define MAKE_DEP
# _target is a temporary "internal" variable used to avoid recomputing
# the current target multiple times.
_target:=$$(subst -,/,$$(patsubst src/%.md, dist/%/index.html, $1))
# Add the current target to the list of targets.
TARGETS:=$$(TARGETS) $$(_target)
# Create the rule proper.
$$(_target):$1
mkdir -p $$(#D) && pandoc $$< -o $$#
endef # MAKE_DEP
# Iterate over $(SRC) to create each rule.
$(foreach x,$(SRC),$(eval $(call MAKE_DEP,$x)))
.PHONY: all
all: $(TARGETS)
If I create:
src/2000-01-bar.md
src/2014-04-foo.md
and run $ make -n, I get:
mkdir -p dist/2000/01/bar && pandoc src/2000-01-bar.md -o dist/2000/01/bar/index.html
mkdir -p dist/2014/04/foo && pandoc src/2014-04-foo.md -o dist/2014/04/foo/index.html
This could also be done using secondary expansion but it did not appear to me to be simpler or nicer.

Resources