Skip to content

Commit 2f795ab

Browse files
authored
feat: simplify go sdk generation (#1714)
* feat: simplify go sdk generation Signed-off-by: Andrew Steurer <94206073+asteurer@users.noreply.github.com> * fix: linting Signed-off-by: Andrew Steurer <94206073+asteurer@users.noreply.github.com> * fix: failing tests Signed-off-by: Andrew Steurer <94206073+asteurer@users.noreply.github.com> --------- Signed-off-by: Andrew Steurer <94206073+asteurer@users.noreply.github.com>
1 parent 59c3e4d commit 2f795ab

2 files changed

Lines changed: 63 additions & 35 deletions

File tree

‎crates/go/src/lib.rs‎

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -122,21 +122,13 @@ pub struct Opts {
122122
#[cfg_attr(feature = "clap", clap(long))]
123123
pub generate_stubs: bool,
124124

125-
/// If specified, organize the bindings into a package for use as a library;
126-
/// otherwise (if `None`), the bindings will be organized for use as a
127-
/// standalone executable.
125+
/// If set, organize the bindings as a library package: `imports` and
126+
/// `exports` (if stubs are generated) each get their own directory named
127+
/// after them. If `None`, organize the bindings for a standalone executable
128+
/// instead.
128129
#[cfg_attr(feature = "clap", clap(long))]
129130
pub pkg_name: Option<String>,
130131

131-
/// When `--pkg-name` is specified, optionally specify a different package
132-
/// for exports.
133-
///
134-
/// This allows you to put the exports and imports in separate packages when
135-
/// building a library. If only `--pkg-name` is specified, this will
136-
/// default to that value.
137-
#[cfg_attr(feature = "clap", clap(long, requires = "pkg_name"))]
138-
pub export_pkg_name: Option<String>,
139-
140132
/// Print the version of the remote package being used for the shared WIT types.
141133
///
142134
/// Must be specified in addition to the `pkg-name` flag.
@@ -248,11 +240,15 @@ struct Go {
248240
impl Go {
249241
/// Adds the bindings module prefix to a package name.
250242
fn mod_pkg(&self, for_export: bool, name: &str) -> String {
251-
let prefix = for_export
252-
.then_some(())
253-
.and(self.opts.export_pkg_name.as_deref())
254-
.or(self.opts.pkg_name.as_deref())
255-
.unwrap_or("wit_component");
243+
let prefix = if let Some(pkg) = self.opts.pkg_name.as_ref() {
244+
if for_export {
245+
format!("{pkg}/exports")
246+
} else {
247+
format!("{pkg}/imports")
248+
}
249+
} else {
250+
"wit_component".to_string()
251+
};
256252
format!(r#""{prefix}/{name}""#)
257253
}
258254

@@ -951,7 +947,7 @@ impl WorldGenerator for Go {
951947
println!("{}", remote_pkg_version());
952948
}
953949
// If a module name is specified, the generated files will be used as a library.
954-
("wit_exports/wit_exports.go", "wit_exports", "")
950+
("exports/wit_exports/wit_exports.go", "wit_exports", "")
955951
} else {
956952
files.push(
957953
"go.mod",
@@ -980,7 +976,7 @@ func main() {}
980976
};
981977

982978
files.push(
983-
exports_file_path,
979+
&exports_file_path,
984980
&maybe_gofmt(
985981
self.opts.format,
986982
format!(
@@ -1005,14 +1001,23 @@ var {SYNC_EXPORT_PINNER} = runtime.Pinner{{}}
10051001
),
10061002
);
10071003

1008-
for (prefix, interfaces) in [("export_", &self.export_interfaces), ("", &self.interfaces)] {
1004+
let (import_path_prefix, export_path_prefix) = if self.opts.pkg_name.is_some() {
1005+
("imports/", "exports/")
1006+
} else {
1007+
("", "")
1008+
};
1009+
1010+
for (prefix, interfaces, path_prefix) in [
1011+
("export_", &self.export_interfaces, export_path_prefix),
1012+
("", &self.interfaces, import_path_prefix),
1013+
] {
10091014
for (name, data) in interfaces {
10101015
let imports = data.imports();
10111016
let code = &data.code;
10121017
let header = if data.has_stubs { stub_header } else { header };
10131018

10141019
files.push(
1015-
&format!("{prefix}{name}/wit_bindings.go"),
1020+
&format!("{path_prefix}{prefix}{name}/wit_bindings.go"),
10161021
&maybe_gofmt(
10171022
self.opts.format,
10181023
format!(
@@ -1030,7 +1035,7 @@ import (
10301035
);
10311036

10321037
files.push(
1033-
&format!("{prefix}{name}/empty.s"),
1038+
&format!("{path_prefix}{prefix}{name}/empty.s"),
10341039
r#"// This file exists for testing this package without WebAssembly,
10351040
// allowing empty function bodies with a //go:wasmimport directive.
10361041
// See https://pkg.go.dev/cmd/compile for more information."#

‎crates/test/src/go.rs‎

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,24 @@ impl LanguageMethods for Go {
3737
runner: &Runner,
3838
name: &str,
3939
config: &crate::config::WitConfig,
40-
_args: &[String],
40+
args: &[String],
4141
) -> bool {
4242
if config.error_context {
4343
return true;
4444
}
45-
if name == "named-fixed-length-list.wit" {
45+
if name.starts_with("named-fixed-length-list.wit") {
4646
return true;
4747
}
4848
if !runner.go_async_supported() {
49-
return name == "async-trait-function.wit" || name == "issue-1598.wit";
49+
// The `--pkg-name` (library) verify path runs `go build ./...`
50+
// with no link step, so the missing `runtime.wasiOnIdle` symbol
51+
// never causes a failure and these tests compile fine even on an
52+
// unpatched toolchain. Only the c-shared (non-pkg) path fails.
53+
let pkg_mode = args.iter().any(|a| a == "--pkg-name");
54+
if !pkg_mode {
55+
return name.starts_with("async-trait-function.wit")
56+
|| name.starts_with("issue-1598.wit");
57+
}
5058
}
5159

5260
false
@@ -77,6 +85,15 @@ impl LanguageMethods for Go {
7785
&["--generate-stubs"]
7886
}
7987

88+
fn codegen_test_variants(&self) -> &[(&str, &[&str])] {
89+
// Also run every codegen fixture through the `--pkg` library layout
90+
// (imports/, exports/ subdirectories). `wit_component` matches the
91+
// module name written by `replace_bindings_go_mod`, so the generated
92+
// `wit_component/imports/...` and `wit_component/exports/...` import
93+
// paths resolve against that go.mod.
94+
&[("pkg-name", &["--pkg-name", "wit_component"])]
95+
}
96+
8097
fn prepare(&self, runner: &mut Runner) -> Result<()> {
8198
let cwd = env::current_dir()?;
8299
let dir = cwd.join(&runner.opts.artifacts).join("go");
@@ -189,18 +206,24 @@ func main() {}
189206
.arg("./..."),
190207
)?;
191208

192-
// `go build`
193-
runner.run_command(
194-
Command::new("go")
195-
.current_dir(verify.bindings_dir)
196-
.env("GOOS", "wasip1")
197-
.env("GOARCH", "wasm")
198-
.arg("build")
199-
.arg("-o")
209+
// `go build`. In `--pkg` (library) mode there is no main package in the
210+
// bindings root, so `-buildmode=c-shared` on the current directory fails.
211+
// Instead compile every subpackage with `./...`.
212+
let pkg_mode = verify.args.iter().any(|a| a == "--pkg-name");
213+
let mut cmd = Command::new("go");
214+
cmd.current_dir(verify.bindings_dir)
215+
.env("GOOS", "wasip1")
216+
.env("GOARCH", "wasm")
217+
.arg("build");
218+
if pkg_mode {
219+
cmd.arg("./...");
220+
} else {
221+
cmd.arg("-o")
200222
.arg(verify.artifacts_dir.join("tmp.wasm"))
201223
.arg("-buildmode=c-shared")
202-
.arg("-ldflags=-checklinkname=0"),
203-
)?;
224+
.arg("-ldflags=-checklinkname=0");
225+
}
226+
runner.run_command(&mut cmd)?;
204227
Ok(())
205228
}
206229
}

0 commit comments

Comments
 (0)