feat: Introduce stable Bazel build

The initial migration to a Bazel based build. All attempts have been made
to maintain configuration parity with the upstream build.
This commit is contained in:
Connor McEntee 2025-02-22 11:27:13 -07:00
parent 54eaed1d8b
commit 7f192e9919
29 changed files with 6564 additions and 1 deletions

57
settings/BUILD.bazel Normal file
View file

@ -0,0 +1,57 @@
load("@bazel_skylib//rules:common_settings.bzl", "bool_setting", "string_setting")
# JEMALLOC_JET - Explicitly used for testing
bool_setting(
name = "enable_jet",
build_setting_default = False,
visibility = ["//:__subpackages__"],
)
config_setting(
name = "jet",
flag_values = {":enable_jet": "True"},
)
# JEMALLOC_NO_PRIVATE_NAMESPACE - To create a private namespace, jemalloc first has to compile without one and
# identify all of the exported symbols.
bool_setting(
name = "enable_no_private_namespace",
build_setting_default = False,
visibility = ["//:__subpackages__"],
)
config_setting(
name = "no_private_namespace",
flag_values = {":enable_no_private_namespace": "True"},
)
# Control defines for compilation under different testing scenarios
string_setting(
name = "with_test",
build_setting_default = "False",
values = [
"False",
"unit",
"integration",
"integration_cpp",
],
visibility = ["//:__subpackages__"],
)
# JEMALLOC_UNIT_TEST
config_setting(
name = "unit_test",
flag_values = {":with_test": "unit"},
)
# JEMALLOC_INTEGRATION_TEST
config_setting(
name = "integration_test",
flag_values = {":with_test": "integration"},
)
# JEMALLOC_INTEGRATION_CPP_TEST
config_setting(
name = "integration_cpp_test",
flag_values = {":with_test": "integration_cpp"},
)

View file

@ -0,0 +1,44 @@
load("@bazel_skylib//lib:selects.bzl", "selects")
selects.config_setting_group(
name = "gcc_compatible",
match_any = [
"@rules_cc//cc/compiler:clang",
"@rules_cc//cc/compiler:clang-cl",
"@rules_cc//cc/compiler:gcc",
"@rules_cc//cc/compiler:mingw-gcc",
"@rules_cc//cc/compiler:emscripten",
],
visibility = ["//:__subpackages__"],
)
selects.config_setting_group(
name = "gcc_core",
match_any = [
"@rules_cc//cc/compiler:gcc",
"@rules_cc//cc/compiler:mingw-gcc",
],
visibility = ["//:__subpackages__"],
)
# GCC-like and target native CPU architectures
# Equivalent of configure.ac "x$GCC" != "xyes"
selects.config_setting_group(
name = "gcc_native",
match_any = [
"@rules_cc//cc/compiler:clang",
"@rules_cc//cc/compiler:gcc",
"@rules_cc//cc/compiler:mingw-gcc",
],
visibility = ["//:__subpackages__"],
)
# Compatibility with MSVCs conventions (intrinsics, frontend).
selects.config_setting_group(
name = "msvc_compatible",
match_any = [
"@rules_cc//cc/compiler:clang-cl",
"@rules_cc//cc/compiler:msvc-cl",
],
visibility = ["//:__subpackages__"],
)

109
settings/config.bzl Normal file
View file

