feat: install release builds and report missing FFmpeg

Add make release and make install PREFIX=/Users/valdemar/.local. Keep optimized no-debug release artifacts separate; install adjacent CLI/app under lib/terebi and link bin/terebi. Preflight FFmpeg/pkg-config before compiling or installing, including cached builds.

Record the player's exact linked FFmpeg ABI paths. AppKit-only CLI/bootstrap validate native loadability and transitive dependencies before entering the FFmpeg-linked terebi-player. CLI fails with status 69 and remediation; Finder bootstrap shows a native alert and execs the player without losing initial events or process identity.

Verified staged installation with spaces, reinstall preservation, absent build dependencies, missing/corrupt/transitively broken dylibs, real modal error surface, installed CLI and Finder startup across exec, local/YouTube resume and window reuse, and full make check.
This commit is contained in:
2026-10-11 01:17:14 +02:00
parent 230949bb95
commit c6d2958744
6 changed files with 245 additions and 73 deletions
+34
View File
@@ -0,0 +1,34 @@
import Foundation
@main
struct FFmpegRuntimeChecks {
static func require(_ condition: Bool, _ message: String) {
if !condition { fatalError("FAIL: " + message) }
}
static func main() throws {
let realApp = URL(fileURLWithPath: CommandLine.arguments[1], isDirectory: true)
let directory = FileManager.default.temporaryDirectory.appendingPathComponent("terebi.ffmpeg-check.\(UUID().uuidString)", isDirectory: true)
let app = directory.appendingPathComponent("terebi.app", isDirectory: true)
let resources = app.appendingPathComponent("Contents/Resources", isDirectory: true)
try FileManager.default.createDirectory(at: resources, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: directory) }
let manifest = resources.appendingPathComponent("ffmpeg-libraries.txt")
let required = try String(contentsOf: realApp.appendingPathComponent("Contents/Resources/ffmpeg-libraries.txt"), encoding: .utf8)
.split(separator: "\n").map(String.init)
require(FFmpegRuntime.error(for: app) != nil, "missing dependency metadata blocks startup")
var unavailable = required
let family = URL(fileURLWithPath: required[0]).lastPathComponent.split(separator: ".")[0]
unavailable[0] = directory.appendingPathComponent("\(family).999999.dylib").path
try (unavailable.joined(separator: "\n") + "\n").write(to: manifest, atomically: true, encoding: .utf8)
require(FFmpegRuntime.error(for: app) != nil, "an unavailable ABI library blocks startup")
try Data("not a dynamic library".utf8).write(to: URL(fileURLWithPath: unavailable[0]))
require(FFmpegRuntime.error(for: app) != nil, "an existing but unloadable library also blocks startup")
try Array(repeating: required[0], count: 5).joined(separator: "\n").write(to: manifest, atomically: true, encoding: .utf8)
require(FFmpegRuntime.error(for: app) != nil, "duplicated entries cannot omit required library families")
try (required.joined(separator: "\n") + "\n").write(to: manifest, atomically: true, encoding: .utf8)
require(FFmpegRuntime.error(for: app) == nil, "all recorded native libraries load successfully")
print("FFmpeg runtime checks passed")
}
}