Explaining makefile % as well as $< and $# - syntax

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.

Related

How can I create the necessary out-directories for targets with make?

Say, in a Makefile, I have the following targets:
EXES=dir1/subdir1/abc dir1/subdir1/def dir1/subdir2/ghi dir1/subdir2/jkl dir2/subdir3/mno dir2/subdir3/pqr
Each item in $(EXES) represents a binary to be created. I want to make sure that the necessary directories (in the example: dir1/subdir1, dir1/subdir2, dir2/subdir3) are created if they are not existent.
How would I achieve this with gnu-make?
Use order-only prerequisites:
target: prerequisites | order-only-prerequisites
order-only-prerequisites:
recipe
Their recipe is executed only if they do not exist yet. Example:
$(BUILDDIR)/foo.o: src/foo.c | $(BUILDDIR)
$(CC) $(CFLAGS) -o $# $<
$(BUILDDIR):
mkdir -p $#
And if you want to extract the list of directories to create from the definition of your EXES variable:
$(EXES): | $(dir $(EXES))
$(dir $(EXES)):
mkdir -p $#
Or, to instantiate exactly one rule per target:
define DIR_rule
$(1): | $$(dir $(1))
endef
$(foreach e,$(EXES),$(eval $(call DIR_rule,$(e))))
$(dir $(EXES)):
mkdir -p $#
I finally found a solution with $(dir ...)
EXE_DIRS=$(dir $(EXES))
EXE_DIRS_UNIQUE=$(shell for DIR in $(EXE_DIRS); do echo $$DIR; done | sort | uniq)
$(shell for DIR in $(EXE_DIRS_UNIQUE); do if [ ! -d $$DIR ]; then mkdir -p $$DIR; fi; done)
all: $(EXES)

makefile run execute one time for all rules