@ -0,0 +1,109 @@
"""Public API for configuring jemalloc build settings."""
load("@rules_cc//cc:defs.bzl", "CcInfo")
def _jemalloc_config_impl(ctx):
target = ctx.attr._target[0]
providers = [DefaultInfo, CcInfo, InstrumentedFilesInfo, OutputGroupInfo]
return [target[provider] for provider in providers if provider in target]
def _jemalloc_transition_impl(settings, attr):
"""Transition implementation that applies user-provided settings."""
result = settings | {}
# Boolean flags
bool_flags = {
"//settings/flags:enable_cache_oblivious": attr.cache_oblivious,
"//settings/flags:enable_cxx": attr.cxx,
"//settings/flags:enable_experimental_smallocx": attr.experimental_smallocx,
"//settings/flags:enable_fill": attr.fill,
"//settings/flags:enable_lazy_lock": attr.lazy_lock,
"//settings/flags:enable_log": attr.log,
"//settings/flags:enable_stats": attr.stats,
"//settings/flags:enable_uaf_detection": attr.uaf_detection,
"//settings/flags:enable_xmalloc": attr.xmalloc,
"//settings/flags:without_export": attr.without_export,
}
for flag, value in bool_flags.items():
if value == "True":
result[flag] = True
elif value == "False":
result[flag] = False
# String flags
result["//settings/flags:jemalloc_prefix"] = attr.jemalloc_prefix
if attr.zone_allocator != "":
result["//settings/flags:enable_zone_allocator"] = attr.zone_allocator
# Integer flags
if attr.lg_quantum != -1:
result["//settings/flags:lg_quantum"] = attr.lg_quantum
return result
_jemalloc_transition = transition(
implementation = _jemalloc_transition_impl,
inputs = [
"//settings/flags:enable_cache_oblivious",
"//settings/flags:enable_cxx",
"//settings/flags:enable_experimental_smallocx",
"//settings/flags:enable_fill",
"//settings/flags:enable_lazy_lock",
"//settings/flags:enable_log",
"//settings/flags:enable_stats",
"//settings/flags:enable_uaf_detection",
"//settings/flags:enable_xmalloc",
"//settings/flags:without_export",
"//settings/flags:jemalloc_prefix",
"//settings/flags:enable_zone_allocator",
"//settings/flags:lg_quantum",
],
outputs = [
"//settings/flags:enable_cache_oblivious",
"//settings/flags:enable_cxx",
"//settings/flags:enable_experimental_smallocx",
"//settings/flags:enable_fill",
"//settings/flags:enable_lazy_lock",
"//settings/flags:enable_log",
"//settings/flags:enable_stats",
"//settings/flags:enable_uaf_detection",
"//settings/flags:enable_xmalloc",
"//settings/flags:without_export",
"//settings/flags:jemalloc_prefix",
"//settings/flags:enable_zone_allocator",
"//settings/flags:lg_quantum",
],
)
jemalloc = rule(
doc = "Convenience rule that returns the jemalloc cc_library transitioned the optional build settings based on user-provided constraints.",
implementation = _jemalloc_config_impl,
attrs = {
"cache_oblivious": attr.string(values = ["True", "False", ""]),
"cxx": attr.string(values = ["True", "False", ""]),
"experimental_smallocx": attr.string(values = ["True", "False", ""]),
"fill": attr.string(values = ["True", "False", ""]),
"jemalloc_prefix": attr.string(),
"lazy_lock": attr.string(values = ["True", "False", ""]),
"lg_quantum": attr.int(default = -1),
"log": attr.string(values = ["True", "False", ""]),
"stats": attr.string(values = ["True", "False", ""]),
"uaf_detection": attr.string(values = ["True", "False", ""]),
"without_export": attr.string(values = ["True", "False", ""]),
"xmalloc": attr.string(values = ["True", "False", ""]),
"zone_allocator": attr.string(values = ["yes", "no", "__auto__"]),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
"_target": attr.label(
doc = "The jemalloc cc_library target to configure.",
default = "//:jemalloc",
providers = [CcInfo],
cfg = _jemalloc_transition,
),
},
provides = [CcInfo],
)

182
settings/flags/BUILD.bazel Normal file
View file

