Contributing
This guide collects the things you need to know when modifying parts of the engine that other parts (or downstream installers) silently depend on. If you only see a section's symptom after shipping, the gates here are how you catch it before shipping next time.
Adding a new artifact to the installed editor
If you produce a new file at engine build time that ships in C:\Polyphase\ (Windows) or /opt/polyphase/ (Linux) — for example a new import library, runtime DLL, ICU data file, prebuilt asset, or generated header — you need to touch multiple files to actually get it into the installer payload. Missing any one of them produces an installer that "looks fine" but is broken in a way that's only visible to end users.
The pipeline (Windows)
[engine build] produces Standalone\Build\Windows\x64\ReleaseEditor\<artifact>
|
v
Installers/stage_distribution.py copies it into dist\Editor\<artifact>
|
v
Installers/Windows/PolyphaseSetup.iss [Files] section bundles dist\Editor\<artifact> into PolyphaseSetup-*.exe
|
v
Installers/build_installer_windows.bat drives the above + gates that each artifact is present
|
v
.github/workflows/release.yml the same gates in CI for tagged releases
Inno Setup only packages files that are explicitly listed in [Files]. A Source: "..\..\dist\Editor\foo.ext" line is required for every shipped file or directory. There is no implicit "ship everything in the staged dir" mode — staging the file is necessary but not sufficient.
The pipeline (Linux)
[engine build] produces Standalone\Build\Linux\PolyphaseEditor.elf
|
v
Installers/stage_distribution.py copies it into dist/Editor/<artifact>
|
v
Installers/build_deb_linux.sh does `cp -r dist/Editor/* $DEB_DIR/opt/polyphase/` (recursive — picks up new files automatically)
Installers/build_tarball_linux.sh does `cp -r dist/Editor $TARBALL_DIR/polyphase` (same)
Linux deb/tarball builders recursively copy whatever staging produced, so they don't have a per-file enumeration to forget. Linux's risk is upstream: forgetting to teach stage_distribution.py to copy the artifact in the first place.
Checklist when adding a shipped artifact
When you introduce a new file that needs to be on a user's installed machine:
- Engine build produces it at a known path. Confirm
msbuild Polyphase.sln /p:Configuration=ReleaseEditor /p:Platform=x64(Windows) ormake -C Standalone -f Makefile_Linux_Editor(Linux) writes the artifact reliably to a stable location. Installers/stage_distribution.pycopies it todist/Editor/. If it's a single file outside the engine's main copied subtrees (Engine/,External/,Standalone/,Tools/,Template/,Documentation/), add an explicitcopy_file(src, dst, verbose)block instage_distribution.pynear the existingPolyphase.lib/Lua.libblock (around line 277). If it lives inside an already-copied subtree, check thatEXCLUDED_EXTENSIONS(line 32) andEXCLUDED_DIRS(line 26) don't filter it out.Installers/Windows/PolyphaseSetup.issbundles it fromdist/Editor/. Add aSource:line in[Files]under the appropriateComponents: core | sdk | tools | docscomponent. Usecorefor files the editor needs to run,sdkfor files only addon authors need (headers, import libraries),toolsfor Python scripts,docsfor Documentation.- Add a presence gate to
Installers/build_installer_windows.bat. Right after the staging step, add anif not exist "dist\Editor\<artifact>" (echo ERROR ... & exit /b 1)check — staging failures otherwise produce a silently broken installer (stage_distribution.pyonlyprints warnings, it does not exit non-zero on missing inputs). - Add the same gate to
.github/workflows/release.ymlin theVerify staged import librariesstep (around line 90). CI is the only gate non-Windows contributors and tagged releases hit. - Verify the .exe payload, not just the staged dir. Both gates above only check that
dist/Editor/<artifact>exists — neither verifies that ISCC actually packaged it. If you forget step 3, both gates pass and the installer still ships broken. After ISCC, run7z l dist\PolyphaseSetup-*.exe | findstr /I "<artifact>"locally to confirm; consider adding this as a CI step. - Linux: usually automatic. As long as the file is in
dist/Editor/after staging, bothbuild_deb_linux.shandbuild_tarball_linux.shpick it up via recursive copy. The exceptions are files that need special placement (e.g./usr/share/applications/...) — those need an explicitcpinbuild_deb_linux.sh.
Files to grep when this kind of bug bites
If a user reports "addon X fails to load on installed Polyphase but works in VS", the cause is almost always a missing artifact in C:\Polyphase\. Walk back up the pipeline:
| Symptom | Where to look |
|---|---|
LNK1181: cannot open input file '<x>.lib' in {Project}\Intermediate\Plugins\<addonId>\<fingerprint>\build.bat |
PolyphaseSetup.iss [Files] (most likely culprit). Check dist\Editor\<x>.lib exists; if yes, .iss is missing the Source: line. |
dist\Editor\<x>.lib is missing |
stage_distribution.py either skipped it or EXCLUDED_EXTENSIONS filtered it. |
Standalone\Build\Windows\x64\ReleaseEditor\<x>.lib is missing |
Engine build itself is producing nothing — check the .vcxproj configuration. |
Failed to construct node '<X>' (type=N, unknown type?) |
Addon DLL didn't load. The build step before it failed silently — open {Project}\Intermediate\Plugins\<addonId>\<fingerprint>\build.bat and run it manually from a vcvars64-initialized prompt to surface the linker error. |
Why this is easy to miss
Each pipeline step has its own validation, but none of them validates the next step's inputs. The engine build doesn't know about staging, staging doesn't know about ISCC, ISCC doesn't know about the user's project. The .iss gap that motivated this guide (commit 2cfdb8c03 Bug: Build was not copying .libs fixed staging but not ISCC) is the canonical example. Add a gate at every layer or accept that the next omission will only surface as an end-user bug report.
Native addon ABI / fingerprint considerations
Engine/Source/Editor/Addons/NativeAddonManager.cpp::ComputeFingerprint (around line 1014) hashes the addon's source files plus the host CRT config tag (dbg vs rel) to produce the {config}_{hex} directory under {Project}/Intermediate/Plugins/<addonId>/. Two consequences worth knowing:
- The hash includes absolute source-file paths, so the dev editor and the installed editor never share a fingerprint folder — the installed editor will rebuild from scratch on first run, even if the dev editor already built a working DLL for the exact same source. This is intended — it lets a single project switch between editors safely — but it does mean addon developers should expect a one-time compile when they switch editors.
- The CRT tag (
/MDvs/MDd) is part of the fingerprint, so a debug-built addon will never be loaded by a release editor and vice versa. If your editor was built with a non-standard CRT, addons must be rebuilt to match — seeproject_addon_dll_crt_fingerprint.mdin the team memory for prior incidents.
Where the build pipeline is documented in detail
Documentation/Development/PackagingFlow.md—ActionManager::BuildData, the per-platform game packaging path. Different from the engine-installer flow this doc covers, but useful when an addon ships build hooks.Documentation/Development/SetupEnvironment/Compiling.md— local dev build prerequisites..github/workflows/release.yml— the source of truth for what a tagged release runs end to end. If you change anything inInstallers/, run through that workflow mentally before merging.