Support Apple Silicon on macOS

As part of supporting Apple Silicon, we've got to upgrade our embedded
Python to a version that comes with an Apple Silicon build. Python 3.9
suffices. This means we ignore python3.9 while fixing up bundles.

Apple requires all code to be signed on Apple Silicon. We've added signing
to the build. This has to be run after anything that adds to or modifies
the installed files. As of Cmake 3.14 (CMP0082), the install rules are run
in the order declared, so we are able to do this just by adding the
signing subdirectory last in the main CMakeLists.txt. By default, the
build will be signed "ad hoc", which does not require a developer to
create keys or get keys from Apple. We added some CMake variables to
control signing, KICAD_OSX_CODESIGN and KICAD_OSX_SIGNING_*.

In order to better support development, we've added some necessary cleanup
steps to KiCad that were performed externally in the release and nightly
build process, like removing any .pyc files and extra Python symlinks
erroneously introduced by fixup_bundle. We also adjusted "refix_rpaths" to
be more accurate. We should not need "wrangle_bundle" when building and
installing a local development copy of KiCad.
This commit is contained in:
Adam Wolf 2022-11-16 15:24:29 -06:00 committed by Jon Evans
parent 91290e8f75
commit 98775afcba
5 changed files with 141 additions and 6 deletions

View File

@ -1059,3 +1059,24 @@ endif ( KICAD_INSTALL_DEMOS )
if( KICAD_BUILD_I18N ) if( KICAD_BUILD_I18N )
add_subdirectory( translation ) add_subdirectory( translation )
endif() endif()
if( APPLE )
set( KICAD_OSX_CODESIGN ON
CACHE BOOL "Sign KiCad.app on macOS" FORCE )
set( KICAD_OSX_SIGNING_ID "-"
CACHE STRING "macOS Signing ID, defaults to ad-hoc" FORCE )
set( KICAD_OSX_SIGNING_USE_SECURE_TIMESTAMP OFF
CACHE BOOL "When signing on macOS, add a secure timestamp" FORCE )
set( KICAD_OSX_SIGNING_USE_HARDENED_RUNTIME OFF
CACHE BOOL "When signing on macOS, use the Hardened Runtime" FORCE )
set( KICAD_OSX_SIGNING_ENTITLEMENTS_FILE ""
CACHE FILEPATH "Path to entitlements file for macOS signing" FORCE )
# We have to sign the app bundle after *all* the files are copied in the bundle
# otherwise the signature will be invalid.
# As of CMP0082 (CMake 3.14), the install rules are run in order, so we can just
# declare it last.
add_subdirectory( signing )
endif()

View File

