I find I\'m writing a lot of Makefiles that could be cleaned up with the use of n-tuple lists. But I can\'t find any way to do this properly (and cleanly). So far I
You can use default rules for a set of files with the same extension as in for compiling each c
file to an o
. Of course you are not restricted to any special file extensions. For compiling a set of .c
files you could do it like this:
OBJS = foo.o bar.o baz.o
OUT = myprog
all: $(OBJS)
$(SILENT) echo "LD $@"
$(SILENT) $(CPP) -o $(OUT) $^ $(LDFLAGS)
%.o: %.c
$(SILENT) echo "CC $<"
$(SILENT) $(CC) $(CCOPTS) -o $@ -c $<
I'd check the GNU Make manual on foreach. here are some random snips that I've used in a different project... the example is incomplete, but maybe it will give you some ideas? I might clean this up later if I've got more time...
REMAKE_PROGS:= dog duck cow
XYZs = \
dog.c pull_tail bark \
duck.c chase quack \
cow.c tip moo
$(foreach src, $(XYZs), $(eval $MAKE $(src))
$(REMAKE_PROGS):
@echo "\n# === $@ linking\n"
$(call compile,$@,$(OBJS_$@),$(CXX),$(MPICXX),$(LDFLAGS) $(LIBS) $(SYSLIBS) $(OTHER_LIB) $(EXTRA_LD_FLAGS))
@echo "\n# --- $@ compiled\n"
define compile
@mkdir -p $(dir $(1))
$(if ${ANNOUNCE},@echo "\n# +++ ${MAKECMDGOALS} compiling\n" $(eval ANNOUNCE=))
$(call compiler,$(1),NOMPI,$(3),$(4))
$(eval MY_FLAGS=$(FLAGS_$(1)) $(5))
$(if $(filter %xlf %xlf90,$(COMPILER_$(1))),$(eval MY_FLAGS:=$(MY_FLAGS:-D%=-WF,-D%)))
$(strip $(COMPILER_$(1)) $(2) $(MY_FLAGS) -o $(1) )
endef
Thanks for the hints -- after some hacking I think this is more what I was hoping for:
XYZs = \
dog.c:pull_tail:bark \
duck.c:chase:quack \
cow.c:tip:moo
all:
@- $(foreach XYZ,$(XYZs), \
$(eval X = $(word 1,$(subst :, ,$(XYZ)))) \
$(eval Y = $(word 2,$(subst :, ,$(XYZ)))) \
$(eval Z = $(word 3,$(subst :, ,$(XYZ)))) \
\
$(CC) $X -o bully/$Y ; \
ln bully/$Y sounds/$Z ; \
)
Can anyone do better?
You're doing it backwards.
You're trying to treat make like it's a script. It's not, instead its a set of rules on how to create X given Y. Then the make engine figures out what needs to happen to get that result.
For the example given, you really should be using a script for the generation steps. Perhaps calling that from make, but let make handle the CC stuff.
Makefiles are essentially declarative in nature, so I don't think that make itself provides what you want. However, you seem to be wanting to associate some string values with specific targets, so maybe the Target specific variable values feature of GNU make will be of interest. This is an extract from the manual:
There is one more special feature of target-specific variables: when you define a target-specific variable, that variable value is also in effect for all dependencies of this target (unless those dependencies override it with their own target-specific variable value). So, for example, a statement like this:
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
will set
CFLAGS
to-g
in the command script forprog
, but it will also setCFLAGS
to-g
in the command scripts that create prog.o, foo.o, and bar.o, and any command scripts which create their dependencies.
If you haven't already read it, the GNU make manual is pretty damn good.
Edit: To do what you asked about in your comment:
dog: ANIMAL=dog.c BULLY=pull_tail SOUND=bark
use:
dog: ANIMAL=dog.c
dog: BULLY=pull_tail
dog: SOUND=bark
None that I know of, but that is because you're trying to force make to work ans an imperative language, when that is not what it is.
In GNU make you'd probably want to do something like:
pull_tail : SOUND=bark
pull_tail : dog.c
$(CC) $< -o $^
ln $@ $(SOUND)
chase : SOUND=quack
chase : duck.c
$(CC) $< -o $^
ln $@ $(SOUND)
...
Or better yet, redefine the default rule for .c files to handle the linking for you, but the strange structure of your names (the program names don't have a lexical relationship to the source names) makes that hard.
If what you want to be able to rebuild this quickly without an lot of hand editing, you probably want to write a script to regenerate the makefile framgment from data and use the include
feature of GNU make...