Skip to content

Commit cd04fc7

Browse files
committed
check for amazonlinux, zip correctly, drop 5.2 and 5.3
1 parent 907da4e commit cd04fc7

File tree

6 files changed

+112
-123
lines changed

6 files changed

+112
-123
lines changed

Package.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ let package = Package(
99
.library(name: "AWSLambdaRuntime", targets: ["AWSLambdaRuntime"]),
1010
// this has all the main functionality for lambda and it does not link Foundation
1111
.library(name: "AWSLambdaRuntimeCore", targets: ["AWSLambdaRuntimeCore"]),
12-
// plugin to package the lambda, preparing an archive that can be uploaded to AWS. requires docker.
12+
// plugin to package the lambda, creating an archive that can be uploaded to AWS
1313
.plugin(name: "AWSLambdaPackager", targets: ["AWSLambdaPackager"]),
1414
// for testing only
1515
.library(name: "AWSLambdaTesting", targets: ["AWSLambdaTesting"]),
@@ -35,7 +35,7 @@ let package = Package(
3535
]),
3636
.plugin(
3737
name: "AWSLambdaPackager",
38-
capability: .command(intent: .custom(verb: "archive", description: "Archive Lambda binary and prepare it for uploading to AWS"))
38+
capability: .command(intent: .custom(verb: "archive", description: "Archive the Lambda binary and prepare it for uploading to AWS. Requires docker on macOS."))
3939
),
4040
.testTarget(name: "AWSLambdaRuntimeCoreTests", dependencies: [
4141
.byName(name: "AWSLambdaRuntimeCore"),

[email protected]

-55
This file was deleted.

[email protected]

-1
This file was deleted.

[email protected]

-1
This file was deleted.

[email protected]

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// swift-tools-version:5.4
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "swift-aws-lambda-runtime",
7+
products: [
8+
// this library exports `AWSLambdaRuntimeCore` and adds Foundation convenience methods
9+
.library(name: "AWSLambdaRuntime", targets: ["AWSLambdaRuntime"]),
10+
// this has all the main functionality for lambda and it does not link Foundation
11+
.library(name: "AWSLambdaRuntimeCore", targets: ["AWSLambdaRuntimeCore"]),
12+
// for testing only
13+
.library(name: "AWSLambdaTesting", targets: ["AWSLambdaTesting"]),
14+
],
15+
dependencies: [
16+
.package(url: "https://github.com/apple/swift-nio.git", .upToNextMajor(from: "2.33.0")),
17+
.package(url: "https://github.com/apple/swift-log.git", .upToNextMajor(from: "1.4.2")),
18+
.package(url: "https://github.com/swift-server/swift-backtrace.git", .upToNextMajor(from: "1.2.3")),
19+
],
20+
targets: [
21+
.target(name: "AWSLambdaRuntime", dependencies: [
22+
.byName(name: "AWSLambdaRuntimeCore"),
23+
.product(name: "NIOCore", package: "swift-nio"),
24+
.product(name: "NIOFoundationCompat", package: "swift-nio"),
25+
]),
26+
.target(name: "AWSLambdaRuntimeCore", dependencies: [
27+
.product(name: "Logging", package: "swift-log"),
28+
.product(name: "Backtrace", package: "swift-backtrace"),
29+
.product(name: "NIOHTTP1", package: "swift-nio"),
30+
.product(name: "NIOCore", package: "swift-nio"),
31+
.product(name: "NIOConcurrencyHelpers", package: "swift-nio"),
32+
.product(name: "NIOPosix", package: "swift-nio"),
33+
]),
34+
.testTarget(name: "AWSLambdaRuntimeCoreTests", dependencies: [
35+
.byName(name: "AWSLambdaRuntimeCore"),
36+
.product(name: "NIOTestUtils", package: "swift-nio"),
37+
.product(name: "NIOFoundationCompat", package: "swift-nio"),
38+
]),
39+
.testTarget(name: "AWSLambdaRuntimeTests", dependencies: [
40+
.byName(name: "AWSLambdaRuntimeCore"),
41+
.byName(name: "AWSLambdaRuntime"),
42+
]),
43+
// testing helper
44+
.target(name: "AWSLambdaTesting", dependencies: [
45+
.byName(name: "AWSLambdaRuntime"),
46+
.product(name: "NIO", package: "swift-nio"),
47+
]),
48+
.testTarget(name: "AWSLambdaTestingTests", dependencies: ["AWSLambdaTesting"]),
49+
// for perf testing
50+
.target(name: "MockServer", dependencies: [
51+
.product(name: "NIOHTTP1", package: "swift-nio"),
52+
.product(name: "NIO", package: "swift-nio"),
53+
]),
54+
]
55+
)

[email protected]

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Package@swift-5.2.swift
1+
Package@swift-5.4.swift

Plugins/AWSLambdaPackager/Plugin.swift

+54-63
Original file line numberDiff line numberDiff line change
@@ -28,27 +28,29 @@ 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 },
@@ -142,10 +144,7 @@ struct AWSLambdaPackager: CommandPlugin {
142144
.product(product.name),
143145
parameters: parameters
144146
)
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 {
149148
throw Errors.unknownExecutable("no executable artifacts found for \(product.name)")
150149
}
151150
results[.init(product)] = artifact.path
@@ -170,14 +169,21 @@ struct AWSLambdaPackager: CommandPlugin {
170169
}
171170

