Makefile — компилировать отдельные объекты в другой каталог

#gcc #makefile #gnu-make

#gcc #makefile #gnu-make

Вопрос:

Я прочесывал веб-страницы и не могу найти правильный способ заставить это работать. Просто пытаюсь создать простой 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
  

Комментарии:

1. Я не вижу связи тега «cmake» с проблемой. CMake — это не Make.

Ответ №1:

С этим makefile существует несколько проблем. По сути, у вас есть правила, целями которых не являются файлы, которые они фактически создают, и правило, предварительными условиями которого не являются файлы, которые ему действительно нужны.

Предположим, вы внесли изменения 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 требует всех (воображаемых) объектных файлов, поэтому все исходные файлы должны быть перекомпилированы — в объектные файлы, которые уже существуют и не устарели, но на которые 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 $@