SCons:具有分层构建的单独调试/发布构建目录

发布于 2024-12-01 17:47:12 字数 1051 浏览 2 评论 0原文

我刚刚开始学习使用 SCons,预计它可以解决我的一些 make 问题。我正在创建一个源层次结构来了解 SCons 的基础知识。

让我们从这个文件夹结构开始:

  • test/foo: 包含 main.cpp, main.h
  • test/bar: 包含它自己的 main.cpp, main.h
  • test/common: 包含 foo 和 foo 都使用的 utils.cpp 和 utils.h bar
  • test/external/moo:一些外部库的源代码,包含'configure',它生成'Makefile'(不使用SCons),因此SCons需要在'configure'之后调用'make';我怀疑当使用构建目录时这部分可能会很棘手
  • test/build/debug: build dir for debug
  • test/build/release: build dir for release

这是我想做的:

  • 有两种类型的构建: debug/release,唯一的区别是 debug 将 -DDEBUG 指定为 g++

  • 使用构建目录,这样就不会.o 文件是在我的源代码树中创建的。我们将这些构建目录称为“构建/调试”和“构建/发布”

  • 能够在另一个不使用 SCons 的项目上调用 ./configure 和 make,然后将它生成的 libmoo.a 与我的项目链接起来

  • 使构建完全并行(scons -j9 对于8 核?)

  • 有一些独立于调试/发布的方式来指定要链接的库。像这样的东西:

    env.Program(target='foo', source=['foo/main.cpp', '#build/(DEBUG_OR_RELEASE)/lib/libsomething.a'])
    

执行上述操作的基本 SConstruct/SConscript 文件是什么样的?即使只是指向正确方向的指针也很棒!

提前致谢 :-)

I just started learning to use SCons, anticipating that it solves some of my issues with make. I'm creating a source hierarchy to understand the basics of SCons.

Let's start with this folder structure:

  • test/foo: contains main.cpp, main.h
  • test/bar: contains its own main.cpp, main.h
  • test/common: contains utils.cpp and utils.h used by both foo and bar
  • test/external/moo: the source to some external library, containing 'configure' which produces 'Makefile' (not using SCons), so SCons needs to invoke 'make' after 'configure'; I suspect this part might be tricky when build dirs are used
  • test/build/debug: build dir for debug
  • test/build/release: build dir for release

Here's what I'd like to do:

  • Have two types of builds: debug/release where the only difference is that debug specifies -DDEBUG to g++

  • Use build dirs so that no .o files are created in my source tree. Let's call these build dirs "build/debug" and "build/release"

  • Be able to invoke ./configure and make on another project that does not use SCons, followed by linking libmoo.a it produces with my project

  • Have the builds be perfectly parallel (scons -j9 for an 8-core?)

  • Have some debug/release-independent way of specifying libraries to link. Something like:

    env.Program(target='foo', source=['foo/main.cpp', '#build/(DEBUG_OR_RELEASE)/lib/libsomething.a'])
    

What would the very basic SConstruct/SConscript files to do the above look like? Even just pointers in the right directions would be great too!

Thanks in advance :-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

染柒℉ 2024-12-08 17:47:12

我这样做是为了构建多个平台(而不是调试/发布),但概念是相同的。基本思想是,项目根目录中需要 2 个文件 - 一个 SConstruct 来设置构建目录(或在 scons 中称为“变体目录”),然后是一个描述实际构建步骤的 SConscript。

在 SConstruct 文件中,您将指定变量目录及其相应的源目录:

SConscript(dirs='.',
           variant_dir=variant_dir,
           duplicate=False,
           exports="env")

现在您希望变量_dir 依赖于一个标志。您可以使用 AddOption 或 Variables 来执行此操作。下面是一个完整的顶级 SConstruct 的示例:

# build with `scons --debug-build` for debug.
AddOption(
    '--debug-build',
    action='store_true',
    help='debug build',
    default=False)

env = Environment()

if GetOption('debug_build'):
    env.ParseFlags('-DDEBUG')
    variant_dir = 'build/debug'
else:
    variant_dir = 'build/release'

SConscript(dirs='.',
           variant_dir=variant_dir,
           duplicate=False,
           exports="env")

AddOption 最容易使用,但是如果您使用变量,那么您可以在运行之间缓存结果,而不必每次都拼写出“scons --debug-build” 。

所有目录设置和关联的内容都在 SConstruct 中。现在 SConscript 文件非常简单,根本不需要担心构建目录。

Import('env')

env.Program(target='foo_prog', source=['foo/main.cpp', 'lib/libmoo.a'])
# foo_prog since foo already exists as the name of the directory...

