我的源代码位于一堆子目录中,例如:
src/widgets/apple.cpp
src/widgets/knob.cpp
src/tests/blend.cpp
src/ui/flash.cpp
在项目的根目录中,我想使用如下规则生成单个 Makefile:
%.o: %.cpp
$(CC) -c $<
build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o
$(LD) build/widgets/apple.o .... build/ui/flash.o -o build/test.exe
当我尝试此操作时,它没有找到
build/widgets/apple.o
的规则。我可以更改一些内容,以便在需要制作 %.o: %.cpp
时使用 build/widgets/apple.o
吗?
原因是你的规则
%.o: %.cpp
...
期望 .cpp 文件与您的建筑物的 .o 位于同一目录中。由于您的情况下的 test.exe 取决于 build/widgets/apple.o (等),因此 make 期望 apple.cpp 为 build/widgets/apple.cpp。
您可以使用 VPATH 来解决此问题:
VPATH = src/widgets
BUILDDIR = build/widgets
$(BUILDDIR)/%.o: %.cpp
...
当尝试构建“build/widgets/apple.o”时,make 将在 VPATH 中搜索 apple.cpp。请注意,构建规则必须使用特殊变量才能访问实际的文件名 make 发现:
$(BUILDDIR)/%.o: %.cpp
$(CC) $< -o $@
哪里“$
<" expands to the path where make located the first dependency.
另请注意,这将构建 build/widgets 中的所有 .o 文件。如果你想在不同的目录中构建二进制文件,你可以这样做
build/widgets/%.o: %.cpp
....
build/ui/%.o: %.cpp
....
build/tests/%.o: %.cpp
....
我建议您使用“
固定命令序列”以避免重复实际的编译器构建规则:
define cc-command
$(CC) $(CFLAGS) $< -o $@
endef
然后你可以有多个这样的规则:
build1/foo.o build1/bar.o: %.o: %.cpp
$(cc-command)
build2/frotz.o build2/fie.o: %.o: %.cpp
$(cc-command)
CC := g++
LD := g++
MODULES := widgets test ui
SRC_DIR := $(addprefix src/,$(MODULES))
BUILD_DIR := $(addprefix build/,$(MODULES))
SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.cpp))
OBJ := $(patsubst src/%.cpp,build/%.o,$(SRC))
INCLUDES := $(addprefix -I,$(SRC_DIR))
vpath %.cpp $(SRC_DIR)
define make-goal
$1/%.o: %.cpp
$(CC) $(INCLUDES) -c $$< -o $$@
endef
.PHONY: all checkdirs clean
all: checkdirs build/test.exe
build/test.exe: $(OBJ)
$(LD) $^ -o $@
checkdirs: $(BUILD_DIR)
$(BUILD_DIR):
@mkdir -p $@
clean:
@rm -rf $(BUILD_DIR)
$(foreach bdir,$(BUILD_DIR),$(eval $(call make-goal,$(bdir))))
此 Makefile 假定您的包含文件位于源目录中。它还检查构建目录是否存在,如果不存在则创建它们。
最后一行是最重要的。它使用函数
make-goal
为每个构建创建隐式规则,并且不需要一一编写您还可以使用
$@
将包括源文件的整个(相对)路径,该路径又用于构造对象名称(及其相对路径)我们使用:
#####################
# rules to build the object files
$(OBJDIR_1)/%.o: %.c
@$(ECHO) "$< -> $@"
@test -d $(OBJDIR_1) || mkdir -pm 775 $(OBJDIR_1)
@test -d $(@D) || mkdir -pm 775 $(@D)
@-$(RM) $@
$(CC) $(CFLAGS) $(CFLAGS_1) $(ALL_FLAGS) $(ALL_DEFINES) $(ALL_INCLUDEDIRS:%=-I%) -c $< -o $@
这将创建一个对象目录,其名称在
$(OBJDIR_1)
中指定 以及根据源中子目录的子目录。例如(假设 objs 为顶级对象目录),在 Makefile 中:
widget/apple.cpp
tests/blend.cpp
产生以下对象目录:
objs/widget/apple.o
objs/tests/blend.o
在主“Makefile”中为每个源目录定义 SRCDIR,并为 SRCDIR 的每个值包含“makef.mk”。在每个源目录中放置文件“files.mk”,其中包含源文件列表以及其中一些文件的编译选项。在主“Makefile”中,可以定义编译选项并排除 SRCDIR 的每个值的文件。
生成文件:
PRG := prog-name
OPTIMIZE := -O2 -fomit-frame-pointer
CFLAGS += -finline-functions-called-once
LDFLAGS += -Wl,--gc-section,--reduce-memory-overheads,--relax
.DEFAULT_GOAL := hex
OBJDIR := obj
MK_DIRS := $(OBJDIR)
SRCDIR := .
include makef.mk
SRCDIR := crc
CFLAGS_crc := -DCRC8_BY_TABLE -DMODBUS_CRC_BY_TABLE
ASFLAGS_crc := -DCRC8_BY_TABLE -DMODBUS_CRC_BY_TABLE
include makef.mk
################################################################
CC := avr-gcc -mmcu=$(MCU_TARGET) -I.
OBJCOPY := avr-objcopy
OBJDUMP := avr-objdump
C_FLAGS := $(CFLAGS) $(REGS) $(OPTIMIZE)
CPP_FLAGS := $(CPPFLAGS) $(REGS) $(OPTIMIZE)
AS_FLAGS := $(ASFLAGS)
LD_FLAGS := $(LDFLAGS) -Wl,-Map,$(OBJDIR)/$(PRG).map
C_OBJS := $(C_SRC:%.c=$(OBJDIR)/%.o)
CPP_OBJS := $(CPP_SRC:%.cpp=$(OBJDIR)/%.o)
AS_OBJS := $(AS_SRC:%.S=$(OBJDIR)/%.o)
C_DEPS := $(C_OBJS:%=%.d)
CPP_DEPS := $(CPP_OBJS:%=%.d)
AS_DEPS := $(AS_OBJS:%=%.d)
OBJS := $(C_OBJS) $(CPP_OBJS) $(AS_OBJS)
DEPS := $(C_DEPS) $(CPP_DEPS) $(AS_DEPS)
hex: $(PRG).hex
lst: $(PRG).lst
$(OBJDIR)/$(PRG).elf : $(OBJS)
$(CC) $(C_FLAGS) $(LD_FLAGS) $^ -o $@
%.lst: $(OBJDIR)/%.elf
-@rm $@ 2> /dev/nul
$(OBJDUMP) -h -s -S $< > $@
%.hex: $(OBJDIR)/%.elf
-@rm $@ 2> /dev/nul
$(OBJCOPY) -j .text -j .data -O ihex $< $@
$(C_OBJS) : $(OBJDIR)/%.o : %.c Makefile
$(CC) -MMD -MF [email protected] -c $(C_FLAGS) $(C_FLAGS_$(call clear_name,$<)) $< -o $@
@sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected]
@sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected]
@sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected]
-@rm -f [email protected]
$(CPP_OBJS) : $(OBJDIR)/%.o : %.cpp Makefile
$(CC) -MMD -MF [email protected] -c $(CPP_FLAGS) $(CPP_FLAGS_$(call clear_name,$<)) $< -o $@
@sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected]
@sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected]
@sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected]
-@rm -f [email protected]
$(AS_OBJS) : $(OBJDIR)/%.o : %.S Makefile
$(CC) -MMD -MF [email protected] -c $(AS_FLAGS) $(AS_FLAGS_$(call clear_name,$<)) $< -o $@
@sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected]
@sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected]
@sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected]
-@rm -f [email protected]
clean:
-@rm -rf $(OBJDIR)/$(PRG).elf
-@rm -rf $(PRG).lst $(OBJDIR)/$(PRG).map
-@rm -rf $(PRG).hex $(PRG).bin $(PRG).srec
-@rm -rf $(PRG)_eeprom.hex $(PRG)_eeprom.bin $(PRG)_eeprom.srec
-@rm -rf $(MK_DIRS:%=%/*.o) $(MK_DIRS:%=%/*.o.d)
-@rm -f tags cscope.out
# -rm -rf $(OBJDIR)/*
# -rm -rf $(OBJDIR)
# -rm $(PRG)
tag: tags
tags: $(SRC_FILES)
if [ -e tags ] ; then ctags -u $? ; else ctags $^ ; fi
cscope -U -b $^
# include dep. files
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEPS)
endif
# Create directory
$(shell mkdir $(MK_DIRS) 2>/dev/null)
makef.mk
SAVE_C_SRC := $(C_SRC)
SAVE_CPP_SRC := $(CPP_SRC)
SAVE_AS_SRC := $(AS_SRC)
C_SRC :=
CPP_SRC :=
AS_SRC :=
include $(SRCDIR)/files.mk
MK_DIRS += $(OBJDIR)/$(SRCDIR)
clear_name = $(subst /,_,$(1))
define rename_var
$(2)_$(call clear_name,$(SRCDIR))_$(call clear_name,$(1)) := \
$($(subst _,,$(2))_$(call clear_name,$(SRCDIR))) $($(call clear_name,$(1)))
$(call clear_name,$(1)) :=
endef
define proc_lang
ORIGIN_SRC_FILES := $($(1)_SRC)
ifneq ($(strip $($(1)_ONLY_FILES)),)
$(1)_SRC := $(filter $($(1)_ONLY_FILES),$($(1)_SRC))
else
ifneq ($(strip $(ONLY_FILES)),)
$(1)_SRC := $(filter $(ONLY_FILES),$($(1)_SRC))
else
$(1)_SRC := $(filter-out $(EXCLUDE_FILES),$($(1)_SRC))
endif
endif
$(1)_ONLY_FILES :=
$(foreach name,$($(1)_SRC),$(eval $(call rename_var,$(name),$(1)_FLAGS)))
$(foreach name,$(ORIGIN_SRC_FILES),$(eval $(call clear_name,$(name)) :=))
endef
$(foreach lang,C CPP AS, $(eval $(call proc_lang,$(lang))))
EXCLUDE_FILES :=
ONLY_FILES :=
SAVE_C_SRC += $(C_SRC:%=$(SRCDIR)/%)
SAVE_CPP_SRC += $(CPP_SRC:%=$(SRCDIR)/%)
SAVE_AS_SRC += $(AS_SRC:%=$(SRCDIR)/%)
C_SRC := $(SAVE_C_SRC)
CPP_SRC := $(SAVE_CPP_SRC)
AS_SRC := $(SAVE_AS_SRC)
./files.mk
C_SRC := main.c
CPP_SRC :=
AS_SRC := timer.S
main.c += -DDEBUG
./crc/files.mk
C_SRC := byte-modbus-crc.c byte-crc8.c
AS_SRC := modbus-crc.S crc8.S modbus-crc-table.S crc8-table.S
byte-modbus-crc.c += --std=gnu99
byte-crc8.c += --std=gnu99
我有一个包含多个C 文件的项目,存储在许多子目录中。 例如:
src/lib.c
src/aa/a1.c
src/aa/a2.c
src/bb/b1.c
src/cc/c1.c
这是我的 Makefile(在
src/
目录中):
# make -> compile the shared library "libfoo.so"
# make clean -> remove the library file and all object files (.o)
# make all -> clean and compile
SONAME = libfoo.so
SRC = lib.c \
aa/a1.c \
aa/a2.c \
bb/b1.c \
cc/c1.c
# compilation options
CFLAGS = -O2 -g -W -Wall -Wno-unused-parameter -Wbad-function-cast -fPIC
# linking options
LDFLAGS = -shared -Wl,-soname,$(SONAME)
# how to compile individual object files
OBJS = $(SRC:.c=.o)
.c.o:
$(CC) $(CFLAGS) -c $< -o $@
.PHONY: all clean
# library compilation
$(SONAME): $(OBJS) $(SRC)
$(CC) $(OBJS) $(LDFLAGS) -o $(SONAME)
# cleaning rule
clean:
rm -f $(OBJS) $(SONAME) *~
# additional rule
all: clean lib
这个示例对于共享库来说效果很好,并且应该很容易适应任何编译过程。
build/%.o: src/%.cpp src/%.o: src/%.cpp %.o: $(CC) -c $
< -o $@ build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o $(LD) $^ -o $@JasperE 已经解释了为什么“%.o: %.cpp”不起作用;此版本有一个带命令但无先决条件的模式规则 (%.o:),以及两个带先决条件但无命令的模式规则(build/%.o: 和 src/%.o:)。 (请注意,我放入 src/%.o 规则来处理 src/ui/flash.o,假设这不是 build/ui/flash.o 的拼写错误,所以如果您不需要它,您可以留下它。)
build/test.exe 需要 build/widgets/apple.o,
build/widgets/apple.o 看起来像 build/%.o,所以它需要 src/%.cpp (在本例中为 src/widgets/apple.cpp),
build/widgets/apple.o 也看起来像 %.o,因此它执行 CC 命令并使用刚刚找到的先决条件(即 src/widgets/apple.cpp)来构建目标(build/widgets/apple.o)