Skip to content

Commit b964cac

Browse files
authored
v0.3.0: Add cargo check and other updates (#2)
* Add `cargo check` and other updates * update `setupLogging` usage * Add `cargo check` to validate Rust code * Make console log messages a bit cleaner * Minor code refactor * Add a CHANGELOG file * Update CHANGELOG * Update docs * Style fix * Update docs * Update docs * Bump version
1 parent 4fa61ac commit b964cac

File tree

9 files changed

+235
-37
lines changed

9 files changed

+235
-37
lines changed

.npmignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ test/
3535

3636
# exclude examples
3737
cdk-examples
38+
39+
# exclude changelog (can always be found on GitHub)
40+
CHANGELOG.md

CHANGELOG.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Changelog
2+
3+
This project follows semantic versioning.
4+
5+
Possible header types:
6+
7+
- `Features` for any new features added, or for backwards-compatible
8+
changes to existing functionality.
9+
- `Bug Fixes` for any bug fixes.
10+
- `Breaking Changes` for any backwards-incompatible changes.
11+
12+
## [Unreleased]
13+
14+
## v0.3.0 (2022-02-23)
15+
16+
### Features
17+
18+
- Add support to run `cargo check` by default before building Rust code.
19+
- Clean up console log messages that get printed out so they are a bit nicer.
20+
- Change the value that is set for the `RUST_LOG` environment variable when `setupLogging` is enabled.
21+
- The format is now `warn,module_name=debug` instead of `module_name=trace`.
22+
- Add below global _Settings_:
23+
- `RUN_CARGO_CHECK`
24+
- `DEFAULT_LOG_LEVEL`
25+
- `MODULE_LOG_LEVEL`
26+
27+
## v0.2.0 (2022-02-19)
28+
29+
### Features
30+
31+
- Add docs on _Rust Function Properties_ and _Settings_.
32+
- Add new Rust Function Properties such as `setupLogging`.
33+
- Update Readme docs.
34+
- Add `cdk-examples/`.
35+
- Reduce overall package size when publishing to `npm`.
36+
- Some other stuff that I don't remember.
37+
38+
## v0.1.0 (2022-02-17)
39+
40+
- Initial Release on [npmjs.com] :tada:
41+
42+
[npmjs.com]: https://www.npmjs.com/package/rust.aws-cdk-lambda

README.md

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
![rust.aws-cdk-lambda: Stable](https://img.shields.io/badge/rust.aws--cdk--lambda-stable-success.svg?style=for-the-badge)
88
[![npm](https://img.shields.io/npm/v/rust.aws-cdk-lambda?style=for-the-badge)](https://www.npmjs.com/package/rust.aws-cdk-lambda)
99

10-
> **This is unofficial CDK library based on the [Amazon Lambda Node.js] and [aws-lambda-rust] Libraries.**
10+
> **This is an unofficial CDK library based on the [Amazon Lambda Node.js] and [aws-lambda-rust] Libraries.**
1111
>
1212
> _It's intended for use with the new **[AWS CDK v2]**_.
1313
@@ -28,16 +28,10 @@ in the [official AWS documentation].
2828
[`cross`]: https://github.com/rust-embedded/cross
2929
[official aws documentation]: https://docs.aws.amazon.com/sdk-for-rust/latest/dg/lambda.html
3030

31-
## Rust Fuction
31+
## Rust Function
3232

3333
The `RustFunction` construct creates a Lambda function with automatic bundling and compilation of Rust code.
3434

35-
## Examples
36-
37-
You can find sample CDK apps built using _Typescript_ or _Node.js_ in the [cdk-examples/] folder of the GitHub project repo.
38-
39-
[cdk-examples/]: https://github.com/rnag/rust.aws-cdk-lambda/tree/main/cdk-examples
40-
4135
## Getting Started
4236

4337
1. Install the [npm](https://nodejs.org/) package:
@@ -62,6 +56,12 @@ Finally, ensure you have [Docker] installed and running, as it will be used by `
6256

6357
[`cargo`]: https://www.rust-lang.org/
6458

59+
## Examples
60+
61+
You can find sample CDK apps built using _Typescript_ or _Node.js_ in the [cdk-examples/] folder of the GitHub project repo.
62+
63+
[cdk-examples/]: https://github.com/rnag/rust.aws-cdk-lambda/tree/main/cdk-examples
64+
6565
## Usage
6666

6767
First, import the construct:
@@ -80,6 +80,15 @@ By default, the construct will use directory where `cdk` was invoked as director
8080
8181
If no `bin` or `package` argument is passed in, it will default to the package name as defined in the main `Cargo.toml`.
8282
83+
That is, the above usage should work for a project structure that looks like this:
84+
85+
```plaintext
86+
.
87+
├── Cargo.toml
88+
└── src
89+
    └── main.rs
90+
```
91+
8392
Alternatively, `directory` and `bin` can be specified:
8493
8594
```ts
@@ -92,6 +101,19 @@ new RustFunction(this, 'MyLambdaFunction', {
92101
93102
All other properties of `lambda.Function` are supported, see also the [AWS Lambda construct library](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-lambda).
94103
104+
## How It Works
105+
106+
When bundling the code, the `RustFunction` runs the following steps in order:
107+
108+
- First it runs `cargo check` to confirm that the Rust code can compile.
109+
Note that this is an optional step, and [can be disabled](#settings) as mentioned below.
110+
111+
- Next it calls `cross build`, and passes in the `--release` and `--target` flags, so it compiles for a Lambda environment - which defaults to the **x86_64-unknown-linux-musl** target, as mentioned above.
112+
113+
- Finally, it copies the release app binary from the `target/` folder to a file named `bootstrap`, which the Lambda custom runtime environment looks for. It adds this new file under the _build directory_, which defaults to a `.build/` folder under the directory where `cdk` was invoked.
114+
115+
- The directory path to the executable is then passed in to `lambda.Code.fromAsset`, which creates a _zip file_ from the release binary asset.
116+
95117
## Multiple Rust Lambdas
96118
97119
Assuming you have a CDK project with more than one Rust
@@ -111,7 +133,7 @@ Suppose your project layout looks like this:
111133
    └── lambda2.rs
112134
```
113135
114-
Here's one way to deploy that:
136+
Here's one way to deploy that via `cdk`:
115137
116138
```ts
117139
new RustFunction(this, 'my-function-1', {
@@ -184,14 +206,15 @@ You can find a more complete project structure in the [rust-workspaces/] CDK sam
184206
185207
Below lists some commonly used properties you can pass in to the `RustFunction` construct.
186208
187-
| Name | Description |
188-
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
189-
| `target` | Build target to cross-compile to. Defaults to the target for Linux MUSL, `x86_64-unknown-linux-musl`. |
190-
| `directory` | Entry point where the project's main `Cargo.toml` is located. By default, the construct will use directory where `cdk` was invoked as the directory where Cargo files are located. |
191-
| `buildDir` | Default Build directory, which defaults to a `.build` folder under the project's root directory. |
192-
| `bin` | Executable name to pass to `--bin` |
193-
| `package` | Workspace package name to pass to `--package` |
194-
| `setupLogging` | Determines whether we want to set up [library logging](https://rust-lang-nursery.github.io/rust-cookbook/development_tools/debugging/config_log.html) - i.e. set the `RUST_LOG` environment variable - for the lambda function. |
209+
| Name | Description |
210+
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
211+
| `target` | Build target to cross-compile to. Defaults to the target for Linux MUSL, `x86_64-unknown-linux-musl`. |
212+
| `directory` | Entry point where the project's main `Cargo.toml` is located. By default, the construct will use directory where `cdk` was invoked as the directory where Cargo files are located. |
213+
| `buildDir` | Default Build directory, which defaults to a `.build` folder under the project's root directory. |
214+
| `bin` | Executable name to pass to `--bin` |
215+
| `package` | Workspace package name to pass to `--package` |
216+
| `setupLogging` | Determines whether we want to set up [library logging](https://rust-lang-nursery.github.io/rust-cookbook/development_tools/debugging/config_log.html) - i.e. set the `RUST_LOG` environment variable - for the lambda function.<br><br>The format defaults to `warn,module_name=debug`, which means that the default log level is `warn`, and the executable or library's log level is `debug`. |
217+
| |
195218
196219
## Settings
197220
@@ -206,4 +229,7 @@ Below are some useful _global_ defaults which can be set for all Rust Lambda Fun
206229
| Name | Description |
207230
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
208231
| `BUILD_INDIVIDUALLY` | Whether to build each executable individually, either via `--bin` or `--package`. |
232+
| `RUN_CARGO_CHECK` | Whether to run `cargo check` to validate Rust code before building it with `cross`. Defaults to _true_. |
233+
| `DEFAULT_LOG_LEVEL` | Log Level for non-module libraries. Note that this value is only used when `RustFunctionProps.setupLogging` is enabled. Defaults to `warn`. |
234+
| `MODULE_LOG_LEVEL` | Log Level for a module (i.e. the executable). Note that this value is only used when `RustFunctionProps.setupLogging` is enabled. Defaults to `debug`. |
209235
| `workspace_dir` | Sets the root workspace directory. By default, the workspace directory is assumed to be the directory where `cdk` was invoked.<br><br>This directory should contain at the minimum a `Cargo.toml` file which defines the workspace members. |

cdk-examples/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,29 @@ For example, using:
6161
```shell
6262
echo '\n# Rust lambda build directory\n.build' >> .gitignore
6363
```
64+
65+
## Local Development and Testing
66+
67+
In case it's desirable to uncomment the following import in the `lib/` folder of a sample CDK app, for local testing purposes:
68+
69+
```ts
70+
import { RustFunction, Settings } from '../../../lib';
71+
```
72+
73+
You may then potentially run into some import errors when deploying the stack via `cdk`.
74+
75+
To fix that, run this command from both the project root folder `$root`, and compare the output when running it from within `$root/cdk-examples/my-app`:
76+
77+
```shell
78+
npm list
79+
```
80+
81+
To resolve the import issues, you'll need to ensure that certain package versions are the same between the two directories.
82+
83+
For example, here are the important ones you'd need to verify:
84+
85+
```plaintext
86+
├── aws-cdk-lib@2.12.0
87+
├── aws-cdk@2.12.0
88+
├── constructs@10.0.65
89+
```

lib/build.ts

Lines changed: 87 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { spawnSync } from 'child_process';
22
import * as fs from 'fs';
33
import * as path from 'path';
4+
import { performance } from 'perf_hooks';
45
import { Settings } from '.';
6+
import { logTime } from './utils';
57

68
let _builtWorkspaces = false,
7-
_builtBinaries = false;
9+
_builtBinaries = false,
10+
_ranCargoCheck = false;
811

912
export interface BaseBuildProps {
1013
/**
@@ -87,10 +90,35 @@ export function build(options: BuildOptions): void {
8790
}
8891
}
8992

93+
let targetReleaseDir = path.join(
94+
options.entry,
95+
'target',
96+
options.target,
97+
'release'
98+
);
99+
const releaseDirExists = fs.existsSync(targetReleaseDir);
100+
90101
if (shouldCompile) {
91-
console.log(
92-
`🍺 Building Rust code with \`cross\`. This may take a few minutes...`
93-
);
102+
// Run `cargo check` on an initial time, if needed
103+
if (Settings.RUN_CARGO_CHECK && !_ranCargoCheck) {
104+
_ranCargoCheck = true;
105+
checkCode(options, releaseDirExists);
106+
}
107+
108+
if (releaseDirExists) {
109+
console.log(`🍺 Building Rust code...`);
110+
} else {
111+
// The `release` directory doesn't exist for the specified
112+
// target. This is most likely an initial run, so `cross` will
113+
// take much longer than usual to cross-compile the code.
114+
//
115+
// Print out an informative message that the `build` step is
116+
// expected to take longer than usual.
117+
console.log(
118+
`🍺 Building Rust code with \`cross\`. This may take a few minutes...`
119+
);
120+
}
121+
94122
const args: string[] = [
95123
'build',
96124
'--release',
@@ -112,13 +140,7 @@ export function build(options: BuildOptions): void {
112140
}
113141
}
114142

115-
let from = path.join(
116-
options.entry,
117-
'target',
118-
options.target,
119-
'release',
120-
outputName
121-
);
143+
let from = path.join(targetReleaseDir, outputName);
122144
let to = path.join(options.outDir, 'bootstrap');
123145

124146
fs.copyFileSync(from, to);
@@ -128,3 +150,57 @@ export function build(options: BuildOptions): void {
128150
);
129151
}
130152
}
153+
154+
/**
155+
* Validate code with `cargo check`
156+
*
157+
* Note: this step is optional, and can be disabled with
158+
* `Settings.RUN_CARGO_CHECK` as needed.
159+
*/
160+
export function checkCode(
161+
options: BuildOptions,
162+
releaseDirExists: boolean
163+
) {
164+
if (!releaseDirExists) {
165+
// The `release` directory doesn't exist for the specified
166+
// target. This is most likely an initial run, so `cargo` will
167+
// take much longer than usual to check the code.
168+
//
169+
// Print out an informative message that the `validate` step is
170+
// expected to take longer than usual.
171+
console.log(
172+
`🧪 Checking code with \`cargo\`. This may take a few minutes...`
173+
);
174+
}
175+
176+
let start = performance.now();
177+
178+
const args: string[] = [
179+
'check',
180+
'--release',
181+
'--target',
182+
options.target,
183+
'--color',
184+
'always',
185+
];
186+
187+
const check = spawnSync('cargo', args, {
188+
cwd: options.entry,
189+
});
190+
191+
if (check.error) {
192+
throw check.error;
193+
}
194+
195+
if (check.status !== 0) {
196+
console.error(check.stderr.toString().trim());
197+
console.error(`💥 Run \`cargo check\` errored.`);
198+
process.exit(1);
199+
// Note: I don't want to raise an error here, as that will clutter the
200+
// output with the stack trace here. But maybe, there's a way to
201+
// suppress that?
202+
// throw new Error(check.stderr.toString().trim());
203+
}
204+
205+
logTime(start, `✅ Run \`cargo check\``);
206+
}

lib/function.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { performance } from 'perf_hooks';
77
import * as toml from 'toml';
88
import { Settings } from '.';
99
import { BaseBuildProps, build } from './build';
10+
import { logTime } from './utils';
1011

1112
/**
1213
* Properties for a RustFunction
@@ -114,7 +115,7 @@ export class RustFunction extends lambda.Function {
114115
// coincidentally how Rust imports are done.
115116
let underscoredName = executable.split('-').join('_');
116117
// Set the `RUST_LOG` environment variable.
117-
lambdaEnv.RUST_LOG = `${underscoredName}=trace`;
118+
lambdaEnv.RUST_LOG = `${Settings.DEFAULT_LOG_LEVEL},${underscoredName}=${Settings.MODULE_LOG_LEVEL}`;
118119
}
119120

120121
super(scope, id, {
@@ -133,13 +134,6 @@ function createDirectory(dir: string) {
133134
}
134135
}
135136

136-
function logTime(start: number, message: string) {
137-
const elapsedSec = ((performance.now() - start) / 1000).toFixed(
138-
2
139-
);
140-
console.log(`${message}: ${elapsedSec}s`);
141-
}
142-
143137
function getPackageName(entry: string) {
144138
const tomlFilePath = path.join(entry, 'Cargo.toml');
145139
// console.trace(`Parsing TOML file at ${tomlFilePath}`);

lib/settings.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,29 @@ export const Settings = {
4141
*/
4242
BUILD_INDIVIDUALLY: false,
4343

44+
/**
45+
* Whether to run `cargo check` to validate Rust code before building it with `cross`.
46+
*
47+
* Defaults to true.
48+
*/
49+
RUN_CARGO_CHECK: true,
50+
51+
/**
52+
* Default Log Level, for non-module libraries.
53+
*
54+
* Note: this value is only used when `RustFunctionProps.setupLogging`
55+
* is enabled.
56+
*/
57+
DEFAULT_LOG_LEVEL: 'warn',
58+
59+
/**
60+
* Default Log Level for a module (i.e. the executable)
61+
*
62+
* Note: this value is only used when `RustFunctionProps.setupLogging`
63+
* is enabled.
64+
*/
65+
MODULE_LOG_LEVEL: 'debug',
66+
4467
/**
4568
* Sets the root workspace directory. By default, the workspace directory
4669
* is assumed to be the directory where `cdk` was invoked.

lib/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { performance } from 'perf_hooks';
2+
3+
export function logTime(start: number, message: string) {
4+
const elapsedSec = ((performance.now() - start) / 1000).toFixed(
5+
2
6+
);
7+
console.log(`${message}: ${elapsedSec}s`);
8+
}

0 commit comments

Comments
 (0)