【问题标题】:scons dependency problems in a hierarchical build with variant dirs具有变体目录的分层构建中的 scons 依赖问题
【发布时间】:2012-02-14 11:20:54
【问题描述】:

我是 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 文件:

构造

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)

模块A/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)

模块B/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.

【问题讨论】:

    标签: scons


    【解决方案1】:

    我建议您使用不正确的依赖项和文件名存在问题。

    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])
    

    【讨论】:

    • 太棒了!这按预期工作。我喜欢 SCons 找出隐含的依赖关系,而不是用 Depends() 函数显式列出它们。非常感谢!
    • 有一点需要注意:moduleB/source.cc 的目标文件放在moduleB 中,而不是放在moduleB 的variant_dir 中,而fileB.o 确实放在variant_dir 中。这是为什么?如何将 source.o 放在 variant_dir 中?
    • 我通过指定moduleB source.cc 文件如下:sourceTarget = os.path.join('#moduleB', env['variantDir'], 'source.cc') 这样现在source.cc 和source.o 文件被放置在variant_dir 中。
    【解决方案2】:

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

    如果我使用我需要构建的目标添加对 env.Default() 的调用,那么它可以工作。所以 SConscript 文件看起来像这样:

    模块A/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])
    

    模块B/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 中的依赖关系。

    【讨论】:

      【解决方案3】:

      我的解决方案:

      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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-05
        • 2011-05-01
        • 2011-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多