chore(cpn): improve SDL2 bundling and add SDL3 support for macOS builds (#7549)
Co-authored-by: Sergey Yugoman <sergey.yugoman@armenotech.com> Co-authored-by: Peter Feerick <5500713+pfeerick@users.noreply.github.com>
This commit is contained in:
co-authored by
Sergey Yugoman
Peter Feerick
parent
cffcbc42a8
commit
fcdf92d35b
@@ -26,4 +26,5 @@ _wasi_sdk_fetch
|
||||
/site/
|
||||
|
||||
output/
|
||||
*.dmg
|
||||
.devcontainer/devcontainer-lock.json
|
||||
|
||||
@@ -570,12 +570,109 @@ elseif(APPLE)
|
||||
# Run macdeployqt
|
||||
install(SCRIPT ${qt_deploy_companion} COMPONENT Runtime)
|
||||
|
||||
# Bundle SDL2
|
||||
# Bundle SDL2 (copy the real dylib; IMPORTED_RUNTIME_ARTIFACTS keeps
|
||||
# Homebrew symlinks and breaks the app inside a .dmg). Homebrew's
|
||||
# sdl2-compat also loads SDL3 from @loader_path/libSDL3.dylib at runtime.
|
||||
if(TARGET SDL2::SDL2)
|
||||
install(IMPORTED_RUNTIME_ARTIFACTS SDL2::SDL2
|
||||
FRAMEWORK DESTINATION "${companion_app_dir}/Contents/Frameworks"
|
||||
LIBRARY DESTINATION "${companion_app_dir}/Contents/Frameworks"
|
||||
COMPONENT Runtime)
|
||||
get_target_property(_sdl2_location SDL2::SDL2 IMPORTED_LOCATION)
|
||||
if(NOT _sdl2_location)
|
||||
get_target_property(_sdl2_location SDL2::SDL2 IMPORTED_LOCATION_RELEASE)
|
||||
endif()
|
||||
if(_sdl2_location AND _sdl2_location MATCHES "\\.framework(/|$)")
|
||||
# Genuine upstream SDL2.framework (CI's setup-sdl2.sh, or a manual framework
|
||||
# install): no SDL3 dlopen shim to worry about, and IMPORTED_RUNTIME_ARTIFACTS
|
||||
# copies frameworks correctly — it's only the Homebrew dylib case below whose
|
||||
# symlinks it mangles. This is what CI has shipped all along.
|
||||
message(STATUS "Bundling SDL2 framework: ${_sdl2_location}")
|
||||
install(IMPORTED_RUNTIME_ARTIFACTS SDL2::SDL2
|
||||
FRAMEWORK DESTINATION "${companion_app_dir}/Contents/Frameworks"
|
||||
LIBRARY DESTINATION "${companion_app_dir}/Contents/Frameworks"
|
||||
COMPONENT Runtime)
|
||||
elseif(_sdl2_location)
|
||||
message(STATUS "Bundling SDL2 library: ${_sdl2_location}")
|
||||
get_filename_component(_sdl2_real "${_sdl2_location}" REALPATH)
|
||||
get_filename_component(_sdl2_name "${_sdl2_location}" NAME)
|
||||
install(FILES "${_sdl2_real}"
|
||||
DESTINATION "${companion_app_dir}/Contents/Frameworks"
|
||||
RENAME "${_sdl2_name}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
set(_sdl3_real "")
|
||||
find_package(SDL3 CONFIG QUIET)
|
||||
if(TARGET SDL3::SDL3-shared)
|
||||
get_target_property(_sdl3_location SDL3::SDL3-shared IMPORTED_LOCATION_RELEASE)
|
||||
if(NOT _sdl3_location)
|
||||
get_target_property(_sdl3_location SDL3::SDL3-shared IMPORTED_LOCATION)
|
||||
endif()
|
||||
if(_sdl3_location)
|
||||
get_filename_component(_sdl3_real "${_sdl3_location}" REALPATH)
|
||||
endif()
|
||||
endif()
|
||||
if(NOT _sdl3_real)
|
||||
set(_sdl3_hints "")
|
||||
if(DEFINED ENV{HOMEBREW_PREFIX})
|
||||
list(APPEND _sdl3_hints
|
||||
"$ENV{HOMEBREW_PREFIX}/opt/sdl3/lib"
|
||||
"$ENV{HOMEBREW_PREFIX}/lib")
|
||||
endif()
|
||||
find_program(_brew_EXECUTABLE brew)
|
||||
if(_brew_EXECUTABLE)
|
||||
execute_process(
|
||||
COMMAND "${_brew_EXECUTABLE}" --prefix sdl3
|
||||
OUTPUT_VARIABLE _brew_sdl3_prefix
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
RESULT_VARIABLE _brew_sdl3_result
|
||||
)
|
||||
if(_brew_sdl3_result EQUAL 0 AND _brew_sdl3_prefix)
|
||||
list(APPEND _sdl3_hints "${_brew_sdl3_prefix}/lib")
|
||||
endif()
|
||||
endif()
|
||||
find_library(_sdl3_library
|
||||
NAMES SDL3.0 SDL3
|
||||
HINTS ${_sdl3_hints}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if(NOT _sdl3_library)
|
||||
find_library(_sdl3_library NAMES SDL3.0 SDL3)
|
||||
endif()
|
||||
if(_sdl3_library)
|
||||
get_filename_component(_sdl3_real "${_sdl3_library}" REALPATH)
|
||||
endif()
|
||||
endif()
|
||||
if(_sdl3_real)
|
||||
install(FILES "${_sdl3_real}"
|
||||
DESTINATION "${companion_app_dir}/Contents/Frameworks"
|
||||
RENAME "libSDL3.dylib"
|
||||
COMPONENT Runtime)
|
||||
else()
|
||||
message(WARNING "SDL3 not found; packaged Companion may crash if linked against sdl2-compat")
|
||||
endif()
|
||||
|
||||
install(CODE "
|
||||
set(_fw \"\${CMAKE_INSTALL_PREFIX}/${companion_app_dir}/Contents/Frameworks\")
|
||||
execute_process(
|
||||
COMMAND install_name_tool -id \"@executable_path/../Frameworks/${_sdl2_name}\" \"\${_fw}/${_sdl2_name}\"
|
||||
RESULT_VARIABLE _sdl2_id_result
|
||||
ERROR_VARIABLE _sdl2_id_error
|
||||
)
|
||||
if(NOT _sdl2_id_result EQUAL 0)
|
||||
message(FATAL_ERROR \"install_name_tool failed for ${_sdl2_name}: \${_sdl2_id_error}\")
|
||||
endif()
|
||||
if(EXISTS \"\${_fw}/libSDL3.dylib\")
|
||||
execute_process(
|
||||
COMMAND install_name_tool -id \"@executable_path/../Frameworks/libSDL3.dylib\" \"\${_fw}/libSDL3.dylib\"
|
||||
RESULT_VARIABLE _sdl3_id_result
|
||||
ERROR_VARIABLE _sdl3_id_error
|
||||
)
|
||||
if(NOT _sdl3_id_result EQUAL 0)
|
||||
message(FATAL_ERROR \"install_name_tool failed for libSDL3.dylib: \${_sdl3_id_error}\")
|
||||
endif()
|
||||
endif()
|
||||
" COMPONENT Runtime)
|
||||
else()
|
||||
message(WARNING "SDL2::SDL2 has no IMPORTED_LOCATION; SDL2 will not be bundled")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Re-sign bundle after all contents are installed
|
||||
|
||||
+102
-48
@@ -3,22 +3,26 @@
|
||||
# Install [Homebrew](https://brew.sh/)
|
||||
|
||||
- Run command in `Terminal`:
|
||||
```
|
||||
|
||||
```bash
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Installing Brew via the command above will automatically install the [Xcode Command Line Tools](https://mac.install.guide/commandlinetools/). If for some reason you need to do this manually, run `xcode-select —install` via the Terminal app.
|
||||
Installing Brew via the command above will automatically install the [Xcode Command Line Tools](https://mac.install.guide/commandlinetools/). If for some reason you need to do this manually, run `xcode-select --install` via the Terminal app.
|
||||
|
||||
# Install Qt 6
|
||||
|
||||
!!! note
|
||||
If you only intend on building the firmware, and not `simu`, `companion` or `simulator`, this is not necessary, and you can skip to the next step.
|
||||
|
||||
```
|
||||
```bash
|
||||
brew install qt@6
|
||||
```
|
||||
|
||||
Once Qt has been installed, you should set a couple environment variables (please modify according to the real installation paths):
|
||||
```
|
||||
|
||||
```bash
|
||||
export QTDIR=$(brew --prefix)/opt/qt@6
|
||||
export QT_PLUGIN_PATH=$QTDIR/plugins
|
||||
```
|
||||
@@ -29,97 +33,146 @@ Please note that `QT_PLUGIN_PATH` is required to be able to run Companion from y
|
||||
|
||||
Download and install the ARM GCC toolchain [from here](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) (installs in `/Applications/ArmGNUToolchain/`):
|
||||
|
||||
- For Intel Mac: https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-darwin-x86_64-arm-none-eabi.pkg
|
||||
- If Mac Silicon (i.e. M1-M5): https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-darwin-arm64-arm-none-eabi.pkg
|
||||
- Intel Mac: <https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-darwin-x86_64-arm-none-eabi.pkg>
|
||||
- Apple Silicon (M1–M5): <https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-darwin-arm64-arm-none-eabi.pkg>
|
||||
|
||||
Please note that this installation takes care of allowing the downloaded binaries to be run and prevents them being quarantined. If you choose to install the `tar.xz` archive to another location, you will have to take care of that yourself (see https://disable-gatekeeper.github.io/ for more details).
|
||||
If you install the `.tar.xz` archive manually instead of the `.pkg`, you may need to remove the macOS quarantine flag yourself. See <https://disable-gatekeeper.github.io/> for details.
|
||||
|
||||
# Install various dependencies
|
||||
# Other tools
|
||||
|
||||
- With `brew` in a `Terminal`:
|
||||
```
|
||||
brew install sdl cmake
|
||||
```bash
|
||||
brew install sdl2 sdl3 cmake uv
|
||||
```
|
||||
|
||||
!!! note
|
||||
Homebrew's `sdl2` package is [sdl2-compat](https://github.com/libsdl-org/sdl2-compat), which depends on **SDL3** at runtime. Both `sdl2` and `sdl3` must be installed before building Companion or a `.dmg`.
|
||||
|
||||
If you plan to run the standalone simulator for debugging:
|
||||
```
|
||||
brew install --cask quartz
|
||||
```
|
||||
|
||||
```bash
|
||||
brew install --cask xquartz
|
||||
```
|
||||
|
||||
# Download EdgeTX code
|
||||
|
||||
- Checkout code
|
||||
```
|
||||
|
||||
```bash
|
||||
git clone --recursive https://github.com/EdgeTX/edgetx.git
|
||||
```
|
||||
|
||||
- Switch into the source directory:
|
||||
```
|
||||
|
||||
```bash
|
||||
cd edgetx
|
||||
```
|
||||
|
||||
# Install Python dependencies
|
||||
|
||||
Since Python 3.11 enabled the "externally managed" flag, it is recommended that you use a virtual environment. [uv](https://docs.astral.sh/uv/getting-started/installation/) is one of the easiest tools to do and manage this, and can be installed with brew. It is recommended you create the virtual environment now rather than earlier in the process, as doing it now will create it in the edgetx directory.
|
||||
Since Python 3.11+, macOS uses an externally managed Python environment. Create a project virtual environment with [uv](https://docs.astral.sh/uv/getting-started/installation/):
|
||||
|
||||
- Install UV:
|
||||
```
|
||||
brew install uv
|
||||
```
|
||||
|
||||
- Create the virtual environment (and use specific version of python for this environment):
|
||||
```
|
||||
```bash
|
||||
uv venv --python 3.14
|
||||
source .venv/bin/activate
|
||||
uv pip install -r requirements.txt
|
||||
```
|
||||
|
||||
- Activate the virtual environment (you will need to run this whenever you want to compile in the future from a new terminal session):
|
||||
```
|
||||
Activate the virtual environment in every new terminal session before building:
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
- Install the packages:
|
||||
```
|
||||
uv pip install Pillow clang lz4 jinja2
|
||||
```
|
||||
# Configure the build
|
||||
|
||||
# Compile EdgeTX
|
||||
|
||||
- Create and enter build directory:
|
||||
```
|
||||
```bash
|
||||
mkdir -p build && cd build
|
||||
```
|
||||
|
||||
Configure build flags using `cmake` (in this case, for RadioMaster TX16S, [see here](https://github.com/EdgeTX/edgetx/blob/main/tools/build-common.sh) for other possible handset specific flags).
|
||||
```
|
||||
Configure for RadioMaster TX16S ([other radio flags](https://github.com/EdgeTX/edgetx/blob/main/tools/build-common.sh)):
|
||||
|
||||
```bash
|
||||
cmake -DPCB=X10 -DPCBREV=TX16S \
|
||||
-DCMAKE_PREFIX_PATH=$QTDIR \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \
|
||||
-DARM_TOOLCHAIN_DIR=/Applications/ArmGNUToolchain/14.2.Rel1/arm-none-eabi/bin/ ..
|
||||
```
|
||||
|
||||
!!! note
|
||||
Please note that the variables `CMAKE_PREFIX_PATH`, `ARM_TOOLCHAIN_DIR` must be specified additionally to what is described in the other compilation HowTos:
|
||||
| Variable | Purpose |
|
||||
|----------|---------|
|
||||
| `CMAKE_PREFIX_PATH` | Path to your Qt installation (`$QTDIR`) |
|
||||
| `CMAKE_OSX_DEPLOYMENT_TARGET` | Minimum macOS version for Companion. `14.0` (Sonoma) is a convenient local-dev default; release builds (CI) target `11.0` for broader compatibility — pass `-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0` if you want a build matching what CI ships. If omitted entirely, no `-mmacosx-version-min` flag is passed and clang targets whatever OS version your Mac is running |
|
||||
| `ARM_TOOLCHAIN_DIR` | Path to ARM GCC binaries. Must end with `/` |
|
||||
|
||||
- `CMAKE_PREFIX_PATH`: this must point to your Qt installation path.
|
||||
- `ARM_TOOLCHAIN_DIR`: this must point to where ARM GCC has been installed (and MUST contain `/` at the end).
|
||||
## Build firmware
|
||||
|
||||
Configure the compiler for firmware building (parallel limits the number of CPU cores used - you can increase this if your machine can handle more):
|
||||
```
|
||||
```bash
|
||||
cmake --build . --target arm-none-eabi-configure --parallel 4
|
||||
```
|
||||
|
||||
Build the firmware!
|
||||
```
|
||||
cmake --build . --target firmware
|
||||
```
|
||||
|
||||
The firmware binary is written to `build/arm-none-eabi/firmware.bin`.
|
||||
|
||||
## Build Companion
|
||||
|
||||
Firmware and Companion are separate targets. Companion requires Qt, SDL2, and SDL3 (see above).
|
||||
|
||||
From the `build` directory:
|
||||
|
||||
```bash
|
||||
source ../.venv/bin/activate
|
||||
export QTDIR=$(brew --prefix)/opt/qt@6
|
||||
export QT_PLUGIN_PATH=$QTDIR/plugins
|
||||
|
||||
cmake --build . --target native-configure --parallel 4
|
||||
cmake --build . --target wasi-module --parallel 4
|
||||
mkdir -p native/plugins
|
||||
cp wasm/edgetx-*-simulator.wasm native/plugins/
|
||||
cmake --build . --target companion --parallel 4
|
||||
```
|
||||
|
||||
The application bundle is created at `build/native/EdgeTX Companion 3.0.app` (the version number matches your EdgeTX release).
|
||||
|
||||
## Run Companion from the build directory
|
||||
|
||||
```bash
|
||||
export QT_PLUGIN_PATH=$(brew --prefix)/opt/qt@6/plugins
|
||||
open native/*.app
|
||||
```
|
||||
|
||||
For the radio simulator to work when running from the build tree, copy the WASM module into the bundle:
|
||||
|
||||
```bash
|
||||
cp wasm/edgetx-*-simulator.wasm native/*.app/Contents/MacOS/
|
||||
```
|
||||
|
||||
## Build a distributable `.dmg`
|
||||
|
||||
The `package` target lives in the `native` build tree, **not** in the top-level `build` directory.
|
||||
|
||||
```bash
|
||||
mkdir -p native/plugins
|
||||
cp wasm/edgetx-*-simulator.wasm native/plugins/ 2>/dev/null
|
||||
cmake --build native --target package
|
||||
```
|
||||
|
||||
The `.dmg` is written to `build/native/edgetx-companion-<version>.dmg` (the version number matches your EdgeTX release).
|
||||
|
||||
Open it and drag **EdgeTX Companion** into `/Applications`:
|
||||
|
||||
```bash
|
||||
open native/edgetx-companion-*.dmg
|
||||
```
|
||||
|
||||
The packaging step bundles Qt, SDL2, and SDL3 into the `.app` automatically.
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
## Notes on compiling simulator plug-ins
|
||||
|
||||
When compiling simulator plug-ins (using `cmake --build . --target libsimulator` with the target properly configured), the product of this compilation will be a `.dylib` stored in your build directory. If you want the Companion or Simulator apps to be able to use it, you will need to copy it manually into the respective directories. Here is how it should look with a couple of plug-ins copied:
|
||||
|
||||
```
|
||||
```bash
|
||||
% ls -l companion.app/Contents/MacOS/
|
||||
total 411976
|
||||
-rwxr-xr-x 1 etx staff 27908864 Jan 7 11:48 companion
|
||||
@@ -140,7 +193,8 @@ total 392616
|
||||
## Notes on possible error while trying to run build-companion.sh
|
||||
|
||||
If you encounter this error:
|
||||
```
|
||||
|
||||
```bash
|
||||
CPack: - Install project: EdgeTX []
|
||||
CMake Error at /opt/homebrew/Cellar/cmake/3.27.7/share/cmake/Modules/BundleUtilities.cmake:458 (message):
|
||||
otool -l failed: 1
|
||||
|
||||
Reference in New Issue
Block a user