load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//lib:paths.bzl", "paths") load( "@io_tweag_rules_haskell//haskell:providers.bzl", "HaskellCcInfo", "HaskellInfo", "HaskellLibraryInfo", "HaskellPrebuiltPackageInfo", "empty_HaskellCcInfo", "merge_HaskellCcInfo", ) load( ":private/path_utils.bzl", "get_lib_name", "is_shared_library", "is_static_library", "ln", ) load(":private/set.bzl", "set") def _cc_get_static_lib(lib_info): """Return the library to use in static linking mode. This returns the first available library artifact in the following order: - static_library - pic_static_library - dynamic_library - interface_library Args: lib_info: LibraryToLink provider. Returns: File: The library to link against in static mode. """ if lib_info.static_library: return lib_info.static_library elif lib_info.pic_static_library: return lib_info.pic_static_library elif lib_info.dynamic_library: return lib_info.dynamic_library else: return lib_info.interface_library def _cc_get_dynamic_lib(lib_info): """Return the library to use in dynamic linking mode. This returns the first available library artifact in the following order: - dynamic_library - interface_library - pic_static_library - static_library Args: lib_info: LibraryToLink provider. Returns: File: The library to link against in dynamic mode. """ if lib_info.dynamic_library: return lib_info.dynamic_library elif lib_info.interface_library: return lib_info.interface_library elif lib_info.pic_static_library: return lib_info.pic_static_library else: return lib_info.static_library def _HaskellCcInfo_from_CcInfo(ctx, cc_info): libs_to_link = cc_info.linking_context.libraries_to_link static_libs_to_link = [] dynamic_libs_to_link = [] static_libs_for_runtime = [] dynamic_libs_for_runtime = [] for l in libs_to_link: _static_lib = _cc_get_static_lib(l) dynamic_lib = _cc_get_dynamic_lib(l) # Bazel itself only mangles dynamic libraries, not static libraries. # However, we need the library name of the static and dynamic version # of a library to match so that we can refer to both with one entry in # the package configuration file. Here we rename any static archives # with mismatching mangled dynamic library name. static_name = get_lib_name(_static_lib) dynamic_name = get_lib_name(dynamic_lib) if static_name != dynamic_name: ext = _static_lib.extension static_lib = ctx.actions.declare_file( "lib%s.%s" % (dynamic_name, ext), ) ln(ctx, _static_lib, static_lib) else: static_lib = _static_lib static_libs_to_link.append(static_lib) if is_shared_library(static_lib): static_libs_for_runtime.append(static_lib) dynamic_libs_to_link.append(dynamic_lib) if is_shared_library(dynamic_lib): dynamic_libs_for_runtime.append(dynamic_lib) return HaskellCcInfo( static_linking = struct( libraries_to_link = depset( direct = static_libs_to_link, order = "topological", ), dynamic_libraries_for_runtime = depset( direct = static_libs_for_runtime, order = "topological", ), user_link_flags = depset( direct = cc_info.linking_context.user_link_flags, order = "topological", ), ), dynamic_linking = struct( libraries_to_link = depset( direct = dynamic_libs_to_link, order = "topological", ), dynamic_libraries_for_runtime = depset( direct = dynamic_libs_for_runtime, order = "topological", ), user_link_flags = depset( direct = cc_info.linking_context.user_link_flags, order = "topological", ), ), ) def gather_dep_info(ctx, deps): """Collapse dependencies into a single `HaskellInfo`. Note that the field `prebuilt_dependencies` also includes prebuilt_dependencies of current target. Args: ctx: Rule context. deps: deps attribute. Returns: HaskellInfo: Unified information about all dependencies. """ acc = HaskellInfo( package_ids = set.empty(), package_databases = set.empty(), version_macros = set.empty(), static_libraries = [], static_libraries_prof = [], dynamic_libraries = set.empty(), interface_dirs = set.empty(), prebuilt_dependencies = set.empty(), direct_prebuilt_deps = set.empty(), cc_dependencies = empty_HaskellCcInfo(), transitive_cc_dependencies = empty_HaskellCcInfo(), ) for dep in deps: if HaskellInfo in dep: binfo = dep[HaskellInfo] package_ids = acc.package_ids if HaskellLibraryInfo not in dep: fail("Target {0} cannot depend on binary".format(ctx.attr.name)) if HaskellLibraryInfo in dep: set.mutable_insert(package_ids, dep[HaskellLibraryInfo].package_id) acc = HaskellInfo( package_ids = package_ids, package_databases = set.mutable_union(acc.package_databases, binfo.package_databases), version_macros = set.mutable_union(acc.version_macros, binfo.version_macros), static_libraries = acc.static_libraries + binfo.static_libraries, static_libraries_prof = acc.static_libraries_prof + binfo.static_libraries_prof, dynamic_libraries = set.mutable_union(acc.dynamic_libraries, binfo.dynamic_libraries), interface_dirs = set.mutable_union(acc.interface_dirs, binfo.interface_dirs), prebuilt_dependencies = set.mutable_union(acc.prebuilt_dependencies, binfo.prebuilt_dependencies), direct_prebuilt_deps = acc.direct_prebuilt_deps, cc_dependencies = acc.cc_dependencies, transitive_cc_dependencies = merge_HaskellCcInfo(acc.transitive_cc_dependencies, binfo.transitive_cc_dependencies), ) elif HaskellPrebuiltPackageInfo in dep: pkg = dep[HaskellPrebuiltPackageInfo] acc = HaskellInfo( package_ids = acc.package_ids, package_databases = acc.package_databases, version_macros = set.mutable_insert(acc.version_macros, pkg.version_macros_file), static_libraries = acc.static_libraries, static_libraries_prof = acc.static_libraries_prof, dynamic_libraries = acc.dynamic_libraries, interface_dirs = acc.interface_dirs, prebuilt_dependencies = set.mutable_insert(acc.prebuilt_dependencies, pkg), direct_prebuilt_deps = set.mutable_insert(acc.direct_prebuilt_deps, pkg), cc_dependencies = acc.cc_dependencies, transitive_cc_dependencies = acc.transitive_cc_dependencies, ) elif CcInfo in dep and HaskellInfo not in dep: # The final link of a binary must include all static libraries we # depend on, including transitives ones. Theses libs are provided # in the `CcInfo` provider. hs_cc_info = _HaskellCcInfo_from_CcInfo(ctx, dep[CcInfo]) acc = HaskellInfo( package_ids = acc.package_ids, package_databases = acc.package_databases, version_macros = acc.version_macros, static_libraries = acc.static_libraries, static_libraries_prof = acc.static_libraries_prof, dynamic_libraries = acc.dynamic_libraries, interface_dirs = acc.interface_dirs, prebuilt_dependencies = acc.prebuilt_dependencies, direct_prebuilt_deps = acc.direct_prebuilt_deps, cc_dependencies = merge_HaskellCcInfo( acc.cc_dependencies, hs_cc_info, ), transitive_cc_dependencies = merge_HaskellCcInfo( acc.transitive_cc_dependencies, hs_cc_info, ), ) return acc