@@ -42,6 +42,7 @@ import func TSCBasic.exec
42
42
import protocol TSCBasic. OutputByteStream
43
43
import class TSCBasic. Process
44
44
import enum TSCBasic. ProcessEnv
45
+ import enum TSCBasic. ProcessLockError
45
46
import var TSCBasic. stderrStream
46
47
import class TSCBasic. TerminalController
47
48
import class TSCBasic. ThreadSafeOutputByteStream
@@ -109,14 +110,27 @@ extension SwiftCommand {
109
110
workspaceLoaderProvider: self . workspaceLoaderProvider
110
111
)
111
112
swiftTool. buildSystemProvider = try buildSystemProvider ( swiftTool)
112
- var toolError : Error ? = . none
113
+
114
+ // Try a non-blocking lock first so that we can inform the user about an already running SwiftPM.
113
115
do {
114
- try self . run ( swiftTool)
115
- if swiftTool. observabilityScope. errorsReported || swiftTool. executionStatus == . failure {
116
- throw ExitCode . failure
116
+ try swiftTool. fileSystem. withLock ( on: swiftTool. scratchDirectory, type: . exclusive, blocking: false ) { }
117
+ } catch let ProcessLockError . unableToAquireLock( errno) {
118
+ if errno == EWOULDBLOCK {
119
+ swiftTool. outputStream. write ( " Another instance of SwiftPM is already running using ' \( swiftTool. scratchDirectory) ', waiting until that process has finished execution... " . utf8)
120
+ swiftTool. outputStream. flush ( )
121
+ }
122
+ }
123
+
124
+ var toolError : Error ? = . none
125
+ try swiftTool. fileSystem. withLock ( on: swiftTool. scratchDirectory, type: . exclusive) {
126
+ do {
127
+ try self . run ( swiftTool)
128
+ if swiftTool. observabilityScope. errorsReported || swiftTool. executionStatus == . failure {
129
+ throw ExitCode . failure
130
+ }
131
+ } catch {
132
+ toolError = error
117
133
}
118
- } catch {
119
- toolError = error
120
134
}
121
135
122
136
// wait for all observability items to process
0 commit comments