Skip to content

Support macOS 15 with the bundled Rosetta runtime - #25

Closed
tplw wants to merge 2 commits into
NotProtonNot:mainfrom
tplw:macos-15-support
Closed

tplw wants to merge 2 commits into
NotProtonNot:mainfrom
tplw:macos-15-support

Conversation

@tplw

@tplw tplw commented Oct 7, 2026

Copy link
Copy Markdown

Summary

Adds macOS 15 support to NotProton and enables the Rosetta runtime bundled in the combined CrossOver Preview 20261006 distribution.

Implementation

macOS 15 app compatibility

  • Change the Swift package deployment target from macOS 26 to macOS 15.
  • Change LSMinimumSystemVersion to 15.0.
  • Lower the Swift tools requirement from 6.2 to 6.1.
  • Guard newer toolbar functionality so the app compiles with the older toolchain while retaining it on supported newer toolchains and systems.

Affected files include Package.swift, Info.plist, StatusView.swift, PrefixesView.swift, and BackupsView.swift.

Packaged resource loading

Add AppResources.swift to resolve resources from the packaged app before falling back to SwiftPM's generated resource accessor.

Swift 6.1's generated accessor can otherwise look beside the executable or in the development build tree instead of the app's Contents/Resources directory.

Update component, manifest, and detour loading to use this resource bundle. Packaging and tests now account for both flat and structured SwiftPM resource-bundle layouts.

Bundled Rosetta runtime selection

Add RunnerRuntime.swift to:

  • Detect availability of the page-size API required by FEX.
  • Read the .notproton-runtime selection marker.
  • Report a useful error when an unsupported FEX runtime is selected.

Add the pinned 27.0.0.41069-bundled-rosetta runtime profile.

Source-distribution detection remains separate from runtime selection: the combined distribution shares a loader identity between its FEX and bundled Rosetta profiles. The source lookup is deterministic, and hosts without the FEX API select the pinned bundled Rosetta profile.

Hosts with the required API retain the existing FEX selection. Distributions without a known, pinned fallback are not silently substituted.

Matching loader, wineserver, and bridge

When cloning the bundled Rosetta profile, write a rosetta runtime-selection marker.

The app and Steam launcher use that marker to select:

  • The x86 Wine loader.
  • The corresponding x86 wineserver.
  • The x86 Unix lsteamclient bridge.
  • A win64 prefix environment.

Runner verification checks the marker. Clones whose marker is missing are treated as incomplete and can be repaired through setup.

The Steam launcher also ignores ntdll patches belonging to the inactive 64-bit runtime when checking a combined distribution.

ntdll patches

Add an 864-byte detour2-bundled-41069.bin resource generated from the existing detour sources.

Add the matching pinned x86_64 ntdll patch for the combined distribution. Reuse its existing i386 patch, which is shared with the FEX profile.

Consolidate the shared patch definition in NtdllPatcher.swift and register both runtime profiles in the normal patch table.

Update:

  • ntdll-patch/resolve.py with the new pinned input and detour constants.
  • ntdll-patch/build-ntdll.sh with a bundled-rosetta-41069 flavor that verifies the input, payload, and patched output hashes.

No CrossOver DLLs are added to the repository.

Clone validation and safety

Verify a newly copied runtime before replacing an existing clone.

An unexpected source loader or ntdll input therefore does not destroy a working clone. Temporary staging directories are cleaned up after failure.

Launch errors and UI behavior

Previously, prefix tools discarded stdout/stderr and reported success immediately after spawning Wine, even if the loader immediately crashed.

The updated launch path:

  • Observes startup briefly without waiting for the application's entire lifetime.
  • Runs startup observation off the main actor.
  • Reports immediate nonzero exits and signal termination.
  • Saves output to notproton-tools.log.
  • Atomically replaces the latest-launch log to avoid mixing it with a previous tool's open output file.
  • Includes a bounded 16 KiB log tail in startup errors.
  • Logs later process exits through AppLog.
  • Reports “Launch requested” rather than claiming that a window opened.

The Status view also displays an actionable warning for an active FEX runtime that cannot run on the host.

Build and packaging changes

Native deployment targets

Propagate the macOS deployment target into Wine-tree setup and lsteamclient builds, defaulting to macOS 15.

Icons

Add a conventional .iconset/.icns generation path that works with macOS 15 Command Line Tools.

Preserve the layered Icon Composer path when a full Xcode installation provides a supported newer SDK and actool. The .icns fallback is retained, and the two pipelines use separate output directories.

ICON_COMPOSER=0 forces the legacy-only icon build.

App bundle validation

Update packaging to:

  • Refresh SwiftPM's payload-resource detection after staging.
  • Verify payload completeness with either SwiftPM resource layout.
  • Validate every bundled Mach-O architecture against the app's declared minimum OS version before signing.

Add helpers/check-deployment.py, its unit tests, and the deploymentcheck-tests Make target.

The validator checks declared deployment targets, not actual runtime API availability.

Tests and CI

Add a macOS 15 / Xcode 16.4 / Swift 6.1 CI job that compiles the app, runs selected tests without requiring CrossOver or the bridge payload, builds the legacy icon, and tests deployment validation.

