Skip to content

Intermediates from build plugin generated files written to working directory #7930

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 task done
samdeane opened this issue Aug 29, 2024 · 25 comments · May be fixed by #8645
Open
1 task done

Intermediates from build plugin generated files written to working directory #7930

samdeane opened this issue Aug 29, 2024 · 25 comments · May be fixed by #8645
Labels

Comments

@samdeane
Copy link

samdeane commented Aug 29, 2024

Is it reproducible with SwiftPM command-line tools: swift build, swift test, swift package etc?

  • Confirmed reproduction steps with SwiftPM CLI. The description text must include reproduction steps with either of command-line SwiftPM commands, swift build, swift test, swift package etc.

Description

I have a buildCommand that generates a Swift file, and optionally some resources.
The generated files are written to the correct place, and can be used by the client of the plugin.
However, some intermediate files seem to sometimes be written to the current working directory, instead of the .build folder.

Specifically, for a generated file called Version.generated.swift, I am seeing the following files appear in the working directory:

  • Version.generated.o
  • Version.generated.swiftdeps
  • Version.generated.swiftdeps~
  • Version.generated.d

This does not seem to happen every time, but I've yet to establish the trigger. A race condition perhaps?

Expected behavior

Generated files are written into a plugin/target specific folder inside .build.
Any resulting intermediates go to the normal places inside .build.

Actual behavior

Generated files are written into a plugin/target specific folder inside .build.
Resulting intermediates sometimes leak into the working directory.

Sometimes the build produces an error (but not always)

image

Steps to reproduce

clone https://github.com/elegantchaos/ReleaseTools.git
checkout commit 7944ebb942e7681b44b00e25cc88837f23d31baf
swift run rt --help

Swift Package Manager version/commit hash

Swift Package Manager - Swift 6.0.0-dev

Swift & OS version (output of swift --version ; uname -a)

swift-driver version: 1.113 Apple Swift version 6.0 (swiftlang-6.0.0.7.6 clang-1600.0.24.1)
Target: arm64-apple-macosx14.0

Darwin space-monster.local 23.6.0 Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T6000 arm64

@samdeane samdeane added the bug label Aug 29, 2024
@samdeane
Copy link
Author

I have another unrelated reproducible case of this:

git clone [email protected]:elegantchaos/SwiftGodot
git checkout 559a6fbcef8c261ceeabe30f64690a8f1304e694
swift test

Version info:

+ uname -a
Darwin space-monster.local 23.6.0 Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T6000 arm64
+ sw_vers
ProductName:		macOS
ProductVersion:		14.6.1
BuildVersion:		23G93
+ swift --version
swift-driver version: 1.115 Apple Swift version 6.0.2 (swiftlang-6.0.2.1.2 clang-1600.0.26.4)
Target: arm64-apple-macosx14.0
+ swift package --version
Swift Package Manager - Swift 6.0.2-dev
+ xcodebuild -version
Xcode 16.2
Build version 16B5100e

@dschaefer2
Copy link
Member

Same issue reported here apple/swift-openapi-generator#676

@czechboy0
Copy link
Member

I spent some time today trying to reproduce this, and here are some notes.

Steps to reproduce:

From looking at the file system while the build is going, it seems to me that VS Code's background indexing (or even the lighter weight project prep that writes to .build pre-6.0, even with Background Indexing disabled in VS Code) is somehow fighting with the build task over some files in .build.

The file most central to my repro case seems to be .build/arm64-apple-macosx/debug/HelloWorldHummingbirdServer.build/output-file-map.json. Initially, VS Code seems to run the lightweight prep step, and this file only includes the HelloWorldHummingbirdExample.swift info. But then during the normal build, the generated files (Types.swift, Client.swift, Server.swift) also need to be included here.

But seems that even when doing a side build in Terminal with swift build, VS Code still keeps overwriting some of these files and removing the information about the generated files, which ultimately leads to them being placed in the wrong directory, and the linker failing to find them in the expected directory.

This is as far as I got, but wonder if this should be moved back into the Swift VS Code extension repo, before we can identify that this truly is a SwiftPM bug.

@jayrhynas
Copy link

This is happening to me intermittently when using just Terminal + Vim, so it's not specific to VS Code

@czechboy0
Copy link
Member

Might be LSP-triggered builds and regular builds interacting, yeah.

@czechboy0
Copy link
Member

czechboy0 commented Jan 10, 2025

