diff --git a/README.md b/README.md index 40f9d3fc8..344111f90 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,6 @@ You'll need [Bazel >= 6.0][bazel-getting-started] installed. If you are on NixOS, skip to the [Nixpkgs](#Nixpkgs) section. -> [!NOTE] -> Bazel 8 users will need to add -> `common --noincompatible_disallow_ctx_resolve_tools` to `.bazelrc` - ### System dependencies Refer to the "Before you begin" section in [the documentation](docs/haskell.rst). diff --git a/haskell/c2hs.bzl b/haskell/c2hs.bzl index 73b98cefc..7cbd74fc9 100644 --- a/haskell/c2hs.bzl +++ b/haskell/c2hs.bzl @@ -31,7 +31,6 @@ def _c2hs_library_impl(ctx): ) args = hs.actions.args() c2hs = ctx.toolchains["@rules_haskell//haskell/c2hs:toolchain"].c2hs - c2hs_exe = ctx.toolchains["@rules_haskell//haskell/c2hs:toolchain"].c2hs_exe if len(ctx.files.srcs) != 1: fail("srcs field should contain exactly one file.") @@ -44,7 +43,7 @@ def _c2hs_library_impl(ctx): args.add_all([chs_file.path, "-o", hs_file.path]) args.add("-C-E") - args.add_all(["--cpp", cc.tools.cc]) + args.add_all(["--cpp", cc.tools.cc.executable.path]) args.add("-C-includeghcplatform.h") args.add("-C-includeghcversion.h") args.add_all(["-C" + x for x in cc.cpp_flags]) @@ -70,21 +69,16 @@ def _c2hs_library_impl(ctx): (version_macro_headers, version_macro_flags) = version_macro_includes(dep_info) args.add_all(["-C" + x for x in version_macro_flags]) - (inputs, input_manifests) = ctx.resolve_tools(tools = [c2hs]) - hs.actions.run_shell( inputs = depset(transitive = [ depset(cc.hdrs), depset([chs_file]), depset(dep_chi_files), - depset(cc.files), depset(hs.toolchain.bindir), depset(hs.toolchain.libdir), set.to_depset(version_macro_headers), - inputs, ]), - input_manifests = input_manifests, - tools = [hs.tools.ghc_pkg, c2hs_exe], + tools = [hs.tools.ghc_pkg, c2hs, cc.tools.cc.as_tool], outputs = [hs_file, chi_file], command = # cpp (called via c2hs) gets very unhappy if the mingw bin dir is @@ -92,7 +86,7 @@ def _c2hs_library_impl(ctx): ( """ export PATH=$PATH:{mingw_bin} - """.format(mingw_bin = paths.dirname(cc.tools.cc)) if hs.toolchain.is_windows else "" + """.format(mingw_bin = paths.dirname(cc.tools.cc.executable.path)) if hs.toolchain.is_windows else "" ) + """ # Include libdir in include path just like hsc2hs does. @@ -103,7 +97,7 @@ def _c2hs_library_impl(ctx): {c2hs} "${{include_dirs_args[@]}}" "$@" """.format( ghc_pkg = hs.tools.ghc_pkg.path, - c2hs = c2hs_exe.path, + c2hs = c2hs.executable.path, ), mnemonic = "HaskellC2Hs", arguments = [args], @@ -159,12 +153,10 @@ def _c2hs_toolchain_impl(ctx): return [ platform_common.ToolchainInfo( name = ctx.label.name, - # We have both c2hs which points to the target and c2hs_exe - # which points to the file. The former is used to collect - # runfiles while the latter is used to get the path to - # c2hs. - c2hs = ctx.attr.c2hs, - c2hs_exe = ctx.executable.c2hs, + # Get a FilesToRunProvider which can be used to insert all the + # necessary files for the tool into the environment of an action + # run. + c2hs = ctx.attr.c2hs[DefaultInfo].files_to_run, ), ] diff --git a/haskell/cabal.bzl b/haskell/cabal.bzl index a20204719..bc19f6566 100644 --- a/haskell/cabal.bzl +++ b/haskell/cabal.bzl @@ -125,17 +125,6 @@ main = defaultMain ) return setup -_CABAL_TOOLS = ["alex", "c2hs", "cpphs", "doctest", "happy"] - -def _cabal_tool_flag(tool): - """Return a --with-PROG=PATH flag if input is a recognized Cabal tool. None otherwise.""" - if tool.basename in _CABAL_TOOLS: - return "--with-{}={}".format(tool.basename, tool.path) - return None - -def _binary_paths(binaries): - return [binary.dirname for binary in binaries.to_list()] - def _concat(sequences): return [item for sequence in sequences for item in sequence] @@ -161,7 +150,7 @@ def _cabal_toolchain_info(hs, cc, workspace_name, runghc): hsc2hs = hs.tools.hsc2hs.path, runghc = runghc.path, ar = ar, - cc = cc.tools.cc, + cc = cc.tools.cc.executable.path, ld = cc.tools.ld, strip = cc.tools.strip, is_windows = hs.toolchain.is_windows, @@ -179,8 +168,6 @@ def _prepare_cabal_inputs( direct_cc_info, component, package_id, - tool_inputs, - tool_input_manifests, cabal, setup, setup_deps, @@ -252,7 +239,7 @@ def _prepare_cabal_inputs( env = dicts.add(hs.env, cc.env) env["PATH"] = join_path_list( hs.toolchain.is_windows, - _binary_paths(tool_inputs) + posix.paths + hs.tools_config.path_for_cabal, + posix.paths + hs.tools_config.path_for_cabal, ) if hs.toolchain.is_darwin: env["SDKROOT"] = "macosx" # See haskell/private/actions/link.bzl @@ -348,7 +335,6 @@ def _prepare_cabal_inputs( extra_ldflags_file = darwin_flags_for_linking_indirect_cc_deps(hs, cc, posix, hs.name, dynamic = True) # Redundant with _binary_paths() above, but better be explicit when we can. - path_args.extend([_cabal_tool_flag(tool_flag) for tool_flag in tool_inputs.to_list() if _cabal_tool_flag(tool_flag)]) repo_name = "_main" if generate_paths_module and label and label.repo_name: @@ -389,7 +375,6 @@ def _prepare_cabal_inputs( input_files, transitive = [ depset(srcs), - depset(cc.files), depset(ghc_files), package_databases, setup_dep_info.package_databases, @@ -402,18 +387,14 @@ def _prepare_cabal_inputs( setup_dep_info.hs_libraries, dep_info.interface_dirs, dep_info.hs_libraries, - tool_inputs, ], ) - input_manifests = tool_input_manifests + hs.toolchain.cc_wrapper.manifests - runfiles_direct = runfiles_libs if static_binary else dynamic_libs return struct( cabal_wrapper = cabal_wrapper, args = args, inputs = inputs, - input_manifests = input_manifests, env = env, runfiles = depset(direct = runfiles_direct), ) @@ -575,7 +556,7 @@ def _haskell_cabal_library_impl(ctx): ), sibling = cabal, ) - (tool_inputs, tool_input_manifests) = ctx.resolve_tools(tools = ctx.attr.tools) + attr_tools = [tool[DefaultInfo].files_to_run for tool in ctx.attr.tools] c = _prepare_cabal_inputs( hs, cc, @@ -586,8 +567,6 @@ def _haskell_cabal_library_impl(ctx): direct_cc_info, component = "lib:{}".format(ctx.attr.sublibrary_name or ctx.attr.package_name or hs.label.name), package_id = package_id, - tool_inputs = tool_inputs, - tool_input_manifests = tool_input_manifests, cabal = cabal, setup = setup, setup_deps = setup_deps, @@ -620,15 +599,23 @@ def _haskell_cabal_library_impl(ctx): if with_profiling: outputs.append(profiling_library) - (_, runghc_manifest) = ctx.resolve_tools(tools = [ctx.attr._runghc]) json_args = ctx.actions.declare_file("{}_cabal_wrapper_args.json".format(ctx.label.name)) ctx.actions.write(json_args, json.encode(c.args)) + + # Ensure that dependent tools can be found. + paths = [] + if "PATH" in c.env: + paths.append(c.env["PATH"]) + paths.extend([t.executable.dirname for t in attr_tools]) + c.env["PATH"] = ":".join(paths) + ctx.actions.run( executable = c.cabal_wrapper, arguments = [json_args.path], inputs = depset([json_args], transitive = [c.inputs]), - input_manifests = c.input_manifests + runghc_manifest, - tools = [c.cabal_wrapper, ctx.executable._runghc] + hs.tools_config.tools_for_ghc, + tools = attr_tools + + [c.cabal_wrapper, ctx.executable._runghc, hs.toolchain.cc_wrapper.as_tool] + + hs.tools_config.tools_for_ghc, outputs = outputs, env = c.env, mnemonic = "HaskellCabalLibrary", @@ -911,7 +898,7 @@ def _haskell_cabal_binary_impl(ctx): "_install/{}_data".format(hs.label.name), sibling = cabal, ) - (tool_inputs, tool_input_manifests) = ctx.resolve_tools(tools = ctx.attr.tools) + attr_tools = [tool[DefaultInfo].files_to_run for tool in ctx.attr.tools] c = _prepare_cabal_inputs( hs, cc, @@ -922,8 +909,6 @@ def _haskell_cabal_binary_impl(ctx): direct_cc_info, component = "exe:{}".format(exe_name), package_id = hs.label.name, - tool_inputs = tool_inputs, - tool_input_manifests = tool_input_manifests, cabal = cabal, setup = setup, setup_deps = setup_deps, @@ -942,20 +927,20 @@ def _haskell_cabal_binary_impl(ctx): static_binary = static_binary, label = ctx.label, ) - (_, runghc_manifest) = ctx.resolve_tools(tools = [ctx.attr._runghc]) json_args = ctx.actions.declare_file("{}_cabal_wrapper_args.json".format(ctx.label.name)) ctx.actions.write(json_args, json.encode(c.args)) ctx.actions.run( executable = c.cabal_wrapper, arguments = [json_args.path], inputs = depset([json_args], transitive = [c.inputs]), - input_manifests = c.input_manifests + runghc_manifest, outputs = [ package_database, binary, data_dir, ], - tools = [c.cabal_wrapper, ctx.executable._runghc] + hs.tools_config.tools_for_ghc, + tools = attr_tools + + [c.cabal_wrapper, ctx.executable._runghc, hs.toolchain.cc_wrapper.as_tool] + + hs.tools_config.tools_for_ghc, env = c.env, mnemonic = "HaskellCabalBinary", progress_message = "HaskellCabalBinary {}".format(hs.label), diff --git a/haskell/cc.bzl b/haskell/cc.bzl index 8d0c0f30a..f030d8616 100644 --- a/haskell/cc.bzl +++ b/haskell/cc.bzl @@ -118,13 +118,10 @@ def cc_interop_info(ctx, override_cc_toolchain = None): # Generate cc wrapper script on Darwin that adjusts load commands. hs_toolchain = ctx.toolchains["@rules_haskell//haskell:toolchain"] cc_wrapper = hs_toolchain.cc_wrapper - cc = cc_wrapper.executable.path - cc_files = depset(transitive = [cc_toolchain.all_files, cc_wrapper.inputs]) - cc_manifests = cc_wrapper.manifests tools = { "ar": cc_toolchain.ar_executable, - "cc": cc, + "cc": cc_wrapper, "ld": cc_toolchain.ld_executable, "cpp": cc_toolchain.preprocessor_executable, "nm": cc_toolchain.nm_executable, @@ -150,8 +147,12 @@ def cc_interop_info(ctx, override_cc_toolchain = None): env["CC_WRAPPER_PLATFORM"] = "linux" env["CC_WRAPPER_CC_PATH"] = real_cc_path + env["CC_WRAPPER_PATH"] = cc_wrapper.executable.path env["CC_WRAPPER_CPU"] = cc_toolchain.cpu + if cc_wrapper.as_tool.runfiles_manifest: + env["CC_WRAPPER_MANIFEST"] = cc_wrapper.as_tool.runfiles_manifest.path + cc_libraries_info = deps_HaskellCcLibrariesInfo( ctx.attr.deps + getattr(ctx.attr, "plugins", []) + getattr(ctx.attr, "setup_deps", []), ) @@ -163,8 +164,6 @@ def cc_interop_info(ctx, override_cc_toolchain = None): return CcInteropInfo( tools = struct(**tools), env = env, - files = cc_files.to_list(), - manifests = cc_manifests, hdrs = hdrs.to_list(), cpp_flags = cpp_flags, include_args = include_args, diff --git a/haskell/doctest.bzl b/haskell/doctest.bzl index 664cd61c9..2fb453c4b 100644 --- a/haskell/doctest.bzl +++ b/haskell/doctest.bzl @@ -98,7 +98,7 @@ def _haskell_doctest_single(target, ctx): ctx, override_cc_toolchain = hs.tools_config.maybe_exec_cc_toolchain, ) - args.add_all(ghc_cc_program_args(hs, cc.tools.cc, cc.tools.ld)) + args.add_all(ghc_cc_program_args(hs, cc.tools.cc.executable.path, cc.tools.ld)) doctest_log = ctx.actions.declare_file( "doctest-log-" + ctx.label.name + "-" + target.label.name, @@ -137,7 +137,6 @@ def _haskell_doctest_single(target, ctx): depset(get_ghci_library_files(hs, cc_libraries_info, cc.transitive_libraries)), depset( toolchain.doctest + - cc.files + [hs.tools.ghc], ), ]), @@ -173,6 +172,7 @@ def _haskell_doctest_single(target, ctx): # sandboxing altogether for doctest tests. "no-sandbox": "1", }, + tools = [cc.tools.cc.as_tool], ) return doctest_log diff --git a/haskell/experimental/private/module.bzl b/haskell/experimental/private/module.bzl index 754a64c72..e94f2bd2d 100644 --- a/haskell/experimental/private/module.bzl +++ b/haskell/experimental/private/module.bzl @@ -1,3 +1,4 @@ +load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_skylib//lib:sets.bzl", "sets") load( @@ -21,10 +22,6 @@ load( ) load("//haskell:private/path_utils.bzl", "infer_main_module") load("//haskell:private/pkg_id.bzl", "pkg_id") -load( - "//haskell:private/plugins.bzl", - "resolve_plugin_tools", -) load( "//haskell:providers.bzl", "GhcPluginInfo", @@ -173,8 +170,9 @@ def _build_haskell_module( module_attr.name, [dep for plugin in plugin_decl for dep in plugin[GhcPluginInfo].deps], ) - plugins = [resolve_plugin_tools(ctx, plugin[GhcPluginInfo]) for plugin in plugin_decl] - (preprocessors_inputs, preprocessors_input_manifests) = ctx.resolve_tools(tools = ctx.attr.tools + module_attr.tools) + plugin_infos = [plugin[GhcPluginInfo] for plugin in plugin_decl] + plugin_tools = [tool[DefaultInfo].files_to_run for i in plugin_infos for tool in i.tools] + attr_tools = [tool[DefaultInfo].files_to_run for tool in (ctx.attr.tools + module_attr.tools)] # TODO[AH] Support additional outputs such as `.hie`. @@ -254,7 +252,7 @@ def _build_haskell_module( args.add_all(cc.include_args) - if plugins or enable_th: + if plugin_infos or enable_th: # cc toolchain linker flags would be necessary when the interpreter wants to # load any libraries args.add_all(cc.linker_flags, format_each = "-optl%s") @@ -278,8 +276,8 @@ def _build_haskell_module( plugin_pkg_info = expose_packages( package_ids = [ pkg_id - for plugin in plugins - for pkg_id in all_dependencies_package_ids(plugin.deps) + for plugin_info in plugin_infos + for pkg_id in all_dependencies_package_ids(plugin_info.deps) ], package_databases = plugin_dep_info.package_databases, version = version, @@ -288,17 +286,10 @@ def _build_haskell_module( ) args.add_all(pkg_info_args) - for plugin in plugins: - args.add("-fplugin={}".format(plugin.module)) - for opt in plugin.args: - args.add_all(["-fplugin-opt", "{}:{}".format(plugin.module, opt)]) - - plugin_tool_inputs = depset(transitive = [plugin.tool_inputs for plugin in plugins]) - plugin_tool_input_manifests = [ - manifest - for plugin in plugins - for manifest in plugin.tool_input_manifests - ] + for plugin_info in plugin_infos: + args.add("-fplugin={}".format(plugin_info.module)) + for opt in plugin_info.args: + args.add_all(["-fplugin-opt", "{}:{}".format(plugin_info.module, opt)]) args.add_all(hs.toolchain.ghcopts) args.add_all(user_ghcopts) @@ -310,7 +301,7 @@ def _build_haskell_module( args.add(paths.join(ar_bindir, "otool"), format = "-pgmotool=%s") args.add(paths.join(ar_bindir, "install_name_tool"), format = "-pgminstall_name_tool=%s") - if plugins and not enable_th: + if plugin_infos and not enable_th: # For #1681. These suppresses bogus warnings about missing libraries which # aren't really needed. args.add("-Wno-missed-extra-shared-lib") @@ -330,6 +321,8 @@ def _build_haskell_module( args.add("-optl@{}".format(extra_ldflags_file.path)) input_files.append(extra_ldflags_file) + env = dicts.add(hs.env, cc.env) + # Compile the module hs.toolchain.actions.run_ghc( hs, @@ -345,8 +338,6 @@ def _build_haskell_module( plugin_dep_info.package_databases, plugin_dep_info.interface_dirs, plugin_dep_info.hs_libraries, - plugin_tool_inputs, - preprocessors_inputs, interface_inputs, abi_inputs, ] + [ @@ -361,16 +352,16 @@ def _build_haskell_module( if enable_th ], ), - input_manifests = preprocessors_input_manifests + plugin_tool_input_manifests, outputs = outputs, mnemonic = "HaskellBuildObject" + ("Prof" if with_profiling else ""), progress_message = "HaskellBuildObject {} {}".format(hs.label, module.label), - env = hs.env, + env = env, arguments = args, interface_inputs = interface_inputs, extra_name = module.label.package.replace("/", "_") + "_" + module.label.name, hi_file = module_outputs.hi, abi_file = module_outputs.abi, + extra_tools = attr_tools + plugin_tools, ) is_boot = _is_boot(src.path) diff --git a/haskell/private/actions/compile.bzl b/haskell/private/actions/compile.bzl index a2203f1af..5971ef6df 100644 --- a/haskell/private/actions/compile.bzl +++ b/haskell/private/actions/compile.bzl @@ -42,9 +42,8 @@ def _compilation_defaults( my_pkg_id, version, extra_ldflags_file, - plugins, - non_default_plugins, - preprocessors): + plugin_infos, + non_default_plugin_infos): """Compute variables common to all compilation targets (binary and library). Returns: @@ -134,9 +133,9 @@ def _compilation_defaults( ] package_ids = [] - all_plugins = plugins + non_default_plugins - for plugin in all_plugins: - package_ids.extend(all_dependencies_package_ids(plugin.deps)) + all_plugin_infos = plugin_infos + non_default_plugin_infos + for plugin_info in all_plugin_infos: + package_ids.extend(all_dependencies_package_ids(plugin_info.deps)) (pkg_info_inputs, pkg_info_args) = pkg_info_to_compile_flags( hs, @@ -258,18 +257,11 @@ def _compilation_defaults( args.add_all(compile_flags) # Plugins - for plugin in plugins: - args.add("-fplugin={}".format(plugin.module)) - for plugin in all_plugins: - for opt in plugin.args: - args.add_all(["-fplugin-opt", "{}:{}".format(plugin.module, opt)]) - - plugin_tool_inputs = depset(transitive = [plugin.tool_inputs for plugin in all_plugins]) - plugin_tool_input_manifests = [ - manifest - for plugin in all_plugins - for manifest in plugin.tool_input_manifests - ] + for plugin_info in plugin_infos: + args.add("-fplugin={}".format(plugin_info.module)) + for plugin_info in all_plugin_infos: + for opt in plugin_info.args: + args.add_all(["-fplugin-opt", "{}:{}".format(plugin_info.module, opt)]) # Pass source files args.add_all(source_files) @@ -313,10 +305,7 @@ def _compilation_defaults( plugin_dep_info.hs_libraries, depset(get_ghci_library_files(hs, cc.cc_libraries_info, cc.transitive_libraries + cc.plugin_libraries)), java.inputs, - preprocessors.inputs, - plugin_tool_inputs, ]), - input_manifests = preprocessors.input_manifests + plugin_tool_input_manifests, object_files = object_files, dyn_object_files = dyn_object_files, interface_files = interface_files, @@ -363,8 +352,10 @@ def compile_binary( version, extra_ldflags_file, inspect_coverage = False, - plugins = [], - non_default_plugins = [], + plugin_infos = [], + plugin_tools = [], + non_default_plugin_infos = [], + non_default_plugin_tools = [], preprocessors = []): """Compile a Haskell target into object files suitable for linking. @@ -394,9 +385,8 @@ def compile_binary( my_pkg_id = None, version = version, extra_ldflags_file = extra_ldflags_file, - plugins = plugins, - non_default_plugins = non_default_plugins, - preprocessors = preprocessors, + plugin_infos = plugin_infos, + non_default_plugin_infos = non_default_plugin_infos, ) c.args.add_all(["-main-is", main_function]) if dynamic: @@ -418,12 +408,12 @@ def compile_binary( hs, cc, inputs = c.inputs, - input_manifests = c.input_manifests, outputs = c.outputs + [datum.mix_file for datum in coverage_data], mnemonic = "HaskellBuildBinary" + ("Prof" if with_profiling else ""), progress_message = "HaskellBuildBinary {}".format(hs.label), env = c.env, arguments = c.args, + extra_tools = preprocessors + plugin_tools + non_default_plugin_tools, ) return struct( @@ -455,8 +445,10 @@ def compile_library( objects_dir, my_pkg_id, extra_ldflags_file, - plugins = [], - non_default_plugins = [], + plugin_infos = [], + plugin_tools = [], + non_default_plugin_infos = [], + non_default_plugin_tools = [], preprocessors = []): """Build arguments for Haskell package build. @@ -490,9 +482,8 @@ def compile_library( my_pkg_id = my_pkg_id, version = my_pkg_id.version, extra_ldflags_file = extra_ldflags_file, - plugins = plugins, - non_default_plugins = non_default_plugins, - preprocessors = preprocessors, + plugin_infos = plugin_infos, + non_default_plugin_infos = non_default_plugin_infos, ) if with_shared: c.args.add("-dynamic-too") @@ -515,12 +506,12 @@ def compile_library( hs, cc, inputs = c.inputs, - input_manifests = c.input_manifests, outputs = c.outputs + [datum.mix_file for datum in coverage_data], mnemonic = "HaskellBuildLibrary" + ("Prof" if with_profiling else ""), progress_message = "HaskellBuildLibrary {}".format(hs.label), env = c.env, arguments = c.args, + extra_tools = preprocessors + plugin_tools + non_default_plugin_tools, ) return struct( diff --git a/haskell/private/actions/info.bzl b/haskell/private/actions/info.bzl index fc970285b..b4a5c4325 100644 --- a/haskell/private/actions/info.bzl +++ b/haskell/private/actions/info.bzl @@ -32,9 +32,8 @@ def write_proto_file(hs, output_name, proto_type, content): hs.actions.run_shell( outputs = [proto_pb], - inputs = depset([proto_txt] + rule_info_protos, transitive = [protoc.inputs]), - input_manifests = protoc.input_manifests, - tools = [protoc.executable], + inputs = depset([proto_txt] + rule_info_protos), + tools = [protoc], command = "{protoc} {rule_info_proto} --encode {proto_type} < {proto_txt} > {proto_pb}" .format( diff --git a/haskell/private/actions/link.bzl b/haskell/private/actions/link.bzl index c01777ac8..0fa4fd99a 100644 --- a/haskell/private/actions/link.bzl +++ b/haskell/private/actions/link.bzl @@ -323,7 +323,7 @@ def link_library_static(hs, cc, _posix, _dep_info, object_files, my_pkg_id, with "lib{0}.a".format(pkg_id.library_name(hs, my_pkg_id, prof_suffix = with_profiling)), ), ) - inputs = depset(cc.files, transitive = [object_files]) + inputs = depset(transitive = [object_files]) args = hs.actions.args() # On Windows, any of the object files might actually be static archives already diff --git a/haskell/private/actions/process_hsc_file.bzl b/haskell/private/actions/process_hsc_file.bzl index b22fcfd58..709714790 100644 --- a/haskell/private/actions/process_hsc_file.bzl +++ b/haskell/private/actions/process_hsc_file.bzl @@ -1,5 +1,6 @@ """Action processing hsc files""" +load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_skylib//lib:sets.bzl", "sets") load(":private/path_utils.bzl", "declare_compiled") @@ -26,8 +27,8 @@ def process_hsc_file(hs, cc, hsc_flags, hsc_inputs, hsc_file): hs_out = declare_compiled(hs, hsc_file, ".hs", directory = hsc_dir_raw) args.add_all([hsc_file.path, "-o", hs_out.path]) - args.add_all(["-c", cc.tools.cc]) - args.add_all(["-l", cc.tools.cc]) + args.add_all(["-c", cc.tools.cc.executable.path]) + args.add_all(["-l", cc.tools.cc.executable.path]) args.add("-ighcplatform.h") args.add("-ighcversion.h") args.add_all(cc.cpp_flags, format_each = "--cflag=%s") @@ -51,24 +52,24 @@ def process_hsc_file(hs, cc, hsc_flags, hsc_inputs, hsc_file): args.add_all(hsc_flags) + env = dicts.add(hs.env, cc.env) + # Add an empty PATH variable if not already specified in hs.env. # Needed to avoid a "Couldn't read PATH" error on Windows. # # On Unix platforms, though, we mustn't set PATH as it is automatically set up # by the run action, unless already set in the env parameter. This triggers # build errors when using GHC bindists on Linux. - if hs.env.get("PATH") == None and hs.toolchain.is_windows: - hs.env["PATH"] = "" + if env.get("PATH") == None and hs.toolchain.is_windows: + env["PATH"] = "" hs.actions.run_shell( inputs = depset(transitive = [ depset(cc.hdrs), depset([hsc_file]), - depset(cc.files), depset(hsc_inputs), depset(hs.toolchain.bindir), ]), - input_manifests = cc.manifests, outputs = [hs_out], mnemonic = "HaskellHsc2hs", command = @@ -84,12 +85,13 @@ def process_hsc_file(hs, cc, hsc_flags, hsc_inputs, hsc_file): include_dirs_args=( "${{include_dirs[@]/#/-C-I}}" ) {hsc2hs} "${{include_dirs_args[@]}}" "$@" """.format( - mingw_bin = paths.dirname(cc.tools.cc) if hs.toolchain.is_windows else "", + mingw_bin = paths.dirname(cc.tools.cc.executable.path) if hs.toolchain.is_windows else "", ghc_pkg = hs.tools.ghc_pkg.path, hsc2hs = hs.tools.hsc2hs.path, ), arguments = [args], - env = hs.env, + env = env, + tools = [cc.tools.cc.as_tool], ) idir = paths.join( diff --git a/haskell/private/actions/runghc.bzl b/haskell/private/actions/runghc.bzl index 2a5c4b53c..9f601bb5e 100644 --- a/haskell/private/actions/runghc.bzl +++ b/haskell/private/actions/runghc.bzl @@ -104,6 +104,5 @@ def build_haskell_runghc( pkg_info_inputs, depset(get_ghci_library_files(hs, cc.cc_libraries_info, cc.transitive_libraries + cc.plugin_libraries)), hs_info.source_files, - hs.toolchain.cc_wrapper.runfiles.files, ]) ln(hs, posix, runghc_file, output, extra_inputs) diff --git a/haskell/private/cabal_wrapper.py b/haskell/private/cabal_wrapper.py index fee5692b6..6e2a40e41 100755 --- a/haskell/private/cabal_wrapper.py +++ b/haskell/private/cabal_wrapper.py @@ -100,8 +100,9 @@ def canonicalize_path(path): os.environ["RULES_HASKELL_GHC_PKG_PATH"] = canonicalize_path(os.getenv("RULES_HASKELL_GHC_PKG_PATH", "")) os.environ["RULES_HASKELL_LIBDIR_PATH"] = canonicalize_path(os.getenv("RULES_HASKELL_LIBDIR_PATH", "")) os.environ["RULES_HASKELL_DOCDIR_PATH"] = canonicalize_path(os.getenv("RULES_HASKELL_DOCDIR_PATH", "")) -if "LOCALE_ARCHIVE" in os.environ: - os.environ["LOCALE_ARCHIVE"] = canonicalize_path(os.getenv("LOCALE_ARCHIVE")) +for path in ["LOCALE_ARCHIVE", "CC_WRAPPER_PATH", "CC_WRAPPER_MANIFEST"]: + if path in os.environ: + os.environ[path] = canonicalize_path(os.getenv(path)) component = json_args["component"] name = json_args["pkg_name"] diff --git a/haskell/private/cc_wrapper.py.tpl b/haskell/private/cc_wrapper.py.tpl index 1e0062743..be9fceb04 100644 --- a/haskell/private/cc_wrapper.py.tpl +++ b/haskell/private/cc_wrapper.py.tpl @@ -571,6 +571,11 @@ def link(output, libraries, rpaths, args): rpaths = shorten_rpaths(rpaths, libraries, output) args.extend(rpath_args(rpaths)) + # See note in cabal_wrapper.py + if "RUNFILES_DIR" in os.environ: + del os.environ["RUNFILES_DIR"] + if "RUNFILES_MANIFEST_FILE" in os.environ: + del os.environ["RUNFILES_MANIFEST_FILE"] # Note: `RULES_HASKELL_SILENCE_LINKER` is only set if called from doctest, # which is used to silence the linker output to not interfere with the output # from GHCi @@ -1021,6 +1026,13 @@ def find_cc(): # being called from a GHCi REPL then we need to find this wrapper # script using Bazel runfiles. r = bazel_runfiles.Create() + if r is None: + manifest_path = os.environ.get("CC_WRAPPER_MANIFEST", None) + exe_path = os.environ.get("CC_WRAPPER_PATH", None) + if manifest_path is not None and os.path.isfile(manifest_path): + r = bazel_runfiles.CreateManifestBased(manifest_path) + elif exe_path is not None and os.path.isfile(exe_path): + r = bazel_runfiles.CreateDirectoryBased(exe_path + ".runfiles") cc = r.Rlocation("/".join([WORKSPACE, CC])) if cc is None and is_windows(): # We must use "/" instead of os.path.join on Windows, because the diff --git a/haskell/private/haskell_impl.bzl b/haskell/private/haskell_impl.bzl index e120d99c7..6fd893e79 100644 --- a/haskell/private/haskell_impl.bzl +++ b/haskell/private/haskell_impl.bzl @@ -41,7 +41,6 @@ load( "parse_pattern", ) load(":private/pkg_id.bzl", "pkg_id") -load(":private/plugins.bzl", "resolve_plugin_tools") load(":private/set.bzl", "set") load(":private/version_macros.bzl", "generate_version_macros") load( @@ -141,18 +140,8 @@ def _condition_coverage_src(hs, src): return conditioned_src -def _resolve_preprocessors(ctx, preprocessors): - if not hasattr(ctx, "resolve_tools"): - # No resolve_tools when ctx is faked (see protobuf.bzl). - return struct( - inputs = depset(), - input_manifests = [], - ) - (inputs, input_manifests) = ctx.resolve_tools(tools = preprocessors) - return struct( - inputs = inputs, - input_manifests = input_manifests, - ) +def _resolve_preprocessors(preprocessors): + return [tool[DefaultInfo].files_to_run for tool in preprocessors] def haskell_module_from_target(m): """ Produces the module name from a HaskellModuleInfo """ @@ -234,9 +223,11 @@ def _haskell_binary_common_impl(ctx, is_test): objects_dir, ) - plugins = [resolve_plugin_tools(ctx, plugin[GhcPluginInfo]) for plugin in plugin_decl] - non_default_plugins = [resolve_plugin_tools(ctx, plugin[GhcPluginInfo]) for plugin in non_default_plugin_decl] - preprocessors = _resolve_preprocessors(ctx, ctx.attr.tools) + plugin_infos = [plugin[GhcPluginInfo] for plugin in plugin_decl] + plugin_tools = [tool[DefaultInfo].files_to_run for i in plugin_infos for tool in i.tools] + non_default_plugin_infos = [plugin[GhcPluginInfo] for plugin in non_default_plugin_decl] + non_default_plugin_tools = [tool[DefaultInfo].files_to_run for i in non_default_plugin_infos for tool in i.tools] + preprocessors = _resolve_preprocessors(ctx.attr.tools) user_compile_flags = haskell_library_expand_make_variables("ghcopts", ctx, ctx.attr.ghcopts) c = hs.toolchain.actions.compile_binary( hs, @@ -258,8 +249,10 @@ def _haskell_binary_common_impl(ctx, is_test): version = ctx.attr.version, inspect_coverage = inspect_coverage, extra_ldflags_file = extra_ldflags_file, - plugins = plugins, - non_default_plugins = non_default_plugins, + plugin_infos = plugin_infos, + plugin_tools = plugin_tools, + non_default_plugin_infos = non_default_plugin_infos, + non_default_plugin_tools = non_default_plugin_tools, preprocessors = preprocessors, ) @@ -466,9 +459,11 @@ def haskell_library_impl(ctx): objects_dir, ) - plugins = [resolve_plugin_tools(ctx, plugin[GhcPluginInfo]) for plugin in ctx.attr.plugins] - non_default_plugins = [resolve_plugin_tools(ctx, plugin[GhcPluginInfo]) for plugin in ctx.attr.non_default_plugins] - preprocessors = _resolve_preprocessors(ctx, ctx.attr.tools) + plugin_infos = [plugin[GhcPluginInfo] for plugin in ctx.attr.plugins] + plugin_tools = [tool[DefaultInfo].files_to_run for i in plugin_infos for tool in i.tools] + non_default_plugin_infos = [plugin[GhcPluginInfo] for plugin in ctx.attr.non_default_plugins] + non_default_plugin_tools = [tool[DefaultInfo].files_to_run for i in non_default_plugin_infos for tool in i.tools] + preprocessors = _resolve_preprocessors(ctx.attr.tools) user_compile_flags = haskell_library_expand_make_variables("ghcopts", ctx, ctx.attr.ghcopts) c = hs.toolchain.actions.compile_library( hs, @@ -488,8 +483,10 @@ def haskell_library_impl(ctx): objects_dir = objects_dir, my_pkg_id = my_pkg_id, extra_ldflags_file = extra_ldflags_file, - plugins = plugins, - non_default_plugins = non_default_plugins, + plugin_infos = plugin_infos, + plugin_tools = plugin_tools, + non_default_plugin_infos = non_default_plugin_infos, + non_default_plugin_tools = non_default_plugin_tools, preprocessors = preprocessors, ) diff --git a/haskell/private/plugins.bzl b/haskell/private/plugins.bzl deleted file mode 100644 index 02cab6306..000000000 --- a/haskell/private/plugins.bzl +++ /dev/null @@ -1,12 +0,0 @@ -"""Utilities for GHC plugins.""" - -def resolve_plugin_tools(ctx, plugin_info): - """Convert a plugin provider to a struct with tools resolved to inputs.""" - (tool_inputs, tool_input_manifests) = ctx.resolve_tools(tools = plugin_info.tools) - return struct( - module = plugin_info.module, - deps = plugin_info.deps, - args = plugin_info.args, - tool_inputs = tool_inputs, - tool_input_manifests = tool_input_manifests, - ) diff --git a/haskell/private/runghc.bzl b/haskell/private/runghc.bzl index 37e87fb39..598039e89 100644 --- a/haskell/private/runghc.bzl +++ b/haskell/private/runghc.bzl @@ -24,21 +24,33 @@ def _runghc_wrapper_impl(ctx): content = """\ #!/usr/bin/env python3 +import os import subprocess import sys from python.runfiles import runfiles r = runfiles.Create() -subprocess.run([r.Rlocation("{runghc}")] + sys.argv[1:], check=True) +# Get the correct location first +ghc_location = r.Rlocation("{runghc}") + +# Clear out the environment so that downstream tools don't use the wrong +# runfiles environment. See the comment in cabal_wrapper.py for more details. +if "RUNFILES_DIR" in os.environ: + del os.environ["RUNFILES_DIR"] +if "RUNFILES_MANIFEST_FILE" in os.environ: + del os.environ["RUNFILES_MANIFEST_FILE"] + +subprocess.run([ghc_location] + sys.argv[1:], check=True) """.format(runghc = runghc_runfile_path), is_executable = True, ) return [DefaultInfo( executable = runghc_wrapper_file, - runfiles = hs_toolchain.cc_wrapper.runfiles.merge( - ctx.runfiles(files = [runghc_wrapper_file, hs_toolchain.tools.runghc]), + runfiles = ctx.runfiles( + files = [runghc_wrapper_file, hs_toolchain.tools.runghc, hs_toolchain.cc_wrapper.executable], + transitive_files = hs_toolchain.cc_wrapper.runfiles.files, ), )] diff --git a/haskell/protobuf.bzl b/haskell/protobuf.bzl index fd6979793..237ebb403 100644 --- a/haskell/protobuf.bzl +++ b/haskell/protobuf.bzl @@ -147,13 +147,12 @@ def _haskell_proto_aspect_impl(target, ctx): ]) ctx.actions.run( - inputs = depset(inputs, transitive = [pb.plugin.inputs, pb.protoc.inputs]), - input_manifests = pb.protoc.input_manifests + pb.plugin.input_manifests, + inputs = inputs, outputs = hs_files, mnemonic = "HaskellProtoc", - executable = pb.protoc.executable, + executable = pb.protoc, arguments = [args], - tools = [pb.plugin.executable], + tools = [pb.plugin], env = { "RULES_HASKELL_GHC_PATH": hs.tools.ghc.path, "RULES_HASKELL_GHC_PKG_PATH": hs.tools.ghc_pkg.path, @@ -351,21 +350,13 @@ registered. """, ) -def _wrap_tool(ctx, exe, tool): - inputs, input_manifests = ctx.resolve_tools(tools = [tool]) - return struct( - executable = exe, - inputs = inputs, - input_manifests = input_manifests, - ) - def _protobuf_toolchain_impl(ctx): return [ platform_common.ToolchainInfo( name = ctx.label.name, tools = struct( - plugin = _wrap_tool(ctx, ctx.executable.plugin, ctx.attr.plugin), - protoc = _wrap_tool(ctx, ctx.executable.protoc, ctx.attr.protoc), + plugin = ctx.attr.plugin[DefaultInfo].files_to_run, + protoc = ctx.attr.protoc[DefaultInfo].files_to_run, ), deps = ctx.attr.deps, ), diff --git a/haskell/toolchain.bzl b/haskell/toolchain.bzl index 5e033af84..aa6457ba0 100644 --- a/haskell/toolchain.bzl +++ b/haskell/toolchain.bzl @@ -62,26 +62,27 @@ def _run_ghc( interface_inputs = [], extra_name = "", hi_file = None, - abi_file = None): + abi_file = None, + extra_tools = []): args = hs.actions.args() extra_inputs = [] # Detect persistent worker support flagsfile_prefix = "" execution_requirements = {} - tools = [] + tools = extra_tools + [cc.tools.cc.as_tool] if hs.worker != None: flagsfile_prefix = "@" execution_requirements = {"supports-workers": "1"} args.add(hs.worker.path) - tools = [hs.worker] + tools = extra_tools + [hs.worker] else: args.add(hs.tools.ghc) extra_inputs.append(hs.tools.ghc) # XXX: We should also tether Bazel's CC toolchain to GHC's, so that we can properly mix Bazel-compiled # C libraries with Haskell targets. - args.add_all(ghc_cc_program_args(hs, cc.tools.cc, cc.tools.ld)) + args.add_all(ghc_cc_program_args(hs, cc.tools.cc.executable.path, cc.tools.ld)) compile_flags_file = hs.actions.declare_file("compile_flags_%s_%s_%s" % (hs.name, extra_name, mnemonic)) extra_args_file = hs.actions.declare_file("extra_args_%s_%s_%s" % (hs.name, extra_name, mnemonic)) @@ -129,7 +130,7 @@ def _run_ghc( extra_inputs += [ compile_flags_file, extra_args_file, - ] + cc.files + hs.toolchain.bindir + hs.toolchain.libdir + ] + hs.toolchain.bindir + hs.toolchain.libdir if hs.toolchain.locale_archive != None: extra_inputs.append(hs.toolchain.locale_archive) @@ -141,11 +142,6 @@ def _run_ghc( inputs = depset(extra_inputs, transitive = [inputs]) - if input_manifests != None: - input_manifests = input_manifests + cc.manifests - else: - input_manifests = cc.manifests - tools.extend(hs.tools_config.tools_for_ghc) append_to_path(env, hs.toolchain.is_windows, hs.tools_config.path_for_run_ghc) @@ -426,11 +422,8 @@ def _haskell_toolchain_impl(ctx): for lib in ctx.attr.libraries } - (cc_wrapper_inputs, cc_wrapper_manifest) = ctx.resolve_tools(tools = [ctx.attr._cc_wrapper]) cc_wrapper_info = ctx.attr._cc_wrapper[DefaultInfo] - cc_wrapper_runfiles = cc_wrapper_info.default_runfiles.merge( - cc_wrapper_info.data_runfiles, - ) + cc_wrapper_files_to_run = cc_wrapper_info.files_to_run if ctx.attr.asterius_binaries: tools_config = asterius_tools_config( @@ -449,8 +442,6 @@ def _haskell_toolchain_impl(ctx): supports_haddock = default_tools_config.supports_haddock, ) - (protoc_inputs, protoc_input_manifests) = ctx.resolve_tools(tools = [ctx.attr._protoc]) - return [ platform_common.ToolchainInfo( name = ctx.label.name, @@ -467,10 +458,9 @@ def _haskell_toolchain_impl(ctx): locale = ctx.attr.locale, locale_archive = locale_archive, cc_wrapper = struct( - executable = ctx.executable._cc_wrapper, - inputs = cc_wrapper_inputs, - manifests = cc_wrapper_manifest, - runfiles = cc_wrapper_runfiles, + executable = cc_wrapper_files_to_run.executable, + as_tool = cc_wrapper_files_to_run, + runfiles = cc_wrapper_info.default_runfiles, ), mode = ctx.var["COMPILATION_MODE"], actions = struct( @@ -491,11 +481,7 @@ def _haskell_toolchain_impl(ctx): version = ctx.attr.version, numeric_version = numeric_version, global_pkg_db = pkgdb_file, - protoc = struct( - executable = ctx.executable._protoc, - inputs = protoc_inputs, - input_manifests = protoc_input_manifests, - ), + protoc = ctx.attr._protoc[DefaultInfo].files_to_run, rule_info_proto = ctx.attr._rule_info_proto, tools_config = tools_config, ), diff --git a/rules_haskell_tests/tests/stackage_zlib_runpath/BUILD.bazel b/rules_haskell_tests/tests/stackage_zlib_runpath/BUILD.bazel index db2fb3aa2..3054be546 100644 --- a/rules_haskell_tests/tests/stackage_zlib_runpath/BUILD.bazel +++ b/rules_haskell_tests/tests/stackage_zlib_runpath/BUILD.bazel @@ -98,14 +98,14 @@ with open(libz_soname) as fh: (sodir, sobase) = os.path.split(sofile) # Locate test artifacts. -libHSzlib = r.Rlocation(os.path.join( +libHSzlib = r.Rlocation(os.path.normpath(os.path.join( os.environ["TEST_WORKSPACE"], sys.argv[2], -)) -cabal_binary = r.Rlocation(os.path.join( +))) +cabal_binary = r.Rlocation(os.path.normpath(os.path.join( os.environ["TEST_WORKSPACE"], sys.argv[3], -)) +))) def read_runpaths(binary, sobase): if platform.system() == "Darwin":