about summary refs log blame commit diff
path: root/third_party/bazel/rules_haskell/haskell/private/dependencies.bzl
blob: 6a2a797872fe96a7588d196f2b55878f1b9dfeeb (plain) (tree)





























































































































































































































                                                                                                                                   
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