Как создать Makefile с исходным кодом в подкаталогах, используя только один 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
когда я пытаюсь это сделать, он не находит правила для сборки/виджетов/apple.o. могу ли я изменить что-то так, чтобы %.о.% :cpp используется, когда ему нужно сделать сборку/виджеты/apple.о ?
7 ответов:
причина в том, что правило
%.o: %.cpp ...
ожидает .cpp-файл должен находиться в том же каталоге, что и файл .о ваше здание. С момента испытания.exe в вашем случае зависит от сборки / виджетов / apple.о (прочее), сделать ожидает от Apple.cpp для сборки / виджеты / apple.СРР.
вы можете использовать VPATH для решения этой проблемы:
VPATH = src/widgets BUILDDIR = build/widgets $(BUILDDIR)/%.o: %.cpp ...
при попытке построить " build / widgets / apple.o", make будет искать apple.cpp в VPATH. Обратите внимание, что правило построения должно использовать специальные переменные для доступа к фактическому имени файла делают находки:
$(BUILDDIR)/%.o: %.cpp $(CC) $< -o $@
где "$
Также обратите внимание, что это будет строить все.o файлы в build/виджеты. Если вы хотите построить двоичные файлы в разных каталогах, вы можете сделать что-то вроде
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 /%.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 предполагает, что у вас есть ваши файлы include в исходных каталогах. Также он проверяет, существуют ли каталоги сборки, и создает их, если они не существуют.
последняя строка самая важная. Это создает неявные правила для каждой сборки с помощью функции
make-goal
и не надо писать их по одномувы также можете добавить автоматическую генерацию зависимостей, используя Tromey по путь
дело в том
$@
будет включать весь (относительный) путь к исходному файлу, который в свою очередь используется для построения имени объекта (и, следовательно, его относительный путь)мы используем:
##################### # 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
это сделает это без болезненных манипуляций или нескольких последовательностей команд:
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 $@Джаспер объяснил, почему"%.о.% :cpp " не будет работать; эта версия имеет одно правило шаблона (%.o:) с командами и без предварительных требований и двумя правилами шаблонов (build/%.o: и src/%.o:) с предварительными запросами и без команд. (Обратите внимание, что я поставил в файле src/%.o правило для работы с src / ui / flash.o, предполагая, что это не опечатка для build/ui/flash.о, так что если вам это не нужно, вы можете оставить его из.)
построить/тест.exe нуждается в сборке / виджеты / apple.о
сборка/виджеты/яблоко.o выглядит как build/%.o, поэтому ему нужен src/%.cpp (в данном случае src/widgets/apple.cpp),
сборка/виджеты/яблоко.о, кажется,%.o, поэтому он выполняет команду CC и использует только что найденные предварительные запросы (а именно src/widgets/apple.cpp) для построения цели (build/widgets/apple.о)
это еще один трюк.
в основном "формирование файла" задайте каталог исходного кода для каждого источника Dir и включать makef.МК' для каждого значения каталог исходного кода. В каждый исходный dir положить файл 'files.mk' со списком исходных файлов и параметров компиляции для некоторых из них. В главном "Makefile" можно определить параметры компиляции и исключить файлы для каждого значения Каталог исходного кода.
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 $@.p.d -c $(C_FLAGS) $(C_FLAGS_$(call clear_name,$<)) $< -o $@ @sed -e 's,.*:,SRC_FILES += ,g' < $@.p.d > $@.d @sed -e "$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < $@.p.d >> $@.d @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \$$,,' -e 's,$$, :,' < $@.p.d >> $@.d -@rm -f $@.p.d $(CPP_OBJS) : $(OBJDIR)/%.o : %.cpp Makefile $(CC) -MMD -MF $@.p.d -c $(CPP_FLAGS) $(CPP_FLAGS_$(call clear_name,$<)) $< -o $@ @sed -e 's,.*:,SRC_FILES += ,g' < $@.p.d > $@.d @sed -e "$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < $@.p.d >> $@.d @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \$$,,' -e 's,$$, :,' < $@.p.d >> $@.d -@rm -f $@.p.d $(AS_OBJS) : $(OBJDIR)/%.o : %.S Makefile $(CC) -MMD -MF $@.p.d -c $(AS_FLAGS) $(AS_FLAGS_$(call clear_name,$<)) $< -o $@ @sed -e 's,.*:,SRC_FILES += ,g' < $@.p.d > $@.d @sed -e "$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < $@.p.d >> $@.d @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \$$,,' -e 's,$$, :,' < $@.p.d >> $@.d -@rm -f $@.p.d 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.МК
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)
./файлов.МК
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 (в
обычно вы создаете Makefile в каждом подкаталоге и записываете в Makefile верхнего уровня, чтобы вызвать make в подкаталогах.
эта страница может помочь: http://www.gnu.org/software/make/