feat(third_party/bazel): Check in rules_haskell from Tweag
This commit is contained in:
parent
2eb1dc26e4
commit
f723b8b878
479 changed files with 51484 additions and 0 deletions
312
third_party/bazel/rules_haskell/haskell/haddock.bzl
vendored
Normal file
312
third_party/bazel/rules_haskell/haskell/haddock.bzl
vendored
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
"""Haddock support"""
|
||||
|
||||
load("@bazel_skylib//lib:paths.bzl", "paths")
|
||||
load(
|
||||
"@io_tweag_rules_haskell//haskell:providers.bzl",
|
||||
"HaddockInfo",
|
||||
"HaskellInfo",
|
||||
"HaskellLibraryInfo",
|
||||
)
|
||||
load(":private/context.bzl", "haskell_context", "render_env")
|
||||
load(":private/set.bzl", "set")
|
||||
|
||||
def _get_haddock_path(package_id):
|
||||
"""Get path to Haddock file of a package given its id.
|
||||
|
||||
Args:
|
||||
package_id: string, package id.
|
||||
|
||||
Returns:
|
||||
string: relative path to haddock file.
|
||||
"""
|
||||
return package_id + ".haddock"
|
||||
|
||||
def _haskell_doc_aspect_impl(target, ctx):
|
||||
if HaskellInfo not in target or HaskellLibraryInfo not in target:
|
||||
return []
|
||||
|
||||
# Packages imported via `//haskell:import.bzl%haskell_import` already
|
||||
# contain an `HaddockInfo` provider, so we just forward it
|
||||
if HaddockInfo in target:
|
||||
return []
|
||||
|
||||
hs = haskell_context(ctx, ctx.rule.attr)
|
||||
|
||||
package_id = target[HaskellLibraryInfo].package_id
|
||||
html_dir_raw = "doc-{0}".format(package_id)
|
||||
html_dir = ctx.actions.declare_directory(html_dir_raw)
|
||||
haddock_file = ctx.actions.declare_file(_get_haddock_path(package_id))
|
||||
|
||||
# XXX Haddock really wants a version number, so invent one from
|
||||
# thin air. See https://github.com/haskell/haddock/issues/898.
|
||||
if target[HaskellLibraryInfo].version:
|
||||
version = target[HaskellLibraryInfo].version
|
||||
else:
|
||||
version = "0"
|
||||
|
||||
args = ctx.actions.args()
|
||||
args.add("--package-name={0}".format(package_id))
|
||||
args.add("--package-version={0}".format(version))
|
||||
args.add_all([
|
||||
"-D",
|
||||
haddock_file.path,
|
||||
"-o",
|
||||
html_dir.path,
|
||||
"--html",
|
||||
"--hoogle",
|
||||
"--title={0}".format(package_id),
|
||||
"--hyperlinked-source",
|
||||
])
|
||||
|
||||
transitive_haddocks = {}
|
||||
transitive_html = {}
|
||||
|
||||
for dep in ctx.rule.attr.deps:
|
||||
if HaddockInfo in dep:
|
||||
transitive_haddocks.update(dep[HaddockInfo].transitive_haddocks)
|
||||
transitive_html.update(dep[HaddockInfo].transitive_html)
|
||||
|
||||
for pid in transitive_haddocks:
|
||||
args.add("--read-interface=../{0},{1}".format(
|
||||
pid,
|
||||
transitive_haddocks[pid].path,
|
||||
))
|
||||
|
||||
prebuilt_deps = ctx.actions.args()
|
||||
for dep in set.to_list(target[HaskellInfo].prebuilt_dependencies):
|
||||
prebuilt_deps.add(dep.package)
|
||||
prebuilt_deps.use_param_file(param_file_arg = "%s", use_always = True)
|
||||
|
||||
compile_flags = ctx.actions.args()
|
||||
for x in target[HaskellInfo].compile_flags:
|
||||
compile_flags.add_all(["--optghc", x])
|
||||
compile_flags.add_all([x.path for x in set.to_list(target[HaskellInfo].source_files)])
|
||||
compile_flags.add("-v0")
|
||||
|
||||
# haddock flags should take precedence over ghc args, hence are in
|
||||
# last position
|
||||
compile_flags.add_all(hs.toolchain.haddock_flags)
|
||||
|
||||
locale_archive_depset = (
|
||||
depset([hs.toolchain.locale_archive]) if hs.toolchain.locale_archive != None else depset()
|
||||
)
|
||||
|
||||
# TODO(mboes): we should be able to instantiate this template only
|
||||
# once per toolchain instance, rather than here.
|
||||
haddock_wrapper = ctx.actions.declare_file("haddock_wrapper-{}".format(hs.name))
|
||||
ctx.actions.expand_template(
|
||||
template = ctx.file._haddock_wrapper_tpl,
|
||||
output = haddock_wrapper,
|
||||
substitutions = {
|
||||
"%{ghc-pkg}": hs.tools.ghc_pkg.path,
|
||||
"%{haddock}": hs.tools.haddock.path,
|
||||
# XXX Workaround
|
||||
# https://github.com/bazelbuild/bazel/issues/5980.
|
||||
"%{env}": render_env(hs.env),
|
||||
},
|
||||
is_executable = True,
|
||||
)
|
||||
|
||||
# Transitive library dependencies for runtime.
|
||||
trans_link_ctx = target[HaskellInfo].transitive_cc_dependencies.dynamic_linking
|
||||
trans_libs = trans_link_ctx.libraries_to_link.to_list()
|
||||
|
||||
ctx.actions.run(
|
||||
inputs = depset(transitive = [
|
||||
set.to_depset(target[HaskellInfo].package_databases),
|
||||
set.to_depset(target[HaskellInfo].interface_dirs),
|
||||
set.to_depset(target[HaskellInfo].source_files),
|
||||
target[HaskellInfo].extra_source_files,
|
||||
set.to_depset(target[HaskellInfo].dynamic_libraries),
|
||||
depset(trans_libs),
|
||||
depset(transitive_haddocks.values()),
|
||||
depset(transitive_html.values()),
|
||||
target[CcInfo].compilation_context.headers,
|
||||
depset([
|
||||
hs.tools.ghc_pkg,
|
||||
hs.tools.haddock,
|
||||
]),
|
||||
locale_archive_depset,
|
||||
]),
|
||||
outputs = [haddock_file, html_dir],
|
||||
mnemonic = "HaskellHaddock",
|
||||
progress_message = "HaskellHaddock {}".format(ctx.label),
|
||||
executable = haddock_wrapper,
|
||||
arguments = [
|
||||
prebuilt_deps,
|
||||
args,
|
||||
compile_flags,
|
||||
],
|
||||
use_default_shell_env = True,
|
||||
)
|
||||
|
||||
transitive_html.update({package_id: html_dir})
|
||||
transitive_haddocks.update({package_id: haddock_file})
|
||||
|
||||
haddock_info = HaddockInfo(
|
||||
package_id = package_id,
|
||||
transitive_html = transitive_html,
|
||||
transitive_haddocks = transitive_haddocks,
|
||||
)
|
||||
output_files = OutputGroupInfo(default = transitive_html.values())
|
||||
|
||||
return [haddock_info, output_files]
|
||||
|
||||
haskell_doc_aspect = aspect(
|
||||
_haskell_doc_aspect_impl,
|
||||
attrs = {
|
||||
"_haddock_wrapper_tpl": attr.label(
|
||||
allow_single_file = True,
|
||||
default = Label("@io_tweag_rules_haskell//haskell:private/haddock_wrapper.sh.tpl"),
|
||||
),
|
||||
},
|
||||
attr_aspects = ["deps"],
|
||||
toolchains = ["@io_tweag_rules_haskell//haskell:toolchain"],
|
||||
)
|
||||
|
||||
def _haskell_doc_rule_impl(ctx):
|
||||
hs = haskell_context(ctx)
|
||||
|
||||
# Reject cases when number of dependencies is 0.
|
||||
|
||||
if not ctx.attr.deps:
|
||||
fail("haskell_doc needs at least one haskell_library component in deps")
|
||||
|
||||
doc_root_raw = ctx.attr.name
|
||||
haddock_dict = {}
|
||||
html_dict_original = {}
|
||||
all_caches = set.empty()
|
||||
|
||||
for dep in ctx.attr.deps:
|
||||
if HaddockInfo in dep:
|
||||
html_dict_original.update(dep[HaddockInfo].transitive_html)
|
||||
haddock_dict.update(dep[HaddockInfo].transitive_haddocks)
|
||||
if HaskellInfo in dep:
|
||||
set.mutable_union(
|
||||
all_caches,
|
||||
dep[HaskellInfo].package_databases,
|
||||
)
|
||||
|
||||
# Copy docs of Bazel deps into predefined locations under the root doc
|
||||
# directory.
|
||||
|
||||
html_dict_copied = {}
|
||||
doc_root_path = ""
|
||||
|
||||
for package_id in html_dict_original:
|
||||
html_dir = html_dict_original[package_id]
|
||||
output_dir = ctx.actions.declare_directory(
|
||||
paths.join(
|
||||
doc_root_raw,
|
||||
package_id,
|
||||
),
|
||||
)
|
||||
doc_root_path = paths.dirname(output_dir.path)
|
||||
|
||||
html_dict_copied[package_id] = output_dir
|
||||
|
||||
ctx.actions.run_shell(
|
||||
inputs = [html_dir],
|
||||
outputs = [output_dir],
|
||||
command = """
|
||||
mkdir -p "{doc_dir}"
|
||||
# Copy Haddocks of a dependency.
|
||||
cp -R -L "{html_dir}/." "{target_dir}"
|
||||
""".format(
|
||||
doc_dir = doc_root_path,
|
||||
html_dir = html_dir.path,
|
||||
target_dir = output_dir.path,
|
||||
),
|
||||
)
|
||||
|
||||
# Do one more Haddock call to generate the unified index
|
||||
|
||||
index_root_raw = paths.join(doc_root_raw, "index")
|
||||
index_root = ctx.actions.declare_directory(index_root_raw)
|
||||
|
||||
args = ctx.actions.args()
|
||||
args.add_all([
|
||||
"-o",
|
||||
index_root.path,
|
||||
"--title={0}".format(ctx.attr.name),
|
||||
"--gen-index",
|
||||
"--gen-contents",
|
||||
])
|
||||
|
||||
if ctx.attr.index_transitive_deps:
|
||||
# Include all packages in the unified index.
|
||||
for package_id in html_dict_copied:
|
||||
args.add("--read-interface=../{0},{1}".format(
|
||||
package_id,
|
||||
haddock_dict[package_id].path,
|
||||
))
|
||||
else:
|
||||
# Include only direct dependencies.
|
||||
for dep in ctx.attr.deps:
|
||||
if HaddockInfo in dep:
|
||||
package_id = dep[HaddockInfo].package_id
|
||||
args.add("--read-interface=../{0},{1}".format(
|
||||
package_id,
|
||||
haddock_dict[package_id].path,
|
||||
))
|
||||
|
||||
for cache in set.to_list(all_caches):
|
||||
args.add("--optghc=-package-db={0}".format(cache.dirname))
|
||||
|
||||
locale_archive_depset = (
|
||||
depset([hs.toolchain.locale_archive]) if hs.toolchain.locale_archive != None else depset()
|
||||
)
|
||||
|
||||
ctx.actions.run(
|
||||
inputs = depset(transitive = [
|
||||
set.to_depset(all_caches),
|
||||
depset(html_dict_copied.values()),
|
||||
depset(haddock_dict.values()),
|
||||
locale_archive_depset,
|
||||
]),
|
||||
outputs = [index_root],
|
||||
mnemonic = "HaskellHaddockIndex",
|
||||
executable = hs.tools.haddock,
|
||||
arguments = [args],
|
||||
)
|
||||
|
||||
return [DefaultInfo(
|
||||
files = depset(html_dict_copied.values() + [index_root]),
|
||||
)]
|
||||
|
||||
haskell_doc = rule(
|
||||
_haskell_doc_rule_impl,
|
||||
attrs = {
|
||||
"deps": attr.label_list(
|
||||
aspects = [haskell_doc_aspect],
|
||||
doc = "List of Haskell libraries to generate documentation for.",
|
||||
),
|
||||
"index_transitive_deps": attr.bool(
|
||||
default = False,
|
||||
doc = "Whether to include documentation of transitive dependencies in index.",
|
||||
),
|
||||
},
|
||||
toolchains = ["@io_tweag_rules_haskell//haskell:toolchain"],
|
||||
)
|
||||
"""Create API documentation.
|
||||
|
||||
Builds API documentation (using [Haddock][haddock]) for the given
|
||||
Haskell libraries. It will automatically build documentation for any
|
||||
transitive dependencies to allow for cross-package documentation
|
||||
linking.
|
||||
|
||||
Example:
|
||||
```bzl
|
||||
haskell_library(
|
||||
name = "my-lib",
|
||||
...
|
||||
)
|
||||
|
||||
haskell_doc(
|
||||
name = "my-lib-doc",
|
||||
deps = [":my-lib"],
|
||||
)
|
||||
```
|
||||
|
||||
[haddock]: http://haskell-haddock.readthedocs.io/en/latest/
|
||||
"""
|
||||
Loading…
Add table
Add a link
Reference in a new issue