diff options
Diffstat (limited to 'third_party/bazel/rules_haskell/haskell/private/haskell_impl.bzl')
-rw-r--r-- | third_party/bazel/rules_haskell/haskell/private/haskell_impl.bzl | 668 |
1 files changed, 0 insertions, 668 deletions
diff --git a/third_party/bazel/rules_haskell/haskell/private/haskell_impl.bzl b/third_party/bazel/rules_haskell/haskell/private/haskell_impl.bzl deleted file mode 100644 index a58239fad1d5..000000000000 --- a/third_party/bazel/rules_haskell/haskell/private/haskell_impl.bzl +++ /dev/null @@ -1,668 +0,0 @@ -"""Implementation of core Haskell rules""" - -load( - "@io_tweag_rules_haskell//haskell:providers.bzl", - "C2hsLibraryInfo", - "HaskellInfo", - "HaskellLibraryInfo", - "HaskellPrebuiltPackageInfo", -) -load(":cc.bzl", "cc_interop_info") -load( - ":private/actions/link.bzl", - "link_binary", - "link_library_dynamic", - "link_library_static", -) -load(":private/actions/package.bzl", "package") -load(":private/actions/repl.bzl", "build_haskell_repl") -load(":private/actions/runghc.bzl", "build_haskell_runghc") -load(":private/context.bzl", "haskell_context") -load(":private/dependencies.bzl", "gather_dep_info") -load(":private/java.bzl", "java_interop_info") -load(":private/mode.bzl", "is_profiling_enabled") -load( - ":private/path_utils.bzl", - "ln", - "match_label", - "parse_pattern", - "target_unique_name", -) -load(":private/pkg_id.bzl", "pkg_id") -load(":private/set.bzl", "set") -load(":private/version_macros.bzl", "generate_version_macros") -load(":providers.bzl", "GhcPluginInfo", "HaskellCoverageInfo") -load("@bazel_skylib//lib:paths.bzl", "paths") -load("@bazel_skylib//lib:collections.bzl", "collections") -load("@bazel_skylib//lib:shell.bzl", "shell") - -def _prepare_srcs(srcs): - srcs_files = [] - import_dir_map = {} - - for src in srcs: - # If it has the "files" attribute, it must be a Target - if hasattr(src, "files"): - if C2hsLibraryInfo in src: - srcs_files += src.files.to_list() - for f in src.files.to_list(): - import_dir_map[f] = src[C2hsLibraryInfo].import_dir - else: - srcs_files += src.files.to_list() - - # otherwise it's just a file - - else: - srcs_files.append(src) - - return srcs_files, import_dir_map - -def haskell_test_impl(ctx): - return _haskell_binary_common_impl(ctx, is_test = True) - -def haskell_binary_impl(ctx): - return _haskell_binary_common_impl(ctx, is_test = False) - -def _should_inspect_coverage(ctx, hs, is_test): - return hs.coverage_enabled and is_test - -def _coverage_enabled_for_target(coverage_source_patterns, label): - for pat in coverage_source_patterns: - if match_label(pat, label): - return True - - return False - -# Mix files refer to genfile srcs including their root. Therefore, we -# must condition the src filepaths passed in for coverage to match. -def _condition_coverage_src(hs, src): - if not src.path.startswith(hs.genfiles_dir.path): - return src - - """ Genfiles have the genfile directory as part of their path, - so declaring a file with the sample path actually makes the new - file double-qualified by the genfile directory. - - This is necessary because mix files capture the genfile - path before compilation, and then expect those files to be - qualified by the genfile directory when `hpc report` or - `hpc markup` are used. But, genfiles included as runfiles - are no longer qualified. So, double-qualifying them results in - only one level of qualification as runfiles. - """ - conditioned_src = hs.actions.declare_file(src.path) - hs.actions.run_shell( - inputs = [src], - outputs = [conditioned_src], - arguments = [ - src.path, - conditioned_src.path, - ], - command = """ - mkdir -p $(dirname "$2") && cp "$1" "$2" - """, - ) - - return conditioned_src - -def _haskell_binary_common_impl(ctx, is_test): - hs = haskell_context(ctx) - dep_info = gather_dep_info(ctx, ctx.attr.deps) - plugin_dep_info = gather_dep_info( - ctx, - [dep for plugin in ctx.attr.plugins for dep in plugin[GhcPluginInfo].deps], - ) - - # Add any interop info for other languages. - cc = cc_interop_info(ctx) - java = java_interop_info(ctx) - - with_profiling = is_profiling_enabled(hs) - srcs_files, import_dir_map = _prepare_srcs(ctx.attr.srcs) - inspect_coverage = _should_inspect_coverage(ctx, hs, is_test) - - c = hs.toolchain.actions.compile_binary( - hs, - cc, - java, - dep_info, - plugin_dep_info, - srcs = srcs_files, - ls_modules = ctx.executable._ls_modules, - import_dir_map = import_dir_map, - extra_srcs = depset(ctx.files.extra_srcs), - user_compile_flags = ctx.attr.compiler_flags, - dynamic = False if hs.toolchain.is_windows else not ctx.attr.linkstatic, - with_profiling = False, - main_function = ctx.attr.main_function, - version = ctx.attr.version, - inspect_coverage = inspect_coverage, - plugins = ctx.attr.plugins, - ) - - # gather intermediary code coverage instrumentation data - coverage_data = c.coverage_data - for dep in ctx.attr.deps: - if HaskellCoverageInfo in dep: - coverage_data += dep[HaskellCoverageInfo].coverage_data - - c_p = None - - if with_profiling: - c_p = hs.toolchain.actions.compile_binary( - hs, - cc, - java, - dep_info, - plugin_dep_info, - srcs = srcs_files, - ls_modules = ctx.executable._ls_modules, - import_dir_map = import_dir_map, - # NOTE We must make the object files compiled without profiling - # available to this step for TH to work, presumably because GHC is - # linked against RTS without profiling. - extra_srcs = depset(transitive = [ - depset(ctx.files.extra_srcs), - depset([c.objects_dir]), - ]), - user_compile_flags = ctx.attr.compiler_flags, - # NOTE We can't have profiling and dynamic code at the - # same time, see: - # https://ghc.haskell.org/trac/ghc/ticket/15394 - dynamic = False, - with_profiling = True, - main_function = ctx.attr.main_function, - version = ctx.attr.version, - plugins = ctx.attr.plugins, - ) - - (binary, solibs) = link_binary( - hs, - cc, - dep_info, - ctx.files.extra_srcs, - ctx.attr.compiler_flags, - c_p.objects_dir if with_profiling else c.objects_dir, - dynamic = False if hs.toolchain.is_windows else not ctx.attr.linkstatic, - with_profiling = with_profiling, - version = ctx.attr.version, - ) - - hs_info = HaskellInfo( - package_ids = dep_info.package_ids, - package_databases = dep_info.package_databases, - version_macros = set.empty(), - source_files = c.source_files, - extra_source_files = c.extra_source_files, - import_dirs = c.import_dirs, - static_libraries = dep_info.static_libraries, - static_libraries_prof = dep_info.static_libraries_prof, - dynamic_libraries = dep_info.dynamic_libraries, - interface_dirs = dep_info.interface_dirs, - compile_flags = c.compile_flags, - prebuilt_dependencies = dep_info.prebuilt_dependencies, - cc_dependencies = dep_info.cc_dependencies, - transitive_cc_dependencies = dep_info.transitive_cc_dependencies, - ) - cc_info = cc_common.merge_cc_infos( - cc_infos = [dep[CcInfo] for dep in ctx.attr.deps if CcInfo in dep], - ) - - target_files = depset([binary]) - - build_haskell_repl( - hs, - ghci_script = ctx.file._ghci_script, - ghci_repl_wrapper = ctx.file._ghci_repl_wrapper, - user_compile_flags = ctx.attr.compiler_flags, - repl_ghci_args = ctx.attr.repl_ghci_args, - output = ctx.outputs.repl, - package_databases = dep_info.package_databases, - version = ctx.attr.version, - hs_info = hs_info, - ) - - # XXX Temporary backwards compatibility hack. Remove eventually. - # See https://github.com/tweag/rules_haskell/pull/460. - ln(hs, ctx.outputs.repl, ctx.outputs.repl_deprecated) - - build_haskell_runghc( - hs, - runghc_wrapper = ctx.file._ghci_repl_wrapper, - extra_args = ctx.attr.runcompile_flags, - user_compile_flags = ctx.attr.compiler_flags, - output = ctx.outputs.runghc, - package_databases = dep_info.package_databases, - version = ctx.attr.version, - hs_info = hs_info, - ) - - executable = binary - extra_runfiles = [] - - if inspect_coverage: - binary_path = paths.join(ctx.workspace_name, binary.short_path) - hpc_path = paths.join(ctx.workspace_name, hs.toolchain.tools.hpc.short_path) - tix_file_path = hs.label.name + ".tix" - mix_file_paths = [ - paths.join(ctx.workspace_name, datum.mix_file.short_path) - for datum in coverage_data - ] - mix_file_paths = collections.uniq(mix_file_paths) # remove duplicates - - # find which modules to exclude from coverage analysis, by using the specified source patterns - raw_coverage_source_patterns = ctx.attr.experimental_coverage_source_patterns - coverage_source_patterns = [parse_pattern(ctx, pat) for pat in raw_coverage_source_patterns] - modules_to_exclude = [paths.split_extension(datum.mix_file.basename)[0] for datum in coverage_data if not _coverage_enabled_for_target(coverage_source_patterns, datum.target_label)] - modules_to_exclude = collections.uniq(modules_to_exclude) # remove duplicates - - expected_covered_expressions_percentage = ctx.attr.expected_covered_expressions_percentage - expected_uncovered_expression_count = ctx.attr.expected_uncovered_expression_count - strict_coverage_analysis = ctx.attr.strict_coverage_analysis - coverage_report_format = ctx.attr.coverage_report_format - - if coverage_report_format != "text" and coverage_report_format != "html": - fail("""haskell_test attribute "coverage_report_format" must be one of "text" or "html".""") - - wrapper = hs.actions.declare_file("{}_coverage/coverage_wrapper.sh".format(ctx.label.name)) - ctx.actions.expand_template( - template = ctx.file._coverage_wrapper_template, - output = wrapper, - substitutions = { - "{binary_path}": shell.quote(binary_path), - "{hpc_path}": shell.quote(hpc_path), - "{tix_file_path}": shell.quote(tix_file_path), - "{expected_covered_expressions_percentage}": str(expected_covered_expressions_percentage), - "{expected_uncovered_expression_count}": str(expected_uncovered_expression_count), - "{mix_file_paths}": shell.array_literal(mix_file_paths), - "{modules_to_exclude}": shell.array_literal(modules_to_exclude), - "{strict_coverage_analysis}": str(strict_coverage_analysis), - "{coverage_report_format}": shell.quote(ctx.attr.coverage_report_format), - "{package_path}": shell.quote(ctx.label.package), - }, - is_executable = True, - ) - executable = wrapper - mix_runfiles = [datum.mix_file for datum in coverage_data] - srcs_runfiles = [_condition_coverage_src(hs, datum.src_file) for datum in coverage_data] - extra_runfiles = [ - ctx.file._bash_runfiles, - hs.toolchain.tools.hpc, - binary, - ] + mix_runfiles + srcs_runfiles - - return [ - hs_info, - cc_info, - DefaultInfo( - executable = executable, - files = target_files, - runfiles = ctx.runfiles( - files = - solibs + - extra_runfiles, - collect_data = True, - ), - ), - ] - -def haskell_library_impl(ctx): - hs = haskell_context(ctx) - dep_info = gather_dep_info(ctx, ctx.attr.deps) - plugin_dep_info = gather_dep_info( - ctx, - [dep for plugin in ctx.attr.plugins for dep in plugin[GhcPluginInfo].deps], - ) - version = ctx.attr.version if ctx.attr.version else None - my_pkg_id = pkg_id.new(ctx.label, version) - with_profiling = is_profiling_enabled(hs) - with_shared = False if hs.toolchain.is_windows else not ctx.attr.linkstatic - - # Add any interop info for other languages. - cc = cc_interop_info(ctx) - java = java_interop_info(ctx) - - srcs_files, import_dir_map = _prepare_srcs(ctx.attr.srcs) - other_modules = ctx.attr.hidden_modules - exposed_modules_reexports = _exposed_modules_reexports(ctx.attr.exports) - - c = hs.toolchain.actions.compile_library( - hs, - cc, - java, - dep_info, - plugin_dep_info, - srcs = srcs_files, - ls_modules = ctx.executable._ls_modules, - other_modules = other_modules, - exposed_modules_reexports = exposed_modules_reexports, - import_dir_map = import_dir_map, - extra_srcs = depset(ctx.files.extra_srcs), - user_compile_flags = ctx.attr.compiler_flags, - with_shared = with_shared, - with_profiling = False, - my_pkg_id = my_pkg_id, - plugins = ctx.attr.plugins, - ) - - c_p = None - - if with_profiling: - c_p = hs.toolchain.actions.compile_library( - hs, - cc, - java, - dep_info, - plugin_dep_info, - srcs = srcs_files, - ls_modules = ctx.executable._ls_modules, - other_modules = other_modules, - exposed_modules_reexports = exposed_modules_reexports, - import_dir_map = import_dir_map, - # NOTE We must make the object files compiled without profiling - # available to this step for TH to work, presumably because GHC is - # linked against RTS without profiling. - extra_srcs = depset(transitive = [ - depset(ctx.files.extra_srcs), - depset([c.objects_dir]), - ]), - user_compile_flags = ctx.attr.compiler_flags, - # NOTE We can't have profiling and dynamic code at the - # same time, see: - # https://ghc.haskell.org/trac/ghc/ticket/15394 - with_shared = False, - with_profiling = True, - my_pkg_id = my_pkg_id, - plugins = ctx.attr.plugins, - ) - - static_library = link_library_static( - hs, - cc, - dep_info, - c.objects_dir, - my_pkg_id, - with_profiling = False, - ) - - if with_shared: - dynamic_library = link_library_dynamic( - hs, - cc, - dep_info, - depset(ctx.files.extra_srcs), - c.objects_dir, - my_pkg_id, - ) - dynamic_libraries = set.insert( - dep_info.dynamic_libraries, - dynamic_library, - ) - else: - dynamic_library = None - dynamic_libraries = dep_info.dynamic_libraries - - static_library_prof = None - if with_profiling: - static_library_prof = link_library_static( - hs, - cc, - dep_info, - c_p.objects_dir, - my_pkg_id, - with_profiling = True, - ) - - conf_file, cache_file = package( - hs, - dep_info, - c.interfaces_dir, - c_p.interfaces_dir if c_p != None else None, - static_library, - dynamic_library, - c.exposed_modules_file, - other_modules, - my_pkg_id, - static_library_prof = static_library_prof, - ) - - static_libraries_prof = dep_info.static_libraries_prof - - if static_library_prof != None: - static_libraries_prof = [static_library_prof] + dep_info.static_libraries_prof - - interface_dirs = set.union( - dep_info.interface_dirs, - set.singleton(c.interfaces_dir), - ) - - if c_p != None: - interface_dirs = set.mutable_union( - interface_dirs, - set.singleton(c_p.interfaces_dir), - ) - - version_macros = set.empty() - if version != None: - version_macros = set.singleton( - generate_version_macros(ctx, hs.name, version), - ) - - hs_info = HaskellInfo( - package_ids = set.insert(dep_info.package_ids, pkg_id.to_string(my_pkg_id)), - package_databases = set.insert(dep_info.package_databases, cache_file), - version_macros = version_macros, - source_files = c.source_files, - extra_source_files = c.extra_source_files, - import_dirs = c.import_dirs, - # NOTE We have to use lists for static libraries because the order is - # important for linker. Linker searches for unresolved symbols to the - # left, i.e. you first feed a library which has unresolved symbols and - # then you feed the library which resolves the symbols. - static_libraries = [static_library] + dep_info.static_libraries, - static_libraries_prof = static_libraries_prof, - dynamic_libraries = dynamic_libraries, - interface_dirs = interface_dirs, - compile_flags = c.compile_flags, - prebuilt_dependencies = dep_info.prebuilt_dependencies, - cc_dependencies = dep_info.cc_dependencies, - transitive_cc_dependencies = dep_info.transitive_cc_dependencies, - ) - lib_info = HaskellLibraryInfo( - package_id = pkg_id.to_string(my_pkg_id), - version = version, - ) - - dep_coverage_data = [] - for dep in ctx.attr.deps: - if HaskellCoverageInfo in dep: - dep_coverage_data += dep[HaskellCoverageInfo].coverage_data - - coverage_info = HaskellCoverageInfo( - coverage_data = dep_coverage_data + c.coverage_data, - ) - - target_files = depset([file for file in [static_library, dynamic_library] if file]) - - if hasattr(ctx, "outputs"): - build_haskell_repl( - hs, - ghci_script = ctx.file._ghci_script, - ghci_repl_wrapper = ctx.file._ghci_repl_wrapper, - repl_ghci_args = ctx.attr.repl_ghci_args, - user_compile_flags = ctx.attr.compiler_flags, - output = ctx.outputs.repl, - package_databases = dep_info.package_databases, - version = ctx.attr.version, - hs_info = hs_info, - lib_info = lib_info, - ) - - # XXX Temporary backwards compatibility hack. Remove eventually. - # See https://github.com/tweag/rules_haskell/pull/460. - ln(hs, ctx.outputs.repl, ctx.outputs.repl_deprecated) - - build_haskell_runghc( - hs, - runghc_wrapper = ctx.file._ghci_repl_wrapper, - extra_args = ctx.attr.runcompile_flags, - user_compile_flags = ctx.attr.compiler_flags, - output = ctx.outputs.runghc, - package_databases = dep_info.package_databases, - version = ctx.attr.version, - hs_info = hs_info, - lib_info = lib_info, - ) - - default_info = None - - if hasattr(ctx, "runfiles"): - default_info = DefaultInfo( - files = target_files, - runfiles = ctx.runfiles(collect_data = True), - ) - else: - default_info = DefaultInfo( - files = target_files, - ) - - # Create a CcInfo provider so that CC rules can work with - # a haskell library as if it was a regular CC one. - - # XXX Workaround https://github.com/bazelbuild/bazel/issues/6874. - # Should be find_cpp_toolchain() instead. - cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo] - feature_configuration = cc_common.configure_features( - cc_toolchain = cc_toolchain, - requested_features = ctx.features, - unsupported_features = ctx.disabled_features, - ) - library_to_link = cc_common.create_library_to_link( - actions = ctx.actions, - feature_configuration = feature_configuration, - dynamic_library = dynamic_library, - static_library = static_library, - cc_toolchain = cc_toolchain, - ) - compilation_context = cc_common.create_compilation_context() - linking_context = cc_common.create_linking_context( - libraries_to_link = [library_to_link], - ) - cc_info = cc_common.merge_cc_infos( - cc_infos = [ - CcInfo( - compilation_context = compilation_context, - linking_context = linking_context, - ), - ] + [dep[CcInfo] for dep in ctx.attr.deps if CcInfo in dep], - ) - - return [ - hs_info, - cc_info, - coverage_info, - default_info, - lib_info, - ] - -def haskell_toolchain_library_impl(ctx): - hs = haskell_context(ctx) - - if ctx.attr.package: - package = ctx.attr.package - else: - package = ctx.label.name - - id_file = hs.actions.declare_file(target_unique_name(hs, "id")) - hs.actions.run_shell( - inputs = [hs.tools.ghc_pkg], - outputs = [id_file], - command = """ - "$1" --simple-output -v1 field "$2" id > "$3" - """, - arguments = [ - hs.tools.ghc_pkg.path, - package, - id_file.path, - ], - ) - - version_macros_file = hs.actions.declare_file("{}_version_macros.h".format(hs.name)) - hs.actions.run_shell( - inputs = [hs.tools.ghc_pkg, ctx.executable._version_macros], - outputs = [version_macros_file], - command = """ - "$1" \\ - `"$2" --simple-output -v1 field "$3" name` \\ - `"$2" --simple-output -v1 field "$3" version` \\ - > "$4" - """, - arguments = [ - ctx.executable._version_macros.path, - hs.tools.ghc_pkg.path, - package, - version_macros_file.path, - ], - ) - - prebuilt_package_info = HaskellPrebuiltPackageInfo( - package = package, - id_file = id_file, - version_macros_file = version_macros_file, - ) - - return [prebuilt_package_info] - -def _exposed_modules_reexports(exports): - """Creates a ghc-pkg-compatible list of reexport declarations. - - A ghc-pkg registration file declares reexports as part of the - exposed-modules field in the following format: - - exposed-modules: A, B, C from pkg-c:C, D from pkg-d:Original.D - - Here, the Original.D module from pkg-d is renamed by virtue of a - different name being used before the "from" keyword. - - This function creates a ghc-pkg-compatible list of reexport declarations - (as shown above) from a dictionary mapping package targets to "Cabal-style" - reexported-modules declarations. That is, something like: - - { - ":pkg-c": "C", - ":pkg-d": "Original.D as D", - ":pkg-e": "E1, Original.E2 as E2", - } - - Args: - exports: a dictionary mapping package targets to "Cabal-style" - reexported-modules declarations. - - Returns: - a ghc-pkg-compatible list of reexport declarations. - """ - exposed_reexports = [] - for dep, cabal_decls in exports.items(): - for cabal_decl in cabal_decls.split(","): - stripped_cabal_decl = cabal_decl.strip() - cabal_decl_parts = stripped_cabal_decl.split(" as ") - original = cabal_decl_parts[0] - if len(cabal_decl_parts) == 2: - reexported = cabal_decl_parts[1] - else: - reexported = cabal_decl_parts[0] - - if HaskellPrebuiltPackageInfo in dep: - pkg = dep[HaskellPrebuiltPackageInfo].package - elif HaskellLibraryInfo in dep: - pkg = dep[HaskellLibraryInfo].package_id - - exposed_reexport = "{reexported} from {pkg}:{original}".format( - reexported = reexported, - pkg = pkg, - original = original, - ) - - exposed_reexports.append(exposed_reexport) - - return exposed_reexports |