Мои знания делать а также Autotools (который я пока не использую для этого проекта) в лучшем случае рудиментарен, несмотря на то, что много гуглит и экспериментирует в течение длительного периода времени. У меня есть исходная иерархия, подобная приведенной ниже, и я пытаюсь найти способ ее создания как можно более плавно.
Приложение состоит из основного приложения с источником в различных подпапках в app / src. Они создаются с соответствующим Makefile в корне этой папки.
Затем у меня есть несколько других утилит, которые находятся в разных папках в app / tools, каждый из которых имеет свой собственный Makefile.
app/src/module1/file1.cpp
app/src/module1/file1.hpp
app/src/module2/file2.cpp
app/src/module2/file2.hpp
app/src/module3/file3.cpp
app/src/module3/file3.hpp
app/src/main.cpp
app/src/main.hpp
app/src/Makefile
app/tools/util1/file1.cpp
app/tools/util1/file1.hpp
app/tools/util1/Makefile
app/tools/util2/file2.cpp
app/tools/util2/file2.hpp
app/tools/util2/Makefile
Для меня проблема в том, что некоторые из этих инструментов зависят от исходных файлов в исходной папке app / src, но с включенным макросом предварительной обработки EXTERNAL_TOOL. Поэтому объектные файлы, созданные при компиляции основного приложения и различных утилит, несовместимы.
В настоящее время для создания каждой части проекта мне нужно очистить исходное дерево между ними. Это больно и, конечно, не то, что я хочу в конце концов. Каков наилучший способ решить эту проблему? У меня были идеи, которые я не смог реализовать на практике:
Я не уверен, что у меня есть время и терпение, необходимые для освоения make / autotools. Может ли один из других инструментов сборки (scons? Cmake?) Упростить выполнение такой задачи? Если так, то какой?
ОБНОВИТЬ: Вот что у меня сейчас
SOURCES := util1.cpp util2.cpp util3.cpp \
../../src/module1/file1.cpp \
../../src/module1/file2.cpp \
../../src/module1/file3.cpp \
../../src/module2/file4.cpp \
../../src/module3/file5.cpp \
../../src/module3/file6.cpp \
../../src/module4/file7.cpp \
../../src/module4/file8.cpp \
../../src/module3/file9.cpp \
../../src/module4/file10.cpp \
../../src/module5/file11.cpp \
../../src/module3/file12.cpp \
../../src/module1/file13.cpp \
../../src/module3/file14.cpp \
../../src/module3/file15.cpp
OBJECTS = $(join $(addsuffix .util/, $(dir $(SOURCES))), $(notdir $(SOURCES:.cpp=.o)))
.PHONY: all mkdir
all: util
util: $(OBJECTS)
$(CXX) $(CXXFLAGS) $(OBJECTS) $(LIBS) -o util
$(OBJECTS): | mkdir
$(CXX) -c $(CXXFLAGS) -o $@ $(patsubst %.o,%.cpp,$(subst .util/,,$@))
mkdir:
@mkdir -p $(sort $(dir $(OBJECTS)))
clean:
-@rm -f $(OBJECTS) util
-@rmdir $(sort $(dir $(OBJECTS))) 2>/dev/null
Я пришел к этому после интенсивного поиска в Google. Кажется, это работает, но эта часть не кажется особенно приятной (похоже на хак):
$(OBJECTS): | mkdir
$(CXX) -c $(CXXFLAGS) -o $@ $(patsubst %.o,%.cpp,$(subst .util/,,$@))
В частности, я не слишком заинтересован в том факте, что я создаю список объектов из источников ранее и добавляю суффикс, только чтобы сделать обратное здесь. Я не мог заставить его работать другим способом.
CMake имеет add_definitions
а также remove_definitions
команды. Вы можете использовать их для определения макросов для разных частей вашего проекта:
# building tools #
add_definitions(-DEXTERNAL_TOOL)
add_subdirectory($TOOL1$ $BUILD_DIR$)
add_subdirectory($TOOL2$ $BUILD_DIR$)
...
# building main app #
remove_definitions(-DEXTERNAL_TOOL)
add_executable(...)
С SCons это можно сделать безболезненно. Вам определенно понадобится иерархия каталогов для объектов, созданных с помощью различных макросов препроцессора. С точки зрения SCons, создание таких каталогов сборки называется option_dir. Я бы порекомендовал следующую иерархическую структуру сборки SCons:
app/SConstruct
app/src/module1/file1.cpp
app/src/module1/file1.hpp
app/src/module2/file2.cpp
app/src/module2/file2.hpp
app/src/module3/file3.cpp
app/src/module3/file3.hpp
app/src/main.cpp
app/src/main.hpp
app/src/SConscript_modules
app/src/SConscript_main
app/tools/util1/file1.cpp
app/tools/util1/file1.hpp
app/tools/util2/file2.cpp
app/tools/util2/file2.hpp
app/tools/SConscript
app/build/main/
app/build/target1/modules/
app/build/target2/modules/
app/build/tools/utils/
Чтобы иметь возможность создавать одни и те же исходные файлы с разными макросами препроцессора, вам необходимо создать один и тот же файл с несколькими различными средами. Эти env могут быть установлены в скриптах src / module SConscript или из корневого SConstruct и переданы. Я предпочитаю второй вариант, так как он сделает скрипты SCons модуля src / module модульными и не будет знать (не зависит) макросов препроцессора.
Вот корневой скрипт сборки, который создает различные env и управляет сценариями сборки подкаталога:
Приложение / SConstruct
defines1 = ['MACRO1']
defines2 = ['MACRO2']
env1 = Environment(CPPDEFINES = defines1)
env2 = Environment(CPPDEFINES = defines2)
includePaths = [
'src/module1',
'src/module2',
'src/module3',
]
env1.Append(CPPPATH = includePaths)
env2.Append(CPPPATH = includePaths)
# Build different versions of the module libs
SConscript('src/SConscript_modules',
variant_dir = '#build/target1/modules',
exports = {'env':env1},
duplicate=0)
SConscript('src/SConscript_modules',
variant_dir = '#build/target2/modules',
exports = {'env':env2},
duplicate=0)
# Build main with env1
SConscript('src/SConscript_main',
variant_dir = '#build/main',
exports = {'env':env2},
duplicate=0)
# Build tools with env2
SConscript('tools/SConscript',
variant_dir = '#build/utils',
exports = {'env':env2},
duplicate=0)
Это скрипт сборки для основного
Приложение / SRC / SConscript_main
Import('env')
sourceFiles = ['main.cpp']
# If you want to modify the env here, Clone() it first, otherwise
# the changes will be visible to all other SConscripts
env.Program(target = 'main', source = sourceFiles)
Это скрипт сборки для модуля libs, он будет вызываться дважды, каждый раз с другим env
Приложение / SRC / SConscript_modules
Import('env')
module1SourceFiles = ['file1.cpp']
module2SourceFiles = ['file2.cpp']
module3SourceFiles = ['file3.cpp']
# If you want to modify the env here, Clone() it first, otherwise
# the changes will be visible to all other SConscripts
env.Library(target = 'module1', source = module1SourceFiles)
env.Library(target = 'module2', source = module2SourceFiles)
env.Library(target = 'module3', source = module3SourceFiles)