build: bundle playback and YouTube dependencies

Build pinned LGPL playback libraries and package standalone YouTube helpers inside the signed native app. Keep the CLI inside the app and relocate private library links for installation without runtime Homebrew dependencies.

Record adaptive fixture completion in its EXIT trap: cancellation can exit with status 143 before the TERM trap writes its marker, while native pause and quality preservation remain correct.
This commit is contained in:
2026-10-12 01:17:22 +02:00
parent 8eba97579f
commit 31c4759c1d
13 changed files with 911 additions and 113 deletions
+42 -32
View File
@@ -1,9 +1,12 @@
CXX := xcrun clang++
SWIFT := xcrun swiftc
FFMPEG := libavformat libavcodec libavutil libswresample libswscale
FF_CFLAGS = $(shell pkg-config --cflags $(FFMPEG))
FF_LIBS = $(shell pkg-config --libs $(FFMPEG))
ARCH := $(shell uname -m)
DEPENDENCY_ROOT := $(abspath .build/dependencies/$(ARCH))
NATIVE_STAMP := $(DEPENDENCY_ROOT)/native.stamp
HELPER_STAMP := $(DEPENDENCY_ROOT)/helpers.stamp
FF_CFLAGS = $(shell PKG_CONFIG_LIBDIR="$(DEPENDENCY_ROOT)/ffmpeg/lib/pkgconfig" pkg-config --cflags $(FFMPEG))
FF_LIBS = $(shell PKG_CONFIG_LIBDIR="$(DEPENDENCY_ROOT)/ffmpeg/lib/pkgconfig" pkg-config --libs $(FFMPEG))
BUILD_DIR ?= .build
DEBUG_FLAGS ?= -g
PREFIX ?= $(HOME)/.local
@@ -20,14 +23,21 @@ CONFIG_FLAGS := -I $(CONFIG_BIN) -I $(CONFIG_BIN)/Modules $(CONFIG_LIBRARY)
APP := $(BUILD_DIR)/terebi.app
PLAYER := $(APP)/Contents/Frameworks/libterebi-player.dylib
BOOTSTRAP := $(APP)/Contents/MacOS/terebi
MANIFEST := $(APP)/Contents/Resources/ffmpeg-libraries.txt
CLI := $(APP)/Contents/MacOS/terebi-cli
BUNDLE_STAMP := $(BUILD_DIR)/bundle.stamp
SIGNING_IDENTITY ?= -
.PHONY: all release install check-dependencies run check check-config check-fixtures check-audio check-window check-youtube check-quality check-adaptive check-network check-progress check-reporting check-cli check-runtime check-media check-startup
all: check-dependencies $(BUILD_DIR)/terebi $(PLAYER) $(BOOTSTRAP) $(MANIFEST) $(APP)/Contents/Info.plist $(APP)/Contents/Resources/TOMLDecoder-LICENSE.txt
.PHONY: all release install check-dependencies run check check-config check-fixtures check-audio check-window check-youtube check-quality check-adaptive check-network check-progress check-reporting check-cli check-runtime check-media check-startup check-bundle
all: $(BUILD_DIR)/terebi
check-dependencies:
@command -v pkg-config >/dev/null 2>&1 || { printf '%s\n' 'Error: pkg-config is required to build terebi. Run: brew install ffmpeg pkg-config' >&2; exit 1; }
@pkg-config --exists $(FFMPEG) || { printf '%s\n' 'Error: FFmpeg development libraries are unavailable. Run: brew install ffmpeg pkg-config' 'If FFmpeg is already installed, check PKG_CONFIG_PATH, then rebuild/reinstall terebi.' >&2; exit 1; }
$(NATIVE_STAMP): Scripts/dependencies.py Dependencies.lock.json
python3 Scripts/dependencies.py --root "$(DEPENDENCY_ROOT)" --arch "$(ARCH)" --part native
$(HELPER_STAMP): Scripts/dependencies.py Dependencies.lock.json
python3 Scripts/dependencies.py --root "$(DEPENDENCY_ROOT)" --arch "$(ARCH)" --part helpers
check-dependencies: $(NATIVE_STAMP)
@PKG_CONFIG_LIBDIR="$(DEPENDENCY_ROOT)/ffmpeg/lib/pkgconfig" pkg-config --exists $(FFMPEG) || { printf '%s\n' 'Error: bundled FFmpeg development libraries are unavailable. Remove .build/dependencies and rebuild terebi.' >&2; exit 1; }
release:
$(MAKE) BUILD_DIR=.build/release DEBUG_FLAGS= all
@@ -36,9 +46,8 @@ install: release
mkdir -p "$(PREFIX)/lib/terebi" "$(PREFIX)/bin"
rm -f "$(PREFIX)/lib/terebi/terebi"
rm -rf "$(PREFIX)/lib/terebi/terebi.app"
cp ".build/release/terebi" "$(PREFIX)/lib/terebi/terebi"
cp -R ".build/release/terebi.app" "$(PREFIX)/lib/terebi/terebi.app"
ln -sfn "$$(cd "$(PREFIX)" && pwd)/lib/terebi/terebi" "$(PREFIX)/bin/terebi"
ln -sfn "$$(cd "$(PREFIX)" && pwd)/lib/terebi/terebi.app/Contents/MacOS/terebi-cli" "$(PREFIX)/bin/terebi"
$(BUILD_DIR):
mkdir -p "$@"
@@ -53,13 +62,13 @@ $(APP)/Contents/Resources/TOMLDecoder-LICENSE.txt: $(CONFIG_LIBRARY)
mkdir -p "$(APP)/Contents/Resources"
cp -f "$(CONFIG_BUILD)/checkouts/TOMLDecoder/LICENSE.md" "$@"
$(BUILD_DIR)/player.o: Sources/Core/player.cpp Sources/Core/player.h Sources/Core/player_macos.h Sources/Core/output.hpp Sources/Core/network_input.hpp Sources/Core/progress_reporter.hpp Makefile | $(BUILD_DIR) check-dependencies
$(BUILD_DIR)/player.o: Sources/Core/player.cpp Sources/Core/player.h Sources/Core/player_macos.h Sources/Core/output.hpp Sources/Core/network_input.hpp Sources/Core/progress_reporter.hpp Makefile $(NATIVE_STAMP) | $(BUILD_DIR) check-dependencies
$(CXX) $(CXXFLAGS) -c $< -o $@
$(BUILD_DIR)/progress_reporter.o: Sources/Core/progress_reporter.cpp Sources/Core/progress_reporter.hpp Sources/Core/player.h Makefile | $(BUILD_DIR)
$(BUILD_DIR)/progress_reporter.o: Sources/Core/progress_reporter.cpp Sources/Core/progress_reporter.hpp Sources/Core/player.h Makefile $(NATIVE_STAMP) | $(BUILD_DIR)
$(CXX) $(CXXFLAGS) -c $< -o $@
$(BUILD_DIR)/Output.o: Sources/macOS/Output.mm Sources/Core/output.hpp Makefile | $(BUILD_DIR) check-dependencies
$(BUILD_DIR)/Output.o: Sources/macOS/Output.mm Sources/Core/output.hpp Makefile $(NATIVE_STAMP) | $(BUILD_DIR) check-dependencies
$(CXX) $(CXXFLAGS) -fobjc-arc -c $< -o $@
$(PLAYER): $(SWIFT_SOURCES) $(OBJECTS) $(CONFIG_LIBRARY) Makefile | check-dependencies
@@ -67,15 +76,6 @@ $(PLAYER): $(SWIFT_SOURCES) $(OBJECTS) $(CONFIG_LIBRARY) Makefile | check-depend
rm -f "$(APP)/Contents/MacOS/terebi-player"
$(SWIFT) -target $(ARCH)-apple-macosx15.0 -swift-version 6 -parse-as-library -emit-library -O $(DEBUG_FLAGS) -import-objc-header Sources/Core/player_macos.h $(SWIFT_SOURCES) $(CONFIG_FLAGS) $(OBJECTS) $(FF_LIBS) $(FRAMEWORKS) -lc++ -Xlinker -install_name -Xlinker @rpath/libterebi-player.dylib -o $@
$(MANIFEST): $(PLAYER) Makefile
mkdir -p "$(APP)/Contents/Resources"
@otool -L "$<" | sed -nE 's#^[[:space:]]+(/.*/lib(avformat|avcodec|avutil|swresample|swscale)(\.[^/[:space:]]+)?\.dylib) \(.*#\1#p' > "$@.tmp"; \
if [ "$$(wc -l < "$@.tmp" | tr -d ' ')" != 5 ]; then \
rm -f "$@.tmp"; \
printf '%s\n' 'Error: could not record all five absolute FFmpeg dylib paths from the player library. Rebuild with Homebrew FFmpeg: brew install ffmpeg pkg-config' >&2; \
exit 1; \
fi; \
mv "$@.tmp" "$@"
$(BOOTSTRAP): Sources/App/Bootstrap.swift $(RUNTIME_SOURCE) Makefile | check-dependencies
mkdir -p "$(APP)/Contents/MacOS"
@@ -85,17 +85,24 @@ $(APP)/Contents/Info.plist: Resources/Info.plist
mkdir -p "$(APP)/Contents"
cp "$<" "$@"
$(BUILD_DIR)/terebi: $(CLI_SOURCES) $(PLAYER) $(BOOTSTRAP) $(MANIFEST) $(APP)/Contents/Info.plist Makefile | check-dependencies
rm -f "$@"
$(CLI): $(CLI_SOURCES) $(CONFIG_LIBRARY) Makefile
mkdir -p "$(APP)/Contents/MacOS"
$(SWIFT) -target $(ARCH)-apple-macosx15.0 -swift-version 6 -parse-as-library -O $(DEBUG_FLAGS) $(CLI_SOURCES) $(CONFIG_FLAGS) -framework AppKit -o $@
$(BUNDLE_STAMP): $(PLAYER) $(BOOTSTRAP) $(CLI) $(APP)/Contents/Info.plist $(APP)/Contents/Resources/TOMLDecoder-LICENSE.txt $(NATIVE_STAMP) $(HELPER_STAMP) Scripts/bundle.py
python3 Scripts/bundle.py --app "$(APP)" --dependencies "$(DEPENDENCY_ROOT)" --signing-identity "$(SIGNING_IDENTITY)"
touch "$@"
$(BUILD_DIR)/terebi: $(BUNDLE_STAMP)
ln -sfn "terebi.app/Contents/MacOS/terebi-cli" "$@"
run: all
open "$(APP)"
$(BUILD_DIR)/playback-check: Checks/playback.mm $(OBJECTS) Makefile | check-dependencies
$(CXX) $(CXXFLAGS) -fobjc-arc $< $(OBJECTS) $(FF_LIBS) $(FRAMEWORKS) -o $@
check: check-dependencies $(BUILD_DIR)/playback-check check-fixtures check-audio check-window check-config check-youtube check-quality check-adaptive check-network check-progress check-reporting check-cli check-runtime check-media check-startup
check: check-dependencies $(BUILD_DIR)/playback-check check-fixtures check-audio check-window check-config check-youtube check-quality check-adaptive check-network check-progress check-reporting check-cli check-runtime check-media check-startup check-bundle
$(BUILD_DIR)/playback-check $(BUILD_DIR)/fixtures
check-fixtures: | check-dependencies
@@ -109,7 +116,7 @@ check-fixtures: | check-dependencies
ffmpeg -hide_banner -loglevel error -y -f lavfi -i testsrc2=size=160x90:rate=12 -t 1 -vf 'format=yuv420p10le,setparams=color_primaries=bt2020:color_trc=smpte2084:colorspace=bt2020nc' -c:v ffv1 $(BUILD_DIR)/fixtures/hdr.mkv
ffmpeg -hide_banner -loglevel error -y -f lavfi -i testsrc2=size=320x180:rate=24 -f lavfi -i sine=frequency=440:sample_rate=44100 -t 120 -c:v mpeg4 -q:v 2 -g 24 -bf 2 -c:a aac -movflags +faststart $(BUILD_DIR)/fixtures/cache.mp4
$(BUILD_DIR)/Output-check.o: Sources/macOS/Output.mm Sources/Core/output.hpp Makefile | $(BUILD_DIR) check-dependencies
$(BUILD_DIR)/Output-check.o: Sources/macOS/Output.mm Sources/Core/output.hpp Makefile $(NATIVE_STAMP) | $(BUILD_DIR) check-dependencies
$(CXX) $(CXXFLAGS) -fobjc-arc -DPLAYER_AUDIO_CHECK -c $< -o $@
$(BUILD_DIR)/audio-continuity-check: Checks/audio-continuity.cpp $(BUILD_DIR)/player.o $(BUILD_DIR)/progress_reporter.o $(BUILD_DIR)/Output-check.o Makefile | check-dependencies
@@ -144,8 +151,8 @@ check-config: $(BUILD_DIR)/configuration-check
$(BUILD_DIR)/youtube-resolver-check: Checks/youtube-resolver.swift Sources/App/YouTubeResolver.swift Makefile | $(BUILD_DIR) check-dependencies
$(SWIFT) -target $(ARCH)-apple-macosx15.0 -swift-version 6 -parse-as-library -O $(DEBUG_FLAGS) Checks/youtube-resolver.swift Sources/App/YouTubeResolver.swift -framework Foundation -o $@
check-youtube: $(BUILD_DIR)/youtube-resolver-check
$(BUILD_DIR)/youtube-resolver-check
check-youtube: $(BUILD_DIR)/youtube-resolver-check $(BUNDLE_STAMP)
$(BUILD_DIR)/youtube-resolver-check "$(abspath $(APP))"
$(BUILD_DIR)/automatic-quality-check: Checks/automatic-quality.swift Sources/App/AutomaticQuality.swift Makefile | $(BUILD_DIR) check-dependencies
$(SWIFT) -target $(ARCH)-apple-macosx15.0 -swift-version 6 -parse-as-library -O $(DEBUG_FLAGS) Checks/automatic-quality.swift Sources/App/AutomaticQuality.swift -framework Foundation -o $@
@@ -156,11 +163,11 @@ check-quality: $(BUILD_DIR)/automatic-quality-check
$(BUILD_DIR)/automatic-quality-playback-check: Checks/automatic-quality-playback.swift $(SWIFT_SOURCES) $(OBJECTS) $(CONFIG_LIBRARY) Makefile | check-dependencies
$(SWIFT) -target $(ARCH)-apple-macosx15.0 -swift-version 6 -parse-as-library -DPLAYER_CHECK -O $(DEBUG_FLAGS) -import-objc-header Sources/Core/player_macos.h $< $(SWIFT_SOURCES) $(CONFIG_FLAGS) $(OBJECTS) $(FF_LIBS) $(FRAMEWORKS) -lc++ -o $@
check-adaptive: $(BUILD_DIR)/automatic-quality-playback-check
check-adaptive: $(BUILD_DIR)/automatic-quality-playback-check $(BUNDLE_STAMP)
mkdir -p $(BUILD_DIR)/fixtures
ffmpeg -hide_banner -loglevel error -y -f lavfi -i color=c=blue:size=1920x1080:rate=24 -f lavfi -i sine=frequency=440:sample_rate=44100 -t 120 -c:v mpeg4 -g 24 -bf 2 -c:a aac -movflags +faststart $(BUILD_DIR)/fixtures/quality-1080.mp4
ffmpeg -hide_banner -loglevel error -y -f lavfi -i color=c=green:size=1280x720:rate=24 -f lavfi -i sine=frequency=440:sample_rate=44100 -t 120 -c:v mpeg4 -g 24 -bf 2 -c:a aac -movflags +faststart $(BUILD_DIR)/fixtures/quality-720.mp4
python3 Checks/automatic-quality.py $(BUILD_DIR)/automatic-quality-playback-check $(BUILD_DIR)/fixtures
python3 Checks/automatic-quality.py $(BUILD_DIR)/automatic-quality-playback-check $(BUILD_DIR)/fixtures "$(APP)"
$(BUILD_DIR)/network-playback-check: Checks/network-playback.mm $(OBJECTS) Makefile | check-dependencies
$(CXX) $(CXXFLAGS) -fobjc-arc $< $(OBJECTS) $(FF_LIBS) $(FRAMEWORKS) -o $@
@@ -193,7 +200,7 @@ check-cli: $(BUILD_DIR)/player-command-check
$(BUILD_DIR)/ffmpeg-runtime-check: Checks/ffmpeg-runtime.swift $(RUNTIME_SOURCE) Makefile | $(BUILD_DIR)
$(SWIFT) -target $(ARCH)-apple-macosx15.0 -swift-version 6 -parse-as-library -O $(DEBUG_FLAGS) Checks/ffmpeg-runtime.swift $(RUNTIME_SOURCE) -framework Foundation -o $@
check-runtime: $(BUILD_DIR)/ffmpeg-runtime-check $(MANIFEST)
check-runtime: $(BUILD_DIR)/ffmpeg-runtime-check $(BUNDLE_STAMP)
$(BUILD_DIR)/ffmpeg-runtime-check "$(APP)"
$(BUILD_DIR)/system-playback-check: Checks/system-playback.swift $(SWIFT_SOURCES) $(OBJECTS) $(CONFIG_LIBRARY) Makefile | check-dependencies
@@ -206,4 +213,7 @@ $(BUILD_DIR)/app-startup-check.dylib: Checks/app-startup.swift $(SWIFT_SOURCES)
$(SWIFT) -target $(ARCH)-apple-macosx15.0 -swift-version 6 -parse-as-library -emit-library -DPLAYER_CHECK -O $(DEBUG_FLAGS) -import-objc-header Sources/Core/player_macos.h Checks/app-startup.swift $(SWIFT_SOURCES) $(CONFIG_FLAGS) $(OBJECTS) $(FF_LIBS) $(FRAMEWORKS) -lc++ -o $@
check-startup: all $(BUILD_DIR)/app-startup-check.dylib check-fixtures
python3 Checks/app-startup.py "$(APP)" "$(BUILD_DIR)/app-startup-check.dylib" "$(BUILD_DIR)/fixtures"
python3 Checks/app-startup.py "$(APP)" "$(BUILD_DIR)/app-startup-check.dylib" "$(BUILD_DIR)/fixtures" "$(DEPENDENCY_ROOT)"
check-bundle: all check-fixtures
python3 Checks/bundle.py "$(APP)" "$(BUILD_DIR)/fixtures" "$(DEPENDENCY_ROOT)"