@@ -28,27 +28,29 @@ struct AWSLambdaPackager: CommandPlugin {
28
28
throw Errors . unknownProduct ( " no appropriate products found to package " )
29
29
}
30
30
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
+ }
51
52
53
+ // create the archive
52
54
let archives = try self . package (
53
55
products: builtProducts,
54
56
toolsProvider: { name in try context. tool ( named: name) . path } ,
@@ -142,10 +144,7 @@ struct AWSLambdaPackager: CommandPlugin {
142
144
. product( product. name) ,
143
145
parameters: parameters
144
146
)
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 {
147
+ guard let artifact = result. executableArtifact ( for: product) else {
149
148
throw Errors . unknownExecutable ( " no executable artifacts found for \( product. name) " )
150
149
}
151
150
results [ . init( product) ] = artifact. path
@@ -170,14 +169,21 @@ struct AWSLambdaPackager: CommandPlugin {
170
169
}
171
170
172
171
// 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)
172
+ let workingDirectory = outputDirectory. appending ( product. name)
173
+ let zipfilePath = workingDirectory. appending ( " \( product. name) .zip " )
174
+ if FileManager . default. fileExists ( atPath: workingDirectory. string) {
175
+ try FileManager . default. removeItem ( atPath: workingDirectory. string)
176
176
}
177
- try FileManager . default. createDirectory ( atPath: zipfilePath. removingLastComponent ( ) . string, withIntermediateDirectories: true )
177
+ try FileManager . default. createDirectory ( atPath: workingDirectory. string, withIntermediateDirectories: true )
178
+
179
+ // rename artifact to "bootstrap"
180
+ let relocatedArtifactPath = workingDirectory. appending ( artifactPath. lastComponent)
181
+ let symbolicLinkPath = workingDirectory. appending ( " bootstrap " )
182
+ try FileManager . default. copyItem ( atPath: artifactPath. string, toPath: relocatedArtifactPath. string)
183
+ try FileManager . default. createSymbolicLink ( atPath: symbolicLinkPath. string, withDestinationPath: relocatedArtifactPath. lastComponent)
178
184
179
185
#if os(macOS) || os(Linux)
180
- let arguments = [ " --junk-paths " , zipfilePath. string, artifactPath . string]
186
+ let arguments = [ " --junk-paths " , " --symlinks " , zipfilePath. string, relocatedArtifactPath . string , symbolicLinkPath . string]
181
187
#else
182
188
throw Error . unsupportedPlatform ( " can't or don't know how to create a zipfile on this platform " )
183
189
#endif
@@ -190,22 +196,6 @@ struct AWSLambdaPackager: CommandPlugin {
190
196
)
191
197
192
198
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"
209
199
}
210
200
return archives
211
201
}
@@ -223,25 +213,24 @@ struct AWSLambdaPackager: CommandPlugin {
223
213
224
214
let sync = DispatchGroup ( )
225
215
var output = " "
226
- let outputLock = NSLock ( )
227
- let outputHandler = { ( fileHandle: FileHandle ) in
216
+ let outputQueue = DispatchQueue ( label: " AWSLambdaPackager.output " )
217
+ let outputHandler = { ( data: Data ? ) in
218
+ dispatchPrecondition ( condition: . onQueue( outputQueue) )
228
219
sync. enter ( )
229
220
defer { sync. leave ( ) }
230
- if !fileHandle . availableData . isEmpty , let _output = String ( data: fileHandle . availableData , encoding: . utf8) ? . trimmingCharacters ( in: CharacterSet ( [ " \n " ] ) ) {
221
+ if let _output = data . flatMap ( { String ( data: $0 , encoding: . utf8) ? . trimmingCharacters ( in: CharacterSet ( [ " \n " ] ) ) } ) , !_output . isEmpty {
231
222
if verboseLogging {
232
223
print ( _output)
233
224
fflush ( stdout)
234
225
}
235
- outputLock. lock ( )
236
226
output += _output
237
- outputLock. unlock ( )
238
227
}
239
228
}
240
229
241
230
let stdoutPipe = Pipe ( )
242
- stdoutPipe. fileHandleForReading. readabilityHandler = outputHandler
231
+ stdoutPipe. fileHandleForReading. readabilityHandler = { fileHandle in outputQueue . async { outputHandler ( fileHandle . availableData ) } }
243
232
let stderrPipe = Pipe ( )
244
- stderrPipe. fileHandleForReading. readabilityHandler = outputHandler
233
+ stderrPipe. fileHandleForReading. readabilityHandler = { fileHandle in outputQueue . async { outputHandler ( fileHandle . availableData ) } }
245
234
246
235
let process = Process ( )
247
236
process. standardOutput = stdoutPipe
@@ -252,10 +241,10 @@ struct AWSLambdaPackager: CommandPlugin {
252
241
process. currentDirectoryURL = URL ( fileURLWithPath: workingDirectory. string)
253
242
}
254
243
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 )
244
+ outputQueue . async {
245
+ outputHandler ( try ? stdoutPipe . fileHandleForReading. readToEnd ( ) )
246
+ outputHandler ( try ? stderrPipe . fileHandleForReading . readToEnd ( ) )
247
+ }
259
248
}
260
249
261
250
try process. run ( )
@@ -270,13 +259,20 @@ struct AWSLambdaPackager: CommandPlugin {
270
259
271
260
return output
272
261
}
262
+
263
+ private func isAmazonLinux2( ) -> Bool {
264
+ if let data = FileManager . default. contents ( atPath: " /etc/system-release " ) , let release = String ( data: data, encoding: . utf8) {
265
+ return release. hasPrefix ( " Amazon Linux release 2 " )
266
+ } else {
267
+ return false
268
+ }
269
+ }
273
270
}
274
271
275
272
private struct Configuration {
276
273
public let outputDirectory : Path
277
274
public let products : [ Product ]
278
275
public let buildConfiguration : PackageManager . BuildConfiguration
279
- public let staticallyLinkRuntime : Bool
280
276
public let verboseLogging : Bool
281
277
public let baseImage : String
282
278
public let version : String
@@ -286,11 +282,6 @@ private struct Configuration {
286
282
self . outputDirectory = context. pluginWorkDirectory. appending ( subpath: " \( AWSLambdaPackager . self) " ) // FIXME: read argument
287
283
self . products = context. package . products. filter { $0 is ExecutableProduct } // FIXME: read argument, filter is ugly
288
284
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
294
285
self . verboseLogging = true // FIXME: read argument
295
286
let swiftVersion = " 5.6 " // FIXME: read dynamically current version
296
287
self . baseImage = " swift: \( swiftVersion) -amazonlinux2 " // FIXME: read argument
@@ -311,7 +302,7 @@ private enum Errors: Error {
311
302
312
303
extension PackageManager . BuildResult {
313
304
// find the executable produced by the build
314
- func executableArtifact( for product: Product ) throws -> PackageManager . BuildResult . BuiltArtifact ? {
305
+ func executableArtifact( for product: Product ) -> PackageManager . BuildResult . BuiltArtifact ? {
315
306
let executables = self . builtArtifacts. filter { $0. kind == . executable && $0. path. lastComponent == product. name }
316
307
guard !executables. isEmpty else {
317
308
return nil
0 commit comments