Skip to content

Commit

Permalink
feat: @target intrinsic (#7161)
Browse files Browse the repository at this point in the history
As a more convenient alternative to importing the `util` module and using `util.env("WING_TARGET")` to obtain the current cloud target, this PR adds a dedicated intrinsic function `@target` which does the same thing.

In terms of design, the motivation of the new syntax is to make it clear that this kind of pattern (where you alter application logic or configuration based on the compilation target string) is an intended pattern in Wing and something handy for building abstractions around, not a hacky workaround.

Existing code that uses the WING_TARGET environment variable will continue to work (no current plans to deprecate it).

## Checklist

- [x] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted)
- [x] Description explains motivation and solution
- [x] Tests added (always)
- [x] Docs updated (only required for features)
- [x] Added `pr/e2e-full` label if this feature requires end-to-end testing

*By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
  • Loading branch information
Chriscbr authored Sep 24, 2024
1 parent 2a78abb commit 62d0fab
Show file tree
Hide file tree
Showing 70 changed files with 234 additions and 222 deletions.
1 change: 1 addition & 0 deletions docs/api/05-language-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ the following properties (given an example intrinsic `@x`):
| `@assert()` | checks a condition and _throws_ if evaluated to false |
| `@filename` | absolute path of the source file |
| `@dirname` | absolute path of the source file's directory |
| `@target` | a string identifying the current target platform |
| `@app` | the root of the construct tree |
| `@unsafeCast()` | cast a value into a different type |
| `@nodeof()` | obtain the [tree node](/docs/concepts/application-tree) of a preflight object |
Expand Down
4 changes: 2 additions & 2 deletions docs/contributing/998-archived/01-compile-targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ new cloud.Function(inflight ()=> {
// push a message to queue
queue.push("m");
// sleep according to target
if util.env("WING_TARGET") == "sim" {
if @target == "sim" {
log("Running on Simulator, sleeping for 1s");
util.sleep(1s);
} else {
Expand All @@ -85,7 +85,7 @@ new cloud.Function(inflight ()=> {
});
```

In this example, we want to sleep briefly for the Simulator target and for 30 seconds for cloud targets, this is achieved using the `WING_TARGET` environment variable.
In this example, we want to sleep briefly for the simulator target and for 30 seconds for cloud targets, this is achieved using the `@target` intrinsic function.

## Compiler plugins

Expand Down
4 changes: 2 additions & 2 deletions docs/docs/03-platforms/01-understanding-platforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ vpc_api_gateway = true

There might be times when you need to write code that is specific to a particular platform target. For example, you may want to activate a verbose logging service only when testing locally to save on cloud log storage costs.

With the Wing `util` library, you can access environment variables. The `WING_TARGET` environment variable contains the current platform target as it's value, which you can use to conditionally run target-specific code. See the example below:
The `@target` intrinsic returns the current platform target as a string value, which you can use to conditionally run target-specific code. See the example below:

```js playground example
bring cloud;
Expand All @@ -138,7 +138,7 @@ new cloud.Function(inflight ()=> {
// push a message to queue
queue.push("m");
// sleep according to target
if util.env("WING_TARGET") == "sim" {
if @target == "sim" {
log("Running on Simulator, sleeping for 1s");
util.sleep(1s);
} else {
Expand Down
4 changes: 0 additions & 4 deletions packages/@winglang/sdk/src/cloud/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,6 @@ export class Function extends Resource implements IInflightHost {
const entrypoint = join(workdir, `${assetName}.cjs`);
this.entrypoint = entrypoint;

if (process.env.WING_TARGET) {
this.addEnvironment("WING_TARGET", process.env.WING_TARGET);
}

if (props.concurrency !== undefined && props.concurrency <= 0) {
throw new Error(
"concurrency option on cloud.Function must be a positive integer"
Expand Down
4 changes: 0 additions & 4 deletions packages/@winglang/sdk/src/cloud/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,6 @@ export class Service extends Resource implements IInflightHost {
const entrypoint = join(workdir, `${this.assetName}.cjs`);
this.entrypoint = entrypoint;

if (process.env.WING_TARGET) {
this.addEnvironment("WING_TARGET", process.env.WING_TARGET);
}

this.handler = handler;
}

Expand Down
4 changes: 4 additions & 0 deletions packages/@winglang/sdk/src/core/lifting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,5 +423,9 @@ export class Lifting {
// no lift-related methods to call - it's probably a primitive
// so no capabilities need to be added to the inflight host
}

if (process.env.WING_TARGET) {
host.addEnvironment("WING_TARGET", process.env.WING_TARGET!);
}
}
}
4 changes: 0 additions & 4 deletions packages/@winglang/sdk/src/target-sim/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,6 @@ export class Resource
const entrypoint = join(workdir, `${assetName}.cjs`);
this.entrypoint = entrypoint;

if (process.env.WING_TARGET) {
this.addEnvironment("WING_TARGET", process.env.WING_TARGET);
}

this.factory = factory;
}

Expand Down
4 changes: 4 additions & 0 deletions packages/@winglang/wingc/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ pub enum IntrinsicKind {
Dirname,
Filename,
App,
Target,
}

impl Display for IntrinsicKind {
Expand All @@ -601,6 +602,7 @@ impl Display for IntrinsicKind {
IntrinsicKind::Dirname => write!(f, "@dirname"),
IntrinsicKind::Filename => write!(f, "@filename"),
IntrinsicKind::App => write!(f, "@app"),
IntrinsicKind::Target => write!(f, "@target"),
}
}
}
Expand All @@ -611,6 +613,7 @@ impl IntrinsicKind {
"@dirname" => IntrinsicKind::Dirname,
"@filename" => IntrinsicKind::Filename,
"@app" => IntrinsicKind::App,
"@target" => IntrinsicKind::Target,
_ => IntrinsicKind::Unknown,
}
}
Expand All @@ -630,6 +633,7 @@ impl IntrinsicKind {
Phase::Preflight => true,
_ => false,
},
IntrinsicKind::Target => true,
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions packages/@winglang/wingc/src/jsify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,9 @@ impl<'a> JSifier<'a> {
IntrinsicKind::App => {
new_code!(expr_span, HELPERS_VAR, ".nodeof(this).app")
}
IntrinsicKind::Target => {
new_code!(expr_span, "process.env.WING_TARGET")
}
},
ExprKind::Call { callee, arg_list } => {
let function_type = match callee {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,11 @@ source: packages/@winglang/wingc/src/lsp/completions.rs
kind: markdown
value: "Get the normalized absolute path of the current Wing source file.\n\nThe resolved path represents a path during preflight only and is not guaranteed to be valid while inflight."
sortText: bb|@filename
- label: "@target"
kind: 6
detail: str
documentation:
kind: markdown
value: "Returns a string identifying the current compilation platform.\n\nThis value is set by the CLI at compile time and can be used to conditionally compile code that is dependent on the target platform."
sortText: bb|@target

Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,11 @@ source: packages/@winglang/wingc/src/lsp/completions.rs
kind: markdown
value: "Get the normalized absolute path of the current Wing source file.\n\nThe resolved path represents a path during preflight only and is not guaranteed to be valid while inflight."
sortText: bb|@filename
- label: "@target"
kind: 6
detail: str
documentation:
kind: markdown
value: "Returns a string identifying the current compilation platform.\n\nThis value is set by the CLI at compile time and can be used to conditionally compile code that is dependent on the target platform."
sortText: bb|@target

2 changes: 1 addition & 1 deletion packages/@winglang/wingc/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2527,7 +2527,7 @@ impl<'s> Parser<'s> {
};

if matches!(kind, IntrinsicKind::Unknown) {
self.add_error("Invalid intrinsic", &expression_node);
self.add_error(format!("Unknown intrinsic: @{}", name), &expression_node);
}

Ok(Expr::new(
Expand Down
28 changes: 27 additions & 1 deletion packages/@winglang/wingc/src/type_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2333,6 +2333,28 @@ See https://www.winglang.io/docs/concepts/application-tree for more information.
AccessModifier::Public,
StatementIdx::Top,
);

// @target
let _ = self.types
.intrinsics
.define(
&Symbol::global(IntrinsicKind::Target.to_string()),
SymbolKind::Variable(VariableInfo {
access: AccessModifier::Public,
name: Symbol::global(IntrinsicKind::Target.to_string()),
docs: Some(Docs::with_summary(
r#"Returns a string identifying the current compilation platform.
This value is set by the CLI at compile time and can be used to conditionally compile code that is dependent on the target platform."#,
)),
kind: VariableKind::StaticMember,
phase: Phase::Independent,
type_: self.types.string(),
reassignable: false,
}),
AccessModifier::Public,
StatementIdx::Top,
);
}

fn add_builtin(&mut self, name: &str, typ: Type, scope: &mut Scope) {
Expand Down Expand Up @@ -2981,7 +3003,11 @@ See https://www.winglang.io/docs/concepts/application-tree for more information.
}

match intrinsic.kind {
IntrinsicKind::Dirname | IntrinsicKind::Filename | IntrinsicKind::App | IntrinsicKind::Unknown => {
IntrinsicKind::Dirname
| IntrinsicKind::Filename
| IntrinsicKind::App
| IntrinsicKind::Target
| IntrinsicKind::Unknown => {
return (sig.return_type, sig.phase);
}
}
Expand Down
5 changes: 2 additions & 3 deletions packages/winglang/project-templates/wing/private-api/main.w
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
bring cloud;
bring http;
bring util;

/**
* The example below is a simple note-taking app.
Expand Down Expand Up @@ -96,7 +95,7 @@ bring util;
let noteService = new NoteService();

// Consumer functions (not required for the app to work, but useful for testing)
if util.env("WING_TARGET") == "tf-aws" {
if @target == "tf-aws" {
new cloud.Function(inflight (event) => {
if let event = event?.tryAsStr() {
let parts = event.split(":");
Expand Down Expand Up @@ -128,4 +127,4 @@ if util.env("WING_TARGET") == "tf-aws" {

return "event is required `NAME`";
}) as "Consumer-GET";
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions tests/error/invalid-token.test.w
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
bring sim;
bring util;

inflight class MyResourceBackend impl sim.IResource {
ctx: sim.IResourceContext;
Expand All @@ -22,7 +21,7 @@ class MyResource {
}

// Only run these tests in the simulator
if util.env("WING_TARGET") == "sim" {
if @target == "sim" {
let r = new MyResource();
let fakeAttr = r.fakeAttr();

Expand Down
5 changes: 1 addition & 4 deletions tests/sdk_tests/api/aws-api.test.w
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
bring cloud;
bring aws;
bring util;

let target = util.env("WING_TARGET");

let api = new cloud.Api() as "api";
api.get("/api", inflight (req: cloud.ApiRequest): cloud.ApiResponse => {
Expand Down Expand Up @@ -39,4 +36,4 @@ test "validates the AWS Api" {
// If the test is not on AWS, it should not fail, so I am returning true.
assert(true);
}
}
}
2 changes: 1 addition & 1 deletion tests/sdk_tests/api/cycle.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ bring util;
// This test checks that an API can have a route whose handler
// references the API's URL.

if ["sim", "tf-aws", "awscdk"].contains(util.env("WING_TARGET")) {
if ["sim", "tf-aws", "awscdk"].contains(@target) {
let api = new cloud.Api();

api.get("/my_url", inflight () => {
Expand Down
7 changes: 2 additions & 5 deletions tests/sdk_tests/bucket/aws-bucket.test.w
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
bring cloud;
bring aws;
bring util;

let target = util.env("WING_TARGET");

let bucket = new cloud.Bucket() as "aws-wing-bucket";

Expand All @@ -20,7 +17,7 @@ let bucketInfo = getBucketInfo(bucket);

test "validates the AWS Bucket" {
if let bucket = bucketInfo {
if target == "tf-aws" {
if @target == "tf-aws" {
assert(bucket.get("bucketArn").contains("arn:aws:s3:::aws-wing-bucket"));
assert(bucket.get("bucketName").contains("aws-wing-bucket"));
} else { // If it's not a 'tf-aws' target, it's an 'awscdk'
Expand All @@ -32,4 +29,4 @@ test "validates the AWS Bucket" {
// If the test is not on AWS, it should not fail, so I am returning true.
assert(true);
}
}
}
4 changes: 2 additions & 2 deletions tests/sdk_tests/bucket/bucket-ref.test.w
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
bring cloud;
bring aws;
bring util;
bring expect;

let b = new cloud.Bucket();

// this will only work if we are testing on tf-aws
Expand All @@ -14,7 +14,7 @@ if let name = aws.Bucket.from(b)?.bucketName {
}
}

if util.env("WING_TARGET") == "sim" {
if @target == "sim" {
let dummyName = "wing-dummy-bucket";
let dummyArn = "arn:aws:s3:::{dummyName}";
let br = new aws.BucketRef(dummyName);
Expand Down
2 changes: 1 addition & 1 deletion tests/sdk_tests/bucket/events.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ new std.Test(inflight () => {
b.delete("c");

// https://github.com/winglang/wing/issues/2724
if (util.env("WING_TARGET") != "tf-aws") {
if @target != "tf-aws" {
// assert that onCreate events about the "a", "b", and "c" objects were each produced exactly 1 time
checkHitCount(key: "a", type: "OnCreate()", source: Source.anyEvent, count: 1);
checkHitCount(key: "b", type: "OnCreate()", source: Source.anyEvent, count: 1);
Expand Down
7 changes: 3 additions & 4 deletions tests/sdk_tests/bucket/signed_url.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,13 @@ test "signedUrl PUT" {

test "signedUrl duration option is respected" {
let isExpiredTokenError = (output: str) => {
let target = util.env("WING_TARGET");
let var result = false;

if target == "tf-aws" {
if @target == "tf-aws" {
result = output.contains("<Code>AccessDenied</Code><Message>Request has expired</Message>");
} else if target == "tf-gcp" {
} else if @target == "tf-gcp" {
result = output.contains("<Code>ExpiredToken</Code><Message>Invalid argument.</Message>");
} else if target == "sim" {
} else if @target == "sim" {
result = output.contains("Signed URL has expired");
}

Expand Down
4 changes: 1 addition & 3 deletions tests/sdk_tests/container/container.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ skipPlatforms:

bring sim;
bring http;
bring util;
bring expect;

// only relevant in simulator
if util.env("WING_TARGET") == "sim" {

if @target == "sim" {
let echo = new sim.Container(
name: "http-echo",
image: "hashicorp/http-echo",
Expand Down
3 changes: 1 addition & 2 deletions tests/sdk_tests/container/entrypoint.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ skipPlatforms:

bring sim;
bring http;
bring util;
bring expect;

// only relevant in simulator
if util.env("WING_TARGET") == "sim" {
if @target == "sim" {
let entrypoint = new sim.Container(
name: "my-entrypoint-app",
image: "./my-docker-image",
Expand Down
3 changes: 1 addition & 2 deletions tests/sdk_tests/container/mount.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ skipPlatforms:
- darwin
\*/
bring sim;
bring util;

// only relevant in simulator
if util.env("WING_TARGET") == "sim" {
if @target == "sim" {
let container = new sim.Container(
name: "postgres",
image: "postgres:15",
Expand Down
3 changes: 1 addition & 2 deletions tests/sdk_tests/container/network.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ skipPlatforms:

bring sim;
bring http;
bring util;
bring expect;

// only relevant in simulator
if util.env("WING_TARGET") == "sim" {
if @target == "sim" {
let networkHost = new sim.Container(
name: "http-echo",
image: "hashicorp/http-echo",
Expand Down
Loading

0 comments on commit 62d0fab

Please sign in to comment.