From ad11d1ec5ea879fd246c45db0b22920ca672bd5e Mon Sep 17 00:00:00 2001 From: Sam Gammon Date: Mon, 1 Jan 2024 21:30:51 -0800 Subject: [PATCH] feat: shared headers, `rules_cc` support This changeset adds `out_headers` and `additional_outputs`, which, together, allow a developer to add additional outputs to a regular `native_image` build. Because a header is generated per C entrypoint class, the developer needs to be able to declare as many as they want. Additionally, a `CcInfo` provider needs to be returned from the `native_image` target to facilitate downsream support for `rules_cc` targets. - feat: add `out_headers` and `additional_outputs` attributes - feat: add `default_outputs` attribute to opt-out of new behavior - feat: resolve and add `CcInfo` to shared library targets Signed-off-by: Sam Gammon --- docs/api/defs.md | 5 ++- graalvm/nativeimage/rules.bzl | 18 +++++++-- internal/native_image/classic.bzl | 5 ++- internal/native_image/common.bzl | 33 +++++++++++++++-- internal/native_image/rules.bzl | 61 +++++++++++++++++++++++++------ 5 files changed, 102 insertions(+), 20 deletions(-) diff --git a/docs/api/defs.md b/docs/api/defs.md index 01490fec..19176249 100755 --- a/docs/api/defs.md +++ b/docs/api/defs.md @@ -11,7 +11,7 @@ native_image(name, jni_configuration, initialize_at_build_time, initialize_at_run_time, native_features, debug, optimization_mode, shared_library, static_zlib, c_compiler_option, data, extra_args, allow_fallback, check_toolchains, native_image_tool, native_image_settings, - profiles, kwargs) + profiles, out_headers, additional_outputs, default_outputs, kwargs) Generates and compiles a GraalVM native image from a Java library target. @@ -43,6 +43,9 @@ Generates and compiles a GraalVM native image from a Java library target. | native_image_tool | Specific `native-image` executable target to use. | `None` | | native_image_settings | Suite(s) of Native Image build settings to use. | `[Label("@rules_graalvm//internal/native_image:defaults")]` | | profiles | Profiles to use for profile-guided optimization (PGO) and obtained from a native image compiled with `--pgo-instrument`. | `[]` | +| out_headers | Shared library headers expected to be emitted by the rule (in addition to defaults). | `[]` | +| additional_outputs | Additional outputs to expect from the rule (for example, polyglot language resources). | `[]` | +| default_outputs | Whether to consider default output files; when `False`, the developer specifies all outputs on top of the binary itself. | `True` | | kwargs | Extra keyword arguments are passed to the underlying `native_image` rule. | none | diff --git a/graalvm/nativeimage/rules.bzl b/graalvm/nativeimage/rules.bzl index 45ba9a7a..de004920 100644 --- a/graalvm/nativeimage/rules.bzl +++ b/graalvm/nativeimage/rules.bzl @@ -4,9 +4,12 @@ load( "@bazel_skylib//lib:dicts.bzl", "dicts", ) +load( + "@bazel_tools//tools/cpp:toolchain_utils.bzl", + "use_cpp_toolchain", +) load( "//internal/native_image:rules.bzl", - _BAZEL_CPP_TOOLCHAIN_TYPE = "BAZEL_CPP_TOOLCHAIN_TYPE", _DEBUG = "DEBUG_CONDITION", _GVM_TOOLCHAIN_TYPE = "GVM_TOOLCHAIN_TYPE", _NATIVE_IMAGE_ATTRS = "NATIVE_IMAGE_ATTRS", @@ -53,8 +56,7 @@ _native_image = rule( "platform", "xcode", ], - toolchains = [ - _BAZEL_CPP_TOOLCHAIN_TYPE, + toolchains = use_cpp_toolchain() + [ _GVM_TOOLCHAIN_TYPE, ], ) @@ -83,6 +85,9 @@ def native_image( native_image_tool = None, # uses toolchains by default native_image_settings = [_DEFAULT_NATIVE_IMAGE_SETTINGS], profiles = [], + out_headers = [], + additional_outputs = [], + default_outputs = True, **kwargs): """Generates and compiles a GraalVM native image from a Java library target. @@ -111,10 +116,14 @@ def native_image( data: Data files to make available during the compilation. No default; optional. extra_args: Extra `native-image` args to pass. Last wins. No default; optional. allow_fallback: Whether to allow fall-back to a partial native image; defaults to `False`. + out_headers: Shared library headers expected to be emitted by the rule (in addition to defaults). + additional_outputs: Additional outputs to expect from the rule (for example, polyglot language resources). check_toolchains: Whether to perform toolchain checks in `native-image`; defaults to `True` on Windows, `False` otherwise. native_image_tool: Specific `native-image` executable target to use. native_image_settings: Suite(s) of Native Image build settings to use. profiles: Profiles to use for profile-guided optimization (PGO) and obtained from a native image compiled with `--pgo-instrument`. + default_outputs: Whether to consider default output files; when `False`, the developer specifies all outputs on top of the + binary itself. **kwargs: Extra keyword arguments are passed to the underlying `native_image` rule. """ @@ -141,5 +150,8 @@ def native_image( native_image_tool = native_image_tool, native_image_settings = native_image_settings, profiles = profiles, + out_headers = out_headers, + additional_outputs = additional_outputs, + default_outputs = default_outputs, **kwargs ) diff --git a/internal/native_image/classic.bzl b/internal/native_image/classic.bzl index f7a78756..82699f47 100644 --- a/internal/native_image/classic.bzl +++ b/internal/native_image/classic.bzl @@ -50,13 +50,14 @@ def _graal_binary_classic_implementation(ctx): ) args = ctx.actions.args() - binary = _prepare_native_image_rule_context( + outputs = _prepare_native_image_rule_context( ctx, args, classpath_depset, direct_inputs, native_toolchain.c_compiler_path, ) + binary = outputs[0] inputs = depset( direct_inputs, @@ -64,7 +65,7 @@ def _graal_binary_classic_implementation(ctx): ) ctx.actions.run( - outputs = [binary], + outputs = outputs, arguments = [args], executable = graal, inputs = inputs, diff --git a/internal/native_image/common.bzl b/internal/native_image/common.bzl index f13132ea..4dc0e84c 100644 --- a/internal/native_image/common.bzl +++ b/internal/native_image/common.bzl @@ -8,11 +8,12 @@ load( _RULES_REPO = "@rules_graalvm" _DEFAULT_GVM_REPO = "@graalvm" _GVM_TOOLCHAIN_TYPE = "%s//graalvm/toolchain" % _RULES_REPO -_BAZEL_CPP_TOOLCHAIN_TYPE = "@bazel_tools//tools/cpp:toolchain_type" _BAZEL_CURRENT_CPP_TOOLCHAIN = "@bazel_tools//tools/cpp:current_cc_toolchain" _LINUX_CONSTRAINT = "@platforms//os:linux" _MACOS_CONSTRAINT = "@platforms//os:macos" _WINDOWS_CONSTRAINT = "@platforms//os:windows" +_GRAALVM_ISOLATE_HEADER = "graal_isolate.h" +_GRAALVM_ISOLATE_DYNAMIC_HEADER = "graal_isolate_dynamic.h" # buildifier: disable=name-conventions _NativeImageOptimization = struct( @@ -113,6 +114,11 @@ _NATIVE_IMAGE_ATTRS = { allow_files = True, mandatory = False, ), + "out_headers": attr.output_list(), + "additional_outputs": attr.output_list(), + "default_outputs": attr.bool( + default = True, + ), "_cc_toolchain": attr.label( default = Label(_BAZEL_CURRENT_CPP_TOOLCHAIN), ), @@ -154,6 +160,9 @@ def _prepare_native_image_rule_context( out_bin_name = ctx.attr.executable_name.replace("%target%", ctx.attr.name) binary = ctx.actions.declare_file(_prepare_bin_name(out_bin_name, bin_postfix)) + additional_outputs = [] + outputs = [binary] + # TODO: This check really should be on the exec platform, not the target platform, but that # requires going through a separate rule. Since GraalVM doesn't support cross-compilation, the # distinction doesn't matter for now. @@ -162,6 +171,25 @@ def _prepare_native_image_rule_context( else: path_list_separator = ":" + # if we are building a shared library, headers will be emitted; by default, the `graal_isolate.h` + # and `graal_isolate_dynamic.h` files are included. additional headers can be added by the `out_headers` + # attribute. + if ctx.attr.shared_library and ctx.attr.default_outputs: + additional_outputs.append(_GRAALVM_ISOLATE_HEADER) + additional_outputs.append(_GRAALVM_ISOLATE_DYNAMIC_HEADER) + + # append all additional outputs + if len(additional_outputs) > 0: + outputs.extend([ctx.actions.declare_file(f) for f in additional_outputs]) + + # append all out headers + if len(ctx.attr.out_headers) > 0: + outputs.extend(ctx.outputs.out_headers) + + # append all attr additional outputs + if len(ctx.attr.additional_outputs) > 0: + outputs.extend(ctx.outputs.additional_outputs) + _assemble_native_build_options( ctx, args, @@ -173,7 +201,7 @@ def _prepare_native_image_rule_context( gvm_toolchain, bin_postfix, ) - return binary + return outputs ## Exports. @@ -186,7 +214,6 @@ DEBUG_CONDITION = _DEBUG_CONDITION COVERAGE_CONDITION = _COVERAGE_CONDITION OPTIMIZATION_MODE_CONDITION = _OPTIMIZATION_MODE_CONDITION GVM_TOOLCHAIN_TYPE = _GVM_TOOLCHAIN_TYPE -BAZEL_CPP_TOOLCHAIN_TYPE = _BAZEL_CPP_TOOLCHAIN_TYPE BAZEL_CURRENT_CPP_TOOLCHAIN = _BAZEL_CURRENT_CPP_TOOLCHAIN MACOS_CONSTRAINT = _MACOS_CONSTRAINT WINDOWS_CONSTRAINT = _WINDOWS_CONSTRAINT diff --git a/internal/native_image/rules.bzl b/internal/native_image/rules.bzl index 5841baff..b009c110 100644 --- a/internal/native_image/rules.bzl +++ b/internal/native_image/rules.bzl @@ -4,9 +4,12 @@ load( "@build_bazel_apple_support//lib:apple_support.bzl", "apple_support", ) +load( + "@bazel_tools//tools/cpp:toolchain_utils.bzl", + "find_cpp_toolchain", +) load( "//internal/native_image:common.bzl", - _BAZEL_CPP_TOOLCHAIN_TYPE = "BAZEL_CPP_TOOLCHAIN_TYPE", _BAZEL_CURRENT_CPP_TOOLCHAIN = "BAZEL_CURRENT_CPP_TOOLCHAIN", _DEBUG_CONDITION = "DEBUG_CONDITION", _DEFAULT_GVM_REPO = "DEFAULT_GVM_REPO", @@ -103,7 +106,7 @@ def _graal_binary_implementation(ctx): bin_postfix = _BIN_POSTFIX_SO args = ctx.actions.args().use_param_file("@%s", use_always=False) - binary = _prepare_native_image_rule_context( + all_outputs = _prepare_native_image_rule_context( ctx, args, classpath_depset, @@ -112,6 +115,7 @@ def _graal_binary_implementation(ctx): gvm_toolchain, bin_postfix = bin_postfix, ) + binary = all_outputs[0] # assemble final inputs inputs = depset( @@ -119,7 +123,7 @@ def _graal_binary_implementation(ctx): transitive = transitive_inputs, ) run_params = { - "outputs": [binary], + "outputs": all_outputs, "executable": graal, "inputs": inputs, "mnemonic": "NativeImage", @@ -159,14 +163,50 @@ def _graal_binary_implementation(ctx): **run_params ) - return [DefaultInfo( + # if we are building a shared library, prepare `CcSharedLibraryInfo` for it + cc_info = [] + runfiles = None + if ctx.attr.shared_library: + cc_toolchain = find_cpp_toolchain(ctx) + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + requested_features = ctx.features, + unsupported_features = ctx.disabled_features, + ) + libraries_to_link = [ + cc_common.create_library_to_link( + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + dynamic_library = binary, + ), + ] + cc_info.extend([ + OutputGroupInfo( + main_shared_library_output = depset([binary]), + ), + CcSharedLibraryInfo( + linker_input = cc_common.create_linker_input( + owner = ctx.label, + libraries = depset(libraries_to_link), + ), + ), + ]) + + # prepare all runfiles + all_runfiles = ctx.runfiles( + collect_data = True, + collect_default = True, + files = [], + ) + if runfiles != None: + all_runfiles = all_runfiles.merge(runfiles) + + return cc_info + [DefaultInfo( executable = binary, - files = depset([binary]), - runfiles = ctx.runfiles( - collect_data = True, - collect_default = True, - files = [], - ), + files = depset(all_outputs), + runfiles = all_runfiles, )] def _wrap_actions_for_graal(actions): @@ -198,7 +238,6 @@ def _wrapped_run_for_graal(_original_actions, arguments = [], env = {}, **kwargs RULES_REPO = _RULES_REPO DEFAULT_GVM_REPO = _DEFAULT_GVM_REPO BAZEL_CURRENT_CPP_TOOLCHAIN = _BAZEL_CURRENT_CPP_TOOLCHAIN -BAZEL_CPP_TOOLCHAIN_TYPE = _BAZEL_CPP_TOOLCHAIN_TYPE NATIVE_IMAGE_ATTRS = _NATIVE_IMAGE_ATTRS GVM_TOOLCHAIN_TYPE = _GVM_TOOLCHAIN_TYPE DEBUG_CONDITION = _DEBUG_CONDITION