@ -8,8 +8,6 @@
# This is not intended to make an install completely # This is not intended to make an install completely
# redistributable and relocatable. # redistributable and relocatable.
# TODO: Check style things
function( refix_kicad_bundle target ) function( refix_kicad_bundle target )
# target should be the path to the kicad.app directory # target should be the path to the kicad.app directory
@ -56,6 +54,10 @@ function( refix_kicad_bundle target )
refix_prereqs( ${binary} ) refix_prereqs( ${binary} )
endforeach( ) endforeach( )
message( "Removing Python pyc files" )
file( GLOB_RECURSE pycs ${target}/*.pyc )
file( REMOVE ${pycs} )
string( TIMESTAMP end_time ) string( TIMESTAMP end_time )
# message( "Refixing start time: ${start_time}\nRefixing end time: ${end_time}" ) # message( "Refixing start time: ${start_time}\nRefixing end time: ${end_time}" )
endfunction( ) endfunction( )
@ -63,6 +65,10 @@ endfunction( )
function( cleanup_python bundle) function( cleanup_python bundle)
# Remove extra Python # Remove extra Python
file( REMOVE_RECURSE ${bundle}/Contents/MacOS/Python ) file( REMOVE_RECURSE ${bundle}/Contents/MacOS/Python )
file( GLOB extra_pythons LIST_DIRECTORIES true ${bundle}/Contents/Applications/*/Contents/MacOS/Python )
message( "Removing extra Pythons copied into Contents/MacOS: ${extra_pythons}" )
file( REMOVE_RECURSE ${extra_pythons} )
# Make sure Python's Current is a symlink to 3.x # Make sure Python's Current is a symlink to 3.x
file( REMOVE_RECURSE ${bundle}/Contents/Frameworks/Python.framework/Versions/Current ) 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* ) file( GLOB python_version LIST_DIRECTORIES true RELATIVE ${bundle}/Contents/Frameworks/Python.framework/Versions ${bundle}/Contents/Frameworks/Python.framework/Versions/3* )
@ -135,7 +141,7 @@ function( refix_prereqs target )
if( "${candidate}" MATCHES "${gp_regex}" ) if( "${candidate}" MATCHES "${gp_regex}" )
string( REGEX REPLACE "${otool_regex}" "\\1" raw_prereq "${candidate}" ) string( REGEX REPLACE "${otool_regex}" "\\1" raw_prereq "${candidate}" )
if ( raw_prereq MATCHES "^@executable_path/\\.\\./\\.\\./.*/Contents/MacOS/Python$" ) if ( raw_prereq MATCHES "^@executable_path/\\.\\./\\.\\./Contents/MacOS/Python$" )
set( changed_prereq "@rpath/Versions/Current/Python" ) set( changed_prereq "@rpath/Versions/Current/Python" )
elseif ( raw_prereq MATCHES "^@executable_path/\\.\\./Frameworks/" ) elseif ( raw_prereq MATCHES "^@executable_path/\\.\\./Frameworks/" )
string( REPLACE "@executable_path/../Frameworks/" string( REPLACE "@executable_path/../Frameworks/"
@ -177,4 +183,4 @@ function( refix_prereqs target )
message( FATAL_ERROR "Command failed:\n ${msg}" ) message( FATAL_ERROR "Command failed:\n ${msg}" )
endif( ) endif( )
endif( ) endif( )
endfunction( ) endfunction( )

100
cmake/SignMacOS.cmake Normal file
View File

@ -0,0 +1,100 @@
function( sign_kicad_bundle target signing_id use_secure_timestamp use_hardened_runtime entitlements_file)
# If the signing ID wasn't passed in, use - which means adhoc signing
if ( NOT signing_id )
set( signing_id "-")
endif()
MESSAGE( STATUS "Signing ${target} with ${signing_id}, hardened runtime: ${use_hardened_runtime}, secure timestamp: ${use_secure_timestamp}, entitlements file: ${entitlements_file}" )
# --deep doesn't really work and is officially deprecated as of macos 13
# https://developer.apple.com/library/archive/technotes/tn2206/_index.html#//apple_ref/doc/uid/DTS40007919-CH1-TNTAG201
# collect a list of things to sign, in order
set( sign_list "${target}/Contents/Applications/eeschema.app/Contents/MacOS/eeschema"
"${target}/Contents/Applications/eeschema.app"
"${target}/Contents/Applications/gerbview.app/Contents/MacOS/gerbview"
"${target}/Contents/Applications/gerbview.app" "${target}/Contents/Applications/pcbnew.app/Contents/MacOS/pcbnew" "${target}/Contents/Applications/pcbnew.app" "${target}/Contents/Applications/bitmap2component.app/Contents/MacOS/bitmap2component" "${target}/Contents/Applications/bitmap2component.app" "${target}/Contents/Applications/pcb_calculator.app/Contents/MacOS/pcb_calculator" "${target}/Contents/Applications/pcb_calculator.app" "${target}/Contents/Applications/pl_editor.app/Contents/MacOS/pl_editor" "${target}/Contents/Applications/pl_editor.app")
# Python things!
if( EXISTS "${target}/Contents/Frameworks/Python.framework" )
set( sign_list ${sign_list} "${target}/Contents/Frameworks/Python.framework/Versions/Current/share/doc/python3.9/examples/Tools/pynche"
"${target}/Contents/Frameworks/Python.framework/Versions/Current/Resources/Python.app/Contents/MacOS/Python")
file( GLOB python_bins "${target}/Contents/Frameworks/Python.framework/Versions/Current/bin/*" )
# add dylib, .so and .a files from Contents/Frameworks/Python.framework/Versions/Current/lib/ and recursively
file( GLOB_RECURSE python_libs ${sign_list} "${target}/Contents/Frameworks/Python.framework/Versions/Current/lib/*.dylib"
"${target}/Contents/Frameworks/Python.framework/Versions/Current/lib/*.so"
"${target}/Contents/Frameworks/Python.framework/Versions/Current/lib/*.a"
"${target}/Contents/Frameworks/Python.framework/Versions/Current/lib/*.o" )
set( sign_list ${sign_list} ${python_bins} ${python_libs} )
endif( )
set( sign_list ${sign_list} "${target}/Contents/Frameworks/Python.framework/Versions/Current/Resources/Python.app"
"${target}/Contents/Frameworks/Python.framework" )
# add all the dylibs from contents/frameworks
file( GLOB framework_dylibs "${target}/Contents/Frameworks/*.dylib" )
# add all the files in Contents/PlugIns
file( GLOB_RECURSE plugins "${target}/Contents/PlugIns/*" )
file( GLOB_RECURSE translations "${target}/Contents/SharedSupport/internat/*.mo" )
# add all the files in Contents/MacOS/
# But we've gotta sign kicad-cli before signing kicad, at least on x86_64
set( kicad_bins "${target}/Contents/MacOS/dxf2idf"
"${target}/Contents/MacOS/idf2vrml"
"${target}/Contents/MacOS/idfcyl"
"${target}/Contents/MacOS/idfrect"
"${target}/Contents/MacOS/kicad-cli"
"${target}/Contents/MacOS/kicad")
set( sign_list ${sign_list} ${framework_dylibs} ${plugins} ${translations} ${kicad_bins} ) # do i need to quote this differently?
# add kicad.app!
set( sign_list ${sign_list} "${target}" )
# build the command used for signing
set( command codesign --force --sign "${signing_id}" )
if( use_secure_timestamp )
set( command ${command} --timestamp )
endif( )
if( use_hardened_runtime )
if ( signing_id STREQUAL "-" )
message( FATAL_ERROR "Hardened runtime requires a (non-ad-hoc) signing identity." )
endif( )
set( command ${command} --options runtime )
endif( )
if( entitlements_file )
set( command ${command} --entitlements "${entitlements_file}" )
endif( )
foreach( item ${sign_list} )
set( cmd ${command} "${item}" )
# MESSAGE( STATUS "Running ${cmd}")
execute_process( COMMAND ${cmd}
RESULT_VARIABLE codesign_result)
if( NOT codesign_result EQUAL 0 )
message( FATAL_ERROR "macOS signing failed; ran ${cmd}" )
endif( )
endforeach( )
endfunction()
function( verify_signing target )
set( cmd codesign --verify --deep --strict --verbose=3 "${target}" )
execute_process( COMMAND ${cmd} RESULT_VARIABLE verify_result )
if( NOT verify_result EQUAL 0 )
message( FATAL_ERROR "macOS signing verification failed; ran ${cmd}" )
endif( )
endfunction( )

View File

@ -248,7 +248,7 @@ if( APPLE )
fixup_bundle( ${OSX_BUNDLE_INSTALL_BIN_DIR}/kicad fixup_bundle( ${OSX_BUNDLE_INSTALL_BIN_DIR}/kicad
\"\${BUNDLE_FIX_LIBS}\" \"\${BUNDLE_FIX_LIBS}\"
\"\${BUNDLE_FIX_DIRS};${PYTHON_FRAMEWORK}\" \"\${BUNDLE_FIX_DIRS};${PYTHON_FRAMEWORK}\"
IGNORE_ITEM \"Python;python;python3;python3.8;pythonw;pythonw3;pythonw3.8\" IGNORE_ITEM \"Python;python;pythonw;python3;pythonw3;python3.8;pythonw3.8;python3.9;python3.9-intel64\"
) )
# BundleUtilities clobbers the rpaths and install_names that we carefully setup in Python.framework, even if # BundleUtilities clobbers the rpaths and install_names that we carefully setup in Python.framework, even if
@ -295,7 +295,7 @@ if( APPLE )
if ( ${PYTHON_FRAMEWORK_HELPER} ) if ( ${PYTHON_FRAMEWORK_HELPER} )
include( ${CMAKE_MODULE_PATH}/RefixupMacOS.cmake ) include( ${CMAKE_MODULE_PATH}/RefixupMacOS.cmake )
refix_kicad_bundle(${OSX_BUNDLE_INSTALL_DIR}) refix_kicad_bundle( ${OSX_BUNDLE_INSTALL_DIR} )
endif( ) endif( )
" COMPONENT Runtime " COMPONENT Runtime

8
signing/CMakeLists.txt Normal file
View File

@ -0,0 +1,8 @@
if (APPLE AND KICAD_OSX_CODESIGN )
install( CODE "
include( ${CMAKE_MODULE_PATH}/SignMacOS.cmake )
message( STATUS \"Signing bundles...\" )
sign_kicad_bundle( \"${OSX_BUNDLE_INSTALL_DIR}\" \"\${KICAD_OSX_SIGNING_ID}\" \"\${KICAD_OSX_SIGNING_USE_SECURE_TIMESTAMP}\" \"\${KICAD_OSX_SIGNING_USE_HARDENED_RUNTIME}\" \"\${KICAD_OSX_SIGNING_ENTITLEMENTS_FILE}\" )
" COMPONENT Runtime )
endif()