@ -0,0 +1,182 @@
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "int_flag", "string_flag", "string_setting")
### Bazel-ification of optional flags accepted by ./configure ###
# JEMALLOC_CACHE_OBLIVIOUS
bool_flag(
name = "enable_cache_oblivious",
build_setting_default = True,
visibility = ["//visibility:public"],
)
config_setting(
name = "cache_oblivious",
flag_values = {":enable_cache_oblivious": "True"},
)
# JEMALLOC_ENABLE_CXX
bool_flag(
name = "enable_cxx",
build_setting_default = True,
visibility = ["//visibility:public"],
)
config_setting(
name = "cxx",
flag_values = {":enable_cxx": "True"},
)
# JEMALLOC_DEBUG
config_setting(
name = "debug",
values = {"compilation_mode": "dbg"},
)
# JEMALLOC_EXPERIMENTAL_SMALLOCX_API
bool_flag(
name = "enable_experimental_smallocx",
build_setting_default = False,
visibility = ["//visibility:public"],
)
config_setting(
name = "experimental_smallocx_api",
flag_values = {":enable_experimental_smallocx": "True"},
)
# JEMALLOC_FILL (support for junk/zero filling)
bool_flag(
name = "enable_fill",
build_setting_default = True,
visibility = ["//visibility:public"],
)
config_setting(
name = "fill",
flag_values = {":enable_fill": "True"},
)
# JEMALLOC_LAZY_LOCK
bool_flag(
name = "enable_lazy_lock",
build_setting_default = False,
visibility = ["//visibility:public"],
)
config_setting(
name = "lazy_lock",
flag_values = {":enable_lazy_lock": "True"},
)
# JEMALLOC_LOG
bool_flag(
name = "enable_log",
build_setting_default = False,
visibility = ["//visibility:public"],
)
config_setting(
name = "log",
flag_values = {":enable_log": "True"},
)
# JEMALLOC_STATS
bool_flag(
name = "enable_stats",
build_setting_default = True,
visibility = ["//visibility:public"],
)
config_setting(
name = "stats",
flag_values = {":enable_stats": "True"},
)
# JEMALLOC_UAF_DETECTION
bool_flag(
name = "enable_uaf_detection",
build_setting_default = False,
visibility = ["//visibility:public"],
)
config_setting(
name = "uaf_detection",
flag_values = {":enable_uaf_detection": "True"},
)
# JEMALLOC_XMALLOC
bool_flag(
name = "enable_xmalloc",
build_setting_default = False,
visibility = ["//visibility:public"],
)
config_setting(
name = "xmalloc",
flag_values = {":enable_xmalloc": "True"},
)
# JEMALLOC_PREFIX optionally set via --with-jemalloc-prefix
string_flag(
name = "jemalloc_prefix",
build_setting_default = "",
make_variable = "JEMALLOC_PREFIX",
visibility = ["//visibility:public"],
)
config_setting(
name = "no_jemalloc_prefix",
flag_values = {":jemalloc_prefix": ""},
)
# JEMALLOC_CPREFIX Uppercase version of JEMALLOC_PREFIX but can't easily be set during templating so uses a separate
# setting to handle it so string manipulation can be stored when the configurable prefix is processed.
string_setting(
name = "jemalloc_cprefix",
build_setting_default = "",
make_variable = "JEMALLOC_CPREFIX",
visibility = ["//:__subpackages__"],
)
# LG_QUANTUM Base 2 log of minimum allocation alignment (--with-lg-quantum), -1 is undefined for resolution internal
# to the headers
int_flag(
name = "lg_quantum",
build_setting_default = -1,
make_variable = "LG_QUANTUM",
visibility = ["//visibility:public"],
)
config_setting(
name = "no_lg_quantum",
flag_values = {":lg_quantum": "-1"},
)
# JEMALLOC_EXPORT via --without-export which disables the export of the jemalloc API
bool_flag(
name = "without_export",
build_setting_default = False,
visibility = ["//visibility:public"],
)
config_setting(
name = "export",
flag_values = {":without_export": "True"},
)
# JEMALLOC_ZONE Enable/Disable zone allocator for Darwin
string_flag(
name = "enable_zone_allocator",
build_setting_default = "__auto__",
values = [
"yes",
"no",
"__auto__",
],
visibility = ["//visibility:public"],
)
config_setting(
name = "zone_allocator",
flag_values = {":enable_zone_allocator": "yes"},
)

View file

