Files
edgetx-gx12/companion/targets/mac/bundle_verify.cmake.in
T

133 lines
4.9 KiB
CMake

# Verify the installed Companion .app carries no references to libraries outside itself.
#
# Configured from companion/targets/mac/bundle_verify.cmake.in - do not edit the copy in the
# build tree.
#
# A .dmg that links against /opt/homebrew, /usr/local/Cellar or a build-machine Qt prefix
# starts fine on the machine that built it and fails everywhere else, which is exactly how the
# sdl2-compat breakage reached users. Catch it at install time instead.
#
# Disable with -DCOMPANION_VERIFY_BUNDLE=OFF; widen with -DCOMPANION_VERIFY_BUNDLE_ALLOW=<regex>.
set(_app "${CMAKE_INSTALL_PREFIX}/@companion_app_dir@")
set(_allow "@COMPANION_VERIFY_BUNDLE_ALLOW@")
set(_expect_sdl3 "@_bundle_expects_sdl3@")
if(NOT IS_DIRECTORY "${_app}")
message(FATAL_ERROR "Bundle not found for verification: ${_app}")
endif()
# sdl2-compat dlopens "@loader_path/libSDL3.dylib" - invisible to otool, so check by hand
if(_expect_sdl3 AND NOT EXISTS "${_app}/Contents/Frameworks/libSDL3.dylib")
message(FATAL_ERROR
"Contents/Frameworks/libSDL3.dylib is missing. Companion was linked against "
"sdl2-compat, which loads SDL3 at runtime, so the bundle will not start.")
endif()
file(GLOB_RECURSE _candidates "${_app}/Contents/*")
set(_offenders "")
set(_toonew "")
set(_checked 0)
set(_max_minos "0.0")
set(_target_minos "@CMAKE_OSX_DEPLOYMENT_TARGET@")
foreach(_file IN LISTS _candidates)
if(IS_SYMLINK "${_file}" OR IS_DIRECTORY "${_file}")
continue()
endif()
execute_process(
COMMAND otool -L "${_file}"
OUTPUT_VARIABLE _otool_out
ERROR_VARIABLE _otool_err
RESULT_VARIABLE _otool_res
)
# non-Mach-O files (plists, icons, translations) simply fail the scan - skip them
if(NOT _otool_res EQUAL 0 OR _otool_out MATCHES "is not an object file")
continue()
endif()
math(EXPR _checked "${_checked} + 1")
# macOS refuses to launch an app stamped above the running system - that is how 2.12.3
# became "requires macOS 15.0 or later" (issue #7732).
execute_process(
COMMAND otool -l "${_file}"
OUTPUT_VARIABLE _otool_l_out
ERROR_QUIET
RESULT_VARIABLE _otool_l_res
)
# MATCHALL, not MATCHES: a universal binary reports one minos per slice and any of them
# can be the one that is wrong.
if(_otool_l_res EQUAL 0)
string(REGEX MATCHALL "minos [0-9]+(\.[0-9]+)+" _minos_all "${_otool_l_out}")
foreach(_entry IN LISTS _minos_all)
string(REPLACE "minos " "" _minos "${_entry}")
if(_minos VERSION_GREATER _max_minos)
set(_max_minos "${_minos}")
endif()
if(_target_minos AND _minos VERSION_GREATER _target_minos)
file(RELATIVE_PATH _rel "${_app}" "${_file}")
list(APPEND _toonew "${_rel}: minos ${_minos}")
endif()
endforeach()
endif()
string(REPLACE "\n" ";" _lines "${_otool_out}")
foreach(_line IN LISTS _lines)
string(REGEX MATCH "^\t([^ ]+)" _matched "${_line}")
if(NOT _matched)
continue()
endif()
set(_ref "${CMAKE_MATCH_1}")
if(NOT _ref MATCHES "^/")
continue() # @rpath / @executable_path / @loader_path resolve inside the bundle
endif()
if(_ref MATCHES "^/usr/lib/" OR _ref MATCHES "^/System/")
continue() # shipped with macOS
endif()
if(_allow AND _ref MATCHES "${_allow}")
continue()
endif()
file(RELATIVE_PATH _rel "${_app}" "${_file}")
list(APPEND _offenders "${_rel}: ${_ref}")
endforeach()
endforeach()
if(_offenders)
list(REMOVE_DUPLICATES _offenders)
string(REPLACE ";" "\n " _report "${_offenders}")
message(FATAL_ERROR
"Bundle references libraries outside itself - this .dmg would not start on another "
"machine:\n ${_report}\n"
"Re-run with -DCOMPANION_VERIFY_BUNDLE=OFF to package anyway.")
endif()
if(_toonew)
list(REMOVE_DUPLICATES _toonew)
string(REPLACE ";" "\n " _report "${_toonew}")
message(FATAL_ERROR
"Bundle contains Mach-O files built for a newer macOS than the deployment target "
"(${_target_minos}) - macOS refuses to launch an app stamped above the running "
"system:\n ${_report}\n"
"Re-run with -DCOMPANION_VERIFY_BUNDLE=OFF to package anyway.")
endif()
# CPack discards message(STATUS) from install scripts, so echo via a subprocess - otherwise
# the bundle's real macOS floor never reaches the packaging log.
execute_process(COMMAND ${CMAKE_COMMAND} -E echo
"-- Bundle verification: ${_checked} Mach-O files, no external references, minimum macOS ${_max_minos}")
# The bundle seal is a separate concern from the per-Mach-O signatures: dyld does not consult
# CodeResources when loading, so a stale seal does not stop the app launching. It would matter
# for Gatekeeper/notarization, so report it - but do not fail a build over it.
execute_process(
COMMAND codesign --verify --deep --strict --verbose=2 "${_app}"
ERROR_VARIABLE _codesign_err
RESULT_VARIABLE _codesign_res
)
if(NOT _codesign_res EQUAL 0)
message(WARNING "Bundle signature is not valid (not fatal): ${_codesign_err}")
endif()