Makefile с объектами в отдельном дереве каталогов

Я пытался разработать пример makefile, который может создавать объектные файлы в другой структуре каталогов, чем связанный источник. Дерево каталогов, как с источником, так и с целями, выглядит следующим образом:

/repo-root
|-- /build
    |-- /example_lib
        |-- makefile
        |-- /Release*
            |-- libexample_lib.a*
            |-- /obj*
                |-- example_lib.o*
                |-- example_lib.d*
|-- /source
    |-- /example_lib
        |-- /inc
            |-- example_lib.h
        |-- /src
            |-- example_lib.cpp

Папки/файлы со звездочкой — это те файлы, которые должен генерировать makefile.

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

Текущий make-файл, который я использую, выглядит следующим образом, запускается с использованием GNU Make 3.82:

SHELL = /bin/sh

.SUFFIXES:
.SUFFIXES: .cpp .o

# @todo Variables passed to make.
BUILD_CONFIG=Release

# Makefile specific variables
REPO_ROOT=../..
LIBRARY_NAME=example_lib

#-------------------------------------------------------------------
# Derived variables
#-------------------------------------------------------------------

LIBRARY_FILENAME=lib$(LIBRARY_NAME).a
LIBRARY_FILEPATH=$(BUILD_CONFIG)/$(LIBRARY_FILENAME)

# Source directories
CPP_SRC_DIRS=$(REPO_ROOT)/source/example_lib/src

# Source files
vpath %.cpp $(CPP_SRC_DIRS)
CPP_SRCS=$(foreach cpp_src_dir, $(CPP_SRC_DIRS), $(wildcard $(cpp_src_dir)/*.cpp))

# Object/dependencies directory
OBJ_DEPS_DIR=./$(BUILD_CONFIG)/obj

# Object files
OBJS=$(CPP_SRCS:%.cpp=$(OBJ_DEPS_DIR)/%.o)

# Dependency files (built with objects)
DEPS=$(CPP_SRCS:%.cpp=$(OBJ_DEPS_DIR)/%.d)

#-------------------------------------------------------------------
# C++ compiler settings
#-------------------------------------------------------------------

CPP_COMMAND=g++
CPP_OPTIONS_INC_PATHS=-I"$(REPO_ROOT)/source/example_lib/inc"
CPP_OPTIONS_OPTIM=-O3
CPP_OPTIONS_WARN=-Wall
CPP_OPTIONS_MISC=-c -fmessage-length=0
CPP_OPTIONS=$(CPP_OPTIONS_INC_PATHS) $(CPP_OPTIONS_OPTIM) $(CPP_OPTIONS_WARN) $(CPP_OPTIONS_MISC)

#-------------------------------------------------------------------
# Archiver settings
#-------------------------------------------------------------------

AR_COMMAND=ar
AR_OPTIONS=-r

#-------------------------------------------------------------------
# Targets
#-------------------------------------------------------------------

# Object/dependency directory target
$(OBJS): | $(OBJ_DEPS_DIR)
$(OBJ_DEPS_DIR):
    mkdir -p $(OBJ_DEPS_DIR)

# Object targets
$(OBJ_DEPS_DIR)/%.o: %.cpp
    @echo 'Building file: $<'
    @echo 'Invoking: GCC C++ Compiler'
    $(CPP_COMMAND) $(CPP_OPTIONS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
    @echo 'Finished building: $<'
    @echo ' '

# 'all' target
all: $(LIBRARY_FILEPATH)

# Output library target
$(LIBRARY_FILEPATH): $(OBJS)
    @echo 'Building target: $@'
    @echo 'Invoking: GCC Archiver'
    $(AR_COMMAND) $(AR_OPTIONS)  "$@" $(OBJS)
    @echo 'Finished building target: $@'
    @echo ' '

# 'clean' target
clean:
    @echo 'Cleaning targets'
    rm -rf $(OBJS) $(DEPS) $(LIBRARY_FILENAME)
    @echo ' '

# 'PHONY' target
.PHONY: all clean

Что «делает» выходы, что имеет смысл:

make all 
Building file: ../../source/example_lib/src/example_lib.cpp
Invoking: GCC C++ Compiler
g++ -I"../../source/example_lib/inc" -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"Release/obj/../../source/example_lib/src/example_lib.d" -MT"Release/obj/../../source/example_lib/src/example_lib.o" -o "Release/obj/../../source/example_lib/src/example_lib.o" "../../source/example_lib/src/example_lib.cpp"
../../source/example_lib/src/example_lib.cpp:6:1: fatal error: opening dependency file Release/obj/../../source/example_lib/src/example_lib.d: No such file or directory
 }
 ^
compilation terminated.
make: *** [Release/obj/../../source/example_lib/src/example_lib.o] Error 1

Правило «от источника к объекту» заменяет полный путь файла %.cpp на цель %.o, что приводит к пути к объекту:

Release/obj/../../source/example_lib/src/example_lib.o

Чего я не понимаю, так это того, как заставить неявные правила для исходного и объектного файлов совпадать, когда они находятся в разных деревьях. Я использовал для этого vpath, чтобы помочь с исходным разрешением, но часть объекта (цели) не совпадает. Я также попытался изменить правило источника-объекта на:

$(OBJ_DEPS_DIR)/$(notdir %.o): %.cpp

Но это привело к тому же пути (похоже, такие команды, как $(notdir ...) не поддерживаются для сопоставления с подстановочными знаками?). Я также понимаю, что удаление всех объектных файлов из потенциально разных исходных каталогов может привести к конфликту имен, если в двух исходных каталогах окажется файл с одинаковым именем — это не проблема для кода, с которым я работаю.

Любая помощь приветствуется, и спасибо заранее!

how to get the implicit rules for a source and object file to match when they are in different trees Затем вам нужно написать правило (шаблон) для каждой пары соответствующих (под)каталогов «сборка-источник». Этот процесс можно автоматизировать с помощью макросов make.
Matt 21.02.2019 13:35
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
346
1

Ответы 1

Хитрость заключается в том, чтобы удалить ненужные пути к исходным файлам перед созданием имен объектных файлов:

CPP_SRCS=$(foreach cpp_src_dir, $(CPP_SRC_DIRS), $(wildcard $(cpp_src_dir)/*.cpp))
# CPP_SRCS now contains "../../source/example_lib/src/example_lib.cpp"
SRCS := $(notdir $(CPP_SRCS))
# SRCS now contains "example_lib.cpp"
BAD_OBJS := $(CPP_SRCS:%.cpp=$(OBJ_DEPS_DIR)/%.o)
# BAD_OBJS now contains "./Release/obj/../../source/example_lib/src/example_lib.o"
OBJS := $(patsubst %.cpp,$(OBJ_DEPS_DIR)/%.o,$(SRCS))
# OBJS now contains "./Release/obj/example_lib.o"

Возможны небольшие корректировки ваших правил, как только вы проработаете так много.

Другие вопросы по теме