"""Linting"""
load(
"@io_tweag_rules_haskell//haskell:providers.bzl",
"HaskellInfo",
"HaskellLibraryInfo",
"HaskellLintInfo",
)
load(":private/context.bzl", "haskell_context", "render_env")
load(":private/packages.bzl", "expose_packages", "pkg_info_to_compile_flags")
load(
":private/path_utils.bzl",
"target_unique_name",
)
load(":providers.bzl", "get_libs_for_ghc_linker")
load(":private/set.bzl", "set")
def _collect_lint_logs(deps):
lint_logs = set.empty()
for dep in deps:
if HaskellLintInfo in dep:
set.mutable_union(lint_logs, dep[HaskellLintInfo].outputs)
return lint_logs
def _haskell_lint_rule_impl(ctx):
return [DefaultInfo(
files = set.to_depset(_collect_lint_logs(ctx.attr.deps)),
)]
def _haskell_lint_aspect_impl(target, ctx):
hs = haskell_context(ctx, ctx.rule.attr)
if HaskellInfo not in target:
return []
hs_info = target[HaskellInfo]
lib_info = target[HaskellLibraryInfo] if HaskellLibraryInfo in target else None
args = ctx.actions.args()
args.add_all([
"-O0",
"-v0",
"-fno-code",
"-Wall",
"-Werror",
"-Wcompat",
"-Wincomplete-record-updates",
"-Wincomplete-uni-patterns",
"-Wredundant-constraints",
"-Wnoncanonical-monad-instances",
"--make",
])
args.add_all(pkg_info_to_compile_flags(expose_packages(
hs_info,
lib_info,
use_direct = False,
use_my_pkg_id = None,
custom_package_databases = None,
version = ctx.rule.attr.version,
)))
sources = set.to_list(hs_info.source_files)
args.add_all(sources)
lint_log = ctx.actions.declare_file(
target_unique_name(hs, "lint-log"),
)
# Transitive library dependencies for runtime.
(library_deps, ld_library_deps, _ghc_env) = get_libs_for_ghc_linker(
hs,
hs_info.transitive_cc_dependencies,
)
ctx.actions.run_shell(
inputs = depset(transitive = [
depset(sources),
set.to_depset(hs_info.package_databases),
set.to_depset(hs_info.interface_dirs),
set.to_depset(hs_info.dynamic_libraries),
depset(library_deps),
depset(ld_library_deps),
depset([hs.tools.ghc]),
]),
outputs = [lint_log],
mnemonic = "HaskellLint",
progress_message = "HaskellLint {}".format(ctx.label),
command = """
{env}
{ghc} "$@" > {output} 2>&1 || rc=$? && cat {output} && exit $rc
""".format(
ghc = hs.tools.ghc.path,
output = lint_log.path,
# XXX Workaround
# https://github.com/bazelbuild/bazel/issues/5980.
env = render_env(hs.env),
),
arguments = [args],
use_default_shell_env = True,
)
lint_info = HaskellLintInfo(outputs = set.singleton(lint_log))
output_files = OutputGroupInfo(default = [lint_log])
return [lint_info, output_files]
haskell_lint_aspect = aspect(
_haskell_lint_aspect_impl,
attr_aspects = ["deps"],
toolchains = ["@io_tweag_rules_haskell//haskell:toolchain"],
)
haskell_lint = rule(
_haskell_lint_rule_impl,
attrs = {
"deps": attr.label_list(
aspects = [haskell_lint_aspect],
doc = "List of Haskell targets to lint.",
),
},
toolchains = ["@io_tweag_rules_haskell//haskell:toolchain"],
)
"""Check source code of targets in `deps` using a restrictive set of GHC
flags.
The following flags will be used:
* `-Wall`
* `-Werror`
* `-Wcompat`
* `-Wincomplete-record-updates`
* `-Wincomplete-uni-patterns`
* `-Wredundant-constraints`
"""