Skip to content

Commit d5fcf43

Browse files
feat(build): statically link TagLib, eliminate Homebrew runtime dependency (task-259)
Build TagLib 2.0.2 from source as static libraries and link into the binary, removing the runtime dependency on /opt/homebrew/opt/taglib/. - Add scripts/build-taglib.sh to download and build TagLib static libs - Change zig-core/build.zig from pkg-config to vendor include/lib paths - Change build.rs from pkg-config to static link directives with auto-trigger of build script when vendor libs are missing - Remove pkg-config build dependency from mt-core - Remove disable-library-validation entitlement from Entitlements.plist - Update CI action to build TagLib from source instead of brew install - Add zig:build:taglib task to Taskfile - Add vendor/ to .gitignore (built per-platform, not committed) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 718b843 commit d5fcf43

14 files changed

Lines changed: 243 additions & 52 deletions

File tree

.github/actions/setup-tauri-build/action.yml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: 'Setup Tauri Build Environment'
2-
description: 'Install Node.js, Rust, Zig, TagLib, and frontend deps for Tauri builds'
2+
description: 'Install Node.js, Rust, Zig, TagLib (static), and frontend deps for Tauri builds'
33

44
inputs:
55
node-version:
@@ -29,12 +29,9 @@ runs:
2929
with:
3030
version: ${{ inputs.zig-version }}
3131

32-
- name: Install TagLib
32+
- name: Build TagLib (static)
3333
shell: bash
34-
run: |
35-
if ! pkg-config --exists taglib_c 2>/dev/null; then
36-
brew install taglib
37-
fi
34+
run: scripts/build-taglib.sh
3835

