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

113
tools/autoconf.bzl Normal file
View file

@ -0,0 +1,113 @@
"Utilities for replicating autoconf behavior"
load("@aspect_bazel_lib//lib:expand_template.bzl", "expand_template")
def define_macro_if(macro, condition):
"""Controls conditional macro definition based on a build condition.
Args:
macro: String macro name to define
condition: Build condition that should enable the macro
Returns:
A select dictionary mapping conditions to substitution dictionaries
"""
return select({
condition: define_macro(macro),
"//conditions:default": undefine_macro(macro),
})
def define_macro_if_with(macro, condition, value):
"""Conditionally defines a macro with a specific value based on a build condition.
Args:
macro: String macro name to define
condition: Build condition that should enable the macro
value: Value to assign to the macro when defined
Returns:
A select dictionary mapping conditions to substitution dictionaries
"""
return select({
condition: define_macro_with(macro, value),
"//conditions:default": undefine_macro(macro),
})
def define_macro(macro):
"""Unconditionally defines a macro.
Args:
macro: String macro name to define
Returns:
A substitution dictionary for the macro
"""
return {"#undef {}".format(macro): "#define {}".format(macro)}
def define_macro_if_any(macro, conditions):
"""Defines a macro if any of the specified conditions are met.
Args:
macro: String macro name to define
conditions: List of build conditions that should individually enable the macro
Returns:
A select dictionary mapping conditions to substitution dictionaries
"""
substitutions = {}
defined = define_macro(macro)
for condition in conditions:
substitutions[condition] = defined
substitutions["//conditions:default"] = undefine_macro(macro)
return select(substitutions)
def define_macro_with(macro, value):
"""Unconditionally defines a macro with a specific value.
Args:
macro: String macro name to define
value: Value to assign to the macro
Returns:
A substitution dictionary for the macro with value
"""
return {"#undef {}".format(macro): "#define {} {}".format(macro, value)}
def undefine_macro(macro):
"""Explicitly undefines a macro with a commented annotation.
Args:
macro: String macro name to undefine
Returns:
A substitution dictionary that replaces the undef with a comment
"""
return {"#undef {}".format(macro): "/* #UNDEF {} */".format(macro)}
def configure_header(name, substitutions, **kwargs):
"""Generates a header file with autoconf-style macro substitutions.
Args:
name: Target name
substitutions: Dictionary of substitutions to apply
**kwargs: Additional arguments passed to expand_template
Returns:
The processed header file with all substitutions applied
"""
return expand_template(
name = name,
# The expand_template substitutions are applied serially and overlapping macro names can interfere with each other.
# Explicitly marking that a macro has been processed such that the template string no longer matches prevents this.
substitutions = substitutions | {"#UNDEF": "#undef"},
**kwargs
)

50
tools/cc.bzl Normal file
View file

@ -0,0 +1,50 @@
"Project specific configuration of rules_cc"
load("@bazel_skylib//lib:selects.bzl", "selects")
_COPTS_GCC_COMPATIBLE = [
"-Wall",
"-Wextra",
"-Wsign-compare",
"-Wundef",
"-Wno-format-zero-length",
"-Wpointer-arith",
"-Wno-missing-braces",
"-Wno-missing-field-initializers",
"-Wimplicit-fallthrough",
"-pipe",
]
_COPTS_GCC = _COPTS_GCC_COMPATIBLE + ["-Wno-missing-attributes"]
_COPTS_MSVC_COMPATIBLE = [
"/Zi",
"/MT",
"/W3",
"/FS",
]
# Platform and compiler COPTS consistent with configure/make
COPTS = ["-D_REENTRANT"] + select({
"@rules_cc//cc/compiler:clang": _COPTS_GCC_COMPATIBLE + ["-Wshorten-64-to-32"],
"@rules_cc//cc/compiler:gcc": _COPTS_GCC,
"@rules_cc//cc/compiler:mingw-gcc": _COPTS_GCC,
"@rules_cc//cc/compiler:msvc-cl": _COPTS_MSVC_COMPATIBLE,
"@rules_cc//cc/compiler:clang-cl": _COPTS_MSVC_COMPATIBLE,
"//conditions:default": [],
}) + selects.with_or({
("@platforms//os:windows", "//settings/platform:darwin"): [],
"//settings/compiler:gcc_compatible": ["-fvisibility=hidden"],
"//conditions:default": [],
}) + selects.with_or({
("@platforms//os:android", "@platforms//os:linux"): [
"-DPIC",
"-D_GNU_SOURCE",
],
"@platforms//os:freebsd": [
"-DPIC",
"-D_BSD_SOURCE",
],
"@platforms//os:macos": ["-DPIC"],
"//conditions:default": [],
})

