diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0655d8c0d..3b5fbf706 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -61,6 +61,7 @@ if(EdgeTX_SUPERBUILD)
ExternalProject_Add(native
SOURCE_DIR ${CMAKE_SOURCE_DIR}
BINARY_DIR ${CMAKE_BINARY_DIR}/native
+ LIST_SEPARATOR |
CMAKE_ARGS ${CMAKE_ARGS} -Wno-dev -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
CMAKE_CACHE_ARGS
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_SOURCE_DIR}/cmake/toolchain/native.cmake
@@ -75,6 +76,7 @@ if(EdgeTX_SUPERBUILD)
ExternalProject_Add(arm-none-eabi
SOURCE_DIR ${CMAKE_SOURCE_DIR}
BINARY_DIR ${CMAKE_BINARY_DIR}/arm-none-eabi
+ LIST_SEPARATOR |
CMAKE_ARGS ${CMAKE_ARGS} -Wno-dev -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
CMAKE_CACHE_ARGS
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_SOURCE_DIR}/cmake/toolchain/arm-none-eabi.cmake
diff --git a/cmake/Macros.cmake b/cmake/Macros.cmake
index 6f6d2978e..b5228f362 100644
--- a/cmake/Macros.cmake
+++ b/cmake/Macros.cmake
@@ -171,7 +171,10 @@ function(CollectCommandLineArgs out_var)
else()
set(_type :${_type})
endif()
- list(APPEND _args "-D${_var}${_type}=${${_var}}")
+ # Escape CMake's ';' so a multi-path value (CMAKE_PREFIX_PATH) stays one argument
+ # instead of splitting; callers pair this with LIST_SEPARATOR | to restore it.
+ string(REPLACE ";" "|" _value "${${_var}}")
+ list(APPEND _args "-D${_var}${_type}=${_value}")
endif()
endforeach()
set(${out_var} ${_args} PARENT_SCOPE)
diff --git a/cmake/wasm/CMakeLists.txt b/cmake/wasm/CMakeLists.txt
index 1562abe25..18b6ff6c5 100644
--- a/cmake/wasm/CMakeLists.txt
+++ b/cmake/wasm/CMakeLists.txt
@@ -25,6 +25,7 @@ include(ExternalProject)
ExternalProject_Add(wasm
SOURCE_DIR ${EDGETX_SOURCE_DIR}
BINARY_DIR ${CMAKE_BINARY_DIR}/wasm-build
+ LIST_SEPARATOR |
CMAKE_ARGS
${_forwarded_args}
-Wno-dev
diff --git a/companion/targets/mac/MacOSXBundleInfo.plist.in b/companion/targets/mac/MacOSXBundleInfo.plist.in
index 820532155..94fa7dfe2 100644
--- a/companion/targets/mac/MacOSXBundleInfo.plist.in
+++ b/companion/targets/mac/MacOSXBundleInfo.plist.in
@@ -34,6 +34,8 @@
+ LSMinimumSystemVersion
+ ${CMAKE_OSX_DEPLOYMENT_TARGET}
NSPrincipalClass
NSApplication
NSHighResolutionCapable
diff --git a/companion/targets/mac/bundle_verify.cmake.in b/companion/targets/mac/bundle_verify.cmake.in
index 8b0102937..63825f37e 100644
--- a/companion/targets/mac/bundle_verify.cmake.in
+++ b/companion/targets/mac/bundle_verify.cmake.in
@@ -27,7 +27,10 @@ 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()
@@ -45,6 +48,30 @@ foreach(_file IN LISTS _candidates)
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}")
@@ -77,7 +104,20 @@ if(_offenders)
"Re-run with -DCOMPANION_VERIFY_BUNDLE=OFF to package anyway.")
endif()
-message(STATUS "Bundle verification: ${_checked} Mach-O files, no external references")
+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
diff --git a/tools/build-companion.sh b/tools/build-companion.sh
index eda29d309..1bd5485d3 100755
--- a/tools/build-companion.sh
+++ b/tools/build-companion.sh
@@ -30,8 +30,9 @@ else
fi
COMMON_OPTIONS="${COMMON_OPTIONS} -DCMAKE_BUILD_TYPE=Release -DCMAKE_MESSAGE_LOG_LEVEL=WARNING -Wno-dev"
+# Qt 6.9's frameworks are macOS 12, so the bundle cannot start below that anyway.
if [ "$(uname)" = "Darwin" ]; then
- COMMON_OPTIONS="${COMMON_OPTIONS} -DCMAKE_OSX_DEPLOYMENT_TARGET='11.0'"
+ COMMON_OPTIONS="${COMMON_OPTIONS} -DCMAKE_OSX_DEPLOYMENT_TARGET='12.0'"
fi
# find_package(... CONFIG) skips the lib/cmake/ search pattern for prefixes that only