3

尽管在很长一段时间内进行了大量的谷歌搜索和实验,但我对makeautotools的了解(我还没有用于这个项目)充其量只是初级知识。我有一个像下面这样的源层次结构,我试图找到尽可能无缝地构建的方法。

该应用程序由一个主应用程序组成,其源代码位于 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。所以编译主应用程序生成的目标文件和各种实用程序不兼容。

目前要构建项目的每个部分,我必须清理其间的源代码树。这很痛苦,而且肯定不是我最终想要的。解决这个问题的最佳方法是什么?我曾经有但无法付诸实践的想法是:

  1. 项目每个部分的单独构建目录
  2. 在构建外部工具时,以某种方式在主应用程序源树中标记它们的目标文件(util.file1.o?)

我不太确定我是否有时间和耐心来掌握 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

我是在广泛搜索 SO 浏览后发现的。这似乎可行,但这部分看起来并不是特别好(感觉有点像黑客):

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

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

4

2 回答 2

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(...)
于 2013-02-07T08:21:00.340 回答
3

这可以通过 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/

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

这是根构建脚本,它创建不同的环境并编排子目录构建脚本:

应用程序/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)

这是主 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)

这是模块库的构建脚本,它将被调用两次,每次使用不同的 env app/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)
于 2013-02-07T10:02:55.237 回答