具有变体目录的分层构建中的 scons 依赖性问题

发布于 2024-12-28 02:03:54 字数 3306 浏览 1 评论 0原文

我是 scons 新手,并且遇到了 scons 依赖问题 在具有变体目录的分层构建中。

我能够在一个简化的环境中重现该问题,其中包括 SConscript 目录下的 2 个子目录(moduleA 和 moduleB)如下:

.
|-- SConstruct
|-- file.conf
|-- moduleA
|   |-- SConscript
|   `-- conf2cc
`-- moduleB
    |-- SConscript
    `-- fileB.cc

以下是需要完成的流程:

  1. moduleA 执行 shell 脚本:conf2cc,输入:$projRootDir/file.conf,输出:moduleA/$variantDir/source .cc
  2. moduleA 编译 source.cc 并创建 moduleA/$variantDir/libmoduleA.a
  3. moduleB 需要将 moduleA/$variantDir/source.cc 复制到moduleB/source.cc
  4. moduleB 需要将 moduleB/source.cc 和 moduleB/fileB.cc 编译为其 库 libmoduleB.a

我完全有可能在这里做错了几件事。例如,我知道 我没有在 moduleA Command() 中使用 $TARGET/$SOURCE,但这是故意的,因为 脚本需要绝对路径名,并且 scons 不会删除前导 '#'

我遇到的问题是 moduleB 中的 Command() 构建器(上面的步骤 3)永远不会执行。

以下是 SConstruct 和 SConscript 文件:

Sconstruct

import os

env = Environment()
env['variantDir'] = 'linux'  # for this example, just make variantDir = linux
modules = ['moduleA', 'moduleB']

for dir in modules:
    SConscript(
        os.path.join(dir, 'SConscript'),
        variant_dir = os.path.join(dir, env['variantDir']),
        exports = ['env'],
        duplicate = 0)

moduleA/Sconscript

import os

Import('env')

scriptInput   = '#file.conf'
sourceFile    = os.path.join('#moduleA', env['variantDir'],  'source.cc')
conf2ccScript = File('#moduleA/conf2cc').abspath

# The script needs abspaths for input and output, not the scons '#' prepended
# the script syntax is: script <inputFile> <outputFile>
cmd = '%s %s %s' % (conf2ccScript, File(scriptInput).abspath, File(sourceFile).abspath)

# Generate source.cc file based on file.conf
conf2ccNode = env.Command(target = sourceFile,
                          source = scriptInput,
                          action = cmd)

libNode = env.Library(target = 'moduleA', source = sourceFile)
env.Depends(libNode, conf2ccNode)

moduleB/Sconscript

import os

Import('env')

sourceFiles = ['fileB.cc', 'source.cc']

# Get the source.cc file
externalSourceFile  = os.path.join('#moduleA', env['variantDir'], 'source.cc')
sourceTarget        = os.path.join('#moduleB', 'source.cc')

cmdNode = env.Command(target = sourceTarget,
                      source = externalSourceFile,
                      action = Copy('$TARGET', '$SOURCE'))

libNode = env.Library(target = 'moduleB', source = sourceFiles)
env.Depends(libNode, cmdNode)

这是我执行 scons 时的输出:

任何帮助将不胜感激!

布雷迪

notroot@ubuntu:~/projects/sconsTest/sconsTestHierDeps$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
/home/notroot/projects/sconsTest/sconsTestHierDeps/moduleA/conf2cc /home/notroot/projects/sconsTest/sconsTestHierDeps/file.conf /home/notroot/projects/sconsTest/sconsTestHierDeps/moduleA/linux/source.cc
g++ -o moduleA/linux/source.o -c moduleA/linux/source.cc
ar rc moduleA/linux/libmoduleA.a moduleA/linux/source.o
ranlib moduleA/linux/libmoduleA.a
g++ -o moduleB/linux/fileB.o -c moduleB/fileB.cc
scons: *** [moduleB/linux/source.o] Source `moduleB/source.cc' not found, needed by target `moduleB/linux/source.o'.
scons: building terminated because of errors.

Im new to scons and am having problems with scons dependancies
in a hierarchichal build with a variant directory.

Im able to reproduce the problem in a reduced environment that consists of
2 subdirs under the SConscript directory (moduleA and moduleB) as follows:

