diff options
Diffstat (limited to 'third_party/bazel/rules_haskell/haskell/lint.bzl')
-rw-r--r-- | third_party/bazel/rules_haskell/haskell/lint.bzl | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/third_party/bazel/rules_haskell/haskell/lint.bzl b/third_party/bazel/rules_haskell/haskell/lint.bzl new file mode 100644 index 000000000000..1ee7d47a2e11 --- /dev/null +++ b/third_party/bazel/rules_haskell/haskell/lint.bzl @@ -0,0 +1,137 @@ +"""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` +""" |