Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,15 @@ cargo install --git https://github.com/bytecodealliance/componentize-go
## Usage

Please reference the `README.md` and `Makefile` files in each of the directories in [examples](./examples/).

## Build tags

When compiling your Go module, `componentize-go` passes `go build` the
`componentizego_async` build tag when the selected WIT world uses async
features (async functions, `stream`s, or `future`s).

SDKs can use `//go:build componentizego_async` to select between WASI 0.2 and
WASI 0.3 implementations of an API at compile time.

Any `-tags` you specify via the `GOFLAGS` environment variable are merged with
the tags above (rather than being overridden by them).
9 changes: 9 additions & 0 deletions examples/wasip3/export_wasi_http_handler/async_tag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build componentizego_async

package export_wasi_http_handler

// componentize-go passes the `componentizego_async` build tag to `go build`
// when the target world uses async features, as this example's
// `wasip3-example` world does. The reference to this constant in handler.go
// will fail to compile if the tag does not reach the Go compiler.
const asyncTagPresent = true
5 changes: 5 additions & 0 deletions examples/wasip3/export_wasi_http_handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import (
. "go.bytecodealliance.org/pkg/wit/types"
)

// This fails to compile unless componentize-go passed the
// `componentizego_async` build tag (see async_tag.go), which it must for this
// async world.
var _ = asyncTagPresent