.
|-- SConstruct
|-- file.conf
|-- moduleA
|   |-- SConscript
|   `-- conf2cc
`-- moduleB
    |-- SConscript
    `-- fileB.cc

Here is the flow of what needs to be done:

  1. moduleA executes a shell script: conf2cc, input: $projRootDir/file.conf, output: moduleA/$variantDir/source.cc
  2. moduleA compiles source.cc and creates moduleA/$variantDir/libmoduleA.a
  3. moduleB needs to copy moduleA/$variantDir/source.cc to moduleB/source.cc
  4. moduleB needs to compile moduleB/source.cc and moduleB/fileB.cc into its
    library libmoduleB.a

Its entirely possible that Im doing several things wrong here. For example, I know
Im not using $TARGET/$SOURCE in moduleA Command(), but thats on purpose, since the
script needs the absolute path names, and scons doesnt remove the leading '#'

The problem I have is the Command() builder in moduleB (step 3 above) never executes.

Here are the SConstruct and SConscript files:

Sconstruct

import os

env = Environment()
env['variantDir'] = 'linux'  # for this example, just make variantDir = linux
modules = ['moduleA', 'moduleB']

for dir in modules:
    SConscript(
        os.path.join(dir, 'SConscript'),
        variant_dir = os.path.join(dir, env['variantDir']),
        exports = ['env'],
        duplicate = 0)

moduleA/Sconscript

import os

Import('env')

scriptInput   = '#file.conf'
sourceFile    = os.path.join('#moduleA', env['variantDir'],  'source.cc')
conf2ccScript = File('#moduleA/conf2cc').abspath

# The script needs abspaths for input and output, not the scons '#' prepended
# the script syntax is: script <inputFile> <outputFile>
cmd = '%s %s %s' % (conf2ccScript, File(scriptInput).abspath, File(sourceFile).abspath)

# Generate source.cc file based on file.conf
conf2ccNode = env.Command(target = sourceFile,
                          source = scriptInput,
                          action = cmd)

libNode = env.Library(target = 'moduleA', source = sourceFile)
env.Depends(libNode, conf2ccNode)

moduleB/Sconscript

import os

Import('env')

sourceFiles = ['fileB.cc', 'source.cc']

# Get the source.cc file
externalSourceFile  = os.path.join('#moduleA', env['variantDir'], 'source.cc')
sourceTarget        = os.path.join('#moduleB', 'source.cc')

cmdNode = env.Command(target = sourceTarget,
                      source = externalSourceFile,
                      action = Copy('$TARGET', '$SOURCE'))

libNode = env.Library(target = 'moduleB', source = sourceFiles)
env.Depends(libNode, cmdNode)

Here is the output when I execute scons:

Any help would be greatly appreciated!

Brady

notroot@ubuntu:~/projects/sconsTest/sconsTestHierDeps$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
/home/notroot/projects/sconsTest/sconsTestHierDeps/moduleA/conf2cc /home/notroot/projects/sconsTest/sconsTestHierDeps/file.conf /home/notroot/projects/sconsTest/sconsTestHierDeps/moduleA/linux/source.cc
g++ -o moduleA/linux/source.o -c moduleA/linux/source.cc
ar rc moduleA/linux/libmoduleA.a moduleA/linux/source.o
ranlib moduleA/linux/libmoduleA.a
g++ -o moduleB/linux/fileB.o -c moduleB/fileB.cc
scons: *** [moduleB/linux/source.o] Source `moduleB/source.cc' not found, needed by target `moduleB/linux/source.o'.
scons: building terminated because of errors.

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

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

发布评论

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

