diff options
Diffstat (limited to 'third_party/bazel/rules_haskell/haskell/cc.bzl')
-rw-r--r-- | third_party/bazel/rules_haskell/haskell/cc.bzl | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/third_party/bazel/rules_haskell/haskell/cc.bzl b/third_party/bazel/rules_haskell/haskell/cc.bzl new file mode 100644 index 000000000000..1ec803764aab --- /dev/null +++ b/third_party/bazel/rules_haskell/haskell/cc.bzl @@ -0,0 +1,353 @@ +"""Interop with cc_* rules + +These rules are deprecated. +""" + +load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") +load( + "@bazel_tools//tools/build_defs/cc:action_names.bzl", + "CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME", + "C_COMPILE_ACTION_NAME", +) +load(":private/path_utils.bzl", "ln") +load("@bazel_skylib//lib:paths.bzl", "paths") +load(":private/set.bzl", "set") +load( + "@io_tweag_rules_haskell//haskell:providers.bzl", + "HaskellInfo", +) + +CcInteropInfo = provider( + doc = "Information needed for interop with cc rules.", + fields = { + "tools": "Tools from the CC toolchain", + # See the following for why this is needed: + # https://stackoverflow.com/questions/52769846/custom-c-rule-with-the-cc-common-api + "files": "Files for all tools (input to any action that uses tools)", + "hdrs": "CC headers", + "cpp_flags": "Preprocessor flags", + "compiler_flags": "Flags for compilation", + "linker_flags": "Flags to forward to the linker", + "include_args": "Extra include dirs", + }, +) + +def cc_interop_info(ctx): + """Gather information from any CC dependencies. + + *Internal function - do not use.* + + Args: + ctx: Rule context. + + Returns: + CcInteropInfo: Information needed for CC interop. + """ + ccs = [dep[CcInfo] for dep in ctx.attr.deps if CcInfo in dep and HaskellInfo not in dep] + + hdrs = [] + include_args = [] + cpp_flags = [] + for cc in ccs: + cc_ctx = cc.compilation_context + hdrs.append(cc_ctx.headers) + include_args.extend(["-I" + include for include in cc_ctx.includes]) + cpp_flags.extend( + [ + "-D" + define + for define in cc_ctx.defines + ] + [ + f + for include in cc_ctx.quote_includes + for f in ["-iquote", include] + ] + [ + f + for include in cc_ctx.system_includes + for f in ["-isystem", include] + ], + ) + + hdrs = depset(transitive = hdrs) + + # 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, + ) + compile_variables = cc_common.create_compile_variables( + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + ) + compiler_flags = cc_common.get_memory_inefficient_command_line( + feature_configuration = feature_configuration, + action_name = C_COMPILE_ACTION_NAME, + variables = compile_variables, + ) + link_variables = cc_common.create_link_variables( + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + is_linking_dynamic_library = False, + is_static_linking_mode = True, + ) + linker_flags = cc_common.get_memory_inefficient_command_line( + feature_configuration = feature_configuration, + action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, + variables = link_variables, + ) + + # Generate cc wrapper script on Darwin that adjusts load commands. + hs_toolchain = ctx.toolchains["@io_tweag_rules_haskell//haskell:toolchain"] + if hs_toolchain.is_darwin: + cc_wrapper = ctx.actions.declare_file("osx_cc_wrapper") + cc = cc_wrapper.path + ctx.actions.expand_template( + template = hs_toolchain.osx_cc_wrapper_tpl, + output = cc_wrapper, + substitutions = { + "%{cc}": cc_toolchain.compiler_executable(), + }, + ) + cc_files = ctx.files._cc_toolchain + [ + cc_wrapper, + ] + else: + cc = cc_toolchain.compiler_executable() + cc_files = ctx.files._cc_toolchain + + # XXX Workaround https://github.com/bazelbuild/bazel/issues/6876. + linker_flags = [flag for flag in linker_flags if flag not in ["-shared"]] + + tools = { + "ar": cc_toolchain.ar_executable(), + "cc": cc, + "ld": cc_toolchain.ld_executable(), + "cpp": cc_toolchain.preprocessor_executable(), + "nm": cc_toolchain.nm_executable(), + } + + # If running on darwin but XCode is not installed (i.e., only the Command + # Line Tools are available), then Bazel will make ar_executable point to + # "/usr/bin/libtool". Since we call ar directly, override it. + # TODO: remove this if Bazel fixes its behavior. + # Upstream ticket: https://github.com/bazelbuild/bazel/issues/5127. + if tools["ar"].find("libtool") >= 0: + tools["ar"] = "/usr/bin/ar" + + return CcInteropInfo( + tools = struct(**tools), + files = cc_files, + hdrs = hdrs.to_list(), + cpp_flags = cpp_flags, + include_args = include_args, + compiler_flags = compiler_flags, + # XXX this might not be the right set of flags for all situations, + # but this will anyways all be replaced (once implemented) by + # https://github.com/bazelbuild/bazel/issues/4571. + linker_flags = linker_flags, + ) + +def _cc_import_impl(ctx): + strip_prefix = ctx.attr.strip_include_prefix + + # cc_library's strip_include_prefix attribute accepts both absolute and + # relative paths. For simplicity we currently only implement absolute + # paths. + if strip_prefix.startswith("/"): + prefix = strip_prefix[1:] + else: + prefix = paths.join(ctx.label.workspace_root, ctx.label.package, strip_prefix) + + roots = set.empty() + for f in ctx.files.hdrs: + # If it's a generated file, strip off the bin or genfiles prefix. + path = f.path + if path.startswith(ctx.bin_dir.path): + path = paths.relativize(path, ctx.bin_dir.path) + elif path.startswith(ctx.genfiles_dir.path): + path = paths.relativize(path, ctx.genfiles_dir.path) + + if not path.startswith(prefix): + fail("Header {} does not have expected prefix {}".format( + path, + prefix, + )) + roots = set.insert(roots, f.root.path if f.root.path else ".") + + include_directories = [paths.join(root, prefix) for root in set.to_list(roots)] + + cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo] + feature_configuration = cc_common.configure_features(cc_toolchain = cc_toolchain) + + compilation_context = cc_common.create_compilation_context( + headers = depset(transitive = [l.files for l in ctx.attr.hdrs]), + includes = depset(direct = include_directories), + ) + linking_context = cc_common.create_linking_context( + libraries_to_link = [ + cc_common.create_library_to_link( + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + dynamic_library = f, + ) + for f in ctx.attr.shared_library.files + ], + ) + + return [ + CcInfo( + compilation_context = compilation_context, + linking_context = linking_context, + ), + ] + +haskell_cc_import = rule( + _cc_import_impl, + attrs = { + "shared_library": attr.label( + # NOTE We do not list all extensions here because .so libraries may + # have numeric suffixes like foo.so.1.2.3, and if they also have + # SONAME with numeric suffix, matching file must be provided, so this + # attributes must accept libraries with almost arbitrary extensions. + # It would be easier if Skylark supported regexps. + allow_files = True, + doc = """A single precompiled shared library. + +Bazel ensures it is available to the binary that depends on it +during runtime. +""", + ), + "hdrs": attr.label_list( + allow_files = [".h"], + doc = """ + +The list of header files published by this precompiled library to be +directly included by sources in dependent rules. +""", + ), + "strip_include_prefix": attr.string( + doc = """ +The prefix to strip from the paths of the headers of this rule. +When set, the headers in the `hdrs` attribute of this rule are +accessible at their path (relative to the repository) with this +prefix cut off. + +If it's a relative path, it's taken as a package-relative one. If it's an +absolute one, it's understood as a repository-relative path. +""", + ), + "_cc_toolchain": attr.label( + default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), + ), + }, +) +"""Imports a prebuilt shared library. + +Use this to make `.so`, `.dll`, `.dylib` files residing in external +[external repositories][bazel-ext-repos] available to Haskell rules. + +*This rule is temporary replacement for [cc_import][cc_import] and is +deprecated. Use [cc_library][cc_library] instead as shown in the example.* + +Example: + ```bzl + # Deprecated, use cc_library instead. + # haskell_cc_import(name = "zlib", shared_library = "@zlib//:lib") + + cc_library(name = "zlib", srcs = ["@zlib//:lib"]) + + haskell_import( + name = "base_pkg", + package = "base", + ) + + haskell_binary( + name = "crc32sum", + srcs = ["Main.hs"], + deps = [ + "bazel_pkg", + ":zlib", + ], + ) + ``` + +[bazel-ext-repos]: https://docs.bazel.build/versions/master/external.html +[cc_import]: https://docs.bazel.build/versions/master/be/c-cpp.html#cc_import +[cc_library]: https://docs.bazel.build/versions/master/be/c-cpp.html#cc_library +""" + +def _cc_haskell_import(ctx): + dyn_libs = set.empty() + + if HaskellInfo in ctx.attr.dep: + set.mutable_union(dyn_libs, ctx.attr.dep[HaskellInfo].dynamic_libraries) + else: + fail("{0} has to provide `HaskellInfo`".format(ctx.attr.dep.label.name)) + + return [ + DefaultInfo( + files = set.to_depset(dyn_libs), + default_runfiles = ctx.runfiles( + files = ctx.attr.dep.default_runfiles.files.to_list(), + collect_default = True, + ), + data_runfiles = ctx.runfiles( + files = ctx.attr.dep.data_runfiles.files.to_list(), + collect_data = True, + ), + ), + ] + +cc_haskell_import = rule( + _cc_haskell_import, + attrs = { + "dep": attr.label( + doc = """ +Target providing a `HaskellInfo` such as `haskell_library` or +`haskell_binary`. +""", + ), + }, + toolchains = ["@io_tweag_rules_haskell//haskell:toolchain"], +) +"""Exports a Haskell library as a CC library. + +Given a [haskell_library](#haskell_library) or +[haskell_binary](#haskell_binary) input, outputs the shared object files +produced as well as the object files it depends on directly and +transitively. This is very useful if you want to link in a Haskell shared +library from `cc_library`. + +There is a caveat: this will not provide any shared libraries that +aren't explicitly given to it. This means that if you're using +`prebuilt_dependencies` and relying on GHC to provide those objects, +they will not be present here. You will have to provide those +separately to your `cc_library`. If you're getting +`prebuilt_dependencies` from your toolchain, you will likely want to +extract those and pass them in as well. + +*This rule is deprecated.* + +Example: + ```bzl + haskell_library( + name = "my-lib", + ... + ) + + cc_haskell_import( + name = "my-lib-objects", + dep = ":my-lib", + ) + + cc_library( + name = "my-cc", + srcs = ["main.c", ":my-lib-objects"], + ) + ``` + +[bazel-cpp-sandwich]: https://github.com/bazelbuild/bazel/issues/2163 +""" |