Skip to content

fix(windows): run Windows post-build steps when cross-compiling from a macOS/Linux editor - #209

Open
bobbyg603 wants to merge 1 commit into
mainfrom
fix/windows-cross-compile-support-files
Open

fix(windows): run Windows post-build steps when cross-compiling from a macOS/Linux editor#209
bobbyg603 wants to merge 1 commit into
mainfrom
fix/windows-cross-compile-support-files

Conversation

@bobbyg603

Copy link
Copy Markdown
Member

Closes #151

What was broken

OnPostprocessBuild dispatched to the Windows post-build work from a preprocessor chain that mixes two unrelated axes:

#if UNITY_IOS          // build-target define
#elif UNITY_ANDROID    // build-target define
#elif UNITY_EDITOR_WIN // editor-OS define  <-- the bug

UNITY_EDITOR_WIN describes the machine running the editor, not the platform being built. All of PostProcessWindows, UploadSymbolFilesWin, CopyWindowsLineNumberMappings and GetPEMachineArchitecture also lived inside a file-level #if UNITY_EDITOR_WIN region.

So building a Windows player from a macOS or Linux editor compiled every one of those methods out of the assembly. The result:

  • no BugSplatMonitor.exe / BugSplatRc.dll / BugSplatWer.dll copied next to the player, so UseNativeCrashReportingForWindows produced a build with no out-of-process monitor and no WER helper
  • no LineNumberMappings.json.zip, so no IL2CPP C# symbolication
  • no symbol upload at all
  • no warning of any kind — the build reported success and native crash reporting was inert at runtime

This is verified, not assumed. See the compile evidence below.

What changed

One file, Editor/PostBuild.cs, +6/-4.

  1. The #if UNITY_IOS / #elif UNITY_ANDROID chain is closed with #endif before the Windows block, and the Windows block is now dispatched unconditionally on target == BuildTarget.StandaloneWindows64 || target == BuildTarget.StandaloneWindows — exactly the shape the macOS branch on the next line has always had.
  2. The file-level #if UNITY_EDITOR_WIN region around the Windows helpers is removed. Nothing in it needs a Windows editor: it reads the COFF machine field off the built .exe and copies files out of the package's Runtime/Plugins/Windows/Support~/<arch> directory, all plain System.IO plus UnityEditor.PackageManager.PackageInfo and the BuildTarget enum, which are core UnityEditor.dll.
  3. The one genuinely Windows-editor-only API, UnityEditor.WindowsStandalone.UserBuildSettings.copyPDBFiles, keeps its UNITY_EDITOR_WIN guard — narrowed from the whole region down to the check itself. The #else arm warns that the setting could not be read and that symbolication will fail if it happens to be off, then proceeds with the upload rather than silently skipping it.

Deliberately unchanged: the using UnityEditor.WindowsStandalone; at the top stays under #if UNITY_EDITOR_WIN. The set of translation units that reference the UnityEditor.WindowsStandalone platform module is therefore bit-for-bit identical to before — the reference is compiled iff UNITY_EDITOR_WIN, on every build target, exactly as it was. No new platform-module reference is introduced on any editor OS, which is what makes this safe to do without touching the iOS/Android guards.

#if permutation analysis

Editor OS x active build target. UNITY_STANDALONE_WIN etc. are the target defines; UNITY_EDITOR_* the host defines.

# Editor OS Build target Before After
1 Windows Windows #elif UNITY_EDITOR_WIN true -> block compiled and runs; copyPDBFiles checked identical: block compiled and runs, #if UNITY_EDITOR_WIN true so copyPDBFiles still checked. No behavior change
2 macOS Windows UNITY_IOS false, UNITY_ANDROID false, UNITY_EDITOR_WIN false -> nothing compiled, silent no-op fixed: support files copied, mappings zipped, symbols uploaded, plus a warning that copyPDBFiles could not be read
3 Linux Windows same silent no-op as 2 same fix as 2
4 Windows iOS #if UNITY_IOS wins; Windows helpers still compiled by the file-level UNITY_EDITOR_WIN region helpers still compiled (now unconditionally); dispatch guard target == StandaloneWindows* is false -> no-op. Same reference set as before
5 macOS/Linux iOS helpers not compiled helpers now compiled but the #else arm is taken, so no UnityEditor.WindowsStandalone reference; dispatch is a no-op
6 Windows Android #elif UNITY_ANDROID wins; helpers compiled by the file-level region unchanged; dispatch no-op
7 macOS/Linux Android helpers not compiled helpers compiled, #else arm, dispatch no-op
8 any macOS Windows block absent or no-op; PostProcessMac runs unchanged
9 any Linux / WebGL / other no-op no-op

Two things this table makes explicit:

  • The only permutations whose runtime behavior changes are 2 and 3 — precisely the broken ones. Permutation 1, the common case, is untouched.
  • The permutations where new code becomes compiled but not executed (4-7) are the ones that could regress compilation, so they are the ones I actually compiled. See below.

One incidental robustness gain: because the Windows block now sits after #endif rather than inside the #elif chain, a build where the target defines disagree with the target argument (batch-mode -buildTarget before a script recompile) still runs the Windows steps instead of falling through the chain into nothing.

What I verified

I cannot run Unity, so there is no real player build behind this. What I did instead was compile the package's Editor/ and Runtime/ sources with dotnet build against Unity 6000.5.6f1's UnityEditor.dll / UnityEngine.dll, plus the real platform-module assemblies from Editor/Data/PlaybackEngines/, toggling the defines and the module references per permutation.

Compiled clean (0 errors) in all of:

