【问题标题】:What is the best way to invoke Checkstyle from within Bazel?从 Bazel 中调用 Checkstyle 的最佳方式是什么?
【发布时间】:2018-08-10 23:54:21
【问题描述】:

我正在尝试添加对调用 Checkstyle 作为我们 Bazel 构建的一部分的支持。我已经看到一些代码使用 Extra Actions 来实现这一点,但我希望避免这种方法并让它与纯 Skylark 代码一起工作。我设法使用以下(糟糕的)genrule 来让 JVM 在一组源文件上执行 Checkstyle,但我意识到这非常糟糕:

native.genrule(
    name = name,
    srcs = srcs,
    outs = ["src_output.txt"],
    cmd = "$(JAVA) -Dconfig_loc=<full-config-loc-path> -classpath <path>/checkstyle-8.4-all.jar com.puppycrawl.tools.checkstyle.Main -c <config-file-path> -o $@ $(SRCS)",
    **kwargs
)

关于如何以正确的方式进行操作的任何建议?我已经在我们的 dependencies.bzl 文件中有所有必要的 JAR 依赖项,所以我很乐意参考这些而不是 checkstyle-all JAR。

【问题讨论】:

    标签: java checkstyle bazel


    【解决方案1】:

    正如在 IRC 上所讨论的,这是我一直在使用的规则(在本文末尾)。我有一个目录config/,其中包含我的 checkstyle 配置、抑制和许可证文件,这些文件在此处引用为默认参数。在您的 WORKSPACE 中,您可以使用宏拉入所有部门:

    load("//tools:checkstyle.bzl", "checkstyle_repositories")
    checkstyle_repositories()
    

    在您的构建文件中导入并使用以下规则:

    load("//tools:checkstyle.bzl", "checkstyle_test")
    
    filegroup(
        name = "java-srcs",
        srcs = glob(["src/main/java/**/*.java"]),
    )
    
    checkstyle_test(
        name = "check",
        srcs = [
            ":java-srcs",
        ],
    ) 
    

    然后你可以用bazel test //path/to/dir:check运行它。

    此规则确实有限制,它需要命令行上的参数,因此对于较大的模块,您需要拆分文件组以停止达到命令行长度限制,例如

    load("//tools:checkstyle.bzl", "checkstyle_test")
    
    filegroup(
        name = "java-foo-srcs",
        srcs = glob(["src/main/java/foo/**/*.java"]),
    )
    filegroup(
        name = "java-bar-srcs",
        srcs = glob(["src/main/java/bar/**/*.java"]),
    )
    
    checkstyle_test(
        name = "check-foo",
        srcs = [
            ":java-foo-srcs",
        ],
    ) 
    checkstyle_test(
        name = "check-bar",
        srcs = [
            ":java-bar-srcs",
        ],
    ) 
    test_suite(
        name = "check",
        tests = [
            ":check-bar",
            ":check-foo",
        ],
    )
    

    如果每个包都有一个 BUILD 文件,这可能是不必要的,但如果你正在转换一个大型 maven 模块并在你的 bazel 构建文件中保持类似的结构,这将是一个更大的问题。

    工具/checkstyle.bzl

    load("//tools/gerrit:maven_jar.bzl", "maven_jar")
    
    def checkstyle_repositories(
        omit = [],
        versions = {
          "antlr_antlr": "2.7.7",
          "org_antlr_antlr4_runtime": "4.5.1-1",
          "com_puppycrawl_tools_checkstyle": "8.2",
          "commons_beanutils_commons_beanutils": "1.9.3",
          "commons_cli_commons_cli": "1.4",
          "commons_collections_commons_collections": "3.2.2",
          "com_google_guava_guava23": "23.0",
          "org_slf4j_slf4j_api": "1.7.7",
          "org_slf4j_slf4j_jcl": "1.7.7",
        }
    ):
      if not "antlr_antlr" in omit:
        maven_jar(
            name = "antlr_antlr",
            attach_source = False,
            artifact = "antlr:antlr:" + versions["antlr_antlr"],
        )
      if not "org_antlr_antlr4_runtime" in omit:
        maven_jar(
            name = "org_antlr_antlr4_runtime",
            artifact = "org.antlr:antlr4-runtime:" + versions["org_antlr_antlr4_runtime"],
        )
      if not "com_puppycrawl_tools_checkstyle" in omit:
        maven_jar(
            name = "com_puppycrawl_tools_checkstyle",
            artifact = "com.puppycrawl.tools:checkstyle:" + versions["com_puppycrawl_tools_checkstyle"],
        )
      if not "commons_beanutils_commons_beanutils" in omit:
        maven_jar(
            name = "commons_beanutils_commons_beanutils",
            artifact = "commons-beanutils:commons-beanutils:" + versions["commons_beanutils_commons_beanutils"],
        )
      if not "commons_cli_commons_cli" in omit:
        maven_jar(
            name = "commons_cli_commons_cli",
            artifact = "commons-cli:commons-cli:" + versions["commons_cli_commons_cli"],
        )
      if not "commons_collections_commons_collections" in omit:
        maven_jar(
            name = "commons_collections_commons_collections",
            artifact = "commons-collections:commons-collections:" + versions["commons_collections_commons_collections"],
        )
      if not "com_google_guava_guava23" in omit:
        maven_jar(
            name = "com_google_guava_guava23",
            artifact = "com.google.guava:guava:" + versions["com_google_guava_guava23"],
        )
      if not "org_slf4j_slf4j_api" in omit:
        maven_jar(
            name = "org_slf4j_slf4j_api",
            artifact = "org.slf4j:slf4j-api:" + versions["org_slf4j_slf4j_api"],
        )
      if not "org_slf4j_slf4j_jcl" in omit:
        maven_jar(
            name = "org_slf4j_slf4j_jcl",
            artifact = "org.slf4j:jcl-over-slf4j:" + versions["org_slf4j_slf4j_jcl"],
        )
    
    
    
    def _checkstyle_test_impl(ctx):
        name = ctx.label.name
        srcs = ctx.files.srcs
        deps = ctx.files.deps
        config = ctx.file.config
        properties = ctx.file.properties
        suppressions = ctx.file.suppressions
        opts = ctx.attr.opts
        sopts = ctx.attr.string_opts
    
        classpath=""
        add=False
        for file in ctx.files._classpath:
            if add:
                classpath += ":"
            add=True
            classpath += file.path
        for file in ctx.files.deps:
            classpath += ":" + file.path
    
        args = ""
        inputs = []
        if config:
          args += " -c %s" % config.path
          inputs.append(config)
        if properties:
          args += " -p %s" % properties.path
          inputs.append(properties)
        if suppressions:
          inputs.append(suppressions)
    
        cmd = " ".join(
            ["java -cp %s com.puppycrawl.tools.checkstyle.Main" % classpath] +
            [args] +
            ["--%s" % x for x in opts] +
            ["--%s %s" % (k, sopts[k]) for k in sopts] +
            [x.path for x in srcs]
        )
    
        ctx.file_action(
            output = ctx.outputs.executable,
            content = cmd,
            executable = True,
        )
        files = [ctx.outputs.executable, ctx.file.license] + srcs + deps + ctx.files._classpath + inputs
        runfiles = ctx.runfiles(
            files = files,
            collect_data = True
        )
        return struct(
            files = depset(files),
            runfiles = runfiles,
        )
    
    checkstyle_test = rule(
        implementation = _checkstyle_test_impl,
        test = True,
        attrs = {
            "_classpath": attr.label_list(default=[
                Label("@com_puppycrawl_tools_checkstyle//jar"),
                Label("@commons_beanutils_commons_beanutils//jar"),
                Label("@commons_cli_commons_cli//jar"),
                Label("@commons_collections_commons_collections//jar"),
                Label("@org_slf4j_slf4j_api//jar"),
                Label("@org_slf4j_slf4j_jcl//jar"),
                Label("@antlr_antlr//jar"),
                Label("@org_antlr_antlr4_runtime//jar"),
                Label("@com_google_guava_guava//jar"),
            ]),
            "config": attr.label(allow_single_file=True, default = "//config:checkstyle"),
            "suppressions": attr.label(allow_single_file=True, default = "//config:suppressions"),
            "license": attr.label(allow_single_file=True, default = "//config:license"),
            "properties": attr.label(allow_single_file=True),
            "opts": attr.string_list(),
            "string_opts": attr.string_dict(),
            "srcs": attr.label_list(allow_files = True),
            "deps": attr.label_list(),
        },
    )
    """Run checkstyle
    
    Args:
      config: A checkstyle configuration file
      suppressions: A checkstyle suppressions file
      license: A license file that can be used with the checkstyle license
        target
      properties: A properties file to be used
      opts: Options to be passed on the command line that have no
        argument
      string_opts: Options to be passed on the command line that have an
        argument
      srcs: The files to check
    """
    

    【讨论】:

    • 虽然这仍然是我的构建文件中的大致内容,但如果命令行是使用an Args object 构建的,您可能可以使用参数文件来绕过命令行长度限制。
    • 有没有办法将它写入控制台而不是文件?
    • @austince 如果您在目标之后添加--test_output=errors,它将打印失败的输出。如果您添加--test_output=all,它将打印所有这些的输出。 docs.bazel.build/versions/master/…
    猜你喜欢
    • 2017-09-29
    • 2015-09-10
    • 1970-01-01
    • 2016-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-22
    • 2020-06-23
    相关资源
    最近更新 更多