# How to force FindPython to locate a specific version of Python without specifying every last artifact?

**URL:** https://discourse.cmake.org/t/how-to-force-findpython-to-locate-a-specific-version-of-python-without-specifying-every-last-artifact/13165
**Category:** Usage
**Created:** [December 9, 2024, 9:56pm UTC](https://discourse.cmake.org/t/how-to-force-findpython-to-locate-a-specific-version-of-python-without-specifying-every-last-artifact/13165 "2024-12-09T21:56:52Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![PieterP](https://discourse.cmake.org/user_avatar/discourse.cmake.org/pieterp/32/5184_2.png) [@PieterP](https://discourse.cmake.org/u/PieterP)
#### Post date: [December 9, 2024, 9:56pm UTC](https://discourse.cmake.org/t/how-to-force-findpython-to-locate-a-specific-version-of-python-without-specifying-every-last-artifact/13165/1 "2024-12-09T21:56:52Z")

</div>

**Background** : the Python build backend [`py-build-cmake`](https://github.com/tttapa/py-build-cmake) that I maintain needs to invoke CMake to build Python extension modules in user-provided CMake projects. The goal of the build backend is to build these extension modules for a specific version of Python, and if CMake’s FindPython module were to locate the wrong version of Python, this would result in incompatible binaries.

**Question** : How can I force FindPython to locate this specific version of Python only, and never fall back to e.g. the system-wide Python installation at /usr/bin/python3?

If the user’s CMake project searches for Python using `find_package(Python3 COMPONENTS Interpreter Development.Module)`, it looks like setting `Python3_EXECUTABLE` does the right thing. However, problems arise when the user only searches for the `Development.Module` component without the `Interpreter`: in such a case, it is necessary to set `Python3_ROOT_DIR`, but this directory may not be unique, it may contain multiple versions of Python, and CMake sometimes picks the wrong one.

For example, if a user installs both Python 3.12 and 3.13 (using `make altinstall`) in `/usr/local`, the installation tree could look like this:

```sh
/usr/local
├── bin
│ ├── python3.12
│ ├── python3.12-config
│ ├── python3.13
│ └── python3.13-config
├── include
│ ├── python3.12
│ └── python3.13
└── lib
    ├── libpython3.12.so.1.0
    ├── libpython3.13.so.1.0
    ├── libpython3.so
    ├── python3.12
    └── python3.13

```

When setting `Python3_ROOT_DIR=/usr/local`, FindPython will always pick Python 3.13, unless I explicitly set all hints, artifacts and result variables such as `Python3_INCLUDE_DIR`, `Python3_SOABI`, etc.

Is there a way to _guarantee_ that FindPython ignores any other Python versions? Even if the given Python installation does not satisfy the version range provided in the user’s `find_package` call, it should not consider any other Python installations that satisfy them, returning `NOTFOUND` instead.

Related: [https://gitlab.kitware.com/cmake/cmake/-/issues/26505](https://gitlab.kitware.com/cmake/cmake/-/issues/26505)

---

<div class="post-metadata">

### Author: ![marc.chevrier](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/m/ecb155/32.png) [@marc.chevrier](https://discourse.cmake.org/u/marc.chevrier)
#### Post date: [December 9, 2024, 10:54pm UTC](https://discourse.cmake.org/t/how-to-force-findpython-to-locate-a-specific-version-of-python-without-specifying-every-last-artifact/13165/2 "2024-12-09T22:54:01Z")

</div>

In your case, specifying the required version should be enough:

```cmake
set(Python3_ROOT_DIR "/usr/local")
find_package(Python3 3.12 EXACT COMPONENTS ...)

```

or even better:

```cmake
set(Python3_ROOT "/usr/local")
find_package(Python3 3.12 EXACT COMPONENTS ...)

```

---

<div class="post-metadata">

### Author: ![PieterP](https://discourse.cmake.org/user_avatar/discourse.cmake.org/pieterp/32/5184_2.png) [@PieterP](https://discourse.cmake.org/u/PieterP)
#### Post date: [December 9, 2024, 11:12pm UTC](https://discourse.cmake.org/t/how-to-force-findpython-to-locate-a-specific-version-of-python-without-specifying-every-last-artifact/13165/3 "2024-12-09T23:12:34Z")

</div>

Thanks! Unfortunately, I don’t have control over the actual `find_package` call site.

The idea is that the user provides a CMake project that uses `find_package` somewhere (possibly in multiple places), and then the Python build tools (`cibuildwheel`, `pip`, `py-build-cmake`, `scikit-build`, etc.) build this user-provided project using a range of Python versions. Control over the Python version would therefore need to happen through the environment or CMake variables.  
Is this possible without modifying any user-provided CMake code?

---

<div class="post-metadata">

### Author: ![PieterP](https://discourse.cmake.org/user_avatar/discourse.cmake.org/pieterp/32/5184_2.png) [@PieterP](https://discourse.cmake.org/u/PieterP)
#### Post date: [December 9, 2024, 11:39pm UTC](https://discourse.cmake.org/t/how-to-force-findpython-to-locate-a-specific-version-of-python-without-specifying-every-last-artifact/13165/4 "2024-12-09T23:39:55Z")

</div>

I’ll try to give a more specific example:  
A user runs `pip install /some/package` on a Python package. The package being built has the following code in one of it’s CMakeLists.txt files.

```cmake
# This code is fixed. It is provided by the user of the build backend.
# As a build backend developer, I have no control over it
# (aside from maybe some guidelines in the documentation).
find_package(Python3 3.10...<3.14 REQUIRED
             COMPONENTS Development.Module)

```

Now it is the job of the Python build backend (e.g. py-build-cmake) to invoke CMake for this package, in such a way that CMake only finds the very specific version selected by the Python build front-end (e.g. Pip). This usually means that we have the full path of the Python interpreter to use and are able to query it.  
The invocation of CMake by the Python build backend then looks like this (simplified):

```sh
cmake -B build -S /some/package \
    -D Python3_SOME_MAGIC_OPTION=/opt/python/bin/python3.12 \
    -D Python3_SOME_ARTIFACT=/opt/python/include/python3.12

```

Which CMake variables does the build backend need to set to make sure that CMake only considers the intended Python installation?

Finding the given Python installation is one thing, but another important consideration is making sure that CMake doesn’t select some other Python version it found elsewhere on the system, e.g. in case it deems the given `/opt/python/bin/python3.12` installation unusable (wrong bitness, not in the CMAKE\_FIND\_ROOT\_PATH when cross-compiling, an incorrect artifact path, a conflicting version requirement in the user’s find\_package call, etc.).

For example, let’s say that the user specified a different version range, `find_package(Python3 3.13...<3.15 ...)`, and the Python build tool tries to build the package for Python 3.12. In this case, FindPython should fail rather than returning an arbitrary Python 3.13 or 3.14 installation it found elsewhere.

---

<div class="post-metadata">

### Author: ![marc.chevrier](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/m/ecb155/32.png) [@marc.chevrier](https://discourse.cmake.org/u/marc.chevrier)
#### Post date: [December 10, 2024, 9:17am UTC](https://discourse.cmake.org/t/how-to-force-findpython-to-locate-a-specific-version-of-python-without-specifying-every-last-artifact/13165/5 "2024-12-10T09:17:21Z")

</div>

At minimum, setting the variable `Python3_ROOT` will ensure the search will be limited to this root directory.  
Now, to have a full control of the artifacts selected, without control of the `find_package()` command is to define, at minima, the following hints:

Python3\_EXECUTABLE  
Python3\_LIBRARY  
Python3\_INCLUDE\_DIR

One possibility, at the top level, is to execute a `CMakeLists.txt` with the correct `find_package()` command and reuse the results of this search to set the hints.

---

<div class="post-metadata">

### Author: ![PieterP](https://discourse.cmake.org/user_avatar/discourse.cmake.org/pieterp/32/5184_2.png) [@PieterP](https://discourse.cmake.org/u/PieterP)
#### Post date: [December 12, 2024, 8:39pm UTC](https://discourse.cmake.org/t/how-to-force-findpython-to-locate-a-specific-version-of-python-without-specifying-every-last-artifact/13165/6 "2024-12-12T20:39:00Z")

</div>

Thanks!

Could you point me to the documentation for the `Python3_ROOT` variable? I didn’t find it on the FindPython page, and the `<PackageName>_ROOT` variable only seems to be mentioned in the config mode section of the `find_package` documentation.  
Can `Python3_ROOT` be set to a list of possible root directories?

---

<div class="post-metadata">

### Author: ![marc.chevrier](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/m/ecb155/32.png) [@marc.chevrier](https://discourse.cmake.org/u/marc.chevrier)
#### Post date: [December 13, 2024, 9:12am UTC](https://discourse.cmake.org/t/how-to-force-findpython-to-locate-a-specific-version-of-python-without-specifying-every-last-artifact/13165/7 "2024-12-13T09:12:22Z")

</div>

`<PackageName>_ROOT` is a CMake feature, this is why there is no specific documentation on it in `FindPython` documentation.  
Indeed, for `find_package()` command, this is used only in `CONFIG` mode, but all other `find_*` commands rely on this variable If called from within a find module. Take a look at these commands for a description of `<PackageName>_ROOT` variable.