Defines UnityEditor.WindowsStandalone.Extensions referenced Result
UNITY_EDITOR_WIN + UNITY_STANDALONE_WIN yes succeeded
UNITY_EDITOR_OSX + UNITY_STANDALONE_WIN no succeeded
UNITY_EDITOR_LINUX + UNITY_STANDALONE_WIN no succeeded
UNITY_EDITOR_OSX + UNITY_STANDALONE_OSX no succeeded
UNITY_EDITOR_WIN + UNITY_ANDROID (+ Android extensions) yes succeeded
UNITY_EDITOR_OSX + UNITY_ANDROID (+ Android extensions) no succeeded

Negative control, to prove the harness actually has teeth rather than silently resolving the module anyway: compiling with UNITY_EDITOR_WIN defined but the UnityEditor.WindowsStandalone.Extensions reference removed fails with CS0234: The type or namespace name 'WindowsStandalone' does not exist in the namespace 'UnityEditor'. So the non-Windows rows above passing is meaningful — the #else arm genuinely avoids the platform-module dependency.

Bug reproduced against the compiled output. I built the pre-fix and post-fix sources under UNITY_EDITOR_OSX + UNITY_STANDALONE_WIN and inspected the emitted assembly's metadata for the method names:

  • pre-fix: PostProcessWindows — absent. GetPEMachineArchitecture — absent.
  • post-fix: both present.

That is direct confirmation the whole Windows post-build path was being compiled away when cross-compiling, and that it now survives.

What I could NOT verify

Stating these plainly rather than implying broader coverage:

  • No Unity build was run. Nothing here proves the copied BugSplatMonitor.exe / BugSplatWer.dll actually catch a crash in a Mac-cross-compiled Windows player. It proves the code that copies them now compiles and is reachable.
  • UNITY_IOS permutations were not compiled. The iOS build module is not installed on this machine, so UnityEditor.iOS.Xcode cannot resolve and rows 4/5 of the compile table are reasoned-through only, not built. I consider the risk low because the change does not touch the #if UNITY_IOS arm and adds no reference the iOS-target compilation did not already have on a Windows editor, but it is unverified.
  • Whether Unity references UnityEditor.WindowsStandalone.Extensions on a macOS/Linux editor is not settled — and deliberately does not need to be. I kept the UNITY_EDITOR_WIN guard on that one API precisely so the answer does not matter. If it turns out the module is referenced cross-platform, the copyPDBFiles check could later be widened and the warning dropped; that would be a follow-up, not a prerequisite.
  • The #else warning fires on every cross-compiled Windows build, including ones where "Copy PDB files" is in fact enabled and everything works. That is intentional — the setting is unreadable from the host, so the honest message is "not checked" — but it is noise, and worth revisiting if it annoys people.
  • symbol-upload variant selection still keys on Application.platform, so an Intel Mac editor gets symbol-upload-macos rather than symbol-upload-macos-intel. Pre-existing, out of scope here, adjacent to G1: Symbol upload silently fails for registry and git package installs #150.

Merge-conflict surface

Scoped to only the Windows platform gating, to stay clear of the two other in-flight PRs against this file:

  • lines ~60-75 (the dispatch block)
  • lines ~93-103 (UploadSymbolFilesWin's guard)
  • one removed #endif at the old end of the Windows region

UploadSymbols / DownloadSymbolUpload (#150) and the Android upload callback (#162) are untouched.

🤖 Generated with Claude Code

The Windows post-build branch was selected by `#elif UNITY_EDITOR_WIN`, an
editor-OS define, in a chain whose other arms (`UNITY_IOS`, `UNITY_ANDROID`)
are build-target defines. Building a Windows player from a macOS or Linux
editor therefore compiled the entire Windows region out: no BugSplatMonitor.exe
/BugSplatRc.dll/BugSplatWer.dll next to the player, no LineNumberMappings.json
zipped, no symbol upload, and no warning. The build looked clean and native
crash reporting was inert at runtime.

Nothing in the support-file copy needs a Windows editor -- it reads the PE
machine field off the built executable and copies files out of the package's
Support~ directory, all plain System.IO. Gate it on the BuildTarget the
callback is already handed, matching how the macOS branch has always worked.

`UnityEditor.WindowsStandalone.UserBuildSettings.copyPDBFiles` does need a
Windows editor, so it keeps its `UNITY_EDITOR_WIN` guard, narrowed to the
check itself. Cross-compiling now warns that the setting could not be read
instead of silently doing nothing. The set of translation units that reference
`UnityEditor.WindowsStandalone` is unchanged, so no new platform-module
reference is introduced on any editor OS.

Closes #151

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 11, 2026 21:19

Copilot AI 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.

Pull request overview

Fixes Windows post-build processing when building a Windows player from a macOS/Linux Unity editor by keying the dispatch on BuildTarget (Windows) instead of UNITY_EDITOR_WIN (host editor OS), while keeping the one Windows-editor-only API call guarded.

Changes:

  • Ends the #if UNITY_IOS / #elif UNITY_ANDROID chain before the Windows branch so Windows post-build steps run for BuildTarget.StandaloneWindows* regardless of editor OS.
  • Removes the file-level #if UNITY_EDITOR_WIN wrapper around Windows helper methods so they remain compiled for cross-compiles.
  • Narrows the UNITY_EDITOR_WIN guard to only the UserBuildSettings.copyPDBFiles check and adds a warning on non-Windows editors when the setting can’t be read.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

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.

G3: Cross-compiling Windows from a macOS/Linux editor silently ships a broken build

3 participants