How do I write a prerequisite to loop through two lists of files together? - makefile

This is an example illustrating the output I would like:
LIST1 := hello.o world.o
LIST2 := hello.c world.c
# Make the first object
# hello.o: hello.c
$(word 1, $(LIST1)): $(word 1, $(LIST2))
cc -c $^ -o $#
# Make the second object
# world.o: world.c
$(word 2, $(LIST1)): $(word 2, $(LIST2))
cc -c $^ -o $#
Is there a way to summarise the target: prerequisite text so that it loops through two entire lists?
I have tried using foreach functions with word functions but I don't get the output. I get non-numeric arguments to word and other invalid expressions.

You actually can do it, but it's really ugly (and would be hard to maintain), I'm showing the answer, but I am definitely not suggesting using it... If you can use pattern rules as #MadScientist suggests, that would be best. If not (say the names differ, you can do something like):
LIST1 := hello.o bob.o
hello.o : hello.c
bob.o : sally.c
$(LIST1):
cc -c $< -o $#
Which allows you to specify custom prereqs for each target. If you really need two lists, the technical answer to your question would be as follows:
LIST1 := hello.o bob.o
LIST2 := hello.c sally.c
all:
define recursive_def
$(firstword $(1)) : $(firstword $(2))
cc -c $$^ -o $$#
$(if $(wordlist 2,3,$1),$(call recursive_def,$(wordlist 2,$(words $(1)),$1),$(wordlist 2,$(words $(2)),$(2))))
endef
$(info -v---v-)
$(info $(call recursive_def,$(LIST1),$(LIST2)))
$(info -^---^-)
$(eval $(call recursive_def,$(LIST1),$(LIST2)))

The short answer is "no". Why don't you just write a pattern rule?
all: $(LIST1)
%.o : %.c
cc -c $^ -o $#
? In fact you don't even need to write a pattern rule at all: make already has a default rule that can build object files from C source files.
ETA
If you have source files in different directories, you have two choices. The best choice is to have one pattern rule and create all your objects in a subdirectory structure that mimics your source directories. So, like this:
SRCS := foo.c bar/bar.c bar/biz/baz.c
OBJS := $(patsubst %.c,obj/%.o,$(SRCS))
all: $(OBJS)
obj/%.o : %.c
#mkdir -p $(#D)
$(COMPILE.c) -o $# $<
If you really don't want to do that and you want to put all the object files into the same directory (a bad idea because if you have two source files with the same name in different source directories, your build will fail) you can use VPATH:
SRCS := foo.c bar/bar.c bar/biz/baz.c
OBJS := $(patsubst %.c,obj/%.o,$(notdir $(SRCS)))
VPATH := $(sort $(dir $(SRCS)))
all: $(OBJS)
obj/%.o : %.c
#mkdir -p $(#D)
$(COMPILE.c) -o $# $<

Related

Makefile - prereq-patterns % in $(wildcard)