// Handle the specified `Request`, returning a `Response`
func Handle(request *Request) Result[*Response, ErrorCode] {
method := request.GetMethod().Tag()
Expand Down
62 changes: 28 additions & 34 deletions src/cmd_build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::utils::{check_go_version, make_path_absolute};
use crate::utils::{check_go_version, go_tags_arg, make_path_absolute};
use anyhow::{Result, anyhow};
use std::{
path::{Path, PathBuf},
Expand All @@ -9,7 +9,16 @@ use std::{
///
/// If the module is not going to be adapted to the component model,
/// set the `only_wasip1` arg to true.
pub fn build_module(out: Option<&PathBuf>, go: &Path, only_wasip1: bool) -> Result<PathBuf> {
///
/// `tags` contains any build tags derived from the target world (see
/// [`crate::utils::world_build_tags`]); these are merged with any `-tags`
/// specified via `GOFLAGS` and passed to `go build`.
pub fn build_module(
out: Option<&PathBuf>,
go: &Path,
only_wasip1: bool,
tags: &[String],
) -> Result<PathBuf> {
check_go_version(go)?;

let out_path_buf = match &out {
Expand All @@ -26,39 +35,24 @@ pub fn build_module(out: Option<&PathBuf>, go: &Path, only_wasip1: bool) -> Resu
.to_str()
.ok_or_else(|| anyhow!("Output path is not valid unicode"))?;

// The -buildmode flag mutes the module's output, so it is ommitted
let module_args = [
"build",
"-C",
".",
"-ldflags=-checklinkname=0",
"-o",
out_path,
];

let component_args = [
"build",
"-C",
".",
"-buildmode=c-shared",
"-ldflags=-checklinkname=0",
"-o",
out_path,
];
let mut args = vec!["build".to_string(), "-C".to_string(), ".".to_string()];
// The -buildmode flag mutes the module's output, so it is ommitted when
// building a plain wasip1 module
if !only_wasip1 {
args.push("-buildmode=c-shared".to_string());
}
args.push("-ldflags=-checklinkname=0".to_string());
if let Some(tags_arg) = go_tags_arg(tags) {
args.push(tags_arg);
}
args.push("-o".to_string());
args.push(out_path.to_string());

let output = if only_wasip1 {
Command::new(go)
.args(module_args)
.env("GOOS", "wasip1")
.env("GOARCH", "wasm")
.output()?
} else {
Command::new(go)
.args(component_args)
.env("GOOS", "wasip1")
.env("GOARCH", "wasm")
.output()?
};
let output = Command::new(go)
.args(&args)
.env("GOOS", "wasip1")
.env("GOARCH", "wasm")
.output()?;

if !output.status.success() {
return Err(anyhow!(
Expand Down
79 changes: 37 additions & 42 deletions src/cmd_test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::utils::{check_go_version, make_path_absolute};
use crate::utils::{check_go_version, go_tags_arg, make_path_absolute};
use anyhow::{Result, anyhow};
use std::{
path::{Path, PathBuf},
Expand All @@ -9,11 +9,16 @@ use std::{
///
/// If the module is not going to be adapted to the component model,
/// set the `only_wasip1` arg to true.
///
/// `tags` contains any build tags derived from the target world (see
/// [`crate::utils::world_build_tags`]); these are merged with any `-tags`
/// specified via `GOFLAGS` and passed to `go test -c`.
pub fn build_test_module(
path: &Path,
output_dir: Option<&PathBuf>,
go: &Path,
only_wasip1: bool,
tags: &[String],
) -> Result<PathBuf> {
check_go_version(go)?;

Expand All @@ -36,51 +41,41 @@ pub fn build_test_module(
std::fs::create_dir_all(dir)?;
}

// The -buildmode flag mutes the unit test output, so it is ommitted
let module_args = [
"test",
"-c",
"-ldflags=-checklinkname=0",
"-o",
test_wasm_path
.to_str()
.expect("the combined paths of 'output-dir' and 'pkg' are not valid unicode"),
path.to_str().expect("pkg path is not valid unicode"),
];

// TODO: for when we figure out how wasip2 tests are to be run
#[allow(unused_variables)]
let component_args = [
"test",
"-c",
"-buildmode=c-shared",
"-ldflags=-checklinkname=0",
"-o",
test_wasm_path
.to_str()
.expect("the combined paths of 'output-dir' and 'pkg' are not valid unicode"),
path.to_str().expect("pkg path is not valid unicode"),
];

let output = if only_wasip1 {
Command::new(go)
.args(module_args)
.env("GOOS", "wasip1")
.env("GOARCH", "wasm")
.output()?
} else {
if !only_wasip1 {
// TODO: for when we figure out how wasip2 tests are to be run; that
// will require adding `-buildmode=c-shared` to the args below.
unimplemented!(
"Building Go test components is not yet supported. Please use the --wasip1 flag when building unit tests."
);
}

// TODO: for when we figure out how wasip2 tests are to be run
#[allow(unreachable_code)]
Command::new(go)
.args(component_args)
.env("GOOS", "wasip1")
.env("GOARCH", "wasm")
.output()?
};
// The -buildmode flag mutes the unit test output, so it is ommitted
let mut args = vec![
"test".to_string(),
"-c".to_string(),
"-ldflags=-checklinkname=0".to_string(),
];
if let Some(tags_arg) = go_tags_arg(tags) {
args.push(tags_arg);
}
args.push("-o".to_string());
args.push(
test_wasm_path
.to_str()
.expect("the combined paths of 'output-dir' and 'pkg' are not valid unicode")
.to_string(),
);
args.push(
path.to_str()
.expect("pkg path is not valid unicode")
.to_string(),
);

let output = Command::new(go)
.args(&args)
.env("GOOS", "wasip1")
.env("GOARCH", "wasm")
.output()?;

if !output.status.success() {
return Err(anyhow!(
Expand Down
24 changes: 21 additions & 3 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use crate::{
cmd_bindings::generate_bindings,
cmd_build::build_module,
cmd_test::build_test_module,
utils::{dummy_wit, embed_wit, install_go, module_to_component, parse_wit, pick_go},
utils::{
dummy_wit, embed_wit, install_go, module_to_component, parse_wit, pick_go, world_build_tags,
},
};
use anyhow::{Result, anyhow};
use clap::{Parser, Subcommand};
Expand Down Expand Up @@ -225,8 +227,16 @@ fn build(wit_opts: WitOpts, build: Build) -> Result<()> {

let go = &pick_go(&resolve, world, build.go.as_deref())?;

// Build tags derived from the target world (none for plain wasip1
// modules, which don't target a world).
let tags = if build.wasip1 {
Vec::new()
} else {
world_build_tags(&resolve, world)
};

// Build a wasm module using `go build`.
let module = build_module(build.output.as_ref(), go, build.wasip1)?;
let module = build_module(build.output.as_ref(), go, build.wasip1, &tags)?;

if !build.wasip1 {
// Embed the WIT documents in the wasip1 component.
Expand Down Expand Up @@ -258,9 +268,17 @@ fn test(wit_opts: WitOpts, test: Test) -> Result<()> {
return Err(anyhow!("Path to a package containing Go tests is required"));
}

// Build tags derived from the target world (none for plain wasip1
// modules, which don't target a world).
let tags = if test.wasip1 {
Vec::new()
} else {
world_build_tags(&resolve, world)
};

for pkg in test.pkg.iter() {
// Build a wasm module using `go test -c`.
let module = build_test_module(pkg, test.output.as_ref(), go, test.wasip1)?;
let module = build_test_module(pkg, test.output.as_ref(), go, test.wasip1, &tags)?;

if !test.wasip1 {
// Embed the WIT documents in the wasm module.
Expand Down
Loading