设置SCons进行自动 lint 检查

10 投票
3 回答
2677 浏览
提问于 2025-04-16 10:36

我正在使用谷歌的 cpplint.py 来检查我项目中的源代码,确保它符合 谷歌C++风格指南 中的标准。我们使用SCons来构建项目,所以我想自动化这个过程,让SCons先读取我们所有的 .h 和 .cc 文件,然后再对它们运行 cpplint.py,只有通过检查的文件才会被编译。现在我遇到了一些问题:

  1. 在SCons中,我该如何在构建过程之前插入一个步骤?在通过检查之前,不应该编译任何文件。
  2. cpplint没有返回退出代码。我该如何在SCons中运行一个命令,并检查结果是否符合某个正则表达式?也就是说,我该如何获取输出的文本?
  3. 项目很大,无论解决方案是什么,当我在SCons中使用 -j 选项时,它应该能够并行运行。
  4. 我需要一个白名单,允许某些文件跳过检查。

3 个回答

1

你可以去我的GitHub上看看,有一对scons脚本,还有一个示例的源代码树。这个脚本使用了谷歌的cpplint.py工具。

https://github.com/xyzisinus/scons-tidbits

2

AddPreAction 看起来就是你要找的东西,来自手册的说明:

AddPreAction(target, action)
env.AddPreAction(target, action)
Arranges for the specified action to be performed before the specified target is built. T

另外,你可以查看这个链接 http://benno.id.au/blog/2006/08/27/filtergensplint,里面有一个例子。

2

一种实现这个功能的方法是对发射器函数进行“猴子补丁”,这个函数可以把C++代码转换成可链接的目标文件。这里有两个这样的发射器函数,一个用于静态对象,另一个用于共享对象。下面是一个你可以直接复制粘贴到SConstruct中的例子:

import sys
import SCons.Defaults
import SCons.Builder
OriginalShared = SCons.Defaults.SharedObjectEmitter
OriginalStatic = SCons.Defaults.StaticObjectEmitter

def DoLint(env, source):
    for s in source:
        env.Lint(s.srcnode().path + ".lint", s)

def SharedObjectEmitter(target, source, env):
    DoLint(env, source)
    return OriginalShared(target, source, env)

def StaticObjectEmitter(target, source, env):
    DoLint(env, source)
    return OriginalStatic(target, source, env)

SCons.Defaults.SharedObjectEmitter = SharedObjectEmitter
SCons.Defaults.StaticObjectEmitter = StaticObjectEmitter
linter = SCons.Builder.Builder(
    action=['$PYTHON $LINT $LINT_OPTIONS $SOURCE','date > $TARGET'],
    suffix='.lint',
    src_suffix='.cpp')

# actual build
env = Environment()
env.Append(BUILDERS={'Lint': linter})
env["PYTHON"] = sys.executable
env["LINT"] = "cpplint.py"
env["LINT_OPTIONS"] = ["--filter=-whitespace,+whitespace/tab", "--verbose=3"]
env.Program("test", Glob("*.cpp"))

其实这并没有什么特别复杂的地方。你只需要把LINT设置为你cpplint.py文件的路径,并为你的项目设置合适的LINT_OPTIONS。唯一有点麻烦的是,如果检查通过了,你需要使用命令行的date程序来创建一个TARGET文件。如果你想要跨平台的话,这部分就得改动了。

添加一个白名单现在只需要写一些普通的Python代码,像这样:

whitelist = """"
src/legacy_code.cpp
src/by_the_PHB.cpp
"""".split()

def DoLint(env, source):
    for s in source:
        src = s.srcnode().path
        if src not in whitelist:
            env.Lint( + ".lint", s)

看起来cpplint.py确实能输出正确的错误状态。当有错误时,它会返回1,否则返回0。所以这部分没有额外的工作需要做。如果lint检查失败,构建过程也会失败。

这个解决方案在使用-j时也能正常工作,但C++文件可能会编译,因为lint的假输出和目标文件之间没有隐式依赖关系。你可以在这里添加一个明确的env.Depends,强制“.lint”输出依赖于目标文件。现在这样做可能已经足够了,因为如果在所有C++编译后还有剩余的lint问题,构建本身就会失败(scons会返回一个非零的代码)。为了完整性,依赖代码在DoLint函数中可能是这样的:

def DoLint(env, source, target):
    for i in range(len(source)):
        s = source[i]
        out = env.Lint(s.srcnode().path + ".lint", s)
        env.Depends(target[i], out)

撰写回答