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:


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

Step-by-Step Breakdown

  1. 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.

  2. Emit Rust Configuration Checks for Python Versions and Features

    The script outputs cargo:rustc-check-cfg lines 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.

  3. Detect Python Interpreter Configuration

    Uses the pyo3_build_config crate 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.

  4. 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.

  5. 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.

  6. Enable SIMD Feature for x86_64 Non-macOS Platforms

    For x86_64 architectures (excluding macOS), if Rust compiler version ≥1.89.0 and Python is 64-bit, enables the avx512 feature:

    #[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\"");
    }
    
  7. Enable Core Intrinsics and Optimize Attributes

    Checks if the Rust compiler supports core_intrinsics and optimize_attribute features, 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\"");
    }
    
  8. Enable Inline Integer Feature for x86_64 and aarch64 Architectures

    For 64-bit Python on supported architectures, enables inline_int feature:

    #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
    if is_64_bit_python {
        println!("cargo:rustc-cfg=feature=\"inline_int\"");
    }
    
  9. Handle yyjson Feature Compilation and Conflicts

    The embedded yyjson C library is optionally compiled unless disabled by the ORJSON_DISABLE_YYJSON environment variable.

    • If ORJSON_DISABLE_YYJSON is set and the Cargo feature yyjson is enabled, the build panics due to conflict.

    • Otherwise, attempts to compile yyjson.c with 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:


Implementation Details and Algorithms


Interaction with Other Parts of the System


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:

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.