我是scons的新手,我遇到了scons dependancies的问题 在使用变体目录的层次结构构建中。
我能够在包含的简化环境中重现问题 SConscript目录下的2个子目录(moduleA和moduleB)如下:
.
|-- SConstruct
|-- file.conf
|-- moduleA
| |-- SConscript
| `-- conf2cc
`-- moduleB
|-- SConscript
`-- fileB.cc
以下是需要完成的工作流程:
我完全有可能在这里犯了几件错事。例如,我知道 我没有在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.
答案 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)