Skip to content

Commit

Permalink
fix cmake FindPython3 shortcomings for Mac Frameworks
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinhendricks committed Feb 7, 2025
1 parent d35d0d3 commit 0811550
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 4 deletions.
15 changes: 11 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
########################################################
###A#####################################################
#
# This is a CMake configuration file.
# To use it you need CMake which can be
Expand Down Expand Up @@ -27,7 +27,7 @@ if ( NOT DEFINED TRY_NEWER_FINDPYTHON3 )
if ( NOT APPLE )
set ( TRY_NEWER_FINDPYTHON3 1 )
else()
set ( TRY_NEWER_FINDPYTHON3 0 )
set ( TRY_NEWER_FINDPYTHON3 1 )
endif()
endif()

Expand Down Expand Up @@ -144,6 +144,9 @@ message(STATUS "CMake version in use: ${CMAKE_VERSION}")
if (${USE_NEWER_FINDPYTHON3})
message(STATUS "Using newer findpython3 cmake module")
if ( APPLE )
# cmake's FindPython3 does *not* find frameworks based on precendence in PATH
# so set some pretty specific hints using our own FindPython3MacFramework.cmake first
find_package(Python3MacFramework 3.9 QUIET)
find_package(Python3 3.9 COMPONENTS Interpreter Development)
endif()
if ( WIN32 )
Expand Down Expand Up @@ -181,8 +184,11 @@ if (${USE_NEWER_FINDPYTHON3})
else()
message(STATUS "Using older findpython cmake module")
if ( APPLE )
find_package(PythonInterp 3.11)
find_package (PythonLibs 3.11)
find_package(PythonMacFramework 3.9 QUIET)
if (NOT PythonMacFramework_FOUND)
find_package(PythonInterp 3.9)
find_package(PythonLibs 3.9)
endif()
endif()
if ( WIN32 )
find_package(PythonInterp 3.9)
Expand All @@ -201,3 +207,4 @@ add_subdirectory( internal/gumbo )

add_subdirectory( 3rdparty/ )
add_subdirectory( src/ )

98 changes: 98 additions & 0 deletions cmake_extras/FindPython3MacFramework.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#.rst:
# FindPython3MacFramework
# ----------------------
#
# Find first Python.framework from version 3 found in your system PATH
#
# This module finds if a Python.framework is installed and determines
# where the interpreter (executable) and libraries are.
# This code sets the following variables:
#
# ::
# Python3MacFramework_FOUND
# Python3_EXECUTABLE - path to the Python interpreter
# Python3_LIBRARIES - path to the python library
# Python3_INCLUDE_DIRS - path to where Python.h is found
# Python3_VERSION_STRING - version of the Python libs found

set(_PYTHON_VERSIONS 3.14 3.13 3.12 3.11 3.10 3.9)

find_program(_PYTHON_EXECUTABLE python3 PATHS ENV PATH)

# determine python version string
if(_PYTHON_EXECUTABLE)
execute_process(COMMAND "${_PYTHON_EXECUTABLE}" -c
"import sys; sys.stdout.write(';'.join([str(x) for x in sys.version_info[:3]]))"
OUTPUT_VARIABLE _VERSION
RESULT_VARIABLE _PYTHON_VERSION_RESULT
ERROR_QUIET)

if(NOT _PYTHON_VERSION_RESULT)
list(GET _VERSION 0 _PYTHON_VERSION_MAJOR)
list(GET _VERSION 1 _PYTHON_VERSION_MINOR)
list(GET _VERSION 2 _PYTHON_VERSION_PATCH)
list(JOIN _VERSION "." _PYTHON_VERSION_STRING)
set(_VERSION_STRING "${_PYTHON_VERSION_MAJOR}.${_PYTHON_VERSION_MINOR}")
# it must be an acceptable version
list( FIND _PYTHON_VERSIONS ${_VERSION_STRING} _ACC_VER)
if ( ${_ACC_VER} GREATER -1 )
# determine if this is a Python.framework
# ie. /Library/Frameworks/Python.framework/Versions/3.13/bin/python3
# ie. /Users/kbhend/ndevpython/libraries/Frameworks/Python.framework/Versions/3.13/bin/python3

