Makefile: rule that match multiple patterns - makefile

I have this rule in my Makefile, that responds to flags I pass:
$(BUILD_DIR)/disable_%:
mkdir -p $(BUILD_DIR)
touch $(BUILD_DIR)/disable_$*
rm -f $(BUILD_DIR)/enable_$*
cd $(BUILD_DIR) && rm -f Makefile
$(BUILD_DIR)/enable_%:
mkdir -p $(BUILD_DIR)
touch $(BUILD_DIR)/enable_$*
rm -f $(BUILD_DIR)/disable_$*
cd $(BUILD_DIR) && rm -f Makefile
What this means is that when changing the flags by which I invoke the makefile, I can trigger some recompilations that could depend on these flags.
The code presented above is a bit redundant: you see that I remove a file, touch another and remove a Makefile in both cases. The only thing that changes is the name of the files that I touch/remove, and they are related.
For instance,
make clean
make enable_debug=yes enable_video=no # will compile from zero
make enable_debug=no enable_video=no # flag change detected -> recompile some submodules that depend on this flag
Provided that the only thing that changes between the two rules ( [en|dis]able ), what I would like is to only have 1 generic rule, something like that:
# match 2 parts in the rule
$(BUILD_DIR)/%ble_%:
mkdir -p $(BUILD_DIR)
touch $(BUILD_DIR)/(???)ble_$* # should be $#
rm -f $(BUILD_DIR)/(???)able_$* # should be disable if $# is enable and inverse
cd $(BUILD_DIR) && rm -f Makefile
Is this possible ?
PS: Sorry if I didn't get the title correctly, I couldn't figure how to explain it better.

