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