You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

215 lines
9.0 KiB

  1. # RefixupMacOS.cmake
  2. # Now that we don't use BundleUtilities and instead use GET_RUNTIME_DEPENDENCIES,
  3. # the binaries that are built all have absolute path library load commands.
  4. # What we need to do here is update all the paths for _the runtime dependencies
  5. # that we installed into the bundle_ to live in @rpath, with Python getting
  6. # special treatment in that it lives in @rpath/Frameworks.
  7. # Make sure GLOB_RECURSE doesn't follow symlinks
  8. cmake_policy( PUSH )
  9. cmake_policy( SET CMP0009 NEW )
  10. function( refix_kicad_bundle target )
  11. # target should be the path to the kicad.app directory
  12. string( TIMESTAMP start_time )
  13. cleanup_python( ${target} )
  14. file( GLOB_RECURSE items ${target}/*.dylib ${target}/*.so ${target}/*.kiface )
  15. foreach( item ${items} )
  16. message( "Refixing prereqs for '${item}'" )
  17. refix_prereqs( ${item} )
  18. endforeach( )
  19. # For binaries, we need to fix the prereqs and the rpaths
  20. file( GLOB subdirs ${target}/Contents/Applications/*.app )
  21. foreach( subdir ${subdirs} )
  22. file( GLOB binaries ${subdir}/Contents/MacOS/* )
  23. foreach( binary ${binaries} )
  24. message( "Refixing rpaths and prereqs for '${binary}'" )
  25. #refix_rpaths( ${binary} )
  26. refix_prereqs( ${binary} )
  27. endforeach( )
  28. endforeach( )
  29. file( GLOB pythonbinbinaries ${target}/Contents/Frameworks/Python.framework/Versions/3.*/bin/python3 )
  30. foreach( pythonbinbinary ${pythonbinbinaries} )
  31. message( "Refixing rpaths and prereqs for '${pythonbinbinary}'" )
  32. refix_rpaths( ${pythonbinbinary} )
  33. refix_prereqs( ${pythonbinbinary} )
  34. endforeach()
  35. file( GLOB pythonresbinaries ${target}/Contents/Frameworks/Python.framework/Versions/3.*/Resources/Python.app/Contents/MacOS/Python )
  36. foreach( pythonresbinary ${pythonresbinaries} )
  37. message( "Refixing rpaths and prereqs for '${pythonresbinary}'" )
  38. refix_rpaths( ${pythonresbinary} )
  39. refix_prereqs( ${pythonresbinary} )
  40. endforeach()
  41. file( GLOB binaries ${target}/Contents/MacOS/* )
  42. foreach( binary ${binaries} )
  43. message( "Refixing prereqs for '${binary}'" )
  44. refix_prereqs( ${binary} )
  45. endforeach( )
  46. message( "Removing Python pyc files" )
  47. file( GLOB_RECURSE pycs ${target}/*.pyc )
  48. file( REMOVE ${pycs} )
  49. string( TIMESTAMP end_time )
  50. # message( "Refixing start time: ${start_time}\nRefixing end time: ${end_time}" )
  51. endfunction( )
  52. function( cleanup_python bundle)
  53. # Remove extra Python
  54. file( REMOVE_RECURSE ${bundle}/Contents/MacOS/Python )
  55. file( GLOB extra_pythons LIST_DIRECTORIES true ${bundle}/Contents/Applications/*/Contents/MacOS/Python )
  56. if( NOT "${extra_pythons}" STREQUAL "" )
  57. message( "Removing extra Pythons copied into Contents/MacOS: ${extra_pythons}" )
  58. file( REMOVE_RECURSE ${extra_pythons} )
  59. endif()
  60. # Make sure Python's Current is a symlink to 3.x
  61. file( REMOVE_RECURSE ${bundle}/Contents/Frameworks/Python.framework/Versions/Current )
  62. file( GLOB python_version LIST_DIRECTORIES true RELATIVE ${bundle}/Contents/Frameworks/Python.framework/Versions ${bundle}/Contents/Frameworks/Python.framework/Versions/3* )
  63. execute_process( COMMAND ln -s ${python_version} ${bundle}/Contents/Frameworks/Python.framework/Versions/Current )
  64. endfunction()
  65. function( refix_rpaths binary )
  66. get_filename_component( executable_path ${binary} DIRECTORY )
  67. set( desired_rpaths )
  68. file( RELATIVE_PATH relative_kicad_framework_path ${executable_path} ${target}/Contents/Frameworks )
  69. string( REGEX REPLACE "/+$" "" relative_kicad_framework_path "${relative_kicad_framework_path}" ) # remove trailing slash
  70. file( RELATIVE_PATH relative_python_framework_path ${executable_path} ${target}/Contents/Frameworks/Python.framework )
  71. string( REGEX REPLACE "/+$" "" relative_python_framework_path "${relative_python_framework_path}" ) # remove trailing slash
  72. list( APPEND desired_rpaths "@executable_path/${relative_kicad_framework_path}" "@executable_path/${relative_python_framework_path}" )
  73. foreach( desired_rpath ${desired_rpaths} )
  74. execute_process(
  75. COMMAND install_name_tool -add_rpath ${desired_rpath} ${binary}
  76. RESULT_VARIABLE add_rpath_rv
  77. OUTPUT_VARIABLE add_rpath_ov
  78. ERROR_VARIABLE add_rpath_ev
  79. )
  80. if( NOT add_rpath_rv STREQUAL "0" )
  81. message( FATAL_ERROR "adding rpath failed: ${add_rpath_rv}\n${add_rpath_ev}" )
  82. endif( )
  83. endforeach( )
  84. endfunction( )
  85. function( refix_prereqs target )
  86. # file(GET_RUNTIME_DEPENDENCIES) does not seem to work properly on libraries, it returns empty
  87. # results. So, to figure out which ones we can remap to rpath, we make use of ${items}, which
  88. # happens to contain all the shared libs we found in the bundle. This is a big hack, because
  89. # we're not actually checking that these shared libs live *in* the rpath, but in practice it
  90. # should work. If this stops being the case, we can always add more logic...
  91. execute_process(
  92. COMMAND otool -L ${target}
  93. RESULT_VARIABLE gp_rv
  94. OUTPUT_VARIABLE gp_cmd_ov
  95. ERROR_VARIABLE gp_ev
  96. )
  97. if( NOT gp_rv STREQUAL "0" )
  98. message( FATAL_ERROR "otool failed: ${gp_rv}\n${gp_ev}" )
  99. else()
  100. message( DEBUG "otool -L ${target} returned: ${gp_cmd_ov}" )
  101. endif( )
  102. string( REPLACE ";" "\\;" candidates "${gp_cmd_ov}" )
  103. string( REPLACE "\n" "${eol_char};" candidates "${candidates}" )
  104. # check for install id and remove it from list, since otool -L can include a
  105. # reference to itself
  106. set( gp_install_id )
  107. execute_process(
  108. COMMAND otool -D ${target}
  109. RESULT_VARIABLE otool_rv
  110. OUTPUT_VARIABLE gp_install_id_ov
  111. ERROR_VARIABLE otool_ev
  112. )
  113. if( NOT otool_rv STREQUAL "0" )
  114. message( FATAL_ERROR "otool -D failed: ${otool_rv}\n${otool_ev}" )
  115. endif()
  116. # second line is install name
  117. string( REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}" )
  118. if( gp_install_id )
  119. # trim
  120. string( REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}" )
  121. endif( )
  122. set( changes "" )
  123. set( otool_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$" )
  124. foreach( candidate ${candidates} )
  125. if( "${candidate}" MATCHES "${gp_regex}" )
  126. string( REGEX REPLACE "${otool_regex}" "\\1" raw_prereq "${candidate}" )
  127. message( DEBUG "processing ${raw_prereq}")
  128. if( raw_prereq MATCHES "^@rpath.*" )
  129. message( DEBUG " already an rpath; skipping" )
  130. continue()
  131. endif()
  132. get_filename_component( prereq_name ${raw_prereq} NAME )
  133. message( DEBUG " prereq name: ${prereq_name}" )
  134. set( changed_prereq "" )
  135. foreach( item ${items} )
  136. get_filename_component( item_name ${item} NAME )
  137. if( "${item_name}" STREQUAL "${prereq_name}" )
  138. message( DEBUG " found match at ${item}" )
  139. if( item MATCHES "^.*/Contents/PlugIns/.*" )
  140. string( REGEX REPLACE "^.*/Contents/PlugIns/(.*)$"
  141. "@rpath/../PlugIns/\\1"
  142. changed_prereq
  143. "${item}" )
  144. else()
  145. set( changed_prereq "@rpath/${item_name}" )
  146. endif()
  147. endif()
  148. endforeach()
  149. if( "${changed_prereq}" STREQUAL "" )
  150. message( DEBUG " not found in items; assumed to be system lib" )
  151. continue()
  152. endif()
  153. # Because of the above continue()s, we know we changed the prereq if we're here
  154. if( raw_prereq STREQUAL gp_install_id )
  155. set( cmd install_name_tool -id ${changed_prereq} "${target}" )
  156. message( DEBUG " updating install id: ${cmd}" )
  157. execute_process( COMMAND ${cmd} RESULT_VARIABLE install_name_tool_result )
  158. if( NOT install_name_tool_result EQUAL 0 )
  159. string( REPLACE ";" "' '" msg "'${cmd}'" )
  160. message( FATAL_ERROR "Command failed setting install id:\n ${msg}" )
  161. endif( )
  162. continue( )
  163. endif( )
  164. if ( NOT raw_prereq STREQUAL changed_prereq )
  165. # we know we need to change this prereq
  166. set( changes ${changes} "-change" "${raw_prereq}" "${changed_prereq}" )
  167. endif( )
  168. endif( )
  169. endforeach( )
  170. if( changes )
  171. set( cmd install_name_tool ${changes} "${target}" )
  172. message( DEBUG "changing prereqs: ${changes}" )
  173. execute_process( COMMAND ${cmd} RESULT_VARIABLE install_name_tool_result )
  174. if( NOT install_name_tool_result EQUAL 0 )
  175. string( REPLACE ";" "' '" msg "'${cmd}'" )
  176. message( FATAL_ERROR "Command failed:\n ${msg}" )
  177. endif( )
  178. endif( )
  179. endfunction( )
  180. cmake_policy( POP )