string(REPLACE "/" ";" _PATH_SEGMENTS ${_PYTHON_EXECUTABLE})
set(_PATH_LIST ${_PATH_SEGMENTS} )
# message("Excutable Path: " ${_PYTHON_EXECUTABLE} )
list( FIND _PATH_LIST "Python.framework" _ISFWK )
if( ${_ISFWK} GREATER -1 )
# message("Found framework: " ${_ISFWK} )
list(POP_BACK _PATH_LIST) # remove python3
list(POP_BACK _PATH_LIST) # remove bin
list(POP_BACK _PATH_LIST _CURRENT_VERSION)
list(POP_BACK _PATH_LIST) # remove Versions
list(JOIN _PATH_LIST "/" _PYTHON_FRAMEWORK_BASE)

# Now we have found a valid Python3 Framework from the PATH
set(Python3_VERSION_STRING ${_PYTHON_VERSION_STRING})
set(Python3_EXECUTABLE "${_PYTHON_EXECUTABLE}")
# Remember to add back the leading "/" since these are absolute paths
set(Python3_LIBRARIES "/${_PYTHON_FRAMEWORK_BASE}/Versions/${_CURRENT_VERSION}/lib/libpython${_VERSION_STRING}.dylib")
set(Python3_INCLUDE_DIRS "/${_PYTHON_FRAMEWORK_BASE}/Versions/${_CURRENT_VERSION}/include/python${_VERSION_STRING}")
# message("Python3_LIBRARIES: " ${Python3_LIBRARIES})
# message("Python3_INCLUDE_DIRS: " ${Python3_INCLUDE_DIRS})
# clean up
unset(_CURRENT_VERSION)
unset(_PYTHON_VERSION_STRING)
unset(_PYTHON_FRAMEWORK_BASE)
endif()
# clean up
unset(_IS_FWK)
unset(_PATH_SEGMENTS)
unset(_PATH_LIST)
endif()
# clean up
unset(_PYTHON_VERSION_MAJOR)
unset(_PYTHON_VERSION_MINOR)
unset(_PYTHON_VERSION_PATCH)
unset(_VERSION_STRING)
unset(_ACC_VER)
endif()
# clean up
unset(_VERSION)
unset(_PYTHON_VERSION_RESULT)
endif()

# cleanup
unset(_PYTHON_VERSIONS)
unset(_PYTHON_EXECUTABLE)

# handle the QUIETLY and REQUIRED arguments and set Python3MacFramework_FOUND to TRUE if
# all listed variables are TRUE
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Python3MacFramework REQUIRED_VARS Python3_EXECUTABLE
Python3_LIBRARIES
Python3_INCLUDE_DIRS
VERSION_VAR Python3_VERSION_STRING)
mark_as_advanced(Python3_EXECUTABLE)
mark_as_advanced(Python3_LIBRARIES)
mark_as_advanced(Python3_INCLUDE_DIRS)
92 changes: 92 additions & 0 deletions cmake_extras/FindPythonMacFramework.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#.rst:
# FindPythonMacFramework
# ----------------------
#
# Find first Python.framework found in Path
#
# This module finds if a Python.framework is installed and determines
# where the interpreter (executable) and libraries are.
# This code sets the following variables:
#
# ::
# PYTHONMACFRAMEWORK_FOUND
# PYTHON_EXECUTABLE - path to the Python interpreter
# PYTHON_VERSION_STRING - Python version found e.g. 3.13.2
# PYTHON_VERSION_MAJOR - Python major version found e.g. 3
# PYTHON_VERSION_MINOR - Python minor version found e.g. 13
# PYTHON_VERSION_PATCH - Python patch version found e.g. 2
# PYTHON_LIBRARIES - path to the python library
# PYTHON_INCLUDE_DIRS - path to where Python.h is found
# PYTHONLIBS_VERSION_STRING - version of the Python libs found

set(_PYTHON_VERSIONS 3.14 3.13 3.12 3.11 3.10 3.9)

find_program(PYTHON_EXECUTABLE python3 PATHS ENV PATH)

