Makefile файлы, соответствующие источникам из разных подпапок в одну папку сборки - PullRequest
0 голосов
/ 03 июля 2018

Я хочу создать make-файл, который будет принимать все файлы в нескольких подкаталогах src и компилировать их каждый непосредственно в один каталог сборки.

т.е. у меня есть, например,

  • ЦСИ / main.c
  • SRC / i2c / i2c.c
  • SRC / i2c / i2c.h

и в качестве вывода я хочу, чтобы объектные файлы, а также окончательный двоичный файл - build / main.o - build / i2c.o - build / release.elf

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

Сбой при попытке сопоставить main.o с i2c.c.

Вот «соответствующая» часть Makefile:

TARGET = $(lastword $(subst /, ,$(CURDIR)))

BUILD_DIR  := buildDir

SOURCES = $(wildcard src/*.c src/*/*.c)
BROKENOBJECTS = $(SOURCES:.c=.o)
LESSBROKEN = $(notdir $(BROKENOBJECTS))
OBJECT_FILES = $(addprefix $(BUILD_DIR)/, $(LESSBROKEN))

$(BUILD_DIR)/%.o: $(SOURCES) $(BUILD_DIR)
    $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<

$(BUILD_DIR)/$(TARGET).elf: $(OBJECT_FILES)
    $(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@

$(BUILD_DIR) :
    mkdir -p $@

compile : $(BUILD_DIR)/$(TARGET).elf

Как мне поступить так, запустив рецепт для каждого файла .c из $ (SOURCES) и просто создать соответствующий файл .o в buildDir /?

Ответы [ 3 ]

0 голосов
/ 03 июля 2018

Вы можете использовать механизм make vpath . Таким образом, вместо указания возможных исходных путей с использованием ...

SOURCES = $(wildcard src/*.c src/*/*.c)

у тебя будет ...

# Build a list of directories under src
#
SOURCE_DIRS := $(shell find src -type d)

# Use the list in $(SOURCE_DIRS) as a search path for .c files.
#
vpath %.c $(SOURCE_DIRS)

Теперь при попытке обновить i2c.o (например) правило ...

$(BUILD_DIR)/%.o: %.c $(BUILD_DIR)
    $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<

заставит make автоматически искать в списке исходных каталогов зависимость i2c.c.


Примечание: По очевидным причинам, несколько файлов с одинаковыми именами в разных исходных каталогах могут вызвать проблемы здесь. Отсюда мой оригинальный вопрос (в комментариях) относительно уникальности имен исходных файлов в разных каталогах.

0 голосов
/ 03 июля 2018

Если вы используете GNU make и все ваши исходные файлы на C * *.c, которые можно найти в текущем каталоге и во всех его подкаталогах (до любой глубины), это должно быть близко к тому, что вы хотите:

BUILDDIR := build
SRC      := $(shell find . -type f -name '*.c')

# Convert C source file name(s) to object file name(s)
# $(1): C source file name(s)
define c2o
$(patsubst %.c,$(BUILDDIR)/%.o,$(notdir $(1)))
endef

OBJ := $(call c2o,$(SRC))

.PHONY: all

all: $(OBJ)

# Compilation rule for a C source file (use echo for testing)
# $(1): C source file name
define MY_rule
$$(call c2o,$(1)): $(1)
    @echo $$(CC) $$(CFLAGS) $$(CPPFLAGS) $$(TARGET_ARCH) -c -o $$@ $$<
endef

# Instantiate compilation rules for all C source files
$(foreach s,$(SRC),$(eval $(call MY_rule,$(s))))

Демо-версия:

host> tree .
.
├── Makefile
├── a.c
├── b
│   └── b.c
└── c
    └── c
        └── c.c

host> make
cc -c -o build/a.o a.c
cc -c -o build/c.o c/c/c.c
cc -c -o build/b.o b/b.c

Обратите внимание на использование $$ в определении MY_rule. Это необходимо, потому что он раскрывается дважды: один раз при расширении параметров функции eval и второй раз, когда make анализирует результат как обычный синтаксис make.

Как объяснено в других комментариях и ответах, это работает, только если у вас нет нескольких исходных файлов на Си с одинаковым базовым именем. Есть способ обнаружить эту ситуацию и выдать ошибку, если она встречается. Функция make sort сортирует свой параметр списка слов, но также удаляет дубликаты. Таким образом, если количество слов до и после сортировки отличается, у вас есть дубликаты. Добавьте следующее сразу после определения OBJ:

SOBJ     := $(sort $(OBJ))

ifneq ($(words $(OBJ)),$(words $(SOBJ)))
$(error Found multiple C source files with same base name)
endif

Демо-версия:

host> touch c/c/a.c
host> make
Makefile:13: *** Found multiple C source files with same base name.  Stop.
0 голосов
/ 03 июля 2018

Вот модифицированный фрагмент кода, который должен делать то, что вы хотите, хотя я не нашел решения, не указав каждый подкаталог в src/ вручную.

SOURCES = $(wildcard src/*.c)
SUBSOURCES = $(wildcard src/*/*.c)

OBJECTS = $(addprefix $(BUILD_DIR)/, $(notdir $(SOURCES:.c=.o)))
SUBOBJECTS = $(addprefix $(BUILD_DIR)/, $(notdir $(SUBSOURCES:.c=.o)))

compile : $(BUILD_DIR)/$(TARGET).elf

$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) $(SUBOBJECTS)
    $(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@

# save some typing for the rules below
COMPILE = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<

$(OBJECTS): $(BUILD_DIR)/%.o: src/%.c | $(BUILD_DIR)
    $(COMPILE)

$(BUILD_DIR)/%.o: src/i2c/%.c | $(BUILD_DIR)
    $(COMPILE)

$(BUILD_DIR)/%.o: src/someOtherSubdir/%.c | $(BUILD_DIR)
    $(COMPILE)

Как @ Г.М. Предложенный в комментариях, вы должны убедиться, что имена исходных файлов уникальны для всех подкаталогов. Также обратите внимание, что я превратил $(BUILD_DIR) в обязательное условие заказа , которое должно более точно отражать ваши намерения.

...