从 Bazel 中调用 Checkstyle 的最佳方式是什么?
What is the best way to invoke Checkstyle from within Bazel?
我正在尝试添加对调用 Checkstyle 的支持,作为我们 Bazel 构建的一部分。我已经看到一些代码使用 Extra Actions 来实现它,但我希望避免这种方法并让它与纯 Skylark 代码一起工作。我设法使用以下(糟糕的)规则让 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。
正如在 IRC 上讨论的那样,这是我一直在使用的规则(在此 post 的末尾)。我有一个目录 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 构建文件中保持类似的结构,则问题更大。
tools/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
"""
我正在尝试添加对调用 Checkstyle 的支持,作为我们 Bazel 构建的一部分。我已经看到一些代码使用 Extra Actions 来实现它,但我希望避免这种方法并让它与纯 Skylark 代码一起工作。我设法使用以下(糟糕的)规则让 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。
正如在 IRC 上讨论的那样,这是我一直在使用的规则(在此 post 的末尾)。我有一个目录 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 构建文件中保持类似的结构,则问题更大。
tools/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
"""