$(BUILD_DIR)/enable_% $(BUILD_DIR)/disable_%:
mkdir -p $(BUILD_DIR)
rm -f $(BUILD_DIR)/*able_$*
touch $#
cd $(BUILD_DIR) && rm -f Makefile
Not literally what you wanted (multi-wildcards are forbidden in make), but does quite the same.

Related

Makefile -j does useless things

I have a shell program that generates 3 files from one source file. For instance I can have 'makedocumentation' that take in input foo.tex and generates foo.pdf, foo.dvi and foo.ps.
Because of performances issues I have to call my Makefile with the option -j8.
With this example file:
names = a b
src = $(addsuffix .def,$(names))
a = $(patsubst %.def,%.inc,$(src))
b = $(patsubst %.def,%.asm,$(src))
c = $(patsubst %.def,%.h,$(src))
obj = $(a) $(b) $(c)
command= cp $(1) $(basename $(1)).inc; \
cp $(1) $(basename $(1)).asm; \
cp $(1) $(basename $(1)).h
all: $(obj)
init: clean $(src)
$(src): %:
touch $#
$(a): %.inc: %.def
$(call command, $<)
$(b): %.asm: %.def
$(call command, $<)
$(c): %.h: %.def
$(call command, $<)
clean:
-rm -f *.def
-rm -f *.inc
-rm -f *.asm
-rm -f *.h
If I run it without -j I get:
$ make
cp a.def a.inc; cp a.def a.asm; cp a.def a.h
cp b.def b.inc; cp b.def b.asm; cp b.def b.h
And in the other case:
$ make -j8
cp a.def a.inc; cp a.def a.asm; cp a.def a.h
cp b.def b.inc; cp b.def b.asm; cp b.def b.h
cp a.def a.inc; cp a.def a.asm; cp a.def a.h
cp b.def b.inc; cp b.def b.asm; cp b.def b.h
cp a.def a.inc; cp a.def a.asm; cp a.def a.h
cp b.def b.inc; cp b.def b.asm; cp b.def b.h
We can observe that in parallel mode, make regenerates files that already exists.
I would like to avoid it...
I also tried something like:
a.inc a.asm a.h: a.def
command $<
But I don't get any better result.
Any help ?
As you wrote the original makefile you haven't told make that the output files share any relationship so it can't know that they've already been built and so it has to try to build each one each time.
Your modified idea is the right idea but unforunately make doesn't interpret that sort of definition as indicating that the single command will generate all the files and instead it sees it as a way to build each individual file (written in a compact shorthand).
A rule with multiple targets is equivalent to writing many rules, each
with one target, and all identical aside from that.
The only way to tell make that N output files are generated from 1 rule body is with multiple pattern rules
Pattern rules may have more than one target. Unlike normal rules, this
does not act as many different rules with the same prerequisites and
recipe. If a pattern rule has multiple targets, make knows that the
rule's recipe is responsible for making all of the targets. The recipe
is executed only once to make all the targets. When searching for a
pattern rule to match a target, the target patterns of a rule other
than the one that matches the target in need of a rule are incidental:
make worries only about giving a recipe and prerequisites to the file
presently in question. However, when this file's recipe is run, the
other targets are marked as having been updated themselves.

Makefile: Copying files with a rule

I am trying to copy files using a my rule but my rule does not get triggered:
BUILDDIR = build
COPY_FILES = code/xml/schema/schema.xsd config.txt
all: $(BUILDDIR) $(COPY_FILES) copy
$(BUILDDIR):
mkdir -p $#
$(COPY_FILES):
cp -f $# $(BUILDDIR)
copy:
cp -f $(COPY_FILES) $(BUILDDIR)
I am trying to use $(COPY_FILES) but it is not being triggered, although $(BUILDDIR) and copy are triggered. I am not sure what is wrong with my Makefile. I would like to get the $(COPY_FILES) rule to work if possible please (and remove copy). Does anyone please know?
The problem with the $(COPY_FILES) rule is that the targets of that rule are two files that already exist, namely code/xml/schema/schema.xsd and config.txt. Make sees no reason to execute the rule. I'm not sure why Make doesn't execute the copy rule, but I suspect that there's a file called copy confusing the matter. Anyway, [copy] a bad rule.
Try this:
COPY_FILES = $(BUILD_DIR)/schema.xsd $(BUILD_DIR)/config.txt
all: $(COPY_FILES)
$(BUILD_DIR)/schema.xsd: code/xml/schema/schema.xsd
$(BUILD_DIR)/config.txt: config.txt
$(BUILD_DIR)/%:
cp -f $< $#
In my case, I use a simple "for loop" to cp all those files.
For examples, write the rule as following:
RELEASE_DIR = ../rc1
RELEASE_FILES = ai.h main.cc main_async.cc bmp_utils.h bmp_utils.cc
release: $(RELEASE_FILES)
for u in $(RELEASE_FILES); do echo $$u; cp -f $$u $(RELEASE_DIR); done
Then,
make release

Explaining makefile % as well as $< and $#

xpi_built := $(build_dir)/$(install_rdf) \
$(build_dir)/$(chrome_manifest) \
$(chrome_jar_file) \
$(default_prefs)
xpi_built_no_dir := $(subst $(build_dir)/,,$(xpi_built))
$(xpi_file): $(build_dir) $(xpi_built)
#echo "Creating XPI file."
cd $(build_dir); $(ZIP) ../$(xpi_file) $(xpi_built_no_dir)
#echo "Creating XPI file. Done!"
$(build_dir)/%: %
cp -f $< $#
$(build_dir):
#if [ ! -x $(build_dir) ]; \
then \
mkdir $(build_dir); \
fi
can anyone explain me this makefile part? particularly interested in
$(build_dir)/%: % as well as $< and $# directives
two labels $(build_dir) exists, I guess both are executed, but in which order?
$(build_dir)/%: %
cp -f $< $#
This is a static pattern rule which uses automatic variables in its command; $< expands to the leftmost prerequisite, $# expands to the target. If you try to make $(build_dir)/foo (whatever $(build_dir) is), Make will treat this rule as
$(build_dir)/foo: foo
cp -f foo $(build_dir)/foo
The next rule,
$(build_dir):
#if [ ! -x $(build_dir) ]; \
then \
mkdir $(build_dir); \
fi
is for $(build_dir) itself, and is unnecessarily complicated. It says "if $(build_dir) doesn't exist, then mkdir it", and it could be written this way:
$(build_dir):
mkdir $#
It looks as if your primary target is $(xpi_file):
$(xpi_file): $(build_dir) $(xpi_built)
So Make will first make $(build_dir) (if necessary), then the members of the list %(xpi_built), which includes a couple of things of the form $(build_dir)/%. Once those are done, it will execute the commands of this rule: it will cd into $(build_dir), zip some things up, and echo a couple of messages.
See Pattern Rules and Automatic Variables in the GNU make documentation. The first rule matches files inside $(build_dir), not $(build_dir) itself. $< expands to the list of prerequisites of the current rule, $# is the target for the current rule.

makefile internal calls to target

How can I distinguish in makefile, which targets and how(when) they are called internally? I have a makefile with number of targets which are actually variables.
UPD: here is an example
build_dir := $(bin_dir)/build
xpi_built := $(build_dir)/$(install_rdf) \
$(build_dir)/$(chrome_manifest) \
$(chrome_jar_file) \
$(default_prefs)/*
xpi_built_no_dir := $(subst $(build_dir)/,,$(xpi_built))
.PHONY: install
install: $(build_dir) $(xpi_built)
#echo "Installing in profile folder: $(profile_location)"
#cp -Rf $(build_dir)/* $(profile_location)
#echo "Installing in profile folder. Done!"
#echo
$(xpi_file): $(build_dir) $(xpi_built)
#echo "Creating XPI file."
#cd $(build_dir); $(ZIP) -r ../$(xpi_file) $(xpi_built_no_dir)
#echo "Creating XPI file. Done!"
#cp update.rdf $(bin_dir)/
#cp -u *.xhtml $(bin_dir)/
#cp -Rf $(default_prefs) $(build_dir)/; \
$(build_dir)/%: %
cp -f $< $#
$(build_dir):
#if [ ! -x $(build_dir) ]; \
then \
mkdir $(build_dir); \
fi
If you specify a target on the command line, as in make clean Make will attempt to build that target. If you don't (that is, if you just run make), Make will attempt to build the default target; the default target is the first target in the makefile (in your case install) unless you set it to something else with the .DEFAULT_GOAL variable.
When Make tries to build a target, it first builds that target's prerequisites, if necessary. (When is it necessary? When a target is a file or directory that does not exist real (unless it's .PHONY, but that's an advanced topic), or when one of it's prerequisites is newer than the target (unless it's "order-only", but that's an advanced topic)). So if Make is trying to build your all, it will first try to build $(build_dir) and $(xpi_built), which have been defined elsewhere in the makefile.
If you're trying to figure out what Make will do and when, there are tricks you can use. For example, you can run make -n, and Make would tell you what it would do, instead of doing it. Or you can put a command like #echo now making $# in a rule, to tell you what it's doing. Or for even more information:
some_target: preq another_preq and_another
#echo Making $#. The prerequisites are $^. Of those, $? are newer than $#.
other_commands...

how to prevent "directory already exists error" in a makefile when using mkdir

I need to generate a directory in my makefile and I would like to not get the "directory already exists error" over and over even though I can easily ignore it.
I mainly use mingw/msys but would like something that works across other shells/systems too.
I tried this but it didn't work, any ideas?
ifeq (,$(findstring $(OBJDIR),$(wildcard $(OBJDIR) )))
-mkdir $(OBJDIR)
endif
Looking at the official make documentation, here is a good way to do it:
OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)
$(OBJDIR)/%.o : %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
all: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
mkdir -p $(OBJDIR)
You should see here the usage of the | pipe operator, defining an order only prerequisite.
Meaning that the $(OBJDIR) target should be existent (instead of more recent) in order to build the current target.
Note that I used mkdir -p. The -p flag was added compared to the example of the docs.
See other answers for another alternative.
On UNIX Just use this:
mkdir -p $(OBJDIR)
The -p option to mkdir prevents the error message if the directory exists.
You can use the test command:
test -d $(OBJDIR) || mkdir $(OBJDIR)
Here is a trick I use with GNU make for creating compiler-output directories. First define this rule:
%/.d:
mkdir -p $(#D)
touch $#
Then make all files that go into the directory dependent on the .d file in that directory:
obj/%.o: %.c obj/.d
$(CC) $(CFLAGS) -c -o $# $<
Note use of $< instead of $^.
Finally prevent the .d files from being removed automatically:
.PRECIOUS: %/.d
Skipping the .d file, and depending directly on the directory, will not work, as the directory modification time is updated every time a file is written in that directory, which would force rebuild at every invocation of make.
If having the directory already exist is not a problem for you, you could just redirect stderr for that command, getting rid of the error message:
-mkdir $(OBJDIR) 2>/dev/null
Inside your makefile:
target:
if test -d dir; then echo "hello world!"; else mkdir dir; fi
On Windows
if not exist "$(OBJDIR)" mkdir $(OBJDIR)
On Unix | Linux
if [ ! -d "$(OBJDIR)" ]; then mkdir $(OBJDIR); fi
ifeq "$(wildcard $(MY_DIRNAME) )" ""
-mkdir $(MY_DIRNAME)
endif
$(OBJDIR):
mkdir $#
Which also works for multiple directories, e.g..
OBJDIRS := $(sort $(dir $(OBJECTS)))
$(OBJDIRS):
mkdir $#
Adding $(OBJDIR) as the first target works well.
It works under mingw32/msys/cygwin/linux
ifeq "$(wildcard .dep)" ""
-include $(shell mkdir .dep) $(wildcard .dep/*)
endif
If you explicitly ignore the return code and dump the error stream then your make will ignore the error if it occurs:
mkdir 2>/dev/null || true
This should not cause a race hazard in a parallel make - but I haven't tested it to be sure.
A little simpler than Lars' answer:
something_needs_directory_xxx : xxx/..
and generic rule:
%/.. : ;#mkdir -p $(#D)
No touch-files to clean up or make .PRECIOUS :-)
If you want to see another little generic gmake trick, or if you're interested in non-recursive make with minimal scaffolding, you might care to check out Two more cheap gmake tricks and the other make-related posts in that blog.

Resources