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.

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