I'm trying to create a makefile with wildcard targets but the wildcards should match on variables.
So I have a makefile like this:
A_OUT := bin/a
B_OUT := bin/b
# This does not work
$(%_OUT):
#echo $#
Output:
$ make bin/a
make: *** No rule to make target 'bin/a'. Stop.
I can not use bin/%: because the variables may point to another directory.
Is there any way to do this in GNU make?
If you just want a list of targets to all have the same recipe then just list them all in the same rule definition. e.g.
$(A_OUT) $(B_OUT):
#echo $#
You can use a variable for that too:
A_OUT := bin/a
TGTS += $(A_OUT)
B_OUT := bin/b
TGTS += $(B_OUT)
$(TGTS):
#echo $#
Depending on what you need you may also want to use Static Pattern Rules
$(TGTS) : bin/% :
#echo $# : $*
Related
I'm trying to use a makefile to convert some markdown files to html files. I'm trying to accomplish in a few lines that which I previously had a long python script doing.
In the simple example below, I would like to see this code:
build: $(patsubst src/pages/%.md, output/%.html, $(wildcard src/pages/*.md))
%.html: %.md
#echo $< to $#
and this output
src/pages/index.md to output/index.html
src/pages/about.md to output/about.html
src/pages/contact.md to output/contact.html
src/page/foo/bar.md to output/foo/bar.html
Instead, it says:
$ make build
make: *** No rule to make target 'output/index.html', needed by 'build'. Stop.
I'm missing something very basic here.
Consider the target output/index.html. The dependency...
%.html: %.md
will effectively expand to...
output/index.html: output/index.md
with $* equal to output/index. So make looks for output/index.md but can't find it -- hence the error message.
To get the correct pattern stem ($* == index) you need to add the base directories to the pattern rule...
output/%.html: src/pages/%.md
#echo $< to $#
EDIT 1:
If you're concerned about repeated hard-coded strings such as output and src/pages then you can always assign them to parameters...
OUTPUT_DIR := output
SOURCE_DIR := src/pages
build: $(patsubst $(SOURCE_DIR)/%.md, $(OUTPUT_DIR)/%.html, $(wildcard $(SOURCE_DIR)/*.md))
$(OUTPUT_DIR)/%.html: $(SOURCE_DIR)/%.md
#echo $< to $#
(Assuming that's what you meant by `optimization' in your comment.)
I have the following Makefile:
all: foo/bar/baz
foo/%:
#echo $(VAR)
cp $#.in $#
# This works
foo/bar/%: VAR := Hello world
# This doesn't
foo/bar/%: foo/bar/%.in
foo/bar/baz.in:
touch $#
When I run it, the output is
Hello world
cp foo/bar/baz.in foo/bar/baz
cp: cannot stat ‘foo/bar/baz.in’: No such file or directory
Makefile:4: recipe for target 'foo/bar/baz' failed
make: *** [foo/bar/baz] Error 1
In other words, the pattern-specific variable rule works, but the equivalent syntax to declare an extra prerequisite doesn't. What should I do instead?
The real use case is for copying headers before a build. I wrote
obj/subdir/%.o: CPPFLAGS += -Igen/include
obj/subdir/%.o: | gen/include
gen/include:
# Copy the headers
but the headers don't get copied.
You cannot do this. Pattern rules must define all prerequisite patterns when the rule is created; they cannot be added later.
Writing a pattern rule with no recipe deletes the pattern rule.
I have the following simplified makefile and I'm trying to set different paths based on different targets. Unfortunately, I'm not getting the results that I expect. This is with make version 3.81.
.SECONDEXPANSION:
all: Debug32
# Object directory set by target
Debug32: OBJDIR = objdir32
#OBJDIR = wrongdirectory
# ObjDir is empty here. :(
OBJS = $(addprefix $(OBJDIR)/,DirUtil.o)
$(OBJDIR)/%.o : %.cpp
echo Compile: $#
Debug32: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
echo mkdir $(OBJDIR) - $#
The results are as follows with no setting of OBJDIR:
echo Compile: /DirUtil.o
If I uncomment the "OBJDIR = wrongdirectory" line, I'll get the following results, which are confusing since I see both values of the variable where I think I should only see one:
echo mkdir objdir32 - wrongdirectory -
echo Compile: wrongdirectory/DirUtil.o
I'm assuming that the variables are not being expanded when I think they should, but I can't figure out how to alter this behavior.
From the GNU info manual
Variables and functions in all parts of a makefile are expanded when
read, except for in recipes, the right-hand sides of variable
definitions using '=', and the bodies of variable definitions using
the 'define' directive.
The target-specific variable only applies within recipes. Within
$(OBJS): | $(OBJDIR)
and
$(OBJDIR):
it is getting the global variable.
So working through what happens when you run make Debug32, it sees the contents of OBJS as a prerequisite, which leads to the first rule above. $(OBJDIR) has already been substituted with the global value, and this matches the target-name in the second rule which has also been substituted the same way.
However, when we get to the recipe:
echo mkdir $(OBJDIR) - $#
$(OBJDIR) has not been substituted yet, so it gets the target-specific variable value.
A working version
.SECONDEXPANSION:
all: Debug32
# Object directory set by target
Debug32: OBJDIR = objdir32
OBJDIR = wrongdirectory
Debug32: OBJS = $(addprefix $(OBJDIR)/,obj.o)
OBJS = wrongobjs
Debug32: $$(OBJS)
echo OBJS are $(OBJS)
echo OBJDIR is $(OBJDIR)
%/obj.o: | %
touch $#
OBJDIRS = objdir32 wrongdirectory anotherdirectory
$(OBJDIRS):
# mkdir $(OBJDIR)
mkdir $#
The main change is using $$ in this line:
Debug32: $$(OBJS)
With only a single $, I get the error message
make: *** No rule to make target `wrongobjs', needed by `Debug32'. Stop.
However, with the $$, I get
echo OBJS are objdir32/obj.o
OBJS are objdir32/obj.o
echo OBJDIR is objdir32
OBJDIR is objdir32
The use of secondary expansion has allowed accessing the target-specific variable in the prerequisites.
The other change is that I made OBJS a target-specific variable (because it is). In order to have a rule to build OBJS whatever its value, I had to use a pattern rule:
%/obj.o: | %
To avoid having a separate line for each object file, you could do the following instead:
OBJ_BASENAMES=obj.o obj2.o obj3.o
$(addprefix %/,$(OBJ_BASENAMES)): | %
touch $# # Replace with the proper recipe
The line containing the addprefix macro expands to
%/obj.o %/obj2.o %/obj3.o: | %
Then running make anotherdirectory/obj2.o creates a directory called "anotherdirectory" first, and creates a file called "obj2.o" within it.
Note all possible directories have to be listed in OBJDIRS. There's no way to collect all the rule-specific values of OBJDIR, so listing them is the best choice. The alternative is a % : rule to build any directory, which be capable of matching and building any target, which could be risky. (If you abandon the use of target-specific variables, there is another way of getting a list of directories that could be built: use variables with predictable names like Debug32_OBJDIR instead, and generate a list of their values using make functions.)
Alternatively, a generic rule that doesn't require listing the object files:
SOURCE=$(basename $(notdir $#)).cpp
DIR=$(patsubst %/,%,$(dir $#))
%.o: $$(SOURCE) | $$(DIR)
touch $# # Replace with proper recipe
There is no feature to read a rule in the context of every target, substituting in target-specific variables and acquiring a new rule for each target. Generic rules cannot be written in this way using target-specific variables.
a good way to handle
%/obj.o: | %
touch $#
OBJDIRS = objdir32 wrongdirectory anotherdirectory
$(OBJDIRS):
mkdir $#
is:
%/.:
mkdir -p $#
.SECONDEXPANSION:
then later you can just write, for any target that may need a directory
target: prerequisites | $$(#D)/.
normal recipe for target
Is it possible to add a prerequisite if another file is found in the workspace? Or how else could I achieve the following idea? Basically if my workspace has a lcf file in a specific location I need to make another file.. Something like this:
lcf := ../base_sw/lcf/base.lcf
.PHONY :
all : $(objects)
# if $(lcf) file exists then
all : $(objects) sup.a2l
sup.a2l :
# Perl script runs here to produce sup.a2l
#echo Chris > $#
This should do it:
lcf := $(wildcard ../base_sw/lcf/base.lcf)
.PHONY :
all : $(objects) $(lcf)
Think I've managed to answer this one myself!
The wildcard function returns nothing if lcf file does not exist:
lcf := $(wildcard ../base_sw/lcf/base.lcf)
Start to build the files that need making:
make_these_file := $(obejcts)
If lcf variable is not empty, append to files list:
ifneq ($(lcf),)
make_these_file += sup.a2l
endif
Now our target with files required to make:
.PHONY :
all : $(make_these_file)
sup.a2l :
# Perl script here to produce sup.a2l
#echo Chris > $#
Works for me :)
BUILT_DIR = /tmp/obj
SRC = /source/dir
/tmp/obj/%/builtin.o : $(SRC)/%/*.c
gcc $^ -o $#
But you know :
In order for the pattern rule to apply, its target pattern must match the file name under consideration and all of its prerequisites (after pattern substitution) must name files that exist or can be made.
If i execute make /tmp/obj/hfa/builtin.o,make will complain :
make: *** No rule to make target/tmp/obj/hfa/builtin.o'. Stop.`
How can i modify the Makefile to satisfy my requirement?
You can use Secondary Expansion
BUILT_DIR = /tmp/obj
SRC = /source/dir
.SECONDEXPANSION:
/tmp/obj/%/builtin.o : $$(wildcard $(SRC)/%/*.c)
gcc $^ -o $#
The error you see indicates that there are no .c files which match the pattern $(SRC)/hfa/*.c since the % translates into hfa. So make cannot use the rule you've defined.
Make then starts to use the implicit rules for building and it would not match either.
Finally make gives up.
I just confirmed that the same Makefile and I get the same error only when there are no *.c files in the $(SRC)/hfa directory.
Otherwise, I see the gcc command getting executed.
And from your question, it is not quite clear what your requirement is.
The rule configuration that worked for me:
bar-%-foo:
#touch $#
.SECONDEXPANSION:
foo-%-bar: bar-$$*-foo
#echo "#: $#"
#echo "<: $<"
#touch $#
foo-bar: foo-biz-bar foo-baz-bar
.PHONY: foo-bar
And demo:
$:make foo-bar
#: foo-biz-bar
<: bar-biz-foo
#: foo-baz-bar
<: bar-baz-foo
rm bar-biz-foo bar-baz-foo
$:ls | grep foo
foo-baz-bar
foo-biz-bar