scikit-build-core

repository·main·Indexed 19 days ago

https://github.com/scikit-build/scikit-build-core

A modern Python build backend that uses CMake to build extension modules. Designed to replace the older setuptools-based scikit-build, it follows current Python packaging standards and supports C++, Fortran, and specialized scientific libraries via a static configuration system in pyproject.toml. It provides integration for other backends via extras for Setuptools and Hatchling.

Tokens
34.9K
Snippets
103
Records
174
Agent score
64%

What's inside scikit-build-core

  1. What is scikit-build-core

    main
    scikit-build-core is a Python build backend that uses CMake to build extension modules. It provides a static configuration system via pyproject.toml and leverages CMake's flexibility to build any package, including those requiring C++, Fortran, or specialized scientific libraries. It is a modern rewrite designed to follow current Python packaging standards without requiring setuptools, distutils, or wheel as dependencies.
  2. Explore the scikit_build_core package structure

    main

    The scikit_build_core package is the primary entry point for the build backend. It is organized into several subpackages that handle specific aspects of the build process, such as AST manipulation, build orchestration, metadata generation, and settings management.

    Key subpackages include:

    • scikit_build_core.build: Core build logic.
    • scikit_build_core.builder: Logic for constructing the build environment.
    • scikit_build_core.metadata: Handling of package metadata.
    • scikit_build_core.settings: Configuration and settings management.
    • scikit_build_core.utils: General utility functions.
  3. Caveats when wrapping the build backend

    main

    When implementing an in-tree backend wrapper, be aware of the following constraints:

    • SDist Requirements: You must commit the backend module to your version control. The Source Distribution (SDist) must contain the module, otherwise builds from the SDist will fail. Note that scikit-build-core includes git-tracked files by default.
    • Wheel Contents: The backend module is used during the build process and is not installed into the resulting wheel.
    • Path Shadowing: Every directory listed in backend-path is prepended to sys.path during the build. Keep these directories minimal to avoid accidentally shadowing installed Python packages.
    • Process Isolation: Build frontends may execute each hook in a separate process. Do not rely on module-level state (global variables) surviving between different hook calls.
    • Configuration: All standard [tool.scikit-build] configuration settings remain applicable and functional.
  4. How SDists and Wheels are built

    main

    SDists and wheels are produced via a standardized hook sequence defined in pyproject.toml:

    1. Read Configuration: The build system reads the [build-system] table in pyproject.toml.
    2. Environment Setup: An isolated environment is created containing the packages listed in build-system.requires.
    3. Dynamic Dependency Resolution: The backend runs .get_requires_for_build_sdist(...) or .get_requires_for_build_wheel(...). If this returns a list, those packages are installed in the environment.
    4. File Production: The backend runs .build_sdist(...) or .build_wheel(...) to produce the final file.

    Note on Isolation: If you use --no-isolation (in build) or --no-build-isolation (in pip), steps 2 and 3 are skipped, and the build runs in the current environment.

  5. Use the scikit_build_core.setuptools.wrapper.setup shim

    main

    The scikit_build_core.setuptools.wrapper.setup shim is a compatibility layer designed to behave as closely as possible to the classic skbuild.setup.

    When using this shim, you can use the following environment variables to pass configuration:

    • SKBUILD_CONFIGURE_OPTIONS: Extra arguments appended during configuration (analogous to SKBUILD_CMAKE_ARGS).
    • SKBUILD_BUILD_OPTIONS: Extra arguments forwarded to cmake --build. Use a leading -- to pass native build-tool options (e.g., SKBUILD_BUILD_OPTIONS="-- -l4").

    Note: These environment variables have no effect if you are using the standard scikit_build_core.setuptools.build_meta backend or setuptools.build_meta directly.

  6. Understand the SDist (Source Distribution) structure

    main

    An SDist is a tarfile containing all the source code required to build the project and a PKG-INFO metadata file.

    Best Practices:

    • Include all files required for the build (including files from git submodules).
    • Omit CI-related files that aren't needed for the build.
    • Ensure the SDist can be installed without an internet connection if possible.

    Manual Build (without isolation): You can manually trigger an SDist build using the scikit_build_core API:

    python -c "from scikit_build_core.build import build_sdist; build_sdist('dist')"
  7. Configure override conditions with `if` and `if.any`

    main

    You can control when overrides are applied using conditional logic:

    • if: All conditions provided must be true (logical AND).
    • if.any: At least one condition provided must be true (logical OR).
    • Combined: If you use both if and if.any, all if conditions AND at least one if.any condition must match.

    Condition Types:

    • bool: Matches a specific boolean value.
    • string: Matches a regular expression (regex). For environment variables, a boolean matches any non-false-like value (non-empty/non-unset).
    • version: Matches a PEP 508 specifier set (e.g., " >=1.0").
    [tool.scikit-build]
    wheel.cmake = false
    
    [[tool.scikit-build.overrides]]
    # Matches if EITHER CIBUILDWHEEL or BUILD_MY_LIB is truthy
    if.any.env.CIBUILDWHEEL = true
    if.any.env.BUILD_MY_LIB = true
    wheel.cmake = true
  8. Understand entry-point configuration precedence levels

    main

    The entry-point group determines where your configuration is inserted into the precedence hierarchy. If multiple providers exist in the same group, they are applied in sorted name order, and the alphabetically-first name wins on conflicts.

    Precedence Groups

    • scikit-build-core.config.default: Applied below pyproject.toml but above built-in defaults. This is the recommended level for suggesting defaults that the project can override.
    • scikit-build-core.config.override: Applied above pyproject.toml. This prevents the project from overriding these settings, though they remain below user environment variables and -C/config-settings.

    Full Precedence Order (Highest to Lowest)

    1. SKBUILD_* environment variables
    2. -C/config-settings
    3. override entry-point providers
    4. Extra settings (build-frontend plugins, e.g., hatchling)
    5. pyproject.toml
    6. default entry-point providers
    7. Built-in defaults
  9. How scikit-build-core populates CMake search paths

    main

    scikit-build-core automatically populates CMake search paths to allow projects to find other CMake projects installed in the same environment. To make a project discoverable, it must define one or more cmake.* entry-points in its pyproject.toml.

    Entry-point groupCMake variable populatedTypical use
    cmake.root<PackageName>_ROOTfind_package(MyProject) (recommended)
    cmake.prefixCMAKE_PREFIX_PATHcatch-all for find_package, find_program, find_path
    cmake.moduleCMAKE_MODULE_PATHCMake modules loaded with include(...)
  10. Detect scikit-build-core in CMakeLists.txt

    main

    To ensure your CMakeLists.txt works both when driven by scikit-build-core and when run standalone, use the ${SKBUILD} variable.

    • ${SKBUILD} is set to 2 when using scikit-build-core.
    • ${SKBUILD} is set to 1 when using classic scikit-build.
    • ${SKBUILD_CORE_VERSION} provides the version of scikit-build-core being used.
    if(${SKBUILD} STREQUAL "2")
        message("Running under scikit-build-core version ${SKBUILD_CORE_VERSION}")
    endif()
  11. Use scikit-build-core as a build backend

    main
    scikit-build-core is designed to be used as a PEP 517 build backend. Instead of calling it directly for standard builds, you should invoke it through a build frontend like pip or build. This allows it to integrate seamlessly into standard Python packaging workflows.