在Bazel内调用Checkstyle的最佳方法是什么?

时间:2018-03-02 17:51:00

标签: java checkstyle bazel

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

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。

1 个答案:

答案 0 :(得分:2)

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

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
"""