@ -0,0 +1,214 @@
load("@bazel_skylib//lib:selects.bzl", "selects")
load("@bazel_skylib//rules:common_settings.bzl", "int_flag", "string_flag")
### Bazel-ification of platform feature detection performed by ./configure ###
# If "yes", the glibc hooks __libc_calloc, __libc_free, __libc_malloc, etc are available for override
string_flag(
name = "glibc_overrides_support",
build_setting_default = "__auto__",
values = [
"yes",
"no",
"__auto__",
],
visibility = ["//visibility:public"],
)
config_setting(
name = "has_glibc_overrides",
flag_values = {":glibc_overrides_support": "yes"},
visibility = ["//visibility:public"],
)
config_setting(
name = "no_glibc_overrides",
flag_values = {":glibc_overrides_support": "no"},
visibility = ["//visibility:public"],
)
# Whether the target libc has `malloc_size` available for linking
string_flag(
name = "malloc_size_support",
build_setting_default = "__auto__",
values = [
"yes",
"no",
"__auto__",
],
visibility = ["//visibility:public"],
)
config_setting(
name = "has_malloc_size",
flag_values = {":malloc_size_support": "yes"},
visibility = ["//visibility:public"],
)
config_setting(
name = "no_malloc_size",
flag_values = {":malloc_size_support": "no"},
visibility = ["//visibility:public"],
)
# JEMALLOC_USABLE_SIZE_CONST: Whether malloc_usable_size can accepts const, glibc on linux apparently omits it
string_flag(
name = "usable_size_const",
build_setting_default = "__auto__",
make_variable = "JEMALLOC_USABLE_SIZE_CONST",
values = [
"",
"const",
"__auto__",
],
visibility = ["//visibility:public"],
)
string_flag(
name = "memalign_support",
build_setting_default = "__auto__",
values = [
"yes",
"no",
"__auto__",
],
visibility = ["//visibility:public"],
)
config_setting(
name = "has_memalign",
flag_values = {":memalign_support": "yes"},
visibility = ["//visibility:public"],
)
config_setting(
name = "no_memalign",
flag_values = {":memalign_support": "no"},
visibility = ["//visibility:public"],
)
# LG_HUGEPAGE Base 2 log of system huge page size, can be explicitly set --with-lg-hugepage
# The correct setting depends on the kernel. configure defaults to 21 (2 MiB) when detection fails and
# there is runtime detection to gracefully handle a misconfigure value
int_flag(
name = "lg_hugepage",
build_setting_default = 21,
make_variable = "LG_HUGEPAGE",
visibility = ["//visibility:public"],
)
# LG_PAGE Base 2 log of system page size, can be explicitly set --with-lg-page
string_flag(
name = "lg_page",
build_setting_default = "__auto__",
make_variable = "LG_PAGE",
visibility = ["//visibility:public"],
)
# LLG_VADDR Number of significant virtual address bits, can be explicitly set --with-lg-vaddr
# The configure cross compilation fallback is the default. This should be safe for any 64-bit target even if the OS
# doesn't allow it because of runtime checks. However, 32-bit architectures should explicitly declare this
# need to declare
int_flag(
name = "lg_vaddr",
build_setting_default = 57,
make_variable = "LG_VADDR",
visibility = ["//visibility:public"],
)
string_flag(
name = "valloc_support",
build_setting_default = "__auto__",
values = [
"yes",
"no",
"__auto__",
],
visibility = ["//visibility:public"],
)
config_setting(
name = "has_valloc",
flag_values = {":valloc_support": "yes"},
visibility = ["//visibility:public"],
)
config_setting(
name = "no_valloc",
flag_values = {":valloc_support": "no"},
visibility = ["//visibility:public"],
)
### Convenience settings groups ###
# ARM
selects.config_setting_group(
name = "aarch",
match_any = [
"@platforms//cpu:aarch32",
"@platforms//cpu:aarch64",
],
visibility = ["//:__subpackages__"],
)
# X86
selects.config_setting_group(
name = "x86",
match_any = [
"@platforms//cpu:x86_32",
"@platforms//cpu:x86_64",
],
visibility = ["//:__subpackages__"],
)
# BSD systems
selects.config_setting_group(
name = "bsd",
match_any = [
"@platforms//os:freebsd",
"@platforms//os:ios",
"@platforms//os:macos",
"@platforms//os:openbsd",
"@platforms//os:tvos",
"@platforms//os:watchos",
],
visibility = ["//:__subpackages__"],
)
# Darwin systems
selects.config_setting_group(
name = "darwin",
match_any = [
"@platforms//os:ios",
"@platforms//os:macos",
"@platforms//os:tvos",
"@platforms//os:watchos",
],
visibility = ["//:__subpackages__"],
)
# GNU systems
selects.config_setting_group(
name = "gnu",
match_any = [
"@platforms//os:android",
"@platforms//os:linux",
],
visibility = ["//:__subpackages__"],
)
# POSIX systems
selects.config_setting_group(
name = "posix",
match_any = [
"@platforms//os:android",
"@platforms//os:freebsd",
"@platforms//os:ios",
"@platforms//os:linux",
"@platforms//os:macos",
"@platforms//os:openbsd",
"@platforms//os:tvos",
"@platforms//os:watchos",
],
visibility = ["//:__subpackages__"],
)