如何使用一个生成文件在子目录中使用源代码生成Makefile

我有一系列的子目录,如:

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 

当我尝试这个时,没有findbuild / widgets / apple.o的规则。 我是否可以改变某些东西,以便在需要构build/部件/ apple.o时使用%.o:%.cpp?

原因是你的规则

 %.o: %.cpp ... 

期望.cpp文件驻留在与您的build筑物相同的目录中。 由于你的情况下的test.exe依赖于构build/部件/ apple.o(等),使得预计apple.cpp是构build/部件/ apple.cpp。

你可以使用VPATH来解决这个问题:

 VPATH = src/widgets BUILDDIR = build/widgets $(BUILDDIR)/%.o: %.cpp ... 

当试图构build“build / widgets / apple.o”时,make会在VPATH中searchapple.cpp。 请注意,构build规则必须使用特殊variables才能访问实际的文件名make find:

 $(BUILDDIR)/%.o: %.cpp $(CC) $< -o $@ 

其中“$ <”扩展到make所在位置的第一个依赖的path。

另外请注意,这将构buildbuild / widgets中的所有.o文件。 如果你想在不同目录下build立二进制文件,你可以做类似的事情

 build/widgets/%.o: %.cpp .... build/ui/%.o: %.cpp .... build/tests/%.o: %.cpp .... 

我build议你使用“ canned command sequences ”来避免重复实际的编译器构build规则:

 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假定你有你的包含文件在源目录中。 它还检查构build目录是否存在,如果它们不存在则创build它们。

最后一行是最重要的。 它使用函数make-goal为每个构build创build隐式规则,而不必逐一写入

您也可以使用Tromey的方式添加自动依赖关系生成

Thing是$@将包括源文件的整个(相对)path,而源文件又被用来构造对象名(以及相对path)

我们用:

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

这将根据源中的子目录创build一个名称在$(OBJDIR_1)指定的对象目录和子目录。

例如(假定objs为顶级对象目录),在Makefile中:

 widget/apple.cpp tests/blend.cpp 

导致以下对象目录:

 objs/widget/apple.o objs/tests/blend.o 

这将做到没有痛苦的操纵或多个命令序列:

 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 :),带有prereqs和没有命令。 (注意,我把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命令,并使用刚发现的prereq(即src / widgets / apple.cpp)来构build目标(build / widgets / apple.o)

这是另一个窍门。

在主“Makefile”中为每个源目录定义SRCDIR,并为每个SRCDIR值包含“makef.mk”。 在每个源代码目录中,将文件“files.mk”放在源文件列表中,并为其中一些编译选项。 在主要的“Makefile”中,可以定义编译选项并为每个SRCDIR值排除文件。

Makefile文件:

 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 $@.pd -c $(C_FLAGS) $(C_FLAGS_$(call clear_name,$<)) $< -o $@ @sed -e 's,.*:,SRC_FILES += ,g' < $@.pd > $@.d @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < $@.pd >> $@.d @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < $@.pd >> $@.d -@rm -f $@.pd $(CPP_OBJS) : $(OBJDIR)/%.o : %.cpp Makefile $(CC) -MMD -MF $@.pd -c $(CPP_FLAGS) $(CPP_FLAGS_$(call clear_name,$<)) $< -o $@ @sed -e 's,.*:,SRC_FILES += ,g' < $@.pd > $@.d @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < $@.pd >> $@.d @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < $@.pd >> $@.d -@rm -f $@.pd $(AS_OBJS) : $(OBJDIR)/%.o : %.S Makefile $(CC) -MMD -MF $@.pd -c $(AS_FLAGS) $(AS_FLAGS_$(call clear_name,$<)) $< -o $@ @sed -e 's,.*:,SRC_FILES += ,g' < $@.pd > $@.d @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < $@.pd >> $@.d @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < $@.pd >> $@.d -@rm -f $@.pd 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:%=%/*.od) -@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 

这是我的解决scheme,从Beta的答案启发。 这比其他build议的解决scheme更简单

我有一个项目与几个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) .co: $(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一个Makefile,并写入顶级Makefile以在子目录中调用make。

此页面可能有所帮助: http : //www.gnu.org/software/make/