66# Additionally, setting Python3_EXECUTABLE can be used to set the Python version explicitly, but
77# may become less reliable with newer versions of CMake (as opposed setting FindPython3 HINTS). Current
88# implementation gives preference to active virtualenvs.
9-
109cmake_policy (SET CMP0094 NEW) # makes FindPython3 prefer activated virtualenv Python to latest version
1110set (PYTHON_VERSION_MIN 3.9)
1211set (PYTHON_VERSION_MAX 3.999)
13- if (MSVC AND CMAKE_BUILD_TYPE STREQUAL "Debug" )
14- set (
15- Python3_FIND_ABI
16- "OFF" # pydebug - use non-debug python even for debug builds
17- "ANY" # pymalloc
18- "ANY" # unicode
19- "ANY" # gil_disabled
12+
13+ if (NOT PYTHON_DEVELOPMENT_REQUIRED)
14+ # if not PYTHON_DEVELOPMENT_REQUIRED, just find some version of
15+ # Python (don't need to be as specific)
16+ find_package (
17+ Python3
18+ ${PYTHON_VERSION_MIN} ...${PYTHON_VERSION_MAX}
19+ COMPONENTS
20+ Interpreter
2021 )
2122else ()
2223 set (
2324 Python3_FIND_ABI
24- "OFF" # pydebug
25+ "OFF" # pydebug - use non-debug python even for debug builds
2526 "ANY" # pymalloc
2627 "ANY" # unicode
27- "ANY" # gil_disabled
2828 )
29- endif ()
29+ # For CMake >= 3.30, append an additional ANY to Python3_FIND_ABI to
30+ # accommodate newer FindPython3 ABI fields (e.g., free-threaded builds).
31+ if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.30" )
32+ list (APPEND Python3_FIND_ABI "ANY" ) # gil_disabled
33+ endif ()
3034
31- if (PYTHON_DEVELOPMENT_REQUIRED)
3235 if (DEFINED Python3_EXECUTABLE) # if already specified
3336 set (_specified_Python3_EXECUTABLE ${Python3_EXECUTABLE} )
3437 endif ()
3538 # set(Python3_FIND_REGISTRY LAST) # default is FIRST. Do we need/want this?
3639 find_package (
40+ # Used to find the exact version for setting ITK_WRAP_PYTHON_VERSION
41+ # NOTE: no REQUIRED FLAG during this call to find_package, just search
42+ # for a best fit version to support wrapping
3743 Python3
3844 ${PYTHON_VERSION_MIN} ...${PYTHON_VERSION_MAX}
3945 COMPONENTS
4046 Interpreter
47+ Development
4148 Development.Module
42- ${SKBUILD_SABI_COMPONENT}
43- NumPy # Required for ITK, prefer to fail early
49+ Development.SABIModule
50+ NumPy
4451 )
52+ if (ITK_WRAP_PYTHON)
53+ set (ITK_WRAP_PYTHON_VERSION "${Python3_VERSION} " )
54+ else ()
55+ set (ITK_WRAP_PYTHON_VERSION "ITK_WRAP_PYTHON=OFF" )
56+ endif ()
57+
4558 # start section to define package components based on LIMITED_API support and choices
4659 set (_ITK_MINIMUM_SUPPORTED_LIMITED_API_VERSION 3.11)
47- if (
48- ITK_WRAP_PYTHON_VERSION
49- VERSION_GREATER_EQUAL
50- ${_ITK_MINIMUM_SUPPORTED_LIMITED_API_VERSION}
60+
61+ # Force ITK_WRAP_PYTHON_VERSION if SKBUILD_SABI_COMPONENT requests it
62+ string (
63+ FIND
64+ "${SKBUILD_SABI_COMPONENT} "
65+ "${SABIModule} "
66+ _SKBUILD_SABI_COMPONENT_REQUIRED
5167 )
68+ if (NOT DEFINED ITK_USE_PYTHON_LIMITED_API)
69+ if (
70+ (
71+ _SKBUILD_SABI_COMPONENT_REQUIRED
72+ GREATER
73+ -1
74+ )
75+ OR
76+ ITK_WRAP_PYTHON_VERSION
77+ VERSION_GREATER_EQUAL
78+ ${_ITK_MINIMUM_SUPPORTED_LIMITED_API_VERSION}
79+ )
80+ set (
81+ ITK_USE_PYTHON_LIMITED_API
82+ 1
83+ CACHE BOOL
84+ "Configure Python's limited API for Python minor version compatibility."
85+ )
86+ else ()
87+ set (
88+ ITK_USE_PYTHON_LIMITED_API
89+ 0
90+ CACHE BOOL
91+ "Configure Python's limited API for Python minor version compatibility."
92+ )
93+ endif ()
94+ mark_as_advanced (ITK_USE_PYTHON_LIMITED_API)
95+ endif ()
96+ unset (_SKBUILD_SABI_COMPONENT_REQUIRED)
97+ if (ITK_USE_PYTHON_LIMITED_API)
5298 set (
53- ITK_USE_PYTHON_LIMITED_API
54- 1
55- CACHE BOOL
56- "Configure Python's limited API for Python minor version compatibility."
99+ _python_find_components
100+ Interpreter
101+ Development
102+ Development.SABIModule
103+ NumPy # NumPy Required for ITK, prefer to fail early
57104 )
58105 else ()
59106 set (
60- ITK_USE_PYTHON_LIMITED_API
61- 0
62- CACHE BOOL
63- "Configure Python's limited API for Python minor version compatibility."
107+ _python_find_components
108+ Interpreter
109+ Development
110+ Development.Module
111+ NumPy # NumPy Required for ITK, prefer to fail early
64112 )
65113 endif ()
66- mark_as_advanced (ITK_USE_PYTHON_LIMITED_API)
67114
68- set (_development_component "Development.Module" )
69- set (_Python3_ABI_SETTINGS)
70- if (ITK_USE_PYTHON_LIMITED_API)
71- set (_development_component "Development.SABIModule" )
72- set (
73- _Python3_ABI_SETTINGS
74- USE_SABI
75- ${_ITK_MINIMUM_SUPPORTED_LIMITED_API_VERSION}
76- WITH_SOABI
77- )
78- endif ()
79- set (
80- _py_components
81- Interpreter
82- ${_development_component}
83- NumPy # Required for ITK, prefer to fail early
84- ${SKBUILD_SABI_COMPONENT}
85- )
86- list (REMOVE_DUPLICATES _py_components)
87115 find_package (
88116 Python3
89117 ${ITK_WRAP_PYTHON_VERSION} # Force python version to match previously found
90118 REQUIRED
91119 COMPONENTS
92- ${_py_components }
120+ ${_python_find_components }
93121 )
94- unset (_py_components)
95- unset (_development_component)
122+ unset (_python_find_components)
96123 unset (_ITK_MINIMUM_SUPPORTED_LIMITED_API_VERSION)
97124 # end section to define package components based on LIMITED_API support and choices
98125 if (DEFINED _specified_Python3_EXECUTABLE)
@@ -104,35 +131,81 @@ if(PYTHON_DEVELOPMENT_REQUIRED)
104131 FORCE
105132 )
106133 endif ()
107- else () # if not PYTHON_DEVELOPMENT_REQUIRED, just find some version of Python (don't need to be as specific)
108- find_package (
109- Python3
110- ${PYTHON_VERSION_MIN} ...${PYTHON_VERSION_MAX}
111- COMPONENTS
112- Interpreter
113- )
114- endif ()
115- if (ITK_WRAP_PYTHON)
116- set (ITK_WRAP_PYTHON_VERSION "${Python3_VERSION} " )
117- else ()
118- set (ITK_WRAP_PYTHON_VERSION "ITK_WRAP_PYTHON=OFF" )
119- endif ()
120- if (NOT Python3_EXECUTABLE AND _specified_Python3_EXECUTABLE) # workaround for cases where FindPython3 fails to set correctly
121- set (
122- Python3_EXECUTABLE
123- ${_specified_Python3_EXECUTABLE}
124- CACHE INTERNAL
125- "Path to the Python interpreter"
126- FORCE
134+
135+ if (NOT Python3_EXECUTABLE AND _specified_Python3_EXECUTABLE) # workaround for cases where FindPython3 fails to set correctly
136+ set (
137+ Python3_EXECUTABLE
138+ ${_specified_Python3_EXECUTABLE}
139+ CACHE INTERNAL
140+ "Path to the Python interpreter"
141+ FORCE
142+ )
143+ endif ()
144+
145+ # If a specific Python3_EXECUTABLE is provided by the user, try to infer
146+ # the corresponding Python3_ROOT_DIR for Unix/macOS/Linux so CMake's
147+ # FindPython3 locates the matching installation or virtual environment.
148+ # This is especially important for virtualenv/venv/conda environments.
149+ if (
150+ DEFINED
151+ Python3_EXECUTABLE
152+ AND
153+ NOT
154+ DEFINED
155+ Python3_ROOT_DIR
156+ AND
157+ (
158+ UNIX
159+ OR
160+ APPLE
161+ )
162+ AND
163+ NOT
164+ WIN32
127165 )
128- endif ()
166+ # First, try sys.prefix from the provided interpreter (works for venv/conda)
167+ execute_process (
168+ COMMAND
169+ "${Python3_EXECUTABLE} " -c "import sys; print(sys.prefix)"
170+ OUTPUT_VARIABLE _py_prefix
171+ ERROR_VARIABLE _py_prefix_err
172+ OUTPUT_STRIP_TRAILING_WHITESPACE
173+ )
174+ if (_py_prefix)
175+ file (TO_CMAKE_PATH "${_py_prefix} " _py_root_hint)
176+ endif ()
129177
130- # Add user-visible cache entry
131- set (
132- Python3_ROOT_DIR
133- ${Python3_ROOT_DIR}
134- CACHE PATH
135- "Which installation or virtual environment of Python to use"
136- FORCE
137- )
138- mark_as_advanced (Python3_ROOT_DIR)
178+ # Fallback: parent of the interpreter's bin directory, e.g., /path/to/env
179+ # from /path/to/env/bin/python3
180+ if (NOT _py_root_hint)
181+ get_filename_component (_py_exe_dir "${Python3_EXECUTABLE} " DIRECTORY )
182+ get_filename_component (_py_root_hint "${_py_exe_dir} /.." REALPATH)
183+ endif ()
184+
185+ if (_py_root_hint)
186+ set (
187+ Python3_ROOT_DIR
188+ "${_py_root_hint} "
189+ CACHE PATH
190+ "Which installation or virtual environment of Python to use"
191+ FORCE
192+ )
193+ mark_as_advanced (Python3_ROOT_DIR)
194+ endif ()
195+ unset (_py_prefix)
196+ unset (_py_prefix_err)
197+ unset (_py_exe_dir)
198+ unset (_py_root_hint)
199+ endif ()
200+ if (Python3_ROOT_DIR)
201+ # Add user-visible cache entry if Python3_ROOT_DIR value is set
202+ set (
203+ Python3_ROOT_DIR
204+ ${Python3_ROOT_DIR}
205+ CACHE PATH
206+ "Which installation or virtual environment of Python to use"
207+ FORCE
208+ )
209+ mark_as_advanced (Python3_ROOT_DIR)
210+ endif ()
211+ endif ()
0 commit comments