如何仅使用一个 makefile 来生成子目录中包含源代码的 Makefile

问题描述 投票:0回答:7

我的源代码位于一堆子目录中,例如:

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
吗?

makefile
7个回答
76
投票

原因是你的规则

%.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)
    

75
投票
这可以解决问题:

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

为每个构建创建隐式规则,并且不需要一一编写

您还可以使用

Tromey 的方式添加自动依赖项生成


8
投票
事情是

$@

将包括源文件的整个(相对)路径,该路径又用于构造对象名称(及其相对路径)

我们使用:

##################### # 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
    

6
投票
这又是一个技巧。

在主“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
    

4
投票
这是我的解决方案,灵感来自 Beta 的答案。它比其他建议的解决方案更简单

我有一个包含多个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

这个示例对于共享库来说效果很好,并且应该很容易适应任何编译过程。


3
投票
这将不需要痛苦的操作或多个命令序列来完成:

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)


0
投票
通常,在每个子目录中创建一个Makefile,并在顶层Makefile中写入以调用子目录中的make。

此页面可能有帮助:

http://www.gnu.org/software/make/

© www.soinside.com 2019 - 2024. All rights reserved.