From f66391ad36ba8e302cc4772f93b83f75abf5990d Mon Sep 17 00:00:00 2001 From: Sam Gammon Date: Sun, 3 Sep 2023 18:58:33 -0700 Subject: [PATCH] feat: extended native-image build options - feat: add experimental build setting which controls the `march` parameter - feat: explicit settings with `native_image_settings`, flag overrides with `native_image_info` - fix: preserve legacy rules `march` flag (missing by default) Not yet implemented: Needs integration with Bazel's platforms feature. Signed-off-by: Sam Gammon --- MODULE.bazel.lock | 2 +- graal/graal.bzl | 9 ++++ graalvm/nativeimage/options.bzl | 11 +++++ graalvm/nativeimage/options/BUILD.bazel | 14 +++++++ internal/native_image/BUILD.bazel | 3 ++ internal/native_image/builder.bzl | 41 ++++++++++++++++++ internal/native_image/common.bzl | 9 ++++ internal/native_image/options/BUILD.bazel | 10 +++++ internal/native_image/options/arch.bzl | 50 ++++++++++++++++++++++ internal/native_image/settings.bzl | 51 +++++++++++++++++++++++ lib/nativeimage/BUILD.bazel | 12 ++++++ 11 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 graalvm/nativeimage/options.bzl create mode 100644 graalvm/nativeimage/options/BUILD.bazel create mode 100644 internal/native_image/options/BUILD.bazel create mode 100644 internal/native_image/options/arch.bzl diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 4a099bb3..d7952cf7 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -2989,7 +2989,7 @@ } }, "//:extensions.bzl%graalvm": { - "bzlTransitiveDigest": "aQJryYP9wiwZXQ8JYwsuauQpVIo/X7CDOS2SiXt2rBI=", + "bzlTransitiveDigest": "qZN6woJ7KzJyFqM6aFbedUsBEN3WR4SctkhSvFbHfTo=", "accumulatedFileDigests": {}, "envVariables": {}, "generatedRepoSpecs": { diff --git a/graal/graal.bzl b/graal/graal.bzl index 80c99e4c..4c4850fa 100644 --- a/graal/graal.bzl +++ b/graal/graal.bzl @@ -63,6 +63,7 @@ def native_image( data = [], extra_args = [], allow_fallback = False, + native_arch = None, check_toolchains = select({ "@bazel_tools//src/conditions:windows": True, "//conditions:default": False, @@ -96,6 +97,8 @@ 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`. + native_arch: Provides native target architecture advice, like `march`; deaults to `None`, which yields to the default + behavior within GraalVM for architecture selection. Some earlier GraalVM versions may not support this flag. 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. **kwargs: Extra keyword arguments are passed to the underlying `native_image` rule. @@ -122,6 +125,7 @@ def native_image( allow_fallback = allow_fallback, executable_name = executable_name, native_image_tool = native_image_tool, + native_arch = native_arch, **kwargs ) @@ -147,6 +151,7 @@ def graal_binary( data = [], extra_args = [], allow_fallback = False, + native_arch = None, check_toolchains = select({ "@bazel_tools//src/conditions:windows": True, "//conditions:default": False, @@ -180,6 +185,8 @@ def graal_binary( 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`. + native_arch: Provides native target architecture advice, like `march`; deaults to `None`, which yields to the default + behavior within GraalVM for architecture selection. Some earlier GraalVM versions may not support this flag. 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. **kwargs: Extra keyword arguments are passed to the underlying `native_image` rule. @@ -205,5 +212,7 @@ def graal_binary( c_compiler_option = c_compiler_option, executable_name = executable_name, native_image_tool = native_image_tool, + allow_fallback = allow_fallback, + native_arch = native_arch, **kwargs ) diff --git a/graalvm/nativeimage/options.bzl b/graalvm/nativeimage/options.bzl new file mode 100644 index 00000000..7a031b0a --- /dev/null +++ b/graalvm/nativeimage/options.bzl @@ -0,0 +1,11 @@ +"Defines targets and enumerations for various Native Image builder options." + +load( + "//internal/native_image/options:arch.bzl", + _NativeImageArchitecture = "NativeImageArchitecture", +) + +# Exports + +# buildifier: disable=name-conventions +NativeImageArchitecture = _NativeImageArchitecture diff --git a/graalvm/nativeimage/options/BUILD.bazel b/graalvm/nativeimage/options/BUILD.bazel new file mode 100644 index 00000000..8b8c4704 --- /dev/null +++ b/graalvm/nativeimage/options/BUILD.bazel @@ -0,0 +1,14 @@ +load( + "//internal/native_image/options:arch.bzl", + "arch", + "NativeImageArchitecture", +) + +package(default_visibility = [ + "//visibility:public", +]) + +arch( + name = "arch", + build_setting_default = NativeImageArchitecture.COMPATIBILITY, +) diff --git a/internal/native_image/BUILD.bazel b/internal/native_image/BUILD.bazel index bee6d107..83bac3c2 100644 --- a/internal/native_image/BUILD.bazel +++ b/internal/native_image/BUILD.bazel @@ -48,6 +48,7 @@ native_image_info( name = "settings", compiler_backend = ":compiler_backend", libc = ":libc", + arch = "//graalvm/nativeimage/options:arch", ) native_image_settings( @@ -55,4 +56,6 @@ native_image_settings( compiler_backend = DEFAULTS.COMPILER_BACKEND, libc = DEFAULTS.LIBC, opt = DEFAULTS.BUILD_OPT, + arch = DEFAULTS.ARCH, + settings = ":settings", ) diff --git a/internal/native_image/builder.bzl b/internal/native_image/builder.bzl index 3ea1a326..84bca639 100644 --- a/internal/native_image/builder.bzl +++ b/internal/native_image/builder.bzl @@ -1,5 +1,14 @@ "Logic to assemble `native-image` options." +load( + "//internal/native_image:settings.bzl", + "NativeImageInfo", +) +load( + "//internal/native_image/options:arch.bzl", + "NativeImageArchInfo", +) + def _configure_static_zlib_compile(ctx, args, direct_inputs): """Configure a static image compile against hermetic/static zlib. @@ -115,6 +124,38 @@ def _configure_native_compiler(ctx, args, c_compiler_path, gvm_toolchain): if gvm_toolchain != None: args.add(c_compiler_path, format = "--native-compiler-path=%s") + # calculate the value to use for `march`, which should default to nothing + native_arch_advice = None + + # legacy rule specifies native arch with an explicit attribute + if ctx.attr.native_arch != None: + native_arch_info = ctx.attr.native_arch[NativeImageArchInfo] + if native_arch_info != None: + native_arch_advice = native_arch_info.target_arch + + # modern rule uses the `native_image_settings` target + if len(ctx.attr.native_image_settings) > 0: + + # loop through all settings, providing last-wins for native arch + for settings in ctx.attr.native_image_settings: + native_arch_explicit = None + native_arch_setting = None + + if settings[NativeImageInfo]: + native_arch_explicit = settings[NativeImageInfo].arch + + if settings[NativeImageInfo].settings: + native_arch_setting = ( + settings[NativeImageInfo].settings[NativeImageInfo].arch + ) + native_arch_advice = native_arch_setting or native_arch_explicit or native_arch_advice + + if native_arch_advice != None: + args.add( + native_arch_advice, + format = "-march=%s", + ) + # add custom compiler options args.add_all( ctx.attr.c_compiler_option, diff --git a/internal/native_image/common.bzl b/internal/native_image/common.bzl index 5c043a8d..f8a604b1 100644 --- a/internal/native_image/common.bzl +++ b/internal/native_image/common.bzl @@ -4,6 +4,10 @@ load( "//internal/native_image:builder.bzl", _assemble_native_build_options = "assemble_native_build_options", ) +load( + "//internal/native_image/options:arch.bzl", + "NativeImageArchInfo", +) _RULES_REPO = "@rules_graalvm" _DEFAULT_GVM_REPO = "@graalvm" @@ -13,6 +17,7 @@ _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" +_SETTING_TARGET_ARCH = "%s//graalvm/nativeimage/options:arch" % _RULES_REPO # buildifier: disable=name-conventions _NativeImageOptimization = struct( @@ -109,6 +114,10 @@ _NATIVE_IMAGE_ATTRS = { "executable_name": attr.string( mandatory = True, ), + "native_arch": attr.label( + default = Label(_SETTING_TARGET_ARCH), + providers = [[NativeImageArchInfo]], + ), "_cc_toolchain": attr.label( default = Label(_BAZEL_CURRENT_CPP_TOOLCHAIN), ), diff --git a/internal/native_image/options/BUILD.bazel b/internal/native_image/options/BUILD.bazel new file mode 100644 index 00000000..2f138a31 --- /dev/null +++ b/internal/native_image/options/BUILD.bazel @@ -0,0 +1,10 @@ +package(default_visibility = [ + "//graalvm/nativeimage/options:__pkg__", + "//graalvm/nativeimage/options:__subpackages__", + "//internal:__pkg__", + "//internal:__subpackages__", +]) + +exports_files([ + "arch.bzl", +]) diff --git a/internal/native_image/options/arch.bzl b/internal/native_image/options/arch.bzl new file mode 100644 index 00000000..08f20d1b --- /dev/null +++ b/internal/native_image/options/arch.bzl @@ -0,0 +1,50 @@ +"Defines provider info for various Native Image build options." + +_SETTING_ARCH_DEFAULT = "//graalvm/nativeimage/options:arch" + +# buildifier: disable=name-conventions +_NativeImageArchitecture = struct( + COMPATIBILITY = "compatibility", + NATIVE = "native", + AMD_64 = "amd64", + ARM_64 = "amd64", +) + +_NativeImageArchInfo = provider( + fields = ["target_arch"], + doc = "Provides target architecture selection support for GraalVM native image targets.", +) + +supported_architectures = [ + _NativeImageArchitecture.COMPATIBILITY, + _NativeImageArchitecture.NATIVE, + _NativeImageArchitecture.AMD_64, + _NativeImageArchitecture.ARM_64, +] + +def _native_image_arch_impl(ctx): + target_arch = ctx.build_setting_value + if target_arch not in supported_architectures: + fail(str(ctx.label) + " build setting allowed to take values {" + + ", ".join(supported_architectures) + "} but was set to unallowed value " + + target_arch) + + return _NativeImageArchInfo( + target_arch = target_arch, + ) + +# Exports. + +# buildifier: disable=name-conventions +NativeImageArchitecture = _NativeImageArchitecture + +NativeImageArchInfo = _NativeImageArchInfo + +SETTING_ARCH_DEFAULT = _SETTING_ARCH_DEFAULT + +arch = rule( + implementation = _native_image_arch_impl, + build_setting = config.string( + flag = True, + ) +) diff --git a/internal/native_image/settings.bzl b/internal/native_image/settings.bzl index 628fd8d6..be9ab015 100644 --- a/internal/native_image/settings.bzl +++ b/internal/native_image/settings.bzl @@ -1,5 +1,12 @@ "Defines a provider which exposes Native Image compilation setting info." +load( + "//internal/native_image/options:arch.bzl", + "NativeImageArchInfo", + _SETTING_ARCH_DEFAULT = "SETTING_ARCH_DEFAULT", + _NativeImageArchitecture = "NativeImageArchitecture", +) + # buildifier: disable=name-conventions _GraalVMLibC = struct( GLIBC = "glibc", @@ -15,12 +22,14 @@ _GraalVMCompilerBackend = struct( _DEFAULT_LIBC = _GraalVMLibC.GLIBC _DEFAULT_COMPILER = _GraalVMCompilerBackend.NATIVE _DEFAULT_BUILD_OPT = "2" # fully optimize +_DEFAULT_ARCH = _NativeImageArchitecture.COMPATIBILITY # buildifier: disable=name-conventions _DEFAULTS = struct( LIBC = _DEFAULT_LIBC, BUILD_OPT = _DEFAULT_BUILD_OPT, COMPILER_BACKEND = _DEFAULT_COMPILER, + ARCH = _DEFAULT_ARCH, ) _NativeImageInfo = provider( @@ -28,6 +37,8 @@ _NativeImageInfo = provider( "opt", "libc", "compiler_backend", + "arch", + "settings", ], doc = """ Defines a provider which keeps track of native image build settings, @@ -37,14 +48,17 @@ _NativeImageInfo = provider( opt: Optimization to use for this target and configuration. libc: Selected `libc` to build against; either `glibc` or `musl`. compiler_backend: Compiler backend to use; either `native` or `llvm`. + arch: Target native architecture setting to use. """, ) + def _gvm_nativeimage_info(ctx): """Provide info about a Native Image target's tooling settings.""" return _NativeImageInfo( # + arch = ctx.attr.arch[NativeImageArchInfo].target_arch, ) def _gvm_nativeimage_settings(ctx): @@ -54,6 +68,8 @@ def _gvm_nativeimage_settings(ctx): opt = ctx.attr.opt, libc = ctx.attr.libc, compiler_backend = ctx.attr.compiler_backend, + arch = ctx.attr.arch, + settings = ctx.attr.settings, ) _native_image_info = rule( @@ -67,11 +83,23 @@ _native_image_info = rule( doc = "Compiler backend setting to build against.", mandatory = True, ), + "arch": attr.label( + default = _SETTING_ARCH_DEFAULT, + doc = "Target native architecture advice (`march`). If unspecified, uses Bazel or GraalVM's defaults.", + mandatory = False, + ), }, ) _native_image_settings = rule( implementation = _gvm_nativeimage_settings, + doc = """ +Defines a package of Native Image build settings, which can be passed into a `native_image` +target in order to customize how the native image is generated and compiled. + +Optimizations settings, `libc`, the compiler backend, and target architecture advice can all +be set via the `native_image_settings` target. +""", attrs = { "opt": attr.string( default = _DEFAULT_BUILD_OPT, @@ -102,6 +130,22 @@ _native_image_settings = rule( _GraalVMCompilerBackend.LLVM, ], ), + "arch": attr.string( + default = _DEFAULT_ARCH, + doc = "Target native architecture advice (`march`). If unspecified, uses Bazel or GraalVM's defaults.", + mandatory = False, + values = [ + _NativeImageArchitecture.COMPATIBILITY, + _NativeImageArchitecture.NATIVE, + _NativeImageArchitecture.AMD_64, + _NativeImageArchitecture.ARM_64, + ], + ), + "settings": attr.label( + default = None, + doc = "Bazel settings which should override settings specified within this target.", + providers = [[_NativeImageInfo]], + ), }, ) @@ -109,6 +153,8 @@ _ALL_SETTINGS = [ "opt", "libc", "compiler_backend", + "arch", + "settings", ] # Exports. @@ -119,12 +165,17 @@ GraalVMLibC = _GraalVMLibC # buildifier: disable=name-conventions GraalVMCompilerBackend = _GraalVMCompilerBackend +# buildifier: disable=name-conventions +NativeImageArchitecture = _NativeImageArchitecture + NativeImageInfo = _NativeImageInfo ALL_SETTINGS = _ALL_SETTINGS DEFAULT_LIBC = _DEFAULT_LIBC +DEFAULT_SETTING_ARCH = _SETTING_ARCH_DEFAULT DEFAULT_COMPILER = _DEFAULT_COMPILER DEFAULT_BUILD_OPT = _DEFAULT_BUILD_OPT +DEFAULT_ARCH = _DEFAULT_ARCH DEFAULTS = _DEFAULTS native_image_settings = _native_image_settings diff --git a/lib/nativeimage/BUILD.bazel b/lib/nativeimage/BUILD.bazel index 6ca41d9a..2db9821f 100644 --- a/lib/nativeimage/BUILD.bazel +++ b/lib/nativeimage/BUILD.bazel @@ -13,6 +13,9 @@ package(default_visibility = [ bzl_library( name = "builder", srcs = ["//internal/native_image:builder.bzl"], + deps = [ + ":options", + ], ) bzl_library( @@ -42,6 +45,7 @@ bzl_library( ":internals", ":settings", ":toolchain", + ":options", "//lib:tooling", "@bazel_skylib//lib:dicts", ], @@ -50,6 +54,9 @@ bzl_library( bzl_library( name = "settings", srcs = ["//internal/native_image:settings.bzl"], + deps = [ + "//lib/nativeimage:options", + ], ) bzl_library( @@ -67,3 +74,8 @@ bzl_library( "@bazel_skylib//lib:dicts", ], ) + +bzl_library( + name = "options", + srcs = ["//internal/native_image/options:arch.bzl"], +)