172171
// 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)
176176
}
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)
178184

179185
#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]
181187
#else
182188
throw Error.unsupportedPlatform("can't or don't know how to create a zipfile on this platform")
183189
#endif
@@ -190,22 +196,6 @@ struct AWSLambdaPackager: CommandPlugin {
190196
)
191197

192198
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"
209199
}
210200
return archives
211201
}
@@ -223,25 +213,24 @@ struct AWSLambdaPackager: CommandPlugin {
223213

224214
let sync = DispatchGroup()
225215
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))
228219
sync.enter()
229220
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 {
231222
if verboseLogging {
232223
print(_output)
233224
fflush(stdout)
234225
}
235-
outputLock.lock()
236226
output += _output
237-
outputLock.unlock()
238227
}
239228
}
240229

241230
let stdoutPipe = Pipe()
242-
stdoutPipe.fileHandleForReading.readabilityHandler = outputHandler
231+
stdoutPipe.fileHandleForReading.readabilityHandler = { fileHandle in outputQueue.async { outputHandler(fileHandle.availableData) } }
243232
let stderrPipe = Pipe()
244-
stderrPipe.fileHandleForReading.readabilityHandler = outputHandler
233+
stderrPipe.fileHandleForReading.readabilityHandler = { fileHandle in outputQueue.async { outputHandler(fileHandle.availableData) } }
245234

246235
let process = Process()
247236
process.standardOutput = stdoutPipe
@@ -252,10 +241,10 @@ struct AWSLambdaPackager: CommandPlugin {
252241
process.currentDirectoryURL = URL(fileURLWithPath: workingDirectory.string)
253242
}
254243
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+
}
259248
}
260249

261250
try process.run()
@@ -270,13 +259,20 @@ struct AWSLambdaPackager: CommandPlugin {
270259

271260
return output
272261
}
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+
}
273270
}
274271

275272
private struct Configuration {
276273
public let outputDirectory: Path
277274
public let products: [Product]
278275
public let buildConfiguration: PackageManager.BuildConfiguration
279-
public let staticallyLinkRuntime: Bool
280276
public let verboseLogging: Bool
281277
public let baseImage: String
282278
public let version: String
@@ -286,11 +282,6 @@ private struct Configuration {
286282
self.outputDirectory = context.pluginWorkDirectory.appending(subpath: "\(AWSLambdaPackager.self)") // FIXME: read argument
287283
self.products = context.package.products.filter { $0 is ExecutableProduct } // FIXME: read argument, filter is ugly
288284
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
294285
self.verboseLogging = true // FIXME: read argument
295286
let swiftVersion = "5.6" // FIXME: read dynamically current version
296287
self.baseImage = "swift:\(swiftVersion)-amazonlinux2" // FIXME: read argument
@@ -311,7 +302,7 @@ private enum Errors: Error {
311302

312303
extension PackageManager.BuildResult {
313304
// 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? {
315306
let executables = self.builtArtifacts.filter { $0.kind == .executable && $0.path.lastComponent == product.name }
316307
guard !executables.isEmpty else {
317308
return nil

0 commit comments

Comments
 (0)