Fix Python loading on macOS without wrangle_bundle.
The goal of this work is to let developers run Python things on macOS on their builds without generating a full relocatable and redistributable build. This is a WIP.
This commit is contained in:
parent
1b94534451
commit
3100cd3599
|
@ -0,0 +1,180 @@
|
|||
# RefixupMacOS.cmake
|
||||
|
||||
# Adjust rpaths and dependency information on macOS
|
||||
# after fixup_bundle.
|
||||
|
||||
# Some of this comes from GetPrerequisites.cmake.
|
||||
|
||||
# This is not intended to make an install completely
|
||||
# redistributable and relocatable.
|
||||
|
||||
# TODO: Check style things
|
||||
|
||||
function( refix_kicad_bundle target )
|
||||
# target should be the path to the kicad.app directory
|
||||
|
||||
string( TIMESTAMP start_time )
|
||||
|
||||
cleanup_python( ${target} )
|
||||
|
||||
file( GLOB_RECURSE items ${target}/*.dylib ${target}/*.so ${target}/*.kiface )
|
||||
|
||||
foreach( item ${items} )
|
||||
message( "Refixing '${item}'" )
|
||||
refix_prereqs( ${item} )
|
||||
endforeach( )
|
||||
|
||||
# For binaries, we need to fix the prereqs and the rpaths
|
||||
file( GLOB subdirs ${target}/Contents/Applications/*.app )
|
||||
foreach( subdir ${subdirs} )
|
||||
file( GLOB binaries ${subdir}/Contents/MacOS/* )
|
||||
foreach( binary ${binaries} )
|
||||
message( "Refixing '${binary}'" )
|
||||
refix_rpaths( ${binary} )
|
||||
refix_prereqs( ${binary} )
|
||||
endforeach( )
|
||||
endforeach( )
|
||||
|
||||
file( GLOB pythonbinbinaries ${target}/Contents/Frameworks/Python.framework/Versions/3.*/bin/python3 )
|
||||
foreach( pythonbinbinary ${pythonbinbinaries} )
|
||||
message( "Refixing '${pythonbinbinary}'" )
|
||||
refix_rpaths( ${pythonbinbinary} )
|
||||
refix_prereqs( ${pythonbinbinary} )
|
||||
endforeach()
|
||||
|
||||
file( GLOB pythonresbinaries ${target}/Contents/Frameworks/Python.framework/Versions/3.*/Resources/Python.app/Contents/MacOS/Python )
|
||||
foreach( pythonresbinary ${pythonresbinaries} )
|
||||
message( "Refixing '${pythonresbinary}'" )
|
||||
refix_rpaths( ${pythonresbinary} )
|
||||
refix_prereqs( ${pythonresbinary} )
|
||||
endforeach()
|
||||
|
||||
file( GLOB binaries ${target}/Contents/MacOS/* )
|
||||
foreach( binary ${binaries} )
|
||||
message( "Refixing '${binary}'" )
|
||||
refix_rpaths( ${binary} )
|
||||
refix_prereqs( ${binary} )
|
||||
endforeach( )
|
||||
|
||||
string( TIMESTAMP end_time )
|
||||
# message( "Refixing start time: ${start_time}\nRefixing end time: ${end_time}" )
|
||||
endfunction( )
|
||||
|
||||
function( cleanup_python bundle)
|
||||
# Remove extra Python
|
||||
file( REMOVE_RECURSE ${bundle}/Contents/MacOS/Python )
|
||||
# Make sure Python's Current is a symlink to 3.x
|
||||
file( REMOVE_RECURSE ${bundle}/Contents/Frameworks/Python.framework/Versions/Current )
|
||||
file( GLOB python_version LIST_DIRECTORIES true RELATIVE ${bundle}/Contents/Frameworks/Python.framework/Versions ${bundle}/Contents/Frameworks/Python.framework/Versions/3* )
|
||||
execute_process( COMMAND ln -s ${python_version} ${bundle}/Contents/Frameworks/Python.framework/Versions/Current )
|
||||
endfunction()
|
||||
|
||||
function( refix_rpaths binary )
|
||||
get_filename_component( executable_path ${binary} DIRECTORY )
|
||||
|
||||
set( desired_rpaths )
|
||||
file( RELATIVE_PATH relative_kicad_framework_path ${executable_path} ${target}/Contents/Frameworks )
|
||||
string( REGEX REPLACE "/+$" "" relative_kicad_framework_path "${relative_kicad_framework_path}" ) # remove trailing slash
|
||||
file( RELATIVE_PATH relative_python_framework_path ${executable_path} ${target}/Contents/Frameworks/Python.framework )
|
||||
string( REGEX REPLACE "/+$" "" relative_python_framework_path "${relative_python_framework_path}" ) # remove trailing slash
|
||||
list( APPEND desired_rpaths "@executable_path/${relative_kicad_framework_path}" "@executable_path/${relative_python_framework_path}" )
|
||||
|
||||
get_item_rpaths( ${binary} old_rpaths )
|
||||
foreach( desired_rpath ${desired_rpaths} )
|
||||
execute_process(
|
||||
COMMAND install_name_tool -add_rpath ${desired_rpath} ${binary}
|
||||
RESULT_VARIABLE add_rpath_rv
|
||||
OUTPUT_VARIABLE add_rpath_ov
|
||||
ERROR_VARIABLE add_rpath_ev
|
||||
)
|
||||
if( NOT add_rpath_rv STREQUAL "0" )
|
||||
message( FATAL_ERROR "adding rpath failed: ${add_rpath_rv}\n${add_rpath_ev}" )
|
||||
endif( )
|
||||
endforeach( )
|
||||
endfunction( )
|
||||
|
||||
function( refix_prereqs target )
|
||||
# Replace '@executable_path/../Frameworks/' in dependencies with '@rpath/'
|
||||
execute_process(
|
||||
COMMAND otool -L ${target}
|
||||
RESULT_VARIABLE gp_rv
|
||||
OUTPUT_VARIABLE gp_cmd_ov
|
||||
ERROR_VARIABLE gp_ev
|
||||
)
|
||||
|
||||
if( NOT gp_rv STREQUAL "0" )
|
||||
message( FATAL_ERROR "otool failed: ${gp_rv}\n${gp_ev}" )
|
||||
endif( )
|
||||
|
||||
string( REPLACE ";" "\\;" candidates "${gp_cmd_ov}" )
|
||||
string( REPLACE "\n" "${eol_char};" candidates "${candidates}" )
|
||||
# check for install id and remove it from list, since otool -L can include a
|
||||
# reference to itself
|
||||
set( gp_install_id )
|
||||
execute_process(
|
||||
COMMAND otool -D ${target}
|
||||
RESULT_VARIABLE otool_rv
|
||||
OUTPUT_VARIABLE gp_install_id_ov
|
||||
ERROR_VARIABLE otool_ev
|
||||
)
|
||||
if( NOT otool_rv STREQUAL "0" )
|
||||
message( FATAL_ERROR "otool -D failed: ${otool_rv}\n${otool_ev}" )
|
||||
endif()
|
||||
# second line is install name
|
||||
string( REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}" )
|
||||
if( gp_install_id )
|
||||
# trim
|
||||
string( REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}" )
|
||||
endif( )
|
||||
|
||||
set( changes "" )
|
||||
|
||||
set( otool_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$" )
|
||||
|
||||
foreach( candidate ${candidates} )
|
||||
if( "${candidate}" MATCHES "${gp_regex}" )
|
||||
string( REGEX REPLACE "${otool_regex}" "\\1" raw_prereq "${candidate}" )
|
||||
|
||||
if ( raw_prereq MATCHES "^@executable_path/\\.\\./\\.\\./.*/Contents/MacOS/Python$" )
|
||||
set( changed_prereq "@rpath/Versions/Current/Python" )
|
||||
elseif ( raw_prereq MATCHES "^@executable_path/\\.\\./Frameworks/" )
|
||||
string( REPLACE "@executable_path/../Frameworks/"
|
||||
"@rpath/" changed_prereq
|
||||
"${raw_prereq}" )
|
||||
elseif ( raw_prereq MATCHES "^@executable_path/\\.\\./PlugIns/" )
|
||||
string( REPLACE "@executable_path/../PlugIns/"
|
||||
"@rpath/../PlugIns/" changed_prereq
|
||||
"${raw_prereq}" )
|
||||
else( )
|
||||
continue( )
|
||||
endif( )
|
||||
|
||||
# Because of the above continue( ) in the else, we know we changed the prereq if we're here
|
||||
|
||||
if( raw_prereq STREQUAL gp_install_id )
|
||||
set( cmd install_name_tool -id ${changed_prereq} "${target}" )
|
||||
execute_process( COMMAND ${cmd} RESULT_VARIABLE install_name_tool_result )
|
||||
if( NOT install_name_tool_result EQUAL 0 )
|
||||
string( REPLACE ";" "' '" msg "'${cmd}'" )
|
||||
message( FATAL_ERROR "Command failed setting install id:\n ${msg}" )
|
||||
endif( )
|
||||
continue( )
|
||||
endif( )
|
||||
|
||||
|
||||
if ( NOT raw_prereq STREQUAL changed_prereq )
|
||||
# we know we need to change this prereq
|
||||
set( changes ${changes} "-change" "${raw_prereq}" "${changed_prereq}" )
|
||||
endif( )
|
||||
endif( )
|
||||
endforeach( )
|
||||
|
||||
if( changes )
|
||||
set( cmd install_name_tool ${changes} "${target}" )
|
||||
execute_process( COMMAND ${cmd} RESULT_VARIABLE install_name_tool_result )
|
||||
if( NOT install_name_tool_result EQUAL 0 )
|
||||
string( REPLACE ";" "' '" msg "'${cmd}'" )
|
||||
message( FATAL_ERROR "Command failed:\n ${msg}" )
|
||||
endif( )
|
||||
endif( )
|
||||
endfunction( )
|
|
@ -142,11 +142,11 @@ if( APPLE )
|
|||
|
||||
# Find python if it is requested
|
||||
if( ${SCRIPTING_HELPER} )
|
||||
file( GLOB WXPYTHON_DIR RELATIVE ${OSX_BUNDLE_BUILD_DIR}/${OSX_BUNDLE_PYTHON_SITE_PACKAGES_DIR} ${OSX_BUNDLE_INSTALL_PYTHON_SITE_PACKAGES_DIR}/wx-?.?-osx_cocoa )
|
||||
file( GLOB PYTHON_SCRIPTING_SO ${OSX_BUNDLE_INSTALL_PYTHON_SITE_PACKAGES_DIR}/*.so )
|
||||
set( BUNDLE_FIX_LIBS \${BUNDLE_FIX_LIBS} \${PYTHON_SCRIPTING_SO} )
|
||||
file( GLOB PYTHON_SCRIPTING_SO ${OSX_BUNDLE_INSTALL_PYTHON_SITE_PACKAGES_DIR}/\${WXPYTHON_DIR}/wx/*.so )
|
||||
set( BUNDLE_FIX_LIBS \${BUNDLE_FIX_LIBS} \${PYTHON_SCRIPTING_SO} )
|
||||
# file( GLOB WXPYTHON_DIR RELATIVE ${OSX_BUNDLE_BUILD_DIR}/${OSX_BUNDLE_PYTHON_SITE_PACKAGES_DIR} ${OSX_BUNDLE_INSTALL_PYTHON_SITE_PACKAGES_DIR}/wx-?.?-osx_cocoa )
|
||||
# file( GLOB PYTHON_SCRIPTING_SO ${OSX_BUNDLE_INSTALL_PYTHON_SITE_PACKAGES_DIR}/*.so )
|
||||
# set( BUNDLE_FIX_LIBS \${BUNDLE_FIX_LIBS} \${PYTHON_SCRIPTING_SO} )
|
||||
# file( GLOB PYTHON_SCRIPTING_SO ${OSX_BUNDLE_INSTALL_PYTHON_SITE_PACKAGES_DIR}/\${WXPYTHON_DIR}/wx/*.so )
|
||||
# set( BUNDLE_FIX_LIBS \${BUNDLE_FIX_LIBS} \${PYTHON_SCRIPTING_SO} )
|
||||
endif()
|
||||
|
||||
# override default embedded path settings
|
||||
|
@ -156,7 +156,7 @@ if( APPLE )
|
|||
include( ${CMAKE_MODULE_PATH}/BundleUtilities.cmake )
|
||||
|
||||
if ( ${PYTHON_FRAMEWORK_HELPER} )
|
||||
# This idea here is to (eventually) repair anything that fixup_bundle doesn't handle
|
||||
# This idea here is to repair anything that fixup_bundle doesn't handle
|
||||
# properly for our setup with both Python.framework *and* symlinked subapps
|
||||
# that's needed for *running* here
|
||||
|
||||
|
@ -166,8 +166,7 @@ if( APPLE )
|
|||
# Of course, making it all work right here would be even slicker,
|
||||
# but if wishes were horses...
|
||||
|
||||
# At this point, the above is not true. The bottom comes from what we were doing in KiCad patches
|
||||
# in kicad-mac-builder for Python 2, but currently the effectual fixing for Python3 is happening in dyldstyle.
|
||||
# It would be awesome if we find a better solution (or BundleUtilities works for our corner case better)
|
||||
|
||||
execute_process( COMMAND cp -RP ${PYTHON_FRAMEWORK} ${OSX_BUNDLE_INSTALL_LIB_DIR}/)
|
||||
# We're using cp -RP because CMake's COPY_RESOLVED_BUNDLE... and COPY_DIRECTORY don't handle symlinks correctly
|
||||
|
@ -182,27 +181,8 @@ if( APPLE )
|
|||
IGNORE_ITEM \"Python;python;python3;python3.8;pythonw;pythonw3;pythonw3.8\"
|
||||
)
|
||||
|
||||
# BundleUtilities clobbers the rpaths and install_names that we carefully setup in Python.framework, even if we mark Python things as IGNORE_ITEMs.
|
||||
|
||||
# python/site-packages/_pcbnew.so
|
||||
execute_process( COMMAND install_name_tool -change @executable_path/../../Contents/MacOS/Python.framework/Versions/3.8/Python @rpath/Python.framework/Python ${OSX_BUNDLE_INSTALL_PYTHON_SITE_PACKAGES_DIR}/_pcbnew.so )
|
||||
execute_process( COMMAND install_name_tool -change @executable_path/../../Contents/MacOS/Python.framework/Python @rpath/Python.framework/Python ${OSX_BUNDLE_INSTALL_PYTHON_SITE_PACKAGES_DIR}/_pcbnew.so )
|
||||
execute_process( COMMAND install_name_tool -change @executable_path/../../Contents/MacOS/Python.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python @rpath/Python.framework/Python ${OSX_BUNDLE_INSTALL_PYTHON_SITE_PACKAGES_DIR}/_pcbnew.so )
|
||||
|
||||
# _pcbnew.kiface
|
||||
execute_process( COMMAND install_name_tool -change @executable_path/../../Contents/MacOS/Python @rpath/Python.framework/Python ${OSX_BUNDLE_INSTALL_KIFACE_DIR}/_pcbnew.kiface )
|
||||
execute_process( COMMAND install_name_tool -add_rpath @executable_path/../Frameworks ${OSX_BUNDLE_INSTALL_KIFACE_DIR}/_pcbnew.kiface )
|
||||
|
||||
# Python.framework/Versions/Current/bin/python3
|
||||
execute_process( COMMAND install_name_tool -change @executable_path/../../Contents/MacOS/Python.framework/Python @rpath/Python.framework/Python ${OSX_BUNDLE_INSTALL_LIB_DIR}/Python.framework/Versions/Current/bin/python3 )
|
||||
execute_process( COMMAND install_name_tool -add_rpath @executable_path/../../../../ ${OSX_BUNDLE_INSTALL_LIB_DIR}/Python.framework/Versions/Current/bin/python3 )
|
||||
|
||||
# Python.framework/Versions/Current/Resources/Python.app/Contents/MacOS/Python
|
||||
execute_process( COMMAND install_name_tool -change @executable_path/../../Contents/MacOS/Python.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python @rpath/Python.framework/Python ${OSX_BUNDLE_INSTALL_LIB_DIR}/Python.framework/Versions/Current/Resources/Python.app/Contents/MacOS/Python )
|
||||
execute_process( COMMAND install_name_tool -add_rpath @executable_path/../Frameworks ${OSX_BUNDLE_INSTALL_LIB_DIR}/Python.framework/Resources/Python.app/Contents/MacOS/Python )
|
||||
execute_process( COMMAND install_name_tool -add_rpath @executable_path/../../../../../../../ ${OSX_BUNDLE_INSTALL_LIB_DIR}/Python.framework/Resources/Python.app/Contents/MacOS/Python )
|
||||
execute_process( COMMAND install_name_tool -add_rpath @executable_path/../../../../../ ${OSX_BUNDLE_INSTALL_LIB_DIR}/Python.framework/Resources/Python.app/Contents/MacOS/Python )
|
||||
|
||||
# BundleUtilities clobbers the rpaths and install_names that we carefully setup in Python.framework, even if
|
||||
# we mark Python things as IGNORE_ITEMs. We'll refix them later.
|
||||
else()
|
||||
fixup_bundle( ${OSX_BUNDLE_INSTALL_BIN_DIR}/kicad
|
||||
\"\${BUNDLE_FIX_LIBS}\"
|
||||
|
@ -242,6 +222,12 @@ if( APPLE )
|
|||
move_to_main_bundle( \"pcb_calculator.app\" \"PCB Calculator.app\" )
|
||||
move_to_main_bundle( \"pcbnew.app\" \"Pcbnew.app\" )
|
||||
move_to_main_bundle( \"pl_editor.app\" \"Page Layout Editor.app\" )
|
||||
|
||||
if ( ${PYTHON_FRAMEWORK_HELPER} )
|
||||
include( ${CMAKE_MODULE_PATH}/RefixupMacOS.cmake )
|
||||
refix_kicad_bundle(${OSX_BUNDLE_INSTALL_DIR})
|
||||
endif( )
|
||||
|
||||
" COMPONENT Runtime
|
||||
)
|
||||
endif()
|
||||
|
|
Loading…
Reference in New Issue