从Bazel中调用CheckStyle的最佳方式是什么?

2022-08-21 00:00:00 bazel java checkstyle

我正在尝试添加对调用Checkstyle的支持,作为Bazel构建的一部分。我已经看到一些代码使用额外的操作来实现这一点,但我希望避免这种方法,并使其与纯Skylark代码一起工作。我设法使用以下(可怕的)通用规则让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依赖项,所以我很乐意引用这些依赖项,而不是check style-all jar。


解决方案

正如irc上所讨论的,这是我一直在使用的规则(在本文的末尾)。我有一个目录config/,其中包含我的CheckStyle配置、抑制和许可证文件,这里引用这些文件作为默认参数。在您的工作区中,您可以使用一个宏将所有的副手都拉进来:

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

如果您每个包都有一个构建文件,这可能是不必要的,但如果您正在转换一个大的Maven模块并在Bazel构建文件中保持类似的结构,这可能会成为一个问题。

工具/检查样式.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
"""

相关文章