lambda-zig/build.zig

212 lines
8 KiB
Zig

const std = @import("std");
const builtin = @import("builtin");
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) !void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});
// Create a module for lambda.zig
const lambda_module = b.createModule(.{
.root_source_file = b.path("src/lambda.zig"),
.target = target,
.optimize = optimize,
});
const lib = b.addLibrary(.{
.name = "lambda-zig",
.linkage = .static,
.root_module = lambda_module,
});
// Export the module for other packages to use
_ = b.addModule("lambda_runtime", .{
.root_source_file = b.path("src/lambda.zig"),
.target = target,
.optimize = optimize,
});
// This declares intent for the library to be installed into the standard
// location when the user invokes the "install" step (the default step when
// running `zig build`).
b.installArtifact(lib);
// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const test_module = b.createModule(.{
.root_source_file = b.path("src/lambda.zig"),
.target = target,
.optimize = optimize,
});
const main_tests = b.addTest(.{
.name = "test",
.root_module = test_module,
});
const run_main_tests = b.addRunArtifact(main_tests);
// Build the lambda-build CLI to ensure it compiles
// This catches dependency version mismatches between tools/build and the main project
const lambda_build_dep = b.dependency("lambda_build", .{
.target = b.graph.host,
.optimize = optimize,
});
const lambda_build_exe = lambda_build_dep.artifact("lambda-build");
// This creates a build step. It will be visible in the `zig build --help` menu,
// and can be selected like this: `zig build test`
// This will evaluate the `test` step rather than the default, which is "install".
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&run_main_tests.step);
test_step.dependOn(&lambda_build_exe.step);
// Create executable module
const exe_module = b.createModule(.{
.root_source_file = b.path("src/sample-main.zig"),
.target = target,
.optimize = optimize,
});
const exe = b.addExecutable(.{
.name = "custom",
.root_module = exe_module,
});
b.installArtifact(exe);
try configureBuildInternal(b, exe);
}
/// Internal version of configureBuild for lambda-zig's own build.
///
/// Both this and configureBuild do the same thing, but resolve the lambda_build
/// dependency differently:
///
/// - Here: we call `b.dependency("lambda_build", ...)` directly since `b` is
/// lambda-zig's own Build context, which has lambda_build in its build.zig.zon
///
/// - configureBuild: consumers pass in their lambda_zig dependency, and we use
/// `lambda_zig_dep.builder.dependency("lambda_build", ...)` to resolve it from
/// lambda-zig's build.zig.zon rather than the consumer's
///
/// This avoids requiring consumers to declare lambda_build as a transitive
/// dependency in their own build.zig.zon.
fn configureBuildInternal(b: *std.Build, exe: *std.Build.Step.Compile) !void {
// When called from lambda-zig's own build, use local dependency
const lambda_build_dep = b.dependency("lambda_build", .{
.target = b.graph.host,
.optimize = .ReleaseSafe,
});
// Ignore return value for internal builds
_ = try @import("lambdabuild.zig").configureBuild(b, lambda_build_dep, exe, .{});
}
/// Re-export types for consumers
pub const LambdaConfig = @import("lambdabuild.zig").Config;
pub const LambdaBuildInfo = @import("lambdabuild.zig").BuildInfo;
/// Configure Lambda build steps for a Zig project.
///
/// This function adds build steps and options for packaging and deploying
/// Lambda functions to AWS. The `lambda_zig_dep` parameter must be the
/// dependency object obtained from `b.dependency("lambda_zig", ...)`.
///
/// Returns a `LambdaBuildInfo` struct containing:
/// - References to all build steps (package, iam, deploy, invoke)
/// - A `deploy_output` LazyPath to a JSON file with deployment info
/// - The function name used
///
/// ## Build Steps
///
/// The following build steps are added:
///
/// - `awslambda_package`: Package the executable into a Lambda deployment zip
/// - `awslambda_iam`: Create or verify the IAM role for the Lambda function
/// - `awslambda_deploy`: Deploy the function to AWS Lambda (depends on package)
/// - `awslambda_run`: Invoke the deployed function (depends on deploy)
///
/// ## Build Options
///
/// The following options are added to the build (command-line options override
/// config defaults):
///
/// - `-Dfunction-name=[string]`: Name of the Lambda function
/// (default: "zig-fn", or as provided by config parameter)
/// - `-Dregion=[string]`: AWS region for deployment and invocation
/// - `-Dprofile=[string]`: AWS profile to use for credentials
/// - `-Drole-name=[string]`: IAM role name
/// (default: "lambda_basic_execution", or as provided by config parameter)
/// - `-Dpayload=[string]`: JSON payload for invocation (default: "{}")
/// - `-Denv-file=[string]`: Path to environment variables file (KEY=VALUE format)
/// - `-Dallow-principal=[string]`: AWS service principal to grant invoke permission
/// (e.g., "alexa-appkit.amazon.com" for Alexa Skills Kit)
///
/// ## Deploy Output
///
/// The `deploy_output` field in the returned struct is a LazyPath to a JSON file
/// containing deployment information (available after deploy completes):
///
/// ```json
/// {
/// "arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function",
/// "function_name": "my-function",
/// "partition": "aws",
/// "region": "us-east-1",
/// "account_id": "123456789012",
/// "role_arn": "arn:aws:iam::123456789012:role/lambda_basic_execution",
/// "architecture": "arm64",
/// "environment_keys": ["MY_VAR"]
/// }
/// ```
///
/// ## Example
///
/// ```zig
/// const lambda_zig = @import("lambda_zig");
///
/// pub fn build(b: *std.Build) !void {
/// const target = b.standardTargetOptions(.{});
/// const optimize = b.standardOptimizeOption(.{});
///
/// const lambda_zig_dep = b.dependency("lambda_zig", .{
/// .target = target,
/// .optimize = optimize,
/// });
///
/// const exe = b.addExecutable(.{ ... });
/// b.installArtifact(exe);
///
/// // Configure Lambda build and get deployment info
/// const lambda = try lambda_zig.configureBuild(b, lambda_zig_dep, exe, .{
/// .default_function_name = "my-function",
/// });
///
/// // Use lambda.deploy_output in other steps that need the ARN
/// const my_step = b.addRunArtifact(my_tool);
/// my_step.addFileArg(lambda.deploy_output);
/// my_step.step.dependOn(lambda.deploy_step); // Ensure deploy runs first
/// }
/// ```
pub fn configureBuild(
b: *std.Build,
lambda_zig_dep: *std.Build.Dependency,
exe: *std.Build.Step.Compile,
config: LambdaConfig,
) !LambdaBuildInfo {
// Get lambda_build from the lambda_zig dependency's Build context
const lambda_build_dep = lambda_zig_dep.builder.dependency("lambda_build", .{
.target = b.graph.host,
.optimize = .ReleaseSafe,
});
return @import("lambdabuild.zig").configureBuild(b, lambda_build_dep, exe, config);
}