# determine python version string
if(PYTHON_EXECUTABLE)
execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c
"import sys; sys.stdout.write(';'.join([str(x) for x in sys.version_info[:3]]))"
OUTPUT_VARIABLE _VERSION
RESULT_VARIABLE _PYTHON_VERSION_RESULT
ERROR_QUIET)

if(NOT _PYTHON_VERSION_RESULT)
list(GET _VERSION 0 _PYTHON_VERSION_MAJOR)
list(GET _VERSION 1 _PYTHON_VERSION_MINOR)
list(GET _VERSION 2 _PYTHON_VERSION_PATCH)
list(JOIN _VERSION "." _PYTHON_VERSION_STRING)
set(_VERSION_STRING "${_PYTHON_VERSION_MAJOR}.${_PYTHON_VERSION_MINOR}")
unset(_VERSION)
unset(_PYTHON_VERSION_RESULT)
# it must be an acceptable version
list( FIND _PYTHON_VERSIONS ${_VERSION_STRING} _ACC_VER)
if ( ${_ACC_VER} GREATER -1 )
# determine if this is a Python.framework
# ie. /Library/Frameworks/Python.framework/Versions/3.13/bin/python3
# ie. /Users/kbhend/ndevpython/libraries/Frameworks/Python.framework/Versions/3.13/bin/python3

string(REPLACE "/" ";" _PATH_SEGMENTS ${PYTHON_EXECUTABLE})
set(_PATH_LIST ${_PATH_SEGMENTS} )
unset(_PATH_SEGMENTS)
# message("Excutable Path: " ${PYTHON_EXECUTABLE} )
list( FIND _PATH_LIST "Python.framework" _ISFWK )
if( ${_ISFWK} GREATER -1 )
# message("Found framework: " ${_ISFWK} )
set(PYTHON_VERSION_MAJOR ${_PYTHON_VERSION_MAJOR})
set(PYTHON_VERSION_MINOR ${_PYTHON_VERSION_MINOR})
set(PYTHON_VERSION_PATCH ${_PYTHON_VERSION_PATCH})
set(PYTHON_VERSION_STRING ${_PYTHON_VERSION_STRING})
list(POP_BACK _PATH_LIST) # remove python3
list(POP_BACK _PATH_LIST) # remove bin
list(POP_BACK _PATH_LIST _CURRENT_VERSION)
list(POP_BACK _PATH_LIST) # remove Versions
list(JOIN _PATH_LIST "/" PYTHON_FRAMEWORK_BASE)
# Remember to add back the leading "/" since these are absolute paths
set(PYTHON_LIBRARIES "/${PYTHON_FRAMEWORK_BASE}/Versions/${_CURRENT_VERSION}/lib/libpython${_VERSION_STRING}.dylib")
set(PYTHON_INCLUDE_DIRS "/${PYTHON_FRAMEWORK_BASE}/Versions/${_CURRENT_VERSION}/include/python${_VERSION_STRING}")
# message("PYTHON_LIBRARIES: " ${PYTHON_LIBRARIES})
# message("PYTHON_INCLUDE_DIRS: " ${PYTHON_INCLUDE_DIRS})
endif()
unset(_PYTHON_VERSION_MAJOR)
unset(_PYTHON_VERSION_MINOR)
unset(_PYTHON_VERSION_PATCH)
unset(_VERSION_STRING)
unset(_PATH_LIST)
endif()
endif()
endif()

# handle the QUIETLY and REQUIRED arguments and set PYTHONMACFRAMEWORK_FOUND to TRUE if
# all listed variables are TRUE
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(PythonMacFramework REQUIRED_VARS PYTHON_EXECUTABLE
PYTHON_VERSION_MAJOR
PYTHON_VERSION_MINOR
PYTHON_VERSION_PATCH
PYTHON_LIBRARIES
PYTHON_INCLUDE_DIRS
VERSION_VAR PYTHON_VERSION_STRING)
mark_as_advanced(PYTHON_EXECUTABLE)
mark_as_advanced(PYTHON_LIBRARIES)
mark_as_advanced(PYTHON_INCLUDE_DIRS)

0 comments on commit 0811550

Please sign in to comment.