├── .coveragerc ├── .git_archival.txt ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── build.yml │ └── update-dependencies.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── AUTHORS.rst ├── CMakeLists.txt ├── CMakeUrls.cmake ├── CONTRIBUTING.rst ├── HISTORY.rst ├── LICENSE_Apache_20 ├── LICENSE_BSD_3 ├── README.rst ├── _build_backend └── backend.py ├── docs ├── authors.rst ├── build_system.rst ├── building.rst ├── conf.py ├── contributing.rst ├── history.rst ├── index.rst ├── installation.rst ├── make_a_release.rst ├── update_cmake_version.rst └── usage.rst ├── noxfile.py ├── pyproject.toml ├── scripts ├── manylinux-build-and-install-openssl.sh ├── update_cmake_version.py ├── update_openssl_version.py └── utils.sh ├── src └── cmake │ ├── __init__.py │ ├── __main__.py │ ├── _version.pyi │ └── py.typed └── tests ├── __init__.py └── test_cmake.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | include = cmake/*.py 4 | omit = cmake/_version.py 5 | 6 | [xml] 7 | output = tests/coverage.xml 8 | -------------------------------------------------------------------------------- /.git_archival.txt: -------------------------------------------------------------------------------- 1 | node: 92d896c63065f2e7185c73cab9c719de2cd3eba4 2 | node-date: 2025-05-16T21:07:41-04:00 3 | describe-name: 4.0.2-1-g92d896c63 4 | ref-names: HEAD -> main 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | .git_archival.txt export-subst 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | groups: 9 | actions: 10 | patterns: 11 | - "*" 12 | # Maintain dependencies for pip constraints-ci.txt 13 | - package-ecosystem: "pip" 14 | directory: "/" 15 | schedule: 16 | interval: "daily" 17 | allow: 18 | - dependency-name: "cmake" 19 | - dependency-name: "ninja" 20 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | use_qemu: 7 | description: 'Use qemu to build linux ppc64le & s390x' 8 | required: true 9 | default: true 10 | schedule: 11 | - cron: '0 18 * * 5' # "At 18:00 on Friday." 12 | pull_request: 13 | push: 14 | branches: 15 | - main 16 | - py2-legacy 17 | tags: 18 | - "*.*.*" 19 | 20 | concurrency: 21 | group: ${{ github.workflow }}-${{ github.ref }} 22 | cancel-in-progress: true 23 | 24 | env: 25 | USE_QEMU: ${{ fromJSON(github.event.inputs.use_qemu || 'false') || (github.event_name == 'schedule') || startsWith(github.ref, 'refs/tags/') }} 26 | 27 | jobs: 28 | lint: 29 | name: Lint 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: actions/setup-python@v5 34 | with: 35 | python-version: "3.x" 36 | - uses: pre-commit/action@v3.0.1 37 | 38 | 39 | build_wheels: 40 | name: Build ${{ matrix.build }}${{ matrix.arch }} wheels on ${{ matrix.os }} 41 | needs: [lint] 42 | runs-on: ${{ matrix.os }} 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | include: 47 | - os: ubuntu-latest 48 | arch: "x86_64" 49 | build: "manylinux_" 50 | use_qemu: false 51 | - os: ubuntu-latest 52 | arch: "x86_64" 53 | build: "musllinux_" 54 | use_qemu: false 55 | - os: ubuntu-latest 56 | arch: "i686" 57 | build: "manylinux_" 58 | use_qemu: false 59 | - os: ubuntu-latest 60 | arch: "i686" 61 | build: "musllinux_" 62 | use_qemu: false 63 | - os: ubuntu-24.04-arm 64 | arch: "aarch64" 65 | build: "manylinux_" 66 | use_qemu: false 67 | - os: ubuntu-24.04-arm 68 | arch: "aarch64" 69 | build: "musllinux_" 70 | use_qemu: false 71 | - os: ubuntu-latest 72 | arch: "ppc64le" 73 | build: "manylinux_" 74 | use_qemu: true 75 | - os: ubuntu-latest 76 | arch: "ppc64le" 77 | build: "musllinux_" 78 | use_qemu: true 79 | - os: ubuntu-latest 80 | arch: "s390x" 81 | build: "manylinux_" 82 | use_qemu: true 83 | - os: ubuntu-latest 84 | arch: "s390x" 85 | build: "musllinux_" 86 | use_qemu: true 87 | - os: ubuntu-24.04-arm 88 | arch: "armv7l" 89 | build: "manylinux_" 90 | use_qemu: false 91 | - os: ubuntu-24.04-arm 92 | arch: "armv7l" 93 | build: "musllinux_" 94 | use_qemu: false 95 | - os: windows-2019 96 | arch: "AMD64" 97 | build: "" 98 | use_qemu: false 99 | - os: windows-2022 100 | arch: "ARM64" 101 | build: "" 102 | use_qemu: false 103 | - os: windows-2019 104 | arch: "x86" 105 | build: "" 106 | use_qemu: false 107 | - os: macos-14 108 | arch: "universal2" 109 | build: "" 110 | use_qemu: false 111 | 112 | steps: 113 | - uses: actions/checkout@v4 114 | if: (!matrix.use_qemu) || fromJSON(env.USE_QEMU) 115 | with: 116 | fetch-depth: 0 # required for versioneer to find tags 117 | 118 | - uses: astral-sh/setup-uv@v6 119 | if: (!matrix.use_qemu) || fromJSON(env.USE_QEMU) 120 | with: 121 | enable-cache: false 122 | 123 | - name: Set up QEMU 124 | uses: docker/setup-qemu-action@v3.6.0 125 | if: matrix.use_qemu && fromJSON(env.USE_QEMU) 126 | 127 | - name: Build wheels 128 | uses: pypa/cibuildwheel@v2.23 129 | if: (!matrix.use_qemu) || fromJSON(env.USE_QEMU) 130 | env: 131 | CIBW_ARCHS: "${{ matrix.arch }}" 132 | CIBW_BUILD: "cp39-${{ matrix.build }}*" 133 | 134 | - uses: actions/upload-artifact@v4 135 | if: (!matrix.use_qemu) || fromJSON(env.USE_QEMU) 136 | with: 137 | name: cibw-${{ runner.os }}-${{ matrix.build }}${{ matrix.arch }} 138 | path: ./wheelhouse/*.whl 139 | 140 | build_manylinux2010_wheels: 141 | name: Build ${{ matrix.arch }} manylinux2010 wheels 142 | needs: [lint] 143 | runs-on: ubuntu-latest 144 | strategy: 145 | fail-fast: false 146 | matrix: 147 | include: 148 | - arch: "x86_64" 149 | - arch: "i686" 150 | 151 | steps: 152 | - uses: actions/checkout@v4 153 | with: 154 | fetch-depth: 0 # required for versioneer to find tags 155 | 156 | - name: Build wheels 157 | uses: pypa/cibuildwheel@v2.23 158 | env: 159 | CIBW_ARCHS: "${{ matrix.arch }}" 160 | CIBW_BUILD: "cp39-manylinux_*" 161 | CIBW_MANYLINUX_X86_64_IMAGE: "manylinux2010" 162 | CIBW_MANYLINUX_I686_IMAGE: "manylinux2010" 163 | CIBW_BUILD_FRONTEND: "build" 164 | 165 | - uses: actions/upload-artifact@v4 166 | with: 167 | name: cibw-manylinux2010-${{ matrix.arch }} 168 | path: ./wheelhouse/*.whl 169 | 170 | build_sdist: 171 | name: Build source distribution 172 | needs: [lint] 173 | runs-on: ubuntu-latest 174 | steps: 175 | - uses: actions/checkout@v4 176 | with: 177 | fetch-depth: 0 # required for versioneer to find tags 178 | 179 | - name: Build SDist 180 | run: pipx run build --sdist 181 | 182 | - uses: actions/upload-artifact@v4 183 | with: 184 | name: cibw-sdist 185 | path: dist/*.tar.gz 186 | 187 | test_sdist: 188 | name: Test SDist with python ${{ matrix.python }} 189 | needs: [build_sdist] 190 | runs-on: ubuntu-latest 191 | strategy: 192 | fail-fast: false 193 | matrix: 194 | python: ["3.8", "3.13"] 195 | 196 | steps: 197 | - uses: actions/checkout@v4 198 | - uses: astral-sh/setup-uv@v6 199 | with: 200 | enable-cache: false 201 | 202 | - name: Setup environment 203 | run: | 204 | uv venv --python ${{ matrix.python }} 205 | uv pip install --group test 206 | 207 | - name: Install dependencies 208 | run: | 209 | sudo apt-get update 210 | sudo apt-get install -y --no-install-recommends libssl-dev 211 | 212 | - uses: actions/download-artifact@v4 213 | with: 214 | name: cibw-sdist 215 | path: dist 216 | 217 | - name: Install SDist 218 | env: 219 | CMAKE_ARGS: "-DBUILD_CMAKE_FROM_SOURCE:BOOL=OFF" 220 | run: | 221 | uv pip install dist/*.tar.gz 222 | rm -rf dist 223 | 224 | - name: Test installed SDist 225 | run: .venv/bin/pytest ./tests 226 | 227 | bootstrap_build: 228 | name: Source only build on ${{ matrix.os }} 229 | needs: [lint] 230 | runs-on: ${{ matrix.os }} 231 | strategy: 232 | fail-fast: false 233 | matrix: 234 | os: ["ubuntu-latest", "windows-latest", "macos-latest"] 235 | 236 | steps: 237 | - uses: actions/checkout@v4 238 | - uses: actions/setup-python@v5 239 | id: python 240 | with: 241 | python-version: "3.x" 242 | 243 | - name: Remove cmake and ninja 244 | shell: bash 245 | run: | 246 | # Remove cmake and ninja 247 | set -euxo pipefail 248 | # https://github.com/scikit-build/scikit-build-core/blob/3943920fa267dc83f9295279bea1c774c0916f13/src/scikit_build_core/program_search.py#L51 249 | # https://github.com/scikit-build/scikit-build-core/blob/3943920fa267dc83f9295279bea1c774c0916f13/src/scikit_build_core/program_search.py#L70 250 | for TOOL in cmake cmake3 ninja-build ninja samu; do 251 | while which ${TOOL}; do 252 | if [ "$RUNNER_OS" == "Windows" ]; then 253 | rm -f "$(which ${TOOL})" 254 | else 255 | sudo rm -f $(which -a ${TOOL}) 256 | fi 257 | done 258 | done 259 | 260 | - name: Build SDist 261 | run: pipx run --python '${{ steps.python.outputs.python-path }}' build --sdist 262 | 263 | - name: Install dependencies 264 | if: runner.os == 'Linux' 265 | run: | 266 | sudo apt-get update 267 | sudo apt-get install -y --no-install-recommends libssl-dev 268 | 269 | - name: Install SDist 270 | shell: bash 271 | env: 272 | CMAKE_ARGS: "-DBUILD_CMAKE_FROM_SOURCE:BOOL=OFF" 273 | CMAKE_BUILD_PARALLEL_LEVEL: "4" 274 | MACOSX_DEPLOYMENT_TARGET: "10.10" 275 | run: | 276 | python -m pip install -v --no-binary='cmake,ninja' dist/*.tar.gz 277 | rm -rf dist 278 | 279 | - name: Test installed SDist 280 | shell: bash 281 | run: python -m pip install pytest pytest-cov && pytest ./tests 282 | 283 | check_dist: 284 | name: Check dist 285 | needs: [build_wheels, build_manylinux2010_wheels, build_sdist, test_sdist, bootstrap_build] 286 | runs-on: ubuntu-latest 287 | steps: 288 | - uses: actions/download-artifact@v4 289 | with: 290 | pattern: cibw-* 291 | merge-multiple: true 292 | path: dist 293 | 294 | - run: pipx run twine check --strict dist/* 295 | 296 | upload_pypi: 297 | name: Upload to PyPI 298 | needs: [check_dist] 299 | runs-on: ubuntu-latest 300 | if: github.event_name == 'push' && github.repository == 'scikit-build/cmake-python-distributions' && startsWith(github.ref, 'refs/tags/') 301 | environment: 302 | name: pypi 303 | url: https://pypi.org/p/cmake 304 | permissions: 305 | id-token: write 306 | attestations: write 307 | steps: 308 | - uses: actions/download-artifact@v4 309 | with: 310 | pattern: cibw-* 311 | merge-multiple: true 312 | path: dist 313 | 314 | - name: Generate artifact attestation for sdist and wheel 315 | uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0 316 | with: 317 | subject-path: "dist/cmake-*" 318 | 319 | - uses: pypa/gh-action-pypi-publish@release/v1 320 | with: 321 | attestations: true 322 | -------------------------------------------------------------------------------- /.github/workflows/update-dependencies.yml: -------------------------------------------------------------------------------- 1 | name: Update Dependencies 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '.github/workflows/update-dependencies.yml' 7 | - 'scripts/update_cmake_version.py' 8 | - 'scripts/update_openssl_version.py' 9 | - 'noxfile.py' 10 | workflow_dispatch: 11 | schedule: 12 | - cron: '0 6 * * *' # "At 06:00 every day." 13 | 14 | jobs: 15 | update-dep: 16 | name: Update ${{ matrix.dependency_nice }} 17 | if: github.repository_owner == 'scikit-build' || github.event_name != 'schedule' 18 | runs-on: ubuntu-latest 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | include: 23 | - dependency: "cmake" 24 | dependency_nice: "CMake" 25 | - dependency: "openssl" 26 | dependency_nice: "OpenSSL" 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: wntrblm/nox@2025.05.01 30 | - name: "Run update: bump ${{ matrix.dependency_nice }}" 31 | id: bump 32 | run: | 33 | nox --force-color -s bump${{ matrix.dependency != 'cmake' && format('-{0}', matrix.dependency) || '' }} 34 | echo "version=$(nox -s ${{ matrix.dependency }}_version 2>/dev/null)" >> $GITHUB_OUTPUT 35 | - run: echo "${{ matrix.dependency_nice }} version is ${{ steps.bump.outputs.version }}" 36 | 37 | # we use this step to grab a Github App auth token, so that PRs generated by this workflow 38 | # run the GHA tests. 39 | - uses: actions/create-github-app-token@v2 40 | id: app-token 41 | if: github.ref == 'refs/heads/main' && github.repository == 'scikit-build/cmake-python-distributions' 42 | with: 43 | app-id: ${{ secrets.SCIKIT_BUILD_BOT_APP_ID }} 44 | private-key: ${{ secrets.SCIKIT_BUILD_BOT_APP_PRIVATE_KEY }} 45 | 46 | - name: Create Pull Request 47 | if: github.ref == 'refs/heads/main' && github.repository == 'scikit-build/cmake-python-distributions' 48 | uses: peter-evans/create-pull-request@v7 49 | with: 50 | commit-message: '[Bot] Update to ${{ matrix.dependency_nice }} ${{ steps.bump.outputs.version }}' 51 | title: '[Bot] Update to ${{ matrix.dependency_nice }} ${{ steps.bump.outputs.version }}' 52 | body: | 53 | Created by update_${{ matrix.dependency }}_version.py 54 | 55 | PR generated by "Update ${{ matrix.dependency_nice }}" [workflow](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}). 56 | branch: update-${{ matrix.dependency }}-pr 57 | sign-commits: true 58 | token: ${{ steps.app-token.outputs.token }} 59 | delete-branch: true 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This project 2 | archive-cache 3 | src/cmake/data/* 4 | env.json 5 | skbuild 6 | CMake-src 7 | standalone-build 8 | standalone-x86-build 9 | standalone-x64-build 10 | 11 | # Python 12 | *.py[cod] 13 | 14 | # C extensions 15 | *.so 16 | 17 | # Packages 18 | *.egg 19 | *.eggs 20 | *.egg-info 21 | dist 22 | build 23 | _skbuild 24 | eggs 25 | parts 26 | bin 27 | var 28 | sdist 29 | develop-eggs 30 | .installed.cfg 31 | lib 32 | lib64 33 | wheelhouse 34 | 35 | # Installer logs 36 | pip-log.txt 37 | 38 | # Unit test / coverage reports 39 | .cache 40 | .coverage 41 | .pytest_cache 42 | .tox 43 | coverage.xml 44 | htmlcov 45 | 46 | # Translations 47 | *.mo 48 | 49 | # Mr Developer 50 | .mr.developer.cfg 51 | .project 52 | .pydevproject 53 | 54 | # Complexity 55 | output/*.html 56 | output/*/index.html 57 | 58 | # Sphinx 59 | docs/_build 60 | 61 | # IDE junk 62 | .idea/* 63 | *.swp 64 | 65 | # Output from cibuildwheel 66 | wheelhouse/ 67 | 68 | # Version 69 | _version.py 70 | 71 | CMakeCache.txt 72 | CMakeFiles 73 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | ci: 2 | autoupdate_commit_msg: "chore(deps): update pre-commit hooks" 3 | autofix_commit_msg: "style: pre-commit fixes" 4 | 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v5.0.0 8 | hooks: 9 | - id: check-added-large-files 10 | - id: check-case-conflict 11 | - id: check-merge-conflict 12 | - id: check-symlinks 13 | - id: check-yaml 14 | - id: debug-statements 15 | - id: end-of-file-fixer 16 | - id: mixed-line-ending 17 | - id: trailing-whitespace 18 | 19 | - repo: https://github.com/astral-sh/ruff-pre-commit 20 | rev: "v0.11.9" 21 | hooks: 22 | - id: ruff 23 | args: [--fix, --show-fixes] 24 | 25 | - repo: https://github.com/pre-commit/mirrors-mypy 26 | rev: "v1.15.0" 27 | hooks: 28 | - id: mypy 29 | files: ^(src|scripts) 30 | additional_dependencies: [types-requests] 31 | args: [] 32 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | version: 2 5 | 6 | build: 7 | os: ubuntu-22.04 8 | tools: 9 | python: "3.12" 10 | commands: 11 | - asdf plugin add uv 12 | - asdf install uv latest 13 | - asdf global uv latest 14 | - uv run --only-group docs sphinx-build -T -b html -d docs/_build/doctrees -D language=en docs $READTHEDOCS_OUTPUT/html 15 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Credits 3 | ======= 4 | 5 | Please see the GitHub project page at https://github.com/scikit-build/cmake-python-distributions/graphs/contributors 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15...4.0) 2 | 3 | # 4 | # For more details, see docs/building.rst 5 | # 6 | 7 | project(CMakePythonDistributions NONE) 8 | 9 | # Set a default build type if none was specified 10 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 11 | message(STATUS "Setting build type to 'Release' as none was specified.") 12 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) 13 | mark_as_advanced(CMAKE_BUILD_TYPE) 14 | # Set the possible values of build type for cmake-gui 15 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") 16 | endif() 17 | 18 | if(NOT DEFINED CMakePythonDistributions_SUPERBUILD) 19 | set(CMakePythonDistributions_SUPERBUILD 1) 20 | endif() 21 | 22 | if(CMakePythonDistributions_SUPERBUILD) 23 | 24 | enable_language(CXX) 25 | 26 | #----------------------------------------------------------------------------- 27 | # Options 28 | set(default ON) 29 | if(WIN32 OR APPLE) 30 | set(default OFF) 31 | endif() 32 | option(BUILD_CMAKE_FROM_SOURCE "Build CMake from source" ${default}) 33 | 34 | option(BUILD_VERBOSE "Build reporting additional information (e.g download progress, ...)" ON) 35 | 36 | option(RUN_CMAKE_TEST "Run CMake test suite when built from sources" OFF) 37 | 38 | set(RUN_CMAKE_TEST_EXCLUDE "BootstrapTest" CACHE STRING "CMake test suite exclusion regex") 39 | 40 | set(CMakePythonDistributions_ARCHIVE_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}" 41 | CACHE PATH "Directory where to download archives" 42 | ) 43 | 44 | message(STATUS "***************************************************") 45 | message(STATUS "Build CMake from source: ${BUILD_CMAKE_FROM_SOURCE}") 46 | message(STATUS "***************************************************") 47 | 48 | include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeUrls.cmake) 49 | 50 | #----------------------------------------------------------------------------- 51 | # Which archives ? 52 | 53 | function(check_archive_var archive_var) 54 | if(NOT DEFINED "${archive_var}_url") 55 | message(FATAL_ERROR "Failed to determine which archive to download: '${archive_var}_url' variable is not defined") 56 | endif() 57 | if(NOT DEFINED "${archive_var}_sha256") 58 | message(FATAL_ERROR "Could you make sure variable '${archive_var}_sha256' is defined ?") 59 | endif() 60 | endfunction() 61 | 62 | set(src_archive "unix_source") 63 | if(WIN32) 64 | set(src_archive "windows_source") 65 | endif() 66 | check_archive_var("${src_archive}") 67 | 68 | set(binary_archive "linux32_binary") 69 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 70 | set(binary_archive "linux64_binary") 71 | endif() 72 | if(APPLE) 73 | if(CMAKE_OSX_DEPLOYMENT_TARGET AND "${CMAKE_OSX_DEPLOYMENT_TARGET}" VERSION_LESS "10.10") 74 | message(FATAL_ERROR "Unsupported macOS deployment target: ${CMAKE_OSX_DEPLOYMENT_TARGET} is less than 10.10") 75 | else() 76 | set(binary_archive "macos10_10_binary") 77 | endif() 78 | endif() 79 | if(WIN32) 80 | set(binary_archive "win32_binary") 81 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 82 | if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ARM64" OR "$ENV{SETUPTOOLS_EXT_SUFFIX}" MATCHES arm64) 83 | set(binary_archive "winarm64_binary") 84 | else() 85 | set(binary_archive "win64_binary") 86 | endif() 87 | endif() 88 | endif() 89 | check_archive_var("${binary_archive}") 90 | 91 | #----------------------------------------------------------------------------- 92 | include(ExternalProject) 93 | 94 | # Add an empty external project 95 | function(cpd_ExternalProject_Add_Empty proj depends) 96 | set(depends_args) 97 | if(NOT depends STREQUAL "") 98 | set(depends_args DEPENDS ${depends}) 99 | endif() 100 | ExternalProject_add(${proj} 101 | SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj} 102 | DOWNLOAD_COMMAND "" 103 | UPDATE_COMMAND "" 104 | CONFIGURE_COMMAND "" 105 | BUILD_COMMAND "" 106 | BUILD_IN_SOURCE 1 107 | INSTALL_COMMAND "" 108 | ${depends_args} 109 | ) 110 | endfunction() 111 | 112 | # Add an external project step named `forceconfigure` to `project_name` ensuring 113 | # the project will always be reconfigured. 114 | # 115 | # Copied from ExternalProjectDependency.cmake (commontk/Artichoke@613e3739a) 116 | function(cpd_ExternalProject_AlwaysConfigure proj) 117 | _ep_get_step_stampfile(${proj} "configure" stampfile) 118 | ExternalProject_Add_Step(${proj} forceconfigure 119 | COMMAND ${CMAKE_COMMAND} -E remove ${stampfile} 120 | COMMENT "Forcing configure step for '${proj}'" 121 | DEPENDEES build 122 | ALWAYS 1 123 | ) 124 | endfunction() 125 | 126 | # Note: To minimize confusion between variables defined by CMake and 127 | # variables used in this project. The following convention applies: 128 | # CMakeProject_xxx : Variables defined in this project 129 | # CMAKE_xxx : Variables set by CMake 130 | 131 | set(${PROJECT_NAME}_CMAKE_CACHE_ARG) 132 | 133 | set(ep_download_no_progress_args) 134 | set(ep_log_configure_build_args) 135 | if(NOT BUILD_VERBOSE) 136 | set(ep_download_no_progress_args 137 | DOWNLOAD_NO_PROGRESS 1 138 | ) 139 | set(ep_log_configure_build_args 140 | LOG_CONFIGURE 1 141 | LOG_BUILD 1 142 | ) 143 | endif() 144 | 145 | set(ep_download_extract_timestamp_arg) 146 | if(CMAKE_VERSION VERSION_EQUAL "3.24" OR CMAKE_VERSION VERSION_GREATER "3.24") 147 | # See https://cmake.org/cmake/help/latest/policy/CMP0135.html 148 | set(ep_download_extract_timestamp_arg DOWNLOAD_EXTRACT_TIMESTAMP 1) 149 | endif() 150 | 151 | # 152 | # CMakeProject_SOURCE_DIR: Always expect the sources (needed for `sdist`) 153 | # 154 | if(NOT DEFINED CMakeProject_SOURCE_DIR) 155 | set(CMakeProject_SOURCE_DIR "${CMAKE_SOURCE_DIR}/CMake-src") 156 | 157 | # Download selected source archive 158 | ExternalProject_add(CMakeProject-src-download 159 | SOURCE_DIR ${CMakeProject_SOURCE_DIR} 160 | URL ${${src_archive}_url} 161 | URL_HASH SHA256=${${src_archive}_sha256} 162 | DOWNLOAD_DIR ${CMakePythonDistributions_ARCHIVE_DOWNLOAD_DIR} 163 | USES_TERMINAL_DOWNLOAD 1 164 | CONFIGURE_COMMAND "" 165 | BUILD_COMMAND "" 166 | BUILD_IN_SOURCE 1 167 | INSTALL_COMMAND "" 168 | ${ep_download_extract_timestamp_arg} 169 | ${ep_download_no_progress_args} 170 | ) 171 | message(STATUS "SuperBuild - CMakeProject-src-download") 172 | message(STATUS "SuperBuild - CMakeProject-src-download - URL: ${${src_archive}_url}") 173 | else() 174 | cpd_ExternalProject_Add_Empty(CMakeProject-src-download "") 175 | message(STATUS "SuperBuild - CMakeProject-src-download") 176 | endif() 177 | message(STATUS "SuperBuild - CMakeProject-src-download - CMakeProject_SOURCE_DIR: ${CMakeProject_SOURCE_DIR}") 178 | 179 | if(NOT EXISTS ${CMakeProject_SOURCE_DIR}) 180 | message(FATAL_ERROR "CMakeProject_SOURCE_DIR variable is defined but corresponds to nonexistent directory") 181 | endif() 182 | 183 | list(APPEND ${PROJECT_NAME}_DEPENDS CMakeProject-src-download) 184 | 185 | if(BUILD_CMAKE_FROM_SOURCE) 186 | 187 | # 188 | # CMakeProject_BINARY_DIR 189 | # 190 | if(NOT DEFINED CMakeProject_BINARY_DIR) 191 | 192 | # glibc check 193 | if(UNIX AND NOT APPLE) 194 | # as of CMake 3.23.0, the minimum supported version of libuv is 1.28.0. 195 | # this implies that the minimum supported glibc version is 2.12 196 | # https://github.com/libuv/libuv/blob/v1.x/SUPPORTED_PLATFORMS.md 197 | include(CheckSymbolExists) 198 | check_symbol_exists(__GLIBC__ "stdlib.h" HAS_GLIBC_MAJOR) 199 | check_symbol_exists(__GLIBC_MINOR__ "stdlib.h" HAS_GLIBC_MINOR) 200 | if(HAS_GLIBC_MAJOR AND HAS_GLIBC_MINOR AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")) 201 | execute_process(COMMAND echo __GLIBC__ COMMAND "${CMAKE_CXX_COMPILER}" -E -P -imacros stdlib.h - OUTPUT_VARIABLE GLIBC_MAJOR_) 202 | string(STRIP "${GLIBC_MAJOR_}" GLIBC_MAJOR) 203 | execute_process(COMMAND echo __GLIBC_MINOR__ COMMAND "${CMAKE_CXX_COMPILER}" -E -P -imacros stdlib.h - OUTPUT_VARIABLE GLIBC_MINOR_) 204 | string(STRIP "${GLIBC_MINOR_}" GLIBC_MINOR) 205 | if("${GLIBC_MAJOR}.${GLIBC_MINOR}" VERSION_LESS "2.12") 206 | message(FATAL_ERROR "GLIBC ${GLIBC_MAJOR}.${GLIBC_MINOR} not supported") 207 | endif() 208 | endif() 209 | endif() 210 | 211 | # cmake cache arguments 212 | set(_cmake_cache_args) 213 | if(DEFINED CMAKE_BUILD_TYPE) 214 | list(APPEND _cmake_cache_args 215 | -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} 216 | ) 217 | endif() 218 | if(DEFINED CMAKE_TOOLCHAIN_FILE) 219 | list(APPEND _cmake_cache_args 220 | -DCMAKE_TOOLCHAIN_FILE:STRING=${CMAKE_TOOLCHAIN_FILE} 221 | ) 222 | endif() 223 | foreach(var_name IN ITEMS 224 | CMAKE_BUILD_PARALLEL_LEVEL 225 | CMAKE_JOB_POOLS 226 | CMAKE_JOB_POOL_COMPILE 227 | CMAKE_JOB_POOL_LINK 228 | ) 229 | if(DEFINED ${var_name}) 230 | list(APPEND _cmake_cache_args 231 | -D${var_name}:STRING=${${var_name}} 232 | ) 233 | message(STATUS "SuperBuild - CMakeProject-build - ${var_name}: ${${var_name}}") 234 | endif() 235 | endforeach() 236 | 237 | if(DEFINED OPENSSL_ROOT_DIR) 238 | list(APPEND _cmake_cache_args 239 | -DOPENSSL_ROOT_DIR:PATH=${OPENSSL_ROOT_DIR} 240 | ) 241 | message(STATUS "SuperBuild - CMakeProject-build - OPENSSL_ROOT_DIR: ${OPENSSL_ROOT_DIR}") 242 | endif() 243 | 244 | if(DEFINED OPENSSL_USE_STATIC_LIBS) 245 | list(APPEND _cmake_cache_args 246 | -DOPENSSL_USE_STATIC_LIBS:BOOL=${OPENSSL_USE_STATIC_LIBS} 247 | ) 248 | message(STATUS "SuperBuild - CMakeProject-build - OPENSSL_USE_STATIC_LIBS: ${OPENSSL_USE_STATIC_LIBS}") 249 | endif() 250 | 251 | if(DEFINED CMAKE_CXX_STANDARD) 252 | list(APPEND _cmake_cache_args 253 | -DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} 254 | ) 255 | message(STATUS "SuperBuild - CMakeProject-build - CMAKE_CXX_STANDARD: ${CMAKE_CXX_STANDARD}") 256 | endif() 257 | 258 | set(_cmake_args ) 259 | if(UNIX AND (NOT APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")) 260 | # Since CMAKE_C_FLAGS and CMAKE_EXE_LINKER_FLAGS arguments contain spaces, we generate an initial 261 | # cache file. 262 | file(WRITE "${CMAKE_BINARY_DIR}/initial-cache.txt" 263 | "set(CMAKE_C_FLAGS \"-D_POSIX_C_SOURCE=199506L -D_POSIX_SOURCE=1 -D_SVID_SOURCE=1 -D_BSD_SOURCE=1\" CACHE STRING \"Initial cache\" FORCE) 264 | set(CMAKE_EXE_LINKER_FLAGS \"-lstdc++ -lgcc -lrt\" CACHE STRING \"Initial cache\" FORCE) 265 | ") 266 | set(_cmake_args 267 | CMAKE_ARGS -C "${CMAKE_BINARY_DIR}/initial-cache.txt" 268 | ) 269 | endif() 270 | 271 | # cmake 272 | set(CMakeProject_BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeProject-build) 273 | 274 | ExternalProject_add(CMakeProject-build 275 | SOURCE_DIR ${CMakeProject_SOURCE_DIR} 276 | BINARY_DIR ${CMakeProject_BINARY_DIR} 277 | DOWNLOAD_COMMAND "" 278 | UPDATE_COMMAND "" 279 | BUILD_ALWAYS 1 280 | ${_cmake_args} 281 | CMAKE_CACHE_ARGS 282 | -DBUILD_CursesDialog:BOOL=ON 283 | -DCMAKE_USE_OPENSSL:BOOL=ON 284 | -DBUILD_TESTING:BOOL=ON 285 | -DCMake_INSTALL_DEPENDENCIES:BOOL=ON 286 | -DCMAKE_INSTALL_MESSAGE:STRING=NEVER 287 | ${_cmake_cache_args} 288 | USES_TERMINAL_CONFIGURE 1 289 | USES_TERMINAL_BUILD 1 290 | ${ep_log_configure_build_args} 291 | INSTALL_COMMAND "" 292 | DEPENDS 293 | CMakeProject-src-download 294 | ) 295 | 296 | set(CMAKEPROJECT_BUILD_LAST_STEP "build") 297 | 298 | if(RUN_CMAKE_TEST) 299 | include(ProcessorCount) 300 | ProcessorCount(NB_CPU) 301 | if(NB_CPU EQUAL 0) 302 | set(NB_CPU 2) 303 | endif() 304 | ExternalProject_Add_Step(CMakeProject-build run_cmake_test_suite 305 | DEPENDEES ${CMAKEPROJECT_BUILD_LAST_STEP} 306 | COMMENT "Running CMake test suite, exclusion list: '${RUN_CMAKE_TEST_EXCLUDE}'" 307 | COMMAND ./bin/ctest --force-new-ctest-process --stop-on-failure --output-on-failure -j${NB_CPU} -E ${RUN_CMAKE_TEST_EXCLUDE} 308 | WORKING_DIRECTORY ${CMakeProject_BINARY_DIR} 309 | USES_TERMINAL 1 310 | ) 311 | set(CMAKEPROJECT_BUILD_LAST_STEP "run_cmake_test_suite") 312 | endif() 313 | else() 314 | cpd_ExternalProject_Add_Empty(CMakeProject-build "CMakeProject-src-download") 315 | endif() 316 | message(STATUS "SuperBuild - CMakeProject-build") 317 | message(STATUS "SuperBuild - CMakeProject-build - CMakeProject_BINARY_DIR: ${CMakeProject_BINARY_DIR}") 318 | 319 | if(NOT EXISTS ${CMakeProject_BINARY_DIR}) 320 | message(FATAL_ERROR "CMakeProject_BINARY_DIR variable is defined but corresponds to nonexistent directory") 321 | endif() 322 | 323 | list(APPEND ${PROJECT_NAME}_DEPENDS CMakeProject-build) 324 | list(APPEND ${PROJECT_NAME}_CMAKE_CACHE_ARG 325 | -DCMakeProject_BINARY_DIR:PATH=${CMakeProject_BINARY_DIR} 326 | ) 327 | 328 | else() 329 | 330 | # 331 | # CMakeProject_BINARY_DISTRIBUTION_DIR 332 | # 333 | 334 | if(${binary_archive}_sha256 STREQUAL "NA") 335 | message(FATAL_ERROR "Pre-built archives not available for '${binary_archive}'. Consider setting BUILD_CMAKE_FROM_SOURCE to ON.") 336 | endif() 337 | 338 | set(CMakeProject_BINARY_DISTRIBUTION_DIR "${CMAKE_BINARY_DIR}/CMakeProject-binary-distribution") 339 | 340 | # Download selected binary archive 341 | ExternalProject_add(CMakeProject-binary-download 342 | SOURCE_DIR ${CMakeProject_BINARY_DISTRIBUTION_DIR} 343 | URL ${${binary_archive}_url} 344 | URL_HASH SHA256=${${binary_archive}_sha256} 345 | DOWNLOAD_DIR ${CMakePythonDistributions_ARCHIVE_DOWNLOAD_DIR} 346 | USES_TERMINAL_DOWNLOAD 1 347 | CONFIGURE_COMMAND "" 348 | BUILD_COMMAND "" 349 | BUILD_IN_SOURCE 1 350 | INSTALL_COMMAND "" 351 | ${ep_download_extract_timestamp_arg} 352 | ${ep_download_no_progress_args} 353 | ) 354 | message(STATUS "SuperBuild - CMakeProject-binary-download") 355 | message(STATUS "SuperBuild - CMakeProject-binary-download - URL: ${${binary_archive}_url}") 356 | 357 | list(APPEND ${PROJECT_NAME}_DEPENDS CMakeProject-binary-download) 358 | list(APPEND ${PROJECT_NAME}_CMAKE_CACHE_ARG 359 | -DCMakeProject_BINARY_DISTRIBUTION_DIR:PATH=${CMakeProject_BINARY_DISTRIBUTION_DIR} 360 | ) 361 | 362 | endif() 363 | 364 | ExternalProject_add(${PROJECT_NAME} 365 | SOURCE_DIR ${CMAKE_SOURCE_DIR} 366 | BINARY_DIR ${CMAKE_BINARY_DIR}/${PROJECT_NAME}-build 367 | DOWNLOAD_COMMAND "" 368 | UPDATE_COMMAND "" 369 | CMAKE_CACHE_ARGS 370 | -D${PROJECT_NAME}_SUPERBUILD:BOOL=0 371 | -DBUILD_CMAKE_FROM_SOURCE:BOOL=${BUILD_CMAKE_FROM_SOURCE} 372 | -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} 373 | ${${PROJECT_NAME}_CMAKE_CACHE_ARG} 374 | USES_TERMINAL_CONFIGURE 1 375 | USES_TERMINAL_BUILD 1 376 | INSTALL_COMMAND "" 377 | DEPENDS 378 | ${${PROJECT_NAME}_DEPENDS} 379 | ) 380 | message(STATUS "SuperBuild - ${PROJECT_NAME}") 381 | 382 | cpd_ExternalProject_AlwaysConfigure(${PROJECT_NAME}) 383 | 384 | # This adds an "install" target in the top-level directory. The 385 | # target will simply include the install rules associated with the 386 | # inner build 387 | install(SCRIPT ${CMAKE_BINARY_DIR}/${PROJECT_NAME}-build/cmake_install.cmake) 388 | 389 | else() 390 | 391 | #----------------------------------------------------------------------------- 392 | if(BUILD_CMAKE_FROM_SOURCE) 393 | 394 | # Install CMakeProject 395 | install(CODE " 396 | message(STATUS \"Install CMake project\") 397 | include\(\"${CMakeProject_BINARY_DIR}/cmake_install.cmake\") 398 | ") 399 | 400 | #----------------------------------------------------------------------------- 401 | else() 402 | 403 | set(CMAKE_INSTALL_MESSAGE "NEVER") 404 | 405 | if(APPLE) 406 | set(distribution_root "${CMakeProject_BINARY_DISTRIBUTION_DIR}/CMake.app/Contents") 407 | else() 408 | set(distribution_root "${CMakeProject_BINARY_DISTRIBUTION_DIR}") 409 | endif() 410 | 411 | # Install all files from binary distribution 412 | file(GLOB_RECURSE binary_distribution_files 413 | LIST_DIRECTORIES FALSE 414 | ${distribution_root}/* 415 | ) 416 | foreach(file IN LISTS binary_distribution_files) 417 | # Skip symlinks like "CMake.app/Contents/Frameworks/QtWidgets.framework/Versions/Current" 418 | if(IS_SYMLINK ${file}) 419 | continue() 420 | endif() 421 | 422 | # skip some mac app bundle files 423 | set(find_index -1) 424 | foreach(name IN ITEMS CodeResources Info.plist Frameworks _CodeSignature MacOS PlugIns Resources doc/cmake/html man) 425 | string(FIND "${file}" "${distribution_root}/${name}" find_index) 426 | if("${find_index}" EQUAL 0) 427 | break() 428 | endif() 429 | endforeach() 430 | if("${find_index}" EQUAL 0) 431 | continue() 432 | endif() 433 | 434 | get_filename_component(directory ${file} DIRECTORY) 435 | file(RELATIVE_PATH relative_directory ${distribution_root} ${directory}) 436 | set(type FILES) 437 | if(relative_directory STREQUAL "bin") 438 | set(type PROGRAMS) 439 | endif() 440 | set(_permissions) 441 | get_filename_component(filename ${file} NAME) 442 | if(filename MATCHES "ccmake|cmake|cmake-gui|cpack|ctest") 443 | set(_permissions PERMISSIONS 444 | OWNER_READ OWNER_WRITE OWNER_EXECUTE 445 | GROUP_READ GROUP_EXECUTE 446 | WORLD_READ WORLD_EXECUTE 447 | ) 448 | endif() 449 | install(${type} ${file} DESTINATION "${relative_directory}" ${_permissions}) 450 | endforeach() 451 | endif() 452 | endif() 453 | -------------------------------------------------------------------------------- /CMakeUrls.cmake: -------------------------------------------------------------------------------- 1 | 2 | #----------------------------------------------------------------------------- 3 | # CMake sources 4 | set(unix_source_url "https://github.com/Kitware/CMake/releases/download/v4.0.2/cmake-4.0.2.tar.gz") 5 | set(unix_source_sha256 "1c3a82c8ca7cf12e0b17178f9d0c32f7ac773bd5651a98fcfd80fbf4977f8d48") 6 | 7 | set(windows_source_url "https://github.com/Kitware/CMake/releases/download/v4.0.2/cmake-4.0.2.zip") 8 | set(windows_source_sha256 "983f2b0cc24edd9d6c2754d2f52f0fce36f0368c95a9b6f45df3faf1dfd0728a") 9 | 10 | #----------------------------------------------------------------------------- 11 | # CMake binaries 12 | 13 | set(linux32_binary_url "NA") # Linux 32-bit binaries not available 14 | set(linux32_binary_sha256 "NA") 15 | 16 | set(linux64_binary_url "https://github.com/Kitware/CMake/releases/download/v4.0.2/cmake-4.0.2-linux-x86_64.tar.gz") 17 | set(linux64_binary_sha256 "80940e81de61584fe4eedd3c40adc597d7c5b76ad8709668007b467a3c2a36c7") 18 | 19 | set(macos10_10_binary_url "https://github.com/Kitware/CMake/releases/download/v4.0.2/cmake-4.0.2-macos10.10-universal.tar.gz") 20 | set(macos10_10_binary_sha256 "edb0713b557040a2e90718909e11cac82810fe2bf8943828b11cfb84b75815a0") 21 | 22 | set(win32_binary_url "https://github.com/Kitware/CMake/releases/download/v4.0.2/cmake-4.0.2-windows-i386.zip") 23 | set(win32_binary_sha256 "026009a57d06cb8342e42840fcbd83bc5e58ee3ea90d3ddc8e46f82a0d93d9ff") 24 | 25 | set(win64_binary_url "https://github.com/Kitware/CMake/releases/download/v4.0.2/cmake-4.0.2-windows-x86_64.zip") 26 | set(win64_binary_sha256 "109ec7de10416d6d78991bab9714d2cb1ccb71d1b436dff42ec978dd283c29fc") 27 | 28 | set(winarm64_binary_url "https://github.com/Kitware/CMake/releases/download/v4.0.2/cmake-4.0.2-windows-arm64.zip") 29 | set(winarm64_binary_sha256 "634d6bab01a639e314f4fb2fee0967c84c3a37e39b9b2c9455c2dff2deb6a6dc") 30 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Contributing 3 | ============ 4 | 5 | Contributions are welcome, and they are greatly appreciated! Every 6 | little bit helps, and credit will always be given. 7 | 8 | Types of Contributions 9 | ---------------------- 10 | 11 | You can contribute in many ways: 12 | 13 | Report Bugs 14 | ~~~~~~~~~~~ 15 | 16 | Report bugs at https://github.com/scikit-build/cmake-python-distributions/issues. 17 | 18 | If you are reporting a bug, please include: 19 | 20 | * Your operating system name and version. 21 | * Any details about your local setup that might be helpful in troubleshooting. 22 | * Detailed steps to reproduce the bug. 23 | 24 | Fix Bugs 25 | ~~~~~~~~ 26 | 27 | Look through the GitHub issues for bugs. Anything tagged with "bug" 28 | is open to whoever wants to implement it. 29 | 30 | Implement Features 31 | ~~~~~~~~~~~~~~~~~~ 32 | 33 | Look through the GitHub issues for features. Anything tagged with "feature" 34 | is open to whoever wants to implement it. 35 | 36 | Write Documentation 37 | ~~~~~~~~~~~~~~~~~~~ 38 | 39 | The cmake-python-distributions project could always use more documentation. We welcome help 40 | with the official cmake-python-distributions docs, in docstrings, or even on blog posts and 41 | articles for the web. 42 | 43 | Submit Feedback 44 | ~~~~~~~~~~~~~~~ 45 | 46 | The best way to send feedback is to file an issue at 47 | https://github.com/scikit-build/cmake-python-distributions/issues. 48 | 49 | If you are proposing a new feature: 50 | 51 | * Explain in detail how it would work. 52 | * Keep the scope as narrow as possible, to make it easier to implement. 53 | * Remember that this is a volunteer-driven project, and that contributions 54 | are welcome :) 55 | 56 | 57 | Get Started 58 | ----------- 59 | 60 | Ready to contribute? Here's how to set up `cmake-python-distributions` for local development. 61 | 62 | 1. Fork the `cmake-python-distributions` repo on GitHub. 63 | 64 | 2. Clone your fork locally:: 65 | 66 | $ git clone git@github.com:your_name_here/cmake-python-distributions.git 67 | 68 | 3. Make sure you have ``nox`` installed (preferably use ``pipx`` or ``brew`` 69 | (macOS) if you have those):: 70 | 71 | $ pip install nox 72 | $ cd cmake-python-distributions/ 73 | 74 | 4. Create a branch for local development:: 75 | 76 | $ git checkout -b name-of-your-bugfix-or-feature 77 | 78 | Now you can make your changes locally. 79 | 80 | 5. When you're done making changes, check that your changes pass linters and 81 | the tests:: 82 | 83 | $ nox 84 | 85 | 6. Commit your changes and push your branch to GitHub:: 86 | 87 | $ git add . 88 | $ git commit -m "Your detailed description of your changes." 89 | $ git push origin name-of-your-bugfix-or-feature 90 | 91 | 7. Submit a pull request through the GitHub website. 92 | 93 | 94 | Pull Request Guidelines 95 | ----------------------- 96 | 97 | Before you submit a pull request, check that it meets these guidelines: 98 | 99 | 1. The pull request should include tests. 100 | 101 | 2. If the pull request adds functionality, the docs should be updated. Put 102 | your new functionality into a function with a docstring, and add the 103 | feature to the list in `README.rst`. 104 | 105 | 3. The pull request should work for Python 2.7, and 3.6+. 106 | Check `GitHub Actions `_ 107 | and make sure that the tests pass for all supported Python versions. 108 | 109 | 110 | Tips 111 | ---- 112 | 113 | To run a subset of tests:: 114 | 115 | $ pytest tests/test_cmake.py 116 | # OR 117 | $ nox -s tests -- tests/test_cmake.py 118 | -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | .. :changelog: 2 | 3 | History 4 | ------- 5 | 6 | cmake-python-distributions was initially developed in September 2016 by 7 | Jean-Christophe Fillion-Robin to facilitate the distribution of project using 8 | `scikit-build `_ and depending on CMake. 9 | -------------------------------------------------------------------------------- /LICENSE_Apache_20: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /LICENSE_BSD_3: -------------------------------------------------------------------------------- 1 | CMake is distributed under the OSI-approved BSD 3-clause License: 2 | 3 | CMake - Cross Platform Makefile Generator 4 | Copyright 2000-2014 Kitware, Inc. 5 | Copyright 2000-2011 Insight Software Consortium 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions 10 | are met: 11 | 12 | * Redistributions of source code must retain the above copyright 13 | notice, this list of conditions and the following disclaimer. 14 | 15 | * Redistributions in binary form must reproduce the above copyright 16 | notice, this list of conditions and the following disclaimer in the 17 | documentation and/or other materials provided with the distribution. 18 | 19 | * Neither the names of Kitware, Inc., the Insight Software Consortium, 20 | nor the names of their contributors may be used to endorse or promote 21 | products derived from this software without specific prior written 22 | permission. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | CMake Python Distributions 3 | ========================== 4 | 5 | `CMake `_ is used to control the software compilation 6 | process using simple platform and compiler independent configuration files, 7 | and generate native makefiles and workspaces that can be used in the 8 | compiler environment of your choice. 9 | 10 | The suite of CMake tools were created by Kitware in response to the need 11 | for a powerful, cross-platform build environment for open-source projects 12 | such as ITK and VTK. 13 | 14 | The CMake python wheels provide `CMake 4.0.2 `_. 15 | 16 | Latest Release 17 | -------------- 18 | 19 | .. table:: 20 | 21 | +----------------------------------------------------------------------+---------------------------------------------------------------------------+ 22 | | Versions | Downloads | 23 | +======================================================================+===========================================================================+ 24 | | .. image:: https://img.shields.io/pypi/v/cmake.svg | .. image:: https://static.pepy.tech/badge/cmake | 25 | | :target: https://pypi.python.org/pypi/cmake | :target: https://pypi.python.org/pypi/cmake | 26 | | | .. image:: https://img.shields.io/pypi/dm/cmake | 27 | | | :target: https://pypi.python.org/pypi/cmake | 28 | +----------------------------------------------------------------------+---------------------------------------------------------------------------+ 29 | 30 | Build Status 31 | ------------ 32 | 33 | .. table:: 34 | 35 | +---------------+--------------------------------------------------------------------------------------------------------------+ 36 | | | GitHub Actions (Windows, macOS, Linux) | 37 | +===============+==============================================================================================================+ 38 | | PyPI | .. image:: https://github.com/scikit-build/cmake-python-distributions/actions/workflows/build.yml/badge.svg | 39 | | | :target: https://github.com/scikit-build/cmake-python-distributions/actions/workflows/build.yml | 40 | +---------------+--------------------------------------------------------------------------------------------------------------+ 41 | 42 | Platforms 43 | --------- 44 | 45 | The following platforms are supported with binary wheels: 46 | 47 | .. table:: 48 | 49 | +---------------+---------------------------+ 50 | | OS | Arch | 51 | +===============+===========================+ 52 | | Windows | | 64-bit | 53 | | | | 32-bit | 54 | +---------------+---------------------------+ 55 | | Linux Intel | | manylinux2010+ x86_64 | 56 | | | | musllinux_1_1+ x86_64 | 57 | | | | manylinux2010+ i686 | 58 | | | | musllinux_1_1+ i686 | 59 | +---------------+---------------------------+ 60 | | Linux ARM | | manylinux2014+ AArch64 | 61 | | | | musllinux_1_1+ AArch64 | 62 | | | | manylinux_2_31+ armv7l | 63 | | | | musllinux_1_2+ armv7l | 64 | +---------------+---------------------------+ 65 | | Linux PowerPC | | manylinux2014+ ppc64le | 66 | | | | musllinux_1_1+ ppc64le | 67 | +---------------+---------------------------+ 68 | | Linux IBM Z | | manylinux2014+ s390x | 69 | | | | musllinux_1_1+ s390x | 70 | +---------------+---------------------------+ 71 | | macOS 10.10+ | Intel | 72 | +---------------+---------------------------+ 73 | | macOS 11+ | Apple Silicon | 74 | +---------------+---------------------------+ 75 | 76 | The last version to provide ``manylinux1`` wheels was ``3.22.x``. 77 | The last version to provide Python 2 to Python 3.6 support was ``3.28.x``. 78 | 79 | Maintainers 80 | ----------- 81 | 82 | * `How to update CMake version? `_ 83 | 84 | * `How to make a release? `_ 85 | 86 | Miscellaneous 87 | ------------- 88 | 89 | * Documentation: https://cmake-python-distributions.readthedocs.io/en/latest/ 90 | * Source code: https://github.com/scikit-build/cmake-python-distributions 91 | * Mailing list: https://groups.google.com/forum/#!forum/scikit-build 92 | 93 | License 94 | ------- 95 | 96 | This project is maintained by Jean-Christophe Fillion-Robin from Kitware Inc. 97 | It is covered by the `Apache License, Version 2.0 `_. 98 | 99 | CMake is distributed under the OSI-approved BSD 3-clause License. 100 | For more information about CMake, visit https://cmake.org 101 | -------------------------------------------------------------------------------- /_build_backend/backend.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | 5 | from scikit_build_core import build as _orig 6 | 7 | if hasattr(_orig, "prepare_metadata_for_build_editable"): 8 | prepare_metadata_for_build_editable = _orig.prepare_metadata_for_build_editable 9 | if hasattr(_orig, "prepare_metadata_for_build_wheel"): 10 | prepare_metadata_for_build_wheel = _orig.prepare_metadata_for_build_wheel 11 | build_editable = _orig.build_editable 12 | build_sdist = _orig.build_sdist 13 | get_requires_for_build_editable = _orig.get_requires_for_build_editable 14 | get_requires_for_build_sdist = _orig.get_requires_for_build_sdist 15 | 16 | 17 | def _strtobool(value: str) -> bool: 18 | """ 19 | Converts a environment variable string into a boolean value. 20 | """ 21 | if not value: 22 | return False 23 | value = value.lower() 24 | if value.isdigit(): 25 | return bool(int(value)) 26 | return value not in {"n", "no", "off", "false", "f"} 27 | 28 | 29 | def get_requires_for_build_wheel( 30 | config_settings: dict[str, str | list[str]] | None = None, 31 | ) -> list[str]: 32 | packages_orig = _orig.get_requires_for_build_wheel(config_settings) 33 | allow_cmake = _strtobool(os.environ.get("CMAKE_PYTHON_DIST_ALLOW_CMAKE_DEP", "")) 34 | allow_ninja = any( 35 | _strtobool(os.environ.get(var, "")) 36 | for var in ("CMAKE_PYTHON_DIST_FORCE_NINJA_DEP", "CMAKE_PYTHON_DIST_ALLOW_NINJA_DEP") 37 | ) 38 | packages = [] 39 | for package in packages_orig: 40 | package_name = package.lower().split(">")[0].strip() 41 | if package_name == "cmake" and not allow_cmake: 42 | continue 43 | if package_name == "ninja" and not allow_ninja: 44 | continue 45 | packages.append(package) 46 | return packages 47 | 48 | 49 | def _bootstrap_build(temp_path: str, config_settings: dict[str, list[str] | str] | None = None) -> str: 50 | import hashlib 51 | import platform 52 | import re 53 | import shutil 54 | import subprocess 55 | import tarfile 56 | import urllib.request 57 | import zipfile 58 | from pathlib import Path 59 | 60 | env = os.environ.copy() 61 | temp_path_ = Path(temp_path) 62 | 63 | archive_dir = temp_path_ 64 | if config_settings: 65 | archive_dir = Path(config_settings.get("cmake.define.CMakePythonDistributions_ARCHIVE_DOWNLOAD_DIR", archive_dir)) 66 | archive_dir.mkdir(parents=True, exist_ok=True) 67 | 68 | if os.name == "posix": 69 | if "MAKE" not in env: 70 | make_path = None 71 | make_candidates = ("gmake", "make", "smake") 72 | for candidate in make_candidates: 73 | make_path = shutil.which(candidate) 74 | if make_path is not None: 75 | break 76 | if make_path is None: 77 | msg = f"Could not find a make program. Tried {make_candidates!r}" 78 | raise ValueError(msg) 79 | env["MAKE"] = make_path 80 | make_path = env["MAKE"] 81 | kind = "unix_source" 82 | else: 83 | assert os.name == "nt" 84 | machine = platform.machine() 85 | kinds = { 86 | "x86": "win32_binary", 87 | "AMD64": "win64_binary", 88 | "ARM64": "winarm64_binary", 89 | } 90 | if machine not in kinds: 91 | msg = f"Could not find CMake required to build on a {machine} system" 92 | raise ValueError(msg) 93 | kind = kinds[machine] 94 | 95 | 96 | cmake_urls = Path("CMakeUrls.cmake").read_text() 97 | archive_url = re.findall(rf'set\({kind}_url\s+"(?P.*)"\)$', cmake_urls, flags=re.MULTILINE)[0] 98 | archive_sha256 = re.findall(rf'set\({kind}_sha256\s+"(?P.*)"\)$', cmake_urls, flags=re.MULTILINE)[0] 99 | 100 | archive_name = archive_url.rsplit("/", maxsplit=1)[1] 101 | archive_path = archive_dir / archive_name 102 | if not archive_path.exists(): 103 | with urllib.request.urlopen(archive_url) as response: 104 | archive_path.write_bytes(response.read()) 105 | 106 | sha256 = hashlib.sha256(archive_path.read_bytes()).hexdigest() 107 | if archive_sha256.lower() != sha256.lower(): 108 | msg = f"Invalid sha256 for {archive_url!r}. Expected {archive_sha256!r}, got {sha256!r}" 109 | raise ValueError(msg) 110 | 111 | if os.name == "posix": 112 | assert archive_name.endswith(".tar.gz") 113 | tar_filter_kwargs = {"filter": "tar"} if hasattr(tarfile, "tar_filter") else {} 114 | with tarfile.open(archive_path) as tar: 115 | tar.extractall(path=temp_path_, **tar_filter_kwargs) 116 | 117 | parallel_str = env.get("CMAKE_BUILD_PARALLEL_LEVEL", "1") 118 | parallel = max(0, int(parallel_str) if parallel_str.isdigit() else 1) or os.cpu_count() or 1 119 | 120 | bootstrap_path = next(temp_path_.glob("cmake-*/bootstrap")) 121 | prefix_path = temp_path_ / "cmake-install" 122 | cmake_path = prefix_path / "bin" / "cmake" 123 | bootstrap_args = [f"--prefix={prefix_path}", "--no-qt-gui", "--no-debugger", "--parallel={parallel}", "--", "-DBUILD_TESTING=OFF", "-DBUILD_CursesDialog:BOOL=OFF"] 124 | previous_cwd = Path().absolute() 125 | os.chdir(bootstrap_path.parent) 126 | try: 127 | subprocess.run([bootstrap_path, *bootstrap_args], env=env, check=True) 128 | subprocess.run([make_path, "-j", f"{parallel}"], env=env, check=True) 129 | subprocess.run([make_path, "install"], env=env, check=True) 130 | finally: 131 | os.chdir(previous_cwd) 132 | else: 133 | assert archive_name.endswith(".zip") 134 | with zipfile.ZipFile(archive_path) as zip_: 135 | zip_.extractall(path=temp_path_) 136 | cmake_path = next(temp_path_.glob("cmake-*/bin/cmake.exe")) 137 | 138 | return str(cmake_path) 139 | 140 | 141 | def build_wheel( 142 | wheel_directory: str, 143 | config_settings: dict[str, list[str] | str] | None = None, 144 | metadata_directory: str | None = None, 145 | ) -> str: 146 | from scikit_build_core.errors import CMakeNotFoundError 147 | 148 | try: 149 | return _orig.build_wheel(wheel_directory, config_settings, metadata_directory) 150 | except CMakeNotFoundError: 151 | if os.name not in {"posix", "nt"}: 152 | raise 153 | # Let's try bootstrapping CMake 154 | import tempfile 155 | with tempfile.TemporaryDirectory() as temp_path: 156 | cmake_path = _bootstrap_build(temp_path, config_settings) 157 | assert cmake_path 158 | os.environ["CMAKE_EXECUTABLE"] = cmake_path 159 | return _orig.build_wheel(wheel_directory, config_settings, metadata_directory) 160 | -------------------------------------------------------------------------------- /docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../AUTHORS.rst 2 | -------------------------------------------------------------------------------- /docs/build_system.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Understanding the Build-system 3 | ============================== 4 | 5 | 6 | The build system is described by the ``CMakeLists.txt`` and is composed of few projects each responsible 7 | for a specific task. Once configured, the `Outer Project` is responsible for driving the overall build 8 | composed of multiple project called `external project`. Here is the list of `external project`: 9 | 10 | * ``CMakeProject-src-download`` 11 | * ``CMakeProject-binary-download`` 12 | * ``CMakeProject-build`` 13 | * ``CMakePythonDistributions``: This corresponds to the `Inner Project` represented below. 14 | 15 | The flow chart represented below illustrates which external projects are included based on the configure 16 | options and describes the role of each one: 17 | 18 | .. mermaid:: 19 | :align: center 20 | 21 | flowchart LR 22 | subgraph OP[Outer Project] 23 | style OP fill:#FFF0D7 24 | configure["CMakeLists.tct"] 25 | ask_download{"Download source?"} 26 | download_source["Download Source archive"] 27 | reuse_source_dir["Re-use source directory"] 28 | ask_build{"Build from Source?"} 29 | download_binaries["CMakeProject-binary-download"] 30 | build_cmake["CMakeProject-build"] 31 | strip_executables["Strip executables"] 32 | ask_inner_build{"Which files to install?"} 33 | install_pre_built["Install prebuilt binaries"] 34 | install_cmake_project["Install CMake project"] 35 | 36 | configure --> ask_download 37 | 38 | subgraph EP1[ExternalProject: CMakeProject-src-download] 39 | style EP1 fill:#E7CA92 40 | ask_download -->|yes| download_source 41 | ask_download -->|no| reuse_source_dir 42 | end 43 | 44 | download_source --> ask_build 45 | reuse_source_dir --> ask_build 46 | 47 | subgraph EP2[External Projects] 48 | style EP2 fill:#E7CA92 49 | ask_build -->|no| download_binaries 50 | ask_build -->|yes| build_cmake 51 | build_cmake --> strip_executables 52 | end 53 | 54 | download_binaries --> ask_inner_build 55 | strip_executables --> ask_inner_build 56 | 57 | subgraph IP[Inner Project: CMakePythonDistributions] 58 | style IP fill:#a1acc2 59 | ask_inner_build -->|no| install_pre_built 60 | ask_inner_build -->|yes| install_cmake_project 61 | end 62 | end 63 | 64 | 65 | +----------------------------------------+--------------------------------------------------------------------------+ 66 | | **Node Title** | **Description** | 67 | +========================================+==========================================================================+ 68 | | CMakeLists | CMake configuration file | 69 | +----------------------------------------+--------------------------------------------------------------------------+ 70 | | Download source ? | If option ``CMakeProject_SOURCE_DIR`` is set, skip source download. | 71 | +----------------------------------------+--------------------------------------------------------------------------+ 72 | | Download Source archive | External project downloading archives from https://cmake.org/files/. | 73 | +----------------------------------------+--------------------------------------------------------------------------+ 74 | | Re-use source directory | Empty external project. | 75 | +----------------------------------------+--------------------------------------------------------------------------+ 76 | | Build from Source ? | Answer based on option ``BUILD_CMAKE_FROM_SOURCE`` | 77 | +----------------------------------------+--------------------------------------------------------------------------+ 78 | | CMakeProject-binary-download | External project downloading pre-built binary archives from | 79 | | | https://cmake.org/files/. | 80 | +----------------------------------------+--------------------------------------------------------------------------+ 81 | | CMakeProject-build | External project building CMake from source. | 82 | +----------------------------------------+--------------------------------------------------------------------------+ 83 | | Strip executables | If possible, reduce wheel size stripping cmake, cpack and ctest | 84 | | | executables | 85 | +----------------------------------------+--------------------------------------------------------------------------+ 86 | | Which files to install? | Answer based on option ``BUILD_CMAKE_FROM_SOURCE`` | 87 | +----------------------------------------+--------------------------------------------------------------------------+ 88 | | Install prebuilt binaries | Recursively glob all files and explicitly add install rules. | 89 | +----------------------------------------+--------------------------------------------------------------------------+ 90 | | Install CMake project | Achieved by including ``${CMakeProject_BINARY_DIR}/cmake_install.cmake`` | 91 | +----------------------------------------+--------------------------------------------------------------------------+ 92 | -------------------------------------------------------------------------------- /docs/building.rst: -------------------------------------------------------------------------------- 1 | =============================== 2 | Building the CMake Python wheel 3 | =============================== 4 | 5 | Overview 6 | -------- 7 | 8 | This project has been designed to work with `scikit-build-core `_. 9 | 10 | This is done ensuring source files and build artifacts 11 | are copied and/or generated in expected locations. 12 | 13 | 14 | Prerequisites 15 | ------------- 16 | 17 | In addition of ``Git``, ``Python`` and `CMake `_, building 18 | the wheel with ``BUILD_CMAKE_FROM_SOURCE`` set to ``ON`` also requires a 19 | ``C++ Compiler``. 20 | 21 | 22 | Quick start 23 | ----------- 24 | 25 | Build the CMake Python wheel with the following command:: 26 | 27 | python3 -m venv .venv 28 | source .venv/bin/activate 29 | pip install -r requirements-dev.txt build 30 | python -m build --wheel 31 | 32 | 33 | Source distribution (sdist) 34 | --------------------------- 35 | 36 | CMake sources will always be downloaded in the ``/src`` 37 | directory. 38 | 39 | This will ensure that the rules specified in ``/MANIFEST.in`` 40 | can successfully glob the source files. 41 | 42 | The source distribution is generated using the following 43 | command:: 44 | 45 | python -m build --sdist 46 | 47 | 48 | Binary distribution (build, bdist, bdist_wheel) 49 | ----------------------------------------------- 50 | 51 | The project has two mode of operations: 52 | 53 | #. build CMake from source (``BUILD_CMAKE_FROM_SOURCE`` set to ``ON``) 54 | #. download CMake binaries (``BUILD_CMAKE_FROM_SOURCE`` set to ``OFF``) 55 | 56 | The binary distribution is generated using the following 57 | command:: 58 | 59 | python -m build --wheel 60 | 61 | 62 | Changing the default mode is achieved by explicitly passing the option 63 | to CMake:: 64 | 65 | python -m build --wheel -Ccmake.define.BUILD_CMAKE_FROM_SOURCE=ON 66 | 67 | 68 | Default value for ``BUILD_CMAKE_FROM_SOURCE`` 69 | --------------------------------------------- 70 | 71 | Depending on the platform, option ``BUILD_CMAKE_FROM_SOURCE`` has 72 | different default: 73 | 74 | - Linux: ON 75 | - MacOSX: OFF 76 | - Windows: OFF 77 | 78 | Controlling verbosity 79 | --------------------- 80 | 81 | configure and build output 82 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 83 | 84 | By default, the output associated to the configure and build steps of the 85 | `CMakeProject-build` external project are logged into files. This can be 86 | changed by setting the ``BUILD_VERBOSE`` option:: 87 | 88 | python -m build --wheel -Ccmake.define.BUILD_VERBOSE=ON 89 | 90 | 91 | Optimizations 92 | ------------- 93 | 94 | On a given platform, when building different "flavor" of CMake python wheels (one 95 | for each ``-`` tag), the whole process can be made faster in two 96 | ways. 97 | 98 | Caching downloads 99 | ^^^^^^^^^^^^^^^^^ 100 | 101 | To avoid the re-download of CMake sources and/or binary packages, passing the 102 | option ``-Ccmake.define.CMakePythonDistributions_ARCHIVE_DOWNLOAD_DIR=/path/to/cache`` 103 | enables successive build to re-use existing archives instead of re-downloading them. 104 | 105 | Re-using build tree 106 | ^^^^^^^^^^^^^^^^^^^ 107 | 108 | And finally, on a given platform, to avoid rebuilding CMake, the idea is to 109 | first create a standalone build of the CMake project and then building the 110 | wheel using it. 111 | 112 | Step 1: Standalone build:: 113 | 114 | mkdir -p standalone-build && cd $_ 115 | cmake -DCMakePythonDistributions_ARCHIVE_DOWNLOAD_DIR:PATH=/path/to/cache -G Ninja ../ 116 | 117 | Step 2: Faster build reusing download and build directories:: 118 | 119 | python -m build -Ccmake.define.CMakePythonDistributions_ARCHIVE_DOWNLOAD_DIR=/path/to/cache 120 | -Ccmake.define.CMakeProject_BINARY_DIR=/path/to/standalone-build 121 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # 2 | # CMake Python Distributions documentation build configuration file, created by 3 | # sphinx-quickstart on Wed Nov 9 02:28:46 2016. 4 | # 5 | # This file is execfile()d with the current directory set to its 6 | # containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | # If extensions (or modules to document with autodoc) are in another directory, 15 | # add these directories to sys.path here. If the directory is relative to the 16 | # documentation root, use os.path.abspath to make it absolute, like shown here. 17 | # 18 | import sys 19 | from pathlib import Path 20 | 21 | if sys.version_info < (3, 11): 22 | from tomli import tomllib 23 | else: 24 | import tomllib 25 | 26 | DIR = Path(__file__).parent.resolve() 27 | pyproject = DIR.parent / "pyproject.toml" 28 | 29 | # -- Options for blockdiag extension -------------------------------------- 30 | 31 | blockdiag_antialias = True 32 | 33 | # -- General configuration ------------------------------------------------ 34 | 35 | # If your documentation needs a minimal Sphinx version, state it here. 36 | # 37 | # needs_sphinx = '1.0' 38 | 39 | # Add any Sphinx extension module names here, as strings. They can be 40 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 41 | # ones. 42 | extensions = ['sphinxcontrib.mermaid'] 43 | 44 | # Add any paths that contain templates here, relative to this directory. 45 | templates_path = ['_templates'] 46 | 47 | # The suffix(es) of source filenames. 48 | # You can specify multiple suffix as a list of string: 49 | # 50 | # source_suffix = ['.rst', '.md'] 51 | source_suffix = '.rst' 52 | 53 | # The encoding of source files. 54 | # 55 | # source_encoding = 'utf-8-sig' 56 | 57 | # The main toctree document. 58 | main_doc = 'index' 59 | 60 | # General information about the project. 61 | project = 'CMake Python Distributions' 62 | copyright = '2016, Jean-Christophe Fillion-Robin' 63 | author = 'Jean-Christophe Fillion-Robin' 64 | 65 | # The version info for the project you're documenting, acts as replacement for 66 | # |version| and |release|, also used in various other places throughout the 67 | # built documents. 68 | with pyproject.open('rb') as f: 69 | release = tomllib.load(f)["project"]["version"] 70 | 71 | # A shorter version 72 | version = ".".join(release.split(".")[:2]) 73 | 74 | # The language for content autogenerated by Sphinx. Refer to documentation 75 | # for a list of supported languages. 76 | # 77 | # This is also used if you do content translation via gettext catalogs. 78 | # Usually you set "language" from the command line for these cases. 79 | language = 'en' 80 | 81 | # There are two options for replacing |today|: either, you set today to some 82 | # non-false value, then it is used: 83 | # 84 | # today = '' 85 | # 86 | # Else, today_fmt is used as the format for a strftime call. 87 | # 88 | # today_fmt = '%B %d, %Y' 89 | 90 | # List of patterns, relative to source directory, that match files and 91 | # directories to ignore when looking for source files. 92 | # This patterns also effect to html_static_path and html_extra_path 93 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 94 | 95 | # The reST default role (used for this markup: `text`) to use for all 96 | # documents. 97 | # 98 | # default_role = None 99 | 100 | # If true, '()' will be appended to :func: etc. cross-reference text. 101 | # 102 | # add_function_parentheses = True 103 | 104 | # If true, the current module name will be prepended to all description 105 | # unit titles (such as .. function::). 106 | # 107 | # add_module_names = True 108 | 109 | # If true, sectionauthor and moduleauthor directives will be shown in the 110 | # output. They are ignored by default. 111 | # 112 | # show_authors = False 113 | 114 | # The name of the Pygments (syntax highlighting) style to use. 115 | pygments_style = 'sphinx' 116 | 117 | # A list of ignored prefixes for module index sorting. 118 | # modindex_common_prefix = [] 119 | 120 | # If true, keep warnings as "system message" paragraphs in the built documents. 121 | # keep_warnings = False 122 | 123 | # If true, `todo` and `todoList` produce output, else they produce nothing. 124 | todo_include_todos = False 125 | 126 | 127 | # -- Options for HTML output ---------------------------------------------- 128 | 129 | # The theme to use for HTML and HTML Help pages. See the documentation for 130 | # a list of builtin themes. 131 | # 132 | html_theme = 'furo' 133 | 134 | # -- Options for LaTeX output --------------------------------------------- 135 | 136 | latex_elements = { 137 | # The paper size ('letterpaper' or 'a4paper'). 138 | # 139 | # 'papersize': 'letterpaper', 140 | 141 | # The font size ('10pt', '11pt' or '12pt'). 142 | # 143 | # 'pointsize': '10pt', 144 | 145 | # Additional stuff for the LaTeX preamble. 146 | # 147 | # 'preamble': '', 148 | 149 | # Latex figure (float) alignment 150 | # 151 | # 'figure_align': 'htbp', 152 | } 153 | 154 | # Grouping the document tree into LaTeX files. List of tuples 155 | # (source start file, target name, title, 156 | # author, documentclass [howto, manual, or own class]). 157 | latex_documents = [ 158 | (main_doc, 'CMakePythonDistributions.tex', 'CMake Python Distributions Documentation', 159 | 'Jean-Christophe Fillion-Robin', 'manual'), 160 | ] 161 | 162 | 163 | # -- Options for manual page output --------------------------------------- 164 | 165 | # One entry per manual page. List of tuples 166 | # (source start file, name, description, authors, manual section). 167 | man_pages = [ 168 | (main_doc, 'cmakepythondistributions', 'CMake Python Distributions Documentation', 169 | [author], 1) 170 | ] 171 | 172 | # If true, show URL addresses after external links. 173 | # 174 | # man_show_urls = False 175 | 176 | 177 | # -- Options for Texinfo output ------------------------------------------- 178 | 179 | # Grouping the document tree into Texinfo files. List of tuples 180 | # (source start file, target name, title, author, 181 | # dir menu entry, description, category) 182 | texinfo_documents = [ 183 | (main_doc, 'CMakePythonDistributions', 'CMake Python Distributions Documentation', 184 | author, 'CMakePythonDistributions', 'One line description of project.', 185 | 'Miscellaneous'), 186 | ] 187 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/history.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../HISTORY.rst 2 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. CMake Python Distributions documentation main file, created by 2 | sphinx-quickstart on Wed Nov 9 02:28:46 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to CMake Python Distributions's documentation! 7 | ====================================================== 8 | 9 | `CMake `_ is used to control the software compilation 10 | process using simple platform and compiler independent configuration files, 11 | and generate native makefiles and workspaces that can be used in the 12 | compiler environment of your choice. 13 | 14 | The suite of CMake tools were created by Kitware in response to the need 15 | for a powerful, cross-platform build environment for open-source projects 16 | such as `ITK `_ and `VTK `_. 17 | 18 | The CMake python wheels provide `CMake 4.0.2 `_. 19 | 20 | .. toctree:: 21 | :maxdepth: 2 22 | :caption: User guide 23 | 24 | installation 25 | usage 26 | building 27 | build_system 28 | contributing 29 | authors 30 | history 31 | 32 | .. toctree:: 33 | :maxdepth: 2 34 | :caption: For maintainers 35 | 36 | update_cmake_version 37 | make_a_release 38 | 39 | 40 | Indices and tables 41 | ================== 42 | 43 | * :ref:`genindex` 44 | * :ref:`modindex` 45 | * :ref:`search` 46 | 47 | 48 | Resources 49 | ========= 50 | 51 | This project is maintained by Jean-Christophe Fillion-Robin from Kitware Inc. 52 | It is covered by the `Apache License, Version 2.0 `_. 53 | 54 | CMake is distributed under the OSI-approved BSD 3-clause License. 55 | For more information about CMake, visit https://cmake.org 56 | 57 | * Documentation: https://cmake-python-distributions.readthedocs.io/en/latest/ 58 | * Source code: https://github.com/scikit-build/cmake-python-distributions 59 | * Mailing list: https://groups.google.com/forum/#!forum/scikit-build 60 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | Install package with pip 6 | ------------------------ 7 | 8 | To install with pip:: 9 | 10 | $ pip install cmake 11 | 12 | Install from source 13 | ------------------- 14 | 15 | See :doc:`building` 16 | -------------------------------------------------------------------------------- /docs/make_a_release.rst: -------------------------------------------------------------------------------- 1 | .. _making_a_release: 2 | 3 | ================ 4 | Making a release 5 | ================ 6 | 7 | A core developer should use the following steps to create a release `X.Y.Z` of 8 | **cmake-python-distributions** on `PyPI`_. 9 | 10 | This is usually done after :ref:`updating_cmake_version`. 11 | 12 | ------------- 13 | Prerequisites 14 | ------------- 15 | 16 | * All CI tests are passing on `GitHub Actions`_. 17 | 18 | * You have a `GPG signing key `_. 19 | 20 | 21 | --------------------- 22 | `PyPI`_: Step-by-step 23 | --------------------- 24 | 25 | 1. Make sure that all CI tests are passing on `GitHub Actions`_. 26 | 27 | 28 | 2. Download the latest sources if you don't already have them 29 | 30 | .. code:: console 31 | 32 | $ git clone git@github.com:scikit-build/cmake-python-distributions 33 | $ cd cmake-python-distributions 34 | 35 | 36 | 3. Ask nox for the instructions on what to type 37 | 38 | .. code:: console 39 | 40 | $ nox -s tag_release 41 | 42 | 43 | 4. Run the suggested lines, probably something like this: 44 | 45 | .. code:: console 46 | 47 | $ git tag --sign -m 'cmake-python-distributions 4.0.2' 4.0.2 main 48 | $ git push origin 4.0.2 49 | 50 | .. warning:: 51 | 52 | We recommend using a `GPG signing key `_ 53 | to sign the tag. 54 | 55 | 56 | 5. Check the status of the builds on `GitHub Actions`_. 57 | 58 | 6. Once the builds are completed, check that the distributions are available on `PyPI`_. 59 | 60 | 7. Make a GitHub release based on the tag. This will display the latest version 61 | in the GitHub sidebar, and will notify release watchers of the release. 62 | Title it `Version X.Y.Z` and add a little note about what changed (Python only). 63 | 64 | 65 | .. _GitHub Actions: https://github.com/scikit-build/cmake-python-distributions/actions/workflows/build.yml 66 | 67 | .. _PyPI: https://pypi.org/project/cmake 68 | -------------------------------------------------------------------------------- /docs/update_cmake_version.rst: -------------------------------------------------------------------------------- 1 | .. _updating_cmake_version: 2 | 3 | ========================== 4 | Updating the CMake version 5 | ========================== 6 | 7 | A developer should use the following steps to update the version ``X.Y.Z`` 8 | of CMake associated with the current CMake python distributions. 9 | 10 | Available CMake archives can be found at https://cmake.org/files. 11 | 12 | Nox procedure 13 | ------------- 14 | 15 | If using nox, run:: 16 | 17 | nox -s bump -- 18 | 19 | 20 | And follow the instructions it gives you. Leave off the version to bump to the latest version. Add `--commit` to run the commit procedure. 21 | 22 | Classic procedure: 23 | ------------------ 24 | 25 | 1. Install `requests`:: 26 | 27 | $ pip install requests 28 | 29 | 2. Execute `scripts/update_cmake_version.py` command line tool with the desired 30 | ``X.Y.Z`` CMake version available for download. For example:: 31 | 32 | $ release=4.0.2 33 | $ ./scripts/update_cmake_version.py $release 34 | Collecting URLs and SHA256s from 'https://api.github.com/repos/Kitware/CMake/releases/tags/v4.0.2' 35 | [...] 36 | Collecting URLs and SHA256s from 'https://api.github.com/repos/Kitware/CMake/releases/tags/v4.0.2' - done 37 | Updating 'CMakeUrls.cmake' with CMake version 4.0.2 38 | Updating 'CMakeUrls.cmake' with CMake version 4.0.2 - done 39 | Updating docs/index.rst 40 | Updating docs/index.rst - done 41 | Updating README.rst 42 | Updating README.rst - done 43 | Updating tests/test_cmake.py 44 | Updating tests/test_cmake.py - done 45 | 46 | 3. Create a topic named `update-to-cmake-X.Y.Z` and commit the changes. 47 | For example:: 48 | 49 | release=4.0.2 50 | git switch -c update-to-cmake-$release 51 | git add -u CMakeUrls.cmake docs/index.rst README.rst tests/test_cmake.py docs/update_cmake_version.rst 52 | git commit -m "Update to CMake $release" 53 | 54 | 4. Push the topic and create a `Pull Request`. 55 | 56 | 5. If all CI tests are passing, merge the topic and consider :doc:`making a new 57 | release `. 58 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | ===== 2 | Usage 3 | ===== 4 | 5 | After :doc:`installing ` the package using `pip`, the executables 6 | ``cmake``, ``cpack`` and ``ctest`` will be available in the ``PATH`` and can be 7 | used to configure and build any projects. 8 | -------------------------------------------------------------------------------- /noxfile.py: -------------------------------------------------------------------------------- 1 | # /// script 2 | # dependencies = ["nox>=2025.2.9"] 3 | # /// 4 | 5 | import argparse 6 | import re 7 | from pathlib import Path 8 | 9 | import nox 10 | 11 | nox.needs_version = ">=2025.2.9" 12 | nox.options.default_venv_backend = "uv|virtualenv" 13 | 14 | BUILD_ENV = { 15 | "MACOSX_DEPLOYMENT_TARGET": "10.10", 16 | "ARCHFLAGS": "-arch x86_64 -arch arm64", 17 | } 18 | 19 | wheel = "" 20 | 21 | 22 | @nox.session 23 | def build(session: nox.Session) -> str: 24 | """ 25 | Make an SDist and a wheel. 26 | """ 27 | session.log( 28 | "The files produced locally by this job are not intended to be redistributable" 29 | ) 30 | extra = ["--installer=uv"] if session.venv_backend == "uv" else [] 31 | session.install("build") 32 | tmpdir = session.create_tmp() 33 | session.run("python", "-m", "build", "--outdir", tmpdir, *extra, env=BUILD_ENV) 34 | (wheel_path,) = Path(tmpdir).glob("*.whl") 35 | (sdist_path,) = Path(tmpdir).glob("*.tar.gz") 36 | Path("dist").mkdir(exist_ok=True) 37 | wheel_path.rename(f"dist/{wheel_path.name}") 38 | sdist_path.rename(f"dist/{sdist_path.name}") 39 | 40 | global wheel 41 | wheel = f"dist/{wheel_path.name}" 42 | 43 | 44 | @nox.session 45 | def lint(session: nox.Session) -> str: 46 | """ 47 | Run linters on the codebase. 48 | """ 49 | session.install("pre-commit") 50 | session.run("pre-commit", "run", "-a") 51 | 52 | 53 | @nox.session(requires=["build"]) 54 | def tests(session: nox.Session) -> str: 55 | """ 56 | Run the tests. 57 | """ 58 | pyproject = nox.project.load_toml("pyproject.toml") 59 | deps = nox.project.dependency_groups(pyproject, "test") 60 | session.install(wheel, *deps) 61 | session.run("pytest", *session.posargs) 62 | 63 | 64 | @nox.session(reuse_venv=True, default=False) 65 | def docs(session: nox.Session) -> None: 66 | """ 67 | Build the docs. Pass "--non-interactive" to avoid serve. Pass "-- -b linkcheck" to check links. 68 | """ 69 | pyproject = nox.project.load_toml("pyproject.toml") 70 | deps = nox.project.dependency_groups(pyproject, "docs") 71 | 72 | parser = argparse.ArgumentParser() 73 | parser.add_argument( 74 | "-b", dest="builder", default="html", help="Build target (default: html)" 75 | ) 76 | args, posargs = parser.parse_known_args(session.posargs) 77 | serve = args.builder == "html" and session.interactive 78 | 79 | extra_installs = ["sphinx-autobuild"] if serve else [] 80 | session.install(*deps, *extra_installs) 81 | session.chdir("docs") 82 | 83 | if args.builder == "linkcheck": 84 | session.run( 85 | "sphinx-build", "-b", "linkcheck", ".", "_build/linkcheck", *posargs 86 | ) 87 | return 88 | 89 | shared_args = ( 90 | "-n", # nitpicky mode 91 | "-T", # full tracebacks 92 | f"-b={args.builder}", 93 | ".", 94 | f"_build/{args.builder}", 95 | *posargs, 96 | ) 97 | 98 | if serve: 99 | session.run( 100 | "sphinx-autobuild", "--open-browser", "--ignore=.build", *shared_args 101 | ) 102 | else: 103 | session.run("sphinx-build", "--keep-going", *shared_args) 104 | 105 | def _bump(session: nox.Session, name: str, repository: str, branch: str, script: str, files) -> None: 106 | parser = argparse.ArgumentParser() 107 | parser.add_argument( 108 | "--commit", action="store_true", help="Make a branch and commit." 109 | ) 110 | parser.add_argument( 111 | "version", nargs="?", help="The version to process - leave off for latest." 112 | ) 113 | args = parser.parse_args(session.posargs) 114 | 115 | if args.version is None: 116 | session.install("lastversion", "requests") 117 | lastversion_args = [] 118 | if branch: 119 | lastversion_args.extend(("--branch", branch)) 120 | lastversion_args.append(repository) 121 | version = session.run("lastversion", *lastversion_args, log=False, silent=True).strip() 122 | else: 123 | session.install("requests") 124 | version = args.version 125 | 126 | extra = ["--quiet"] if args.commit else [] 127 | session.run("python", script, version, *extra) 128 | 129 | if args.commit: 130 | session.run("git", "switch", "-c", f"update-to-{name.lower()}-{version}", external=True) 131 | session.run("git", "add", "-u", *files, external=True) 132 | session.run("git", "commit", "-m", f"Update to {name} {version}", external=True) 133 | session.log( 134 | f'Complete! Now run: gh pr create --fill --body "Created by running `nox -s {session.name} -- --commit`"' 135 | ) 136 | 137 | 138 | @nox.session(default=False) 139 | def bump(session: nox.Session) -> None: 140 | """ 141 | Set to a new version, use -- , otherwise will use the latest version. 142 | """ 143 | files = ( 144 | "pyproject.toml", 145 | "CMakeUrls.cmake", 146 | "docs/index.rst", 147 | "README.rst", 148 | "tests/test_cmake.py", 149 | "docs/update_cmake_version.rst", 150 | ) 151 | _bump(session, "CMake", "kitware/cmake", "", "scripts/update_cmake_version.py", files) 152 | 153 | 154 | @nox.session(name="bump-openssl", default=False) 155 | def bump_openssl(session: nox.Session) -> None: 156 | """ 157 | Set openssl to a new version, use -- , otherwise will use the latest version. 158 | """ 159 | files = ( 160 | "scripts/manylinux-build-and-install-openssl.sh", 161 | ) 162 | _bump(session, "OpenSSL", "openssl/openssl", "3.0", "scripts/update_openssl_version.py", files) 163 | 164 | 165 | def _get_version() -> str: 166 | txt = Path("pyproject.toml").read_text() 167 | return next(iter(re.finditer(r'^version = "([\d\.]+)"$', txt, flags=re.MULTILINE))).group(1) 168 | 169 | 170 | @nox.session(venv_backend="none", default=False) 171 | def tag_release(session: nox.Session) -> None: 172 | """ 173 | Print instructions for tagging a release and pushing it to GitHub. 174 | """ 175 | 176 | session.log("Run the following commands to make a release:") 177 | current_version = _get_version() 178 | print(f"git tag --sign -m 'cmake-python-distributions {current_version}' {current_version} main") 179 | print(f"git push origin {current_version}") 180 | 181 | 182 | @nox.session(venv_backend="none", default=False) 183 | def cmake_version(session: nox.Session) -> None: # noqa: ARG001 184 | """ 185 | Print upstream cmake version. 186 | """ 187 | 188 | current_version = _get_version() 189 | print(".".join(current_version.split(".")[:3])) 190 | 191 | 192 | @nox.session(venv_backend="none", default=False) 193 | def openssl_version(session: nox.Session) -> None: # noqa: ARG001 194 | """ 195 | Print upstream OpenSSL version. 196 | """ 197 | txt = Path("scripts/manylinux-build-and-install-openssl.sh").read_text() 198 | current_version = next(iter(re.finditer(r'^OPENSSL_ROOT=openssl-([\d\.]+)$', txt, flags=re.MULTILINE))).group(1) 199 | print(".".join(current_version.split(".")[:3])) 200 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["scikit-build-core>=0.10"] 3 | build-backend = "backend" 4 | backend-path = ["_build_backend"] 5 | 6 | [project] 7 | name = "cmake" 8 | version = "4.0.2" 9 | description = "CMake is an open-source, cross-platform family of tools designed to build, test and package software" 10 | keywords = ["CMake", "build", "c++", "fortran", "cross-platform", "cross-compilation"] 11 | readme = "README.rst" 12 | license = {text = "Apache 2.0"} 13 | authors = [ 14 | {name = "Jean-Christophe Fillion-Robin", email = "jchris.fillionr@kitware.com"}, 15 | ] 16 | classifiers = [ 17 | "License :: OSI Approved :: Apache Software License", 18 | "License :: OSI Approved :: BSD License", 19 | "Programming Language :: C", 20 | "Programming Language :: C++", 21 | "Programming Language :: Fortran", 22 | "Programming Language :: Python", 23 | "Operating System :: OS Independent", 24 | "Development Status :: 5 - Production/Stable", 25 | "Intended Audience :: Developers", 26 | "Topic :: Software Development :: Build Tools", 27 | "Typing :: Typed" 28 | ] 29 | dependencies = [ 30 | "importlib_metadata>=1.4; python_version<'3.8'", 31 | ] 32 | requires-python = ">=3.7" 33 | 34 | [project.urls] 35 | Homepage = "https://cmake.org" 36 | Documentation = "https://cmake-python-distributions.readthedocs.io" 37 | Source = "https://github.com/scikit-build/cmake-python-distributions" 38 | "Mailing list" = "https://groups.google.com/forum/#!forum/scikit-build" 39 | "Bug Tracker" = "https://github.com/scikit-build/cmake-python-distributions/issues" 40 | 41 | [project.scripts] 42 | ccmake = "cmake:ccmake" 43 | cmake = "cmake:cmake" 44 | cpack = "cmake:cpack" 45 | ctest = "cmake:ctest" 46 | 47 | 48 | [dependency-groups] 49 | test = [ 50 | "coverage>=4.2", 51 | "pytest>=6", 52 | "pytest-cov>=2.4.0", 53 | ] 54 | docs = [ 55 | "docutils", 56 | "funcparserlib>=1.0.0", 57 | "furo", 58 | "pygments", 59 | "sphinx", 60 | "sphinxcontrib-mermaid", 61 | "tomli; python_version<'3.11'", 62 | ] 63 | dev = [{ include-group="test" }] 64 | 65 | 66 | [tool.scikit-build] 67 | minimum-version = "build-system.requires" 68 | build-dir = "build/{wheel_tag}" 69 | cmake.version = "CMakeLists.txt" 70 | ninja.make-fallback = true 71 | wheel.py-api = "py3" 72 | wheel.expand-macos-universal-tags = true 73 | wheel.install-dir = "cmake/data" 74 | 75 | [[tool.scikit-build.generate]] 76 | path = "cmake/_version.py" 77 | template = ''' 78 | version = "${version}" 79 | ''' 80 | 81 | [[tool.scikit-build.overrides]] 82 | if.env.CMAKE_PYTHON_DIST_FORCE_NINJA_DEP = true 83 | ninja.make-fallback = false 84 | 85 | [[tool.scikit-build.overrides]] 86 | if.state = "metadata_wheel" 87 | wheel.cmake = false 88 | wheel.platlib = true 89 | 90 | 91 | [tool.pytest.ini_options] 92 | minversion = "6.0" 93 | testpaths = ["tests"] 94 | 95 | 96 | [tool.cibuildwheel] 97 | build = "cp39-*" 98 | test-groups = ["test"] 99 | test-command = "pytest {project}/tests" 100 | build-verbosity = 1 101 | build-frontend = "build[uv]" 102 | config-settings."cmake.define.RUN_CMAKE_TEST" = "ON" 103 | environment = { CMAKE_PYTHON_DIST_FORCE_NINJA_DEP = "1" } 104 | musllinux-x86_64-image = "musllinux_1_1" 105 | musllinux-i686-image = "musllinux_1_1" 106 | musllinux-aarch64-image = "musllinux_1_1" 107 | musllinux-ppc64le-image = "musllinux_1_1" 108 | musllinux-s390x-image = "musllinux_1_1" 109 | musllinux-armv7l-image = "musllinux_1_2" 110 | 111 | [[tool.cibuildwheel.overrides]] 112 | select = "*-macos*" 113 | inherit.environment = "append" 114 | environment = { MACOSX_DEPLOYMENT_TARGET = "10.10" } 115 | 116 | [[tool.cibuildwheel.overrides]] 117 | select = "*-*linux*" 118 | before-all = "./scripts/manylinux-build-and-install-openssl.sh" 119 | inherit.environment = "prepend" 120 | environment = { PKG_CONFIG_PATH = "/usr/local/ssl/lib/pkgconfig" } 121 | inherit.config-settings = "prepend" 122 | config-settings."cmake.define.OPENSSL_ROOT_DIR" = "/usr/local/ssl" 123 | config-settings."cmake.define.OPENSSL_USE_STATIC_LIBS" = "ON" 124 | config-settings."cmake.define.CMAKE_JOB_POOL_COMPILE" = "compile" 125 | config-settings."cmake.define.CMAKE_JOB_POOL_LINK" = "link" 126 | config-settings."cmake.define.CMAKE_JOB_POOLS" = "compile=4;link=1" 127 | 128 | [[tool.cibuildwheel.overrides]] 129 | select = ["*-musllinux_*"] 130 | inherit.config-settings = "append" 131 | # disable some tests 132 | # - BootstrapTest fails with custom OpenSSL and probably does not make much sense for this project 133 | # - ExportImport|RunCMake.install|RunCMake.file-GET_RUNTIME_DEPENDENCIES: c.f. https://discourse.cmake.org/t/cmake-test-suite-failing-on-alpine-linux/5064 134 | config-settings."cmake.define.RUN_CMAKE_TEST_EXCLUDE" = "BootstrapTest|ExportImport|RunCMake.install|RunCMake.RuntimePath|RunCMake.file-GET_RUNTIME_DEPENDENCIES" 135 | 136 | [[tool.cibuildwheel.overrides]] 137 | select = ["*-musllinux_armv7l"] 138 | inherit.config-settings = "append" 139 | # disable some tests 140 | # - BootstrapTest fails with custom OpenSSL and probably does not make much sense for this project 141 | # - ExportImport|RunCMake.install|RunCMake.file-GET_RUNTIME_DEPENDENCIES: c.f. https://discourse.cmake.org/t/cmake-test-suite-failing-on-alpine-linux/5064 142 | # - CTestTestFdSetSize fails on gcc14+ with "error: implicit declaration of function 'usleep'"" 143 | config-settings."cmake.define.RUN_CMAKE_TEST_EXCLUDE" = "BootstrapTest|CTestTestFdSetSize|ExportImport|RunCMake.install|RunCMake.RuntimePath|RunCMake.file-GET_RUNTIME_DEPENDENCIES" 144 | 145 | [[tool.cibuildwheel.overrides]] 146 | select = ["*linux_ppc64le", "*linux_s390x"] 147 | inherit.config-settings = "append" 148 | # disable tests on those platforms, QEMU is taking to long for jobs to pass on GHA 149 | config-settings."cmake.define.RUN_CMAKE_TEST" = "OFF" 150 | 151 | [[tool.cibuildwheel.overrides]] 152 | select = ["*-musllinux_s390x"] 153 | build-frontend = "pip" 154 | 155 | 156 | [tool.mypy] 157 | files = ["src", "scripts"] 158 | python_version = "3.8" 159 | strict = true 160 | enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] 161 | warn_unreachable = true 162 | 163 | 164 | [tool.ruff.lint] 165 | extend-select = [ 166 | "ARG", # flake8-unused-arguments 167 | "B", # flake8-bugbear 168 | "C4", # flake8-comprehensions 169 | "EXE", # flake8-executable 170 | "G", # flake8-logging-format 171 | "I", # isort 172 | "ICN", # flake8-import-conventions 173 | "ISC", # flake8-implicit-str-concat 174 | "PGH", # pygrep-hooks 175 | "PIE", # flake8-pie 176 | "PL", # pylint 177 | "PT", # flake8-pytest-style 178 | "RET", # flake8-return 179 | "RUF", # Ruff-specific 180 | "SIM", # flake8-simplify 181 | "UP", # pyupgrade 182 | ] 183 | ignore = [ 184 | "PLR09", # Too many X 185 | ] 186 | exclude = ["src/cmake/_version.py"] 187 | flake8-unused-arguments.ignore-variadic-names = true 188 | 189 | [tool.ruff.lint.per-file-ignores] 190 | "docs/conf.py" = ["E402"] 191 | "*.pyi" = ["ARG001"] 192 | "noxfile.py" = ["PLW0603"] 193 | -------------------------------------------------------------------------------- /scripts/manylinux-build-and-install-openssl.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Configure, build and install OpenSSL to support building of CMake using manylinux docker images 5 | # 6 | 7 | set -eux 8 | set -o pipefail 9 | 10 | MY_DIR=$(dirname "${BASH_SOURCE[0]}") 11 | source $MY_DIR/utils.sh 12 | 13 | OPENSSL_ROOT=openssl-3.0.16 14 | OPENSSL_HASH=57e03c50feab5d31b152af2b764f10379aecd8ee92f16c985983ce4a99f7ef86 15 | 16 | cd /tmp 17 | 18 | if ! perl -e 'use 5.10.0' &> /dev/null; then 19 | # perl>=5.10.0 is needed to build openssl 20 | PERL_ROOT=perl-5.32.1 21 | # Hash from https://www.cpan.org/src/5.0/perl-5.32.1.tar.gz.sha256.txt 22 | PERL_HASH=03b693901cd8ae807231b1787798cf1f2e0b8a56218d07b7da44f784a7caeb2c 23 | 24 | curl -fsSLO https://www.cpan.org/src/5.0/${PERL_ROOT}.tar.gz 25 | check_sha256sum ${PERL_ROOT}.tar.gz ${PERL_HASH} 26 | tar -xzf ${PERL_ROOT}.tar.gz 27 | rm -rf ${PERL_ROOT}.tar.gz 28 | 29 | pushd ${PERL_ROOT} 30 | ./Configure -des -Dprefix=/tmp/perl-openssl > /dev/null 31 | make -j$(nproc) > /dev/null 32 | make install > /dev/null 33 | popd 34 | export PATH=/tmp/perl-openssl/bin:${PATH} 35 | else 36 | if [ "${AUDITWHEEL_PLAT:0:9}" == "manylinux" ] && command -v yum >/dev/null 2>&1; then 37 | # more perl modules are needed than the bare minimum already installed in CentOS 38 | # c.f. https://github.com/openssl/openssl/blob/openssl-3.0.0/NOTES-PERL.md#general-notes 39 | yum -y install perl-core 40 | fi 41 | fi 42 | 43 | # Download 44 | curl -fsSLO https://github.com/openssl/openssl/releases/download/${OPENSSL_ROOT}/${OPENSSL_ROOT}.tar.gz 45 | check_sha256sum ${OPENSSL_ROOT}.tar.gz ${OPENSSL_HASH} 46 | tar -xzf ${OPENSSL_ROOT}.tar.gz 47 | rm -rf ${OPENSSL_ROOT}.tar.gz 48 | 49 | # Configure 50 | pushd ${OPENSSL_ROOT} 51 | ./config no-shared no-tests -fPIC --prefix=/usr/local/ssl --openssldir=/usr/local/ssl > /dev/null 52 | 53 | # Build 54 | make -j$(nproc) > /dev/null 55 | 56 | # Install 57 | make install_sw > /dev/null 58 | 59 | popd 60 | rm -rf ${OPENSSL_ROOT} 61 | -------------------------------------------------------------------------------- /scripts/update_cmake_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # /// script 4 | # dependencies = ["requests"] 5 | # /// 6 | 7 | """ 8 | Command line executable allowing to update CMakeUrls.cmake given a CMake 9 | version. 10 | """ 11 | 12 | from __future__ import annotations 13 | 14 | import argparse 15 | import contextlib 16 | import re 17 | import textwrap 18 | from pathlib import Path 19 | 20 | import requests 21 | 22 | TYPE_CHECKING = False 23 | 24 | if TYPE_CHECKING: 25 | from collections.abc import Generator, Mapping 26 | 27 | 28 | ROOT_DIR = Path(__file__).parent.parent.resolve() 29 | 30 | 31 | @contextlib.contextmanager 32 | def _log(txt: str, verbose: bool = True) -> Generator[None, None, None]: 33 | if verbose: 34 | print(txt) 35 | yield 36 | if verbose: 37 | print(f"{txt} - done") 38 | 39 | 40 | def _major_minor(version: str) -> str: 41 | """Given a string of the form ``X.Y.Z``, returns ``X.Y``.""" 42 | return ".".join(version.split(".")[:2]) 43 | 44 | 45 | def get_cmake_archive_urls_and_sha256s(version: str, verbose: bool=False) -> dict[str,tuple[str, str]]: 46 | files_base_url = ( 47 | f"https://api.github.com/repos/Kitware/CMake/releases/tags/v{version}" 48 | ) 49 | 50 | with _log(f"Collecting URLs and SHA256s from '{files_base_url}'"): 51 | 52 | assets = requests.get(files_base_url).json()["assets"] 53 | 54 | sha_256_file = f"cmake-{version}-SHA-256.txt" 55 | 56 | expected_files = { 57 | f"cmake-{version}.tar.gz": "unix_source", 58 | f"cmake-{version}.zip": "win_source", 59 | f"cmake-{version}-linux-x86_64.tar.gz": "linux64_binary", 60 | f"cmake-{version}-macos10.10-universal.tar.gz": "macos10_10_binary", 61 | f"cmake-{version}-windows-i386.zip": "win32_binary", 62 | f"cmake-{version}-windows-x86_64.zip": "win64_binary", 63 | f"cmake-{version}-windows-arm64.zip": "winarm64_binary", 64 | } 65 | 66 | # Get SHA256s for each asset 67 | shas = {} 68 | for asset in assets: 69 | if asset["name"] == sha_256_file: 70 | sha_256_url = asset["browser_download_url"] 71 | for line in requests.get(sha_256_url).text.splitlines(): 72 | file = line.split()[1].strip() 73 | if file in expected_files: 74 | sha256 = line.split()[0].strip() 75 | identifier = expected_files[file] 76 | shas[identifier] = sha256 77 | assert len(shas) == len(expected_files), f"{len(shas)} != {len(expected_files)}" 78 | 79 | # Get download URLs for each asset 80 | urls = {} 81 | for asset in assets: 82 | if asset["name"] in expected_files: 83 | identifier = expected_files[asset["name"]] 84 | urls[identifier] = asset["browser_download_url"] 85 | if len(urls) != len(expected_files): 86 | expected_files_by_identifier = { 87 | value: key for key, value in expected_files.items() 88 | } 89 | missing_files = [] 90 | for identifier in set(expected_files.values()) - set(urls.keys()): 91 | missing_files.append(expected_files_by_identifier[identifier]) 92 | raise RuntimeError( 93 | f"Couldn't find {missing_files} at {files_base_url}" 94 | ) 95 | 96 | # combine the URLs and SHA256s into a single dictionary 97 | zipped = {} 98 | for value in expected_files.values(): 99 | print(f"[{value}]\n{urls[value]}\n{shas[value]}\n") 100 | zipped[value] = (urls[value], shas[value]) 101 | assert len(zipped) == len(expected_files) 102 | 103 | if verbose: 104 | for identifier, (url, sha256) in zipped.items(): 105 | print(f"[{identifier}]\n{url}\n{sha256}\n") 106 | 107 | return zipped 108 | 109 | 110 | def generate_cmake_variables(urls_and_sha256s: Mapping[str, tuple[str, str]]) -> str: 111 | template_inputs = {} 112 | 113 | # Get SHA256s and URLs 114 | for var_prefix, urls_and_sha256s_values in urls_and_sha256s.items(): 115 | template_inputs[f"{var_prefix}_url"] = urls_and_sha256s_values[0] 116 | template_inputs[f"{var_prefix}_sha256"] = urls_and_sha256s_values[1] 117 | 118 | return textwrap.dedent( 119 | """ 120 | #----------------------------------------------------------------------------- 121 | # CMake sources 122 | set(unix_source_url "{unix_source_url}") 123 | set(unix_source_sha256 "{unix_source_sha256}") 124 | 125 | set(windows_source_url "{win_source_url}") 126 | set(windows_source_sha256 "{win_source_sha256}") 127 | 128 | #----------------------------------------------------------------------------- 129 | # CMake binaries 130 | 131 | set(linux32_binary_url "NA") # Linux 32-bit binaries not available 132 | set(linux32_binary_sha256 "NA") 133 | 134 | set(linux64_binary_url "{linux64_binary_url}") 135 | set(linux64_binary_sha256 "{linux64_binary_sha256}") 136 | 137 | set(macos10_10_binary_url "{macos10_10_binary_url}") 138 | set(macos10_10_binary_sha256 "{macos10_10_binary_sha256}") 139 | 140 | set(win32_binary_url "{win32_binary_url}") 141 | set(win32_binary_sha256 "{win32_binary_sha256}") 142 | 143 | set(win64_binary_url "{win64_binary_url}") 144 | set(win64_binary_sha256 "{win64_binary_sha256}") 145 | 146 | set(winarm64_binary_url "{winarm64_binary_url}") 147 | set(winarm64_binary_sha256 "{winarm64_binary_sha256}") 148 | """ 149 | ).format(**template_inputs) 150 | 151 | 152 | def update_cmake_urls_script(version: str) -> set[str]: 153 | content = generate_cmake_variables(get_cmake_archive_urls_and_sha256s(version)) 154 | cmake_urls_filename = "CMakeUrls.cmake" 155 | cmake_urls_filepath = ROOT_DIR / cmake_urls_filename 156 | 157 | msg = f"Updating '{cmake_urls_filename}' with CMake version {version}" 158 | with _log(msg), cmake_urls_filepath.open("w") as cmake_file: 159 | cmake_file.write(content) 160 | 161 | return {cmake_urls_filename} 162 | 163 | 164 | def _update_file(filepath: Path, regex: re.Pattern[str], replacement: str) -> None: 165 | with _log(f"Updating {filepath.relative_to(ROOT_DIR)}"): 166 | pattern = re.compile(regex) 167 | with filepath.open() as doc_file: 168 | updated_content = [pattern.sub(replacement, line) for line in doc_file] 169 | with filepath.open("w") as doc_file: 170 | doc_file.writelines(updated_content) 171 | 172 | 173 | def update_docs(version: str) -> set[str]: 174 | pattern = re.compile( 175 | r"CMake \d.(\d)+.\d " 176 | ) 177 | replacement = f"CMake {version} " 178 | files = {"docs/index.rst", "README.rst"} 179 | for filename in files: 180 | _update_file(ROOT_DIR / filename, pattern, replacement) 181 | return files 182 | 183 | 184 | def update_tests(version: str) -> set[str]: 185 | pattern = re.compile(r'expected_version = "\d.\d+.\d"') 186 | replacement = f'expected_version = "{version}"' 187 | filename = "tests/test_cmake.py" 188 | _update_file( 189 | ROOT_DIR / filename, pattern, replacement 190 | ) 191 | return {filename} 192 | 193 | 194 | def update_pyproject_toml(version: str) -> set[str]: 195 | pattern = re.compile(r'^version = "[\w\.]+"$') 196 | replacement = f'version = "{version}"' 197 | filename = "pyproject.toml" 198 | _update_file( 199 | ROOT_DIR / filename, pattern, replacement 200 | ) 201 | return {filename} 202 | 203 | 204 | def update_raw_versions(version: str, filename: str) -> set[str]: 205 | pattern = re.compile(r"\d\.\d+\.\d") 206 | replacement = version 207 | _update_file( 208 | ROOT_DIR / filename, pattern, replacement 209 | ) 210 | return {filename} 211 | 212 | 213 | def main() -> None: 214 | parser = argparse.ArgumentParser(description=__doc__) 215 | parser.add_argument( 216 | "cmake_version", 217 | metavar="CMAKE_VERSION", 218 | type=str, 219 | help="CMake version of the form X.Y.Z", 220 | ) 221 | parser.add_argument( 222 | "--collect-only", 223 | action="store_true", 224 | help="If specified, only display the archive URLs and associated hashsums", 225 | ) 226 | parser.add_argument( 227 | "--quiet", 228 | action="store_true", 229 | help="Hide the output", 230 | ) 231 | args = parser.parse_args() 232 | 233 | if args.collect_only: 234 | get_cmake_archive_urls_and_sha256s(args.cmake_version, verbose=True) 235 | return 236 | 237 | filenames = set() 238 | filenames |= update_cmake_urls_script(args.cmake_version) 239 | filenames |= update_docs(args.cmake_version) 240 | filenames |= update_tests(args.cmake_version) 241 | filenames |= update_raw_versions(args.cmake_version, "docs/update_cmake_version.rst") 242 | filenames |= update_raw_versions(args.cmake_version, "docs/make_a_release.rst") 243 | filenames |= update_pyproject_toml(args.cmake_version) 244 | 245 | if args.quiet: 246 | return 247 | 248 | msg = f"""\ 249 | Complete! Now run: 250 | 251 | git switch -c update-to-cmake-{args.cmake_version} 252 | git add -u {' '.join(filenames)} 253 | git commit -m "Update to CMake {args.cmake_version}" 254 | gh pr create --fill --body "Created by update_cmake_version.py" 255 | """ 256 | print(textwrap.dedent(msg)) 257 | 258 | 259 | if __name__ == "__main__": 260 | main() 261 | -------------------------------------------------------------------------------- /scripts/update_openssl_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # /// script 4 | # dependencies = ["requests"] 5 | # /// 6 | 7 | """ 8 | Command line executable allowing to update OpenSSL version. 9 | """ 10 | 11 | from __future__ import annotations 12 | 13 | import argparse 14 | import contextlib 15 | import re 16 | import textwrap 17 | from pathlib import Path 18 | 19 | import requests 20 | 21 | TYPE_CHECKING = False 22 | 23 | if TYPE_CHECKING: 24 | from collections.abc import Generator 25 | 26 | ROOT_DIR = Path(__file__).parent.parent.resolve() 27 | 28 | 29 | @contextlib.contextmanager 30 | def _log(txt: str, verbose: bool=True) -> Generator[None, None, None]: 31 | if verbose: 32 | print(txt) 33 | yield 34 | if verbose: 35 | print(txt, "-", "done") 36 | 37 | 38 | def get_openssl_sha256(version: str, verbose: bool=False) -> str: 39 | files_base_url = ( 40 | f"https://github.com/openssl/openssl/releases/download/openssl-{version}/openssl-{version}.tar.gz.sha256" 41 | ) 42 | with _log(f"Collecting SHA256 from '{files_base_url}'"): 43 | parts = requests.get(files_base_url).content.decode("ascii").strip().split() 44 | sha256 = parts[0] 45 | if len(parts) > 1: 46 | expected_parts = 2 # f"{sha256} {filename}"" 47 | assert len(parts) == expected_parts 48 | assert parts[1] == f"openssl-{version}.tar.gz" 49 | if verbose: 50 | print("got sha256:", sha256) 51 | return sha256 52 | 53 | 54 | def _update_file(filepath: Path, regex: re.Pattern[str], replacement: str) -> None: 55 | with _log(f"Updating {filepath.relative_to(ROOT_DIR)}"): 56 | pattern = re.compile(regex) 57 | with filepath.open() as doc_file: 58 | updated_content = [pattern.sub(replacement, line) for line in doc_file] 59 | with filepath.open("w") as doc_file: 60 | doc_file.writelines(updated_content) 61 | 62 | 63 | def update_openssl_script(version: str, sha256: str) -> None: 64 | pattern = re.compile(r"^OPENSSL_ROOT=.*") 65 | replacement = f"OPENSSL_ROOT=openssl-{version}" 66 | _update_file( 67 | ROOT_DIR / "scripts/manylinux-build-and-install-openssl.sh", pattern, replacement 68 | ) 69 | pattern = re.compile(r"^OPENSSL_HASH=.*") 70 | replacement = f"OPENSSL_HASH={sha256}" 71 | _update_file( 72 | ROOT_DIR / "scripts/manylinux-build-and-install-openssl.sh", pattern, replacement 73 | ) 74 | 75 | 76 | def main() -> None: 77 | parser = argparse.ArgumentParser(description=__doc__) 78 | parser.add_argument( 79 | "openssl_version", 80 | metavar="OPENSSL_VERSION", 81 | type=str, 82 | help="OpenSSL version", 83 | ) 84 | parser.add_argument( 85 | "--collect-only", 86 | action="store_true", 87 | help="If specified, only display the hashsum for the requested version", 88 | ) 89 | parser.add_argument( 90 | "--quiet", 91 | action="store_true", 92 | help="Hide the output", 93 | ) 94 | args = parser.parse_args() 95 | 96 | sha256 = get_openssl_sha256(args.openssl_version, verbose=args.collect_only) 97 | if args.collect_only: 98 | return 99 | 100 | update_openssl_script(args.openssl_version, sha256) 101 | 102 | if not args.quiet: 103 | msg = """\ 104 | Complete! Now run: 105 | 106 | git switch -c update-to-openssl-{release} 107 | git add -u scripts/manylinux-build-and-install-openssl.sh 108 | git commit -m "Update to OpenSSL {release}" 109 | gh pr create --fill --body "Created by update_openssl_version.py" 110 | """ 111 | print(textwrap.dedent(msg.format(release=args.openssl_version))) 112 | 113 | 114 | if __name__ == "__main__": 115 | main() 116 | -------------------------------------------------------------------------------- /scripts/utils.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | # Copied from https://github.com/pypa/manylinux/blob/main/docker/build_scripts/build_utils.sh 6 | function check_var { 7 | if [ -z "$1" ]; then 8 | echo "required variable not defined" 9 | exit 1 10 | fi 11 | } 12 | 13 | # Copied from https://github.com/pypa/manylinux/blob/main/docker/build_scripts/build_utils.sh 14 | function check_sha256sum { 15 | local fname=$1 16 | check_var ${fname} 17 | local sha256=$2 18 | check_var ${sha256} 19 | 20 | echo "${sha256} ${fname}" > ${fname}.sha256 21 | sha256sum -c ${fname}.sha256 22 | rm -f ${fname}.sha256 23 | } 24 | -------------------------------------------------------------------------------- /src/cmake/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | import subprocess 5 | import sys 6 | from pathlib import Path 7 | 8 | if sys.version_info < (3, 8): 9 | from importlib_metadata import distribution 10 | else: 11 | from importlib.metadata import distribution 12 | 13 | from ._version import version as __version__ 14 | 15 | TYPE_CHECKING = False 16 | 17 | if TYPE_CHECKING: 18 | from typing import Iterable, NoReturn 19 | 20 | 21 | __all__ = ["CMAKE_BIN_DIR", "CMAKE_DATA", "CMAKE_DOC_DIR", "CMAKE_SHARE_DIR", "__version__", "cmake", "cpack", "ctest"] 22 | 23 | 24 | def __dir__() -> list[str]: 25 | return __all__ 26 | 27 | 28 | cmake_executable_path = None 29 | cmake_files = distribution("cmake").files 30 | assert cmake_files is not None, "This is the cmake package so it must be installed and have files" 31 | for script in cmake_files: 32 | if str(script).startswith("cmake/data/bin/cmake"): 33 | resolved_script = Path(script.locate()).resolve(strict=True) 34 | cmake_executable_path = resolved_script.parents[1] 35 | break 36 | CMAKE_DATA = str(cmake_executable_path) if cmake_executable_path else None 37 | 38 | assert CMAKE_DATA is not None 39 | assert os.path.exists(CMAKE_DATA) 40 | 41 | CMAKE_BIN_DIR = os.path.join(CMAKE_DATA, 'bin') 42 | CMAKE_DOC_DIR = os.path.join(CMAKE_DATA, 'doc') 43 | CMAKE_SHARE_DIR = os.path.join(CMAKE_DATA, 'share') 44 | 45 | 46 | def _program(name: str, args: Iterable[str]) -> int: 47 | return subprocess.call([os.path.join(CMAKE_BIN_DIR, name), *args], close_fds=False) 48 | 49 | def _program_exit(name: str, *args: str) -> NoReturn: 50 | if sys.platform.startswith("win"): 51 | raise SystemExit(_program(name, args)) 52 | cmake_exe = os.path.join(CMAKE_BIN_DIR, name) 53 | os.execl(cmake_exe, cmake_exe, *args) 54 | 55 | 56 | def ccmake() -> NoReturn: 57 | _program_exit('ccmake', *sys.argv[1:]) 58 | 59 | 60 | def cmake() -> NoReturn: 61 | _program_exit('cmake', *sys.argv[1:]) 62 | 63 | 64 | def cpack() -> NoReturn: 65 | _program_exit('cpack', *sys.argv[1:]) 66 | 67 | 68 | def ctest() -> NoReturn: 69 | _program_exit('ctest', *sys.argv[1:]) 70 | -------------------------------------------------------------------------------- /src/cmake/__main__.py: -------------------------------------------------------------------------------- 1 | from cmake import cmake 2 | 3 | if __name__ == "__main__": 4 | cmake() 5 | -------------------------------------------------------------------------------- /src/cmake/_version.pyi: -------------------------------------------------------------------------------- 1 | version: str 2 | -------------------------------------------------------------------------------- /src/cmake/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scikit-build/cmake-python-distributions/92d896c63065f2e7185c73cab9c719de2cd3eba4/src/cmake/py.typed -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from contextlib import contextmanager 3 | 4 | 5 | @contextmanager 6 | def push_argv(argv): 7 | old_argv = sys.argv 8 | sys.argv = argv 9 | yield 10 | sys.argv = old_argv 11 | -------------------------------------------------------------------------------- /tests/test_cmake.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import sys 4 | import sysconfig 5 | import textwrap 6 | 7 | import pytest 8 | 9 | if sys.version_info < (3, 8): 10 | from importlib_metadata import distribution 11 | else: 12 | from importlib.metadata import distribution 13 | 14 | import cmake 15 | 16 | from . import push_argv 17 | 18 | all_tools = pytest.mark.parametrize("tool", ["cmake", "cpack", "ctest"]) 19 | 20 | 21 | def _run(program, args): 22 | func = getattr(cmake, program) 23 | args = [f"{program}.py", *args] 24 | with push_argv(args), pytest.raises(SystemExit) as excinfo: 25 | func() 26 | assert excinfo.value.code == 0 27 | 28 | 29 | @all_tools 30 | def test_cmake_module(tool, monkeypatch): 31 | monkeypatch.setattr(sys, "platform", "win32") # do not use os.execl 32 | _run(tool, ["--version"]) 33 | 34 | 35 | def test_cmake_https(tmpdir, monkeypatch): 36 | monkeypatch.setattr(sys, "platform", "win32") # do not use os.execl 37 | test_script = tmpdir.join("cmake-test-https-download.cmake") 38 | test_script.write(textwrap.dedent( 39 | """ 40 | file( 41 | DOWNLOAD 42 | https://github.com/scikit-build/cmake-python-distributions 43 | ${TMP_DIR}/page.html 44 | SHOW_PROGRESS 45 | STATUS status 46 | ) 47 | list(GET status 0 error_code) 48 | list(GET status 1 error_msg) 49 | if(error_code) 50 | message( 51 | FATAL_ERROR "error: Failed to download ${url}: ${error_msg}") 52 | endif() 53 | """ 54 | )) 55 | 56 | _run("cmake", [f"-DTMP_DIR:PATH={tmpdir}", "-P", str(test_script)]) 57 | 58 | 59 | def _get_scripts(): 60 | dist = distribution("cmake") 61 | scripts_paths = [os.path.abspath(sysconfig.get_path("scripts", scheme)) for scheme in sysconfig.get_scheme_names()] 62 | scripts = [] 63 | for file in dist.files: 64 | if os.path.abspath(str(file.locate().parent)) in scripts_paths: 65 | scripts.append(file.locate().resolve(strict=True)) 66 | return scripts 67 | 68 | 69 | @all_tools 70 | def test_cmake_script(tool): 71 | expected_version = "4.0.2" 72 | scripts = [script for script in _get_scripts() if script.stem == tool] 73 | assert len(scripts) == 1 74 | output = subprocess.check_output([str(scripts[0]), "--version"]).decode("ascii") 75 | assert output.splitlines()[0] == f"{tool} version {expected_version}" 76 | 77 | 78 | def test_cmake_main(): 79 | expected_version = "4.0.2" 80 | output = subprocess.run([sys.executable, "-m", "cmake", "--version"], text=True, capture_output=True, check=False).stdout 81 | assert output.splitlines()[0] == f"cmake version {expected_version}" 82 | --------------------------------------------------------------------------------