这是我发现的设置不同构建目录而不会出现奇怪错误的最简单方法。它也非常灵活 - 您只需修改顶级脚本中的“env”即可添加不同的平台构建,而无需更改构建的实际内容。

您的问题中唯一有效的工具是直接从 SCons 编译 autoconf 样式项目的方法。最简单的方法可能是使用几个 Command() 调用,但 SCons 喜欢了解每个步骤的输入和输出,因此这可能会变得很麻烦。另外,您必须依赖具有正确 VPATH 设置的 autoconf 构建 - 如果您尝试在源代码树之外进行编译,某些项目将无法工作。无论如何,编译 autoconf 项目的方法如下:

import os
Import('env')

# get the path to the configure script from the "moo" source directory
conf = env.File('moo/configure').srcnode().abspath

# Create the "moo" build directory in the build dir
build_dir = env.Dir('.').path
moo_dir = os.path.join(build_dir, 'moo')
Mkdir(moo_dir)

# run configure from within the moo dir
env.Command('moo/Makefile', 'moo/Makefile.am',
    conf, chdir=moo_dir)
# run make in the moo dir
env.Command('moo/libmoo.a', 'moo/Makefile',
    'make', chdir=moo_dir)

env.Program(target='foo_prog', source=['foo/main.cpp', 'moo/libmoo.a'])

当当前工作目录位于构建层次结构中的某个位置时,从源目录运行配置步骤是很尴尬的。 make 步骤不太混乱,但仍然需要了解当前的构建目录。由于您指定“libmoo.a”作为 make 步骤的输出,并将 libmoo.a 作为程序的输入,因此所有依赖项都可以正常工作,因此并行构建可以正常工作。仅当您过多地捏造依赖项时,并行构建才会崩溃。

I do this for builds for multiple platforms (rather than debug/release) but the concept's the same. The basic idea is that you need 2 files in the project root - a SConstruct to set up the build directories (or "variant directories" as they are known in scons), then a SConscript that describes the actual build steps.

In the SConstruct file you'd specify the variant directory and its corresponding source directory:

SConscript(dirs='.',
           variant_dir=variant_dir,
           duplicate=False,
           exports="env")

Now you want variant_dir to depend on a flag. You'd use AddOption or Variables to do this. Here's one example of a complete top-level SConstruct to do that:

# build with `scons --debug-build` for debug.
AddOption(
    '--debug-build',
    action='store_true',
    help='debug build',
    default=False)

env = Environment()

if GetOption('debug_build'):
    env.ParseFlags('-DDEBUG')
    variant_dir = 'build/debug'
else:
    variant_dir = 'build/release'

SConscript(dirs='.',
           variant_dir=variant_dir,
           duplicate=False,
           exports="env")

AddOption is easiest to use, but if you use Variables then you can cache the result between runs, rather than having to spell out "scons --debug-build" each time.

All the directory setup and associated cruft is in the SConstruct. Now the SConscript file is quite simple and doesn't need to worry about build directories at all.

Import('env')

env.Program(target='foo_prog', source=['foo/main.cpp', 'lib/libmoo.a'])
# foo_prog since foo already exists as the name of the directory...

This is about the simplest way I've found to set up different build directories without getting weird errors. It's also pretty flexible - you can add different platform builds just by modifying the "env" in the top-level script without having to alter the actual meat of the build.

The only spanner in the works in your question is the way to compile autoconf-style projects directly from SCons. The easiest way is probably with a couple of Command() calls, but SCons likes to know about the inputs and outputs of each step, so this can get hacky. Also, you have to rely on the autoconf build having a correct VPATH setup - some projects don't work if you try and compile outside the source tree. Anyway, a way to compile autoconf projects would be something like this:

import os
Import('env')

# get the path to the configure script from the "moo" source directory
conf = env.File('moo/configure').srcnode().abspath

# Create the "moo" build directory in the build dir
build_dir = env.Dir('.').path
moo_dir = os.path.join(build_dir, 'moo')
Mkdir(moo_dir)

# run configure from within the moo dir
env.Command('moo/Makefile', 'moo/Makefile.am',
    conf, chdir=moo_dir)
# run make in the moo dir
env.Command('moo/libmoo.a', 'moo/Makefile',
    'make', chdir=moo_dir)

env.Program(target='foo_prog', source=['foo/main.cpp', 'moo/libmoo.a'])

Running the configure step from the source directory while the current working directory is somewhere in the build hierarchy is awkward. The make step is less messy, but still needs to know about the current build directory. Since you specify "libmoo.a" as an output of the make step and libmoo.a as an input to the program, all the dependencies Just Work, so a parallel build works fine. Parallel builds only break down when you fudge dependencies too much.

季末如歌 2024-12-08 17:47:12