cc @ahoppen for visibility, in case something similar has shown up with other IDEs that use sourcekit-lsp

@ahoppen
Copy link
Member

ahoppen commented Jan 13, 2025

I have seen this before in VS Code / SourceKit-LSP but haven’t been able to figure out what might be causing it.

@czechboy0
Copy link
Member

I'm working on isolating it into a smaller example. Seems to be a bug around output-file-map.json of modules that have plugin-generated files in them.

Out of curiosity, what's the expected locking mechanism over the contents of .build when a package is opened in an editor that uses sourcekit-lsp, and a manual build is run outside the IDE? How are those reads and writes coordinated between the two?

@czechboy0
Copy link
Member

Looks like sourcekit-lsp and swift-package are overwriting each other's output-file-map.json, which is a problem for targets with generated files by plugins, as the sourcekit-lsp's variant doesn't include the generated files, only swift-package's does. So if between swift-package writing the file and swift-driver ingesting the file there's enough time for sourcekit-lsp to rewrite the file again and remove the generated file entries, then the compilation will fail.

I'll try to reproduce with some arbitrary sleeps tomorrow on a trivial project. I'm still interested to learn what the intended synchronization mechanism between sourcekit-lsp and swift-package is supposed to be.

@dschaefer2
Copy link
Member

That could be a thing. The output-file-map is written early in the build planning which is totally the wrong place to do it.

@czechboy0
Copy link
Member

Right, for any target that has a plugin registered, output-file-map shouldn't be created until the plugin build command has been created and the output files are known (but the plugin build command itself can be run later, that's not a blocker).

@ahoppen
Copy link
Member

ahoppen commented Jan 13, 2025

Thanks for investigating, Honza. sourcekit-lsp should operate in .index-build (or .build/index-build in newer versions) and not touch anything in .build and thus shouldn’t interact with the build folder itself.

@czechboy0
Copy link
Member

@ahoppen does that mean that this is unexpected? Output of fs_events:

13:40:46.512191  stat64                 [  2]           /Users/honzadvorsky/Desktop/SwiftPMBugReproOrig/.build/arm64-apple-macosx/debug/HelloWorldHummingbirdServer.build/output-file-map.json                                0.000006   sourcekit-lsp.19452335
13:40:46.512197  getattrlist            [  2]           /Users/honzadvorsky/Desktop/SwiftPMBugReproOrig/.build/arm64-apple-macosx/debug/HelloWorldHummingbirdServer.build/output-file-map.json                                0.000002   sourcekit-lsp.19452335
13:40:46.512211  fstatat64              [  2]           [-2]//Users/honzadvorsky/Desktop/SwiftPMBugReproOrig/.build/arm64-apple-macosx/debug/HelloWorldHummingbirdServer.build/.dat.nosyncD6C6.OYmwOf                         0.000004   sourcekit-lsp.19452335
13:40:46.512221  getattrlist                            /Users/honzadvorsky/Desktop/SwiftPMBugReproOrig/.build/arm64-apple-macosx/debug/HelloWorldHummingbirdServer.build                                                     0.000003   sourcekit-lsp.19452335
13:40:46.512273  open              F=6        (RWC__E______)  /Users/honzadvorsky/Desktop/SwiftPMBugReproOrig/.build/arm64-apple-macosx/debug/HelloWorldHummingbirdServer.build/.dat.nosyncD6C6.OYmwOf                        0.000052   sourcekit-lsp.19452335
13:40:46.512309  write             F=6    B=0x396                                                                                                                                                                             0.000034   sourcekit-lsp.19452335
13:40:46.512343    WrData[A]       D=0x0eab0951  B=0x1000   /dev/disk3s5  /Users/honzadvorsky/Desktop/SwiftPMBugReproOrig/.build/arm64-apple-macosx/debug/HelloWorldHummingbirdServer.build/.dat.nosyncD6C6.OYmwOf            0.000025 W sourcekit-lsp.19452335
13:40:46.512352  fsync             F=6                                                                                                                                                                                        0.000044   sourcekit-lsp.19452335
13:40:46.512447  rename                                 /Users/honzadvorsky/Desktop/SwiftPMBugReproOrig/.build/arm64-apple-macosx/debug/HelloWorldHummingbirdServer.build/.dat.nosyncD6C6.OYmwOf                              0.000092   sourcekit-lsp.19452335
13:40:46.512453  close             F=6                                                                                                                                                                                        0.000005   sourcekit-lsp.19452335

