@@ -28,41 +28,39 @@ struct AWSLambdaPackager: CommandPlugin {
2828 throw Errors . unknownProduct ( " no appropriate products found to package " )
2929 }
3030
31- #if os(macOS)
32- let builtProducts = try self . buildInDocker (
33- packageIdentity: context. package . id,
34- packageDirectory: context. package . directory,
35- products: configuration. products,
36- toolsProvider: { name in try context. tool ( named: name) . path } ,
37- outputDirectory: configuration. outputDirectory,
38- baseImage: configuration. baseImage,
39- buildConfiguration: configuration. buildConfiguration,
40- verboseLogging: configuration. verboseLogging
41- )
42- #elseif os(Linux)
43- let builtProducts = try self . build (
44- products: configuration. products,
45- buildConfiguration: configuration. buildConfiguration,
46- verboseLogging: configuration. verboseLogging
47- )
48- #else
49- throw Errors . unsupportedPlatform ( " only macOS and Linux are supported " )
50- #endif
31+ let builtProducts : [ LambdaProduct : Path ]
32+ if self . isAmazonLinux2 ( ) {
33+ // build directly on the machine
34+ builtProducts = try self . build (
35+ products: configuration. products,
36+ buildConfiguration: configuration. buildConfiguration,
37+ verboseLogging: configuration. verboseLogging
38+ )
39+ } else {
40+ // build with docker
41+ builtProducts = try self . buildInDocker (
42+ packageIdentity: context. package . id,
43+ packageDirectory: context. package . directory,
44+ products: configuration. products,
45+ toolsProvider: { name in try context. tool ( named: name) . path } ,
46+ outputDirectory: configuration. outputDirectory,
47+ baseImage: configuration. baseImage,
48+ buildConfiguration: configuration. buildConfiguration,
49+ verboseLogging: configuration. verboseLogging
50+ )
51+ }
5152
53+ // create the archive
5254 let archives = try self . package (
5355 products: builtProducts,
5456 toolsProvider: { name in try context. tool ( named: name) . path } ,
5557 outputDirectory: configuration. outputDirectory,
5658 verboseLogging: configuration. verboseLogging
5759 )
5860
59- if !archives. isEmpty {
60- print ( " \( archives. count) archives created: " )
61- for (product, archivePath) in archives {
62- print ( " * \( product. name) at \( archivePath. string) " )
63- }
64- } else {
65- print ( " no archives created " )
61+ print ( " \( archives. count > 0 ? archives. count. description : " no " ) archive \( archives. count != 1 ? " s " : " " ) created " )
62+ for (product, archivePath) in archives {
63+ print ( " * \( product. name) at \( archivePath. string) " )
6664 }
6765 }
6866
@@ -115,7 +113,7 @@ struct AWSLambdaPackager: CommandPlugin {
115113 arguments: [ " run " , " --rm " , " -v " , " \( packageDirectory. string) :/workspace " , " -w " , " /workspace " , builderImageName, " bash " , " -cl " , buildCommand] ,
116114 verboseLogging: verboseLogging
117115 )
118- #warning(" this knows too much about the underlying implementation")
116+ // TODO: this knows too much about the underlying implementation
119117 builtProducts [ . init( product) ] = packageDirectory. appending ( [ " .build " , buildConfiguration. rawValue, product. name] )
120118 }
121119 return builtProducts
@@ -142,10 +140,7 @@ struct AWSLambdaPackager: CommandPlugin {
142140 . product( product. name) ,
143141 parameters: parameters
144142 )
145- guard result. builtArtifacts. count <= 1 else {
146- throw Errors . unknownExecutable ( " too many executable artifacts found for \( product. name) " )
147- }
148- guard let artifact = result. builtArtifacts. first else {
143+ guard let artifact = result. executableArtifact ( for: product) else {
149144 throw Errors . unknownExecutable ( " no executable artifacts found for \( product. name) " )
150145 }
151146 results [ . init( product) ] = artifact. path
@@ -170,14 +165,21 @@ struct AWSLambdaPackager: CommandPlugin {
170165 }
171166
172167 // prep zipfile location
173- let zipfilePath = outputDirectory. appending ( product. name, " \( product. name) .zip " )
174- if FileManager . default. fileExists ( atPath: zipfilePath. string) {
175- try FileManager . default. removeItem ( atPath: zipfilePath. string)
168+ let workingDirectory = outputDirectory. appending ( product. name)
169+ let zipfilePath = workingDirectory. appending ( " \( product. name) .zip " )
170+ if FileManager . default. fileExists ( atPath: workingDirectory. string) {
171+ try FileManager . default. removeItem ( atPath: workingDirectory. string)
176172 }
177- try FileManager . default. createDirectory ( atPath: zipfilePath. removingLastComponent ( ) . string, withIntermediateDirectories: true )
173+ try FileManager . default. createDirectory ( atPath: workingDirectory. string, withIntermediateDirectories: true )
174+
175+ // rename artifact to "bootstrap"
176+ let relocatedArtifactPath = workingDirectory. appending ( artifactPath. lastComponent)
177+ let symbolicLinkPath = workingDirectory. appending ( " bootstrap " )
178+ try FileManager . default. copyItem ( atPath: artifactPath. string, toPath: relocatedArtifactPath. string)
179+ try FileManager . default. createSymbolicLink ( atPath: symbolicLinkPath. string, withDestinationPath: relocatedArtifactPath. lastComponent)
178180
179181 #if os(macOS) || os(Linux)
180- let arguments = [ " --junk-paths " , zipfilePath. string, artifactPath . string]
182+ let arguments = [ " --junk-paths " , " --symlinks " , zipfilePath. string, relocatedArtifactPath . string , symbolicLinkPath . string]
181183 #else
182184 throw Error . unsupportedPlatform ( " can't or don't know how to create a zipfile on this platform " )
183185 #endif
@@ -190,22 +192,6 @@ struct AWSLambdaPackager: CommandPlugin {
190192 )
191193
192194 archives [ product] = zipfilePath
193-
194- /*
195-
196- target=".build/lambda/$executable"
197- rm -rf "$target"
198- mkdir -p "$target"
199- cp ".build/release/$executable" "$target/"
200- # add the target deps based on ldd
201- ldd ".build/release/$executable" | grep swift | awk '{print $3}' | xargs cp -Lv -t "$target"
202- cd "$target"
203- ln -s "$executable" "bootstrap"
204- zip --symlinks lambda.zip *
205-
206- */
207- // docker run --rm -v "$workspace":/workspace -w /workspace/Examples/Deployment builder \
208- // bash -cl "./scripts/package.sh $executable"
209195 }
210196 return archives
211197 }
@@ -223,25 +209,25 @@ struct AWSLambdaPackager: CommandPlugin {
223209
224210 let sync = DispatchGroup ( )
225211 var output = " "
226- let outputLock = NSLock ( )
227- let outputHandler = { ( fileHandle: FileHandle ) in
212+ let outputQueue = DispatchQueue ( label: " AWSLambdaPackager.output " )
213+ let outputHandler = { ( data: Data ? ) in
214+ dispatchPrecondition ( condition: . onQueue( outputQueue) )
215+ guard let _output = data. flatMap ( { String ( data: $0, encoding: . utf8) ? . trimmingCharacters ( in: CharacterSet ( [ " \n " ] ) ) } ) , !_output. isEmpty else {
216+ return
217+ }
228218 sync. enter ( )
229219 defer { sync. leave ( ) }
230- if !fileHandle. availableData. isEmpty, let _output = String ( data: fileHandle. availableData, encoding: . utf8) ? . trimmingCharacters ( in: CharacterSet ( [ " \n " ] ) ) {
231- if verboseLogging {
232- print ( _output)
233- fflush ( stdout)
234- }
235- outputLock. lock ( )
236- output += _output
237- outputLock. unlock ( )
220+ if verboseLogging {
221+ print ( _output)
222+ fflush ( stdout)
238223 }
224+ output += _output
239225 }
240226
241227 let stdoutPipe = Pipe ( )
242- stdoutPipe. fileHandleForReading. readabilityHandler = outputHandler
228+ stdoutPipe. fileHandleForReading. readabilityHandler = { fileHandle in outputQueue . async { outputHandler ( fileHandle . availableData ) } }
243229 let stderrPipe = Pipe ( )
244- stderrPipe. fileHandleForReading. readabilityHandler = outputHandler
230+ stderrPipe. fileHandleForReading. readabilityHandler = { fileHandle in outputQueue . async { outputHandler ( fileHandle . availableData ) } }
245231
246232 let process = Process ( )
247233 process. standardOutput = stdoutPipe
@@ -252,10 +238,10 @@ struct AWSLambdaPackager: CommandPlugin {
252238 process. currentDirectoryURL = URL ( fileURLWithPath: workingDirectory. string)
253239 }
254240 process. terminationHandler = { _ in
255- // Read and pass on any remaining free-form text output from the plugin.
256- stderrPipe . fileHandleForReading. readabilityHandler ? ( stderrPipe . fileHandleForReading )
257- // Read and pass on any remaining messages from the plugin.
258- stdoutPipe . fileHandleForReading . readabilityHandler ? ( stdoutPipe . fileHandleForReading )
241+ outputQueue . async {
242+ outputHandler ( try ? stdoutPipe . fileHandleForReading. readToEnd ( ) )
243+ outputHandler ( try ? stderrPipe . fileHandleForReading . readToEnd ( ) )
244+ }
259245 }
260246
261247 try process. run ( )
@@ -270,13 +256,20 @@ struct AWSLambdaPackager: CommandPlugin {
270256
271257 return output
272258 }
259+
260+ private func isAmazonLinux2( ) -> Bool {
261+ if let data = FileManager . default. contents ( atPath: " /etc/system-release " ) , let release = String ( data: data, encoding: . utf8) {
262+ return release. hasPrefix ( " Amazon Linux release 2 " )
263+ } else {
264+ return false
265+ }
266+ }
273267}
274268
275269private struct Configuration {
276270 public let outputDirectory : Path
277271 public let products : [ Product ]
278272 public let buildConfiguration : PackageManager . BuildConfiguration
279- public let staticallyLinkRuntime : Bool
280273 public let verboseLogging : Bool
281274 public let baseImage : String
282275 public let version : String
@@ -286,11 +279,6 @@ private struct Configuration {
286279 self . outputDirectory = context. pluginWorkDirectory. appending ( subpath: " \( AWSLambdaPackager . self) " ) // FIXME: read argument
287280 self . products = context. package . products. filter { $0 is ExecutableProduct } // FIXME: read argument, filter is ugly
288281 self . buildConfiguration = . release // FIXME: read argument
289- #if os(Linux)
290- self . staticallyLinkRuntime = true // FIXME: read argument
291- #else
292- self . staticallyLinkRuntime = false // FIXME: read argument, warn if set to true
293- #endif
294282 self . verboseLogging = true // FIXME: read argument
295283 let swiftVersion = " 5.6 " // FIXME: read dynamically current version
296284 self . baseImage = " swift: \( swiftVersion) -amazonlinux2 " // FIXME: read argument
@@ -311,7 +299,7 @@ private enum Errors: Error {
311299
312300extension PackageManager . BuildResult {
313301 // find the executable produced by the build
314- func executableArtifact( for product: Product ) throws -> PackageManager . BuildResult . BuiltArtifact ? {
302+ func executableArtifact( for product: Product ) -> PackageManager . BuildResult . BuiltArtifact ? {
315303 let executables = self . builtArtifacts. filter { $0. kind == . executable && $0. path. lastComponent == product. name }
316304 guard !executables. isEmpty else {
317305 return nil
0 commit comments