Makefile $(eval $(call)) function - foreach

simple code in makefile
define fun
#echo this is function
endef
78 all:
79 $(foreach objdir, $(OBJECTDIRS), $(eval $(call fun)))
the err message is :
Makefile:79:* missing separator. Stop.**
Note: i have checked the $(OBJECTDIRS) is not empty and $(oobjdir) too
if i change the code to flowing lines:
define fun
#echo this is function
endef
all:
$(call fun())
then it's running good

The make eval function evaluates the text you give is as a makefile (expects makefile sytnax).
The text #echo this is a function is NOT a makefile.
In other words, if you create a Makefile and you put into it the text:
#echo this is a function
(by itself, with nothing else) and you run make on that makefile, you'll get this same missing separator error.

Related

Make offsetting file contents during build

I'm trying to use Make to ... make modular Dockerfiles. Long story short, I want to centralize certain elements and make the composable and reusable, like classes and functions really, but the Dockerfile syntax does not - and according to the developers, will not - offer any facilities in the image of C's #include or similar composability solutions. Not to worry, #include and friends to the rescue!
Except...
I have the following Makefile in my project:
BUILD_DIR := ${CI_PROJECT_DIR}/build
TEMPLATE_FILES := $(shell find ${CI_PROJECT_DIR} -name '*.build')
TEMPLATE_FILENAMES := $(foreach file,$(TEMPLATE_FILES),$(BUILD_DIR)/$(notdir $(file)).built)
BUILT_TEMPLATES := $(TEMPLATE_FILENAMES:.build.built=.built)
DOCKER_FILES := $(shell find ${CI_PROJECT_DIR} -name '*.Dockerfile')
DOCKER_OBJS := $(foreach file,$(DOCKER_FILES),$(BUILD_DIR)/$(notdir $(file)))
all: $(BUILT_TEMPLATES) $(DOCKER_OBJS)
$(BUILD_DIR)/%.built: $(TEMPLATE_FILES) $(BUILD_DIR) # build any templated Dockerfiles
cpp -E -P -o $(BUILD_DIR)/$(notdir $#) -I ${CI_PROJECT_DIR}/modules $<
sed -i 's/__NL__ /\n/g' $(BUILD_DIR)/$(notdir $#)
$(BUILD_DIR)/%.Dockerfile: $(DOCKER_FILES) $(BUILD_DIR)
cp $< $(BUILD_DIR)/$(notdir $(#))
$(BUILD_DIR):
mkdir -p $(BUILD_DIR)
.PHONY: clean
clean:
-rm -r $(BUILD_DIR)
The objective is to run the templated Dockerfiles through GCC to compile the #includes in them into proper Docker instructions, and just copy the rest of the files. Sounds simple enough.
Except that it looks like all the target files are "offset" from their sources - like the file names are correct, but the contents are from a file elsewhere in the list, and with no discernible order either.
One thing that I'm fairly sure is wrong - but even more wrong otherwise - is the line
$(BUILD_DIR)/%.built: $(TEMPLATE_FILES) $(BUILD_DIR) # build any templated Dockerfiles
By all manuals and documentation, it ought to be
$(BUILD_DIR)/%.built: %.build $(BUILD_DIR) # build any templated Dockerfiles
but that's even worse, because then Make just says make: *** No rule to make target '/docker/build/runner-dart-2-18-firebase.built', needed by 'all'. Stop.
I'm out of ideas here, along with my limited knowledge of Make. What am I missing to make Make make - sorry - my Dockerfiles?
This line:
$(BUILD_DIR)/%.built: $(TEMPLATE_FILES) $(BUILD_DIR)
Says that if make wants to build a target that matches that pattern, and it can find all the prerequisites, then the pattern rule matches and the recipe can be used. Let's ignore BUILD_DIR (note that it's always a bad idea to list a directory as a prerequisite, but that's not causing this problem). Suppose TEMPLATE_FILES is set to the value ./foo/foo.build ./bar/bar.build. Now the above rule expands to:
./build/%.built: ./foo/foo.build ./foo/bar.build ./build
What is the recipe?
cpp -E -P -o $(BUILD_DIR)/$(notdir $#) -I ${CI_PROJECT_DIR}/modules $<
First it's always wrong to create a file that is not exactly $# so you should use just $# not $(BUILD_DIR)/$(notdir $#). But more importantly, what will $< be set to? It is always set to the first prerequisite, and the first prerequisite is always ./foo/foo.build. So every time you run this recipe, regardless of which .built file you're trying to create, you will always be preprocessing the first .build file.
Your idea that you want this instead:
$(BUILD_DIR)/%.built: %.build $(BUILD_DIR)
is correct, in general. Why do you get the error? Because if you are trying to build the target ./build/foo.built, then the stem (part that matches %) is foo. Then make will look to see if the prerequisite foo.build exists or can be created, because you said the prerequisite is %.build. That file does NOT exist and CANNOT be created (make doesn't know how to create it), because the file is ./foo/foo.build not foo.build which is a totally different file.
You have three options. You can either write separate rules for each source directory:
$(BUILD_DIR)/%.built: foo/%.build
...
$(BUILD_DIR)/%.built: bar/%.build
...
Or, you can change your generated files so they are not all in the same directory but instead keep the source directory structure; you would change this:
TEMPLATE_FILENAMES := $(foreach file,$(TEMPLATE_FILES),$(BUILD_DIR)/$(notdir $(file)).built)
BUILT_TEMPLATES := $(TEMPLATE_FILENAMES:.build.built=.built)
to just this:
BUILT_TEMPLATES := $(patsubst %.build,$(BUILD_DIR)/%.built,$(TEMPLATE_FILES))
then create the output directory as part of the recipe:
#mkdir -p $(#D)
cpp -E -P -o $# -I ${CI_PROJECT_DIR}/modules $<
sed -i 's/__NL__ /\n/g' $#
Or finally, you could use VPATH to tell make what directories to look in to find the *.build files:
VPATH := $(sort $(dir $(TEMPLATE_FILES)))
(note, you should choose only one of these options).

Makefile variable and loop

I've got some makefile generated by conan which is included by my main Makefile. That generated Makefile (conanbuildinfo.mak) contains some variable lets say it is something like this:
CONAN_LIBS = librarya libraryb libraryc
Starting with this in my main makefile:
LIBS=-lsocket
I'd like to achieve following end result:
LIBS=-lsocket -llibrarya -llibraryb -llibraryc
so iterate over $CONAN_LIBS and add each variable with -l prefix to LIBS.
How can I do it? :)
With GNU make you can try 'foreach':
CONAN_LIBS = librarya libraryb libraryc
LIBS = $(foreach entry, $(CONAN_LIBS), -l$(entry))
all:
echo $(LIBS)
make all
-llibrarya -llibraryb -llibraryc

Show and execute

In this makefile
dirs = $(shell ls)
clean:
$(foreach dir,$(dirs),echo $(dir);)
The output is
$ make clean
echo bin; echo install.sh; echo Makefile; echo README.md; echo utils;
bin
install.sh
Makefile
README.md
utils
Why does it first show the command, then execute it?
How I can omit the first line?
Prepend the command with the # character. Example:
dirs = $(shell ls)
clean:
#$(foreach dir,$(dirs),echo $(dir);)
From the manual (5.2 Recipe Echoing, bold emphasis mine):
Normally make prints each line of the recipe before it is executed. We call this echoing because it gives the appearance that you are typing the lines yourself.
When a line starts with #, the echoing of that line is suppressed. The # is discarded before the line is passed to the shell. [...]
Alternatively:
The -s or --silent flag to make prevents all echoing, as if all recipes started with #.

Makefile ifeq problem

I am trying to use ifeq in my rule pattern and I have problems with it. This is the rule I am having trouble with:
$(OBJS): $(OBJDIR)/%.o : ../%.c
#mkdir -p $(dir $#)
ifeq(mcc.exe,$(CC))
o_file:=$(shell echo $# | sed -e 's/\/cygdrive\///' | sed -e 's/\([a-zA-Z]\)/\1:/')
$(CC) $(CFLAGS) $< -o $(o_file)
else
$(CC) $(CFLAGS) $< -o $#
endif
When I run this, I get:
"/bin/sh: -c: line 0: syntax error near unexpected token `mcc.exe,mcc.exe'
/bin/sh: -c: line 0: `ifeq(mcc.exe,mcc.exe)'"
But, when I don't use indention, then I get : "Makefile:77: * missing separator. Stop."
I am using GNU make 3.81 on Cygwin. Whole issue with the ifeq comes from the fact that I have same Makefile for two toolchains and one of them (mcc.exe) can not cope with the /cygdrive/c/.../something paths, but instead there should be c:/.../something path. If you know any other way to work around this, I would be also very grateful!
Thank you in advance!
The lines containing ifeq..., else, and endif should not start with a tab. If they do they are treated as part of the recipe and sent to the shell; this leads to the syntax error you get from /bin/sh; see the make manual for an example.
I'm not exactly sure why you get an error when you don't use indentation. Perhaps you can't define a variable in a recipe like this?
Edit: I see you've found the answer. So, no indentation, and a space between ifeq and the parenthesis.

How to make two different source directories in a Makefile output to one bin directory?

I have the following Makefile to build my erlang project:
.SUFFIXES: .erl .beam .yrl
ERL_SRC := $(wildcard src/*.erl)
ERL_OBJ := $(patsubst src/%.erl,ebin/%.beam,${ERL_SRC})
all: main
main: ${ERL_OBJ}
ebin/%.beam: src/%.erl
erlc +debug_info -W -o ebin $<
clean:
rm -fr ebin/*.beam
I'm trying to update this to also build my eunit tests in the test/eunit folder and have the output go to the same ebin folder as the src like this:
.SUFFIXES: .erl .beam .yrl
ERL_SRC := $(wildcard src/*.erl)
ERL_OBJ := $(patsubst src/%.erl,ebin/%.beam,${ERL_SRC})
EUNIT_SRC := $(wildcard test/eunit/*.erl)
EUNIT_OBJ := $(patsubst test/eunit/%.erl,ebin/%.beam,${EUNIT_SRC})
all: main
main: ${ERL_OBJ}
ebin/%.beam: src/%.erl test/eunit/%.erl
erlc +debug_info -W -o ebin $<
clean:
rm -fr ebin/*.beam
eunit: ${EUNIT_OBJ}
test: main eunit
Making main works fine, but if I try make test it fails with:
make: *** No rule to make target `ebin/data_eunit.beam', needed by `eunit'. Stop.
The test module data_eunit.erl is located in test/eunit. The problem seems to be with the ebin/%.beam target. If I swap src/%.erl with test/eunit/%.erl then I can build the tests but not the src. How can I do a build from two source folders and have the output go to one output folder?
You can use the vpath/VPATH in your Makefile
.SUFFIXES: .erl .beam .yrl
# use vpath to tell make where to search for %.erl files
vpath %.erl src eunit
# or use VPATH to tell make where to search for any prerequisite
# VPATH=src:eunit
ERL_OBJ = $(patsubst src/%.erl,ebin/%.beam, $(wildcard src/*erl))
ERL_OBJ += $(patsubst eunit/%.erl,ebin/%.beam, $(wildcard eunit/*erl))
all: main
main: ${ERL_OBJ}
ebin/%.beam: %.erl
erlc +debug_info -W -o ebin $<
clean:
rm -fr ebin/*.beam
Perhaps you should greatly simplify your build. My erlang build systems just invoke erl -make with an Emakefile that looks like this:
{"src/*", [debug_info, {outdir, "ebin"}, {i, "include"}]}.
You could, of course, have more than one src loc, but just mix the tests and the regular code -- you're already mixing them in ebin. Don't make it harder on yourself than it needs to be.
This wont really answer your question, but I dont like to have the risk of polluting my ebin/ with test-enabled code. So this is how I organize my toplevel Makefile:
all:
(cd src && erl -make)
test:
(cd test && erl -make && \
erl -noinput -eval 'eunit:test({dir, "."}, [verbose]), init:stop()')
Then I put the following into src/Emakefile:
{['*'],
[{outdir,"../ebin"}]}.
And into test/Emakefile I put
{['../src/*'],
[debug_info, {d, 'TEST'}]}.
{['*'],
[debug_info, {d, 'TEST'}]}.
So if I run make all then src/*.erl is compiled into ebin/, but if I run make test I compile src/*.erl and test/*.erl into test/ and run all beam files there with unit tests.
When compiling the tests the TEST macro is enabled so that unit-tests are disabled if surrounded with ifdefs as the eunit guide suggest:
-ifdef(TEST).
test_code_() -> ...
-endif.
Its a setup that I'm quite pleased with.
You should make another target that compiles that tree into ebin, make it depend on the original build target.
This is doing what I want:
.SUFFIXES: .erl .beam .yrl
ERL_SRC := $(wildcard src/*.erl)
ERL_OBJ := $(patsubst src/%.erl,ebin/%.beam,${ERL_SRC})
EUNIT_SRC := $(wildcard test/eunit/*.erl)
EUNIT_OBJ := $(patsubst test/eunit/%.erl,ebin/%.beam,${EUNIT_SRC})
all: main
main: ${ERL_OBJ}
${ERL_OBJ}: ${ERL_SRC}
erlc +debug_info -W -o ebin $<
clean:
rm -fr ebin/*.beam
${EUNIT_OBJ}: ${EUNIT_SRC}
erlc +debug_info -W -o ebin $<
eunit: ${EUNIT_OBJ}
test: main eunit
Any other way I can improve this? Maybe move similar compile lines in ${ERL_OBJ} and ${EUNIT_OBJ} to a variable.
Instead of "ebin/%.beam: src/%.erl test/eunit/%.erl" have you tried just:
%.beam: %.erl

Resources