I have write a makefile that prepare some files. I create ORIGINAL directory and then I use the file inside the folder for start the others rules
RDIR=.
RFILES:=$(wildcard $(RDIR)/*.vcf)
OUTDIR=ORIGINAL
OUTFILES=$(patsubst %.vcf,$(OUTDIR)/%.gz,$(RFILES))
BCFTOOLS=bcftools
OUTSOMATIC=SOMATIC
OUTVARDICT=$(patsubst
$(OUTDIR)/%vardict.gz,$(OUTSOMATIC)/%.somatic.vcf,$(wildcard
$(OUTDIR)/*vardict.gz))
OUTMUTEC2=$(patsubst
$(OUTDIR)/%mutect2_all.gz,$(OUTSOMATIC)/%mutect2.somatic.vcf,$(wildcard
$(OUTDIR)/*mutect2_all.gz))
OUTVARSCAN2=$(patsubst
$(OUTDIR)/%varscan.gz,$(OUTSOMATIC)/%varscan2.somatic.vcf,$(wildcard
$(OUTDIR)/*varscan.gz))
.PHONY: all
all: $(OUTDIR) $(OUTFILES) $(OUTSOMATIC) $(OUTVARDICT) $(OUTMUTEC2)
$(OUTVARSCAN2)
$(OUTDIR)/%.gz: %.vcf
bgzip -c $< > $#
$(OUTDIR):
test -d $# || mkdir $#
$(OUTSOMATIC):
test -d $# || mkdir $#
$(OUTSOMATIC)/%.somatic.vcf: $(OUTDIR)/%vardict.gz
$(BCFTOOLS) view -f PASS -i 'INFO/STATUS ~ ".*Somatic"' $< > $#
$(OUTSOMATIC)/%mutect2.somatic.vcf: $(OUTDIR)/%mutect2_all.gz
$(BCFTOOLS) view -f PASS $< > $#
$(OUTSOMATIC)/%varscan2.somatic.vcf: $(OUTDIR)/%varscan.gz
$(BCFTOOLS) view -f PASS -i 'SS="2"' $< > $#
clean:
rm -rf $(OUTDIR)
rm -rf $(OUTSOMATIC)
I need to launch 3 time make -f Makefile for execute all the rules. How
can improve that script?
What is the right way?
thanks for any help
If I understand you correctly, your makefile is zip vcf files in one directory into gz files in a second directory, then use those gz files to build vcf files in a third directory (building the directories as needed), and that those final vcf files are the real goal.
You can do it in one pass, if you modify the variable assignments to derive target names from the planned gz files, not the gz files that already exist:
OUTVARDICT=$(patsubst $(OUTDIR)/%vardict.gz,$(OUTSOMATIC)/%.somatic.vcf,$(filter $(OUTDIR)/%vardict.gz, $(OUTFILES)))
OUTMUTEC2= $(patsubst $(OUTDIR)/%mutect2_all.gz, $(OUTSOMATIC)/%mutect2.somatic.vcf, $(filter $(OUTDIR)/%mutect2_all.gz, $(OUTFILES)))
OUTVARSCAN2 = $(patsubst $(OUTDIR)/%varscan.gz,$(OUTSOMATIC)/%varscan2.somatic.vcf, $(filter $(OUTDIR)/%varscan.gz, $(OUTFILES)))
and modify the rules to allow Make to determine which intermediates to build:
all: $(OUTVARDICT) $(OUTMUTEC2) $(OUTVARSCAN2)
$(OUTDIR)/%.gz: %.vcf $(OUTDIR)
bgzip -c $< > $#
$(OUTDIR):
test -d $# || mkdir $#
$(OUTSOMATIC):
test -d $# || mkdir $#
$(OUTSOMATIC)/%.somatic.vcf: $(OUTDIR)/%vardict.gz $(OUTSOMATIC)
$(BCFTOOLS) view -f PASS -i 'INFO/STATUS ~ ".*Somatic"' $< > $#
$(OUTSOMATIC)/%mutect2.somatic.vcf: $(OUTDIR)/%mutect2_all.gz $(OUTSOMATIC)
$(BCFTOOLS) view -f PASS $< > $#
$(OUTSOMATIC)/%varscan2.somatic.vcf: $(OUTDIR)/%varscan.gz $(OUTSOMATIC)
$(BCFTOOLS) view -f PASS -i 'SS="2"' $< > $#

Copy files with GNU make

I need to copy several files. Doing a makefile:
FILES=foo.txt d1/bar.dat d2/baz.txt
TARGETDIR=/app
targets=$(addprefix $(TARGETDIR)/,$(FILES))
all: $(targets)
$(targets): $(FILES)
cp $(subst $(TARGETDIR)/,,$#) $#
Files copied correctly, but if I do touch foo.txt, all three files are copied.
I know that "the correct way" is to define three rules like:
$(TARGETDIR)/foo.txt: foo.txt
cp $^ $#
$(TARGETDIR)/d1/bar.dat: d1/bar.dat
cp $^ $#
$(TARGETDIR)/d2/baz.txt: d2/baz.txt
cp $^ $#
But in this case I have to write names of the files twice, once for these rules and once for all rule.
Is there a way to 'multiply' the rule for each name in the prerequisite?
Something like
$(TARGETDIR)/%: $(FILES)
cp $< $#
You can manipulate the target name using the text manipulation functions, if you use secondary expansion:
.SECONDEXPANSION:
$(targets): $$(patsubst $(TARGETDIR)/%, %, $$#)
echo cp $< $#
This is not the only way, but it's probably the simplest.

Makefile: rule that match multiple patterns

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.

error in makefile

I am using gnu Make 3.82 and have an annoying problem.
I have a rule setting dependencies between directories.
OBJDIR=../obj
$(objdir)/%.o: %.C
$(COMPILE) -MM -MT$(objdir)/$(notdir $#) $< -o $(DEPDIR)/$(notdir $(basename $<).d )
$(COMPILE) -o $(objdir)/$(notdir $# ) -c $<
In order to do this, the obj directory must exist.
I want to mkdir the directory as a prerequisite
$(objdir)/%.o: %.C $(objdir)
$(COMPILE) -MM -MT$(objdir)/$(notdir $#) $< -o $(DEPDIR)/$(notdir $(basename $<).d )
$(COMPILE) -o $(objdir)/$(notdir $# ) -c $<
$(objdir):
mkdir $(objdir)
This doesn't work, because it fails when the directory is there and then the make stops
I tried shell
if [ ! -d $(objdir) ] ; then \
mkdir $(objdir) \
fi
but obviously I've got something wrong. What's the best way of doing this?
One simple way is to use:
mkdir -p ../obj
It doesn't fail when the directory exists.
I usually create a macro, MKPATH, for this:
MKPATH = mkdir -p
and then reference the macro in the rule:
$(objdir):
$(MKPATH) $(objdir)
That way, I can change the behaviour without changing the makefile if it becomes necessary.
Your shell fragment:
if [ ! -d $(objdir) ] ; then
mkdir $(objdir)
fi
does not work as written because make executes each line separately.
You could write (note the added semi-colon):
if [ ! -d $(objdir) ] ; then \
$(MKPATH) $(objdir) ; \
fi
Or:
if [ ! -d $(objdir) ] ; then $(MKPATH) $(objdir); fi
Or:
[ -d $(objdir) ] || $(MKPATH) $(objdir)
Note that the command line must be successful overall, so do not try:
[ ! -d $(objdir) ] && $(MKPATH) $(objdir)
If the directory exists, the first alternative fails, but the shell exits with a non-zero status, thus failing...and causing the build to fail.
mkdir
"mkdir -p"
Change:
$(objdir): mkdir $(objdir)
to =>
$(objdir):
mkdir -p $(objdir)
If that particular mkdir does not have -p then:
$(objdir):
test -d $(objdir) || mkdir $(objdir)
Makefiles
Keep the target: and the comands (mkdir, etc) on seperate lines.
Also, in make, to ignore failed commands, prefix command with minus:
$(objdir):
-mkdir $(objdir)
Commands (if-then-else; for loops, etc) with multiple lines require adding `\;' to represent newlines to the shell:
$(objdir):
if [ ! -d $(objdir) ] ; then \
mkdir $(objdir) ; \
fi
This particular usage of if-then-else can also written as:
$(objdir):
if [ ! -d $(objdir) ] ; then mkdir $(objdir) ; fi
The following Makefile that demonstrates each point above
all: setup dirs report
# Create an intefering dir1
# Remove dir2. It is work to be done later.
setup:
#mkdir -p dir1
#if test -d dir2 ; then rmdir dir2 ; fi
# Continue (with dir2), even though dir1 re-creation fails
dirs:
-mkdir dir1
mkdir -v dir2
# Show we're still running
report:
#echo DIRS:
#for d in dir?; do \
test -d $$d || break ; \
echo -n "$$d " ; \
done
#echo
Output from running running make:
mkdir dir1
mkdir: cannot create directory `dir1': File exists
make: [dirs] Error 1 (ignored)
mkdir -v dir2
mkdir: created directory `dir2'
DIRS:
dir1 dir2

Resources