From e4e3b881f1f919a6a50553055bafdbf33188a4fb Mon Sep 17 00:00:00 2001 From: UebelAndre Date: Wed, 9 Apr 2025 07:45:36 -0700 Subject: [PATCH] Reorganized rules to match interface of Bazel rules --- perl/defs.bzl | 26 ++ perl/perl.bzl | 364 ++------------------------ perl/perl_binary.bzl | 8 + perl/perl_info.bzl | 8 + perl/perl_library.bzl | 8 + perl/perl_test.bzl | 8 + perl/private/BUILD | 1 + perl/{ => private}/binary_wrapper.tpl | 0 perl/private/perl.bzl | 203 ++++++++++++++ perl/private/perl_xs.bzl | 138 ++++++++++ perl/private/providers.bzl | 9 + 11 files changed, 428 insertions(+), 345 deletions(-) create mode 100644 perl/defs.bzl create mode 100644 perl/perl_binary.bzl create mode 100644 perl/perl_info.bzl create mode 100644 perl/perl_library.bzl create mode 100644 perl/perl_test.bzl rename perl/{ => private}/binary_wrapper.tpl (100%) create mode 100644 perl/private/perl.bzl create mode 100644 perl/private/perl_xs.bzl create mode 100644 perl/private/providers.bzl diff --git a/perl/defs.bzl b/perl/defs.bzl new file mode 100644 index 0000000..5e6e99d --- /dev/null +++ b/perl/defs.bzl @@ -0,0 +1,26 @@ +"""Perl rules for Bazel""" + +load( + "//perl/private:perl.bzl", + _perl_binary = "perl_binary", + _perl_library = "perl_library", + _perl_test = "perl_test", +) +load( + "//perl/private:perl_xs.bzl", + _perl_xs = "perl_xs", +) +load( + "//perl/private:providers.bzl", + _PerlInfo = "PerlInfo", +) + +PerlInfo = _PerlInfo +perl_binary = _perl_binary +perl_library = _perl_library +perl_test = _perl_test +perl_xs = _perl_xs + +# Keep this name around for legacy support. +# buildifier: disable=name-conventions +PerlLibrary = PerlInfo diff --git a/perl/perl.bzl b/perl/perl.bzl index f213106..f2aaeff 100644 --- a/perl/perl.bzl +++ b/perl/perl.bzl @@ -14,353 +14,27 @@ """Perl rules for Bazel""" -load("@bazel_skylib//lib:new_sets.bzl", "sets") -load("@bazel_skylib//lib:paths.bzl", "paths") -load("@rules_cc//cc:defs.bzl", "CcInfo", "cc_common") -load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain") - -# buildifier: disable=name-conventions -PerlLibrary = provider( - doc = "A provider containing components of a `perl_library`", - fields = [ - "transitive_perl_sources", - "includes", - ], +load( + "//perl/private:perl.bzl", + _perl_binary = "perl_binary", + _perl_library = "perl_library", + _perl_test = "perl_test", ) - -PERL_XS_COPTS = [ - "-fwrapv", - "-fPIC", - "-fno-strict-aliasing", - "-D_LARGEFILE_SOURCE", - "-D_FILE_OFFSET_BITS=64", -] - -_perl_file_types = [".pl", ".pm", ".t", ".so", ".ix", ".al", ""] -_perl_srcs_attr = attr.label_list(allow_files = _perl_file_types) - -_perl_deps_attr = attr.label_list( - allow_files = False, - providers = [PerlLibrary], -) - -_perl_data_attr = attr.label_list( - allow_files = True, +load( + "//perl/private:perl_xs.bzl", + _perl_xs = "perl_xs", ) - -_perl_main_attr = attr.label( - allow_single_file = _perl_file_types, +load( + "//perl/private:providers.bzl", + _PerlInfo = "PerlInfo", ) -_perl_env_attr = attr.string_dict() - -def _get_main_from_sources(ctx): - sources = ctx.files.srcs - if len(sources) != 1: - fail("Cannot infer main from multiple 'srcs'. Please specify 'main' attribute.", "main") - return sources[0] - -def _transitive_srcs(deps): - return struct( - srcs = [ - d[PerlLibrary].transitive_perl_sources - for d in deps - if PerlLibrary in d - ], - files = [ - d[DefaultInfo].default_runfiles.files - for d in deps - ], - ) - -def transitive_deps(ctx, extra_files = [], extra_deps = []): - """Calculates transitive sets of args. - - Calculates the transitive sets for perl sources, data runfiles, - include flags and runtime flags from the srcs, data and deps attributes - in the context. - - Also adds extra_deps to the roots of the traversal. - - Args: - ctx: a ctx object for a perl_library or a perl_binary rule. - extra_files: a list of File objects to be added to the default_files - extra_deps: a list of Target objects. - """ - deps = _transitive_srcs(ctx.attr.deps + extra_deps) - files = ctx.runfiles( - files = extra_files + ctx.files.srcs + ctx.files.data, - transitive_files = depset(transitive = deps.files), - collect_default = True, - ) - return struct( - srcs = depset( - direct = ctx.files.srcs, - transitive = deps.srcs, - ), - files = files, - ) - -def _include_paths(ctx): - """Calculate the PERL5LIB paths for a perl_library rule's includes.""" - workspace_name = ctx.label.workspace_name - if workspace_name: - workspace_root = "../" + workspace_name - else: - workspace_root = "" - package_root = (workspace_root + "/" + ctx.label.package).strip("/") or "." - include_paths = [package_root] if "." in ctx.attr.includes else [] - include_paths.extend([package_root + "/" + include for include in ctx.attr.includes if include != "."]) - for dep in ctx.attr.deps: - include_paths.extend(dep[PerlLibrary].includes) - include_paths = depset(direct = include_paths).to_list() - return include_paths - -def _perl_library_implementation(ctx): - transitive_sources = transitive_deps(ctx) - return [ - DefaultInfo( - runfiles = transitive_sources.files, - ), - PerlLibrary( - transitive_perl_sources = transitive_sources.srcs, - includes = _include_paths(ctx), - ), - ] - -def sum(items, initial): - result = initial - for item in items: - result += item - return result - -def _perl_binary_implementation(ctx): - toolchain = ctx.toolchains["@rules_perl//perl:toolchain_type"].perl_runtime - interpreter = toolchain.interpreter - - transitive_sources = transitive_deps(ctx, extra_files = toolchain.runtime + [ctx.outputs.executable]) - - main = ctx.file.main - if main == None: - main = _get_main_from_sources(ctx) - - include_paths = sum([dep[PerlLibrary].includes for dep in ctx.attr.deps], []) - perl5lib = ":" + ":".join(include_paths) if include_paths else "" - - ctx.actions.expand_template( - template = ctx.file._wrapper_template, - output = ctx.outputs.executable, - substitutions = { - "{PERL5LIB}": perl5lib, - "{env_vars}": _env_vars(ctx), - "{interpreter}": interpreter.short_path, - "{main}": main.short_path, - "{workspace_name}": ctx.label.workspace_name or ctx.workspace_name, - }, - is_executable = True, - ) - - return DefaultInfo( - executable = ctx.outputs.executable, - runfiles = transitive_sources.files, - ) - -def _env_vars(ctx): - environment = "" - for name, value in ctx.attr.env.items(): - if not _is_identifier(name): - fail("%s is not a valid environment variable name." % str(name)) - value = ctx.expand_location(value, targets = ctx.attr.data) - environment += ("{key}='{value}' ").format( - key = name, - value = value.replace("'", "\\'"), - ) - return environment - -def _is_identifier(name): - # Must be non-empty. - if name == None or len(name) == 0: - return False - - # Must start with alpha or '_' - if not (name[0].isalpha() or name[0] == "_"): - return False - - # Must consist of alnum characters or '_'s. - for c in name.elems(): - if not (c.isalnum() or c == "_"): - return False - return True - -def _perl_test_implementation(ctx): - return _perl_binary_implementation(ctx) +PerlInfo = _PerlInfo +perl_binary = _perl_binary +perl_library = _perl_library +perl_test = _perl_test +perl_xs = _perl_xs -def _perl_xs_cc_lib(ctx, toolchain, srcs): - cc_toolchain = find_cc_toolchain(ctx) - xs_headers = toolchain.xs_headers - - includes = [f.dirname for f in xs_headers.to_list()] - - textual_hdrs = [] - for hdrs in ctx.attr.textual_hdrs: - for hdr in hdrs.files.to_list(): - textual_hdrs.append(hdr) - includes.append(hdr.dirname) - - includes = sets.make(includes) - includes = sets.to_list(includes) - - feature_configuration = cc_common.configure_features( - ctx = ctx, - cc_toolchain = cc_toolchain, - requested_features = ctx.features, - unsupported_features = ctx.disabled_features, - ) - - (compilation_context, compilation_outputs) = cc_common.compile( - actions = ctx.actions, - name = ctx.label.name, - feature_configuration = feature_configuration, - cc_toolchain = cc_toolchain, - srcs = srcs + ctx.files.cc_srcs, - defines = ctx.attr.defines, - additional_inputs = textual_hdrs, - private_hdrs = xs_headers.to_list(), - includes = includes, - user_compile_flags = ctx.attr.copts + PERL_XS_COPTS, - compilation_contexts = [], - ) - - (linking_context, _linking_outputs) = cc_common.create_linking_context_from_compilation_outputs( - actions = ctx.actions, - name = ctx.label.name, - feature_configuration = feature_configuration, - cc_toolchain = cc_toolchain, - compilation_outputs = compilation_outputs, - user_link_flags = ctx.attr.linkopts, - linking_contexts = [], - ) - - return CcInfo( - compilation_context = compilation_context, - linking_context = linking_context, - ) - -def _perl_xs_implementation(ctx): - toolchain = ctx.toolchains["@rules_perl//perl:toolchain_type"].perl_runtime - xsubpp = toolchain.xsubpp - - toolchain_files = depset(toolchain.runtime) - - gen = [] - cc_infos = [] - args_typemaps = [] - - for typemap in ctx.files.typemaps: - args_typemaps += ["-typemap", typemap.short_path] - - for src in ctx.files.srcs: - c_execpath = paths.replace_extension(src.path, ".c") - o_packagepath = paths.join("_objs/execroot/", c_execpath) - out = ctx.actions.declare_file(o_packagepath) - - ctx.actions.run( - outputs = [out], - inputs = [src] + ctx.files.typemaps, - arguments = args_typemaps + ["-output", out.path, src.path], - progress_message = "Translitterating %s to %s" % (src.short_path, out.short_path), - executable = xsubpp, - tools = toolchain_files, - ) - - gen.append(out) - - cc_info = _perl_xs_cc_lib(ctx, toolchain, gen) - cc_infos = [cc_info] + [dep[CcInfo] for dep in ctx.attr.deps] - cc_info = cc_common.merge_cc_infos(cc_infos = cc_infos) - lib = cc_info.linking_context.linker_inputs.to_list()[0].libraries[0] - dyn_lib = lib.dynamic_library - - if len(ctx.attr.output_loc): - output = ctx.actions.declare_file(ctx.attr.output_loc) - else: - output = ctx.actions.declare_file(ctx.label.name + ".so") - - ctx.actions.run_shell( - outputs = [output], - inputs = [dyn_lib], - arguments = [dyn_lib.path, output.path], - command = "cp $1 $2", - ) - - return [ - cc_info, - DefaultInfo(files = depset([output])), - ] - -perl_library = rule( - attrs = { - "data": _perl_data_attr, - "deps": _perl_deps_attr, - "includes": attr.string_list(default = [".", "lib"]), - "srcs": _perl_srcs_attr, - }, - implementation = _perl_library_implementation, - toolchains = ["@rules_perl//perl:toolchain_type"], -) - -perl_binary = rule( - attrs = { - "data": _perl_data_attr, - "deps": _perl_deps_attr, - "env": _perl_env_attr, - "main": _perl_main_attr, - "srcs": _perl_srcs_attr, - "_wrapper_template": attr.label( - allow_single_file = True, - default = "binary_wrapper.tpl", - ), - }, - executable = True, - implementation = _perl_binary_implementation, - toolchains = ["@rules_perl//perl:toolchain_type"], -) - -perl_test = rule( - attrs = { - "data": _perl_data_attr, - "deps": _perl_deps_attr, - "env": _perl_env_attr, - "main": _perl_main_attr, - "srcs": _perl_srcs_attr, - "_wrapper_template": attr.label( - allow_single_file = True, - default = "binary_wrapper.tpl", - ), - }, - executable = True, - test = True, - implementation = _perl_test_implementation, - toolchains = ["@rules_perl//perl:toolchain_type"], -) - -perl_xs = rule( - attrs = { - "cc_srcs": attr.label_list(allow_files = [".c", ".cc"]), - "copts": attr.string_list(), - "defines": attr.string_list(), - "deps": attr.label_list(providers = [CcInfo]), - "linkopts": attr.string_list(), - "output_loc": attr.string(), - "srcs": attr.label_list(allow_files = [".xs"]), - "textual_hdrs": attr.label_list(allow_files = True), - "typemaps": attr.label_list(allow_files = True), - "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), - }, - implementation = _perl_xs_implementation, - fragments = ["cpp"], - toolchains = [ - "@rules_perl//perl:toolchain_type", - "@bazel_tools//tools/cpp:toolchain_type", - ], -) +# Keep this name around for legacy support. +# buildifier: disable=name-conventions +PerlLibrary = PerlInfo diff --git a/perl/perl_binary.bzl b/perl/perl_binary.bzl new file mode 100644 index 0000000..1b3adb7 --- /dev/null +++ b/perl/perl_binary.bzl @@ -0,0 +1,8 @@ +"""Perl rules for Bazel""" + +load( + "//perl/private:perl.bzl", + _perl_binary = "perl_binary", +) + +perl_binary = _perl_binary diff --git a/perl/perl_info.bzl b/perl/perl_info.bzl new file mode 100644 index 0000000..cacc61c --- /dev/null +++ b/perl/perl_info.bzl @@ -0,0 +1,8 @@ +"""PerlInfo""" + +load( + "//perl/private:providers.bzl", + _PerlInfo = "PerlInfo", +) + +PerlInfo = _PerlInfo diff --git a/perl/perl_library.bzl b/perl/perl_library.bzl new file mode 100644 index 0000000..4d48d10 --- /dev/null +++ b/perl/perl_library.bzl @@ -0,0 +1,8 @@ +"""perl_library""" + +load( + "//perl/private:perl.bzl", + _perl_library = "perl_library", +) + +perl_library = _perl_library diff --git a/perl/perl_test.bzl b/perl/perl_test.bzl new file mode 100644 index 0000000..5e7ec6e --- /dev/null +++ b/perl/perl_test.bzl @@ -0,0 +1,8 @@ +"""perl_test""" + +load( + "//perl/private:perl.bzl", + _perl_test = "perl_test", +) + +perl_test = _perl_test diff --git a/perl/private/BUILD b/perl/private/BUILD index e69de29..8ab63db 100644 --- a/perl/private/BUILD +++ b/perl/private/BUILD @@ -0,0 +1 @@ +exports_files(["binary_wrapper.tpl"]) diff --git a/perl/binary_wrapper.tpl b/perl/private/binary_wrapper.tpl similarity index 100% rename from perl/binary_wrapper.tpl rename to perl/private/binary_wrapper.tpl diff --git a/perl/private/perl.bzl b/perl/private/perl.bzl new file mode 100644 index 0000000..3e67b23 --- /dev/null +++ b/perl/private/perl.bzl @@ -0,0 +1,203 @@ +"""Perl rules for Bazel""" + +load(":providers.bzl", "PerlInfo") + +_PERL_FILE_TYPES = [".pl", ".pm", ".t", ".so", ".ix", ".al", ""] + +_COMMON_PERL_ATTRS = { + "data": attr.label_list( + doc = "Files needed by this rule at runtime. May list file or rule targets. Generally allows any target.", + allow_files = True, + ), + "deps": attr.label_list( + doc = "Other perl targets to link to the current target.", + allow_files = False, + providers = [PerlInfo], + ), + "srcs": attr.label_list( + doc = "The list of source files that are processed to create the target.", + allow_files = _PERL_FILE_TYPES, + ), +} + +_LIBRARY_PERL_ATTRS = _COMMON_PERL_ATTRS | { + "includes": attr.string_list( + doc = "List of include dirs to be added to the Perl include path (`PERL5LIB`).", + default = [".", "lib"], + ), +} + +_EXECUTABLE_PERL_ATTRS = _COMMON_PERL_ATTRS | { + "env": attr.string_dict( + doc = "Dictionary of strings; values are subject to `$(location)` and \"Make variable\" substitution.", + ), + "main": attr.label( + doc = "The name of the source file that is the main entry point of the application.", + allow_single_file = _PERL_FILE_TYPES, + ), + "_wrapper_template": attr.label( + allow_single_file = True, + default = Label("//perl/private:binary_wrapper.tpl"), + ), +} + +def _transitive_srcs(deps): + return struct( + srcs = [ + d[PerlInfo].transitive_perl_sources + for d in deps + if PerlInfo in d + ], + files = [ + d[DefaultInfo].default_runfiles.files + for d in deps + ], + ) + +def _transitive_deps(ctx, extra_files = [], extra_deps = []): + """Calculates transitive sets of args. + + Calculates the transitive sets for perl sources, data runfiles, + include flags and runtime flags from the srcs, data and deps attributes + in the context. + + Also adds extra_deps to the roots of the traversal. + + Args: + ctx: a ctx object for a perl_library or a perl_binary rule. + extra_files: a list of File objects to be added to the default_files + extra_deps: a list of Target objects. + """ + deps = _transitive_srcs(ctx.attr.deps + extra_deps) + files = ctx.runfiles( + files = extra_files + ctx.files.srcs + ctx.files.data, + transitive_files = depset(transitive = deps.files), + collect_default = True, + ) + return struct( + srcs = depset( + direct = ctx.files.srcs, + transitive = deps.srcs, + ), + files = files, + ) + +def _include_paths(ctx): + """Calculate the PERL5LIB paths for a perl_library rule's includes.""" + workspace_name = ctx.label.workspace_name + if workspace_name: + workspace_root = "../" + workspace_name + else: + workspace_root = "" + package_root = (workspace_root + "/" + ctx.label.package).strip("/") or "." + include_paths = [package_root] if "." in ctx.attr.includes else [] + include_paths.extend([package_root + "/" + include for include in ctx.attr.includes if include != "."]) + for dep in ctx.attr.deps: + include_paths.extend(dep[PerlInfo].includes) + include_paths = depset(direct = include_paths).to_list() + return include_paths + +def _perl_library_implementation(ctx): + transitive_sources = _transitive_deps(ctx) + return [ + DefaultInfo( + runfiles = transitive_sources.files, + ), + PerlInfo( + transitive_perl_sources = transitive_sources.srcs, + includes = _include_paths(ctx), + ), + ] + +perl_library = rule( + attrs = _LIBRARY_PERL_ATTRS, + implementation = _perl_library_implementation, + toolchains = ["@rules_perl//perl:toolchain_type"], +) + +def _get_main_from_sources(ctx): + sources = ctx.files.srcs + if len(sources) != 1: + fail("Cannot infer main from multiple 'srcs'. Please specify 'main' attribute.", "main") + return sources[0] + +def _is_identifier(name): + # Must be non-empty. + if name == None or len(name) == 0: + return False + + # Must start with alpha or '_' + if not (name[0].isalpha() or name[0] == "_"): + return False + + # Must consist of alnum characters or '_'s. + for c in name.elems(): + if not (c.isalnum() or c == "_"): + return False + return True + +def _env_vars(ctx): + environment = "" + for name, value in ctx.attr.env.items(): + if not _is_identifier(name): + fail("%s is not a valid environment variable name." % str(name)) + value = ctx.expand_location(value, targets = ctx.attr.data) + environment += ("{key}='{value}' ").format( + key = name, + value = value.replace("'", "\\'"), + ) + return environment + +def _perl_binary_implementation(ctx): + toolchain = ctx.toolchains["@rules_perl//perl:toolchain_type"].perl_runtime + interpreter = toolchain.interpreter + + transitive_sources = _transitive_deps( + ctx, + extra_files = toolchain.runtime + [ctx.outputs.executable], + ) + + main = ctx.file.main + if main == None: + main = _get_main_from_sources(ctx) + + include_paths = [] + for dep in ctx.attr.deps: + include_paths.extend(dep[PerlInfo].includes) + perl5lib = ":" + ":".join(include_paths) if include_paths else "" + + ctx.actions.expand_template( + template = ctx.file._wrapper_template, + output = ctx.outputs.executable, + substitutions = { + "{PERL5LIB}": perl5lib, + "{env_vars}": _env_vars(ctx), + "{interpreter}": interpreter.short_path, + "{main}": main.short_path, + "{workspace_name}": ctx.label.workspace_name or ctx.workspace_name, + }, + is_executable = True, + ) + + return DefaultInfo( + executable = ctx.outputs.executable, + runfiles = transitive_sources.files, + ) + +def _perl_test_implementation(ctx): + return _perl_binary_implementation(ctx) + +perl_binary = rule( + attrs = _EXECUTABLE_PERL_ATTRS, + executable = True, + implementation = _perl_binary_implementation, + toolchains = ["@rules_perl//perl:toolchain_type"], +) + +perl_test = rule( + attrs = _EXECUTABLE_PERL_ATTRS, + executable = True, + test = True, + implementation = _perl_test_implementation, + toolchains = ["@rules_perl//perl:toolchain_type"], +) diff --git a/perl/private/perl_xs.bzl b/perl/private/perl_xs.bzl new file mode 100644 index 0000000..ffa9c5d --- /dev/null +++ b/perl/private/perl_xs.bzl @@ -0,0 +1,138 @@ +"""Perl XS rules for Bazel""" + +load("@bazel_skylib//lib:new_sets.bzl", "sets") +load("@bazel_skylib//lib:paths.bzl", "paths") +load("@rules_cc//cc:defs.bzl", "CcInfo", "cc_common") +load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain") + +_PERL_XS_COPTS = [ + "-fwrapv", + "-fPIC", + "-fno-strict-aliasing", + "-D_LARGEFILE_SOURCE", + "-D_FILE_OFFSET_BITS=64", +] + +def _perl_xs_cc_lib(ctx, toolchain, srcs): + cc_toolchain = find_cc_toolchain(ctx) + xs_headers = toolchain.xs_headers + + includes = [f.dirname for f in xs_headers.to_list()] + + textual_hdrs = [] + for hdrs in ctx.attr.textual_hdrs: + for hdr in hdrs.files.to_list(): + textual_hdrs.append(hdr) + includes.append(hdr.dirname) + + includes = sets.make(includes) + includes = sets.to_list(includes) + + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + requested_features = ctx.features, + unsupported_features = ctx.disabled_features, + ) + + (compilation_context, compilation_outputs) = cc_common.compile( + actions = ctx.actions, + name = ctx.label.name, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + srcs = srcs + ctx.files.cc_srcs, + defines = ctx.attr.defines, + additional_inputs = textual_hdrs, + private_hdrs = xs_headers.to_list(), + includes = includes, + user_compile_flags = ctx.attr.copts + _PERL_XS_COPTS, + compilation_contexts = [], + ) + + (linking_context, _linking_outputs) = cc_common.create_linking_context_from_compilation_outputs( + actions = ctx.actions, + name = ctx.label.name, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + compilation_outputs = compilation_outputs, + user_link_flags = ctx.attr.linkopts, + linking_contexts = [], + ) + + return CcInfo( + compilation_context = compilation_context, + linking_context = linking_context, + ) + +def _perl_xs_implementation(ctx): + toolchain = ctx.toolchains["@rules_perl//perl:toolchain_type"].perl_runtime + xsubpp = toolchain.xsubpp + + toolchain_files = depset(toolchain.runtime) + + gen = [] + cc_infos = [] + args_typemaps = [] + + for typemap in ctx.files.typemaps: + args_typemaps += ["-typemap", typemap.short_path] + + for src in ctx.files.srcs: + c_execpath = paths.replace_extension(src.path, ".c") + o_packagepath = paths.join("_objs/execroot/", c_execpath) + out = ctx.actions.declare_file(o_packagepath) + + ctx.actions.run( + outputs = [out], + inputs = [src] + ctx.files.typemaps, + arguments = args_typemaps + ["-output", out.path, src.path], + progress_message = "Translitterating %s to %s" % (src.short_path, out.short_path), + executable = xsubpp, + tools = toolchain_files, + ) + + gen.append(out) + + cc_info = _perl_xs_cc_lib(ctx, toolchain, gen) + cc_infos = [cc_info] + [dep[CcInfo] for dep in ctx.attr.deps] + cc_info = cc_common.merge_cc_infos(cc_infos = cc_infos) + lib = cc_info.linking_context.linker_inputs.to_list()[0].libraries[0] + dyn_lib = lib.dynamic_library + + if len(ctx.attr.output_loc): + output = ctx.actions.declare_file(ctx.attr.output_loc) + else: + output = ctx.actions.declare_file(ctx.label.name + ".so") + + ctx.actions.run_shell( + outputs = [output], + inputs = [dyn_lib], + arguments = [dyn_lib.path, output.path], + command = "cp $1 $2", + ) + + return [ + cc_info, + DefaultInfo(files = depset([output])), + ] + +perl_xs = rule( + attrs = { + "cc_srcs": attr.label_list(allow_files = [".c", ".cc"]), + "copts": attr.string_list(), + "defines": attr.string_list(), + "deps": attr.label_list(providers = [CcInfo]), + "linkopts": attr.string_list(), + "output_loc": attr.string(), + "srcs": attr.label_list(allow_files = [".xs"]), + "textual_hdrs": attr.label_list(allow_files = True), + "typemaps": attr.label_list(allow_files = True), + "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), + }, + implementation = _perl_xs_implementation, + fragments = ["cpp"], + toolchains = [ + "@rules_perl//perl:toolchain_type", + "@bazel_tools//tools/cpp:toolchain_type", + ], +) diff --git a/perl/private/providers.bzl b/perl/private/providers.bzl new file mode 100644 index 0000000..1d4c3d2 --- /dev/null +++ b/perl/private/providers.bzl @@ -0,0 +1,9 @@ +"""Perl providers""" + +PerlInfo = provider( + doc = "A provider containing components of a `perl_library`", + fields = { + "includes": "list[str]: Include paths to add to `PERL5LIB`.", + "transitive_perl_sources": "list[File]: Transitive perl source dependencies.", + }, +)