diff --git a/Plugins/AWSLambdaBuilder/Plugin.swift b/Plugins/AWSLambdaBuilder/Plugin.swift index 0450d8d5..1a9ca86a 100644 --- a/Plugins/AWSLambdaBuilder/Plugin.swift +++ b/Plugins/AWSLambdaBuilder/Plugin.swift @@ -44,12 +44,45 @@ struct AWSLambdaBuilder: CommandPlugin { ) } - // Resolve the container CLI that matches the requested cross-compilation method. The plugin - // sandbox can only run tools it resolves up front, so we must pick the right binary here: + // Resolve the tool that matches the requested cross-compilation method. The plugin sandbox + // can only run tools it resolves up front, so we must pick the right binary here: + // `swift` for `--cross-compile swift-static-sdk` (no container runtime needed), // `container` for `--cross-compile container`, `docker` otherwise. let crossCompileMethod = crossCompileArgument.first?.lowercased() - let containerCLIToolName = crossCompileMethod == "container" ? "container" : "docker" - let containerToolPath = try context.tool(named: containerCLIToolName).url + let crossCompileToolName: String + switch crossCompileMethod { + case "swift-static-sdk": + crossCompileToolName = "swift" + // The Static Linux SDK builds without a container, so the docker/container-specific + // options do not apply. Reject them here rather than let them be silently ignored. + // These flags are forwarded verbatim to the helper, so inspect the raw arguments. + let incompatibleWithStaticSDK = [ + "--base-docker-image", + "--swift-version", + "--disable-docker-image-update", + "--base-oci-image", + ] + for flag in incompatibleWithStaticSDK where arguments.contains(flag) { + throw BuilderErrors.invalidArgument( + "'\(flag)' cannot be used with '--cross-compile swift-static-sdk'; it targets a " + + "container-based build. Remove it, or choose '--cross-compile docker' or 'container'." + ) + } + // The OCI image build requires a container CLI, so it is incompatible too. Match the + // value that follows --archive-format rather than a bare "oci" token anywhere. + if let formatIndex = arguments.firstIndex(of: "--archive-format"), + arguments.indices.contains(formatIndex + 1), + arguments[formatIndex + 1].lowercased() == "oci" + { + throw BuilderErrors.invalidArgument( + "'--archive-format oci' cannot be used with '--cross-compile swift-static-sdk'; " + + "building an OCI image requires a container CLI. Use '--cross-compile docker' or 'container'." + ) + } + case "container": crossCompileToolName = "container" + default: crossCompileToolName = "docker" + } + let crossCompileToolPath = try context.tool(named: crossCompileToolName).url let zipToolPath = try context.tool(named: "zip").url // Resolve the output directory. The default lives under the plugin's work directory, whose @@ -90,7 +123,7 @@ struct AWSLambdaBuilder: CommandPlugin { "--package-display-name", context.package.displayName, "--package-directory", context.package.directoryURL.path(), "--configuration", configurationArgument.first ?? "release", - "--cross-compile-tool-path", containerToolPath.path, + "--cross-compile-tool-path", crossCompileToolPath.path, "--zip-tool-path", zipToolPath.path, ] // Re-inject the cross-compilation method (normalised to --cross-compile) so the helper can diff --git a/Sources/AWSLambdaPluginHelper/lambda-build/BuildArchitecture.swift b/Sources/AWSLambdaPluginHelper/lambda-build/BuildArchitecture.swift index 8a4e4c10..cb923f44 100644 --- a/Sources/AWSLambdaPluginHelper/lambda-build/BuildArchitecture.swift +++ b/Sources/AWSLambdaPluginHelper/lambda-build/BuildArchitecture.swift @@ -32,6 +32,15 @@ enum BuildArchitecture: String, Codable, CustomStringConvertible { #endif } + /// The Static Linux SDK (musl) target triple for this architecture, passed to + /// `swift build --swift-sdk`. + var muslTriple: String { + switch self { + case .x64: return "x86_64-swift-linux-musl" + case .arm64: return "aarch64-swift-linux-musl" + } + } + /// Parses the `--architecture` value, defaulting to the host architecture when omitted. static func parse(_ value: String?) throws -> Self { guard let value else { diff --git a/Sources/AWSLambdaPluginHelper/lambda-build/BuildBackends/StaticLinuxSDKBuildBackend.swift b/Sources/AWSLambdaPluginHelper/lambda-build/BuildBackends/StaticLinuxSDKBuildBackend.swift new file mode 100644 index 00000000..20744875 --- /dev/null +++ b/Sources/AWSLambdaPluginHelper/lambda-build/BuildBackends/StaticLinuxSDKBuildBackend.swift @@ -0,0 +1,126 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftAWSLambdaRuntime open source project +// +// Copyright SwiftAWSLambdaRuntime project authors +// Copyright (c) Amazon.com, Inc. or its affiliates. +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +/// Cross-compiles products with the Static Linux SDK (musl), producing a statically-linked binary +/// that runs on Amazon Linux without a container runtime. +/// +/// Unlike ``ContainerBuildBackend`` this shells out to `swift` directly on the host — no docker or +/// Apple `container` involved. The target architecture is selected with `--swift-sdk `, +/// so this backend genuinely cross-compiles (e.g. building an arm64 binary on an x64 host and vice +/// versa), independent of the host architecture. +/// +/// The SDK must be installed beforehand (`swift sdk install …`). The plugin's network sandbox +/// (`.docker` scope) forbids downloading it at build time, so this backend detects a missing SDK +/// and fails with install guidance rather than attempting to fetch it. +@available(LambdaSwift 2.0, *) +struct StaticLinuxSDKBuildBackend: BuildBackend { + /// The target CPU architecture, mapped to a musl target triple via ``BuildArchitecture/muslTriple``. + let architecture: BuildArchitecture + + /// Path to the `swift` executable resolved by the plugin (the toolchain location differs across + /// hosts, so we never hardcode `/usr/bin/swift` here). + let swiftToolPath: URL + + let name = "swift-static-sdk" + + func build( + packageIdentity: String, + packageDirectory: URL, + products: [String], + buildConfiguration: BuildConfiguration, + noStrip: Bool, + verboseLogging: Bool + ) throws -> [String: URL] { + + // verify the swift binary exists at the resolved path + guard FileManager.default.fileExists(atPath: self.swiftToolPath.path()) else { + throw BuilderErrors.swiftToolNotFound(self.swiftToolPath.path()) + } + + let triple = self.architecture.muslTriple + + // Build into a dedicated scratch path, NOT the package's default `.build`. This plugin runs + // as a SwiftPM command plugin, which holds the workspace lock on `.build` for its whole + // duration; a nested `swift build` targeting the same `.build` would block forever waiting + // for that lock. A separate scratch path sidesteps the deadlock (the container backend does + // not hit this because its build runs inside the container, not against the host `.build`). + let scratchPath = packageDirectory.appending(path: ".build").appending(path: "lambda-static-sdk") + + // Resolve the build output path with the same `--swift-sdk` selector the build uses. This + // doubles as the SDK preflight: SwiftPM resolves the SDK exactly as a real build would, so + // if no SDK targets the triple this fails, and we surface actionable install guidance. We + // cannot download the SDK ourselves (the plugin sandbox limits network to Docker). + // + // `swift sdk list` is deliberately NOT used here: it prints SDK bundle identifiers (e.g. + // `swift-…_static-linux-0.1.0`), which do not contain the target triple, so matching on the + // triple gives false negatives even when the SDK is installed. + let binPath: String + do { + binPath = try Utils.execute( + executable: self.swiftToolPath, + arguments: [ + "build", "-c", buildConfiguration.rawValue, + "--swift-sdk", triple, + "--scratch-path", scratchPath.path(), + "--show-bin-path", + ], + customWorkingDirectory: packageDirectory, + logLevel: verboseLogging ? .debug : .silent + ).trimmingCharacters(in: .whitespacesAndNewlines) + } catch { + throw BuilderErrors.staticSDKNotInstalled(triple) + } + let buildOutputPath = URL(fileURLWithPath: binPath) + + print("-------------------------------------------------------------------------") + print("building \"\(packageIdentity)\" with the Static Linux SDK (\(triple))") + print("-------------------------------------------------------------------------") + + var builtProducts = [String: URL]() + for product in products { + print("building \"\(product)\"") + var buildArguments = [ + "build", "-c", buildConfiguration.rawValue, + "--product", product, + "--swift-sdk", triple, + "--scratch-path", scratchPath.path(), + "--static-swift-stdlib", + ] + if !noStrip { + buildArguments += ["-Xlinker", "-s"] + } + try Utils.execute( + executable: self.swiftToolPath, + arguments: buildArguments, + customWorkingDirectory: packageDirectory, + logLevel: verboseLogging ? .debug : .output + ) + + let productPath = buildOutputPath.appending(path: product) + guard FileManager.default.fileExists(atPath: productPath.path()) else { + print("expected '\(product)' binary at \"\(productPath.path())\"") + throw BuilderErrors.productExecutableNotFound(product) + } + builtProducts[product] = productPath + } + return builtProducts + } +} diff --git a/Sources/AWSLambdaPluginHelper/lambda-build/Builder.swift b/Sources/AWSLambdaPluginHelper/lambda-build/Builder.swift index c8f4db49..f9259a91 100644 --- a/Sources/AWSLambdaPluginHelper/lambda-build/Builder.swift +++ b/Sources/AWSLambdaPluginHelper/lambda-build/Builder.swift @@ -35,9 +35,13 @@ struct Builder { } // Select the build backend: build natively when already on an Amazon Linux host, - // otherwise cross-compile using the backend chosen by --cross-compile. + // otherwise cross-compile using the backend chosen by --cross-compile. An explicit + // --cross-compile swift-static-sdk always cross-compiles (it can target either + // architecture), so it takes precedence over the native path even on Amazon Linux. let backend: any BuildBackend - if self.isAmazonLinux(.al2) || self.isAmazonLinux(.al2023) { + if configuration.crossCompileMethod == .swiftStaticSdk { + backend = try configuration.makeCrossCompileBackend() + } else if self.isAmazonLinux(.al2) || self.isAmazonLinux(.al2023) { // A native build compiles for the host architecture only; it cannot target another one. // Recording a mismatched architecture in the manifest would recreate the very bug the // --architecture flag exists to prevent, so reject an explicit cross-architecture request. @@ -152,7 +156,10 @@ struct Builder { --cross-compile The cross-compilation method to use. Values: docker, container, swift-static-sdk, custom-sdk (default is docker) - Note: swift-static-sdk and custom-sdk are not yet supported. + swift-static-sdk requires a pre-installed Static Linux + SDK (musl); it needs no docker/container. Install it with + 'swift sdk install '. + Note: custom-sdk is not yet supported. --archive-format The packaging format for the build artifact. Values: zip, oci (default is zip) @@ -329,15 +336,26 @@ struct BuilderConfiguration: CustomStringConvertible { /// everything a backend needs (the resolved tool path, base image, and image-update /// preference), so the factory lives here rather than on ``CrossCompileMethod``. func makeCrossCompileBackend() throws -> any BuildBackend { - let cli = try self.makeContainerCLI() - return ContainerBuildBackend( - cli: cli, - toolPath: self.crossCompileToolPath, - baseImage: self.baseDockerImage, - disableImageUpdate: self.disableDockerImageUpdate, - architecture: self.architecture, - method: self.crossCompileMethod - ) + switch self.crossCompileMethod { + case .docker, .container: + return ContainerBuildBackend( + cli: try self.makeContainerCLI(), + toolPath: self.crossCompileToolPath, + baseImage: self.baseDockerImage, + disableImageUpdate: self.disableDockerImageUpdate, + architecture: self.architecture, + method: self.crossCompileMethod + ) + case .swiftStaticSdk: + // The Static Linux SDK build shells out to `swift`, not a container CLI. The plugin + // resolves the swift toolchain and forwards its path via --cross-compile-tool-path. + return StaticLinuxSDKBuildBackend( + architecture: self.architecture, + swiftToolPath: self.crossCompileToolPath + ) + case .customSdk: + throw BuilderErrors.unsupportedCrossCompileMethod(self.crossCompileMethod) + } } /// Resolves the ``ContainerCLI`` argument flavor for the configured cross-compile method. @@ -402,6 +420,8 @@ enum BuilderErrors: Error, CustomStringConvertible { case unsupportedCrossCompileMethod(CrossCompileMethod) case unsupportedArchiveFormat(ArchiveFormat) case containerCLINotFound(CrossCompileMethod) + case swiftToolNotFound(String) + case staticSDKNotInstalled(String) case failedWritingDockerfile case failedParsingDockerOutput(String) case processFailed([String], Int32) @@ -439,6 +459,14 @@ enum BuilderErrors: Error, CustomStringConvertible { + "For information on how to install and use Swift cross-compilation SDKs, visit: " + "https://www.swift.org/documentation/articles/static-linux-getting-started.html" } + case .swiftToolNotFound(let path): + return "The 'swift' executable was not found at the expected path '\(path)'." + case .staticSDKNotInstalled(let triple): + return + "No Static Linux SDK targeting '\(triple)' is installed. " + + "Install it with 'swift sdk install ' and try again. " + + "For information on how to install and use Swift cross-compilation SDKs, visit: " + + "https://www.swift.org/documentation/articles/static-linux-getting-started.html" case .failedWritingDockerfile: return "failed writing dockerfile" case .failedParsingDockerOutput(let output): diff --git a/Sources/AWSLambdaPluginHelper/lambda-build/CrossCompileMethod.swift b/Sources/AWSLambdaPluginHelper/lambda-build/CrossCompileMethod.swift index 20a7e146..9df83b47 100644 --- a/Sources/AWSLambdaPluginHelper/lambda-build/CrossCompileMethod.swift +++ b/Sources/AWSLambdaPluginHelper/lambda-build/CrossCompileMethod.swift @@ -34,8 +34,8 @@ enum CrossCompileMethod: String, CustomStringConvertible { var isSupported: Bool { switch self { - case .docker, .container: return true - case .swiftStaticSdk, .customSdk: return false + case .docker, .container, .swiftStaticSdk: return true + case .customSdk: return false } } diff --git a/Sources/AWSLambdaRuntime/Docs.docc/using-the-spm-plugins.md b/Sources/AWSLambdaRuntime/Docs.docc/using-the-spm-plugins.md index 5b6925c8..aa3f1d37 100644 --- a/Sources/AWSLambdaRuntime/Docs.docc/using-the-spm-plugins.md +++ b/Sources/AWSLambdaRuntime/Docs.docc/using-the-spm-plugins.md @@ -30,8 +30,9 @@ Because SwiftPM plugins run in a sandbox, some plugins require you to explicitly grant permissions on the command line: - `lambda-init` needs `--allow-writing-to-package-directory` to write files. -- `lambda-build` needs `--allow-network-connections docker` to - reach the build container. +- `lambda-build` needs `--allow-network-connections docker` for the default + Docker build. The `container` and `swift-static-sdk` methods need + `--disable-sandbox` instead (see [Choosing how to compile](#Choosing-how-to-compile)). - `lambda-deploy` needs `--allow-network-connections all:443` to call the AWS APIs. > Tip: You can see a quick end-to-end walkthrough in . @@ -66,9 +67,12 @@ swift package lambda-init --allow-writing-to-package-directory --with-url ## lambda-build `lambda-build` compiles your executable targets for Amazon Linux 2023 and -packages them into deployment ZIP archives. The build runs inside a container, -so you must have [Docker](https://docs.docker.com/desktop/install/mac-install/) -(or `container`) installed and started. +packages them into deployment archives. You choose along two independent axes: +how to compile (see [Choosing how to compile](#Choosing-how-to-compile)) and +what format to package (see [Choosing the package format](#Choosing-the-package-format)). +By default the build runs inside a container and produces a ZIP, so you must have +[Docker](https://docs.docker.com/desktop/install/mac-install/) (or `container`) +installed and started. ```sh swift package --allow-network-connections docker lambda-build @@ -100,41 +104,91 @@ swift package --allow-network-connections docker lambda-build \ | `--swift-version ` | The Swift version to use for building. (default: latest) Cannot be combined with `--base-docker-image`. | | `--base-docker-image ` | The base Docker image to build with. (default: `swift:-amazonlinux2023`) Cannot be combined with `--swift-version`. | | `--disable-docker-image-update` | Do not attempt to update the Docker image. | -| `--cross-compile ` | The cross-compilation method: `docker`, `container`, `swift-static-sdk`, or `custom-sdk`. (default: `docker`) `swift-static-sdk` and `custom-sdk` are not yet supported. | -| `--archive-format ` | The packaging format: `zip` or `oci`. (default: `zip`) See [Building an OCI image](#Building-an-OCI-image). | +| `--cross-compile ` | The cross-compilation method: `docker`, `container`, `swift-static-sdk`, or `custom-sdk`. (default: `docker`) See [Choosing how to compile](#Choosing-how-to-compile). `custom-sdk` is not yet supported. | +| `--archive-format ` | The packaging format: `zip` or `oci`. (default: `zip`) See [Choosing the package format](#Choosing-the-package-format). | | `--architecture ` | The CPU architecture to build for: `x64` or `arm64`. (default: host architecture) Recorded in the build manifest so `lambda-deploy` deploys the function for the architecture it was built for. See [Selecting the architecture](#Selecting-the-architecture). | | `--base-oci-image ` | The base image for the OCI image when `--archive-format oci` is used. (default: `public.ecr.aws/amazonlinux/amazonlinux:2023-minimal`) | | `--no-strip` | Do not strip debug symbols from the binary. | | `--verbose` | Produce verbose output for debugging. | | `--help` | Show help information. | -### Selecting the architecture +### Choosing how to compile -By default `lambda-build` builds for the architecture of the machine running the -build (`arm64` on Apple Silicon, `x64` on Intel). Pass `--architecture` to build -for a specific architecture regardless of your host: +`--cross-compile` selects how your code is compiled for Amazon Linux. All three +methods produce a `bootstrap` that runs on the Lambda `provided.al2023` runtime; +they differ in what tooling they need on your machine, and in which sandbox +permission they require. + +- `docker` (default) cross-compiles inside a Docker container. Requires Docker + installed and running, and `--allow-network-connections docker`. +- `container` cross-compiles inside Apple's `container` runtime instead of + Docker. Same workflow, different runtime. It requires `--disable-sandbox`, + because `container` talks to its daemon over IPC rather than the Docker socket + the `docker` scope grants. +- `swift-static-sdk` compiles directly on your host with no container at all, + using the Static Linux SDK. It also requires `--disable-sandbox`; without it + the plugin sandbox asks for Docker permission and blocks the on-host build. See + [Building without a container](#Building-without-a-container). + +The `docker` and `container` methods also accept `--base-docker-image`, +`--swift-version`, and `--disable-docker-image-update`. Those options do not +apply to `swift-static-sdk`, which builds on the host. + +#### Building without a container + +The [Static Linux SDK](https://www.swift.org/documentation/articles/static-linux-getting-started.html) +compiles a fully static, musl-linked binary directly on your host with no +container runtime at all: ```sh -swift package --allow-network-connections docker lambda-build \ - --architecture arm64 +swift package --disable-sandbox lambda-build --cross-compile swift-static-sdk ``` -`arm64` (AWS Graviton) is generally cheaper and faster for Swift workloads; pick -`x64` only if a dependency requires it. +This needs `--disable-sandbox` rather than `--allow-network-connections docker`. +The build runs on the host and writes to a scratch directory, which the plugin +sandbox blocks; without `--disable-sandbox` the plugin instead asks for Docker +permission and the build fails. -The architecture you build for is recorded in the `build-manifest.json` written -next to the artifact. `lambda-deploy` reads it and deploys the function for that -same architecture, so the two can never silently disagree. If you pass -`--architecture` to `lambda-deploy` as well, it must match what was built, -otherwise the deploy fails fast rather than creating a function whose declared -architecture doesn't match its binary (which would only surface as a failure at -invoke time). See [lambda-deploy](#lambda-deploy). +The Static Linux SDK must be installed beforehand. `lambda-build` does not +install it for you (the plugin's sandbox does not permit the download), and +fails with guidance if a matching SDK is not found. Install it once with the +`swift sdk install` command, passing the SDK download URL and its checksum. Find +the current URL and checksum on the +[Static Linux SDK](https://www.swift.org/documentation/articles/static-linux-getting-started.html) +page. For example, for Swift 6.3.3: + +```sh +swift sdk install \ + https://download.swift.org/swift-6.4.x-branch/static-sdk/swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-05-31-a/swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-05-31-a_static-linux-0.1.0.artifactbundle.tar.gz \ + --checksum 9b85e278d38f362941ae008b08b77bd890e46cfc6f6e07db5099dfac4b11b189 +``` + +> Important: the Static Linux SDK version must exactly match your installed +> Swift toolchain version. A 6.3.3 toolchain needs the 6.3.3 SDK. If you +> previously installed an SDK for a different toolchain version, remove it first +> with `swift sdk remove `. + +`--architecture` selects the target: `arm64` maps to the +`aarch64-swift-linux-musl` SDK triple and `x64` to `x86_64-swift-linux-musl`. +Because the SDK genuinely cross-compiles, you can build either architecture from +either host. + +On binary size: statically linking musl does not, in practice, produce a much +larger binary than the container build. Both statically link the Swift standard +library, which dominates the size, so the two land close to each other (in a +trivial function, the static-SDK binary is actually slightly smaller). Stripping applies through `--no-strip` exactly as with the other methods. -### Building an OCI image +### Choosing the package format -By default `lambda-build` produces a ZIP archive, which is the simplest option -and gives the fastest cold starts for most functions. Packaging your function as -a container image instead is useful when: +`--archive-format` selects what `lambda-build` packages, independently of how you +compiled. `zip` (default) is the simplest option and gives the fastest cold +starts for most functions. `oci` builds a container image instead. Both formats +work with any `--cross-compile` method, except that `oci` needs a container CLI +and so cannot be combined with `swift-static-sdk`. + +#### Building an OCI image + +Packaging your function as a container image instead of a ZIP is useful when: - **Your deployment package is larger than the ZIP limits.** A ZIP-packaged function is capped at 50 MB zipped / 250 MB unzipped, whereas a container image @@ -147,9 +201,7 @@ a container image instead is useful when: - **You already build and ship with containers.** If your team's CI/CD, scanning, and artifact registries are container-based, an image in Amazon ECR slots into the same tooling and promotion workflow as the rest of your services. -- **You want a reproducible, self-contained runtime.** The image pins the OS, the - Swift runtime libraries, and your binary together, so what you test locally is - byte-for-byte what runs in Lambda. +- **You want a reproducible, self-contained runtime.** The image pins the OS, the Swift runtime libraries, and your binary together, so what you test locally is byte-for-byte what runs in Lambda. If none of these apply, prefer the default ZIP format. @@ -166,6 +218,8 @@ This builds a minimal Amazon Linux 2023 image (`public.ecr.aws/amazonlinux/amazo with your compiled binary as the `bootstrap` entrypoint, using the same container CLI selected by `--cross-compile` (`docker` or `container`). The image is built for a single architecture and tagged locally as `swift-lambda/:latest`. +As above, the `docker` method uses `--allow-network-connections docker` while the +`container` method needs `--disable-sandbox`. To build from a different base image, for example to add system packages or to use `public.ecr.aws/lambda/provided:al2023`, pass `--base-oci-image`: @@ -190,6 +244,28 @@ Alongside the artifact, `lambda-build` writes a `build-manifest.json` recording the package type, architecture, and (for images) the container CLI and local tag. `lambda-deploy` reads this manifest to determine how to deploy. +### Selecting the architecture + +By default `lambda-build` builds for the architecture of the machine running the +build (`arm64` on Apple Silicon, `x64` on Intel). Pass `--architecture` to build +for a specific architecture regardless of your host: + +```sh +swift package --allow-network-connections docker lambda-build \ + --architecture arm64 +``` + +`arm64` (AWS Graviton) is generally cheaper and faster for Swift workloads; pick +`x64` only if a dependency requires it. + +The architecture you build for is recorded in the `build-manifest.json` written +next to the artifact. `lambda-deploy` reads it and deploys the function for that +same architecture, so the two can never silently disagree. If you pass +`--architecture` to `lambda-deploy` as well, it must match what was built, +otherwise the deploy fails fast rather than creating a function whose declared +architecture doesn't match its binary (which would only surface as a failure at +invoke time). See [lambda-deploy](#lambda-deploy). + ## lambda-deploy `lambda-deploy` deploys the ZIP archive produced by `lambda-build` to AWS. It diff --git a/Tests/AWSLambdaPluginHelperTests/BuildBackendTests.swift b/Tests/AWSLambdaPluginHelperTests/BuildBackendTests.swift index 20e31944..bc965537 100644 --- a/Tests/AWSLambdaPluginHelperTests/BuildBackendTests.swift +++ b/Tests/AWSLambdaPluginHelperTests/BuildBackendTests.swift @@ -336,4 +336,28 @@ struct CrossCompileBackendSelectionTests { #expect(container.name == "container") #expect(container.cli is AppleContainerCLI) } + + @available(LambdaSwift 2.0, *) + @Test("swift-static-sdk selects the Static Linux SDK backend, no container CLI") + func staticLinuxSDKBackend() throws { + let config = try Self.makeConfiguration(method: "swift-static-sdk") + let backend = try config.makeCrossCompileBackend() + let sdk = try #require(backend as? StaticLinuxSDKBuildBackend) + #expect(sdk.name == "swift-static-sdk") + // The plugin forwards the resolved swift path via --cross-compile-tool-path. + #expect(sdk.swiftToolPath.path == "/usr/local/bin/docker") + } +} + +// MARK: - Static Linux SDK triple mapping + +@Suite("BuildArchitecture musl triple") +struct BuildArchitectureMuslTripleTests { + + @available(LambdaSwift 2.0, *) + @Test("maps each architecture to its Static Linux SDK triple") + func muslTriples() { + #expect(BuildArchitecture.arm64.muslTriple == "aarch64-swift-linux-musl") + #expect(BuildArchitecture.x64.muslTriple == "x86_64-swift-linux-musl") + } } diff --git a/Tests/AWSLambdaPluginHelperTests/BuilderConfigurationTests.swift b/Tests/AWSLambdaPluginHelperTests/BuilderConfigurationTests.swift index 61539ed4..bf16ebb5 100644 --- a/Tests/AWSLambdaPluginHelperTests/BuilderConfigurationTests.swift +++ b/Tests/AWSLambdaPluginHelperTests/BuilderConfigurationTests.swift @@ -59,12 +59,11 @@ struct BuilderConfigurationTests { } @available(LambdaSwift 2.0, *) - @Test("--cross-compile with 'swift-static-sdk' throws unsupported error") + @Test("--cross-compile with valid value 'swift-static-sdk'") func crossCompileSwiftStaticSdk() throws { let args = defaultArgs() + ["--cross-compile", "swift-static-sdk"] - #expect(throws: (any Error).self) { - _ = try BuilderConfiguration(arguments: args) - } + let config = try BuilderConfiguration(arguments: args) + #expect(config.crossCompileMethod == .swiftStaticSdk) } @available(LambdaSwift 2.0, *) diff --git a/Tests/AWSLambdaPluginHelperTests/PropertyTests.swift b/Tests/AWSLambdaPluginHelperTests/PropertyTests.swift index 4cca4235..309f987c 100644 --- a/Tests/AWSLambdaPluginHelperTests/PropertyTests.swift +++ b/Tests/AWSLambdaPluginHelperTests/PropertyTests.swift @@ -173,8 +173,8 @@ struct DeprecatedAliasEquivalencePropertyTests { /// **Validates: Requirements 2.7** /// /// For any valid `CrossCompileMethod` enum case, `rawValue` → parse → original case. -/// Note: swift-static-sdk and custom-sdk throw "unsupported" on `parse()`, but their -/// rawValue round-trips through the enum initializer correctly. +/// Note: custom-sdk throws "unsupported" on `parse()`, but its rawValue round-trips +/// through the enum initializer correctly. @Suite("Property 3: Cross-compile method parsing round-trip") struct CrossCompileMethodRoundTripPropertyTests { @@ -580,8 +580,7 @@ struct AL2WarningLogicPropertyTests { struct UnsupportedCrossCompileMethodsPropertyTests { static let unsupportedMethods: [String] = [ - "swift-static-sdk", - "custom-sdk", + "custom-sdk" ] static let sdkGuideURL = "https://www.swift.org/documentation/articles/static-linux-getting-started.html"