使用变量dirs进行分层构建时的scons依赖问题

时间:2012-01-19 19:35:45

标签: scons

我是scons的新手,我遇到了scons dependancies的问题 在使用变体目录的层次结构构建中。

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

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

以下是需要完成的工作流程:

  1. moduleA执行shell脚本:conf2cc,输入:$ projRootDir / file.conf,output: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编译到其中 library libmoduleB.a
  5. 我完全有可能在这里犯了几件错事。例如,我知道 我没有在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.
    

3 个答案:

答案 0 :(得分:0)

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

如果我使用我需要构建的目标添加对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中的依赖性。

答案 1 :(得分:0)

我建议使用不正确的依赖项和文件名。

可能是variant_dir和moduleB的源文件有问题,你使用Command来生成#/ 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)

并尝试使用Command之类的源文件。而是文件名。它看起来更正确。 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])

答案 2 :(得分:0)

我的解决方案:

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)