Skip to content

fix(v3/appimage): GTK4 default + relative builddir + output filename (#5475)#5476

Merged
leaanthony merged 6 commits into
masterfrom
fix/5475-appimage-gtk4-default
May 20, 2026
Merged

fix(v3/appimage): GTK4 default + relative builddir + output filename (#5475)#5476
leaanthony merged 6 commits into
masterfrom
fix/5475-appimage-gtk4-default

Conversation

@leaanthony

@leaanthony leaanthony commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Closes #5475. Bundled with two adjacent fixes for pre-existing AppImage bugs that surfaced while verifying the GTK4 default fix.

1. GTK4 default support (the #5475 fix)

Since alpha.93, the default Linux build stack is GTK4 / WebKitGTK 6.0 (GTK3 / WebKit2GTK 4.1 is now the legacy -tags gtk3 path). But wails3 generate appimage still hard-coded the GTK3 set of runtime files, so on a runner with only the GTK4 toolchain installed:

Processing GTK files. [3/5] 60% | 1m13s
ERROR  Unable to locate all required files: libwebkit2gtkinjectedbundle.so
  • Moved the ldd-based GTK detection in front of findGTKFiles, so we know which stack the binary links against before searching for runtime files.
  • Run ldd on the source options.Binary directly. The previous filepath.Join(appDir, "usr", "bin", options.Binary) mishandled -binary with a path prefix and pointed at a file that may not exist; the source binary is unambiguous and already required to exist.
  • Pick the injected-bundle filename to match the detected stack:
    • GTK4: libwebkitgtkinjectedbundle.so (under webkitgtk-6.0/injected-bundle/)
    • GTK3: libwebkit2gtkinjectedbundle.so (under webkit2gtk-4.1/injected-bundle/)
  • WebKitWebProcess and WebKitNetworkProcess are unchanged - both stacks ship them.
  • Extended hasRelrDynSections to probe libgtk-4.so.1 (and aarch64 paths) so the strip workaround from fix(linux): auto-detect and handle .relr.dyn sections for AppImage builds #4772 still triggers on modern toolchains regardless of which stack the user built against.

2. Relative -builddir race

wails3 generate appimage with a relative -builddir failed inside the AppRun download goroutine: the flow does s.CD(BuildDir) between launching the goroutines and their file writes, so any path computed before the CD (e.g. appDir = filepath.Join(BuildDir, "...AppDir")) was being resolved relative to the wrong CWD by the downstream s.DOWNLOAD(target) and the ldd <binary> shell-out.

Fix: resolve -binary, -icon, -desktopfile, -builddir and -outputdir to absolute paths up-front, alongside the existing OutputDir resolution.

3. AppImage output filename mismatch

The post-bundle s.MOVE(targetFile, OutputDir) failed when the desktop Name= field didn't match normaliseName(basename(Binary)). linuxdeploy's appimage plugin names the output AppImage from the desktop Name=, but the Go code expected <binary>-<arch>.AppImage and looked it up by that path.

Fix: set OUTPUT=<binary>-<arch>.AppImage (honored by linuxdeploy-plugin-appimage) so the produced filename matches what the move step expects.

Test plan

Verified end-to-end on Ubuntu 26.04 (lin-node1) with both GTK3 and GTK4 dev/runtime packages installed:

  • GTK4 default, absolute paths: go build a minimal Wails v3 app; wails3 generate appimage succeeds, bundles usr/lib/x86_64-linux-gnu/webkitgtk-6.0/injected-bundle/libwebkitgtkinjectedbundle.so into the AppDir, output AppImage launches under xvfb-run and logs GTK=4.22.2 WebKitGTK=2.52.3.
  • GTK3 legacy, absolute paths: same flow with -tags gtk3 bundles usr/lib/x86_64-linux-gnu/webkit2gtk-4.1/injected-bundle/libwebkit2gtkinjectedbundle.so; AppImage launches under xvfb and logs -tags=gtk3 ... Webkit2Gtk=v2.52.3.
  • Relative -builddir + Name= mismatch (bugs 2 & 3): desktop file with Name=Differently Named App, all paths passed as relative. Exit 0, AppImage produced at the expected out/<binary>-<arch>.AppImage (not Differently_Named_App-x86_64.AppImage), launches under xvfb and logs GTK=4.22.2 WebKitGTK=2.52.3.
  • go test ./internal/commands/... passes.
  • go vet ./... clean (only pre-existing warnings in internal/generator/testcases, unrelated).

Summary by CodeRabbit

  • Bug Fixes
    • AppImage generation now detects GTK4 from the built binary and selects the appropriate WebKit runtime for modern toolchains.
    • Build-related inputs (binary, icon, desktop file, build/output dirs) are normalized to absolute paths to prevent packaging failures with relative paths.
    • Runtime probing and packaging now avoid improper stripping and produce a deterministic final AppImage filename.

Review Change Stack

Since alpha.93, the default Linux build target is GTK4 / WebKitGTK 6.0
(GTK3 / WebKit2GTK 4.1 is now the legacy `-tags gtk3` path). The AppImage
bundler still hard-coded the GTK3 set of runtime files, so on a runner
with only the GTK4 toolchain installed `wails3 generate appimage` aborted
with:

    ERROR  Unable to locate all required files: libwebkit2gtkinjectedbundle.so

Move the ldd-based version detection in front of `findGTKFiles`, run it
on the source binary (not the post-copy path, which `filepath.Join` mis-
handles when `-binary` includes a path), and pick the injected-bundle
filename to match: `libwebkitgtkinjectedbundle.so` for GTK4, the older
`libwebkit2gtkinjectedbundle.so` for GTK3. `WebKitWebProcess` and
`WebKitNetworkProcess` are unchanged - both stacks ship them.

Also extend `hasRelrDynSections` to probe `libgtk-4.so.1` (plus aarch64
paths) so the strip workaround introduced in #4772 still triggers on
modern toolchains regardless of which stack the user built against.

Verified on Ubuntu 26.04 lin-node1:

- Default (GTK4): `go build` then `wails3 generate appimage` bundles
  `libwebkitgtkinjectedbundle.so` under `webkitgtk-6.0/injected-bundle/`
  in the AppDir; the resulting AppImage launches under xvfb and logs
  `GTK=4.22.2 WebKitGTK=2.52.3`.
- Legacy (`-tags gtk3`): same flow bundles
  `libwebkit2gtkinjectedbundle.so` under `webkit2gtk-4.1/injected-bundle/`
  and the AppImage launches with `Webkit2Gtk=v2.52.3` and `-tags=gtk3`.

Two pre-existing unrelated issues observed during verification, flagged
here so reviewers don't conflate them with this fix:

- `wails3 generate appimage` with a relative `-builddir` races on the
  AppRun download goroutine because `s.CD(BuildDir)` is called between
  the goroutine launch and its file write. Workaround: pass absolute
  paths.
- The post-bundle `s.MOVE` expects the AppImage to be named after
  `normaliseName(filepath.Base(Binary))`, but `linuxdeploy` names it
  after the desktop `Name=` field, so the move fails when those don't
  match. Workaround: align `Name=` with the binary basename. Will be
  addressed in a follow-up.

Refs #5475.
Copilot AI review requested due to automatic review settings May 18, 2026 11:47
@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 53f14bde-c95e-4763-b16d-323874aeb80f

📥 Commits

Reviewing files that changed from the base of the PR and between 08e4870 and 14c199f.

📒 Files selected for processing (1)
  • v3/UNRELEASED_CHANGELOG.md
✅ Files skipped from review due to trivial changes (1)
  • v3/UNRELEASED_CHANGELOG.md

Walkthrough

Normalize CLI paths, determine GTK version from the built binary early to pick the correct WebKit injected-bundle and runtime files, force linuxdeploy to write a deterministic AppImage name, and expand .relr.dyn probing to include GTK4.

Changes

AppImage generation fixes

Layer / File(s) Summary
Normalize input paths
v3/internal/commands/appimage.go
Resolve OutputDir, BuildDir, Binary, Icon, and DesktopFile to absolute paths at the start of GenerateAppImage to avoid relative-path breakage after changing directories.
Early GTK detection and WebKit selection
v3/internal/commands/appimage.go
Run ldd on the input binary to compute DeployGtkVersion early, choose the correct injected-bundle filename for GTK2/3/4, and construct filesNeeded before locating GTK runtime files.
Force linuxdeploy OUTPUT and move artifact
v3/internal/commands/appimage.go
Export earlier-computed DEPLOY_GTK_VERSION, set OUTPUT/targetFile to <binary>-<arch>.AppImage before invoking linuxdeploy, then move the created file into options.OutputDir and log the final path.
Expand .relr.dyn probing for GTK4
v3/internal/commands/appimage.go
hasRelrDynSections now probes for GTK4 (libgtk-4.so.1) alongside GTK3 across common lib dirs/architectures before running readelf to check for .relr.dyn.
Changelog
v3/UNRELEASED_CHANGELOG.md
Add entries documenting GTK4 default handling, CLI path normalization when -builddir is relative, and forced linuxdeploy AppImage naming.

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant generateAppImage
  participant RuntimeSearch
  participant linuxdeploy
  CLI->>generateAppImage: provide options (Binary, BuildDir, OutputDir, Icon, DesktopFile)
  generateAppImage->>generateAppImage: resolve absolute paths
  generateAppImage->>RuntimeSearch: run ldd on Binary -> determine DeployGtkVersion
  generateAppImage->>RuntimeSearch: select filesNeeded (WebKit/GTK + injected bundle)
  generateAppImage->>linuxdeploy: export DEPLOY_GTK_VERSION, set OUTPUT, invoke linuxdeploy
  linuxdeploy-->>generateAppImage: writes targetFile
  generateAppImage->>CLI: move targetFile -> options.OutputDir, log final AppImage path
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #5459: Related GTK4 vs GTK3 detection/selection in tooling — this PR adds binary-derived GTK detection and probes libgtk-4.so.1.

Possibly related PRs

Suggested labels

Bug, Linux, go, v3, cli

"I sniffed the binary, found GTK4's trace,
Resolved wandering paths, set OUTPUT in place,
WebKit now chosen by what the build shows,
AppImage hops home where the final filename goes,
Hooray—packed clean, with a carrot-shaped grace."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the three main bug fixes addressed in the PR: GTK4 default support, relative builddir handling, and output filename consistency.
Description check ✅ Passed The PR description is comprehensive and well-structured. It includes a detailed summary of changes, references the linked issue (#5475), explains the problems and solutions, and documents a thorough test plan across multiple scenarios.
Linked Issues check ✅ Passed The code changes successfully address the core requirement from #5475: detecting the GTK stack (GTK4 vs GTK3) by running ldd on the source binary and selecting matching runtime files, ensuring AppImage generation works with GTK4 builds.
Out of Scope Changes check ✅ Passed All changes in the PR are directly related to the three AppImage bugs documented in the PR objectives. The changelog entry documents the fixes, and the code changes implement GTK stack detection, path resolution, and output filename handling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/5475-appimage-gtk4-default

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes v3 AppImage generation so it detects whether the target Linux binary uses GTK3 or GTK4 before selecting WebKit runtime files, aligning packaging with the current GTK4 default and legacy -tags gtk3 path.

Changes:

  • Moves GTK stack detection earlier and uses ldd output to choose the appropriate injected-bundle library.
  • Keeps DEPLOY_GTK_VERSION setup for linuxdeploy based on detected GTK version.
  • Expands .relr.dyn probing to include GTK4 and aarch64 GTK library paths.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
v3/internal/commands/appimage.go Detects GTK stack before file discovery, selects GTK3/GTK4 injected bundle, and expands strip-compatibility probing.
v3/UNRELEASED_CHANGELOG.md Documents the AppImage GTK stack detection fix and .relr.dyn probe update.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread v3/internal/commands/appimage.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
v3/internal/commands/appimage.go (1)

159-159: ⚡ Quick win

Improve error diagnostics for GTK version detection failure.

When GTK version cannot be determined, include a snippet of the ldd output to help users diagnose whether no GTK library was found or an unexpected version string appeared.

📊 Proposed enhancement for error context
 	default:
-		return fmt.Errorf("unable to determine GTK version")
+		return fmt.Errorf("unable to determine GTK version from ldd output (checked libgtk-4.so, libgtk-3.so, libgtk-x11-2.0.so). ldd output snippet: %.200s...", lddString)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/commands/appimage.go` at line 159, Replace the bare error return
at the GTK detection failure (the existing return fmt.Errorf("unable to
determine GTK version")) with an error that includes the ldd output used to
probe GTK (e.g., the variable holding the command output such as out or
lddOutput) so callers see diagnostic info; format the message like "unable to
determine GTK version: ldd output: %q" (or include a trimmed prefix if output
can be very large) and ensure you propagate the formatted error from the same
code path where the ldd invocation and parsing occur.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@v3/internal/commands/appimage.go`:
- Around line 140-160: The code builds a shell command with fmt.Sprintf("ldd
%s", options.Binary) and passes it to s.EXEC, which allows command injection or
failure on paths with spaces; update the call site that produces lddOutput so it
does not interpolate options.Binary into a shell string—either call s.EXEC with
the command and args separately (e.g., pass "ldd" and options.Binary if s.EXEC
supports arg lists) or invoke a safe exec path (exec.Command/Run) to run ldd
directly without shell interpretation; ensure the change is applied where
lddOutput/err are produced and preserve the subsequent lddString parsing and
DeployGtkVersion switch and error handling.

---

Nitpick comments:
In `@v3/internal/commands/appimage.go`:
- Line 159: Replace the bare error return at the GTK detection failure (the
existing return fmt.Errorf("unable to determine GTK version")) with an error
that includes the ldd output used to probe GTK (e.g., the variable holding the
command output such as out or lddOutput) so callers see diagnostic info; format
the message like "unable to determine GTK version: ldd output: %q" (or include a
trimmed prefix if output can be very large) and ensure you propagate the
formatted error from the same code path where the ldd invocation and parsing
occur.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 77dc169e-37d4-4f4a-a165-6dab985571a5

📥 Commits

Reviewing files that changed from the base of the PR and between 06456f1 and 58b7469.

📒 Files selected for processing (2)
  • v3/UNRELEASED_CHANGELOG.md
  • v3/internal/commands/appimage.go

Comment thread v3/internal/commands/appimage.go
Two pre-existing bugs surfaced while verifying #5475:

1. `wails3 generate appimage` with a relative `-builddir` would fail
   inside the AppRun download goroutine. The flow does `s.CD(BuildDir)`
   between launching the goroutines and their file writes, so any
   path computed before the CD (e.g. `appDir = filepath.Join(BuildDir,
   "...AppDir")`) was being resolved relative to the wrong CWD by the
   downstream `s.DOWNLOAD(target)` and the `ldd <binary>` shell-out.

   Fix: resolve `-binary`, `-icon`, `-desktopfile`, `-builddir` and
   `-outputdir` to absolute paths up-front, alongside the existing
   `OutputDir` resolution. Now CWD-sensitive operations downstream
   all see stable absolute paths.

2. The post-bundle `s.MOVE(targetFile, OutputDir)` would fail when the
   desktop file's `Name=` field didn't match `normaliseName(basename(
   Binary))`. linuxdeploy's appimage plugin names the output AppImage
   from the desktop `Name=`, but the Go code expected
   `<binary>-<arch>.AppImage` and looked it up by that path.

   Fix: set the `OUTPUT` env var (honored by linuxdeploy-plugin-appimage)
   to the binary-derived name we already expect, so the produced
   filename matches what we then move.

Verified on Ubuntu 26.04 lin-node1:

- Relative `-builddir` + a desktop `Name=Differently Named App` (which
  used to fail twice over): exit 0, AppImage produced at
  `out/appimage-smoke-gtk4-x86_64.AppImage`, launches under xvfb,
  logs `GTK=4.22.2 WebKitGTK=2.52.3`.
- The original absolute-paths flow (both GTK4 default and `-tags gtk3`)
  still produces the matching AppImage in `-outputdir` and exits 0 -
  no regression of the #5475 fix.

Refs #5475.
@leaanthony leaanthony changed the title fix(v3/appimage): detect GTK stack from binary before bundling (#5475) fix(v3/appimage): GTK4 default + relative builddir + output filename (#5475) May 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
v3/internal/commands/appimage.go (1)

146-167: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use argument-safe execution for ldd invocation.

At Line 150, options.Binary is interpolated into a command string. This is shell-fragile (spaces/special chars) and potentially unsafe depending on s.EXEC internals.

Proposed fix
-	lddOutput, err := s.EXEC(fmt.Sprintf("ldd %s", options.Binary))
+	lddOutput, err := s.EXEC(fmt.Sprintf("ldd %q", options.Binary))
#!/bin/bash
# Verify how s.EXEC executes commands and whether it shells out.
# Expected: confirm whether command strings are passed through a shell.
fd -i 's.go' v3/internal | xargs -r rg -n -C3 'func\s+EXEC\s*\(|exec\.Command|sh -c|bash -c'
rg -n -C2 'EXEC\(fmt\.Sprintf\("ldd %s"|EXEC\(fmt\.Sprintf\("readelf -S %s"' v3/internal/commands/appimage.go
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/commands/appimage.go` around lines 146 - 167, The ldd invocation
builds a shell-interpolated string with options.Binary and should use
argument-safe execution: change the call site that currently uses
s.EXEC(fmt.Sprintf("ldd %s", options.Binary)) in
v3/internal/commands/appimage.go to a form that passes the command and arguments
separately (e.g. s.EXEC("ldd", options.Binary) or an equivalent API provided by
s.EXEC) or otherwise safely escape/quote options.Binary before invoking the
shell; update the lddOutput/error handling around s.EXEC, keep the subsequent
lddString/DeployGtkVersion logic unchanged, and if s.EXEC does not support arg
lists implement exec.Command/CommandContext to run ldd with the binary as a
separate argument.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@v3/internal/commands/appimage.go`:
- Around line 146-167: The ldd invocation builds a shell-interpolated string
with options.Binary and should use argument-safe execution: change the call site
that currently uses s.EXEC(fmt.Sprintf("ldd %s", options.Binary)) in
v3/internal/commands/appimage.go to a form that passes the command and arguments
separately (e.g. s.EXEC("ldd", options.Binary) or an equivalent API provided by
s.EXEC) or otherwise safely escape/quote options.Binary before invoking the
shell; update the lddOutput/error handling around s.EXEC, keep the subsequent
lddString/DeployGtkVersion logic unchanged, and if s.EXEC does not support arg
lists implement exec.Command/CommandContext to run ldd with the binary as a
separate argument.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5e65e0b5-16c8-4380-b848-e7517c499293

📥 Commits

Reviewing files that changed from the base of the PR and between 58b7469 and 25ca368.

📒 Files selected for processing (2)
  • v3/UNRELEASED_CHANGELOG.md
  • v3/internal/commands/appimage.go
✅ Files skipped from review due to trivial changes (1)
  • v3/UNRELEASED_CHANGELOG.md

…ailure

Addresses CodeRabbit and Copilot review on #5476:

- Quote the binary path passed to `ldd` (`%s` -> `%q`). `s.EXEC` splits
  the command string with shlex; an unquoted path with spaces gets
  fragmented and the ldd call fails.
- Same treatment for the `readelf` probe in `hasRelrDynSections` and
  for the `linuxdeploy ... --appdir <dir>` command. The linuxdeploy
  call site was the latent breakage — invoking it with a `-builddir`
  containing a space produced `fork/exec /tmp/space: no such file or
  directory` even after the abs-path resolution from the previous
  commit.
- Include a truncated ldd-output snippet in the
  "unable to determine GTK version" error so users hitting this branch
  can see which libraries the binary actually links against.

Verified on lin-node1 (Ubuntu 26.04):

- Paths with spaces (`-binary "/tmp/space test/my app"` ...): now exit
  0 and produce `out/my-app-x86_64.AppImage`. Previously failed at
  the linuxdeploy step.
- The original absolute-path GTK4 and `-tags gtk3` flows still pass
  and the GTK4 AppImage still launches under xvfb with
  `GTK=4.22.2 WebKitGTK=2.52.3` - no regression.

Refs #5475.
@leaanthony leaanthony merged commit e70185e into master May 20, 2026
14 of 16 checks passed
@leaanthony leaanthony deleted the fix/5475-appimage-gtk4-default branch May 20, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[v3]wails3 generate appimage looks for GTK3 runtime files on the new GTK4 default (alpha.93)

2 participants