Skip to content

Commit b795816

Browse files
feat(build): add Linux amd64 Docker build mirroring arm64
Add Dockerfile.linux-amd64 and corresponding Taskfile tasks so `task build:linux-amd64` produces a .deb from macOS via Docker (QEMU emulation on Apple Silicon). Fix TagLib static build to use -DCMAKE_POSITION_INDEPENDENT_CODE=ON — x86_64 requires -fPIC for static libs linked into shared objects, unlike arm64 where PC-relative addressing is the default. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 874061c commit b795816

5 files changed

Lines changed: 238 additions & 2 deletions

File tree

docker/Dockerfile.linux-amd64

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# ==============================================================================
2+
# Docker cross-build for x86_64-unknown-linux-gnu
3+
#
4+
# On Apple Silicon, this uses QEMU emulation (--platform linux/amd64) —
5+
# slower than the native arm64 Docker build but functional.
6+
# Base image matches target OS (Debian 13 trixie).
7+
#
8+
# Usage:
9+
# task build:linux-amd64
10+
# # or manually:
11+
# docker build --platform linux/amd64 \
12+
# -f docker/Dockerfile.linux-amd64 \
13+
# --target artifacts --output type=local,dest=dist/linux-amd64 .
14+
# ==============================================================================
15+
16+
# ------------------------------------------------------------------------------
17+
# Stage 1: Build environment
18+
# ------------------------------------------------------------------------------
19+
FROM debian:trixie-slim AS builder
20+
21+
ENV DEBIAN_FRONTEND=noninteractive
22+
23+
# -- System packages: build tools --
24+
RUN apt-get update && apt-get install -y --no-install-recommends \
25+
build-essential \
26+
pkg-config \
27+
cmake \
28+
mold \
29+
curl \
30+
wget \
31+
file \
32+
ca-certificates \
33+
git \
34+
unzip \
35+
xz-utils \
36+
&& rm -rf /var/lib/apt/lists/*
37+
38+
# -- System packages: Tauri + audio dev libraries --
39+
ARG WEBKIT_VERSION=4.1
40+
ARG SOUP_VERSION=3.0
41+
42+
RUN apt-get update && apt-get install -y --no-install-recommends \
43+
libjavascriptcoregtk-${WEBKIT_VERSION}-dev \
44+
libwebkit2gtk-${WEBKIT_VERSION}-dev \
45+
libsoup-${SOUP_VERSION}-dev \
46+
libgtk-3-dev \
47+
libasound2-dev \
48+
libdbus-1-dev \
49+
libayatana-appindicator3-dev \
50+
zlib1g-dev \
51+
libutfcpp-dev \
52+
libssl-dev \
53+
librsvg2-dev \
54+
&& rm -rf /var/lib/apt/lists/*
55+
56+
# -- Rust (nightly) --
57+
ENV RUSTUP_HOME=/usr/local/rustup \
58+
CARGO_HOME=/usr/local/cargo \
59+
PATH="/usr/local/cargo/bin:${PATH}"
60+
61+
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
62+
| sh -s -- -y --default-toolchain nightly --profile minimal \
63+
&& rustup target add x86_64-unknown-linux-gnu
64+
65+
# -- Zig --
66+
ARG ZIG_VERSION=0.14.0
67+
RUN curl -sSL "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-x86_64-${ZIG_VERSION}.tar.xz" \
68+
| tar -xJ -C /opt \
69+
&& ln -s /opt/zig-linux-x86_64-${ZIG_VERSION}/zig /usr/local/bin/zig
70+
71+
# -- Node.js --
72+
ARG NODE_MAJOR=24
73+
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - \
74+
&& apt-get install -y --no-install-recommends nodejs \
75+
&& rm -rf /var/lib/apt/lists/*
76+
77+
# -- Deno --
78+
RUN curl -fsSL -L https://github.com/denoland/deno/releases/latest/download/deno-x86_64-unknown-linux-gnu.zip \
79+
-o /tmp/deno.zip \
80+
&& unzip -o /tmp/deno.zip -d /usr/local/bin \
81+
&& chmod +x /usr/local/bin/deno \
82+
&& rm /tmp/deno.zip
83+
84+
# -- Last.fm API keys (embedded at compile time via build.rs) --
85+
ARG LASTFM_API_KEY
86+
ARG LASTFM_API_SECRET
87+
ENV LASTFM_API_KEY=${LASTFM_API_KEY}
88+
ENV LASTFM_API_SECRET=${LASTFM_API_SECRET}
89+
90+
# -- Build environment --
91+
ENV RUSTUP_TOOLCHAIN=nightly \
92+
RUSTFLAGS="-Zthreads=16"
93+
94+
WORKDIR /build
95+
96+
# -- Layer: dependency files (cached unless deps change) --
97+
COPY Cargo.toml Cargo.lock ./
98+
COPY crates/mt-core/Cargo.toml crates/mt-core/
99+
COPY crates/mt-core/build.rs crates/mt-core/
100+
COPY crates/mt-tauri/Cargo.toml crates/mt-tauri/
101+
COPY crates/mt-tauri/.cargo crates/mt-tauri/.cargo/
102+
103+
# Stub source files for cargo fetch
104+
RUN mkdir -p crates/mt-core/src && echo "pub fn stub() {}" > crates/mt-core/src/lib.rs \
105+
&& mkdir -p crates/mt-tauri/src && echo "fn main() {}" > crates/mt-tauri/src/main.rs \
106+
&& echo "" > crates/mt-tauri/src/lib.rs
107+
108+
RUN cargo fetch --target x86_64-unknown-linux-gnu
109+
110+
# Frontend deps
111+
COPY deno.jsonc deno.lock ./
112+
COPY app/frontend/package.json app/frontend/package-lock.json app/frontend/
113+
114+
RUN deno install --node-modules-dir=auto
115+
116+
# -- Layer: full source --
117+
RUN rm -rf crates/mt-core/src crates/mt-tauri/src
118+
119+
COPY crates/ crates/
120+
COPY app/ app/
121+
COPY zig-core/ zig-core/
122+
COPY scripts/ scripts/
123+
COPY static/ static/
124+
125+
# -- Build TagLib for Linux (force — ignore any host-platform .a files) --
126+
RUN bash scripts/build-taglib.sh --force
127+
128+
# -- Build frontend --
129+
RUN cd app/frontend && npm run build
130+
131+
# -- Build Tauri app --
132+
RUN deno run -A npm:@tauri-apps/cli build \
133+
--target x86_64-unknown-linux-gnu \
134+
--bundles deb
135+
136+
# ------------------------------------------------------------------------------
137+
# Stage 2: Artifact extraction
138+
# ------------------------------------------------------------------------------
139+
FROM scratch AS artifacts
140+
141+
COPY --from=builder /build/target/x86_64-unknown-linux-gnu/release/bundle/deb/*.deb /
142+
COPY --from=builder /build/target/x86_64-unknown-linux-gnu/release/mt-tauri /mt-tauri

docs/builds.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ Build configuration, performance tuning, signing, and distribution for mt.
77
| Platform | Architecture | Runner | Bundle |
88
|----------|-------------|--------|--------|
99
| macOS | ARM64 | Self-hosted `[macOS, ARM64]` | `.app`, `.dmg` |
10-
| Linux | amd64 | `ubuntu-latest` | `.deb` |
11-
| Linux | arm64 | Local (Raspberry Pi) | `.deb` |
10+
| Linux | amd64 | `ubuntu-latest` (CI) or Docker (`Dockerfile.linux-amd64`) | `.deb` |
11+
| Linux | arm64 | Docker (`Dockerfile.linux-arm64`) | `.deb` |
1212

1313
## Taskfile Commands
1414

@@ -25,6 +25,8 @@ All `task tauri:*` commands default to nightly with parallel codegen
2525
| `task tauri:build:signed` | Build signed + notarized `.app` and `.dmg` (macOS) |
2626
| `task tauri:build:dmg` | Build signed + notarized `.dmg` only (macOS) |
2727
| `task tauri:icons` | Generate app icons from `static/logo.png` |
28+
| `task tauri:build:linux-amd64` | Build Linux amd64 `.deb` via Docker |
29+
| `task tauri:build:linux-arm64` | Build Linux arm64 `.deb` via Docker |
2830
| `task tauri:clean` | Clean all build artifacts |
2931
| `task tauri:clean:rust` | Clean only Rust build artifacts |
3032
| `task tauri:doctor` | Run Tauri environment check |
@@ -361,6 +363,35 @@ task tauri:build
361363

362364
`task tauri:build` auto-detects the current platform via `{{OS}}/{{ARCH}}` and selects the correct Rust target triple.
363365

366+
### Docker Builds (Linux .deb)
367+
368+
Both Linux architectures can be built locally via Docker, which is useful for producing `.deb` packages from a macOS development machine.
369+
370+
| Architecture | Task | Dockerfile | Notes |
371+
|-------------|------|------------|-------|
372+
| arm64 | `task build:linux-arm64` | `docker/Dockerfile.linux-arm64` | Native on Apple Silicon |
373+
| amd64 | `task build:linux-amd64` | `docker/Dockerfile.linux-amd64` | QEMU emulation on Apple Silicon |
374+
375+
The arm64 build runs natively on Apple Silicon with no emulation overhead. The amd64 build uses `--platform linux/amd64` which triggers QEMU emulation — functional but slower.
376+
377+
Artifacts are written to `dist/linux-{arm64,amd64}/`.
378+
379+
```bash
380+
# Build amd64 .deb
381+
task build:linux-amd64
382+
383+
# Build arm64 .deb
384+
task build:linux-arm64
385+
386+
# Copy to target machine
387+
scp dist/linux-amd64/*.deb zima:~/Downloads/
388+
scp dist/linux-arm64/*.deb rpi:~/Downloads/
389+
390+
# Debug shell (inspect build environment)
391+
task build:linux-amd64:shell
392+
task build:linux-arm64:shell
393+
```
394+
364395
### Windows
365396

366397
> Not yet implemented. Placeholder for future Windows code signing with Authenticode / EV certificates.

scripts/build-taglib.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ fi
7878

7979
cmake -S "${SOURCE_DIR}" -B "${BUILD_DIR}" \
8080
-DCMAKE_BUILD_TYPE=Release \
81+
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
8182
-DBUILD_SHARED_LIBS=OFF \
8283
-DBUILD_TESTING=OFF \
8384
-DBUILD_EXAMPLES=OFF \

taskfile.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ tasks:
9797
cmds:
9898
- task: tauri:build:linux-arm64
9999

100+
build:linux-amd64:
101+
desc: "Build Linux amd64 .deb via Docker"
102+
cmds:
103+
- task: tauri:build:linux-amd64
104+
100105
build:timings:
101106
desc: "Analyze build performance bottlenecks"
102107
cmds:

taskfiles/tauri.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,60 @@ tasks:
318318
.
319319
status:
320320
- docker image inspect mt-builder:linux-arm64 >/dev/null 2>&1
321+
322+
build:linux-amd64:
323+
desc: "Build Linux amd64 .deb via Docker"
324+
cmds:
325+
- |
326+
echo "Building mt for x86_64-unknown-linux-gnu via Docker..."
327+
mkdir -p dist/linux-amd64
328+
329+
docker build \
330+
--platform linux/amd64 \
331+
-f docker/Dockerfile.linux-amd64 \
332+
--build-arg LASTFM_API_KEY=${LASTFM_API_KEY:-} \
333+
--build-arg LASTFM_API_SECRET=${LASTFM_API_SECRET:-} \
334+
--target artifacts \
335+
--output type=local,dest=dist/linux-amd64 \
336+
.
337+
338+
echo ""
339+
echo "Build complete. Artifacts:"
340+
ls -lh dist/linux-amd64/
341+
sources:
342+
- "{{.TAURI_DIR}}/src/**/*.rs"
343+
- "{{.TAURI_DIR}}/Cargo.toml"
344+
- "crates/mt-core/src/**/*.rs"
345+
- "crates/mt-core/Cargo.toml"
346+
- "zig-core/src/**/*.zig"
347+
- "zig-core/build.zig"
348+
- "app/frontend/js/**/*.js"
349+
- "app/frontend/index.html"
350+
- "docker/Dockerfile.linux-amd64"
351+
generates:
352+
- "dist/linux-amd64/*.deb"
353+
354+
build:linux-amd64:shell:
355+
desc: "Open a shell in the Linux amd64 build container (debugging)"
356+
deps: ["build:linux-amd64:image"]
357+
cmds:
358+
- |
359+
docker run --rm -it \
360+
--platform linux/amd64 \
361+
mt-builder:linux-amd64 \
362+
/bin/bash
363+
interactive: true
364+
365+
build:linux-amd64:image:
366+
internal: true
367+
desc: "Build the Docker builder image (amd64)"
368+
cmds:
369+
- |
370+
docker build \
371+
--platform linux/amd64 \
372+
-f docker/Dockerfile.linux-amd64 \
373+
--target builder \
374+
-t mt-builder:linux-amd64 \
375+
.
376+
status:
377+
- docker image inspect mt-builder:linux-amd64 >/dev/null 2>&1

0 commit comments

Comments
 (0)