@@ -75,6 +75,7 @@ public struct SwiftScriptTool: ParsableCommand {
7575 Clean . self,
7676 Reset . self,
7777 Resolve . self,
78+ List . self,
7879 ] ,
7980 helpNames: [ . short, . long, . customLong( " help " , withSingleDash: true ) ] )
8081
@@ -138,7 +139,10 @@ extension SwiftScriptTool {
138139
139140 @OptionGroup ( )
140141 var options : ScriptToolOptions
141-
142+
143+ @Option ( name: . shortAndLong, help: " Save the prebuilt script binary " , transform: AbsolutePath . init)
144+ var output : AbsolutePath ?
145+
142146 func run( _ swiftTool: SwiftTool , as productName: String , at cacheDirPath: AbsolutePath ) throws {
143147 swiftTool. redirectStdoutToStderr ( )
144148 swiftTool. diagnostics. emit ( note: " Using cache: \( cacheDirPath. basename) " )
@@ -148,6 +152,15 @@ extension SwiftScriptTool {
148152 if options. shouldBuild {
149153 try buildSystem. build ( subset: . product( productName) )
150154 }
155+
156+ if let output = output {
157+ let executablePath = try swiftTool. buildParameters ( ) . buildPath. appending ( component: productName)
158+ guard !localFileSystem. isDirectory ( output) else {
159+ throw ScriptError . isDirectory ( output. pathString)
160+ }
161+ try ? localFileSystem. removeFileTree ( output)
162+ try localFileSystem. copy ( from: executablePath, to: output)
163+ }
151164 } catch let error as ScriptError {
152165 swiftTool. diagnostics. emit ( error)
153166 throw ExitCode . failure
@@ -238,6 +251,35 @@ extension SwiftScriptTool {
238251 }
239252}
240253
254+ extension SwiftScriptTool {
255+ struct List : ParsableCommand {
256+ static let configuration = CommandConfiguration (
257+ abstract: " List script caches " )
258+
259+ @OptionGroup ( _hiddenFromHelp: true )
260+ var swiftOptions : SwiftToolOptions
261+
262+ func run( ) throws {
263+ let cacheDir = try localFileSystem. getOrCreateSwiftScriptCacheDirectory ( )
264+ let scripts = try localFileSystem. getDirectoryContents ( cacheDir)
265+ let resolved = try scripts. map { script -> ( String , AbsolutePath ) in
266+ let sourceDir = cacheDir. appending ( components: script, " Sources " )
267+ guard localFileSystem. isDirectory ( sourceDir) ,
268+ let name = try localFileSystem. getDirectoryContents ( sourceDir) . first,
269+ case let realpath = resolveSymlinks ( sourceDir. appending ( components: name, " main.swift " ) ) else {
270+ throw Diagnostics . fatalError
271+ }
272+ return ( script, realpath)
273+ }
274+ print ( " \( scripts. count) script \( scripts. count > 1 ? " s " : " " ) cached at \( cacheDir) " )
275+ guard let maxLength = resolved. map ( \. 0 . count) . max ( ) else { return }
276+ resolved. forEach { ( name, path) in
277+ print ( name + String( repeating: " " , count: maxLength - name. count + 2 ) + path. pathString)
278+ }
279+ }
280+ }
281+ }
282+
241283extension SwiftScriptTool {
242284 struct ResolveOptions : ParsableArguments {
243285 @Option ( help: " The version to resolve at " , transform: { Version ( string: $0) } )
0 commit comments