Generate XOJIT preview thunks for source files reached through a symlink#1465
Merged
pmattos merged 2 commits intoJun 19, 2026
Merged
Conversation
When SwiftUI Previews run in XOJIT mode, SwiftCompilerSpec.generatePreviewInfo(.thunkInfo) locates the requested source file among the compile command's inputs and hands it to LibSwiftDriver.frontendCommandLine as the primary input. The lookup compared paths by exact string, so when the build description recorded the source under a symlink-resolved spelling (e.g. /private/tmp/...) while the preview request used the unresolved path (e.g. /tmp/...), no input matched: the driver returned no command line and generatePreviewInfo came back empty, which clients report as noPreviewInfos. This affects standalone Swift packages whose sources live under a symlinked directory such as /tmp (a symlink to /private/tmp on Darwin). Match the requested source file against the command's inputs by resolved path (realpath), and key the output-file-map and VFS overlay on the spelling that actually appears in the command. No-op when the paths already agree. Add previewXOJITThunkInfoResolvesSymlinkedSourcePath, which reproduces the mismatch via a symlink and fails without this change (zero preview infos), passing with it. rdar://176386125
Contributor
Author
|
@swift-ci test |
jakepetroules
approved these changes
Jun 18, 2026
f00e5d6 to
3459bd9
Compare
Contributor
Author
|
@swift-ci test |
Contributor
Author
|
@swift-ci test |
vsarunas
pushed a commit
to ordo-one/swift-build
that referenced
this pull request
Jun 30, 2026
…ink (swiftlang#1465) CONTEXT When SwiftUI Previews run in XOJIT mode, the previews client asks Swift Build — per source file — for the compile command that builds that file's "preview thunk", via generatePreviewInfo(.thunkInfo), identifying the file by path. In SwiftCompilerSpec.generatePreviewInfo, Swift Build locates that path among the target's compile inputs and hands it to LibSwiftDriver.frontendCommandLine as the primary input, which produces the swift-frontend invocation for the thunk. That lookup compares paths by exact string. If the source file was recorded in the build description under a different — but equivalent — spelling than the one in the request, nothing matches: frontendCommandLine returns no command line, generatePreviewInfo returns an empty result, and the client reports noPreviewInfos, so the preview never renders. This happens for standalone Swift packages whose sources live under a symlinked directory. On macOS, temporary directories such as /tmp are symlinks to /private/tmp, and SwiftPM's new package PIF builder symlink-resolves source paths (to match what the index store records). The compile command then holds /private/tmp/.../View.swift while the preview request still carries the unresolved /tmp/.../View.swift — the same file, a different spelling, no match. FIX In the XOJIT branch of SwiftCompilerSpec.generatePreviewInfo, match the requested source file against the command's inputs by resolved path (ie, FSProxy.realpath), and use the spelling that actually appears in the command as the primary input. The output-file-map and the VFS overlay are keyed on that same spelling so they line up with what the driver resolves. When the requested and recorded paths already agree — e.g. the legacy package PIF builder, which doesn't resolve symlinks — this is a no-op. TESTING Adds previewXOJITThunkInfoResolvesSymlinkedSourcePath to PreviewsBuildOperationTests. Rather than standing up a real package, it reproduces the spelling mismatch with a single symlink: it builds a one-file app under a real directory and then requests the preview thunk through a symlink to that directory, so the requested path and the build's input path resolve to the same file but differ as strings. It asserts that exactly one preview info comes back and that the returned compile command references the real (build) spelling, never the symlinked request path. Without the fix the test returns zero preview infos (ie, the noPreviewInfos condition); with it one, as expected. Fixes rdar://176386125 (and related to rdar://179887248).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
When SwiftUI Previews run in XOJIT mode, the previews client asks Swift Build — per source file — for the compile command that builds that file's "preview thunk", via
generatePreviewInfo(.thunkInfo), identifying the file by path. InSwiftCompilerSpec.generatePreviewInfo, Swift Build locates that path among the target's compile inputs and hands it toLibSwiftDriver.frontendCommandLineas the primary input, which produces theswift-frontendinvocation for the thunk.That lookup compares paths by exact string. If the source file was recorded in the build description under a different — but equivalent — spelling than the one in the request, nothing matches:
frontendCommandLinereturns no command line,generatePreviewInforeturns an empty result, and the client reportsnoPreviewInfos, so the preview never renders.This happens for standalone Swift packages whose sources live under a symlinked directory. On macOS, temporary directories such as
/tmpare symlinks to/private/tmp, and SwiftPM's new package PIF builder symlink-resolves source paths (to match what the index store records). The compile command then holds/private/tmp/.../View.swiftwhile the preview request still carries the unresolved/tmp/.../View.swift— the same file, a different spelling, no match.Fix
In the XOJIT branch of
SwiftCompilerSpec.generatePreviewInfo, match the requested source file against the command's inputs by resolved path (ie,FSProxy.realpath), and use the spelling that actually appears in the command as the primary input. The output-file-map and the VFS overlay are keyed on that same spelling so they line up with what the driver resolves. When the requested and recorded paths already agree — e.g. the legacy package PIF builder, which doesn't resolve symlinks — this is a no-op.Testing
Adds
previewXOJITThunkInfoResolvesSymlinkedSourcePathtoPreviewsBuildOperationTests. Rather than standing up a real package, it reproduces the spelling mismatch with a single symlink: it builds a one-file app under a real directory and then requests the preview thunk through a symlink to that directory, so the requested path and the build's input path resolve to the same file but differ as strings. It asserts that exactly one preview info comes back and that the returned compile command references the real (build) spelling, never the symlinked request path.Without the fix the test returns zero preview infos (ie, the
noPreviewInfoscondition); with it one, as expected.Fixes rdar://176386125 (and related to rdar://179887248).