New and expanded regression coverage includes:

  • Launch Services minimum OS metadata.
  • Flat and structured packaged resource layouts.
  • Source-distribution versus runtime-profile lookup.
  • Complete patch-table coverage.
  • App/Steam runtime-selection agreement.
  • Unsupported FEX refusal.
  • Preservation of the active runner when runtime selection fails.
  • Repair of missing runtime-selection markers.
  • Preservation of a working clone when source verification fails.
  • Immediate detached-process failure reporting.
  • Bounded launch-log capture.
  • Output capture when descendants retain output handles.
  • Per-architecture deployment-target validation.

Existing tests are adjusted for:

  • Foundation's handling of the System Settings URL on macOS 15.
  • Shared i386 inputs between runtime profiles, without confusing an inactive profile with the active bridge.
  • The additional early runtime refusal in the Steam launcher.

Isolated real-runtime test

Add an opt-in RealBundledRosettaTests suite.

It requires an activated, supported Preview installation and:

  1. Copies Preview into a temporary runner.
  2. Selects the bundled Rosetta profile independently of the test host's FEX capability.
  3. Applies and verifies both ntdll patches.
  4. Installs the matching bridge components.
  5. Boots a fresh prefix.
  6. Runs both 64-bit and 32-bit Windows commands.
  7. Stops only that temporary prefix's wineserver.

Commands have timeouts. The test does not modify the installed CrossOver, active runner, or existing game prefixes.

Validation performed

Tested on Apple Silicon running macOS 15.8 with Swift 6.1.2:

  • The 386-test Swift suite passed with RealRebuildTests excluded.
  • Additional focused shell, packaged-resource, and runtime-selection tests passed after the final refinements.
  • All 5 Python deployment-validator tests passed.
  • ShellCheck 0.11.0 and shell syntax checks passed.
  • C signature, resolver, compatibility, icon-resource, and spawn checks passed.
  • Both detour rebuilds reproduced their pinned output hashes.
  • The isolated patched Rosetta runtime successfully booted and ran 64-bit and 32-bit Windows commands.
  • The release app built successfully.
  • All 12 bundled native binaries passed deployment-target validation.
  • Strict code-signature verification passed.

Representative validation commands:

swift test --package-path app --no-parallel --skip RealRebuildTests
make deploymentcheck-tests
make scriptcheck runcheck

NOTPROTON_TEST_BUNDLED_ROSETTA=1 \
  swift test --package-path app --no-parallel \
  --filter RealBundledRosettaTests

FLAVOR=bundled-rosetta-41069 ntdll-patch/build-ntdll.sh
make app ICON_COMPOSER=0

Detour reproduction requires the existing cross-compilers and Python's capstone module.

The integration test supports NOTPROTON_TEST_CROSSOVER for a nonstandard Preview app path and NOTPROTON_TEST_TEMP for its temporary directory.

@NotProtonNot

Copy link
Copy Markdown
Owner

This is not going to get merged for 1.1.0 (it's nearly done and is on a different branch than what you based against). I can explore it for an update after that point. You are targeting Main, the development is happening on dev- branches currently.

I have not published a Contributors policy yet (apologies, under a great deal of work) but more or less the core of the policy is 'the PR text itself needs to be human written', since I am a human and will be reading it as well as 'a human needs to comment complex changes, so I can understand what I am looking at'. This change is minimally commented and the text in the PR itself is entirely AI, which makes me sad.

Anyway, this is a large change. Unfortunately, it's coming at a bad time to be reviewed (1.1.0 is more or less locked and is under going final touches and user testing now) and is targeting the 'main' branch rather than 'dev-1.1.0'.

@tplw

tplw commented Oct 7, 2026 •

Copy link
Copy Markdown
Author

@NotProtonNot
I know i also don't like to use ai for coding but i dont have any coding skills. I only used it because i really wanted to try out NotProton and also maybe share it to others in the process. So people that dont want/can upgrade to macOS 26 still can try it.
Anyway would u still allow a merge, if the next pull request is using the latest dev tree?
Even if i would used ai?

@NotProtonNot

Copy link
Copy Markdown
Owner

@NotProtonNot I know i also don't like to use ai for coding but i dont have any coding skills. I only used it because i really wanted to try out NotProton and also maybe share it to others in the process. So people that dont want/can upgrade to macOS 26 still can try it. Anyway would u still allow a merge, if the next pull request is using the latest dev tree? Even if i would used ai?

If you write the actual text in the PR reasonably well and hand write what the comments that the AI wrote in the code are for/what they do, I will take the code and rewrite it but attribute it to you and merge it - but it won't be immediately or even this week. Probably next week. It's a large change, needs a lot of testing and work.

@tplw

tplw commented Oct 7, 2026 •

Copy link
Copy Markdown
Author

@NotProtonNot I know i also don't like to use ai for coding but i dont have any coding skills. I only used it because i really wanted to try out NotProton and also maybe share it to others in the process. So people that dont want/can upgrade to macOS 26 still can try it. Anyway would u still allow a merge, if the next pull request is using the latest dev tree? Even if i would used ai?

If you write the actual text in the PR reasonably well and hand write what the comments that the AI wrote in the code are for/what they do, I will take the code and rewrite it but attribute it to you and merge it - but it won't be immediately or even this week. Probably next week. It's a large change, needs a lot of testing and work.

Ok understood. I Hope this code will still provides some use for you.

@tplw tplw closed this Oct 7, 2026
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.

2 participants