From f99591caf1bbc59d224c949de7f3d58b29b62baa Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Thu, 28 Apr 2022 12:51:04 +0300 Subject: [PATCH 01/12] Clarify set vs depset in some docs --- haskell/providers.bzl | 2 +- haskell/repl.bzl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/haskell/providers.bzl b/haskell/providers.bzl index 7c43f0d45..eb040460e 100644 --- a/haskell/providers.bzl +++ b/haskell/providers.bzl @@ -6,7 +6,7 @@ HaskellInfo = provider( "package_databases": "Depset of package cache files.", "empty_lib_package_databases": "Depset of package cache files corresponding to empty libraries.", "version_macros": "Depset of version macro files.", - "import_dirs": "Import hierarchy roots.", + "import_dirs": "Set of import hierarchy roots.", "source_files": "Depset of files that contain Haskell modules.", "boot_files": "Depset of Haskell boot files", "extra_source_files": "Depset of non-Haskell source files.", diff --git a/haskell/repl.bzl b/haskell/repl.bzl index 6cc44499e..a5fd63682 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -41,9 +41,9 @@ HaskellReplLoadInfo = provider( Information to a Haskell target to load into the REPL as source. """, fields = { - "source_files": "Set of files that contain Haskell modules.", - "boot_files": "Set of Haskell boot files.", - "import_dirs": "Set of Haskell import directories.", + "source_files": "Depset of files that contain Haskell modules.", + "boot_files": "Depset of Haskell boot files.", + "import_dirs": "Depset of Haskell import directories.", "cc_libraries_info": "HaskellCcLibrariesInfo of transitive C dependencies.", "cc_info": "CcInfo of transitive C dependencies.", "compiler_flags": "Flags to pass to the Haskell compiler.", From a7daf4993d23a358965452ab4cac82d6f47ed9b9 Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Thu, 28 Apr 2022 12:55:05 +0300 Subject: [PATCH 02/12] Implement repl support for targets with modules --- haskell/experimental/defs.bzl | 1 + haskell/experimental/private/module.bzl | 53 ++++++++++++++++++++++--- haskell/private/haskell_impl.bzl | 16 ++++---- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/haskell/experimental/defs.bzl b/haskell/experimental/defs.bzl index ddda68a61..2d30dd4fd 100644 --- a/haskell/experimental/defs.bzl +++ b/haskell/experimental/defs.bzl @@ -7,6 +7,7 @@ load( ) load("//haskell:private/cc_libraries.bzl", "haskell_cc_libraries_aspect") +# TODO[GL] should we have repl_ghci_args here? _haskell_module = rule( _haskell_module_impl, # NOTE: Documentation needs to be added to the wrapper macros below. diff --git a/haskell/experimental/private/module.bzl b/haskell/experimental/private/module.bzl index 7c7c0f908..fa2d5b657 100644 --- a/haskell/experimental/private/module.bzl +++ b/haskell/experimental/private/module.bzl @@ -22,6 +22,7 @@ load( "//haskell:private/plugins.bzl", "resolve_plugin_tools", ) +load("//haskell:private/set.bzl", "set") load( "//haskell:providers.bzl", "GhcPluginInfo", @@ -109,7 +110,7 @@ def _build_haskell_module( narrowed_objects, extra_ldflags_file, module): - """Build a module + """Build a module. Returns data needed by haskell_repl. Args: ctx: The context of the binary, library, or test rule using the module @@ -130,6 +131,13 @@ def _build_haskell_module( narrowed_objects: A depset containing the narrowed object files needed as arguments to ghc. extra_ldflags_file: A File with flags for ld or None. module: The Target of the haskell_module rule + + Returns: + struct(source_file, boot_file, import_dir, user_compile_flags): + source_file: The file that contains the Haskell module. None if it's a boot file. + boot_file: The file that contains the boot Haskell module. None if it's not a boot file. + import_dir: A possibly (e.g. due to hsc) newly generated import directory. + user_compile_flags: Compiler flags specified by the user in this module after location expansion. """ moduleAttr = module[HaskellModuleInfo].attr @@ -148,15 +156,17 @@ def _build_haskell_module( moduleAttr.tools, ] - user_ghcopts += expand_make_variables("ghcopts", ctx, moduleAttr.ghcopts, module_extra_attrs) + user_compile_flags = expand_make_variables("ghcopts", ctx, moduleAttr.ghcopts, module_extra_attrs) + user_ghcopts += user_compile_flags + import_dir = None if src.extension == "hsc": # TODO[AH] Support version macros hsc_flags, hsc_inputs = preprocess_hsc_flags_and_inputs(dep_info, user_ghcopts, None) - # TODO[GL] Use idir when providing HaskellReplLoadInfo hs_out, idir = process_hsc_file(hs, cc, hsc_flags, hsc_inputs, src) src = hs_out + import_dir = idir # Note [Plugin order] plugin_decl = reversed(ctx.attr.plugins + moduleAttr.plugins) @@ -342,6 +352,14 @@ def _build_haskell_module( extra_name = module.label.package.replace("/", "_") + "_" + module.label.name, ) + is_boot = _is_boot(src.path) + return struct( + source_file = None if is_boot else src, + boot_file = src if is_boot else None, + import_dir = import_dir, + user_compile_flags = user_compile_flags, + ) + def get_module_path_from_target(module): module_name = module[HaskellModuleInfo].attr.module_name if module_name: @@ -358,11 +376,14 @@ def get_module_path_from_target(module): return paths.split_extension(paths.relativize(src, prefix_path))[0] +def _is_boot(path): + return paths.split_extension(path)[1] in [".hs-boot", ".lhs-boot"] + def _declare_module_outputs(hs, with_profiling, with_shared, hidir, odir, module): module_path = get_module_path_from_target(module) src = module[HaskellModuleInfo].attr.src.files.to_list()[0].path - hs_boot = paths.split_extension(src)[1] in [".hs-boot", ".lhs-boot"] + hs_boot = _is_boot(src) extension_template = "%s" if hs_boot: extension_template = extension_template + "-boot" @@ -555,7 +576,7 @@ def build_haskell_modules( odir: The directory in which to output object files Returns: - struct(his, dyn_his, os, dyn_os, per_module_transitive_interfaces, per_module_transitive_objects, per_module_transitive_dyn_objects): + struct(his, dyn_his, os, dyn_os, per_module_transitive_interfaces, per_module_transitive_objects, per_module_transitive_dyn_objects, repl_info): his: interface files of all modules in ctx.attr.modules dyn_his: dynamic interface files of all modules in ctx.attr.modules os: object files of all modules in ctx.attr.modules @@ -567,6 +588,7 @@ def build_haskell_modules( object files and the object files of their transitive module dependencies. See Note [Narrowed Dependencies]. per_module_transitive_dyn_objects: like per_module_transitive_objects but for dyn_o files + repl_info: struct(source_files, boot_files, import_dirs, user_compile_flags) """ per_module_maps = _merge_narrowed_deps_dicts(ctx.label, ctx.attr.narrowed_deps) @@ -584,6 +606,12 @@ def build_haskell_modules( module_interfaces = {} module_objects = {} module_dyn_objects = {} + + source_files = [] + boot_files = [] + import_dirs = [] + user_compile_flags = [] + for dep in transitive_module_deps: # called in all cases to validate cross_library_deps, although the output # might be ignored when disabling narrowing @@ -611,7 +639,7 @@ def build_haskell_modules( _collect_module_inputs(module_dyn_objects, narrowed_objects, dyn_os, dep), ]) - _build_haskell_module( + module_output = _build_haskell_module( ctx, hs, cc, @@ -631,6 +659,13 @@ def build_haskell_modules( extra_ldflags_file, dep, ) + if module_output.source_file: + source_files.append(module_output.source_file) + if module_output.boot_file: + boot_files.append(module_output.boot_file) + if module_output.import_dir: + import_dirs.append(module_output.import_dir) + user_compile_flags += module_output.user_compile_flags module_outputs_list = module_outputs.values() hi_set = depset([outputs.hi for outputs in module_outputs_list]) @@ -676,6 +711,12 @@ def build_haskell_modules( per_module_transitive_interfaces = per_module_transitive_interfaces0, per_module_transitive_objects = per_module_transitive_objects0, per_module_transitive_dyn_objects = per_module_transitive_dyn_objects0, + repl_info = struct( + source_files = depset(source_files), + boot_files = depset(boot_files), + import_dirs = set.from_list(import_dirs), + user_compile_flags = user_compile_flags, + ), ) def haskell_module_impl(ctx): diff --git a/haskell/private/haskell_impl.bzl b/haskell/private/haskell_impl.bzl index 54c9a692c..9adf15011 100644 --- a/haskell/private/haskell_impl.bzl +++ b/haskell/private/haskell_impl.bzl @@ -286,16 +286,16 @@ def _haskell_binary_common_impl(ctx, is_test): hs_info = HaskellInfo( package_databases = all_deps_info.package_databases, version_macros = set.empty(), - source_files = c.source_files, - boot_files = c.boot_files, + source_files = depset(transitive = [c.source_files, module_outputs.repl_info.source_files]), + boot_files = depset(transitive = [c.boot_files, module_outputs.repl_info.boot_files]), extra_source_files = c.extra_source_files, - import_dirs = c.import_dirs, + import_dirs = set.mutable_union(c.import_dirs, module_outputs.repl_info.import_dirs), hs_libraries = all_deps_info.hs_libraries, deps_hs_libraries = all_deps_info.deps_hs_libraries, interface_dirs = all_deps_info.interface_dirs, deps_interface_dirs = all_deps_info.deps_interface_dirs, compile_flags = c.compile_flags, - user_compile_flags = user_compile_flags, + user_compile_flags = user_compile_flags + module_outputs.repl_info.user_compile_flags, user_repl_flags = haskell_library_expand_make_variables("repl_ghci_args", ctx, ctx.attr.repl_ghci_args), ) cc_info = cc_common.merge_cc_infos( @@ -616,10 +616,10 @@ def haskell_library_impl(ctx): order = "preorder", ), version_macros = version_macros, - source_files = c.source_files, - boot_files = c.boot_files, + source_files = depset(transitive = [c.source_files, module_outputs.repl_info.source_files]), + boot_files = depset(transitive = [c.boot_files, module_outputs.repl_info.boot_files]), extra_source_files = c.extra_source_files, - import_dirs = set.mutable_union(c.import_dirs, export_infos.import_dirs), + import_dirs = set.mutable_union(c.import_dirs, set.mutable_union(export_infos.import_dirs, module_outputs.repl_info.import_dirs)), hs_libraries = depset( direct = [lib for lib in [static_library, dynamic_library] if lib], transitive = [all_deps_info.hs_libraries], @@ -634,7 +634,7 @@ def haskell_library_impl(ctx): interface_dirs = depset(transitive = [interface_dirs, export_infos.interface_dirs]), deps_interface_dirs = depset(transitive = [dep_info.interface_dirs, narrowed_deps_info.deps_interface_dirs]), compile_flags = c.compile_flags, - user_compile_flags = user_compile_flags, + user_compile_flags = user_compile_flags + module_outputs.repl_info.user_compile_flags, user_repl_flags = haskell_library_expand_make_variables("repl_ghci_args", ctx, ctx.attr.repl_ghci_args), per_module_transitive_interfaces = module_outputs.per_module_transitive_interfaces, per_module_transitive_objects = module_outputs.per_module_transitive_objects, From 3d5a97674da98fbd31c619ac45ad7c1bd87c3f0d Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Wed, 4 May 2022 15:02:07 +0300 Subject: [PATCH 03/12] Add a test for haskell_repl with haskell_modules --- tests/haskell_module/BUILD.bazel | 1 + tests/haskell_module/repl/BUILD.bazel | 21 +++ .../repl/haskell_module_repl_test.go | 171 ++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 tests/haskell_module/repl/BUILD.bazel create mode 100644 tests/haskell_module/repl/haskell_module_repl_test.go diff --git a/tests/haskell_module/BUILD.bazel b/tests/haskell_module/BUILD.bazel index 751a5d688..7f032bba0 100644 --- a/tests/haskell_module/BUILD.bazel +++ b/tests/haskell_module/BUILD.bazel @@ -15,6 +15,7 @@ filegroup( "//tests/haskell_module/th:all_files", "//tests/haskell_module/tools:all_files", "//tests/haskell_module/hsc:all_files", + "//tests/haskell_module/repl:all_files", ], visibility = ["//visibility:public"], ) diff --git a/tests/haskell_module/repl/BUILD.bazel b/tests/haskell_module/repl/BUILD.bazel new file mode 100644 index 000000000..bc5563841 --- /dev/null +++ b/tests/haskell_module/repl/BUILD.bazel @@ -0,0 +1,21 @@ +load("//tests:integration_tests.bzl", "integration_test") + +package(default_testonly = 1) + +integration_test( + name = "haskell_module_repl_test", + size = "small", + bazel = "//tests:bazel", + tags = [ + # See https://github.com/tweag/rules_haskell/issues/1486 + "dont_test_on_darwin_with_bindist", + "dont_test_on_windows", + ], +) + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +) diff --git a/tests/haskell_module/repl/haskell_module_repl_test.go b/tests/haskell_module/repl/haskell_module_repl_test.go new file mode 100644 index 000000000..4dba9002c --- /dev/null +++ b/tests/haskell_module/repl/haskell_module_repl_test.go @@ -0,0 +1,171 @@ +package haskell_module_repl_test + +import ( + bt "github.com/bazelbuild/rules_go/go/tools/bazel_testing" + it "github.com/tweag/rules_haskell/tests/integration_testing" + "testing" +) + +var testcase = ` +-- WORKSPACE -- +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +local_repository( + name = "rules_haskell", + path = "../rules_haskell", +) + +load("@rules_haskell//haskell:repositories.bzl", "rules_haskell_dependencies") +load("@rules_haskell//tools:os_info.bzl", "os_info") + +os_info(name = "os_info") + +load("@os_info//:os_info.bzl", "is_windows") + +rules_haskell_dependencies() +load("@rules_haskell//haskell:nixpkgs.bzl", "haskell_register_ghc_nixpkgs") + +haskell_register_ghc_nixpkgs( + attribute_path = "haskell.compiler.ghc8107", + repository = "@rules_haskell//nixpkgs:default.nix", + version = "8.10.7", +) + +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_python_configure", + "nixpkgs_local_repository", + "nixpkgs_package", +) + +nixpkgs_cc_configure( + name = "nixpkgs_config_cc", + repository = "@rules_haskell//nixpkgs:default.nix", +) + +nixpkgs_python_configure( + repository = "@rules_haskell//nixpkgs:default.nix", +) + +nixpkgs_local_repository( + name = "nixpkgs_default", + nix_file = "@rules_haskell//nixpkgs:default.nix", +) + +load("@rules_haskell//haskell:cabal.bzl", "stack_snapshot") + +stack_snapshot( + name = "stackage", + components = {}, + local_snapshot = "@rules_haskell//:stackage_snapshot.yaml", + packages = ["base"], + stack_snapshot_json = "@rules_haskell//:stackage_snapshot.json" if not is_windows else None, + tools = [], + vendored_packages = { + "ghc-paths": "@rules_haskell//tools/ghc-paths", + }, +) +-- BUILD.bazel -- +"""Test compilation of a multiple interdependent Haskell modules with only core-package dependencies.""" + +load("@rules_haskell//haskell:defs.bzl", "haskell_library", "haskell_repl", "haskell_toolchain_library") +load("@rules_haskell//haskell/experimental:defs.bzl", "haskell_module") + +haskell_toolchain_library(name = "base") + +haskell_repl( + name = "repl", + deps = [":lib"], +) + +haskell_module( + name = "root", + src = "Root.hs", +) + +haskell_module( + name = "branch_left", + src = "BranchLeft.hs", + deps = [ + ":root", + ], +) + +haskell_module( + name = "branch_right", + src = "BranchRight.hs", + deps = [ + ":root", + ], +) + +haskell_module( + name = "leaf", + src = "Leaf.hs", + deps = [ + ":branch_left", + ":branch_right", + ], +) + +haskell_library( + name = "lib", + modules = [ + ":root", + ":branch_left", + ":branch_right", + ":leaf", + ], + deps = [":base"], +) + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +) +-- BranchLeft.hs -- +module BranchLeft where + +import Root + +branch_left :: Int +branch_left = 3 * root +-- BranchRight.hs -- +module BranchRight where + +import Root + +branch_right :: Int +branch_right = 5 * root +-- Leaf.hs -- +module Leaf where + +import BranchLeft +import BranchRight + +leaf :: Int +leaf = 7 * branch_left * branch_right +-- Root.hs -- +module Root where + +root :: Int +root = 2 +` + +func TestMain(m *testing.M) { + it.TestMain(m, bt.Args{Main: testcase}) +} + +func TestHsModRepl(t *testing.T) { + out, err := it.BazelOutput(it.Context.BazelBinary, "run", "//:repl", "--", "-ignore-dot-ghci", "-e", "leaf") + if err != nil { + t.Fatal(err) + } + it.AssertOutput(t, out, "420\n") +} From a1f27bdbae1395262d0bdb0aa6dd13e370caf28e Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Wed, 4 May 2022 15:14:31 +0300 Subject: [PATCH 04/12] Improve docs for repl_info --- haskell/experimental/private/module.bzl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/haskell/experimental/private/module.bzl b/haskell/experimental/private/module.bzl index fa2d5b657..f8c8b99df 100644 --- a/haskell/experimental/private/module.bzl +++ b/haskell/experimental/private/module.bzl @@ -561,7 +561,8 @@ def build_haskell_modules( hidir, odir): """ Build all the modules of haskell_module rules in ctx.attr.modules - and in their dependencies + and in their dependencies. The repl_info returned here aggregates data + from all the haskell_modules being run here. Args: ctx: The context of the rule with module dependencies @@ -588,7 +589,11 @@ def build_haskell_modules( object files and the object files of their transitive module dependencies. See Note [Narrowed Dependencies]. per_module_transitive_dyn_objects: like per_module_transitive_objects but for dyn_o files - repl_info: struct(source_files, boot_files, import_dirs, user_compile_flags) + repl_info: struct(source_files, boot_files, import_dirs, user_compile_flags): + source_files: Depset of files that contain Haskell modules. + boot_files: Depset of files that contain Haskell boot modules. + import_dirs: Set of newly generated import directories. hsc2hs generates these. + user_compile_flags: Compiler flags specified by the user, after location expansion. """ per_module_maps = _merge_narrowed_deps_dicts(ctx.label, ctx.attr.narrowed_deps) From 4cc50a15603ce6c6afa6470267e4eba554da65dc Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Thu, 5 May 2022 22:21:26 +0300 Subject: [PATCH 05/12] Delete redundant filegroup from test --- tests/haskell_module/repl/haskell_module_repl_test.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/haskell_module/repl/haskell_module_repl_test.go b/tests/haskell_module/repl/haskell_module_repl_test.go index 4dba9002c..93004d198 100644 --- a/tests/haskell_module/repl/haskell_module_repl_test.go +++ b/tests/haskell_module/repl/haskell_module_repl_test.go @@ -122,13 +122,6 @@ haskell_library( ], deps = [":base"], ) - -filegroup( - name = "all_files", - testonly = True, - srcs = glob(["**"]), - visibility = ["//visibility:public"], -) -- BranchLeft.hs -- module BranchLeft where From cc69c2e6d9cf9833bcf182753a35d660633ca8c4 Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Thu, 26 May 2022 16:33:36 +0300 Subject: [PATCH 06/12] Propagate repl aspect along narrowed_deps --- haskell/private/cc_libraries.bzl | 2 +- haskell/repl.bzl | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/haskell/private/cc_libraries.bzl b/haskell/private/cc_libraries.bzl index 60f7ed589..5a78f7763 100644 --- a/haskell/private/cc_libraries.bzl +++ b/haskell/private/cc_libraries.bzl @@ -379,7 +379,7 @@ def _haskell_cc_libraries_aspect_impl(target, ctx): haskell_cc_libraries_aspect = aspect( implementation = _haskell_cc_libraries_aspect_impl, - attr_aspects = ["deps", "exports", "plugins"], + attr_aspects = ["deps", "narrowed_deps", "exports", "plugins"], provides = [HaskellCcLibrariesInfo], required_aspect_providers = [HaskellProtobufInfo], toolchains = [ diff --git a/haskell/repl.bzl b/haskell/repl.bzl index a5fd63682..243df1a9d 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -545,14 +545,22 @@ def _haskell_repl_aspect_impl(target, ctx): return [] target_info = _create_HaskellReplCollectInfo(target, ctx) + deps_infos = [] + if hasattr(ctx.rule.attr, "deps"): - deps_infos = [ + deps_infos += [ dep[HaskellReplCollectInfo] for dep in ctx.rule.attr.deps if HaskellReplCollectInfo in dep ] - else: - deps_infos = [] + + if hasattr(ctx.rule.attr, "narrowed_deps"): + deps_infos += [ + dep[HaskellReplCollectInfo] + for dep in ctx.rule.attr.narrowed_deps + if HaskellReplCollectInfo in dep + ] + collect_info = _merge_HaskellReplCollectInfo([target_info] + deps_infos) # This aspect currently does not generate an executable REPL script by @@ -563,7 +571,7 @@ def _haskell_repl_aspect_impl(target, ctx): haskell_repl_aspect = aspect( implementation = _haskell_repl_aspect_impl, - attr_aspects = ["deps"], + attr_aspects = ["deps", "narrowed_deps"], required_aspect_providers = [HaskellCcLibrariesInfo], doc = """\ Haskell REPL aspect. From 02ed7729dcf17ff24eb0bffbfee855521d62b125 Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Thu, 26 May 2022 16:58:26 +0300 Subject: [PATCH 07/12] Add unresolved question on narrowed_deps checking --- haskell/repl.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/haskell/repl.bzl b/haskell/repl.bzl index 243df1a9d..be3d0ed6d 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -182,6 +182,7 @@ def _create_HaskellReplCollectInfo(target, ctx): hs_info = target[HaskellInfo] + # TODO[GL]: do we need to also take into account narrowed_deps here, everywhere "deps" is checked? if not HaskellToolchainLibraryInfo in target: if hasattr(ctx.rule.attr, "deps"): java_deps = java_interop_info(ctx.rule.attr.deps).inputs From c329cb1970def77519f5e48db7c21ad708e71092 Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Thu, 26 May 2022 17:29:16 +0300 Subject: [PATCH 08/12] Add a repl module test for cross_library_deps Thanks @pranaysashank --- tests/haskell_module/repl/BUILD.bazel | 11 ++ ...ell_module_repl_cross_library_deps_test.go | 183 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 tests/haskell_module/repl/haskell_module_repl_cross_library_deps_test.go diff --git a/tests/haskell_module/repl/BUILD.bazel b/tests/haskell_module/repl/BUILD.bazel index bc5563841..9b92c9779 100644 --- a/tests/haskell_module/repl/BUILD.bazel +++ b/tests/haskell_module/repl/BUILD.bazel @@ -13,6 +13,17 @@ integration_test( ], ) +integration_test( + name = "haskell_module_repl_cross_library_deps_test", + size = "small", + bazel = "//tests:bazel", + tags = [ + # See https://github.com/tweag/rules_haskell/issues/1486 + "dont_test_on_darwin_with_bindist", + "dont_test_on_windows", + ], +) + filegroup( name = "all_files", testonly = True, diff --git a/tests/haskell_module/repl/haskell_module_repl_cross_library_deps_test.go b/tests/haskell_module/repl/haskell_module_repl_cross_library_deps_test.go new file mode 100644 index 000000000..b3d42ee93 --- /dev/null +++ b/tests/haskell_module/repl/haskell_module_repl_cross_library_deps_test.go @@ -0,0 +1,183 @@ +package haskell_module_repl_test + +import ( + bt "github.com/bazelbuild/rules_go/go/tools/bazel_testing" + it "github.com/tweag/rules_haskell/tests/integration_testing" + "testing" +) + +var testcase = ` +-- WORKSPACE -- +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +local_repository( + name = "rules_haskell", + path = "../rules_haskell", +) + +load("@rules_haskell//haskell:repositories.bzl", "rules_haskell_dependencies") +load("@rules_haskell//tools:os_info.bzl", "os_info") + +os_info(name = "os_info") + +load("@os_info//:os_info.bzl", "is_windows") + +rules_haskell_dependencies() +load("@rules_haskell//haskell:nixpkgs.bzl", "haskell_register_ghc_nixpkgs") + +haskell_register_ghc_nixpkgs( + attribute_path = "haskell.compiler.ghc8107", + repository = "@rules_haskell//nixpkgs:default.nix", + version = "8.10.7", +) + +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_python_configure", + "nixpkgs_local_repository", + "nixpkgs_package", +) + +nixpkgs_cc_configure( + name = "nixpkgs_config_cc", + repository = "@rules_haskell//nixpkgs:default.nix", +) + +nixpkgs_python_configure( + repository = "@rules_haskell//nixpkgs:default.nix", +) + +nixpkgs_local_repository( + name = "nixpkgs_default", + nix_file = "@rules_haskell//nixpkgs:default.nix", +) + +load("@rules_haskell//haskell:cabal.bzl", "stack_snapshot") + +stack_snapshot( + name = "stackage", + components = {}, + local_snapshot = "@rules_haskell//:stackage_snapshot.yaml", + packages = ["base"], + stack_snapshot_json = "@rules_haskell//:stackage_snapshot.json" if not is_windows else None, + tools = [], + vendored_packages = { + "ghc-paths": "@rules_haskell//tools/ghc-paths", + }, +) +-- package-a/BUILD.bazel -- +load("@rules_haskell//haskell/experimental:defs.bzl", "haskell_module") + +# Load rules_haskell rules. +load( + "@rules_haskell//haskell:defs.bzl", + "haskell_library", + "haskell_toolchain_library", +) + +package(default_visibility = ["//visibility:public"]) + +haskell_toolchain_library(name = "base") + + +# gazelle_haskell_modules:srcs: src/ +haskell_library( + name = "package-a", + ghcopts = [ + ], + modules = [ + ":package-a.PackageA.Mod1", + ":package-a.PackageA.Mod2", + ], + src_strip_prefix = "src", + deps = [ + ":base", + ], +) + +# rule generated by gazelle_haskell_modules +haskell_module( + name = "package-a.PackageA.Mod1", + src = "src/PackageA/Mod1.hs", + src_strip_prefix = "src", +) + +# rule generated by gazelle_haskell_modules +haskell_module( + name = "package-a.PackageA.Mod2", + src = "src/PackageA/Mod2.hs", + src_strip_prefix = "src", + deps = [":package-a.PackageA.Mod1"], +) + +-- package-a/src/PackageA/Mod1.hs -- +module PackageA.Mod1 where + +mod1num :: Int +mod1num = 2 +-- package-a/src/PackageA/Mod2.hs -- +module PackageA.Mod2 where + +import PackageA.Mod1 + +mod2num :: Int +mod2num = mod1num * 3 +-- package-b/BUILD.bazel -- +load("@rules_haskell//haskell/experimental:defs.bzl", "haskell_module") + +# Load rules_haskell rules. +load( + "@rules_haskell//haskell:defs.bzl", + "haskell_library", + "haskell_toolchain_library", +) + +package(default_visibility = ["//visibility:public"]) + +haskell_toolchain_library(name = "base") + +# gazelle_haskell_modules:srcs: src/ +haskell_library( + name = "package-b", + ghcopts = [ + ], + modules = [ + ":package-b.PackageB.Mod1", + ], + narrowed_deps = ["//package-a"], + src_strip_prefix = "src", + deps = + [":base"], +) + +# rule generated by gazelle_haskell_modules +haskell_module( + name = "package-b.PackageB.Mod1", + src = "src/PackageB/Mod1.hs", + cross_library_deps = ["//package-a:package-a.PackageA.Mod1"], + src_strip_prefix = "src", +) +-- package-b/src/PackageB/Mod1.hs -- +module PackageB.Mod1 (PackageB.Mod1.mod1num) where + +import qualified PackageA.Mod2 + +mod1num :: Int +mod1num = PackageA.Mod2.mod2num * 7 +` + +func TestMain(m *testing.M) { + it.TestMain(m, bt.Args{Main: testcase}) +} + +func TestHsModRepl(t *testing.T) { + out, err := it.BazelOutput(it.Context.BazelBinary, "run", "//package-b:package-b@repl", "--", "-ignore-dot-ghci", "-e", "mod1num") + if err != nil { + t.Fatal(err) + } + it.AssertOutput(t, out, "42\n") +} From e85b1232c456d4a0facc56606e962502243c3f43 Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Fri, 27 May 2022 12:08:46 +0300 Subject: [PATCH 09/12] Add a TODO to use provide and required_providers --- haskell/repl.bzl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/haskell/repl.bzl b/haskell/repl.bzl index be3d0ed6d..f850ebf48 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -542,6 +542,7 @@ def _create_hie_bios(hs, cc, posix, ctx, repl_info, path_prefix): return [OutputGroupInfo(hie_bios = [args_link])] def _haskell_repl_aspect_impl(target, ctx): + # TODO[GL]: try removing this and using required_providers, once we're on a newer version of bazel if HaskellInfo not in target: return [] @@ -570,6 +571,9 @@ def _haskell_repl_aspect_impl(target, ctx): return [collect_info] +# We don't have a provides field here, since we might not actually return HaskellReplCollectInfo, +# if the target doesn't have a HaskellInfo. +# TODO[GL]: try adding it once we can use required_providers. haskell_repl_aspect = aspect( implementation = _haskell_repl_aspect_impl, attr_aspects = ["deps", "narrowed_deps"], From 3f033793e6db4043fbeda56daec0249e7841c9b9 Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Mon, 30 May 2022 12:31:49 +0300 Subject: [PATCH 10/12] Refactor adding deps and narrowed_deps --- haskell/repl.bzl | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/haskell/repl.bzl b/haskell/repl.bzl index f850ebf48..d5da64cd3 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -547,21 +547,13 @@ def _haskell_repl_aspect_impl(target, ctx): return [] target_info = _create_HaskellReplCollectInfo(target, ctx) - deps_infos = [] - - if hasattr(ctx.rule.attr, "deps"): - deps_infos += [ - dep[HaskellReplCollectInfo] - for dep in ctx.rule.attr.deps - if HaskellReplCollectInfo in dep - ] - - if hasattr(ctx.rule.attr, "narrowed_deps"): - deps_infos += [ - dep[HaskellReplCollectInfo] - for dep in ctx.rule.attr.narrowed_deps - if HaskellReplCollectInfo in dep - ] + + deps_infos = [ + dep[HaskellReplCollectInfo] + for deps_field_name in ["deps", "narrowed_deps"] + for dep in getattr(ctx.rule.attr, deps_field_name, []) + if HaskellReplCollectInfo in dep + ] collect_info = _merge_HaskellReplCollectInfo([target_info] + deps_infos) From e308a119716475395180f3847733d47c69dc457f Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Mon, 30 May 2022 14:11:07 +0300 Subject: [PATCH 11/12] Also add java deps from narrowed_deps --- haskell/repl.bzl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/haskell/repl.bzl b/haskell/repl.bzl index d5da64cd3..b2dc6630f 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -182,12 +182,19 @@ def _create_HaskellReplCollectInfo(target, ctx): hs_info = target[HaskellInfo] - # TODO[GL]: do we need to also take into account narrowed_deps here, everywhere "deps" is checked? if not HaskellToolchainLibraryInfo in target: + java_deps_list = [] + if hasattr(ctx.rule.attr, "deps"): - java_deps = java_interop_info(ctx.rule.attr.deps).inputs - else: - java_deps = depset() + java_deps_list += [java_interop_info(ctx.rule.attr.deps).inputs] + + # TODO[GL]: add tests for the java deps in narrowed_deps + if hasattr(ctx.rule.attr, "narrowed_deps"): + java_deps_list += [java_interop_info(ctx.rule.attr.narrowed_deps).inputs] + + java_deps = depset(transitive = java_deps_list) + + # TODO[GL]: do we need to also take into account narrowed_deps here, everywhere "deps" is checked? load_infos[target.label] = HaskellReplLoadInfo( source_files = hs_info.source_files, boot_files = hs_info.boot_files, From 46440f845c7574b1bb54b0b8233aafdf071032e1 Mon Sep 17 00:00:00 2001 From: Georgi Lyubenov Date: Mon, 30 May 2022 14:14:19 +0300 Subject: [PATCH 12/12] Also add CcInfo deps from narrowed_deps --- haskell/repl.bzl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/haskell/repl.bzl b/haskell/repl.bzl index b2dc6630f..f3f9424b7 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -194,21 +194,21 @@ def _create_HaskellReplCollectInfo(target, ctx): java_deps = depset(transitive = java_deps_list) - # TODO[GL]: do we need to also take into account narrowed_deps here, everywhere "deps" is checked? + # TODO[GL]: add tests for CcInfo deps in narrowed_deps + ccInfoDeps = [ + dep + for dep in getattr(ctx.rule.attr, "deps", []) + getattr(ctx.rule.attr, "narrowed_deps", []) + if CcInfo in dep and not HaskellInfo in dep + ] load_infos[target.label] = HaskellReplLoadInfo( source_files = hs_info.source_files, boot_files = hs_info.boot_files, import_dirs = set.to_depset(hs_info.import_dirs), - cc_libraries_info = deps_HaskellCcLibrariesInfo([ - dep - for dep in getattr(ctx.rule.attr, "deps", []) - if CcInfo in dep and not HaskellInfo in dep - ]), + cc_libraries_info = deps_HaskellCcLibrariesInfo(ccInfoDeps), cc_info = cc_common.merge_cc_infos(cc_infos = [ # Collect pure C library dependencies, no Haskell dependencies. dep[CcInfo] - for dep in getattr(ctx.rule.attr, "deps", []) - if CcInfo in dep and not HaskellInfo in dep + for dep in ccInfoDeps ]), compiler_flags = hs_info.user_compile_flags, repl_ghci_args = hs_info.user_repl_flags,