See that sourcekit-lsp is checking for, and then creating, the file /Users/honzadvorsky/Desktop/SwiftPMBugReproOrig/.build/arm64-apple-macosx/debug/HelloWorldHummingbirdServer.build/output-file-map.json.

Is this unexpected?

@czechboy0
Copy link
Member

Confirmed this with more logging. sourcekit-lsp and swift-package (triggered by the manual build from VS Code) are fighting over output-file-map.json. swift-package is writing the correct one, which includes the generated file entries. sourcekit-lsp keeps overwriting it with the wrong one, which doesn't include the generate file entries. Both are using .build as the scratch path by default.

I suspect that this only causes issues whenever swift-package and sourcekit-lsp disagree on which files are included in a target, which I've only seen happen for targets that use build plugins.

I have two questions:

  1. Is sourcekit-lsp supposed to be operating in .build? I'm using Xcode 16.2 with Swift 6.0.3 on macOS, and the latest VS Code Swift extension.
  2. Why isn't sourcekit-lsp including the generated files? Is it caused by Source generated by build plugin is ignored sourcekit-lsp#665, which links to Add ability to get compiler arguments for files generated by build plugin without running build plugin #6700?

@czechboy0
Copy link
Member

And the reason why this isn't 100% reproducible (but I can ~100% reproduce it using the instructions I shared earlier on a specific sample project) is because this bug only shows up if there's enough time between swift-package writing the file and swift-driver reading the file for sourcekit-lsp to overwrite. On very small projects, there isn't enough time, so the bug doesn't reproduce. But when the plugin e.g. depends on a CLI that takes a few seconds to compile, and you keep VS Code open so that things are being watched/updated constantly, then it happens every time.

@czechboy0
Copy link
Member

Only workaround to be able to build from VS Code without this bug is disabling sourcekit-lsp in the extension's settings, which is obviously a quite nuclear option. I'm not currently aware of a better workaround.

@jayrhynas
Copy link

This does seem to match up with my environment: a package with a build plugin (swift-argument-parser) and sourcekit-lsp (via Sublime Text).

Maybe one workaround for now would be to adjust the swift build command in VS code to pass a different --scratch-path other than .build?

@samdeane
Copy link
Author

The bug manifests for me when building from the command line. That said, I'm wondering whether I've always had vscode open, and potentially doing background indexing, at the same time.

Next time I hit the problem I will try shutting down xcode and making sure that sourcekit-lsp isn't doing anything, and see if the issue goes away.

@samdeane
Copy link
Author

samdeane commented Jan 23, 2025

If I quit vscode and build on the command line, it does seem to fix the problem.

I was hoping that turning off background indexing and background compilation in the plugin would be enough to fix it with VS code still running, but sadly not. The plugin is still doing something with swift-lsp even then, and it's enough to reproduce the bug.

@ahoppen
Copy link
Member

ahoppen commented Jan 24, 2025

Turning on background indexing should help here because with background indexing enabled, SourceKit-LSP will operate in its own .build/index-build (or .index-build in older toolchains) scratch path and thus not interfere with the real build.

@tayloraswift
Copy link
Member

i run into this on nearly every build with a project that uses package-benchmark

remark: Incremental compilation has been disabled: __BenchmarkBoilerplate.swift has no swiftDeps file
remark: Incremental compilation has been disabled: __BenchmarkBoilerplate.swift has no swiftDeps file
remark: Incremental compilation has been disabled: malformed dependencies file 'none?!'

there doesn’t seem to be any way to get around this, except to close out VSCode each time and build the project from the command line.

@plemarquand
Copy link
Contributor

@tayloraswift does turning on background indexing in sourcekit-lsp help? Then it will use .index-build and wont contend with other build operations.

@dschaefer2
Copy link
Member

I've been looking for a package that consistently reproduces this.

@tayloraswift
Copy link
Member

https://github.com/ordo-one/external-reproducers/tree/existential-copy/benchmarks/existential-copy is a good example of a project that frequently experiences this issue

@dschaefer2
Copy link
Member

@tayloraswift Awesome. Thanks! I'll take a look. It's very weird it doesn't happen all the time. Hard to think of what would be non-deterministic there. But must be.

jakepetroules added a commit that referenced this issue May 9, 2025
…ally

This resolves an issue where outputs of build tool plugins could be emitted to a random output directory based on whatever the working directory of the calling process happened to be at the time the build tool plugin was executed.

Closes #7930
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
7 participants