Я пытался разработать пример 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 ...) не поддерживаются для сопоставления с подстановочными знаками?). Я также понимаю, что удаление всех объектных файлов из потенциально разных исходных каталогов может привести к конфликту имен, если в двух исходных каталогах окажется файл с одинаковым именем — это не проблема для кода, с которым я работаю.
Любая помощь приветствуется, и спасибо заранее!





Хитрость заключается в том, чтобы удалить ненужные пути к исходным файлам перед созданием имен объектных файлов:
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"
Возможны небольшие корректировки ваших правил, как только вы проработаете так много.
how to get the implicit rules for a source and object file to match when they are in different treesЗатем вам нужно написать правило (шаблон) для каждой пары соответствующих (под)каталогов «сборка-источник». Этот процесс можно автоматизировать с помощью макросов make.