3936
- name: Install frontend dependencies
4037
shell: bash

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,8 @@ jobs:
200200
- name: Verify build dependencies
201201
run: |
202202
echo "Zig: $(zig version)"
203-
echo "TagLib: $(pkg-config --modversion taglib_c)"
204-
echo "pkg-config path: $(pkg-config --variable=libdir taglib_c)"
203+
echo "TagLib: static (vendor/taglib/lib/)"
204+
ls -la vendor/taglib/lib/*.a
205205
206206
- name: Build frontend
207207
working-directory: ./app/frontend

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ dist
186186
zig-out/
187187
*.o
188188

189+
# Vendored static libraries (built per-platform)
190+
vendor/
191+
189192
# Sidecar binaries
190193
src-tauri/bin/
191194

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backlog/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ zero_padded_ids: 3
1414
bypass_git_hooks: true
1515
check_active_branches: false
1616
active_branch_days: 15
17+
task_prefix: "task"

backlog/tasks/task-021 - Implement-code-signing.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
2-
id: task-021
2+
id: TASK-021
33
title: Implement code signing
44
status: In Progress
55
assignee: []
66
created_date: '2025-09-17 04:11'
7-
updated_date: '2026-02-06 07:54'
7+
updated_date: '2026-02-07 00:46'
88
labels:
99
- signing
1010
- macos
@@ -23,11 +23,11 @@ Set up code signing for application packages and releases
2323

2424
## Acceptance Criteria
2525
<!-- AC:BEGIN -->
26-
- [ ] #1 Research code signing requirements for macOS (done - Tauri v2 docs reviewed)
27-
- [ ] #2 Set up macOS code signing certificate (Developer ID Application)
28-
- [ ] #3 Create Entitlements.plist with hardened runtime and app-specific entitlements
29-
- [ ] #4 Configure tauri.conf.json with signingIdentity, entitlements, and DMG settings
30-
- [ ] #5 Set up notarization via App Store Connect API key
26+
- [x] #1 Research code signing requirements for macOS (done - Tauri v2 docs reviewed)
27+
- [x] #2 Set up macOS code signing certificate (Developer ID Application)
28+
- [x] #3 Create Entitlements.plist with hardened runtime and app-specific entitlements
29+
- [x] #4 Configure tauri.conf.json with signingIdentity, entitlements, and DMG settings
30+
- [x] #5 Set up notarization via App Store Connect API key
3131

3232
- [ ] #6 Configure GitHub secrets for certificate and notarization
3333
- [ ] #7 Create release.yml GitHub Actions workflow with macOS ARM64 + x64 builds

backlog/tasks/task-108 - P5-Add-Linux-platform-support.md

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
2-
id: task-108
2+
id: TASK-108
33
title: 'P5: Add Linux platform support'
4-
status: To Do
4+
status: In Progress
55
assignee: []
66
created_date: '2026-01-12 04:09'
7-
updated_date: '2026-01-27 03:02'
7+
updated_date: '2026-02-07 01:05'
88
labels:
99
- linux
1010
- platform
@@ -26,15 +26,11 @@ Extend the Tauri app to support Linux.
2626
- symphonia + rodio should work on Linux via ALSA/PulseAudio
2727
- Test with common audio backends
2828

29-
**PEX sidecar:**
30-
- Build PEX for `x86_64-unknown-linux-gnu`
31-
- Test on Ubuntu/Debian and Fedora
32-
- Document system dependencies (if any)
33-
3429
**Platform-specific considerations:**
3530
- Media keys: Use D-Bus MPRIS interface
3631
- File dialogs: GTK integration
3732
- System tray: May need additional configuration
33+
- System dependencies: libwebkit2gtk, libgtk-3, libasound2, etc.
3834

3935
**Testing matrix:**
4036
- Ubuntu 22.04 LTS
@@ -52,6 +48,5 @@ cargo tauri build --target x86_64-unknown-linux-gnu
5248
<!-- AC:BEGIN -->
5349
- [ ] #1 App launches on Ubuntu 22.04
5450
- [ ] #2 Audio playback works (FLAC, MP3, M4A)
55-
- [ ] #3 PEX sidecar runs correctly
56-
- [ ] #4 Basic functionality matches macOS
51+
- [ ] #3 Basic functionality matches macOS
5752
<!-- AC:END -->

backlog/tasks/task-259 - Bundle-or-statically-link-TagLib-for-distribution.md

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
---
2-
id: task-259
2+
id: TASK-259
33
title: Bundle or statically link TagLib for distribution
4-
status: To Do
4+
status: Done
55
assignee: []
66
created_date: '2026-02-06 23:09'
7+
updated_date: '2026-02-07 01:45'
78
labels:
89
- build
910
- distribution
1011
- macos
1112
dependencies: []
1213
priority: high
14+
ordinal: 500
1315
---
1416

1517
## Description
@@ -32,7 +34,30 @@ Once resolved, the `com.apple.security.cs.disable-library-validation` entitlemen
3234

3335
## Acceptance Criteria
3436
<!-- AC:BEGIN -->
35-
- [ ] #1 Signed mt.app launches without crash on a machine without Homebrew TagLib installed
36-
- [ ] #2 otool -L shows no /opt/homebrew references in the binary
37-
- [ ] #3 disable-library-validation entitlement removed from Entitlements.plist
37+
- [x] #1 Signed mt.app launches without crash on a machine without Homebrew TagLib installed
38+
- [x] #2 otool -L shows no /opt/homebrew references in the binary
39+
- [x] #3 disable-library-validation entitlement removed from Entitlements.plist
3840
<!-- AC:END -->
41+
42+
## Final Summary
43+
44+
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
45+
Statically linked TagLib 2.0.2 into the mt binary, eliminating the runtime dependency on Homebrew's dynamic libraries.
46+
47+
## Changes
48+
- `scripts/build-taglib.sh` - New script to download TagLib source and build static `.a` libraries into `vendor/taglib/`
49+
- `zig-core/build.zig` - Replaced `use_pkg_config = .force` with explicit vendor include/library paths
50+
- `crates/mt-core/build.rs` - Static linking directives for `tag_c`, `tag`, `z`, `c++`; auto-triggers build script if vendor libs missing
51+
- `crates/mt-core/Cargo.toml` - Removed `pkg-config` build dependency
52+
- `crates/mt-tauri/Entitlements.plist` - Removed `disable-library-validation` entitlement
53+
- `.github/actions/setup-tauri-build/action.yml` - Replaced `brew install taglib` with `scripts/build-taglib.sh`
54+
- `.github/workflows/test.yml` - Updated diagnostic echo for static TagLib
55+
- `taskfiles/zig.yml` - Added `zig:build:taglib` and `zig:build:taglib:force` tasks
56+
- `.gitignore` - Added `vendor/`
57+
58+
## Verification
59+
- `otool -L` shows zero `/opt/homebrew` references
60+
- `codesign --verify --deep --strict` passes (exit 0)
61+
- `codesign -d --entitlements` confirms no `disable-library-validation`
62+
- 596 Rust tests pass, Zig tests pass
63+
<!-- SECTION:FINAL_SUMMARY:END -->

crates/mt-core/Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ edition = "2024"
99
name = "mt_core"
1010
crate-type = ["rlib"]
1111

12-
[build-dependencies]
13-
pkg-config = "0.3"
14-
1512
[dependencies]
1613
# Serialization (for types shared across FFI boundary)
1714
serde = { version = "1", features = ["derive"] }

crates/mt-core/build.rs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,22 @@ fn main() {
66
let workspace_root = manifest_dir.parent().unwrap().parent().unwrap();
77
let zig_core_dir = workspace_root.join("zig-core");
88
let zig_lib_dir = zig_core_dir.join("zig-out").join("lib");
9+
let vendor_taglib_lib = workspace_root.join("vendor").join("taglib").join("lib");
910

10-
// Build Zig library first
11+
// Build TagLib static libraries if not present
12+
if !vendor_taglib_lib.join("libtag.a").exists()
13+
|| !vendor_taglib_lib.join("libtag_c.a").exists()
14+
{
15+
let build_script = workspace_root.join("scripts").join("build-taglib.sh");
16+
eprintln!("TagLib static libraries not found, running {}...", build_script.display());
17+
let status = std::process::Command::new("bash")
18+
.arg(&build_script)
19+
.status()
20+
.expect("failed to run scripts/build-taglib.sh");
21+
assert!(status.success(), "TagLib static build failed");
22+
}
23+
24+
// Build Zig library
1125
let status = std::process::Command::new("zig")
1226
.args(["build", "-Doptimize=ReleaseFast"])
1327
.current_dir(&zig_core_dir)
@@ -16,15 +30,33 @@ fn main() {
1630

1731
assert!(status.success(), "zig-core build failed");
1832

19-
// Link the static library using absolute path
33+
// Link the Zig static library
2034
println!("cargo:rustc-link-search=native={}", zig_lib_dir.display());
2135
println!("cargo:rustc-link-lib=static=mtcore");
2236

23-
// Link TagLib (required by zig-core) via pkg-config
24-
pkg_config::Config::new()
25-
.probe("taglib_c")
26-
.expect("failed to find taglib_c via pkg-config");
37+
// Link TagLib statically from vendor directory
38+
println!(
39+
"cargo:rustc-link-search=native={}",
40+
vendor_taglib_lib.display()
41+
);
42+
println!("cargo:rustc-link-lib=static=tag_c");
43+
println!("cargo:rustc-link-lib=static=tag");
44+
45+
// TagLib depends on zlib and C++ standard library
46+
println!("cargo:rustc-link-lib=z");
47+
if cfg!(target_os = "macos") {
48+
println!("cargo:rustc-link-lib=c++");
49+
} else {
50+
println!("cargo:rustc-link-lib=stdc++");
51+
}
2752

28-
// Rebuild if zig sources change
29-
println!("cargo:rerun-if-changed={}", zig_core_dir.join("src").display());
53+
// Rebuild if zig sources or vendor libs change
54+
println!(
55+
"cargo:rerun-if-changed={}",
56+
zig_core_dir.join("src").display()
57+
);
58+
println!(
59+
"cargo:rerun-if-changed={}",
60+
vendor_taglib_lib.join("libtag.a").display()
61+
);
3062
}

0 commit comments

Comments
 (0)