如何仅使用一个Makefile在子目录中生成源代码为的Makefile

如何仅使用一个Makefile在子目录中生成源代码为的Makefile,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

我有很多子目录中的源代码,如:

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

通常,您在每个子目录中创建一个Makefile,然后写入顶级Makefile以调用子目录中的make


此页面可能会有所帮助:

原因是您的规则

%.o: %.cpp
       ...
希望.cpp文件与建筑中的.o文件位于同一目录中。由于您的test.exe依赖于build/widgets/apple.o(etc),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 finds:

$(BUILDDIR)/%.o: %.cpp
        $(CC) $< -o $@
我建议您使用“”,以避免重复实际的编译器生成规则:

define cc-command
$(CC) $(CFLAGS) $< -o $@
endef

问题是,
$@
将包含源文件的整个(相对)路径,该路径反过来用于构造对象名称(以及其相对路径)

我们使用:

#####################
# 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 $@
结果显示在以下对象目录中:

objs/widget/apple.o
objs/tests/blend.o
# 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 $@ 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命令并使用刚刚找到的prereq(即src/widgets/apple.cpp)来构建目标(build/widgets/apple.o)

这样做的诀窍是:

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))))
CC:=g++
LD:=g++
模块:=小部件测试ui
SRC_DIR:=$(addprefix SRC/,$(模块))
BUILD_DIR:=$(addprefix BUILD/,$(MODULES))
SRC:=$(foreach sdir,$(SRC_DIR),$(通配符$(sdir)/*.cpp))
对象:=$(patsubst src/%.cpp,build/%.o,$(src))
包括:=$(addprefix-I,$(SRC_DIR))
vpath%.cpp$(SRC\u目录)
制定目标
$1/%.o:%.cpp
$(CC)$(包括)-c$$<-o$$@
恩德夫
.冒牌货:所有的支票都是干净的
全部:checkdirs build/test.exe
build/test.exe:$(OBJ)
$(LD)$^-o$@
checkdirs:$(生成目录)
$(构建目录):
@mkdir-p$@
清洁:
@rm-rf$(构建目录)
$(foreach bdir,$(BUILD_DIR),$(eval$(call make goal,$(bdir)))
此生成文件假定源目录中有include文件。它还检查构建目录是否存在,如果不存在则创建它们

最后一行是最重要的。它使用函数
make goal
为每个构建创建隐式规则,不需要逐个编写

您还可以使用

添加自动依赖项生成这是另一个技巧

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

生成文件:

widget/apple.cpp
tests/blend.cpp
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)
./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

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

我有一个项目,有几个C文件,存储在许多子目录中。 例如:

src/lib.c
src/aa/a1.c
src/aa/a2.c
src/bb/b1.c
src/cc/c1.c
这是我的Makefile(在
src/
目录中):

#make->编译共享库“libfoo.so”
#清除->删除库文件和所有对象文件(.o)
#使所有->清理并编译
SONAME=libfoo.so
SRC=lib.c\
aa/a1.c\
aa/a2.c\
bb/b1.c\
cc/c1.c
#编译选项
CFLAGS=-O2-g-W-Wall-Wno未使用参数-Wbad函数cast-fPIC
#链接选项
LDFLAGS=-shared-Wl,-soname,$(soname)
#如何编译单个对象文件
OBJS=$(SRC:.c=.o)
.c.o.:
$(CC)$(CFLAGS)-c$<-o$@
.骗子:都是干净的
#图书馆编辑
$(SONAME):$(OBJS)$(SRC)
$(CC)$(OBJS)$(LDFLAGS)-o$(SONAME)
#清洁规则
清洁:
rm-f$(OBJS)$(索纳姆)*~
#附加规则
全部:清洁库

此示例适用于共享库,并且应该很容易适应任何编译过程。

这是通常的做法,但充满了问题。主要的一点是,没有人让进程知道所有的依赖关系,所以在多核系统上像-j2这样的东西是不起作用的。见基思的参考文献是一篇优秀的论文,名为“递归使被认为是有害的”。这是对系列文章的一个贡献,从Dijkstra的“转到被认为有害的”信开始,最后是“被认为有害的”被认为有害。这样做通常是因为人们不了解如何编写Makefiles。每个目录一个Makefile很糟糕。你对foreach的使用真的帮助了我。我创建了一个长长的C文件列表,生成了一个单独的对象文件。谢谢@Manzill0在处理多个目标及其依赖关系时,以下问题具有相同的问题集?这是
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
src/lib.c
src/aa/a1.c
src/aa/a2.c
src/bb/b1.c
src/cc/c1.c
# 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