With this simplified example Makefile
PHONY: all
all: prog_main
prog_main.c:
echo 'int module(); int main(){return module();}' > $#
module.c:
echo 'int module(){return 0;}' > $#
main_objects := module.o
prog_%: prog_%.o $(%_objects)
$(CC) -o $# $< $($*_objects)
a make will fail, because
the syntax $(%_objects) is not supported by make and ignored,
module.o is not a dependency of prog_main,
make sees no need to compile module.o, and
the link fails, because there is no module.o.
Is there a way to tell make, that each prog_% depends on prog_%.o and the targets listed int the variable %_objects?
You can't do what your describing as make expands % and variables at different times during processing. But you could replace the line: main_objects := module.o with prog_main: modules.o. If you did that, then module.o would appear in $^ in the prog_%: recipes. So you would have something like:
.PHONY: all
all: prog_main
prog_main.c:
echo 'int module(); int main(){return module();}' > $#
module.c:
echo 'int module(){return 0;}' > $#
# main_objects := module.o
prog_main: module.o
prog_%: prog_%.o
$(CC) -o $# $^
Related
Background, I suspect XY problem
I have simpler C modules in a directory. I want to write unit tests for these in a sub-directory test/. These unit tests are no more than C programs linking to the module under test, one directory above. I want a Makefile that defines several build targets and lets me build and run the test executables in one step, or separately.
My attempted solution
I've attempted the following:
CC = gcc
CFLAGS = -ggdb -Wall -Wextra -Werror -O3 -std=c99
PARAM_LIST_TARGET = parameter_list_test
PARAM_LIST_SOURCE_FILES = \
../parameter_list.c \
parameter_list_test.c
PARAM_LIST_OBJECT_FILES := $(addsuffix .o,$(basename $(PARAM_LIST_SOURCE_FILES)))
TARGETS = $(PARAM_LIST_TARGET)
all: $(TARGETS)
$(%_TARGET): $(%_OBJECT_FILES)
$(CC) $(CFLAGS) $^ -o $#
.c.o:
$(CC) -c $< -o $# $(CFLAGS)
clean:
$(RM) *.o $(TARGETS)
test: all
#for t in $(TARGETS) ; do ./$$t ; done
This doesn't work, and it's because of the $(%_TARGET): row. Not surprising, I didn't expect it to work, but I hope this illustrates what I'm trying to achieve.
I want to create more chunks of the form _TARGET, _SOURCE_FILES, and _OBJECT_FILES, to test other modules besides PARAM_LIST, for example:
PARAM_LIST_TARGET = parameter_list_test
PARAM_LIST_SOURCE_FILES = \
../parameter_list.c \
parameter_list_test.c
PARAM_LIST_OBJECT_FILES := $(addsuffix .o,$(basename $(PARAM_LIST_SOURCE_FILES)))
OTHER_MODULE_TARGET = other_module_test
OTHER_MODULE_SOURCE_FILES = \
../other_module.c \
other_module_test.c
OTHER_MODULE_OBJECT_FILES := $(addsuffix .o,$(basename $(OTHER_MODULE_SOURCE_FILES)))
I understand that % works on filenames, so attempting to use it on variables fails:
$(%_TARGET): $(%_OBJECT_FILES)
$(CC) $(CFLAGS) $^ -o $#
How can I write a rule that matches the Makefile variables _TARGET to their associated _OBJECT_FILES, without creating one per test target?
Or more importantly, how should I do it totally differently?
Edit: I've seen this, however it seems it's only working with a single source file per executable.
You can always access make variables by constructing their names:
MY_VAR := "my var"
HIS_VAR := "his var"
HER_VAR := "her var"
CATS_VAR := "cats var"
DOGS_VAR := "dogs var"
ALL_PERSONS := MY HIS HER CATS DOGS
ALL_VARS := $(foreach p,$(ALL_PERSONS),$($(p)_VAR))
$(info $(ALL_VARS))
Output:
$ make
"my var" "his var" "her var" "cats var" "dogs var"
Defining the dependencies separately seems to work, thanks to this answer:
TARGETS = $(PARAM_LIST_TARGET) $(OTHER_MODULE_TARGET)
all: $(TARGETS)
$(PARAM_LIST_TARGET): $(PARAM_LIST_OBJECT_FILES)
$(OTHER_MODULE_TARGET): $(OTHER_MODULE_OBJECT_FILES)
$(TARGETS):
$(CC) $(CFLAGS) $^ -o $#
This eliminates the need for a duplicate rule (one per target). Still, the definition of dependencies for each target looks like duplicates, a pattern match for these would be nice.
More than that, the OBJECT_FILES variable becomes unnecessary. This works:
PARAM_LIST_TARGET = parameter_list_test
PARAM_LIST_SOURCE_FILES = \
../parameter_list.c \
parameter_list_test.c
$(PARAM_LIST_TARGET): $(addsuffix .o,$(basename $(PARAM_LIST_SOURCE_FILES))) # The dependencies directly
It would still feel nice to have this last row as one rule for all targets. Something like "for all variables ending with TARGET, build a dependency to the content of the variable with the same name, but ending with SOURCE_FILES instead".
I have the following makefile in the root of the project:
Makefile
# Board version
# Available: 3
PI ?= 3
# Kernel binaries
ifeq ($(PI), 3)
KERNEL_IMG := kernel8.img
else ifeq ($(PI), 2)
KERNEL_IMG := kernel7.img
else ifeq ($(PI), 1)
KERNEL_IMG := kernel.img
else
$(error Unsupported Raspberry Pi version)
endif
KERNEL_ELF := $(patsubst %.img,%.elf,$(KERNEL_IMG))
# Directories/paths
BUILD_DIR := build
# Toolchain
TOOLCHAIN ?= aarch64-elf
OBJCOPY := $(TOOLCHAIN)-objcopy
LD := $(TOOLCHAIN)-ld
CC := $(TOOLCHAIN)-gcc
# Misc
LINKER_SCRIPT := linker.ld
# Flags
LDFLAGS := -T $(LINKER_SCRIPT)
ASFLAGS :=
CFLAGS :=
# Source files
C_SRC := $(wildcard *.c)
ASM_SRC := $(wildcard *.S)
# Include
include pi/$(PI)/mod.mk
# Object files
OBJECTS := $(patsubst %,$(BUILD_DIR)/%.o,$(C_SRC))
OBJECTS += $(patsubst %,$(BUILD_DIR)/%.o,$(ASM_SRC))
# Targets
.PHONY: all builddirs clean
all: $(BUILD_DIR)/$(KERNEL_IMG)
$(BUILD_DIR)/$(KERNEL_IMG): $(BUILD_DIR)/$(KERNEL_ELF)
$(OBJCOPY) $< -O binary $#
$(BUILD_DIR)/$(KERNEL_ELF): $(LINKER_SCRIPT) $(OBJECTS)
$(LD) $(OBJECTS) $(LDFLAGS) -o $#
$(OBJECTS): | builddirs
builddirs: $(BUILD_DIR)/pi/$(PI)
$(BUILD_DIR)/pi/$(PI):
mkdir -p $#
$(BUILD_DIR)/%.S.o: %.S
$(CC) -c $< $(ASFLAGS) -o $#
$(BUILD_DIR)/%.c.o: %.c
$(CC) -c $< $(CFLAGS) -o $#
clean:
$(RM) -r $(BUILD_DIR)
It includes pi/3/mod.mk
C_SRC +=
ASM_SRC += pi/3/start.S
$(BUILD_DIR)/pi/3/start.S.o: pi/3/start.S pi/3/include/cpu/sysregs.h
$(CC) -c $< $(ASFLAGS) -o $#
Now here's the problem: whenever I run 'make' in the root of a project, '$(BUILD_DIR)/pi/3/start.S.o' rule invokes, instead of 'all'. If I move 'include pi/$(PI)/mod.mk' to the very bottom of the root makefile, and replace 'C_SRC' and 'ASM_SRC' variables in 'pi/3/mod.mk' with 'OBJECTS += $(BUILD_DIR)/pi/3/start.S.o' and invoke 'make', this rule isn't even invoked, so I get an error that make doesn't know how to build start.S.o.
What am I doing wrong and what is the best way to handle this?
Make's default goal is the first target in your Makefile. In your case the first target is the one defined in the included Makefile: $(BUILD_DIR)/pi/3/start.S.o. Either invoke make all or move the all rule in your Makefile such that it becomes the first one, or tell make that the default goal is all:
.DEFAULT_GOAL := all
(see GNU make manual).
In trying to implement nonrecursive make, I have a Rules.mk which looks like:
############
# Enter Stack
############
sp := $(sp).x
dirstack_$(sp) := $(d)
d := $(dir)
.. setup things like OBJECTS_$(d), DEPS_$(d), TARGET_$(d), etc ...
############
# Exit Stack
############
-include $(DEPS_$(d))
d := $(dirstack_$(sp))
sp := $(basename $(sp))
One of the variables I wanted to set was:
INCLUDE_PATH_$(d) := -Isomething -Isomething/else ...
To be used in the compilation rule:
$(OBJDIR_$(d))/%.o : $(d)/%.cpp $(OBJDIR_$(d))/%.d
$(CC) $(CFLAGS) $(INCLUDE_PATH_$(d)) -o $# -c $<
But this doesn't work - $(INCLUDE_PATH_$(d)) doesn't get expanded until later - when $(d) is no longer has the value I need it to have in order for this to work. What's the way for me to do this properly?
You could use a target-specific variable
$(OBJDIR_$d)/%.o : INCLUDES := $(INCLUDE_PATH_$d)
$(OBJDIR_$d)/%.o : $d/%.cpp $(OBJDIR_$d)/%.d
$(CC) $(CFLAGS) $(INCLUDES) -o $# -c $<
The following is perhaps more standard / flexible (assuming CPPFLAGS isn't set to recursively expand) although it depends on your needs
$(OBJDIR_$d)/%.o : CPPFLAGS += $(INCLUDE_PATH_$d)
$(OBJDIR_$d)/%.o : $d/%.cpp $(OBJDIR_$d)/%.d
$(CC) $(CPPFLAGS) $(CFLAGS) -o $# -c $<
I would like to define a generic makefile that can be included by different makefiles like this (where a source t.c is compiled into t.o and archived in libl.a):
Makefile:
LIBS := libl
OBJS_libl := t
include c.mk
c.mk:
ALIBS := $(LIBS:%=%.a)
.SECONDEXPANSION:
all : $(ALIBS)
%.a : $$(patsubst %,%.o,$$(OBJS_$$(patsubst %.a,%,$$(#F))))
ar crs $# $(patsubst %,%.o,$(OBJS_$(#F:%.a=%)))
%.o : %.c
gcc -c $(#F:%.o=%.c) -o $#
The goal is that the prerequisites of the rule %.a expand as t.o for libl.a
This seems to work for explicit rules (if I replace %.a by libl.a) but not for implicit rules.
=> make: *** No rule to make target `libl.a', needed by `all'. Stop.
Can I achieve this and how please?
[Edit] The goal indeed is to have in the same directory several libraries like libl.a, each made with some of the objects:
LIBS := libl1 libl2
OBJS_libl1 := t1 u1
OBJS_libl2 := t2 u2
Makefile:
LIBS := libl.a
$(LIBS): t.o
include c.mk
c.mk:
%.a:
ar crs $# $^
%.o : %.c
gcc -c $< -o $#
EDIT:
LIBS := libl1.a libl2.a
$(LIBS):
libl1.a : t1.o u1.o
libl2.a : t2.o u2.o
Anything else?
How do I make this work? It errors out with "make: somevariable: Command not found"
sometarget:
somevariable = somevalue
Full example:
CXXFLAGS = -I/usr/include/test -shared -fPIC
OBJ = main.o Server.o
blabla : $(OBJ)
ifeq ($(argsexec),true)
# Creates an executable
CXXFLAGS = -I/usr/include/test
$(CXX) -o blabla $(OBJ) $(CXXFLAGS)
else
# Creates a library
DESTDIR = /home/pc
$(CXX) -o blabla $(OBJ) $(CXXFLAGS)
./bn.sh
endif
I found a solution using the eval function:
$(eval variablename=whatever)
This works :)
(although I may now try to find an easier build system ;))
Thanks everyone for reading and also of course #eriktous for writing!
If you write it like you did, the assignment will be executed as a shell command, which gives the error you got.
I would try organising it something like this:
CXXFLAGS = -I/usr/include/test
ifneq ($(argsexec),true)
CXXFLAGS += -shared -fPIC
DESTDIR = /home/pc
endif
OBJ = main.o Server.o
blabla : $(OBJ)
$(CXX) -o blabla $(OBJ) $(CXXFLAGS)
ifneq ($(argsexec),true)
./bn.sh
endif
This should do what you want, although I'm not quite happy with using the ifneq construct twice. I'd have to think harder to come up with something that avoids that.