我知道这是一个老问题,我只是想添加一个替代方案:

  • 能够知道 sconscript 文件中的当前变体(不仅在父级中),
  • 并且能够在单个 scons 命令中构建多个变

sconstruct 文件(父级),我们定义一个名为 variantsListVariable ,其中包含我们允许的变体列表(例如 [ '发布', '调试'])。

然后,为了能够知道 sconscript 文件中的当前变体,我们只需循环定义的选项并将其导出到 sconscript 中。

我使用 genv 作为变量名来表示全局环境:

# sconstruct
opts = Variables()
opts.AddVariables(
    ListVariable('variants', 'list of variants to build', 'all', names = ['debug','release']),
)

genv = Environment( options = opts )

for variant in genv['variants']:
    SConscript('sconscript', exports=['genv', 'variant'], variant_dir='#build/'+variant, duplicate=False)

在 sconscript 文件中,我们克隆 genv 我们可以使用 variant 变量在本地环境 env 中进行设置:

# sconscript
Import('*')
import os.path

env = genv.Clone()    

if variant == 'debug':
    env.Append( CPPFLAGS = ['/Zi'])

src     = 'src/hello.cpp'
app,ext = os.path.splitext(os.path.basename(src))

obj = env.Object ('obj/'+app, src)
bin = env.Program('bin/'+app, obj)

使用 ListVariable 允许我们调用

scons variants=release

scons variants=debug

scons variants=all

最后一个命令(和默认命令)构建所有变种。

I know this is an old question, I just want to add an alternative to:

  • be able to know the current variant in the sconscript file (not only in the parent)
  • and to be able to build multiple variants in a single scons command

In the sconstruct file (the parent), we define a ListVariable named variants with the list of the variants that we allow (eg. ['release', 'debug']).

Then to be able to know the current variant in the sconscript file, we just loop option we have defined and export it into the sconscript.

I use genv as variable name to notate global environment:

# sconstruct
opts = Variables()
opts.AddVariables(
    ListVariable('variants', 'list of variants to build', 'all', names = ['debug','release']),
)

genv = Environment( options = opts )

for variant in genv['variants']:
    SConscript('sconscript', exports=['genv', 'variant'], variant_dir='#build/'+variant, duplicate=False)

In the sconscript file we Clone de genv and we can use the variant variable to do our setup in the local environment env:

# sconscript
Import('*')
import os.path

env = genv.Clone()    

if variant == 'debug':
    env.Append( CPPFLAGS = ['/Zi'])

src     = 'src/hello.cpp'
app,ext = os.path.splitext(os.path.basename(src))

obj = env.Object ('obj/'+app, src)
bin = env.Program('bin/'+app, obj)

Using a ListVariable allows us to call

scons variants=release

or

scons variants=debug

or

scons variants=all

This last command (and the default command) builds all the variants.

肥爪爪 2024-12-08 17:47:12

SCons Wiki 中有一个定义多种构建模式(“调试”、“发布”)的好解决方案:

http: //www.scons.org/wiki/SconstructMultiple

这就是 richq SConstruct 文件的样子:

#get the mode flag from the command line
#default to 'release' if the user didn't specify
mymode = ARGUMENTS.get('mode', 'release')

#check if the user has been naughty: only 'debug' or 'release' allowed
if not (mymode in ['debug', 'release']):
    print "Error: expected 'debug' or 'release', found: " + mymode
    Exit(1)

#tell the user what we're doing
print '**** Compiling in ' + mymode + ' mode...'

env = Environment()

if mode == 'debug':
    env.Append(CPPDEFINES = ['DEBUG'])
    variant_dir = 'build/debug'
else:
    variant_dir = 'build/release'

SConscript(dirs = '.', variant_dir = variant_dir, duplicate = False, exports = "env")

然后调用 scons mode=release (或者只是 scons因为发布版是默认模式),或 scons mode=debug

There's a good solution to define multiple build modes ('debug', 'release') in the SCons Wiki:

http://www.scons.org/wiki/SconstructMultiple

That's how the richq SConstruct file would look like:

#get the mode flag from the command line
#default to 'release' if the user didn't specify
mymode = ARGUMENTS.get('mode', 'release')

#check if the user has been naughty: only 'debug' or 'release' allowed
if not (mymode in ['debug', 'release']):
    print "Error: expected 'debug' or 'release', found: " + mymode
    Exit(1)

#tell the user what we're doing
print '**** Compiling in ' + mymode + ' mode...'

env = Environment()

if mode == 'debug':
    env.Append(CPPDEFINES = ['DEBUG'])
    variant_dir = 'build/debug'
else:
    variant_dir = 'build/release'

SConscript(dirs = '.', variant_dir = variant_dir, duplicate = False, exports = "env")

You then call scons mode=release (or just scons as the release is the default mode), or scons mode=debug.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文