Как лучше всего вызвать Checkstyle из Bazel?

Я пытаюсь добавить поддержку вызова 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
)

Любые предложения о том, как сделать это правильно? У меня уже есть все обязательные JAR-зависимости в нашем файле dependencies.bzl, поэтому я был бы рад сослаться на них вместо JAR-файла checkstyle-all.


person Matt Passell    schedule 02.03.2018    source источник


Ответы (1)


Как обсуждалось в 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",
    ],
)

Если у вас есть файл 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
"""
person Brent Douglas    schedule 16.03.2018
comment
Хотя это все еще примерно то, что находится в моих файлах сборки, если вместо этого командная строка была построена с использованием объект Args, возможно, вы могли бы использовать файл параметров, чтобы обойти ограничения длины командной строки. - person Matt Passell; 16.07.2019
comment
Есть ли способ записать это в консоль вместо файла? - person austin_ce; 21.08.2019
comment
@austince Если вы добавите --test_output=errors после цели, она напечатает вывод неудачных. Если вы добавите --test_output=all, он выведет вывод из всех них. docs.bazel.build/versions/master/< /а> - person Brent Douglas; 28.08.2019