使用不同的预处理器宏在同一源树上构建多个目标

时间:2013-02-07 07:00:52

标签: c++ build makefile cmake scons

我对制作 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。因此,编译主应用程序和varous实用程序生成的目标文件不兼容。

目前要构建项目的每个部分,我必须清理它们之间的源代码树。这很痛苦,当然不是我想要的。解决这个问题的最佳方法是什么?我没有能够付诸实践的想法是:

  1. 为项目的每个部分分隔构建目录
  2. 构建外部工具时,以某种方式在主应用程序源代码树中标记其目标文件(util.file1.o?)
  3. 我不太确定我是否有足够的时间和耐心来掌握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
    

    我经过广泛的谷歌浏览后发现了这一点。这似乎有效,但这部分看起来并不特别好(感觉有点像黑客):

    $(OBJECTS): | mkdir
        $(CXX) -c $(CXXFLAGS) -o $@ $(patsubst %.o,%.cpp,$(subst .util/,,$@))
    

    特别是我不太热衷于我之前从源创建对象列表并添加后缀的事实,只是在这里做反向。我似乎无法以任何其他方式工作。

2 个答案:

答案 0 :(得分:5)

CMake有add_definitionsremove_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(...)

答案 1 :(得分:2)

这可以通过SCons轻松完成。对于使用不同预处理器宏构建的对象,您肯定需要构建目录层次结构。在SCons术语中,创建这样的构建目录称为variant_dir。我建议使用以下SCons Hierarchical构建结构:

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/

为了能够使用不同的预处理器宏构建相同的源文件,您需要使用多个不同的环境构建相同的文件。这些环境可以在src / module SConscript脚本中设置,也可以从根SConstruct设置并传递下来。我更喜欢第二个选项,因为它会使src /模块SCons脚本模块化,并且不知道(不可知)预处理器宏。

这是根构建脚本,它创建不同的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)

这是main的构建脚本 应用程序/ 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)

这是模块库的构建脚本,它将被调用两次,每次都使用不同的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)
相关问题