mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-11-22 23:30:42 +03:00
838 lines
27 KiB
Python
Generated
838 lines
27 KiB
Python
Generated
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
#
|
|
# This source code is licensed under both the MIT license found in the
|
|
# LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
# License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
# of this source tree.
|
|
|
|
# Implementation of the Haskell build rules.
|
|
|
|
load("@prelude//:paths.bzl", "paths")
|
|
load("@prelude//cxx:archive.bzl", "make_archive")
|
|
load(
|
|
"@prelude//cxx:cxx_toolchain_types.bzl",
|
|
"CxxPlatformInfo",
|
|
"CxxToolchainInfo",
|
|
)
|
|
load(
|
|
"@prelude//cxx:preprocessor.bzl",
|
|
"CPreprocessor",
|
|
"cxx_inherited_preprocessor_infos",
|
|
"cxx_merge_cpreprocessors",
|
|
)
|
|
load(
|
|
"@prelude//linking:link_groups.bzl",
|
|
"merge_link_group_lib_info",
|
|
)
|
|
load(
|
|
"@prelude//linking:link_info.bzl",
|
|
"Archive",
|
|
"ArchiveLinkable",
|
|
"LinkInfo",
|
|
"LinkInfos",
|
|
"LinkStyle",
|
|
"Linkage",
|
|
"LinkedObject",
|
|
"MergedLinkInfo",
|
|
"SharedLibLinkable",
|
|
"create_merged_link_info",
|
|
"get_actual_link_style",
|
|
"get_link_args",
|
|
"get_link_styles_for_linkage",
|
|
"merge_link_infos",
|
|
"unpack_link_args",
|
|
)
|
|
load(
|
|
"@prelude//linking:linkable_graph.bzl",
|
|
"create_linkable_graph",
|
|
"create_linkable_graph_node",
|
|
"create_linkable_node",
|
|
)
|
|
load(
|
|
"@prelude//linking:shared_libraries.bzl",
|
|
"SharedLibraryInfo",
|
|
"create_shared_libraries",
|
|
"merge_shared_libraries",
|
|
"traverse_shared_library_info",
|
|
)
|
|
load(
|
|
"@prelude//python:python.bzl",
|
|
"PythonLibraryInfo",
|
|
)
|
|
load("@prelude//utils:platform_flavors_util.bzl", "by_platform")
|
|
load("@prelude//utils:utils.bzl", "flatten")
|
|
|
|
HaskellPlatformInfo = provider(fields = [
|
|
"name",
|
|
])
|
|
|
|
HaskellToolchainInfo = provider(fields = [
|
|
"compiler",
|
|
"compiler_flags",
|
|
"linker",
|
|
"linker_flags",
|
|
"haddock",
|
|
"compiler_major_version",
|
|
"package_name_prefix",
|
|
"packager",
|
|
"use_argsfile",
|
|
"support_expose_package",
|
|
"archive_contents",
|
|
"ghci_script_template",
|
|
"ghci_iserv_template",
|
|
"ide_script_template",
|
|
"ghci_binutils_path",
|
|
"ghci_lib_path",
|
|
"ghci_ghc_path",
|
|
"ghci_iserv_path",
|
|
"ghci_iserv_prof_path",
|
|
"ghci_cxx_path",
|
|
"ghci_cc_path",
|
|
"ghci_cpp_path",
|
|
"ghci_packager",
|
|
"cache_links",
|
|
])
|
|
|
|
# A list of `HaskellLibraryInfo`s.
|
|
HaskellLinkInfo = provider(
|
|
# Contains a list of HaskellLibraryInfo records.
|
|
fields = [
|
|
"info", # { LinkStyle.type : [HaskellLibraryInfo] } # TODO use a tset
|
|
],
|
|
)
|
|
|
|
HaskellIndexingTSet = transitive_set()
|
|
|
|
# A list of hie dirs
|
|
HaskellIndexInfo = provider(
|
|
fields = [
|
|
"info", # { LinkStyle.type : HaskellIndexingTset }
|
|
],
|
|
)
|
|
|
|
# If the target is a haskell library, the HaskellLibraryProvider
|
|
# contains its HaskellLibraryInfo. (in contrast to a HaskellLinkInfo,
|
|
# which contains the HaskellLibraryInfo for all the transitive
|
|
# dependencies). Direct dependencies are treated differently from
|
|
# indirect dependencies for the purposes of module visibility.
|
|
HaskellLibraryProvider = provider(
|
|
fields = [
|
|
"lib", # { LinkStyle.type : HaskellLibraryInfo }
|
|
],
|
|
)
|
|
|
|
# A record of a Haskell library.
|
|
HaskellLibraryInfo = record(
|
|
# The library target name: e.g. "rts"
|
|
name = str.type,
|
|
# package config database: e.g. platform009/build/ghc/lib/package.conf.d
|
|
db = "artifact",
|
|
# e.g. "base-4.13.0.0"
|
|
id = str.type,
|
|
import_dirs = ["artifact"],
|
|
stub_dirs = ["artifact"],
|
|
|
|
# This field is only used as hidden inputs to compilation, to
|
|
# support Template Haskell which may need access to the libraries
|
|
# at compile time. The real library flags are propagated up the
|
|
# dependency graph via MergedLinkInfo.
|
|
libs = field(["artifact"], []),
|
|
)
|
|
|
|
# --
|
|
|
|
def _by_platform(ctx: "context", xs: [(str.type, ["_a"])]) -> ["_a"]:
|
|
platform = ctx.attrs._cxx_toolchain[CxxPlatformInfo].name
|
|
return flatten(by_platform([platform], xs))
|
|
|
|
def _attr_deps(ctx: "context") -> ["dependency"]:
|
|
return ctx.attrs.deps + _by_platform(ctx, ctx.attrs.platform_deps)
|
|
|
|
# Disable until we have a need to call this.
|
|
# def _attr_deps_merged_link_infos(ctx: "context") -> ["MergedLinkInfo"]:
|
|
# return filter(None, [d[MergedLinkInfo] for d in _attr_deps(ctx)])
|
|
|
|
def _attr_deps_haskell_link_infos(ctx: "context") -> ["HaskellLinkInfo"]:
|
|
return filter(None, [d.get(HaskellLinkInfo) for d in _attr_deps(ctx) + ctx.attrs.template_deps])
|
|
|
|
def _attr_deps_haskell_lib_infos(
|
|
ctx: "context",
|
|
link_style: LinkStyle.type) -> ["HaskellLibraryInfo"]:
|
|
return [
|
|
x.lib[link_style]
|
|
for x in filter(None, [
|
|
d.get(HaskellLibraryProvider)
|
|
for d in _attr_deps(ctx) + ctx.attrs.template_deps
|
|
])
|
|
]
|
|
|
|
def _cxx_toolchain_link_style(ctx: "context") -> LinkStyle.type:
|
|
return ctx.attrs._cxx_toolchain[CxxToolchainInfo].linker_info.link_style
|
|
|
|
def _attr_link_style(ctx: "context") -> LinkStyle.type:
|
|
if ctx.attrs.link_style != None:
|
|
return LinkStyle(ctx.attrs.link_style)
|
|
else:
|
|
return _cxx_toolchain_link_style(ctx)
|
|
|
|
def _attr_preferred_linkage(ctx: "context") -> Linkage.type:
|
|
preferred_linkage = ctx.attrs.preferred_linkage
|
|
|
|
# force_static is deprecated, but it has precedence over preferred_linkage
|
|
if getattr(ctx.attrs, "force_static", False):
|
|
preferred_linkage = "static"
|
|
|
|
return Linkage(preferred_linkage)
|
|
|
|
# --
|
|
|
|
def _is_boot_src(x: str.type) -> bool.type:
|
|
_, ext = paths.split_extension(x)
|
|
return ext == ".hs-boot"
|
|
|
|
def _src_to_module_name(x: str.type) -> str.type:
|
|
base, _ext = paths.split_extension(x)
|
|
return base.replace("/", ".")
|
|
|
|
def haskell_prebuilt_library_impl(ctx: "context") -> ["provider"]:
|
|
native_infos = []
|
|
haskell_infos = []
|
|
shared_library_infos = []
|
|
for dep in ctx.attrs.deps:
|
|
used = False
|
|
if HaskellLinkInfo in dep:
|
|
used = True
|
|
haskell_infos.append(dep[HaskellLinkInfo])
|
|
if MergedLinkInfo in dep:
|
|
used = True
|
|
native_infos.append(dep[MergedLinkInfo])
|
|
if SharedLibraryInfo in dep:
|
|
used = True
|
|
shared_library_infos.append(dep[SharedLibraryInfo])
|
|
if PythonLibraryInfo in dep:
|
|
used = True
|
|
if not used:
|
|
fail("Unexpected link info encountered")
|
|
|
|
hlibinfos = {}
|
|
hlinkinfos = {}
|
|
link_infos = {}
|
|
for link_style in LinkStyle:
|
|
libs = []
|
|
if ctx.attrs.enable_profiling:
|
|
if link_style == LinkStyle("static"):
|
|
libs = ctx.attrs.profiled_static_libs
|
|
if link_style == LinkStyle("static_pic"):
|
|
libs = ctx.attrs.pic_profiled_static_libs
|
|
elif link_style == LinkStyle("shared"):
|
|
libs = ctx.attrs.shared_libs.values()
|
|
elif link_style == LinkStyle("static"):
|
|
libs = ctx.attrs.static_libs
|
|
elif link_style == LinkStyle("static_pic"):
|
|
libs = ctx.attrs.pic_static_libs
|
|
hlibinfo = HaskellLibraryInfo(
|
|
name = ctx.attrs.name,
|
|
db = ctx.attrs.db,
|
|
import_dirs = ctx.attrs.import_dirs,
|
|
stub_dirs = [],
|
|
id = ctx.attrs.id,
|
|
libs = libs,
|
|
)
|
|
|
|
def archive_linkable(lib):
|
|
return ArchiveLinkable(
|
|
archive = Archive(artifact = lib),
|
|
linker_type = "unknown",
|
|
)
|
|
|
|
def shared_linkable(lib):
|
|
return SharedLibLinkable(
|
|
lib = lib,
|
|
)
|
|
|
|
linkables = [
|
|
(shared_linkable if link_style == LinkStyle("shared") else archive_linkable)(lib)
|
|
for lib in libs
|
|
]
|
|
|
|
hlibinfos[link_style] = hlibinfo
|
|
hlinkinfos[link_style] = [hlibinfo]
|
|
link_infos[link_style] = LinkInfos(
|
|
default = LinkInfo(
|
|
pre_flags = ctx.attrs.exported_linker_flags,
|
|
linkables = linkables,
|
|
),
|
|
)
|
|
|
|
haskell_link_infos = HaskellLinkInfo(info = hlinkinfos)
|
|
haskell_lib_provider = HaskellLibraryProvider(lib = hlibinfos)
|
|
|
|
merged_link_info = create_merged_link_info(
|
|
ctx,
|
|
link_infos = link_infos,
|
|
exported_deps = native_infos,
|
|
)
|
|
|
|
solibs = {}
|
|
for soname, lib in ctx.attrs.shared_libs.items():
|
|
solibs[soname] = LinkedObject(output = lib)
|
|
|
|
linkable_graph = create_linkable_graph(
|
|
ctx,
|
|
node = create_linkable_graph_node(
|
|
ctx,
|
|
linkable_node = create_linkable_node(
|
|
ctx = ctx,
|
|
exported_deps = ctx.attrs.deps,
|
|
link_infos = link_infos,
|
|
shared_libs = solibs,
|
|
),
|
|
),
|
|
deps = ctx.attrs.deps,
|
|
)
|
|
|
|
inherited_pp_info = cxx_inherited_preprocessor_infos(ctx.attrs.deps)
|
|
own_pp_info = CPreprocessor(
|
|
args = flatten([["-isystem", d] for d in ctx.attrs.cxx_header_dirs]),
|
|
)
|
|
|
|
return [
|
|
DefaultInfo(),
|
|
haskell_lib_provider,
|
|
cxx_merge_cpreprocessors(ctx, [own_pp_info], inherited_pp_info),
|
|
merge_shared_libraries(
|
|
ctx.actions,
|
|
create_shared_libraries(ctx, solibs),
|
|
shared_library_infos,
|
|
),
|
|
merge_link_group_lib_info(deps = ctx.attrs.deps),
|
|
merge_haskell_link_infos(haskell_infos + [haskell_link_infos]),
|
|
merged_link_info,
|
|
linkable_graph,
|
|
]
|
|
|
|
def merge_haskell_link_infos(deps: [HaskellLinkInfo.type]) -> HaskellLinkInfo.type:
|
|
merged = {}
|
|
for link_style in LinkStyle:
|
|
children = []
|
|
for dep in deps:
|
|
if link_style in dep.info:
|
|
children.extend(dep.info[link_style])
|
|
merged[link_style] = dedupe(children)
|
|
|
|
return HaskellLinkInfo(info = merged)
|
|
|
|
# The type of the return value of the `_compile()` function.
|
|
CompileResultInfo = record(
|
|
objects = field("artifact"),
|
|
hi = field("artifact"),
|
|
stubs = field("artifact"),
|
|
producing_indices = field("bool"),
|
|
)
|
|
|
|
def _link_style_extensions(link_style: LinkStyle.type) -> (str.type, str.type):
|
|
if link_style == LinkStyle("shared"):
|
|
return ("dyn_o", "dyn_hi")
|
|
elif link_style == LinkStyle("static_pic"):
|
|
return ("o", "hi") # is this right?
|
|
elif link_style == LinkStyle("static"):
|
|
return ("o", "hi")
|
|
fail("unknown LinkStyle")
|
|
|
|
def _output_extensions(
|
|
link_style: LinkStyle.type,
|
|
profiled: bool.type) -> (str.type, str.type):
|
|
osuf, hisuf = _link_style_extensions(link_style)
|
|
if profiled:
|
|
return ("p_" + osuf, "p_" + hisuf)
|
|
else:
|
|
return (osuf, hisuf)
|
|
|
|
def _srcs_to_objfiles(
|
|
ctx: "context",
|
|
odir: "artifact",
|
|
osuf: str.type) -> "cmd_args":
|
|
objfiles = cmd_args()
|
|
for src in ctx.attrs.srcs:
|
|
# Don't link boot sources, as they're only meant to be used for compiling.
|
|
if not _is_boot_src(src):
|
|
objfiles.add(cmd_args([odir, "/", paths.replace_extension(src, "." + osuf)], delimiter = ""))
|
|
return objfiles
|
|
|
|
# Compile all the context's sources.
|
|
def _compile(
|
|
ctx: "context",
|
|
link_style: LinkStyle.type,
|
|
extra_args = []) -> CompileResultInfo.type:
|
|
haskell_toolchain = ctx.attrs._haskell_toolchain[HaskellToolchainInfo]
|
|
compile_cmd = cmd_args(haskell_toolchain.compiler)
|
|
compile_cmd.add(haskell_toolchain.compiler_flags)
|
|
|
|
# Some rules pass in RTS (e.g. `+RTS ... -RTS`) options for GHC, which can't
|
|
# be parsed when inside an argsfile.
|
|
compile_cmd.add(ctx.attrs.compiler_flags)
|
|
|
|
compile_args = cmd_args()
|
|
compile_args.add("-no-link", "-i")
|
|
|
|
if ctx.attrs.enable_profiling:
|
|
compile_args.add("-prof")
|
|
|
|
if link_style == LinkStyle("shared"):
|
|
compile_args.add("-dynamic", "-fPIC")
|
|
elif link_style == LinkStyle("static_pic"):
|
|
compile_args.add("-fPIC")
|
|
|
|
osuf, hisuf = _output_extensions(link_style, ctx.attrs.enable_profiling)
|
|
compile_args.add("-osuf", osuf, "-hisuf", hisuf)
|
|
|
|
if getattr(ctx.attrs, "main", None) != None:
|
|
compile_args.add(["-main-is", ctx.attrs.main])
|
|
|
|
objects = ctx.actions.declare_output("objects-" + link_style.value, dir = True)
|
|
hi = ctx.actions.declare_output("hi-" + link_style.value, dir = True)
|
|
stubs = ctx.actions.declare_output("stubs-" + link_style.value, dir = True)
|
|
|
|
compile_args.add(
|
|
"-odir",
|
|
objects.as_output(),
|
|
"-hidir",
|
|
hi.as_output(),
|
|
"-hiedir",
|
|
hi.as_output(),
|
|
"-stubdir",
|
|
stubs.as_output(),
|
|
)
|
|
|
|
# Add -package-db and -expose-package flags for each Haskell
|
|
# library dependency. Note that these don't need to be in a
|
|
# particular order and we really want to remove duplicates (there
|
|
# are a *lot* of duplicates).
|
|
libs = {}
|
|
for lib in merge_haskell_link_infos(_attr_deps_haskell_link_infos(ctx)).info[link_style]:
|
|
libs[lib.db] = lib # lib.db is a good enough unique key
|
|
for lib in libs.values():
|
|
compile_args.hidden(lib.import_dirs)
|
|
compile_args.hidden(lib.stub_dirs)
|
|
|
|
# libs of dependencies might be needed at compile time if
|
|
# we're using Template Haskell:
|
|
compile_args.hidden(lib.libs)
|
|
for db in libs:
|
|
compile_args.add("-package-db", db)
|
|
|
|
# Expose only the packages we depend on directly
|
|
for lib in _attr_deps_haskell_lib_infos(ctx, link_style):
|
|
compile_args.add("-expose-package", lib.name)
|
|
|
|
# base is special and gets exposed by default
|
|
compile_args.add("-expose-package", "base")
|
|
|
|
# Add args from preprocess-able inputs.
|
|
inherited_pre = cxx_inherited_preprocessor_infos(ctx.attrs.deps)
|
|
pre = cxx_merge_cpreprocessors(ctx, [], inherited_pre)
|
|
pre_args = pre.set.project_as_args("args")
|
|
compile_args.add(cmd_args(pre_args, format = "-optP={}"))
|
|
|
|
compile_args.add(extra_args)
|
|
|
|
for (path, src) in ctx.attrs.srcs.items():
|
|
# hs-boot files aren't expected to be an argument to compiler but does need
|
|
# to be included in the directory of the associated src file
|
|
if _is_boot_src(path):
|
|
compile_args.hidden(src)
|
|
else:
|
|
compile_args.add(src)
|
|
|
|
argsfile = ctx.actions.declare_output("haskell_compile_" + link_style.value + ".argsfile")
|
|
ctx.actions.write(argsfile.as_output(), compile_args, allow_args = True)
|
|
hidden_args = [compile_args]
|
|
compile_cmd.add(cmd_args(argsfile, format = "@{}").hidden(hidden_args))
|
|
|
|
ctx.actions.run(
|
|
compile_cmd,
|
|
category = "haskell_compile_" + link_style.value,
|
|
no_outputs_cleanup = True,
|
|
)
|
|
|
|
producing_indices = "-fwrite-ide-info" in ctx.attrs.compiler_flags
|
|
|
|
return CompileResultInfo(
|
|
objects = objects,
|
|
hi = hi,
|
|
stubs = stubs,
|
|
producing_indices = producing_indices,
|
|
)
|
|
|
|
_REGISTER_PACKAGE = """\
|
|
set -euo pipefail
|
|
GHC_PKG=$1
|
|
DB=$2
|
|
PKGCONF=$3
|
|
"$GHC_PKG" init "$DB"
|
|
"$GHC_PKG" register --package-conf "$DB" --no-expand-pkgroot "$PKGCONF"
|
|
"""
|
|
|
|
# Create a package
|
|
#
|
|
# The way we use packages is a bit strange. We're not using them
|
|
# at link time at all: all the linking info is in the
|
|
# HaskellLibraryInfo and we construct linker command lines
|
|
# manually. Packages are used for:
|
|
#
|
|
# - finding .hi files at compile time
|
|
#
|
|
# - symbol namespacing (so that modules with the same name in
|
|
# different libraries don't clash).
|
|
#
|
|
# - controlling module visibility: only dependencies that are
|
|
# directly declared as dependencies may be used
|
|
#
|
|
# - Template Haskell: the compiler needs to load libraries itself
|
|
# at compile time, so it uses the package specs to find out
|
|
# which libraries and where.
|
|
def _make_package(
|
|
ctx: "context",
|
|
link_style: LinkStyle.type,
|
|
pkgname: str.type,
|
|
libname: str.type,
|
|
hlis: [HaskellLibraryInfo.type],
|
|
hi: "artifact",
|
|
lib: "artifact") -> "artifact":
|
|
# Don't expose boot sources, as they're only meant to be used for compiling.
|
|
modules = [_src_to_module_name(x) for x in ctx.attrs.srcs if not _is_boot_src(x)]
|
|
|
|
uniq_hlis = {}
|
|
for x in hlis:
|
|
uniq_hlis[x.id] = x
|
|
|
|
conf = [
|
|
"name: " + pkgname,
|
|
"version: 1.0.0",
|
|
"id: " + pkgname,
|
|
"key: " + pkgname,
|
|
"exposed: False",
|
|
"exposed-modules: " + ", ".join(modules),
|
|
"import-dirs: \"${pkgroot}/hi-" + link_style.value + "\"",
|
|
"library-dirs: \"${pkgroot}/lib-" + link_style.value + "\"",
|
|
"extra-libraries: " + libname,
|
|
"depends: " + ", ".join(uniq_hlis),
|
|
]
|
|
pkg_conf = ctx.actions.write("pkg-" + link_style.value + ".conf", conf)
|
|
|
|
db = ctx.actions.declare_output("db-" + link_style.value)
|
|
|
|
db_deps = {}
|
|
for x in uniq_hlis.values():
|
|
db_deps[repr(x.db)] = x.db
|
|
|
|
# So that ghc-pkg can find the DBs for the dependencies. We might
|
|
# be able to use flags for this instead, but this works.
|
|
ghc_package_path = cmd_args(
|
|
db_deps.values(),
|
|
delimiter = ":",
|
|
)
|
|
|
|
haskell_toolchain = ctx.attrs._haskell_toolchain[HaskellToolchainInfo]
|
|
ctx.actions.run(
|
|
cmd_args([
|
|
"sh",
|
|
"-c",
|
|
_REGISTER_PACKAGE,
|
|
"",
|
|
haskell_toolchain.packager[RunInfo],
|
|
db.as_output(),
|
|
pkg_conf,
|
|
]).hidden(hi).hidden(lib), # needs hi, because ghc-pkg checks that the .hi files exist
|
|
category = "haskell_package_" + link_style.value,
|
|
env = {"GHC_PACKAGE_PATH": ghc_package_path},
|
|
)
|
|
|
|
return db
|
|
|
|
def haskell_library_impl(ctx: "context") -> ["provider"]:
|
|
libname = repr(ctx.label.path).replace("//", "_").replace("/", "_") + "_" + ctx.label.name
|
|
pkgname = libname.replace("_", "-")
|
|
|
|
# Link the objects into a library
|
|
haskell_toolchain = ctx.attrs._haskell_toolchain[HaskellToolchainInfo]
|
|
|
|
preferred_linkage = _attr_preferred_linkage(ctx)
|
|
if ctx.attrs.enable_profiling and preferred_linkage == Linkage("any"):
|
|
preferred_linkage = Linkage("static")
|
|
|
|
hlis = []
|
|
nlis = []
|
|
shared_library_infos = []
|
|
|
|
for lib in _attr_deps(ctx):
|
|
li = lib.get(HaskellLinkInfo)
|
|
if li != None:
|
|
hlis.append(li)
|
|
li = lib.get(MergedLinkInfo)
|
|
if li != None:
|
|
nlis.append(li)
|
|
li = lib.get(SharedLibraryInfo)
|
|
if li != None:
|
|
shared_library_infos.append(li)
|
|
|
|
solibs = {}
|
|
link_infos = {}
|
|
hlib_infos = {}
|
|
hlink_infos = {}
|
|
indexing_tsets = {}
|
|
sub_targets = {}
|
|
|
|
for link_style in get_link_styles_for_linkage(preferred_linkage):
|
|
osuf, _hisuf = _output_extensions(link_style, ctx.attrs.enable_profiling)
|
|
|
|
# Compile the sources
|
|
compiled = _compile(ctx, link_style, ["-this-unit-id", pkgname])
|
|
|
|
if link_style == LinkStyle("static_pic"):
|
|
libstem = libname + "_pic"
|
|
else:
|
|
libstem = libname
|
|
|
|
if link_style == LinkStyle("shared"):
|
|
libfile = "lib" + libstem + ".so"
|
|
else:
|
|
libfile = "lib" + libstem + ".a"
|
|
lib_short_path = paths.join("lib-{}".format(link_style.value), libfile)
|
|
|
|
uniq_infos = dedupe(flatten([x.info[link_style] for x in hlis]))
|
|
|
|
objfiles = _srcs_to_objfiles(ctx, compiled.objects, osuf)
|
|
|
|
if link_style == LinkStyle("shared"):
|
|
lib = ctx.actions.declare_output(lib_short_path)
|
|
link = cmd_args(haskell_toolchain.linker)
|
|
link.add(haskell_toolchain.linker_flags)
|
|
link.add(ctx.attrs.linker_flags)
|
|
link.add("-o", lib.as_output())
|
|
link.add(
|
|
"-shared",
|
|
"-dynamic",
|
|
"-optl",
|
|
"-Wl,-soname",
|
|
"-optl",
|
|
"-Wl," + libfile,
|
|
)
|
|
|
|
link.add(objfiles)
|
|
link.hidden(compiled.stubs)
|
|
|
|
infos = get_link_args(merge_link_infos(ctx, nlis), link_style)
|
|
link.add(cmd_args(unpack_link_args(infos), prepend = "-optl"))
|
|
ctx.actions.run(link, category = "haskell_link")
|
|
|
|
solibs[libfile] = LinkedObject(output = lib)
|
|
libs = [lib]
|
|
link_infos[link_style] = LinkInfos(
|
|
default = LinkInfo(linkables = [SharedLibLinkable(lib = lib)]),
|
|
)
|
|
|
|
else: # static flavours
|
|
# TODO: avoid making an archive for a single object, like cxx does
|
|
# (but would that work with Template Haskell?)
|
|
archive = make_archive(ctx, lib_short_path, [compiled.objects], objfiles)
|
|
lib = archive.artifact
|
|
libs = [lib] + archive.external_objects
|
|
link_infos[link_style] = LinkInfos(
|
|
default = LinkInfo(linkables = [ArchiveLinkable(archive = archive, linker_type = "unknown")]),
|
|
)
|
|
|
|
db = _make_package(ctx, link_style, pkgname, libstem, uniq_infos, compiled.hi, lib)
|
|
|
|
hlib = HaskellLibraryInfo(
|
|
name = pkgname,
|
|
db = db,
|
|
id = pkgname,
|
|
import_dirs = [compiled.hi],
|
|
stub_dirs = [compiled.stubs],
|
|
libs = libs,
|
|
)
|
|
hlib_infos[link_style] = hlib
|
|
hlink_infos[link_style] = [hlib]
|
|
|
|
if compiled.producing_indices:
|
|
tset = derive_indexing_tset(ctx.actions, link_style, compiled.hi, _attr_deps(ctx))
|
|
indexing_tsets[link_style] = tset
|
|
|
|
sub_targets[link_style.value.replace("_", "-")] = [DefaultInfo(
|
|
default_outputs = libs,
|
|
)]
|
|
|
|
merged_link_info = create_merged_link_info(
|
|
ctx,
|
|
link_infos = link_infos,
|
|
preferred_linkage = preferred_linkage,
|
|
exported_deps = nlis,
|
|
)
|
|
|
|
linkable_graph = create_linkable_graph(
|
|
ctx,
|
|
node = create_linkable_graph_node(
|
|
ctx,
|
|
linkable_node = create_linkable_node(
|
|
ctx = ctx,
|
|
preferred_linkage = preferred_linkage,
|
|
exported_deps = ctx.attrs.deps,
|
|
link_infos = link_infos,
|
|
shared_libs = solibs,
|
|
),
|
|
),
|
|
deps = ctx.attrs.deps,
|
|
)
|
|
|
|
link_style = _cxx_toolchain_link_style(ctx)
|
|
actual_link_style = get_actual_link_style(link_style, preferred_linkage)
|
|
default_output = hlib_infos[actual_link_style].libs
|
|
|
|
inherited_pp_info = cxx_inherited_preprocessor_infos(_attr_deps(ctx))
|
|
|
|
# We would like to expose the generated _stub.h headers to C++
|
|
# compilations, but it's hard to do that without overbuilding. Which
|
|
# link_style should we pick below? If we pick a different link_style from
|
|
# the one being used by the root rule, we'll end up building all the
|
|
# Haskell libraries multiple times.
|
|
#
|
|
# pp = [CPreprocessor(
|
|
# args =
|
|
# flatten([["-isystem", dir] for dir in hlib_infos[actual_link_style].stub_dirs]),
|
|
# )]
|
|
pp = []
|
|
|
|
providers = [
|
|
DefaultInfo(
|
|
default_outputs = default_output,
|
|
sub_targets = sub_targets,
|
|
),
|
|
HaskellLibraryProvider(lib = hlib_infos),
|
|
merge_haskell_link_infos(hlis + [HaskellLinkInfo(info = hlink_infos)]),
|
|
merged_link_info,
|
|
linkable_graph,
|
|
cxx_merge_cpreprocessors(ctx, pp, inherited_pp_info),
|
|
merge_shared_libraries(ctx.actions, create_shared_libraries(ctx, solibs), shared_library_infos),
|
|
]
|
|
|
|
if indexing_tsets:
|
|
providers.append(HaskellIndexInfo(info = indexing_tsets))
|
|
|
|
templ_vars = {}
|
|
|
|
# Add in ldflag macros.
|
|
for link_style in (LinkStyle("static"), LinkStyle("static_pic")):
|
|
name = "ldflags-" + link_style.value.replace("_", "-")
|
|
args = cmd_args()
|
|
linker_info = ctx.attrs._cxx_toolchain[CxxToolchainInfo].linker_info
|
|
args.add(linker_info.linker_flags)
|
|
args.add(unpack_link_args(
|
|
get_link_args(
|
|
merged_link_info,
|
|
link_style,
|
|
),
|
|
))
|
|
templ_vars[name] = args
|
|
|
|
# TODO(T110378127): To implement `$(ldflags-shared ...)` properly, we'd need
|
|
# to setup a symink tree rule for all transitive shared libs. Since this
|
|
# currently would be pretty costly (O(N^2)?), and since it's not that
|
|
# commonly used anyway, just use `static-pic` instead. Longer-term, once
|
|
# v1 is gone, macros that use `$(ldflags-shared ...)` (e.g. Haskell's
|
|
# hsc2hs) can move to a v2 rules-based API to avoid needing this macro.
|
|
templ_vars["ldflags-shared"] = templ_vars["ldflags-static-pic"]
|
|
|
|
providers.append(TemplatePlaceholderInfo(keyed_variables = templ_vars))
|
|
|
|
providers.append(merge_link_group_lib_info(deps = _attr_deps(ctx)))
|
|
|
|
return providers
|
|
|
|
def derive_indexing_tset(
|
|
actions: "actions",
|
|
link_style: LinkStyle.type,
|
|
value: ["artifact", None],
|
|
children: ["dependency"]) -> "HaskellIndexingTSet":
|
|
index_children = []
|
|
for dep in children:
|
|
li = dep.get(HaskellIndexInfo)
|
|
if li:
|
|
if (link_style in li.info):
|
|
index_children.append(li.info[link_style])
|
|
|
|
return actions.tset(
|
|
HaskellIndexingTSet,
|
|
value = value,
|
|
children = index_children,
|
|
)
|
|
|
|
def haskell_binary_impl(ctx: "context") -> ["provider"]:
|
|
# Decide what kind of linking we're doing
|
|
link_style = _attr_link_style(ctx)
|
|
|
|
# Profiling doesn't support shared libraries
|
|
if ctx.attrs.enable_profiling and link_style == LinkStyle("shared"):
|
|
link_style = LinkStyle("static")
|
|
|
|
compiled = _compile(ctx, link_style)
|
|
|
|
haskell_toolchain = ctx.attrs._haskell_toolchain[HaskellToolchainInfo]
|
|
|
|
output = ctx.actions.declare_output(ctx.attrs.name)
|
|
link = cmd_args(haskell_toolchain.compiler)
|
|
link.add("-o", output.as_output())
|
|
link.add(haskell_toolchain.linker_flags)
|
|
link.add(ctx.attrs.linker_flags)
|
|
link.hidden(compiled.stubs)
|
|
|
|
osuf, _hisuf = _output_extensions(link_style, ctx.attrs.enable_profiling)
|
|
|
|
objfiles = _srcs_to_objfiles(ctx, compiled.objects, osuf)
|
|
link.add(objfiles)
|
|
|
|
hlis = []
|
|
nlis = []
|
|
sos = {}
|
|
indexing_tsets = {}
|
|
for lib in _attr_deps(ctx):
|
|
li = lib.get(HaskellLinkInfo)
|
|
if li != None:
|
|
hlis.extend(li.info[link_style])
|
|
li = lib.get(MergedLinkInfo)
|
|
if li != None:
|
|
nlis.append(li)
|
|
li = lib.get(SharedLibraryInfo)
|
|
if li != None:
|
|
# TODO This should probably use merged_shared_libraries to check
|
|
# for soname conflicts.
|
|
for name, shared_lib in traverse_shared_library_info(li).items():
|
|
sos[name] = shared_lib.lib.output
|
|
|
|
if compiled.producing_indices:
|
|
tset = derive_indexing_tset(ctx.actions, link_style, compiled.hi, _attr_deps(ctx))
|
|
indexing_tsets[link_style] = tset
|
|
|
|
nlis = merge_link_infos(ctx, nlis)
|
|
infos = get_link_args(nlis, link_style)
|
|
link.add(cmd_args(unpack_link_args(infos), prepend = "-optl"))
|
|
|
|
ctx.actions.run(link, category = "haskell_link")
|
|
|
|
run = cmd_args(output)
|
|
|
|
if link_style == LinkStyle("shared"):
|
|
link.add("-optl", "-Wl,-rpath", "-optl", "-Wl,$ORIGIN/sos")
|
|
symlink_dir = ctx.actions.symlinked_dir("sos", sos)
|
|
run.hidden(symlink_dir)
|
|
|
|
providers = [
|
|
DefaultInfo(default_output = output),
|
|
RunInfo(args = run),
|
|
]
|
|
|
|
if indexing_tsets:
|
|
providers.append(HaskellIndexInfo(info = indexing_tsets))
|
|
|
|
return providers
|