使用不同的预处理器宏在同一源树上构建多个目标
Building multiple targets off same source tree with different preprocessor macros
我对make和自动工具(我还没有在这个项目中使用)的了解充其量只是初步的,尽管经过了长时间的谷歌搜索和实验。我有一个像下面这样的源层次结构,我正试图找到一种尽可能无缝构建的方法。
该应用程序由一个主应用程序组成,其源代码位于app/src下的各个子文件夹中。这些文件是使用该文件夹根目录中的相应Makefile构建的。
然后,我有多个其他实用程序,它们位于应用程序/工具下的不同文件夹中,每个文件夹都有自己的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。因此,编译主应用程序和varous实用程序生成的对象文件是不兼容的。
目前,为了构建项目的每一部分,我必须清理其间的源代码树。这很痛苦,最终肯定不是我想要的。解决这个问题的最佳方法是什么?我一直无法付诸实践的想法是:
- 项目的每个部分都有单独的生成目录
- 构建外部工具时,以某种方式在主应用程序源代码树中标记其对象文件(util.file1.o?)
我不太确定我是否有掌握制作/自动工具所需的时间和耐心。其他构建工具(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
我是在大量谷歌搜索SO浏览后才想到这一点的。这似乎有效,但这部分看起来并不特别好(感觉有点像黑客):
$(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的术语来说,创建这样的构建目录称为variant_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中设置并向下传递。我更喜欢第二个选项,因为它将使src/module SCons脚本模块化,并且不知道(不可知)预处理器宏。
这是根构建脚本,它创建不同的env并协调子目录构建脚本:
app/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)
这是main的构建脚本app/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)
这是模块库的构建脚本,它将被调用两次,每次都使用不同的envapp/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)
- 如何让KDevelop & CMake只构建特定目标?
- 指定 NDK 构建的目标架构
- 在多个目标上 CMake 后期构建自定义命令?
- 在QT中的大型项目中指定构建/目标/安装路径
- 在构建 Android 应用程序时禁用 CMake 目标
- CMake 将目标作为项目的一部分独立构建
- Cmake:如何构建自定义编译器二进制文件,然后将其用于某些目标?
- 当许多目标使用相同的C 源时,如何加快介子的构建
- 如何配置要使用默认的HTML包装器构建的Emscripten目标
- 致命错误LNK1112:通过 vcvarsall .bat x86 运行构建'X86'模块计算机类型'x64'与目标计算机类型冲突
- 如何只构建一个依赖项目标
- 制定目标需要32和64位的DLL构建
- Microsoft Visual Studio 2010 没有将构建工具放在 PATH 中以实现干净的目标?
- Scons VisualStudio,多个构建目标生成重复的NMakeOutput标签
- boost构建中的多个构建目标
- 在Qt中使用不同的模板构建目标
- Visual Studio C/C++ 中的多个构建目标
- EclipseCDT-带有现有代码的新Makefile项目-更改全局默认构建目标
- 由于错误条件,构建目标被跳过;("@(条件名称此处)" - 不确定如何更改
- Cmake /ctest:是否可以使用创建的构建目标作为测试