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