Skip to content

fix(link): #6023 LNK1158 'cannot run mt.exe' on Windows MSVC UI builds - #6051

Merged
proggeramlug merged 2 commits into
mainfrom
fix/6023-mt-exe-lnk1158
Jul 6, 2026
Merged

fix(link): #6023 LNK1158 'cannot run mt.exe' on Windows MSVC UI builds#6051
proggeramlug merged 2 commits into
mainfrom
fix/6023-mt-exe-lnk1158

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Problem

Since v0.5.1129, every perry/ui build on Windows with the MSVC toolchain fails:

LINK : fatal error LNK1158: cannot run 'mt.exe'

preceded by hundreds of LNK4006 duplicate-definition warnings (#6023, follow-up to #2169 which is unverifiable while the build is broken).

Root cause

v0.5.1129 (#4683) added the comctl32 v6 application-manifest embed (/MANIFEST:EMBED + /MANIFESTINPUT:) for themed controls. lld-link embeds manifests in-process, but MSVC link.exe shells out to the Windows SDK's mt.exe for this. Perry locates link.exe via vswhere and spawns it from a plain shell — not a vcvars64.bat developer prompt — so the SDK bin dir is not on PATH, link.exe can't find mt.exe, and the link dies. The old doc comment in windows_link.rs explicitly (and incorrectly) claimed no external mt.exe was needed, which is why this shipped.

Fix

  • mt.exe reachability for MSVC link.exe (windows_link.rs): before embedding, check mt.exe is resolvable. If not on PATH, probe the Windows SDK bin\<ver>\<arch> dirs (registry KitsRoot10, ProgramFiles roots, legacy hardcoded path — same probe order find_msvc_lib_paths already uses for Lib) and prepend the dir holding mt.exe to the child's PATH.
  • Graceful fallback: if mt.exe genuinely doesn't exist, skip the embed with a loud warning (controls render classic-style) instead of failing the link — mirrors the existing manifest-temp-file-write-failure fallback from feat(compile): embed comctl32 v6 manifest so Windows UI apps get themed controls #4683. lld-link path is untouched.
  • /IGNORE:4006 (platform_cmd.rs): the duplicate-definition merge is deliberate (/FORCE:MULTIPLE), so the per-symbol LNK4006 flood is pure noise that buried the real error in the report. lld-link silently ignores unknown /IGNORE codes.

Tests

6 new unit tests in windows_link_tests.rs (all host-independent, run on any CI OS):

  • MSVC-vs-lld linker classification (bare names, absolute paths, case)
  • console builds stay manifest-free; lld-link UI builds always get the full /MANIFEST:EMBED arg set
  • SDK bin probe: newest-version pick, x64-over-x86 preference, skipping version dirs without mt.exe, pre-10.0.15063 unversioned layout, missing/empty roots

cargo test -p perry --bin perry windows_link: 18 passed. The cfg(target_os = "windows") bodies were additionally type-checked standalone on the dev host (they use only cross-platform std APIs).

Notes

Closes #6023

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved Windows UI linking by more reliably locating mt.exe (using Windows SDK search) and skipping manifest embedding with a warning if it can’t be reached.
    • Reduced noisy LNK4006 output when merging duplicate symbols.
  • Tests
    • Added Windows linker regression coverage for MSVC link detection, conditional manifest embedding, and SDK mt.exe path/version selection across common layouts.

MSVC link.exe implements /MANIFEST:EMBED by shelling out to the Windows
SDK's mt.exe. Perry launches a vswhere-located link.exe from a plain
shell (not a vcvars64.bat developer prompt), so the SDK bin dir is not
on PATH and every UI build since the comctl32-v6 manifest embed landed
(v0.5.1129 / #4683) died with LNK1158.

- When the linker is MSVC link.exe, probe for mt.exe: if it isn't on
  PATH already, locate the Windows SDK bin\<ver>\<arch> dir that holds
  it (registry KitsRoot10, ProgramFiles roots, legacy path — same probe
  order as find_msvc_lib_paths) and prepend it to the child's PATH.
- If mt.exe can't be found anywhere, skip the manifest embed with a
  loud warning instead of failing the link — an unthemed app that
  builds beats a fatal LNK1158 (mirrors the existing manifest-write-
  failure fallback). lld-link embeds manifests in-process and is
  untouched.
- Pass /IGNORE:4006: /FORCE:MULTIPLE makes the duplicate-definition
  merge deliberate, and the hundreds of LNK4006 lines it produced
  buried the real error in the report.

Closes #6023

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b8d5e890-a0a5-41cd-9299-bf1a868bec84

📥 Commits

Reviewing files that changed from the base of the PR and between c53555f and a126ef3.

📒 Files selected for processing (1)
  • crates/perry/src/commands/compile/windows_link_tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/perry/src/commands/compile/windows_link_tests.rs

📝 Walkthrough

Walkthrough

This PR adds Windows SDK mt.exe discovery, gates manifest embedding on mt.exe reachability for MSVC link.exe, suppresses LNK4006 warnings with /IGNORE:4006, and adds regression tests plus test-only re-exports.

Changes

Windows link.exe manifest and LNK4006 fix

Layer / File(s) Summary
Windows SDK mt.exe directory discovery
crates/perry/src/commands/compile/library_search.rs
Adds find_windows_sdk_mt_dir() and newest_mt_dir_under() to locate a Windows SDK mt.exe directory from registry, environment, and legacy SDK layouts.
Conditional manifest embedding based on mt.exe reachability
crates/perry/src/commands/compile/link/windows_link.rs
Widens embed_app_manifest, adds MSVC link.exe detection, and skips manifest embedding with a warning when mt.exe cannot be reached.
LNK4006 warning suppression
crates/perry/src/commands/compile/link/platform_cmd.rs
Adds /IGNORE:4006 alongside /FORCE:MULTIPLE in the native link command.
Test re-exports and regression test suite
crates/perry/src/commands/compile/link/mod.rs, crates/perry/src/commands/compile/windows_link_tests.rs
Re-exports manifest/linker helpers for tests and adds regression coverage for linker detection, manifest argument behavior, and SDK mt.exe directory selection.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Cmd as Compile Command
  participant EmbedManifest as embed_app_manifest
  participant LinkerCheck as linker_is_msvc_link_exe
  participant MtCheck as ensure_mt_exe_reachable
  participant SdkProbe as find_windows_sdk_mt_dir

  Cmd->>EmbedManifest: needs_ui = true
  EmbedManifest->>LinkerCheck: is this link.exe?
  LinkerCheck-->>EmbedManifest: true/false
  alt is link.exe
    EmbedManifest->>MtCheck: ensure mt.exe reachable
    MtCheck->>MtCheck: where mt.exe on PATH
    alt not found
      MtCheck->>SdkProbe: find_windows_sdk_mt_dir()
      SdkProbe-->>MtCheck: bin dir or None
      MtCheck->>MtCheck: prepend dir to PATH
    end
    MtCheck-->>EmbedManifest: reachable true/false
    alt unreachable
      EmbedManifest-->>Cmd: warn and skip embed
    else reachable
      EmbedManifest-->>Cmd: embed manifest args
    end
  else not link.exe
    EmbedManifest-->>Cmd: embed manifest args
  end
Loading

Related issues: #6023 — fixes the Windows LNK1158: cannot run 'mt.exe' regression and reduces LNK4006 warning noise during native compilation.

Suggested labels: windows, bug, linker

Suggested reviewers: perry-maintainers

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is informative, but it does not follow the required template sections for Summary, Changes, Related issue, and Test plan. Reformat the PR description to the repository template and add the missing Summary, Changes, Related issue, Test plan, and Checklist sections.
Linked Issues check ⚠️ Warning The PR addresses the LNK1158 and LNK4006 parts of #6023, but it does not show any change for the required TextField onChange/state binding fix. Add the TextField onChange/stateBindTextfield fix from #2169/#6023, or split that requirement into a separate issue if it is out of scope.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is specific and matches the main change: fixing LNK1158 on Windows MSVC UI builds.
Out of Scope Changes check ✅ Passed The changes stay focused on Windows linker, manifest, and warning handling, plus targeted tests, with no obvious unrelated scope creep.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/6023-mt-exe-lnk1158

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
crates/perry/src/commands/compile/windows_link_tests.rs (1)

185-208: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test comment overstates coverage of arch preference.

The comment says the probe "prefers x64 over x86," but the assertion only exercises version-ordering (10.0.22621.0 wins over 10.0.19041.0 because the latter lacks a newer mt.exe-bearing dir at a higher version); no case has two arches present under the same winning version to actually verify x64-over-x86 selection. Consider adding a fixture where the newest version directory has both x64 and x86 with mt.exe, asserting x64 wins.

🤖 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 `@crates/perry/src/commands/compile/windows_link_tests.rs` around lines 185 -
208, The test mt_dir_probe_picks_newest_versioned_sdk currently verifies version
ordering but not the stated x64-over-x86 preference. Update the fixture in
newest_mt_dir_under coverage so the winning SDK version contains both x64 and
x86 mt.exe dirs, then assert that x64 is selected; keep the existing
newer-version skip case to preserve the probe behavior being tested.
🤖 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.

Nitpick comments:
In `@crates/perry/src/commands/compile/windows_link_tests.rs`:
- Around line 185-208: The test mt_dir_probe_picks_newest_versioned_sdk
currently verifies version ordering but not the stated x64-over-x86 preference.
Update the fixture in newest_mt_dir_under coverage so the winning SDK version
contains both x64 and x86 mt.exe dirs, then assert that x64 is selected; keep
the existing newer-version skip case to preserve the probe behavior being
tested.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f9268398-e405-4d3c-881c-bf8c056e2352

📥 Commits

Reviewing files that changed from the base of the PR and between 9016b44 and c53555f.

📒 Files selected for processing (5)
  • crates/perry/src/commands/compile/library_search.rs
  • crates/perry/src/commands/compile/link/mod.rs
  • crates/perry/src/commands/compile/link/platform_cmd.rs
  • crates/perry/src/commands/compile/link/windows_link.rs
  • crates/perry/src/commands/compile/windows_link_tests.rs

…xe probe

cargo fmt wrapped the long fixture array (lint gate), and per CodeRabbit
review the newest-version fixture now carries both x64 and x86 mt.exe so
the asserted x64 pick actually exercises the arch preference instead of
just version ordering.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@proggeramlug
proggeramlug merged commit a868c75 into main Jul 6, 2026
25 checks passed
@proggeramlug
proggeramlug deleted the fix/6023-mt-exe-lnk1158 branch July 6, 2026 03:16
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.

Regression: TextField onChange broken and LNK1158 build error in v0.5.1220 (follow-up to #2169)

1 participant