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.

186 lines
7.6 KiB

  1. # RefixupMacOS.cmake
  2. # Adjust rpaths and dependency information on macOS
  3. # after fixup_bundle.
  4. # Some of this comes from GetPrerequisites.cmake.
  5. # This is not intended to make an install completely
  6. # redistributable and relocatable.
  7. function( refix_kicad_bundle target )
  8. # target should be the path to the kicad.app directory
  9. string( TIMESTAMP start_time )
  10. cleanup_python( ${target} )
  11. file( GLOB_RECURSE items ${target}/*.dylib ${target}/*.so ${target}/*.kiface )
  12. foreach( item ${items} )
  13. message( "Refixing '${item}'" )
  14. refix_prereqs( ${item} )
  15. endforeach( )
  16. # For binaries, we need to fix the prereqs and the rpaths
  17. file( GLOB subdirs ${target}/Contents/Applications/*.app )
  18. foreach( subdir ${subdirs} )
  19. file( GLOB binaries ${subdir}/Contents/MacOS/* )
  20. foreach( binary ${binaries} )
  21. message( "Refixing '${binary}'" )
  22. refix_rpaths( ${binary} )
  23. refix_prereqs( ${binary} )
  24. endforeach( )
  25. endforeach( )
  26. file( GLOB pythonbinbinaries ${target}/Contents/Frameworks/Python.framework/Versions/3.*/bin/python3 )
  27. foreach( pythonbinbinary ${pythonbinbinaries} )
  28. message( "Refixing '${pythonbinbinary}'" )
  29. refix_rpaths( ${pythonbinbinary} )
  30. refix_prereqs( ${pythonbinbinary} )
  31. endforeach()
  32. file( GLOB pythonresbinaries ${target}/Contents/Frameworks/Python.framework/Versions/3.*/Resources/Python.app/Contents/MacOS/Python )
  33. foreach( pythonresbinary ${pythonresbinaries} )
  34. message( "Refixing '${pythonresbinary}'" )
  35. refix_rpaths( ${pythonresbinary} )
  36. refix_prereqs( ${pythonresbinary} )
  37. endforeach()
  38. file( GLOB binaries ${target}/Contents/MacOS/* )
  39. foreach( binary ${binaries} )
  40. message( "Refixing '${binary}'" )
  41. refix_rpaths( ${binary} )
  42. refix_prereqs( ${binary} )
  43. endforeach( )
  44. message( "Removing Python pyc files" )
  45. file( GLOB_RECURSE pycs ${target}/*.pyc )
  46. file( REMOVE ${pycs} )
  47. string( TIMESTAMP end_time )
  48. # message( "Refixing start time: ${start_time}\nRefixing end time: ${end_time}" )
  49. endfunction( )
  50. function( cleanup_python bundle)
  51. # Remove extra Python
  52. file( REMOVE_RECURSE ${bundle}/Contents/MacOS/Python )
  53. file( GLOB extra_pythons LIST_DIRECTORIES true ${bundle}/Contents/Applications/*/Contents/MacOS/Python )
  54. message( "Removing extra Pythons copied into Contents/MacOS: ${extra_pythons}" )
  55. file( REMOVE_RECURSE ${extra_pythons} )
  56. # Make sure Python's Current is a symlink to 3.x
  57. file( REMOVE_RECURSE ${bundle}/Contents/Frameworks/Python.framework/Versions/Current )
  58. file( GLOB python_version LIST_DIRECTORIES true RELATIVE ${bundle}/Contents/Frameworks/Python.framework/Versions ${bundle}/Contents/Frameworks/Python.framework/Versions/3* )
  59. execute_process( COMMAND ln -s ${python_version} ${bundle}/Contents/Frameworks/Python.framework/Versions/Current )
  60. endfunction()
  61. function( refix_rpaths binary )
  62. get_filename_component( executable_path ${binary} DIRECTORY )
  63. set( desired_rpaths )
  64. file( RELATIVE_PATH relative_kicad_framework_path ${executable_path} ${target}/Contents/Frameworks )
  65. string( REGEX REPLACE "/+$" "" relative_kicad_framework_path "${relative_kicad_framework_path}" ) # remove trailing slash
  66. file( RELATIVE_PATH relative_python_framework_path ${executable_path} ${target}/Contents/Frameworks/Python.framework )
  67. string( REGEX REPLACE "/+$" "" relative_python_framework_path "${relative_python_framework_path}" ) # remove trailing slash
  68. list( APPEND desired_rpaths "@executable_path/${relative_kicad_framework_path}" "@executable_path/${relative_python_framework_path}" )
  69. get_item_rpaths( ${binary} old_rpaths )
  70. foreach( desired_rpath ${desired_rpaths} )
  71. execute_process(
  72. COMMAND install_name_tool -add_rpath ${desired_rpath} ${binary}
  73. RESULT_VARIABLE add_rpath_rv
  74. OUTPUT_VARIABLE add_rpath_ov
  75. ERROR_VARIABLE add_rpath_ev
  76. )
  77. if( NOT add_rpath_rv STREQUAL "0" )
  78. message( FATAL_ERROR "adding rpath failed: ${add_rpath_rv}\n${add_rpath_ev}" )
  79. endif( )
  80. endforeach( )
  81. endfunction( )
  82. function( refix_prereqs target )
  83. # Replace '@executable_path/../Frameworks/' in dependencies with '@rpath/'
  84. execute_process(
  85. COMMAND otool -L ${target}
  86. RESULT_VARIABLE gp_rv
  87. OUTPUT_VARIABLE gp_cmd_ov
  88. ERROR_VARIABLE gp_ev
  89. )
  90. if( NOT gp_rv STREQUAL "0" )
  91. message( FATAL_ERROR "otool failed: ${gp_rv}\n${gp_ev}" )
  92. endif( )
  93. string( REPLACE ";" "\\;" candidates "${gp_cmd_ov}" )
  94. string( REPLACE "\n" "${eol_char};" candidates "${candidates}" )
  95. # check for install id and remove it from list, since otool -L can include a
  96. # reference to itself
  97. set( gp_install_id )
  98. execute_process(
  99. COMMAND otool -D ${target}
  100. RESULT_VARIABLE otool_rv
  101. OUTPUT_VARIABLE gp_install_id_ov
  102. ERROR_VARIABLE otool_ev
  103. )
  104. if( NOT otool_rv STREQUAL "0" )
  105. message( FATAL_ERROR "otool -D failed: ${otool_rv}\n${otool_ev}" )
  106. endif()
  107. # second line is install name
  108. string( REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}" )
  109. if( gp_install_id )
  110. # trim
  111. string( REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}" )
  112. endif( )
  113. set( changes "" )
  114. set( otool_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$" )
  115. foreach( candidate ${candidates} )
  116. if( "${candidate}" MATCHES "${gp_regex}" )
  117. string( REGEX REPLACE "${otool_regex}" "\\1" raw_prereq "${candidate}" )
  118. if ( raw_prereq MATCHES "^@executable_path/\\.\\./\\.\\./Contents/MacOS/Python$" )
  119. set( changed_prereq "@rpath/Versions/Current/Python" )
  120. elseif ( raw_prereq MATCHES "^@executable_path/\\.\\./Frameworks/" )
  121. string( REPLACE "@executable_path/../Frameworks/"
  122. "@rpath/" changed_prereq
  123. "${raw_prereq}" )
  124. elseif ( raw_prereq MATCHES "^@executable_path/\\.\\./PlugIns/" )
  125. string( REPLACE "@executable_path/../PlugIns/"
  126. "@rpath/../PlugIns/" changed_prereq
  127. "${raw_prereq}" )
  128. else( )
  129. continue( )
  130. endif( )
  131. # Because of the above continue( ) in the else, we know we changed the prereq if we're here
  132. if( raw_prereq STREQUAL gp_install_id )
  133. set( cmd install_name_tool -id ${changed_prereq} "${target}" )
  134. execute_process( COMMAND ${cmd} RESULT_VARIABLE install_name_tool_result )
  135. if( NOT install_name_tool_result EQUAL 0 )
  136. string( REPLACE ";" "' '" msg "'${cmd}'" )
  137. message( FATAL_ERROR "Command failed setting install id:\n ${msg}" )
  138. endif( )
  139. continue( )
  140. endif( )
  141. if ( NOT raw_prereq STREQUAL changed_prereq )
  142. # we know we need to change this prereq
  143. set( changes ${changes} "-change" "${raw_prereq}" "${changed_prereq}" )
  144. endif( )
  145. endif( )
  146. endforeach( )
  147. if( changes )
  148. set( cmd install_name_tool ${changes} "${target}" )
  149. execute_process( COMMAND ${cmd} RESULT_VARIABLE install_name_tool_result )
  150. if( NOT install_name_tool_result EQUAL 0 )
  151. string( REPLACE ";" "' '" msg "'${cmd}'" )
  152. message( FATAL_ERROR "Command failed:\n ${msg}" )
  153. endif( )
  154. endif( )
  155. endfunction( )