109
tools/transition.bzl Normal file
View file

@ -0,0 +1,109 @@
"Transition build and platform settings"
load("@rules_cc//cc:defs.bzl", "CcInfo")
PLATFORM = select({
"@platforms//os:freebsd": "freebsd",
"@platforms//os:linux": "linux",
"@platforms//os:windows": "windows",
"//settings/platform:darwin": "darwin",
"//conditions:default": "unknown",
})
def _platform_constraints_transition_impl(settings, attr):
# Assume typical defaults for target platforms
default_settings = {
"//settings/flags:enable_zone_allocator": {"darwin": "yes", "default": "no"},
"//settings/platform:glibc_overrides_support": {"linux": "yes", "default": "no"},
"//settings/platform:lg_page": {"darwin": "14", "default": "12"},
"//settings/platform:memalign_support": {"linux": "yes", "default": "no"},
"//settings/platform:malloc_size_support": {"darwin": "yes", "default": "no"},
"//settings/platform:usable_size_const": {"linux": "", "default": "const"},
"//settings/platform:valloc_support": {"darwin": "yes", "linux": "yes", "default": "no"},
}
result = {}
for label, platform_defaults in default_settings.items():
configured_value = settings[label]
# If unset, use platform-specific default or universal fallback
result[label] = (platform_defaults.get(attr.platform, platform_defaults["default"]) if configured_value == "__auto__" else configured_value)
# Apply explicitly passed settings
boolean_settings = ["//settings:enable_jet", "//settings/flags:enable_fill"]
for setting in boolean_settings:
result[setting] = attr.settings[Label(setting)] == "True" if Label(setting) in attr.settings else settings[setting]
string_settings = ["//settings:with_test", "//settings/flags:jemalloc_prefix"]
for setting in string_settings:
result[setting] = attr.settings[Label(setting)] if Label(setting) in attr.settings else settings[setting]
# Equivalent of JEMALLOC_CPREFIX=`echo ${JEMALLOC_PREFIX} | tr "a-z" "A-Z"`
result["//settings/flags:jemalloc_cprefix"] = result["//settings/flags:jemalloc_prefix"].upper()
return result
_platform_constraints_transition = transition(
implementation = _platform_constraints_transition_impl,
inputs = [
"//settings:enable_jet",
"//settings:with_test",
"//settings/flags:jemalloc_prefix",
"//settings/flags:jemalloc_cprefix",
"//settings/flags:enable_fill",
"//settings/flags:enable_zone_allocator",
"//settings/platform:glibc_overrides_support",
"//settings/platform:lg_page",
"//settings/platform:malloc_size_support",
"//settings/platform:memalign_support",
"//settings/platform:usable_size_const",
"//settings/platform:valloc_support",
],
outputs = [
"//settings:enable_jet",
"//settings:with_test",
"//settings/flags:jemalloc_prefix",
"//settings/flags:jemalloc_cprefix",
"//settings/flags:enable_fill",
"//settings/flags:enable_zone_allocator",
"//settings/platform:glibc_overrides_support",
"//settings/platform:lg_page",
"//settings/platform:malloc_size_support",
"//settings/platform:memalign_support",
"//settings/platform:usable_size_const",
"//settings/platform:valloc_support",
],
)
def _transition_default_constraints_impl(ctx):
target = ctx.attr.src[0]
providers = [DefaultInfo, CcInfo, InstrumentedFilesInfo, OutputGroupInfo]
return [target[provider] for provider in providers if provider in target]
transition_default_constraints = rule(
doc = "Configure detects target platform constraints. The target platform should declare these, but " +
"intelligent defaults can be set for the majority of cases.",
implementation = _transition_default_constraints_impl,
attrs = {
"src": attr.label(cfg = _platform_constraints_transition),
"platform": attr.string(
doc = "Hack to inject platform constraints into the transition to apply unset defaults",
mandatory = True,
values = [
"freebsd",
"linux",
"darwin",
"windows",
"unknown",
],
),
"settings": attr.label_keyed_string_dict(
doc = "Settings to explicitly transition",
default = {},
),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
},
)

27
tools/version.bzl Normal file
View file

@ -0,0 +1,27 @@
"Version string utilities."
def parse_version(version):
"""Parses raw version string into a structured version object.
Returns:
struct: Contains version components:
- full: Full version
- gid: Version git id
- major: Major version number
- minor: Minor version number
- patch: Patch version number
- bugfix: Bugfix version
- nrev: Revision number
"""
prefix, gid = version.split("-bcr", 2)
major, minor, patch = prefix.split(".", 3)
return struct(
full = prefix,
gid = "0",
major = major,
minor = minor,
patch = patch,
bugfix = "0",
nrev = "0",
)