Of course! Finding Python and its components with CMake is a common but sometimes tricky task. The key is to use the modern find_package(Python) command, which is available since CMake 3.12.

Here's a comprehensive guide covering the basics, best practices, and common use cases.
The Modern Way: find_package(Python)
This is the recommended approach. It's powerful, flexible, and provides a lot of useful variables.
Basic Usage
The simplest way to find a Python interpreter and its libraries.
# Find the Python interpreter and development components
find_package(Python REQUIRED)
# --- Use the found variables ---
# For executables:
add_executable(my_app main.py)
# You can add the Python script as a dependency to your executable
# This is a bit of a CMake "trick" to ensure the script is included in the build directory.
add_custom_command(
TARGET my_app POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:my_app> $<TARGET_FILE_DIR:my_app>/main.py
COMMENT "Copying Python script to build directory"
)
# For libraries (if you're embedding Python):
# The include directories for Python headers
target_include_directories(my_app PRIVATE ${Python_INCLUDE_DIRS})
# The libraries needed to link against Python
target_link_libraries(my_app PRIVATE ${Python_LIBRARIES})
What find_package(Python) Provides
When you call find_package(Python), it populates several variables. The most common ones are:

${Python_EXECUTABLE}: The full path to the Python interpreter (e.g.,/usr/bin/python3).${Python_VERSION_STRING}: The version of the found Python (e.g.,9.7).${Python_INCLUDE_DIRS}: The directory containing the PythonPython.hheader. This is crucial for compiling C/C++ extensions.${Python_LIBRARIES}: The libraries needed to link against the Python interpreter (e.g.,python39on Windows,-lpython3.9on Linux).${Python_SITE_PACKAGES}: The directory where third-party packages are typically installed (e.g.,.../lib/python3.9/site-packages). This is very useful for installing your own modules.
Advanced Usage: Specifying Components
Python has different "components" you might need. The most important one for C/C++ extension development is Development.
Interpreter: Just the${Python_EXECUTABLE}. This is the default component.Development: The headers (Python.h) and libraries needed for building C/C++ extensions. This is what you need most of the time.NumPy: Finds the NumPy package and its headers. Useful for scientific computing.
Example: Finding Python for a C++ Extension
cmake_minimum_required(VERSION 3.15)
project(MyPythonExtension)
# We need the Python interpreter AND the development files
find_package(Python COMPONENTS Interpreter Development REQUIRED)
# Add our C++ extension library
add_library(my_ext MODULE my_extension.cpp)
# Link against Python libraries
target_link_libraries(my_ext PRIVATE ${Python_LIBRARIES})
# Add the Python header directory
target_include_directories(my_ext PRIVATE ${Python_INCLUDE_DIRS})
# On some platforms (like macOS), you might need extra linker flags
if(APPLE)
target_link_libraries(my_ext PRIVATE "-undefined dynamic_lookup")
endif()
The Classic Way: find_program and find_library
Before find_package(Python) was standardized, developers used a more manual approach. It's good to recognize this pattern if you see it in older code, but you should not use it for new projects.
# --- NOT RECOMMENDED for new projects ---
# Find the Python interpreter executable
find_program(PYTHON_EXECUTABLE python python3 python3.9)
# Find the Python library (this is platform-specific and brittle)
if(UNIX)
# On Linux, the library name is usually pythonX.Y
find_library(PYTHON_LIBRARIES python python3 python3.9)
elseif(WIN32)
# On Windows, it's often pythonXX (e.g., python39)
find_library(PYTHON_LIBRARIES python39)
endif()
# Finding the include directory is also tricky
# You might have to execute Python to get the path
if(PYTHON_EXECUTABLE)
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())"
OUTPUT_VARIABLE PYTHON_INCLUDE_DIRS
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
# Now you can use PYTHON_EXECUTABLE, PYTHON_LIBRARIES, etc.
# This is much more error-prone!
Why is this bad?
- Brittle: It relies on guessing library names (
python,python3,python3.9). - Incomplete: It doesn't reliably find the correct include directory for the found executable.
- Less Standard: Different projects might do this differently, leading to inconsistency.
Common Use Cases & Examples
Use Case 1: Building a C++ Python Extension
This is the most common reason to find Python in a CMake project. You want to compile a C++ file into a shared library (.so or .pyd) that Python can import.
File Structure:
my_project/
├── CMakeLists.txt
└── src/
├── my_extension.cpp
└── my_module.py
src/my_extension.cpp
#include <Python.h>
// A simple function that adds two numbers
static PyObject* add_numbers(PyObject* self, PyObject* args) {
double a, b;
if (!PyArg_ParseTuple(args, "dd", &a, &b)) {
return NULL;
}
return PyFloat_FromDouble(a + b);
}
// Define the methods of our module
static PyMethodDef MyMethods[] = {
{"add_numbers", add_numbers, METH_VARARGS, "Add two numbers"},
{NULL, NULL, 0, NULL} // Sentinel
};
// Define the module
static struct PyModuleDef mymodule = {
PyModuleDef_HEAD_INIT,
"my_extension", // Module name
NULL,
-1,
MyMethods
};
// Module initialization function
PyMODINIT_FUNC PyInit_my_extension(void) {
return PyModule_Create(&mymodule);
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(MyPythonExtension)
# Find Python with the Development component
find_package(Python COMPONENTS Interpreter Development REQUIRED)
# Add the C++ extension as a shared library
# MODULE creates a shared library on Unix (.so) and a DLL on Windows (.pyd)
add_library(my_extension MODULE src/my_extension.cpp)
# Link against the Python libraries
target_link_libraries(my_extension PRIVATE ${Python_LIBRARIES})
# Include the Python headers
target_include_directories(my_extension PRIVATE ${Python_INCLUDE_DIRS})
# This is important for macOS to allow unresolved symbols at runtime
if(APPLE)
target_link_libraries(my_extension PRIVATE "-undefined dynamic_lookup")
endif()
# --- Install the module ---
# We want to install the .so/.pyd file into Python's site-packages directory
install(TARGETS my_extension
DESTINATION ${Python_SITE_PACKAGES}
COMPONENT Runtime)
# You can also install the Python module itself
install(FILES src/my_module.py
DESTINATION ${Python_SITE_PACKAGES}
COMPONENT Runtime)
Use Case 2: Running a Python Script as Part of the Build
Sometimes you need to generate source files or run a script during the CMake configuration or build process.
generate_data.py
import sys
import os
# Write a header file with the current Python version
output_file = sys.argv[1]
with open(output_file, 'w') as f:
f.write(f"#pragma once\n")
f.write(f"#define PYTHON_VERSION_STR \"{sys.version}\"\n")
f.write(f"#define PYTHON_VERSION_MAJOR {sys.version_info.major}\n")
f.write(f"#define PYTHON_VERSION_MINOR {sys.version_info.minor}\n")
CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project(MyProject)
# Find Python
find_package(Python REQUIRED)
# Define the output file for the generated header
set(GENERATED_HEADER "${CMAKE_CURRENT_BINARY_DIR}/python_version.h")
# Use add_custom_command to run the script
add_custom_command(
OUTPUT ${GENERATED_HEADER}
COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generate_data.py ${GENERATED_HEADER}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generate_data.py
COMMENT "Generating Python version header"
)
# Create an executable that depends on the generated header
add_executable(my_app main.cpp)
# Add the generated header as a source so CMake knows when to re-run the command
target_sources(my_app PRIVATE ${GENERATED_HEADER})
Troubleshooting & Best Practices
-
"Could NOT find Python": This is the most common error.
- Reason: CMake couldn't find a Python interpreter in your system's
PATH. - Solution: Make sure Python is installed and accessible from your terminal/command prompt. You can specify the exact interpreter using
-DPython_EXECUTABLE=/path/to/your/pythonwhen running CMake.cmake -B build -DPython_EXECUTABLE=/usr/bin/python3.9
- Reason: CMake couldn't find a Python interpreter in your system's
-
"Could NOT find Python (missing: Development Component)":
- Reason: You asked for the
Developmentcomponent, but CMake couldn't find the necessary header files (Python.h) and/or libraries. - Solution: This means you have a Python interpreter but not the "development" or "dev" package.
- On Debian/Ubuntu:
sudo apt-get install python3-dev - On Fedora/CentOS:
sudo dnf install python3-devel - On macOS (using Homebrew):
brew install python3(the headers are included). - On Windows: Make sure you checked the "Install development libraries" or "Download debugging symbols" option during the Python installer.
- On Debian/Ubuntu:
- Reason: You asked for the
-
Which Python Version?
find_package(Python)is version-agnostic. It will find the firstpython3it sees. If you need a specific version (e.g., Python 3.9), you can usefind_package(Python 3.9 EXACT REQUIRED). This is useful for projects that depend on a specific Python version. -
Best Practice: Always use
find_package(Python COMPONENTS Interpreter Development REQUIRED). This makes your intent clear and ensures you have everything you need to build both run and develop your project.
