diff --git a/haskell/cc.bzl b/haskell/cc.bzl index 5f7a820e8..e6851444e 100644 --- a/haskell/cc.bzl +++ b/haskell/cc.bzl @@ -12,7 +12,6 @@ load( load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain") load( "//haskell:providers.bzl", - "GhcPluginInfo", "HaskellInfo", ) load(":private/cc_libraries.bzl", "deps_HaskellCcLibrariesInfo", "get_cc_libraries") @@ -156,6 +155,11 @@ def cc_interop_info(ctx, override_cc_toolchain = None): cc_libraries_info = deps_HaskellCcLibrariesInfo( ctx.attr.deps + getattr(ctx.attr, "plugins", []) + getattr(ctx.attr, "setup_deps", []), ) + + transitive_libraries = _transitive_libraries_from_attr(ctx, "deps") + plugin_libraries = _transitive_libraries_from_attr(ctx, "plugins") + setup_libraries = _transitive_libraries_from_attr(ctx, "setup_deps") + return CcInteropInfo( tools = struct(**tools), env = env, @@ -171,22 +175,9 @@ def cc_interop_info(ctx, override_cc_toolchain = None): linker_flags = linker_flags, cc_libraries_info = cc_libraries_info, cc_libraries = get_cc_libraries(cc_libraries_info, [lib for li in cc_common.merge_cc_infos(cc_infos = ccs).linking_context.linker_inputs.to_list() for lib in li.libraries]), - transitive_libraries = [lib for li in cc_common.merge_cc_infos(cc_infos = [ - dep[CcInfo] - for dep in ctx.attr.deps - if CcInfo in dep - ]).linking_context.linker_inputs.to_list() for lib in li.libraries], - plugin_libraries = [lib for li in cc_common.merge_cc_infos(cc_infos = [ - dep[CcInfo] - for plugin in getattr(ctx.attr, "plugins", []) - for dep in plugin[GhcPluginInfo].deps - if CcInfo in dep - ]).linking_context.linker_inputs.to_list() for lib in li.libraries], - setup_libraries = [lib for li in cc_common.merge_cc_infos(cc_infos = [ - dep[CcInfo] - for dep in getattr(ctx.attr, "setup_deps", []) - if CcInfo in dep - ]).linking_context.linker_inputs.to_list() for lib in li.libraries], + transitive_libraries = transitive_libraries, + plugin_libraries = plugin_libraries, + setup_libraries = setup_libraries, ) def ghc_cc_program_args(hs, cc, ld): @@ -237,3 +228,17 @@ def ghc_cc_program_args(hs, cc, ld): "-r", ]) return args + +def _transitive_libraries_from_attr(ctx, attr_name): + cc_infos = [ + input[CcInfo] + for input in getattr(ctx.attr, attr_name, []) + if CcInfo in input + ] + cc_shared_library_infos = [ + input[CcSharedLibraryInfo] + for input in getattr(ctx.attr, attr_name, []) + if CcSharedLibraryInfo in input + ] + linker_inputs = cc_common.merge_cc_infos(cc_infos = cc_infos).linking_context.linker_inputs.to_list() + [info.linker_input for info in cc_shared_library_infos] + return [lib for li in linker_inputs for lib in li.libraries] diff --git a/haskell/private/cc_libraries.bzl b/haskell/private/cc_libraries.bzl index e892a1a5c..3c5bc049e 100644 --- a/haskell/private/cc_libraries.bzl +++ b/haskell/private/cc_libraries.bzl @@ -290,6 +290,7 @@ def extend_HaskellCcLibrariesInfo( ctx, cc_libraries_info, cc_info, + cc_shared_info, is_haskell): """Adapt new LibraryToLink and add to HaskellCcLibrariesInfo. @@ -301,6 +302,7 @@ def extend_HaskellCcLibrariesInfo( ctx: Aspect or rule context. cc_libraries_info: HaskellCcLibrariesInfo of all dependencies. cc_info: CcInfo of the current target. + cc_shared_info: CcSharedLibraryInfo of the current target. is_haskell: Bool, whether the current target is a Haskell library. Returns: @@ -309,7 +311,11 @@ def extend_HaskellCcLibrariesInfo( posix = ctx.toolchains["@rules_sh//sh/posix:toolchain_type"] libraries = dict(cc_libraries_info.libraries) - for li in cc_info.linking_context.linker_inputs.to_list(): + linker_inputs = cc_info.linking_context.linker_inputs.to_list() if cc_info else [] + if cc_shared_info: + linker_inputs.append(cc_shared_info.linker_input) + + for li in linker_inputs: for lib_to_link in li.libraries: key = cc_library_key(lib_to_link) if key in libraries: @@ -343,7 +349,7 @@ def extend_HaskellCcLibrariesInfo( def _haskell_cc_libraries_aspect_impl(target, ctx): if HaskellProtobufInfo in target: - # haskell_cc_libraries_aspect depends on the CcInfo and optionally + # haskell_cc_libraries_aspect depends on the CcInfo, CcSharedLibraryInfo and optionally # HaskellInfo providers of a target. In the case of proto_library # targets these providers are returned by the _haskell_proto_aspect. # That aspect in turn requires HaskellCcLibrariesInfo in all its @@ -361,11 +367,14 @@ def _haskell_cc_libraries_aspect_impl(target, ctx): if HaskellCcLibrariesInfo in dep ]) - if CcInfo in target: + cc_info = target[CcInfo] if CcInfo in target else None + cc_shared_info = target[CcSharedLibraryInfo] if CcSharedLibraryInfo in target else None + if cc_info or cc_shared_info: cc_libraries_info = extend_HaskellCcLibrariesInfo( ctx = ctx, cc_libraries_info = cc_libraries_info, - cc_info = target[CcInfo], + cc_info = cc_info, + cc_shared_info = cc_shared_info, is_haskell = HaskellInfo in target, ) @@ -386,3 +395,40 @@ haskell_cc_libraries_aspect = aspect( Create a symbolic link for each static library whose name doesn't match the mangled name of the corresponding dynamic library. """ + +def merge_cc_shared_library_infos(owner, cc_shared_library_infos): + """Similar to cc_common.merge_cc_infos but for CcSharedLibraryInfo + + Args: + owner: The label of the target that produced all files used in this input. + cc_shared_library_infos: CcSharedLibraryInfo providers to be merged. + + Returns: + CcSharedLibraryInfo + """ + dynamic_deps = [] + exports = [] + link_once_static_libs = {} + transitive_dynamic_deps = [] + libraries_to_link = [] + for cc_shared_library_info in cc_shared_library_infos: + dynamic_dep_entry = struct( + exports = cc_shared_library_info.exports, + linker_input = cc_shared_library_info.linker_input, + link_once_static_libs = cc_shared_library_info.link_once_static_libs, + ) + dynamic_deps.append(dynamic_dep_entry) + transitive_dynamic_deps.append(cc_shared_library_info.dynamic_deps) + libraries_to_link.extend(cc_shared_library_info.linker_input.libraries) + link_once_static_libs.update(pairs = cc_shared_library_info.link_once_static_libs) + exports.extend(cc_shared_library_info.exports) + + return CcSharedLibraryInfo( + dynamic_deps = depset(direct = dynamic_deps, transitive = transitive_dynamic_deps, order = "topological"), + exports = exports, + link_once_static_libs = link_once_static_libs, + linker_input = cc_common.create_linker_input( + owner = owner, + libraries = depset(libraries_to_link), + ), + ) diff --git a/haskell/private/haskell_impl.bzl b/haskell/private/haskell_impl.bzl index 1a98b565f..fe3147e64 100644 --- a/haskell/private/haskell_impl.bzl +++ b/haskell/private/haskell_impl.bzl @@ -23,6 +23,7 @@ load( ) load(":private/actions/package.bzl", "package") load(":private/actions/runghc.bzl", "build_haskell_runghc") +load(":private/cc_libraries.bzl", "merge_cc_shared_library_infos") load(":private/context.bzl", "haskell_context") load(":private/dependencies.bzl", "gather_dep_info") load(":private/expansions.bzl", "haskell_library_expand_make_variables") @@ -710,10 +711,15 @@ def haskell_library_impl(ctx): ), ] + [dep[CcInfo] for dep in deps if CcInfo in dep], ) + out_cc_shared_library_info = merge_cc_shared_library_infos( + owner = ctx.label, + cc_shared_library_infos = [dep[CcSharedLibraryInfo] for dep in deps if CcSharedLibraryInfo in dep], + ) return [ hs_info, out_cc_info, + out_cc_shared_library_info, coverage_info, default_info, lib_info, diff --git a/haskell/private/versions.bzl b/haskell/private/versions.bzl index 54f3f5393..ab6e1ff7e 100644 --- a/haskell/private/versions.bzl +++ b/haskell/private/versions.bzl @@ -17,10 +17,12 @@ SUPPORTED_BAZEL_VERSIONS = [ "6.0.0", "6.3.2", "6.5.0", + "7.1.0", ] SUPPORTED_NIXPKGS_BAZEL_PACKAGES = [ "bazel_6", + "bazel_7", ] def _parse_version_chunk(version_chunk): diff --git a/haskell/protobuf.bzl b/haskell/protobuf.bzl index a9779a8ae..c1cc4adc6 100644 --- a/haskell/protobuf.bzl +++ b/haskell/protobuf.bzl @@ -215,7 +215,7 @@ def _haskell_proto_aspect_impl(target, ctx): # TODO this pattern match is very brittle. Let's not do this. The # order should match the order in the return value expression in # haskell_library_impl(). - [hs_info, cc_info, _coverage_info, default_info, library_info, output_groups] = _haskell_library_impl(patched_ctx) + [hs_info, cc_info, cc_shared_library_info, _coverage_info, default_info, library_info, output_groups] = _haskell_library_impl(patched_ctx) # Build haddock informations transitive_html = {} @@ -255,11 +255,13 @@ def _haskell_proto_aspect_impl(target, ctx): for dep in getattr(ctx.rule.attr, attr, []) ]), cc_info = cc_info, + cc_shared_info = cc_shared_library_info, is_haskell = True, ) return [ cc_info, # CcInfo + cc_shared_library_info, # CcSharedLibraryInfo hs_info, # HaskellInfo library_info, # HaskellLibraryInfo # We can't return DefaultInfo here because target already provides that. diff --git a/haskell/repl.bzl b/haskell/repl.bzl index ffecef79a..f768c07a3 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -14,6 +14,7 @@ load( "haskell_cc_libraries_aspect", "link_libraries", "merge_HaskellCcLibrariesInfo", + "merge_cc_shared_library_infos", ) load(":private/context.bzl", "haskell_context", "render_env") load(":private/expansions.bzl", "expand_make_variables") @@ -48,6 +49,7 @@ HaskellReplLoadInfo = provider( "import_dirs": "Depset of Haskell import directories.", "cc_libraries_info": "HaskellCcLibrariesInfo of transitive C dependencies.", "cc_info": "CcInfo of transitive C dependencies.", + "cc_shared_library_infos": "CcSharedLibraryInfo providers of transitive C dependencies.", "compiler_flags": "Flags to pass to the Haskell compiler.", "repl_ghci_args": "Arbitrary extra arguments to pass to GHCi. This extends `compiler_flags` and `repl_ghci_args` from the toolchain", "data_runfiles": "Runtime data dependencies of this target, i.e. the files and runfiles of the `data` attribute.", @@ -68,6 +70,7 @@ HaskellReplDepInfo = provider( "interface_dirs": "Set of interface dirs for all the dependencies", "cc_libraries_info": "HaskellCcLibrariesInfo of transitive C dependencies.", "cc_info": "CcInfo of the package itself (includes its transitive dependencies).", + "cc_shared_library_infos": "CcSharedLibraryInfo providers of transitive C dependencies.", "runfiles": "Runfiles of this target.", }, ) @@ -146,6 +149,7 @@ def _merge_HaskellReplLoadInfo(load_infos): import_dirs = depset() cc_libraries_infos = [] cc_infos = [] + cc_shared_library_infos = [] compiler_flags = [] repl_ghci_args = [] data_runfiles = [] @@ -158,6 +162,7 @@ def _merge_HaskellReplLoadInfo(load_infos): import_dirs = depset(transitive = [import_dirs, load_info.import_dirs]) cc_libraries_infos.append(load_info.cc_libraries_info) cc_infos.append(load_info.cc_info) + cc_shared_library_infos.extend(load_info.cc_shared_library_infos) compiler_flags += load_info.compiler_flags repl_ghci_args += load_info.repl_ghci_args data_runfiles.append(load_info.data_runfiles) @@ -171,6 +176,7 @@ def _merge_HaskellReplLoadInfo(load_infos): import_dirs = import_dirs, cc_libraries_info = merge_HaskellCcLibrariesInfo(infos = cc_libraries_infos), cc_info = cc_common.merge_cc_infos(cc_infos = cc_infos), + cc_shared_library_infos = cc_shared_library_infos, compiler_flags = compiler_flags, repl_ghci_args = repl_ghci_args, data_runfiles = _merge_runfiles(data_runfiles), @@ -180,11 +186,13 @@ def _merge_HaskellReplLoadInfo(load_infos): def _merge_HaskellReplLoadInfoMulti(root_info, load_infos): cc_libraries_infos = [] cc_infos = [] + cc_shared_library_infos = [] data_runfiles = [] java_deps = [] for load_info in load_infos: cc_libraries_infos.append(load_info.cc_libraries_info) cc_infos.append(load_info.cc_info) + cc_shared_library_infos.extend(load_info.cc_shared_library_infos) data_runfiles.append(load_info.data_runfiles) java_deps.append(load_info.java_deps) @@ -197,6 +205,7 @@ def _merge_HaskellReplLoadInfoMulti(root_info, load_infos): import_dirs = root_info.import_dirs, cc_libraries_info = merge_HaskellCcLibrariesInfo(infos = cc_libraries_infos), cc_info = cc_common.merge_cc_infos(cc_infos = cc_infos), + cc_shared_library_infos = cc_shared_library_infos, compiler_flags = root_info.compiler_flags, repl_ghci_args = root_info.repl_ghci_args, data_runfiles = _merge_runfiles(data_runfiles), @@ -209,6 +218,7 @@ def _merge_HaskellReplDepInfo(dep_infos, dep_infos_for_package_dbs = []): interface_dirs = depset() cc_libraries_infos = [] cc_infos = [] + cc_shared_library_infos = [] runfiles = [] for dep_info in dep_infos: @@ -217,6 +227,7 @@ def _merge_HaskellReplDepInfo(dep_infos, dep_infos_for_package_dbs = []): interface_dirs = depset(transitive = [interface_dirs, dep_info.interface_dirs]) cc_libraries_infos.append(dep_info.cc_libraries_info) cc_infos.append(dep_info.cc_info) + cc_shared_library_infos.extend(dep_info.cc_shared_library_infos) runfiles.append(dep_info.runfiles) for dep_info in dep_infos_for_package_dbs: @@ -229,6 +240,7 @@ def _merge_HaskellReplDepInfo(dep_infos, dep_infos_for_package_dbs = []): interface_dirs = interface_dirs, cc_libraries_info = merge_HaskellCcLibrariesInfo(infos = cc_libraries_infos), cc_info = cc_common.merge_cc_infos(cc_infos = cc_infos), + cc_shared_library_infos = cc_shared_library_infos, runfiles = _merge_runfiles(runfiles), ) @@ -260,6 +272,11 @@ def _create_HaskellReplCollectInfo(target, dep_labels, dep_package_ids, dep_pack for dep in getattr(ctx.rule.attr, "deps", []) + getattr(ctx.rule.attr, "narrowed_deps", []) if CcInfo in dep and not HaskellInfo in dep ] + ccSharedLibraryInfoDeps = [ + dep + for dep in getattr(ctx.rule.attr, "deps", []) + getattr(ctx.rule.attr, "narrowed_deps", []) + if CcSharedLibraryInfo in dep and not HaskellInfo in dep + ] if HaskellLibraryInfo in target: package_id = target[HaskellLibraryInfo].package_id load_info = HaskellReplLoadInfo( @@ -269,12 +286,19 @@ def _create_HaskellReplCollectInfo(target, dep_labels, dep_package_ids, dep_pack boot_files = hs_info.boot_files, module_names = hs_info.module_names, import_dirs = set.to_depset(hs_info.import_dirs), - cc_libraries_info = deps_HaskellCcLibrariesInfo(ccInfoDeps), + cc_libraries_info = deps_HaskellCcLibrariesInfo(ccInfoDeps + ccSharedLibraryInfoDeps), cc_info = cc_common.merge_cc_infos(cc_infos = [ # Collect pure C library dependencies, no Haskell dependencies. dep[CcInfo] for dep in ccInfoDeps ]), + cc_shared_library_infos = [merge_cc_shared_library_infos( + owner = ctx.label, + cc_shared_library_infos = [ + dep[CcSharedLibraryInfo] + for dep in ccSharedLibraryInfoDeps + ], + )], compiler_flags = hs_info.user_compile_flags, repl_ghci_args = hs_info.user_repl_flags, data_runfiles = _data_runfiles(ctx, ctx.rule, "data"), @@ -290,6 +314,7 @@ def _create_HaskellReplCollectInfo(target, dep_labels, dep_package_ids, dep_pack interface_dirs = hs_info.interface_dirs, cc_libraries_info = target[HaskellCcLibrariesInfo], cc_info = target[CcInfo], + cc_shared_library_infos = [target[CcSharedLibraryInfo]] if CcSharedLibraryInfo in target else [], runfiles = target[DefaultInfo].default_runfiles, ) dep_infos[target.label] = dep_info @@ -492,7 +517,9 @@ def _compiler_flags_and_inputs(hs, cc, repl_info, get_dirname, static = False, i repl_info.load_info.cc_info, repl_info.dep_info.cc_info, ]) - all_libraries = [lib for li in cc_info.linking_context.linker_inputs.to_list() for lib in li.libraries] + cc_shared_library_infos = repl_info.load_info.cc_shared_library_infos + repl_info.dep_info.cc_shared_library_infos + linker_inputs = cc_info.linking_context.linker_inputs.to_list() + [info.linker_input for info in cc_shared_library_infos] + all_libraries = [lib for li in linker_inputs for lib in li.libraries] cc_libraries = get_cc_libraries(cc_libraries_info, all_libraries) if static: cc_library_files = _concat(get_library_files(hs, cc_libraries_info, cc_libraries)) diff --git a/rules_haskell_tests/MODULE.bazel b/rules_haskell_tests/MODULE.bazel index bc5d0d660..56f965761 100644 --- a/rules_haskell_tests/MODULE.bazel +++ b/rules_haskell_tests/MODULE.bazel @@ -113,9 +113,11 @@ use_repo( non_modules_deps_1, "asterius_bundle_linux_amd64", "bazel_6", + "bazel_7", "build_bazel_bazel_6_0_0", "build_bazel_bazel_6_3_2", "build_bazel_bazel_6_5_0", + "build_bazel_bazel_7_1_0", "glibc_locales", "linux_amd64_asterius-toolchain", "nixpkgs_config_cc", diff --git a/rules_haskell_tests/tests/recompilation/recompilation_workspace/WORKSPACE b/rules_haskell_tests/tests/recompilation/recompilation_workspace/WORKSPACE index a9040a428..05498eb59 100644 --- a/rules_haskell_tests/tests/recompilation/recompilation_workspace/WORKSPACE +++ b/rules_haskell_tests/tests/recompilation/recompilation_workspace/WORKSPACE @@ -21,3 +21,10 @@ haskell_register_ghc_nixpkgs( load("@rules_haskell//haskell:toolchain.bzl", "rules_haskell_toolchains") rules_haskell_toolchains(version = "8.10.7") + +load("@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl", "nixpkgs_cc_configure") + +nixpkgs_cc_configure( + name = "nixpkgs_config_cc", + repository = "@rules_haskell//nixpkgs:default.nix", +) diff --git a/rules_haskell_tests/tests/repl-targets/hs_lib_repl_test/BUILD.bazel b/rules_haskell_tests/tests/repl-targets/hs_lib_repl_test/BUILD.bazel index e35c8ef48..dea4818d8 100644 --- a/rules_haskell_tests/tests/repl-targets/hs_lib_repl_test/BUILD.bazel +++ b/rules_haskell_tests/tests/repl-targets/hs_lib_repl_test/BUILD.bazel @@ -4,6 +4,7 @@ load( "haskell_library", "haskell_toolchain_library", ) +load(":maybe_cc_shared_library.bzl", "maybe_cc_shared_library") [ haskell_toolchain_library(name = name) @@ -42,6 +43,8 @@ cc_library( visibility = ["//visibility:public"], ) +library_name = maybe_cc_shared_library(name = "ourclibrary") + config_setting( name = "nix", constraint_values = [ @@ -72,7 +75,7 @@ haskell_library( deps = [ ":array", ":base", - ":ourclibrary", ":zlib", + library_name, ], ) diff --git a/rules_haskell_tests/tests/repl-targets/hs_lib_repl_test/WORKSPACE b/rules_haskell_tests/tests/repl-targets/hs_lib_repl_test/WORKSPACE index 04153e6a2..4f705828f 100644 --- a/rules_haskell_tests/tests/repl-targets/hs_lib_repl_test/WORKSPACE +++ b/rules_haskell_tests/tests/repl-targets/hs_lib_repl_test/WORKSPACE @@ -140,3 +140,14 @@ stack_snapshot( register_toolchains( ":c2hs-toolchain", ) + +http_archive( + name = "bazel_features", + sha256 = "ba1282c1aa1d1fffdcf994ab32131d7c7551a9bc960fbf05f42d55a1b930cbfb", + strip_prefix = "bazel_features-1.15.0", + url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.15.0/bazel_features-v1.15.0.tar.gz", +) + +load("@bazel_features//:deps.bzl", "bazel_features_deps") + +bazel_features_deps() diff --git a/rules_haskell_tests/tests/repl-targets/hs_lib_repl_test/maybe_cc_shared_library.bzl b/rules_haskell_tests/tests/repl-targets/hs_lib_repl_test/maybe_cc_shared_library.bzl new file mode 100644 index 000000000..a738c3b6d --- /dev/null +++ b/rules_haskell_tests/tests/repl-targets/hs_lib_repl_test/maybe_cc_shared_library.bzl @@ -0,0 +1,17 @@ +load("@bazel_features//:features.bzl", "bazel_features") + +def maybe_cc_shared_library(name, **kwargs): + if _has_cc_shared_library(): + shared_name = "%s_shared" % name + native.cc_shared_library( + name = shared_name, + deps = [name], + **kwargs, + ) + return shared_name + return name + + + +def _has_cc_shared_library(): + return bazel_features.globals.CcSharedLibraryInfo != None