build: make tvOS awg prebuilt explicit docs: make Apple TV build manual portable
9.8 KiB
AmneziaVPN Apple TV Build
This document describes how to build the current branch for Apple TV from the repository root.
The pipeline is:
- Use a separately built static Qt 6.9.2 for
tvOS. - Let Conan build/provide C/C++ dependencies.
- Generate an Xcode project with
qt-cmake. - Build the
.appand embedded Network Extension withxcodebuild.
Important:
- Run the project commands from the repository root.
- This is a device build for
appletvos, not a simulator build. xcodebuild buildproduces.app..ipais produced later viaarchiveand-exportArchive.- The current tvOS Network Extension scope is WireGuard-only.
- The temporary tvOS WireGuard bridge prebuilt is opt-in. The Conan recipe does not contain machine-specific fallback paths.
- Do not initialize or update submodules just for this build. If a clean checkout has empty
client/3rdfolders, passAMNEZIA_THIRDPARTY_ROOTto an already initialized read-onlyclient/3rdtree.
1. Environment
Set these paths for your machine:
export REPO_ROOT="$PWD"
export QT_DESKTOP_PREFIX="$HOME/Qt/6.9.2/macos"
export QT_TVOS_SRC="$HOME/Qt_tv/qt-6.9.2-tvos-src"
export QT_TVOS_PREFIX="$HOME/Qt_tv/6.9.2/tvos-device"
export BUILD_DIR="$REPO_ROOT/build-tvos-device-conan"
If this checkout does not have initialized client/3rd sources, point CMake at an initialized tree:
export AMNEZIA_THIRDPARTY_ROOT="/path/to/initialized/amnezia/client/3rd"
If you are using a temporary prebuilt tvOS WireGuard bridge, point Conan at it explicitly:
export AMNEZIA_TVOS_AWG_PREBUILT_DIR="/path/to/WireGuardKitGo-appletvos"
export AMNEZIA_TVOS_AWG_VERSION_HEADER_DIR="/path/to/directory/with/wireguard-go-version.h"
AMNEZIA_TVOS_AWG_PREBUILT_DIR must contain libwg-go.a.
AMNEZIA_TVOS_AWG_VERSION_HEADER_DIR is optional when wireguard-go-version.h lives in the same directory as libwg-go.a.
If the env vars are not set, the recipe uses the normal source build path. Rebuilding and publishing the tvOS WireGuard bridge through the registry is a separate task.
2. Required Local Tools
Conan must be available:
uv tool install conan
export PATH="$HOME/.local/bin:$PATH"
conan --version
Validated version:
Conan version 2.27.1
The build uses Xcode's AppleTVOS SDK:
xcrun --sdk appletvos --show-sdk-path
3. Prepare Qt Sources
Do not edit the installed Qt sources in place. Copy them into a separate tvOS fork:
mkdir -p "$HOME/Qt_tv"
rsync -a "$HOME/Qt/6.9.2/Src/" "$QT_TVOS_SRC/"
Recommended for reproducibility:
cd "$QT_TVOS_SRC"
git init
git add .
git commit -m "Qt 6.9.2 source snapshot"
4. Apply the Qt tvOS Patchset
Apply the local Qt tvOS patchset to $QT_TVOS_SRC.
If you need to recreate the patchset from a fresh copy, compare these files against $HOME/Qt/6.9.2/Src and reapply the same changes:
qtbase/cmake/QtBaseGlobalTargets.cmakeqtbase/cmake/QtBaseHelpers.cmakeqtbase/cmake/QtBuildPathsHelpers.cmakeqtbase/cmake/QtMkspecHelpers.cmakeqtbase/cmake/QtConfig.cmake.inqtbase/mkspecs/unsupported/macx-tvos-clang/qplatformdefs.hqtbase/src/corelib/CMakeLists.txtqtbase/src/corelib/platform/darwin/qdarwinpermissionplugin_location.mmqtbase/src/gui/CMakeLists.txtqtbase/src/widgets/CMakeLists.txtqtbase/src/network/kernel/qnetworkproxy_darwin.cppqtbase/src/testlib/qtestcrashhandler.cppqtbase/src/plugins/platforms/ios/qiosapplicationdelegate.mmqtbase/src/plugins/platforms/ios/qiosscreen.mmqtbase/src/plugins/platforms/ios/qiostheme.mmqtbase/src/plugins/platforms/ios/quiview.mmqtbase/src/plugins/platforms/ios/qiosclipboard.mm
Recommended after patching:
git -C "$QT_TVOS_SRC" diff > "$HOME/Qt_tv/qt-6.9.2-tvos.patch"
Do not use QT_APPLE_SDK=appletvos. The working path is CMAKE_SYSTEM_NAME=tvOS with CMAKE_OSX_SYSROOT=appletvos.
5. Build Qt 6.9.2 for Apple TV
Only the modules required by this project are built.
mkdir -p /private/tmp/qt6.9.2-tvos-device-build
cd /private/tmp/qt6.9.2-tvos-device-build
"$QT_TVOS_SRC/configure" \
-release -static -appstore-compliant \
-nomake tests -nomake examples \
-submodules qtbase,qtdeclarative,qtshadertools,qtremoteobjects,qtsvg,qt5compat,qttools \
-qt-host-path "$QT_DESKTOP_PREFIX" \
-prefix "$QT_TVOS_PREFIX" \
-- \
-G Ninja \
-DQT_QMAKE_TARGET_MKSPEC=macx-tvos-clang \
-DCMAKE_SYSTEM_NAME=tvOS \
-DCMAKE_OSX_SYSROOT=appletvos \
-DCMAKE_OSX_ARCHITECTURES=arm64 \
-DCMAKE_OSX_DEPLOYMENT_TARGET=17.0 \
-DBUILD_SHARED_LIBS=OFF \
-DQT_NO_APPLE_SDK_MAX_VERSION_CHECK=ON
cmake --build . --parallel 8
cmake --install .
Sanity checks:
"$QT_TVOS_PREFIX/bin/qt-cmake" --version
"$QT_TVOS_PREFIX/bin/qmake" -query QMAKE_XSPEC
Expected QMAKE_XSPEC:
macx-tvos-clang
Return to the repository root after building Qt:
cd "$REPO_ROOT"
6. Conan Dependency Behavior
For CMAKE_SYSTEM_NAME=tvOS, the project-level Conan graph is intentionally reduced:
- included:
awg-apple/2.0.1 - included:
libssh/0.11.3@amnezia - included:
openssl/3.6.1withno_apps=True - excluded:
openvpnadapter - excluded:
hev-socks5-tunnel
This keeps the current Apple TV target in the same practical scope as before: app plus WireGuard-only Network Extension.
libssh is built with WITH_EXEC=OFF on tvOS because tvOS does not provide fork() or execv().
7. Configure the Project
From the repository root:
cd "$REPO_ROOT"
"$QT_TVOS_PREFIX/bin/qt-cmake" \
-B"$BUILD_DIR" \
-GXcode \
-DQT_HOST_PATH="$QT_DESKTOP_PREFIX" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_SYSTEM_NAME=tvOS \
-DCMAKE_OSX_SYSROOT=appletvos
If you need to provide an external initialized client/3rd tree:
"$QT_TVOS_PREFIX/bin/qt-cmake" \
-B"$BUILD_DIR" \
-GXcode \
-DQT_HOST_PATH="$QT_DESKTOP_PREFIX" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_SYSTEM_NAME=tvOS \
-DCMAKE_OSX_SYSROOT=appletvos \
-DAMNEZIA_THIRDPARTY_ROOT="$AMNEZIA_THIRDPARTY_ROOT"
Expected non-fatal configure warnings:
Warning: plug-in QIOSIntegrationPlugin is not known to the current Qt installation.
Warning: plug-in QJpegPlugin is not known to the current Qt installation.
...
In this repo those warnings are tolerated because client/cmake/ios.cmake also links the static plugin targets explicitly when available.
8. Build the Apple TV App
xcodebuild -quiet \
-project "$BUILD_DIR/AmneziaVPN.xcodeproj" \
-scheme AmneziaVPN \
-configuration RelWithDebInfo \
-sdk appletvos \
CODE_SIGNING_ALLOWED=NO \
build
Outputs:
$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/PlugIns/AmneziaVPNNetworkExtension.appex
Verification:
file "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/AmneziaVPN"
file "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/PlugIns/AmneziaVPNNetworkExtension.appex/AmneziaVPNNetworkExtension"
lipo -info "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/AmneziaVPN"
lipo -info "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/PlugIns/AmneziaVPNNetworkExtension.appex/AmneziaVPNNetworkExtension"
Expected:
Mach-O 64-bit executable arm64
Non-fat file: ... is architecture: arm64
Useful plist checks:
plutil -p "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/Info.plist" | rg 'CFBundleIdentifier|DTPlatformName|UIDeviceFamily|MinimumOSVersion' -C 1
plutil -p "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/PlugIns/AmneziaVPNNetworkExtension.appex/Info.plist" | rg 'CFBundleIdentifier|NSExtension|DTPlatformName|MinimumOSVersion' -C 1
Expected:
DTPlatformName => appletvosUIDeviceFamily => 3MinimumOSVersion => 17.0- extension point
com.apple.networkextension.packet-tunnel
9. .app vs .ipa
This is the normal sequence:
xcodebuild build->.appxcodebuild archive->.xcarchivexcodebuild -exportArchive->.ipa
So seeing .app after a successful build is correct.
10. Optional Archive and Export
The commands below are the next step for packaging, but signing and provisioning must be configured first.
Archive:
xcodebuild \
-project "$BUILD_DIR/AmneziaVPN.xcodeproj" \
-scheme AmneziaVPN \
-configuration RelWithDebInfo \
-sdk appletvos \
-archivePath "$BUILD_DIR/AmneziaVPN-tvos.xcarchive" \
archive
Export:
xcodebuild -exportArchive \
-archivePath "$BUILD_DIR/AmneziaVPN-tvos.xcarchive" \
-exportPath "$BUILD_DIR/export-tvos" \
-exportOptionsPlist /absolute/path/to/ExportOptions.plist
The resulting .ipa should appear under:
$BUILD_DIR/export-tvos
11. Known Non-Fatal Warnings
The validated xcodebuild still prints warnings that do not break the build:
- missing Swift search path under the active Xcode Metal toolchain
SDKROOT[sdk=...]target-level warnings generated by Xcode project export- Swift conditional compilation flag warnings such as
GROUP_ID="..." - asset catalog warnings because the current icon set is still iOS-shaped, not a full tvOS Top Shelf asset set
- Go/WireGuard umbrella-header warnings from the temporary local
libwg-go.abridge - deprecated libssh SCP API warnings in existing app code
qt_import_plugins()warnings shown during configure
If the static platform plugin is not linked correctly, the typical failure is:
_OBJC_CLASS_$_QIOSApplicationDelegate_qt_main_wrapper
Those are cleanup tasks, not blockers for the current build proof.
12. Fast Rebuild Checklist
If everything is already built once:
- Reuse
$QT_TVOS_PREFIX - Reuse Conan cache under
$HOME/.conan2 - Reuse or pass an initialized
AMNEZIA_THIRDPARTY_ROOT - Re-run
qt-cmakeinto$BUILD_DIR - Re-run
xcodebuild -quiet ... build