I want to add rules of .cc files to a static mode rule. And I try to use $(wildcard ) to enable the prereq-pattern which exists in folder in order to avoid a No rule to make target ... error. But % in $(wildcard ) isn't transformed to the file basename. $(wildcard %.cpp) and $(wildcard %.cc) turn to be nothing.
I want to know how to solve this and make .cc, .cpp in one static mode rule.
# before
# CXX_SOURCE_FILE = $(wildcard *.cpp)
CXX_SOURCE_FILE = $(wildcard *.cpp) $(wildcard *.cc)
C++ = g++
CXX_FLAGS = -g -Wall
c++ : $(basename $(CXX_SOURCE_FILE))
# before
# $(basename $(CXX_SOURCE_FILE)) : % : %.cpp
# $(C++) $< -o $# $(CXX_FLAGS)
$(basename $(CXX_SOURCE_FILE)) : % : $(wildcard %.cpp) $(wildcard %.cc)
$(C++) $< -o $# $(CXX_FLAGS)
You cannot use wildcard like this because it will be evaluated during the parsing, before static pattern rules are considered. Unless you have source files that are literally %.cpp or %.cc your prerequisite list will be empty when the time comes to consider the rules.
A simple solution consists in separating your two sets of source files (CPP_SOURCE_FILE and CC_SOURCE_FILE) and use two different static pattern rules:
CPP_SOURCE_FILE = $(wildcard *.cpp)
CC_SOURCE_FILE = $(wildcard *.cc)
$(basename $(CPP_SOURCE_FILE)): %: %.cpp
$(C++) $< -o $# $(CXX_FLAGS)
$(basename $(CC_SOURCE_FILE)): %: %.cc
$(C++) $< -o $# $(CXX_FLAGS)
There are other solutions but they are more complicated. If you use GNU make you can either use the foreach-eval-call construct or the secondary expansion.
Example with foreach-eval-call:
# $1: executable
define MY_rule
$1: $$(wildcard $1.cpp) $$(wildcard $1.cc)
$$(C++) $$< -o $$# $$(CXX_FLAGS)
endef
$(foreach e,$(basename $(CXX_SOURCE_FILE)),$(eval $(call MY_rule,$e)))
For executable foo this will call MY_rule with parameter foo, pass the result to eval that will expand it and instantiate it as a new rule. $(call MY_rule,foo) expands as:
foo: $$(wildcard foo.cpp) $$(wildcard foo.cc)
$$(C++) $$< -o $$# $$(CXX_FLAGS)
eval expands it as:
foo: $(wildcard foo.cpp) $(wildcard foo.cc)
$(C++) $< -o $# $(CXX_FLAGS)
This is exactly what you wanted for this executable. Note the need of $$ to escape the first expansion by eval.
Example with secondary expansion:
.SECONDEXPANSION:
$(basename $(CXX_SOURCE_FILE)): $$(wildcard $$#.cpp) $$(wildcard $$#.cc)
$(C++) $< -o $# $(CXX_FLAGS)
After the first expansion, during the parsing of the Makefile, this becomes:
foo bar baz: $(wildcard $#.cpp) $(wildcard $#.cc)
$(C++) $< -o $# $(CXX_FLAGS)
Note that if you were trying to use this rule in your Makefile, instead of your static pattern rule, this would not work for the very same reason: when the Makefile is parsed and the prerequisites are expanded the automatic variables are not yet set. So, unless you have files named .cpp or .cc your liste of prerequisites would also be empty.
But every rule after .SECONDEXPANSION: has its prerequisites expanded a second time. And the second time, different from the first, the automatic variables are set, including $#. The final result is equivalent to the 3 following rules:
foo: $(wildcard foo.cpp) $(wildcard foo.cc)
$(C++) $< -o $# $(CXX_FLAGS)
bar: $(wildcard bar.cpp) $(wildcard bar.cc)
$(C++) $< -o $# $(CXX_FLAGS)
baz: $(wildcard baz.cpp) $(wildcard baz.cc)
$(C++) $< -o $# $(CXX_FLAGS)
From the GNU make manual:
the true power of this feature only becomes apparent when you discover that secondary expansions always take place within the scope of the automatic variables for that target. This means that you can use variables such as $#, $*, etc. during the second expansion and they will have their expected values, just as in the recipe. All you have to do is defer the expansion by escaping the $.
Note: here again the $$ are essential to escape the first expansion.

Makefile with two targets and separate build folders

I am trying to use one Makefile with two similar targets and two separate build folders. The only difference between the targets is the addition of a CFLAG define.
Here is a snippet of what I have, however I can't get the build folder to evaluate to something different depending on the target. I used foo and bar to represent the different targets.
...
foo_BUILD_DIR := build_foo/
bar_BUILD_DIR := build_bar/
C_SRCS := main.c
CFLAGS := -std=c99 -Os -Wall
foo_CFLAGS := $(CFLAGS) -DBLE=1
C_OBJS := $(addprefix $(BUILD_DIR),$(subst .c,.o,$(C_SRCS)))
$(BUILD_DIR)%.o: %.c
#mkdir -p $(#D)
$(CC) $(CFLAGS) $^ -o $#
foo:$(OBJS)
$(CC) $(OBJS) $(LDFLAGS) -o $#
bar:$(OBJS)
$(CC) $(OBJS) $(LDFLAGS) -o $#
You have three problems to solve here.
The first is building object files in the build directories. First we write a pattern rule to build foo objects:
foo_BUILD_DIR := build_foo # Don't put the trailing slash in the dir name, it's a pain.
CFLAGS := -std=c99 -Os -Wall
foo_CFLAGS := $(CFLAGS) -DBLE=1
$(foo_BUILD_DIR)/%.o: %.c
#mkdir -p $(#D)
$(CC) $(foo_CFLAGS) $^ -o $#
Once that's working perfectly, we write another rule for the bar objects:
$(bar_BUILD_DIR)/%.o: %.c
#mkdir -p $(#D)
$(CC) $(CFLAGS) $^ -o $#
The second problem is tidying up what we've written so far. That foo_CFLAGS variable is now the only thing making these two recipes different, so let's get rid of it with a target-specific variable assignment:
$(foo_BUILD_DIR)/%.o: CFLAGS += -DBLE=1
$(foo_BUILD_DIR)/%.o: %.c
#mkdir -p $(#D)
$(CC) $(CFLAGS) $^ -o $#
Now we can combine the rules into one pattern rule with two targets:
$(foo_BUILD_DIR)/%.o: CFLAGS += -DBLE=1
$(foo_BUILD_DIR)/%.o $(bar_BUILD_DIR)/%.o: %.c
#mkdir -p $(#D)
$(CC) $(CFLAGS) $^ -o $#
The third problem is getting the foo and bar rules to require the right objects. Obviously this rule:
foo:$(OBJS)
...
won't work, we need something specific to foo:
foo: $(addprefix $(foo_BUILD_DIR)/, $(OBJS))
...
This works, but it requires us to write a foo rule that specifies $(foo_BUILD_DIR), a bar rule that specifies $(bar_BUILD_DIR), and so on. Is there a lazier way? All we need is to take the target (e.g. foo) and get it into the prerequisite list. As you know, the automatic variable $# contains the target, but it isn't available in the prerequisite list, because the prerequisite list is expanded before a value is assigned to that variable-- unless we use Secondary Expansion. This is an advanced technique, but it lets us do a second expansion in the later phase (escaping our variables with an extra $ to protect them from the first expansion):
.SECONDEXPANSION:
foo: $(addprefix $$($$#_BUILD_DIR)/, $(OBJS))
...
And once that's working, we can add another target, or as many as we want:
foo bar: $(addprefix $$($$#_BUILD_DIR)/, $(OBJS))
...
There are one or two more refinements possible, but this is enough to start with.

makefile how to output to separate build directory

I have this directory structure:
makefile
src
foo.c
build
My goal is simply to build foo.c and output the build files to the build directory.
I have the following makefile:
SRCS_DIR := ./src
BUILD_DIR := ./build
SRCS := $(shell find $(SRCS_DIR) -name "*.c")
OBJS := $(subst $(SRCS_DIR),$(BUILD_DIR),$(SRCS))
OBJS := $(OBJS:.c=.o)
test.exe: $(OBJS)
gcc $(OBJS) -o $#
%.o: %.c
gcc -c $< -o $#
The problem is the pattern rule. One of the object files is build/foo.o. The problem is that %.c gets turned into build/foo.c, which doesn't exist. What I want %.c to be is src/foo.c instead, but I have no idea how to do that.
The stem of the pattern must match exactly. So if you want a pattern that will put things into a different directory, you have to modify the pattern so that the non-matching parts are not part of the stem. So you can write:
build/%.o : src/%.o
gcc -c $< -o $#
so that the % matches only the common string.

Is it possible to create all missing directory at once with make?

The common pattern is:
obj/%.o : src/%.c
mkdir -p $(dir $#)
$(CC) -c $< -o $#
I am wondering if I can space the call to mkdir for each file. I came across this solution:
CSRC=some/path/here/foo.c another/directory/random/here/bar.c
OBJDIR=obj/
objdirs = $(sort $(foreach path,$(CSRC),$(addprefix $(OBJDIR),$(dir $(path)))))
$(objdirs): %:
mkdir -p $#
obj/%.o : src/%.c | $(objdirs)
$(CC) -c $< -o $#
Is there a better solution?
A simple solution, but not 100% satisfactory, resembles yours:
OBJDIR := obj
CSRC := some/path/here/foo.c another/directory/random/here/bar.c
CSRCDIRS := $(sort $(dir $(CSRC)))
objdirs := $(addprefix $(OBJDIR)/,$(CSRCDIRS))
OBJS := $(patsubst %.c,$(OBJDIR)/%.o,$(CSRC))
$(OBJS): $(OBJDIR)/%.o: %.c | $(objdirs)
$(CC) -c $< -o $#
$(objdirs):
mkdir -p $#
It should work but its drawback is that each object file has all object directories as order-only prerequisite, which is overkill. All object directories will be created, even if only one object file is built. They will be created only once because they are order-only, but still, it is not 100% satisfactory.
In order to have only one order-only object directory prerequisite per object file, the only solution I see (but there may be better ones) is something like:
OBJDIR := obj
CSRC := some/path/here/foo.c another/directory/random/here/bar.c
CSRCDIRS := $(sort $(dir $(CSRC)))
objdirs := $(addprefix $(OBJDIR)/,$(CSRCDIRS))
# $(1): source directory
define MY_rule
$(OBJDIR)/$(1)%.o: $(1)%.c | $(OBJDIR)/$(1)
$(CC) -c $$< -o $$#
endef
$(foreach c,$(CSRCDIRS),$(eval $(call MY_rule,$(c))))
$(objdirs):
mkdir -p $#
The iterator explicitly declares each object directory as the only order-only prerequisite of all object files it contains. But it is more difficult to understand (especially the $$). Is it worth the extra complexity?

Makefile mirror build directory

I need to create Makefile that compiles .c files with a lot of subdirs (sources directory goes in around 5 level depth) and I need to place the object files in the mirrored build directory. So far, I have created this Makefile:
CC := gcc.exe
AS := as.exe
CFLAGS = -DCORE_SW_VERSION='"$(CORE_SW_VERSION)"' -Wall -mA6 -c -fmessage-length=0 -Hsdata0
CFLAGS += -fgnu89-inline -mno-volatile-cache $(INCLUDE) -Hon=each_function_in_own_section -Xcrc -std=c99 -O1
CORE_SW_VERSION:=CORE.07.01.04.01.03.01.R
HAL_SW_VERSION:=16.01.06.01.06.00
MODE_CORE := dev
MODE_HAL := dev
OBJDIR := $(shell pwd)/$(TARGET12) #TARGET12 is a make parameter
INCLUDE := $(shell cat ./$(TARGET12)_include.txt)
SOURCEDIR := ../sources
CSRC := $(shell find $(SOURCEDIR) -name '*.c')
EXCLUDES := $(shell cat ./$(TARGET12)_exclude.txt)
OBJ := $(CSRC:.c=.o)
OBJS := $(patsubst ../%.c,$(OBJDIR)/%.o,$(CSRC))
.PHONY: $(TARGET12)
$(TARGET12): $(OBJS)
$(AR) -r $(CORE_SW_VERSION).a $(OBJS)
$(OBJS): $(CSRC)
mkdir -p $(dir $#)
$(CC) $(CFLAGS) $< -o $(patsubst ../%,$(OBJDIR)/%,$#)
If I define rule for $(OBJS) this way, $< is always the first .c file in $(CSRC).
If I define $(OBJS) this way:
$(OBJS): %.o: %.c
mkdir -p $(dir $#)
$(CP) $< $#
I get error that there is no rule to make target for .c file. But I see that make is looking for .c file in build mirrored directory, and it should look at the source dir. Do you maybe know how this could be arranged?
Thanks you in advance!
The rule $(OBJS): %.o: %.c means something like this: when trying to create a .o file, use this rule if the corresponding .c file exists. For example: when make is looking for a way to create $(OBJDIR)/foo.o, it will look for $(OBJDIR)/foo.c.
In your case this file does not exists, so the rule is ignored.
What you want is rather something like this:
$(OBJS): $(OBJDIR)/%.o: $(SOURCEDIR)/%.c
mkdir -p $(dir $#)
$(CP) $< $#
The first rule for $(OBJS) you tried, states that every object file individually depends on all source files. Surely that's not correct.
Your second attempt is better, although the recipe is weird. Fix that and use VPATH to make make find the sources.

Resources