Skip to content

Commit

Permalink
feat: extended native-image build options
Browse files Browse the repository at this point in the history
- 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 <[email protected]>
  • Loading branch information
sgammon committed Sep 4, 2023
1 parent d08e249 commit f66391a
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 1 deletion.
2 changes: 1 addition & 1 deletion MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions graal/graal.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand All @@ -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
)

Expand All @@ -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,
Expand Down Expand Up @@ -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.
Expand All @@ -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
)
11 changes: 11 additions & 0 deletions graalvm/nativeimage/options.bzl
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions graalvm/nativeimage/options/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -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,
)
3 changes: 3 additions & 0 deletions internal/native_image/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,14 @@ native_image_info(
name = "settings",
compiler_backend = ":compiler_backend",
libc = ":libc",
arch = "//graalvm/nativeimage/options:arch",
)

native_image_settings(
name = "defaults",
compiler_backend = DEFAULTS.COMPILER_BACKEND,
libc = DEFAULTS.LIBC,
opt = DEFAULTS.BUILD_OPT,
arch = DEFAULTS.ARCH,
settings = ":settings",
)
41 changes: 41 additions & 0 deletions internal/native_image/builder.bzl
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions internal/native_image/common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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(
Expand Down Expand Up @@ -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),
),
Expand Down
10 changes: 10 additions & 0 deletions internal/native_image/options/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package(default_visibility = [
"//graalvm/nativeimage/options:__pkg__",
"//graalvm/nativeimage/options:__subpackages__",
"//internal:__pkg__",
"//internal:__subpackages__",
])

exports_files([
"arch.bzl",
])
50 changes: 50 additions & 0 deletions internal/native_image/options/arch.bzl
Original file line number Diff line number Diff line change
@@ -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,
)
)
51 changes: 51 additions & 0 deletions internal/native_image/settings.bzl
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -15,19 +22,23 @@ _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(
fields = [
"opt",
"libc",
"compiler_backend",
"arch",
"settings",
],
doc = """
Defines a provider which keeps track of native image build settings,
Expand All @@ -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):
Expand All @@ -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(
Expand All @@ -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,
Expand Down Expand Up @@ -102,13 +130,31 @@ _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]],
),
},
)

_ALL_SETTINGS = [
"opt",
"libc",
"compiler_backend",
"arch",
"settings",
]

# Exports.
Expand All @@ -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
Expand Down
Loading

0 comments on commit f66391a

Please sign in to comment.