build.rs
Overview
The `build.rs` file is a Rust build script executed by Cargo prior to compiling the main Rust crate. Its primary role is to configure the build environment dynamically based on the target platform, the Python interpreter version and implementation, enabled Cargo features, and compiler capabilities. It sets conditional compilation flags, manages integration with an embedded C library (`yyjson`), and enforces compatibility constraints.
This build script ensures that the Rust code compiles correctly and optimally for the detected environment by:
Monitoring source files and environment variables for changes to trigger rebuilds.
Detecting Python interpreter details to enable Python-version-specific features and enforce support for CPython only.
Enabling CPU-architecture and Rust-compiler-specific features like SIMD instructions and core intrinsics where supported.
Compiling the embedded
yyjsonC library with tailored configuration to optimize JSON parsing performance.Preventing conflicting build configurations by checking environment variables and feature flags.
Detailed Explanation of Contents
Main Function: fn main()
The sole function in this file is `main`, which Cargo executes automatically during the build phase.
Purpose
Sets up Cargo directives for rebuild triggers.
Detects Python configuration using
pyo3_build_config.Enables Rust conditional compilation flags based on Python version, architecture, and Rust compiler capabilities.
Compiles the
yyjsonC library when appropriate.Validates mutually exclusive feature flags.
Step-by-Step Breakdown
Change and Environment Monitoring
The build script informs Cargo to rerun if key files or environment variables change:
println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=include/yyjson/*"); println!("cargo:rerun-if-env-changed=CC"); println!("cargo:rerun-if-env-changed=CFLAGS"); println!("cargo:rerun-if-env-changed=LDFLAGS"); println!("cargo:rerun-if-env-changed=ORJSON_DISABLE_YYJSON"); println!("cargo:rerun-if-env-changed=RUSTFLAGS");This ensures the build script is rerun when the build script itself, the embedded C source files, or relevant environment variables change.
Emit Rust Configuration Checks for Python Versions and Features
The script outputs
cargo:rustc-check-cfglines for various Python-related cfg flags:println!("cargo:rustc-check-cfg=cfg(CPython)"); println!("cargo:rustc-check-cfg=cfg(GraalPy)"); println!("cargo:rustc-check-cfg=cfg(intrinsics)"); println!("cargo:rustc-check-cfg=cfg(optimize)"); println!("cargo:rustc-check-cfg=cfg(Py_3_10)"); println!("cargo:rustc-check-cfg=cfg(Py_3_11)"); println!("cargo:rustc-check-cfg=cfg(Py_3_12)"); println!("cargo:rustc-check-cfg=cfg(Py_3_13)"); println!("cargo:rustc-check-cfg=cfg(Py_3_14)"); println!("cargo:rustc-check-cfg=cfg(Py_3_15)"); println!("cargo:rustc-check-cfg=cfg(Py_3_9)"); println!("cargo:rustc-check-cfg=cfg(Py_GIL_DISABLED)"); println!("cargo:rustc-check-cfg=cfg(PyPy)");These enable the Rust code to conditionally compile sections based on Python version support or features detected.
Detect Python Interpreter Configuration
Uses the
pyo3_build_configcrate to obtain the Python interpreter information:let python_config = pyo3_build_config::get(); for cfg in python_config.build_script_outputs() { println!("{cfg}"); }This outputs further configuration lines based on the detected Python implementation and environment.
Enforce CPython Only
The script explicitly supports only CPython:
if python_config.implementation == pyo3_build_config::PythonImplementation::CPython { println!("cargo:rustc-cfg=CPython"); } else { panic!("orjson only supports CPython") }Builds will fail immediately if another Python implementation like PyPy or GraalPy is used.
Check for 64-bit Python
Determines if the Python pointer width is 64 bits:
let is_64_bit_python = matches!(python_config.pointer_width, Some(64));This influences conditional feature enabling later.
Enable SIMD Feature for x86_64 Non-macOS Platforms
For
x86_64architectures (excluding macOS), if Rust compiler version ≥1.89.0 and Python is 64-bit, enables theavx512feature:#[cfg(all(target_arch = "x86_64", not(target_os = "macos")))] if version_check::is_min_version("1.89.0").unwrap_or(false) && is_64_bit_python { println!("cargo:rustc-cfg=feature=\"avx512\""); }Enable Core Intrinsics and Optimize Attributes
Checks if the Rust compiler supports
core_intrinsicsandoptimize_attributefeatures, enabling them conditionally:if version_check::supports_feature("core_intrinsics").unwrap_or(false) { println!("cargo:rustc-cfg=feature=\"intrinsics\""); } if version_check::supports_feature("optimize_attribute").unwrap_or(false) { println!("cargo:rustc-cfg=feature=\"optimize\""); }Enable Inline Integer Feature for
x86_64andaarch64ArchitecturesFor 64-bit Python on supported architectures, enables
inline_intfeature:#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] if is_64_bit_python { println!("cargo:rustc-cfg=feature=\"inline_int\""); }Handle
yyjsonFeature Compilation and ConflictsThe embedded
yyjsonC library is optionally compiled unless disabled by theORJSON_DISABLE_YYJSONenvironment variable.If
ORJSON_DISABLE_YYJSONis set and the Cargo featureyyjsonis enabled, the build panics due to conflict.Otherwise, attempts to compile
yyjson.cwith specific defines disabling certain features:
match cc::Build::new() .file("include/yyjson/yyjson.c") .include("include/yyjson") .define("YYJSON_DISABLE_NON_STANDARD", "1") .define("YYJSON_DISABLE_UTF8_VALIDATION", "1") .define("YYJSON_DISABLE_UTILS", "1") .define("YYJSON_DISABLE_WRITER", "1") .try_compile("yyjson") { Ok(_) => { println!("cargo:rustc-cfg=feature=\"yyjson\""); } Err(_) => { if env::var("CARGO_FEATURE_YYJSON").is_ok() { panic!( "yyjson was enabled but the build failed. To build with a different backend do not specify the feature." ) } } }This builds the C source and propagates a Rust feature flag if successful, or errors out if there is a conflict or compilation failure with the feature requested.
Usage Example
This file is automatically executed by Cargo during the build phase and is not called manually. However, its effects influence compilation as follows:
If you build the crate with the feature
yyjsonenabled and without disabling it via environment variables, the script will compile the embeddedyyjsonC library and enable related Rust features.If you target a 64-bit Python on an
x86_64platform with a recent Rust compiler, SIMD features likeavx512will be enabled automatically.If you attempt to use a Python implementation other than CPython, the build will fail with a panic.
Implementation Details and Algorithms
Conditional Compilation Based on Rust Compiler Features:
Uses theversion_checkcrate to detect whether the Rust compiler supports certain unstable or new features likecore_intrinsicsoroptimize_attribute. This dynamic detection allows the crate to use advanced Rust features only when available, improving portability.Integration of C Compilation in Rust Build Process:
Uses thecccrate to compile embedded C source files with custom preprocessor defines disabling unused or unsafe JSON features, tailored to the library’s requirements. This approach avoids external build systems and integrates C compilation seamlessly.Environment Variable and Feature Conflict Detection:
Ensures that mutually exclusive configurations (e.g., disablingyyjsonvia environment variable while enabling its feature flag) cause immediate build errors, preventing ambiguous builds and runtime failures.
Interaction with Other Parts of the System
Rust Crate Source (
src/):
The build script setscfgflags that influence conditional compilation of Rust modules, enabling or disabling code paths optimized for certain Python versions or CPU capabilities.Embedded C Library (
include/yyjson/):
The script compiles theyyjson.csource into the final Rust binary when the feature is enabled and not disabled by environment variables.Python Integration via PyO3 (
pyo3_build_config):
Uses PyO3’s build configuration utilities to detect Python interpreter details and enforce compatibility.Cargo Build Process:
Cargo runs this build script automatically, and the emittedcargo:directives control rebuild triggers and compilation flags.Feature Flag Management:
The script’s output feature flags control conditional compilation in the Rust crate source code, tailoring functionality and optimizations.
Visual Diagram
Build Script Workflow Flowchart
flowchart TD
Start[Start build.rs execution]
CheckChanges[Emit rerun-if-changed/env instructions]
DetectPython[Detect Python implementation and version]
VerifyCPython{Is CPython?}
SetupRustFlags[Emit Rust cfg flags for features & Python versions]
CheckEnvVars[Check ORJSON_DISABLE_YYJSON and Cargo features]
CompileYYJSON{Compile yyjson C library?}
Success[Emit feature flags and finish]
PanicBuild[Abort build with error]
Start --> CheckChanges --> DetectPython --> VerifyCPython
VerifyCPython -- Yes --> SetupRustFlags --> CheckEnvVars --> CompileYYJSON
VerifyCPython -- No --> PanicBuild
CheckEnvVars -- Conflict --> PanicBuild
CompileYYJSON -- Success --> Success
CompileYYJSON -- Fail & feature enabled --> PanicBuild
CompileYYJSON -- Fail & feature disabled --> Success
This diagram illustrates the main decision points and steps carried out during the build script execution, from environment monitoring through Python detection, feature flag emission, conflict checking, and optional compilation of `yyjson`.
Summary
The `build.rs` script is a vital component that adapts the Rust build process dynamically to the target environment, Python interpreter, and user-defined features. It ensures:
Compatibility by restricting to CPython.
Optimal performance through enabling advanced CPU and compiler features.
Seamless integration of a C JSON parser (
yyjson) with custom configuration.Prevention of conflicting build configurations.
Automated rebuilds triggered on relevant changes.
By doing so, it establishes a robust and flexible build foundation that supports efficient development and distribution of the Rust extension tailored for Python environments.