diff options
Diffstat (limited to 'third_party/bazel/rules_haskell/haskell/private/path_utils.bzl')
-rw-r--r-- | third_party/bazel/rules_haskell/haskell/private/path_utils.bzl | 471 |
1 files changed, 0 insertions, 471 deletions
diff --git a/third_party/bazel/rules_haskell/haskell/private/path_utils.bzl b/third_party/bazel/rules_haskell/haskell/private/path_utils.bzl deleted file mode 100644 index 1162a95aebe1..000000000000 --- a/third_party/bazel/rules_haskell/haskell/private/path_utils.bzl +++ /dev/null @@ -1,471 +0,0 @@ -"""Utilities for module and path manipulations.""" - -load("@bazel_skylib//lib:paths.bzl", "paths") -load(":private/set.bzl", "set") - -def module_name(hs, f, rel_path = None): - """Given Haskell source file path, turn it into a dot-separated module name. - - module_name( - hs, - "some-workspace/some-package/src/Foo/Bar/Baz.hs", - ) => "Foo.Bar.Baz" - - Args: - hs: Haskell context. - f: Haskell source file. - rel_path: Explicit relative path from import root to the module, or None - if it should be deduced. - - Returns: - string: Haskell module name. - """ - - rpath = rel_path - - if not rpath: - rpath = _rel_path_to_module(hs, f) - - (hsmod, _) = paths.split_extension(rpath.replace("/", ".")) - return hsmod - -def target_unique_name(hs, name_prefix): - """Make a target-unique name. - - `name_prefix` is made target-unique by adding a rule name - suffix to it. This means that given two different rules, the same - `name_prefix` is distinct. Note that this is does not disambiguate two - names within the same rule. Given a haskell_library with name foo - you could expect: - - target_unique_name(hs, "libdir") => "libdir-foo" - - This allows two rules using same name_prefix being built in same - environment to avoid name clashes of their output files and directories. - - Args: - hs: Haskell context. - name_prefix: Template for the name. - - Returns: - string: Target-unique name_prefix. - """ - return "{0}-{1}".format(name_prefix, hs.name) - -def module_unique_name(hs, source_file, name_prefix): - """Make a target- and module- unique name. - - module_unique_name( - hs, - "some-workspace/some-package/src/Foo/Bar/Baz.hs", - "libdir" - ) => "libdir-foo-Foo.Bar.Baz" - - This is quite similar to `target_unique_name` but also uses a path built - from `source_file` to prevent clashes with other names produced using the - same `name_prefix`. - - Args: - hs: Haskell context. - source_file: Source file name. - name_prefix: Template for the name. - - Returns: - string: Target- and source-unique name. - """ - return "{0}-{1}".format( - target_unique_name(hs, name_prefix), - module_name(hs, source_file), - ) - -def declare_compiled(hs, src, ext, directory = None, rel_path = None): - """Given a Haskell-ish source file, declare its output. - - Args: - hs: Haskell context. - src: Haskell source file. - ext: New extension. - directory: String, directory prefix the new file should live in. - rel_path: Explicit relative path from import root to the module, or None - if it should be deduced. - - Returns: - File: Declared output file living in `directory` with given `ext`. - """ - - rpath = rel_path - - if not rpath: - rpath = _rel_path_to_module(hs, src) - - fp = paths.replace_extension(rpath, ext) - fp_with_dir = fp if directory == None else paths.join(directory, fp) - - return hs.actions.declare_file(fp_with_dir) - -def make_path(libs, prefix = None, sep = None): - """Return a string value for using as LD_LIBRARY_PATH or similar. - - Args: - libs: List of library files that should be available - prefix: String, an optional prefix to add to every path. - sep: String, the path separator, defaults to ":". - - Returns: - String: paths to the given library directories separated by ":". - """ - r = set.empty() - - sep = sep if sep else ":" - - for lib in libs: - lib_dir = paths.dirname(lib.path) - if prefix: - lib_dir = paths.join(prefix, lib_dir) - - set.mutable_insert(r, lib_dir) - - return sep.join(set.to_list(r)) - -def darwin_convert_to_dylibs(hs, libs): - """Convert .so dynamic libraries to .dylib. - - Bazel's cc_library rule will create .so files for dynamic libraries even - on MacOS. GHC's builtin linker, which is used during compilation, GHCi, - or doctests, hard-codes the assumption that all dynamic libraries on MacOS - end on .dylib. This function serves as an adaptor and produces symlinks - from a .dylib version to the .so version for every dynamic library - dependencies that does not end on .dylib. - - Args: - hs: Haskell context. - libs: List of library files dynamic or static. - - Returns: - List of library files where all dynamic libraries end on .dylib. - """ - lib_prefix = "_dylibs" - new_libs = [] - for lib in libs: - if is_shared_library(lib) and lib.extension != "dylib": - dylib_name = paths.join( - target_unique_name(hs, lib_prefix), - lib.dirname, - "lib" + get_lib_name(lib) + ".dylib", - ) - dylib = hs.actions.declare_file(dylib_name) - ln(hs, lib, dylib) - new_libs.append(dylib) - else: - new_libs.append(lib) - return new_libs - -def windows_convert_to_dlls(hs, libs): - """Convert .so dynamic libraries to .dll. - - Bazel's cc_library rule will create .so files for dynamic libraries even - on Windows. GHC's builtin linker, which is used during compilation, GHCi, - or doctests, hard-codes the assumption that all dynamic libraries on Windows - end on .dll. This function serves as an adaptor and produces symlinks - from a .dll version to the .so version for every dynamic library - dependencies that does not end on .dll. - - Args: - hs: Haskell context. - libs: List of library files dynamic or static. - - Returns: - List of library files where all dynamic libraries end on .dll. - """ - lib_prefix = "_dlls" - new_libs = [] - for lib in libs: - if is_shared_library(lib) and lib.extension != "dll": - dll_name = paths.join( - target_unique_name(hs, lib_prefix), - paths.dirname(lib.short_path), - "lib" + get_lib_name(lib) + ".dll", - ) - dll = hs.actions.declare_file(dll_name) - ln(hs, lib, dll) - new_libs.append(dll) - else: - new_libs.append(lib) - return new_libs - -def get_lib_name(lib): - """Return name of library by dropping extension and "lib" prefix. - - Args: - lib: The library File. - - Returns: - String: name of library. - """ - - base = lib.basename[3:] if lib.basename[:3] == "lib" else lib.basename - n = base.find(".so.") - end = paths.replace_extension(base, "") if n == -1 else base[:n] - return end - -def link_libraries(libs_to_link, args): - """Add linker flags to link against the given libraries. - - Args: - libs_to_link: List of library Files. - args: Append arguments to this list. - - Returns: - List of library names that were linked. - - """ - seen_libs = set.empty() - libraries = [] - for lib in libs_to_link: - lib_name = get_lib_name(lib) - if not set.is_member(seen_libs, lib_name): - set.mutable_insert(seen_libs, lib_name) - args += ["-l{0}".format(lib_name)] - libraries.append(lib_name) - -def is_shared_library(f): - """Check if the given File is a shared library. - - Args: - f: The File to check. - - Returns: - Bool: True if the given file `f` is a shared library, False otherwise. - """ - return f.extension in ["so", "dylib"] or f.basename.find(".so.") != -1 - -def is_static_library(f): - """Check if the given File is a static library. - - Args: - f: The File to check. - - Returns: - Bool: True if the given file `f` is a static library, False otherwise. - """ - return f.extension in ["a"] - -def _rel_path_to_module(hs, f): - """Make given file name relative to the directory where the module hierarchy - starts. - - _rel_path_to_module( - "some-workspace/some-package/src/Foo/Bar/Baz.hs" - ) => "Foo/Bar/Baz.hs" - - Args: - hs: Haskell context. - f: Haskell source file. - - Returns: - string: Relative path to module file. - """ - - # If it's a generated file, strip off the bin or genfiles prefix. - path = f.path - if path.startswith(hs.bin_dir.path): - path = paths.relativize(path, hs.bin_dir.path) - elif path.startswith(hs.genfiles_dir.path): - path = paths.relativize(path, hs.genfiles_dir.path) - - return paths.relativize(path, hs.src_root) - -# TODO Consider merging with paths.relativize. See -# https://github.com/bazelbuild/bazel-skylib/pull/44. -def _truly_relativize(target, relative_to): - """Return a relative path to `target` from `relative_to`. - - Args: - target: string, path to directory we want to get relative path to. - relative_to: string, path to directory from which we are starting. - - Returns: - string: relative path to `target`. - """ - t_pieces = target.split("/") - r_pieces = relative_to.split("/") - common_part_len = 0 - - for tp, rp in zip(t_pieces, r_pieces): - if tp == rp: - common_part_len += 1 - else: - break - - result = [".."] * (len(r_pieces) - common_part_len) - result += t_pieces[common_part_len:] - - return "/".join(result) - -def ln(hs, target, link, extra_inputs = depset()): - """Create a symlink to target. - - Args: - hs: Haskell context. - extra_inputs: extra phony dependencies of symlink. - - Returns: - None - """ - relative_target = _truly_relativize(target.path, link.dirname) - hs.actions.run_shell( - inputs = depset([target], transitive = [extra_inputs]), - outputs = [link], - mnemonic = "Symlink", - command = "ln -s {target} {link}".format( - target = relative_target, - link = link.path, - ), - use_default_shell_env = True, - ) - -def link_forest(ctx, srcs, basePath = ".", **kwargs): - """Write a symlink to each file in `srcs` into a destination directory - defined using the same arguments as `ctx.actions.declare_directory`""" - local_files = [] - for src in srcs.to_list(): - dest = ctx.actions.declare_file( - paths.join(basePath, src.basename), - **kwargs - ) - local_files.append(dest) - ln(ctx, src, dest) - return local_files - -def copy_all(ctx, srcs, dest): - """Copy all the files in `srcs` into `dest`""" - if list(srcs.to_list()) == []: - ctx.actions.run_shell( - command = "mkdir -p {dest}".format(dest = dest.path), - outputs = [dest], - ) - else: - args = ctx.actions.args() - args.add_all(srcs) - ctx.actions.run_shell( - inputs = depset(srcs), - outputs = [dest], - mnemonic = "Copy", - command = "mkdir -p {dest} && cp -L -R \"$@\" {dest}".format(dest = dest.path), - arguments = [args], - ) - -def parse_pattern(ctx, pattern_str): - """Parses a string label pattern. - - Args: - ctx: Standard Bazel Rule context. - - pattern_str: The pattern to parse. - Patterns are absolute labels in the local workspace. E.g. - `//some/package:some_target`. The following wild-cards are allowed: - `...`, `:all`, and `:*`. Also the `//some/package` shortcut is allowed. - - Returns: - A struct of - package: A list of package path components. May end on the wildcard `...`. - target: The target name. None if the package ends on `...`. May be one - of the wildcards `all` or `*`. - - NOTE: it would be better if Bazel itself exposed this functionality to Starlark. - - Any feature using this function should be marked as experimental, until the - resolution of https://github.com/bazelbuild/bazel/issues/7763. - """ - - # We only load targets in the local workspace anyway. So, it's never - # necessary to specify a workspace. Therefore, we don't allow it. - if pattern_str.startswith("@"): - fail("Invalid haskell_repl pattern. Patterns may not specify a workspace. They only apply to the current workspace") - - # To keep things simple, all patterns have to be absolute. - if not pattern_str.startswith("//"): - if not pattern_str.startswith(":"): - fail("Invalid haskell_repl pattern. Patterns must start with either '//' or ':'.") - - # if the pattern string doesn't start with a package (it starts with :, e.g. :two), - # then we prepend the contextual package - pattern_str = "//{package}{target}".format(package = ctx.label.package, target = pattern_str) - - # Separate package and target (if present). - package_target = pattern_str[2:].split(":", maxsplit = 2) - package_str = package_target[0] - target_str = None - if len(package_target) == 2: - target_str = package_target[1] - - # Parse package pattern. - package = [] - dotdotdot = False # ... has to be last component in the pattern. - for s in package_str.split("/"): - if dotdotdot: - fail("Invalid haskell_repl pattern. ... has to appear at the end.") - if s == "...": - dotdotdot = True - package.append(s) - - # Parse target pattern. - if dotdotdot: - if target_str != None: - fail("Invalid haskell_repl pattern. ... has to appear at the end.") - elif target_str == None: - if len(package) > 0 and package[-1] != "": - target_str = package[-1] - else: - fail("Invalid haskell_repl pattern. The empty string is not a valid target.") - - return struct( - package = package, - target = target_str, - ) - -def match_label(patterns, label): - """Whether the given local workspace label matches any of the patterns. - - Args: - patterns: A list of parsed patterns to match the label against. - Apply `parse_pattern` before passing patterns into this function. - label: Match this label against the patterns. - - Returns: - A boolean. True if the label is in the local workspace and matches any of - the given patterns. False otherwise. - - NOTE: it would be better if Bazel itself exposed this functionality to Starlark. - - Any feature using this function should be marked as experimental, until the - resolution of https://github.com/bazelbuild/bazel/issues/7763. - """ - - # Only local workspace labels can match. - # Despite the docs saying otherwise, labels don't have a workspace_name - # attribute. So, we use the workspace_root. If it's empty, the target is in - # the local workspace. Otherwise, it's an external target. - if label.workspace_root != "": - return False - - package = label.package.split("/") - target = label.name - - # Match package components. - for i in range(min(len(patterns.package), len(package))): - if patterns.package[i] == "...": - return True - elif patterns.package[i] != package[i]: - return False - - # If no wild-card or mismatch was encountered, the lengths must match. - # Otherwise, the label's package is not covered. - if len(patterns.package) != len(package): - return False - - # Match target. - if patterns.target == "all" or patterns.target == "*": - return True - else: - return patterns.target == target |