Wildcards in Makefile rules - makefile

I have the following rules in a Makefile
%.00.png: %.dat
genimg.py $< $#
%.10.png: %.dat
genimg.py $< $#
%.20.png: %.dat
genimg.py $< $#
%.30.png: %.dat
genimg.py $< $#
where genimg.py is a script that generates an image based on the data in a *.dat file and a parameter which is stored in the name of the target file (00, 10, 20, 30). Is it possible to combine all these rules into one? I tried the obvious:
%.*.png: %.dat
genimg.py $< $#
but that does not work.

No, there's no way to combine this into one rule.
But, you could auto-generate the rules. Assuming you can write it as a single line you can use something like:
SIZES := 00 10 20 30
$(foreach S,$(SIZES),$(eval %.$S.png: %.dat ; genimg.py $$< $$#))

You can achieve something very close to this using secondary expansion, the automatic variable $* and the basename function :
.SECONDEXPANSION:
%.png: $$(basename $$*).dat
genimg.py $< $#
But this has the following limitation that a file named foo.png will still be fitted by this rule and depend upon foo.dat.
If this is not acceptable behavior for your application, you can use the more elaborate version :
.SECONDEXPANSION:
# This relies on the assumption that there's no file named '/-'
%.png: $$(if $$(word 2,$$(subst ., ,$$*)),$$(word 1,$$(subst ., ,$$*)).dat,/-)
genimg.py $< $#
But this comes with the additional limitation that a file named a.b.10.png will get as a prerequisite a.dat instead of the expected a.b.dat.

Related

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 - generic target rule with src- and obj-files in nested directories

I have code sorted in nested directories like
src/cmn/abc.cpp
src/voc/xyz.cpp
And desired object output should be
obj/cmn/abc.o
obj/voc/xyz.o
The Makefile entries are
SRC_FILES := src/cmn/abc.cpp src/voc/xyz.cpp
OBJ_FILES := $(patsubst %.cpp,*.o,$(patsubst src/%,obj%,$SRC_FILES))
The generic target rule is simple (too simple) and not working as desired. It creates the obj-files right next to the src-files as it misses pattern substitution. Further it misses directory creation (like obj/voc).
.cpp.o:
#$(CC) $(CC_FLAGS) $< -o $#
How should a target be defined to achieve the desired goals from above?
Since you're using GNU make already (patsubst) you might as well use pattern rules which are much more powerful than suffix rules:
obj/%.o : src/%.c
#mkdir -p $(#D)
$(CC) $(CC_FLAGS) -c $< -o $#

Makefile target matching

I'm having troubles with my Makefile :-(
I have a mix of assembly and C sourcecode that I need to link together. I need different build-instructions for those two types. Since both the assembler and C compiler output *.o files, I cannot use the general %.o:%.c construction often found in example Makefiles
This what I'm trying now:
Get a list of all C files and their resulting output files:
C_SRCFILES := $(shell find $(SRCDIRS) -type -f -name "*.c")
C_OBJFILES := $(patsub %.c,%.o,$(C_SRCFILES))
Get a list of all asm files and their resulting output files:
A_SRCFILES := $(shell find $(SRCDIRS) -type -f -name "*.asm")
A_OBJFILES := $(patsub %.asm,%.o,$(A_SRCFILES))
When I echo those vars to the screen, they seem to be correct, but how I do define my targets now?
I tried something like this
$(A_OBJFILES): ($A_SRCFILES)
$(AS) $(AFLAGS) -o $# $*
$(C_OBJFILES): ($C_SRCFILES)
$(CC) $(CFLAGS) -c -o $# $*
all: $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $(A_OBJFILES) $(C_OBJFILES) -o $(TARGET_OUTPUT)
but ofcourse, this doesn't work...
Any suggestions?
First problem: a misplaced parenthesis or two.
$(A_OBJFILES): ($A_SRCFILES)
Notice that you have the $ inside the ( in ($A_SRCFILES). Make expands $A, which is nothing, and things go downhill. I think you meant $(A_SRCFILES), and the same thing in the other rule.
Second problem: I don't know the syntax of the assembler, but the syntax of the compiler command is wrong:
$(CC) $(CFLAGS) -c -o $# $*
The variable $* is nothing if we're not in a pattern rule, which we're not (yet). And anyway, if we were in a pattern rule and you were trying to build foo.o, this command would look for the source file foo, and there's no such file. Do it this way:
$(CC) $(CFLAGS) -c -o $# $<
Third problem: each object file depends on all source files (in each rule). Try this instead:
$(A_OBJFILES): %.o : %.asm
...
$(C_OBJFILES): %.o : %.c
...
(Now it's a pattern rule.)
Fourth problem: a lot of redundancy in the last rule. Change it to this:
all: $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $^ -o $(TARGET_OUTPUT)
or better still:
all: $(TARGET_OUTPUT)
$(TARGET_OUTPUT): $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $^ -o $#
Since both the assembler and C compiler output *.o files, I cannot use the general %.o:%.c construction often found in example Makefiles
Sure you can:
%.o : %.c
# commands to make .o from a corresponding .c
%.o : %.asm
# commands to make .o from a corresponding .asm

How can I manage make targets that don't have suffixes?

I've got a make file that generates multiple targets. Something like:
target-a: target-a.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
target-b: target-b.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
target-c: target-c.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
The actual build process (abbreviated as $(BUILD_TOOL) above) is a multiple line thing involving compilers, scripts and various whatnot, but suffice to say, the build process acts on the first target dependency ($<) and produces the output target ($#).
This is quite unwieldly. Would what I've got below be considered a safe way to replace the above (using a pattern rule that doesn't have a suffix)?
all: target-a target-b target-c
% : %.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
The make tool is GNU, and I'm content to use it's powerful extensions.
If target is a literal string, renierpost's solution is very good. If it isn't (or even if it is) this will work:
TARGETS := target-a target-b target-c
all: $(TARGETS)
$(TARGETS): % : %.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
Note that this rule will not build targets you did not intend, not even target-include.
It depends on the rest of your Makefile, but in principle this should work,
if all files are in one directory.
It's better practice to use extensions on your targets.
Is target a literal string? In that case, you can be more specific
(and speed up rule application a tiny little bit, but it's fast already) by using
all: target-a target-b target-c
target-% : target-%.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
GNU make's advanced syntax will come into play if you want to automatically deduce the names of target-a target-b target-c from the target-*.src filenames on the filesystem or something similar.

How to add different rules for specific files?

I have a certain problem with my Makefile.
With this command, I can compile all my *.c files to *.o which works well:
$(OBJ) : %.o : %.c $(LDSCRIPT) Makefile
$(CC) $(ARM9_INCLUDES) -c $(CFLAGS) $< -o $#
But now I'm wondering, what if I want to run -O3 optimization on just ONE particular file, and have -O0 on the rest?
Is there any command to add a different rule for a specific file?
What I'm doing right now is compiling each C file with its own rules, which is very annoying because I have around 30 files which makes the Makefile huge, and every time I change something in one file it compiles EVERYTHING again.
particular_file.o : CFLAGS+=-O3
(assuming GNU make) see target-specific variable values in GNU Make manual
(and the immediately following pattern-specific variable values, maybe).
Also note, that commands are used from the most specific rule for given file, so you can have in case target-specific variable value is not sufficient:
particular_file.o : particular_file.c
completely_special_compiler -o $# $<
%.o : %.c
$(CC) $(CFLAGS) -o $# $<
It's possible to make the solution a bit more extensible.
Suppose you need to compile one set of files in one way and the other set of files in another way, rather than having only one exception, and you could identify patterns in those two sets of files, e.g. one set starts with "a", and the other set starts with "b", you can do something like this:
a%.o : a%.c
completely_special_compiler -o $# $<
b%.o : b%.c
$(CC) $(CFLAGS) -o $# $<
For more explanation, see Static Patterns.

Resources