하나의 메이크 파일 만 사용하여 하위 디렉토리의 소스로 메이크 파일을 생성하는 방법
다음과 같은 여러 하위 디렉토리에 소스가 있습니다.
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에 대한 규칙을 찾지 못합니다. build / widgets / apple.o를 만들어야 할 때 % .o : % .cpp가 사용되도록 변경할 수 있습니까?
그 이유는 당신의 규칙이
%.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 를 검색 합니다 . 빌드 규칙은 실제 파일 이름에 액세스하기 위해 특수 변수를 사용해야합니다.
$(BUILDDIR)/%.o: %.cpp
$(CC) $< -o $@
여기서 "$ <"는 make가 첫 번째 종속성을 찾은 경로로 확장됩니다.
또한 이것은 빌드 / 위젯의 모든 .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
필요는 없습니다.
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
이것은 또 다른 트릭입니다.
메인 '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 $@.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.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
이렇게하면 고통스러운 조작이나 여러 명령 시퀀스없이 수행 할 수 있습니다.
빌드 /%.o : src / %. cpp 소스 /%.o : 소스 /%.cpp %.영형: $ (참조) -c $ <-o $ @ build / test.exe : build / widgets / apple.o build / widgets / knob.o build / tests / blend.o src / ui / flash.o $ (LD) $ ^ -o $ @
JasperE has explained why "%.o: %.cpp" won't work; this version has one pattern rule (%.o:) with commands and no prereqs, and two pattern rules (build/%.o: and src/%.o:) with prereqs and no commands. (Note that I put in the src/%.o rule to deal with src/ui/flash.o, assuming that wasn't a typo for build/ui/flash.o, so if you don't need it you can leave it out.)
build/test.exe needs build/widgets/apple.o,
build/widgets/apple.o looks like build/%.o, so it needs src/%.cpp (in this case src/widgets/apple.cpp),
build/widgets/apple.o also looks like %.o, so it executes the CC command and uses the prereqs it just found (namely src/widgets/apple.cpp) to build the target (build/widgets/apple.o)
Here is my solution, inspired from Beta's answer. It's simpler than the other proposed solutions
I have a project with several C files, stored in many subdirectories. For example:
src/lib.c
src/aa/a1.c
src/aa/a2.c
src/bb/b1.c
src/cc/c1.c
Here is my Makefile (in the src/
directory):
# 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
This example works fine for a shared library, and it should be very easy to adapt for any compilation process.
Usually, you create a Makefile in each subdirectory, and write in the top-level Makefile to call make in the subdirectories.
This page may help: http://www.gnu.org/software/make/
ReferenceURL : https://stackoverflow.com/questions/231229/how-to-generate-a-makefile-with-source-in-sub-directories-using-just-one-makefil
'Development Tip' 카테고리의 다른 글
오류! (0) | 2021.01.05 |
---|---|
Node.js에서 디렉토리를 제거하지 않고 디렉토리에서 모든 파일을 제거하는 방법 (0) | 2021.01.05 |
Django-템플릿 'for'루프에서 튜플 풀기 방법 (0) | 2021.01.05 |
내 DateTime을 UTC로 변환하는 데 문제가 있습니다. (0) | 2021.01.05 |
`File` 객체의 액세스 모드 (예 : w +, r +)의 차이점 (0) | 2021.01.05 |