Makefile - 在不同的目录中编译单个对象

Makefile - Compile Single Objects in different directory

我一直在梳理网络,但我想不出使它起作用的正确方法。只是尝试创建一个简单的 Makefile,它采用我的源代码并只构建更改的文件。我需要将所有 .o 文件放在同一个输出文件夹中。我目前一切正常,除了如果我更改一个文件,整个文件都会重建。例如,如果我更改 main.c,它也会编译 EOL.c。但是,如果没有任何变化,它表示无需执行任何操作。

NAME=Program

CC=arm-none-eabi-gcc
CFLAGS=-c -Wall -O0  -std=c99 \
-nostartfiles --specs=nano.specs \
-mthumb -fmessage-length=0 \
-fsigned-char -ffunction-sections \
-fdata-sections -mcpu=cortex-m0

BID?=_DEV
DEFINES= -DPROD -DBLD_ID=\"$(BID)\"
LDFLAGS= -nostartfiles 
INCLUDES= -ISrc/App/Include -ISrc/Device/CMSIS/Include 
SOURCES= Src/main.c Src/App/Source/Application.c Src/App/Source/EOL.c Src/Svc/Source/TimerManager.c
OBJECTS=$(OBJECTS1:.c=.o)
OBJECTS1=$(SOURCES:.S=.o)
OFILES1=$(notdir ${OBJECTS})
OFILES=$(addprefix $(OBJDIR)/,$(OFILES1))
OBJDIR=Output

.PHONY: all rebuild clean

all: $(OBJDIR) $(SOURCES) $(OBJDIR)/$(NAME).hex

%.hex: %.elf
    arm-none-eabi-objcopy -O ihex $< $@

%elf: $(OBJECTS)
    $(CC) $(LDFLAGS) $(OFILES) -o $@


rebuild: clean all

.SECONDARY:
.c.o:
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) $< -o $(OBJDIR)/$(notdir $@)

.S.o:
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) $< -o $(OBJDIR)/$(notdir $@)

$(OBJDIR):
    mkdir $(OBJDIR)

clean:
    rm -f $(OBJDIR)/*.o $(OBJDIR)/*.elf $(OBJDIR)/*.hex $(OBJDIR)/*.bin

这个生成文件有几个问题。基本上,您有一些规则,其目标不是它们实际生成的文件,还有一个规则,其先决条件不是它实际需要的文件。

假设您已修改 Src/main.c 并尝试使用此规则重建 Output/Program.elf

%elf: $(OBJECTS)
    $(CC) $(LDFLAGS) $(OFILES) -o $@

先决条件($(OBJECTS))其实就是Src/main.o Src/App/Source/EOL.o等等。这些文件不存在——它们从不存在——但是它们有一个规则:

.c.o:
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) $< -o $(OBJDIR)/$(notdir $@)

Make 发现 Src/main.o 依赖于 Src/main.c,因此必须重建,Output/Program.elf 也是如此。所以它调用了这条规则——它实际上构建了 Output/main.o。但是 elf 规则要求 all 个(虚构的)目标文件,所以所有的源代码都必须重新编译——变成已经存在并且没有过时的目标文件,但是 Make没注意。

首先要做的是修复对象规则,但是有一个问题:虽然规则有缺陷,但它们的优点是帮助 Make 找到相应的源文件(在滥用它们之前),就像这样:

Src/App/Source/EOL.o: Src/App/Source/EOL.c
    ...

如何告诉Make去哪里找Output/EOL.o对应的源文件呢?方法不止一种,但一个好方法是使用 vpath:

vpath %.c Src/App/Source

Output/EOL.o: EOL.c
   ...

我们所要做的就是创建一个源目录列表,将其传递给 vpath,并修改模式规则:

SRCDIRS := $(dir $(SOURCES))
vpath %.c $(SRCDIRS)

$(OBJDIR)/%.o: %.c
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) $< -o $@

.S.o 规则可以用同样的方法修复。)

然后修改 elf 规则以命名并使用其真正的先决条件:

%elf: $(OFILES)
    $(CC) $(LDFLAGS) $^ -o $@