@@ -28,41 +28,39 @@ 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 } ,
55
57
outputDirectory: configuration. outputDirectory,
56
58
verboseLogging: configuration. verboseLogging
57
59
)
58
60
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) " )
66
64
}
67
65
}
68
66
@@ -115,7 +113,7 @@ struct AWSLambdaPackager: CommandPlugin {
115
113
arguments: [ " run " , " --rm " , " -v " , " \( packageDirectory. string) :/workspace " , " -w " , " /workspace " , builderImageName, " bash " , " -cl " , buildCommand] ,
116
114
verboseLogging: verboseLogging
117
115
)
118
- #warning(" this knows too much about the underlying implementation")
116
+ // TODO: this knows too much about the underlying implementation
119
117
builtProducts [ . init( product) ] = packageDirectory. appending ( [ " .build " , buildConfiguration. rawValue, product. name] )
120
118
}
121
119
return builtProducts
@@ -142,10 +140,7 @@ struct AWSLambdaPackager: CommandPlugin {
142
140
. product( product. name) ,
143
141
parameters: parameters
144
142
)
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 {
149
144
throw Errors . unknownExecutable ( " no executable artifacts found for \( product. name) " )
150
145
}
151
146
results [ . init( product) ] = artifact. path
@@ -170,14 +165,21 @@ struct AWSLambdaPackager: CommandPlugin {
170
165
}
171
166
172
167
// 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)
176
172
}
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)
178
180
179
181
#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]
181
183
#else
182
184
throw Error . unsupportedPlatform ( " can't or don't know how to create a zipfile on this platform " )
183
185
#endif
@@ -190,22 +192,6 @@ struct AWSLambdaPackager: CommandPlugin {
190
192
)
191
193
192
194
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
195
}
210
196
return archives
211
197
}
@@ -223,25 +209,25 @@ struct AWSLambdaPackager: CommandPlugin {
223
209
224
210
let sync = DispatchGroup ( )
225
211
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
+ }
228
218
sync. enter ( )
229
219
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)
238
223
}
224
+ output += _output
239
225
}
240
226
241
227
let stdoutPipe = Pipe ( )
242
- stdoutPipe. fileHandleForReading. readabilityHandler = outputHandler
228
+ stdoutPipe. fileHandleForReading. readabilityHandler = { fileHandle in outputQueue . async { outputHandler ( fileHandle . availableData ) } }
243
229
let stderrPipe = Pipe ( )
244
- stderrPipe. fileHandleForReading. readabilityHandler = outputHandler
230
+ stderrPipe. fileHandleForReading. readabilityHandler = { fileHandle in outputQueue . async { outputHandler ( fileHandle . availableData ) } }
245
231
246
232
let process = Process ( )
247
233
process. standardOutput = stdoutPipe
@@ -252,10 +238,10 @@ struct AWSLambdaPackager: CommandPlugin {
252
238
process. currentDirectoryURL = URL ( fileURLWithPath: workingDirectory. string)
253
239
}
254
240
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
+ }
259
245
}
260
246
261
247
try process. run ( )
@@ -270,13 +256,20 @@ struct AWSLambdaPackager: CommandPlugin {
270
256
271
257
return output
272
258
}
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
+ }
273
267
}
274
268
275
269
private struct Configuration {
276
270
public let outputDirectory : Path
277
271
public let products : [ Product ]
278
272
public let buildConfiguration : PackageManager . BuildConfiguration
279
- public let staticallyLinkRuntime : Bool
280
273
public let verboseLogging : Bool
281
274
public let baseImage : String
282
275
public let version : String
@@ -286,11 +279,6 @@ private struct Configuration {
286
279
self . outputDirectory = context. pluginWorkDirectory. appending ( subpath: " \( AWSLambdaPackager . self) " ) // FIXME: read argument
287
280
self . products = context. package . products. filter { $0 is ExecutableProduct } // FIXME: read argument, filter is ugly
288
281
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
282
self . verboseLogging = true // FIXME: read argument
295
283
let swiftVersion = " 5.6 " // FIXME: read dynamically current version
296
284
self . baseImage = " swift: \( swiftVersion) -amazonlinux2 " // FIXME: read argument
@@ -311,7 +299,7 @@ private enum Errors: Error {
311
299
312
300
extension PackageManager . BuildResult {
313
301
// 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 ? {
315
303
let executables = self . builtArtifacts. filter { $0. kind == . executable && $0. path. lastComponent == product. name }
316
304
guard !executables. isEmpty else {
317
305
return nil
0 commit comments