评论(3

任性一次 2025-01-04 02:03:54

我建议问题在于您使用的内容和文件名不正确的依赖性。

moduleB 的variant_dir 和源文件可能有问题,您使用命令生成#/moduleB/source.cc,但在sourceFiles 中您有'source.cc'。

因此,帮助您的方法之一可能是正确的 moduleB SConscript :

# Get the source.cc file
externalSourceFile  = os.path.join('#moduleA', env['variantDir'], 'source.cc')
sourceTarget        = os.path.join('#moduleB', 'source.cc')

sourceFiles = ['fileB.cc', sourceTarget]

cmdNode = env.Command(target = sourceTarget,
                      source = externalSourceFile,
                      action = Copy('$TARGET', '$SOURCE'))

libNode = env.Library(target = 'moduleB', source = sourceFiles)

并尝试使用像源文件一样的命令。而不是文件名。看起来更正确。
模块A:

conf2ccNode = env.Command(target = sourceFile,
                          source = scriptInput,
                          action = cmd)
libNode = env.Library(target = 'moduleA', source = conf2ccNode)

模块B:

cmdNode = env.Command(target = sourceTarget,
                      source = externalSourceFile,
                      action = Copy('$TARGET', '$SOURCE'))

libNode = env.Library(target = 'moduleB', source = ['fileB.cc', cmdNode])

I suggest problem in incorrect dependency what you use and filenames.

May be problem in variant_dir and source files for moduleB, you use Command to generate #/moduleB/source.cc, but in sourceFiles you have 'source.cc'.

So, one of ways to help you may be correct moduleB SConscript :

# Get the source.cc file
externalSourceFile  = os.path.join('#moduleA', env['variantDir'], 'source.cc')
sourceTarget        = os.path.join('#moduleB', 'source.cc')

sourceFiles = ['fileB.cc', sourceTarget]

cmdNode = env.Command(target = sourceTarget,
                      source = externalSourceFile,
                      action = Copy('$TARGET', '$SOURCE'))

libNode = env.Library(target = 'moduleB', source = sourceFiles)

And try to use Command like source file. instead filename. It's looks more correct.
moduleA :

conf2ccNode = env.Command(target = sourceFile,
                          source = scriptInput,
                          action = cmd)
libNode = env.Library(target = 'moduleA', source = conf2ccNode)

moduleB:

cmdNode = env.Command(target = sourceTarget,
                      source = externalSourceFile,
                      action = Copy('$TARGET', '$SOURCE'))

libNode = env.Library(target = 'moduleB', source = ['fileB.cc', cmdNode])
酸甜透明夹心 2025-01-04 02:03:54

我找到了问题的解决方案,但我不太明白它为什么有效。

如果我添加对 env.Default() 的调用以及我需要构建的目标,那么它就可以工作。因此,SConscript 文件将如下所示:

moduleA/Sconscript

import os

Import('env')

scriptInput   = '#file.conf'
sourceFile    = os.path.join('#moduleA', env['variantDir'],  'source.cc')
conf2ccScript = File('#moduleA/conf2cc').abspath

# The script needs abspaths for input and output, not the scons '#' prepended
# the script syntax is: script <inputFile> <outputFile>
cmd = '%s %s %s' % (conf2ccScript, File(scriptInput).abspath, File(sourceFile).abspath)

# Generate source.cc file based on file.conf
conf2ccNode = env.Command(target = sourceFile,
                          source = scriptInput,
                          action = cmd)

libNode = env.Library(target = 'moduleA', source = sourceFile)
env.Depends(libNode, conf2ccNode)
env.Default([conf2ccNode, libNode])

moduleB/Sconscript

import os

Import('env')

sourceFiles = ['fileB.cc', 'source.cc']

# Get the source.cc file
externalSourceFile  = os.path.join('#moduleA', env['variantDir'], 'source.cc')
sourceTarget        = os.path.join('#moduleB', 'source.cc')

cmdNode = env.Command(target = sourceTarget,
                      source = externalSourceFile,
                      action = Copy('$TARGET', '$SOURCE'))

libNode = env.Library(target = 'moduleB', source = sourceFiles)
env.Depends(libNode, cmdNode)
env.Default(cmdNode, libNode)

那么这就引出了一个问题:如果我不指定 Default() 目标并且有多个目标,那么 scons 如何知道要构建哪一个?

另外,我仍然不明白为什么 scons 不能解决 libNode 对 cmdNode 的 moduleB/SConscript 的依赖关系。

I found a solution to the problem, but I dont really understand why it works.

If I add a call to env.Default() with the targets I need built, then it works. So the SConscript files would then look like this:

moduleA/Sconscript

import os

Import('env')

scriptInput   = '#file.conf'
sourceFile    = os.path.join('#moduleA', env['variantDir'],  'source.cc')
conf2ccScript = File('#moduleA/conf2cc').abspath

# The script needs abspaths for input and output, not the scons '#' prepended
# the script syntax is: script <inputFile> <outputFile>
cmd = '%s %s %s' % (conf2ccScript, File(scriptInput).abspath, File(sourceFile).abspath)

# Generate source.cc file based on file.conf
conf2ccNode = env.Command(target = sourceFile,
                          source = scriptInput,
                          action = cmd)

libNode = env.Library(target = 'moduleA', source = sourceFile)
env.Depends(libNode, conf2ccNode)
env.Default([conf2ccNode, libNode])

moduleB/Sconscript

import os

Import('env')

sourceFiles = ['fileB.cc', 'source.cc']

# Get the source.cc file
externalSourceFile  = os.path.join('#moduleA', env['variantDir'], 'source.cc')
sourceTarget        = os.path.join('#moduleB', 'source.cc')

cmdNode = env.Command(target = sourceTarget,
                      source = externalSourceFile,
                      action = Copy('$TARGET', '$SOURCE'))

libNode = env.Library(target = 'moduleB', source = sourceFiles)
env.Depends(libNode, cmdNode)
env.Default(cmdNode, libNode)

So that leads to the question: If I dont specify Default() targets and there is more than one target, how does scons know which one to build?

Also, I still dont understand why scons doesnt resolve the dependancy in moduleB/SConscript that libNode has on the cmdNode.

﹎☆浅夏丿初晴 2025-01-04 02:03:54

我的解决方案:

def CreateLibrary(env, name, sources, shared=True):

    def GetObjectFile(sourceFileName):

        def GetFileNameWithoutExtension(path):
            return os.path.splitext(os.path.basename(path))[0]

        def IsFileNameExist(newFileName):
            return fileNames.count(newFileName) > 0

        sourceAbsPath = os.path.abspath(sourceFileName)
        fileNameWithoutExtension = GetFileNameWithoutExtension(sourceAbsPath)
        destFileName = fileNameWithoutExtension
        attemptNumber = 0
        while IsFileNameExist(destFileName):
            attemptNumber += 1
            destFileName = fileNameWithoutExtension + str(attemptNumber)
        fileNames.append(destFileName)
        destFilePath = os.path.join(compilationDirRoot, destFileName)
        if shared:
            return env.SharedObject(destFilePath, sourceAbsPath)
        else:
            return env.StaticObject(destFilePath, sourceAbsPath)

    objFiles = []
    fileNames = []
    compilationDirRoot = Dir('.').abspath
    for src in sources:
        if isinstance(src,str):
            objFiles.append(GetObjectFile(src))
        elif isinstance(src, SCons.Node.FS.File):
            objFiles.append(GetObjectFile(SCons.Node.FS.File.rstr(src)))
        else:
            for f in src:
                objFiles.append(GetObjectFile(str(f)))
    if shared:
        return env.SharedLibrary(name, objFiles, no_import_lib=True)
    else:
        return env.StaticLibrary(name, objFiles)

使用示例:

theora = CreateLibrary(env, 'theora', sources)

My solution:

def CreateLibrary(env, name, sources, shared=True):

    def GetObjectFile(sourceFileName):

        def GetFileNameWithoutExtension(path):
            return os.path.splitext(os.path.basename(path))[0]

        def IsFileNameExist(newFileName):
            return fileNames.count(newFileName) > 0

        sourceAbsPath = os.path.abspath(sourceFileName)
        fileNameWithoutExtension = GetFileNameWithoutExtension(sourceAbsPath)
        destFileName = fileNameWithoutExtension
        attemptNumber = 0
        while IsFileNameExist(destFileName):
            attemptNumber += 1
            destFileName = fileNameWithoutExtension + str(attemptNumber)
        fileNames.append(destFileName)
        destFilePath = os.path.join(compilationDirRoot, destFileName)
        if shared:
            return env.SharedObject(destFilePath, sourceAbsPath)
        else:
            return env.StaticObject(destFilePath, sourceAbsPath)

    objFiles = []
    fileNames = []
    compilationDirRoot = Dir('.').abspath
    for src in sources:
        if isinstance(src,str):
            objFiles.append(GetObjectFile(src))
        elif isinstance(src, SCons.Node.FS.File):
            objFiles.append(GetObjectFile(SCons.Node.FS.File.rstr(src)))
        else:
            for f in src:
                objFiles.append(GetObjectFile(str(f)))
    if shared:
        return env.SharedLibrary(name, objFiles, no_import_lib=True)
    else:
        return env.StaticLibrary(name, objFiles)

Example of use:

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