diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 7e146e7d9..3d3283b60 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -5,6 +5,7 @@ use heck::*; use std::collections::{HashMap, HashSet}; use std::fmt::Write; use std::mem; +use std::path::PathBuf; use wit_bindgen_core::abi::{self, AbiVariant, Bindgen, Bitcast, Instruction, LiftLower, WasmType}; use wit_bindgen_core::{ dealias, uwrite, uwriteln, wit_parser::*, Direction, Files, InterfaceGenerator as _, Ns, @@ -52,6 +53,9 @@ pub struct Opts { // Skip generating C object file #[cfg_attr(feature = "clap", arg(long, default_value_t = false))] pub no_object_file: bool, + /// output directory + #[cfg_attr(feature = "clap", arg(long, hide = true))] + pub c_out_dir: Option, } impl Opts { @@ -375,11 +379,16 @@ impl WorldGenerator for C { #endif" ); - files.push(&format!("{snake}.h"), h_str.as_bytes()); - files.push(&format!("{snake}.c"), c_str.as_bytes()); + let dst = match &self.opts.c_out_dir { + Some(path) => path, + None => "", + }; + + files.push(&format!("{dst}{snake}.h"), h_str.as_bytes()); + files.push(&format!("{dst}{snake}.c"), c_str.as_bytes()); if !self.opts.no_object_file { files.push( - &format!("{snake}_component_type.o",), + &format!("{dst}{snake}_component_type.o",), component_type_object::object(resolve, id, self.opts.string_encoding) .unwrap() .as_slice(), diff --git a/crates/go/src/interface.rs b/crates/go/src/interface.rs index 7312dc80d..39e12237f 100644 --- a/crates/go/src/interface.rs +++ b/crates/go/src/interface.rs @@ -9,7 +9,9 @@ use wit_bindgen_core::wit_parser::{ Field, Function, FunctionKind, Handle, InterfaceId, LiveTypes, Resolve, Type, TypeDefKind, TypeId, TypeOwner, WorldKey, }; -use wit_bindgen_core::{uwriteln, Direction, InterfaceGenerator as _, Source}; +use wit_bindgen_core::{uwriteln, Direction, InterfaceGenerator as _, Source, WorldGenerator}; + +use crate::path::GoPath; use super::{avoid_keyword, bindgen, TinyGo}; @@ -61,7 +63,7 @@ impl InterfaceGenerator<'_> { assert!(prev.is_none()); // add Go types to the list - let mut name = self.owner_namespace(ty); + let mut name = "".to_string(); name.push_str(&self.ty_name(&Type::Id(ty))); let prev = self.gen.type_names.insert(ty, name.clone()); @@ -101,7 +103,7 @@ impl InterfaceGenerator<'_> { c_owner_namespace( self.interface, matches!(self.direction, Direction::Import), - self.gen.world.clone(), + self.gen.world.name().to_string(), self.resolve, id, ) @@ -127,18 +129,18 @@ impl InterfaceGenerator<'_> { WorldKey::Name(k) => k.to_upper_camel_case(), WorldKey::Interface(id) => { let mut name = String::new(); - if matches!(self.direction, Direction::Export) { - name.push_str("Exports"); - } + // if matches!(self.direction, Direction::Export) { + // name.push_str("Exports"); + // } let iface = &self.resolve.interfaces[*id]; - let pkg = &self.resolve.packages[iface.package.unwrap()]; - name.push_str(&pkg.name.namespace.to_upper_camel_case()); - name.push_str(&pkg.name.name.to_upper_camel_case()); - if let Some(version) = &pkg.name.version { - let version = version.to_string().replace(['.', '-', '+'], "_"); - name.push_str(&version); - name.push('_'); - } + // let pkg = &self.resolve.packages[iface.package.unwrap()]; + // name.push_str(&pkg.name.namespace.to_upper_camel_case()); + // name.push_str(&pkg.name.name.to_upper_camel_case()); + // if let Some(version) = &pkg.name.version { + // let version = version.to_string().replace(['.', '-', '+'], "_"); + // name.push_str(&version); + // name.push('_'); + // } name.push_str(&iface.name.as_ref().unwrap().to_upper_camel_case()); name } @@ -171,13 +173,13 @@ impl InterfaceGenerator<'_> { /// Otherwise, the type name is not converted. pub(crate) fn type_name(&self, ty_name: &str, convert: bool) -> String { let mut name = String::new(); - let namespace = self.namespace(); + // let namespace = self.namespace(); let ty_name = if convert { ty_name.to_upper_camel_case() } else { ty_name.into() }; - name.push_str(&namespace); + // name.push_str(&namespace); name.push_str(&ty_name); name } @@ -530,13 +532,13 @@ impl InterfaceGenerator<'_> { c_func_name( matches!(direction, Direction::Import), self.resolve, - &self.gen.world, + self.gen.world.name(), self.interface.map(|(_, key)| key), func, ) } else { // do not want to generate public functions - format!("{}{}", self.namespace(), self.func_name(func)).to_lower_camel_case() + self.func_name(func).to_lower_camel_case() }; if matches!(direction, Direction::Export) { @@ -577,8 +579,8 @@ impl InterfaceGenerator<'_> { match func.kind { FunctionKind::Freestanding => { - let namespace = self.namespace(); - self.src.push_str(&namespace); + // let namespace = self.namespace(); + // self.src.push_str(&namespace); } FunctionKind::Method(ty) => { let ty = self.get_ty(&Type::Id(ty)); @@ -820,7 +822,7 @@ impl InterfaceGenerator<'_> { let name = c_func_name( matches!(self.direction, Direction::Import), self.resolve, - &self.gen.world, + self.gen.world.name(), self.interface.map(|(_, key)| key), func, ); @@ -990,6 +992,37 @@ impl InterfaceGenerator<'_> { } self.src.push_str("}\n"); } + + pub(crate) fn start_append_submodule(&mut self, name: &WorldKey) -> (String, Vec) { + let snake = match name { + WorldKey::Name(name) => avoid_keyword(name), + WorldKey::Interface(id) => { + avoid_keyword(self.resolve.interfaces[*id].name.as_ref().unwrap()) + } + }; + let module_path: Vec = name.to_path(&self.resolve, self.direction); + (snake, module_path) + } + + pub(crate) fn finish_append_submodule(mut self, snake: &str, module_path: Vec) { + self.finish(); + let _ = match self.direction { + Direction::Import => self.gen.go_import_packages.push( + snake, + self.src, + self.preamble, + &self.gen.world, + module_path, + ), + Direction::Export => self.gen.go_export_packages.push( + snake, + self.src, + self.preamble, + &self.gen.world, + module_path, + ), + }; + } } impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { @@ -1227,6 +1260,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { ) { let name = self.type_name(name, true); let ty = self.get_ty(ty); + // TODO: determine where `ty` is from and add import path to preamble self.src.push_str(&format!("type {name} = {ty}\n")); } diff --git a/crates/go/src/lib.rs b/crates/go/src/lib.rs index 023e04d0b..fd99d1a95 100644 --- a/crates/go/src/lib.rs +++ b/crates/go/src/lib.rs @@ -1,19 +1,25 @@ use std::collections::{HashMap, HashSet}; -use std::io::{Read, Write}; +use std::fmt::Write; +use std::io::{Read, Write as _}; use std::mem; use std::process::Stdio; use anyhow::Result; -use heck::{ToKebabCase, ToSnakeCase}; +use heck::ToKebabCase; use wit_bindgen_c::imported_types_used_by_exported_interfaces; use wit_bindgen_core::wit_parser::{ - Function, InterfaceId, LiveTypes, Resolve, SizeAlign, Type, TypeId, WorldId, WorldKey, + Function, InterfaceId, LiveTypes, Resolve, Type, TypeId, WorldId, WorldKey, }; -use wit_bindgen_core::{Direction, Files, Source, WorldGenerator}; +use wit_bindgen_core::{generated_preamble, uwriteln, Direction, Files, Source, WorldGenerator}; +use world::{Packages, TinyGoWorld}; mod bindgen; mod imports; mod interface; +mod path; +mod world; + +static C_GEN_FILES_PATH: &'static str = "c_files_"; #[derive(Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Args))] @@ -21,11 +27,18 @@ pub struct Opts { /// Whether or not `gofmt` is executed to format generated code. #[cfg_attr(feature = "clap", arg(long))] pub gofmt: bool, + + /// The optional package name to use for the generated code. + #[cfg_attr(feature = "clap", arg(long))] + pub package_name: Option, } impl Default for Opts { fn default() -> Self { - Self { gofmt: true } // Set the default value of gofmt to true + Self { + gofmt: true, // Set the default value of gofmt to true + package_name: None, // Set the default value of package_name to None + } } } @@ -40,22 +53,21 @@ impl Opts { #[derive(Default)] pub struct TinyGo { + // the options for the generator provided by the user opts: Opts, + + // the generated code src: Source, // the parts immediately precede the import of "C" preamble: Source, - world: String, + // the name of the world being generated + world: TinyGoWorld, // import requirements for the generated code import_requirements: imports::ImportRequirements, - sizes: SizeAlign, - - // mapping from interface ID to the name of the interface - interface_names: HashMap, - // C type names c_type_names: HashMap, @@ -69,16 +81,17 @@ pub struct TinyGo { // resource interface and the resource destructors exported_resources: HashSet, - // the world ID - world_id: Option, + /// tracking all the pending Go packages to be generated + go_import_packages: Packages, + go_export_packages: Packages, } impl TinyGo { - fn interface<'a>( - &'a mut self, - resolve: &'a Resolve, + fn interface<'b>( + &'b mut self, + resolve: &'b Resolve, direction: Direction, - ) -> interface::InterfaceGenerator<'a> { + ) -> interface::InterfaceGenerator { interface::InterfaceGenerator { src: Source::default(), preamble: Source::default(), @@ -145,10 +158,10 @@ impl TinyGo { impl WorldGenerator for TinyGo { fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { - let name = &resolve.worlds[world].name; - self.world = name.to_string(); - self.sizes.fill(resolve); - self.world_id = Some(world); + self.world = TinyGoWorld::from_world_id(world, resolve); + + self.go_import_packages.prefix_name = self.opts.package_name.clone(); + self.go_export_packages.prefix_name = self.opts.package_name.clone(); } fn import_interface( @@ -161,20 +174,18 @@ impl WorldGenerator for TinyGo { let name_raw = &resolve.name_world_key(name); self.src .push_str(&format!("// Import functions from {name_raw}\n")); - self.interface_names.insert(id, name.clone()); let mut gen = self.interface(resolve, Direction::Import); gen.interface = Some((id, name)); + let (snake, module_path) = gen.start_append_submodule(name); + gen.define_interface_types(id); for (_name, func) in resolve.interfaces[id].functions.iter() { gen.import(resolve, func); } - let src = mem::take(&mut gen.src); - let preamble = mem::take(&mut gen.preamble); - self.src.push_str(&src); - self.preamble.append_src(&preamble); + gen.finish_append_submodule(&snake, module_path); } fn import_funcs( @@ -189,19 +200,18 @@ impl WorldGenerator for TinyGo { .push_str(&format!("// Import functions from {name}\n")); let mut gen = self.interface(resolve, Direction::Import); + gen.define_function_types(funcs); for (_name, func) in funcs.iter() { gen.import(resolve, func); } - let src = mem::take(&mut gen.src); - let preamble = mem::take(&mut gen.preamble); - self.src.push_str(&src); - self.preamble.append_src(&preamble); + + gen.finish_append_submodule(name, vec!["imports".to_string(), name.to_owned()]); } fn pre_export_interface(&mut self, resolve: &Resolve, _files: &mut Files) -> Result<()> { - let world = self.world_id.unwrap(); + let world = self.world.unwrap_id(); let live_import_types = imported_types_used_by_exported_interfaces(resolve, world); self.c_type_namespaces .retain(|k, _| live_import_types.contains(k)); @@ -218,12 +228,12 @@ impl WorldGenerator for TinyGo { id: InterfaceId, _files: &mut Files, ) -> Result<()> { - self.interface_names.insert(id, name.clone()); let name_raw = &resolve.name_world_key(name); self.src .push_str(&format!("// Export functions from {name_raw}\n")); let mut gen = self.interface(resolve, Direction::Export); + let (snake, module_path) = gen.start_append_submodule(name); gen.interface = Some((id, name)); gen.define_interface_types(id); @@ -231,12 +241,7 @@ impl WorldGenerator for TinyGo { gen.export(resolve, func); } - gen.finish(); - - let src = mem::take(&mut gen.src); - let preamble = mem::take(&mut gen.preamble); - self.src.push_str(&src); - self.preamble.append_src(&preamble); + gen.finish_append_submodule(&snake, module_path); Ok(()) } @@ -257,100 +262,130 @@ impl WorldGenerator for TinyGo { for (_name, func) in funcs.iter() { gen.export(resolve, func); } - - gen.finish(); - - let src = mem::take(&mut gen.src); - let preamble = mem::take(&mut gen.preamble); - self.src.push_str(&src); - self.preamble.append_src(&preamble); + gen.finish_append_submodule(name, vec!["exports".to_string(), name.to_owned()]); Ok(()) } fn import_types( &mut self, resolve: &Resolve, - _world: WorldId, + world: WorldId, types: &[(&str, TypeId)], _files: &mut Files, ) { + let name = &resolve.worlds[world].name; let mut gen = self.interface(resolve, Direction::Import); let mut live = LiveTypes::default(); for (_, id) in types { live.add_type_id(resolve, *id); } gen.define_live_types(&live); - let src = mem::take(&mut gen.src); - self.src.push_str(&src); + gen.finish_append_submodule(name, vec!["imports".to_string(), name.to_owned()]); } fn finish(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) { - // make sure all types are defined on top of the file - let src = mem::take(&mut self.src); - self.src.push_str(&src); - - // prepend package and imports header - let src = mem::take(&mut self.src); - wit_bindgen_core::generated_preamble(&mut self.src, env!("CARGO_PKG_VERSION")); - let snake = self.world.to_snake_case(); - // add package - self.src.push_str("package "); - self.src.push_str(&snake); - self.src.push_str("\n\n"); - - // import C - self.src.push_str("// #include \""); - self.src.push_str(self.world.to_snake_case().as_str()); - self.src.push_str(".h\"\n"); - if self.preamble.len() > 0 { - self.src.append_src(&self.preamble); - } - self.src.push_str("import \"C\"\n"); - let world = &resolve.worlds[id]; + self.go_import_packages.finish(resolve, id, files); + self.go_export_packages.finish(resolve, id, files); - self.import_requirements.generate( - snake, - files, - format!("{}_types.go", world.name.to_kebab_case()), - ); - self.src.push_str(&self.import_requirements.src); - - self.src.push_str(&src); - - if self.opts.gofmt { - let mut child = std::process::Command::new("gofmt") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .expect("failed to spawn gofmt"); - child - .stdin - .take() - .unwrap() - .write_all(self.src.as_bytes()) - .expect("failed to write to gofmt"); - self.src.as_mut_string().truncate(0); - child - .stdout - .take() - .unwrap() - .read_to_string(self.src.as_mut_string()) - .expect("failed to read from gofmt"); - let status = child.wait().expect("failed to wait on gofmt"); - assert!(status.success()); - } - files.push( - &format!("{}.go", world.name.to_kebab_case()), - self.src.as_bytes(), - ); + // TODO: + // 1. import_requirements + // 2. opts (gofmt) let mut opts = wit_bindgen_c::Opts::default(); opts.no_sig_flattening = true; opts.no_object_file = true; + opts.c_out_dir = Some(format!("{C_GEN_FILES_PATH}/")); opts.build() .generate(resolve, id, files) - .expect("C generator should be infallible") + .expect("C generator should be infallible"); + + let mut c_lib: Source = Source::default(); + let version = env!("CARGO_PKG_VERSION"); + generated_preamble(&mut c_lib, version); + c_lib.push_str(&format!("package {C_GEN_FILES_PATH}\n\n")); + uwriteln!( + c_lib, + " + // Go will only compile the C code if there is a C file in the same directory + // as the Go file. This file is a dummy Go package that it's only purpose is + // to compile the C code and expose it to the Go code. In order to use this Go + // package, you need to import it in your Go code + // and then import the C header file using CGo. + // + // This package is only used internally by wit-bindgen, and it's not meant to + // be used by the user. The user should not import this package. + " + ); + c_lib.push_str("import \"C\"\n\n"); + files.push(&format!("{C_GEN_FILES_PATH}/lib.go"), c_lib.as_bytes()); } + // fn finish_(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) { + // // make sure all types are defined on top of the file + // let src = mem::take(&mut self.src); + // self.src.push_str(&src); + + // // prepend package and imports header + // let src = mem::take(&mut self.src); + // wit_bindgen_core::generated_preamble(&mut self.src, env!("CARGO_PKG_VERSION")); + // let snake = self.world.to_snake_case(); + // // add package + // self.src.push_str("package "); + // self.src.push_str(&snake); + // self.src.push_str("\n\n"); + + // // import C + // self.src.push_str("// #include \""); + // self.src.push_str(self.world.to_snake_case().as_str()); + // self.src.push_str(".h\"\n"); + // if self.preamble.len() > 0 { + // self.src.append_src(&self.preamble); + // } + // self.src.push_str("import \"C\"\n"); + // let world = &resolve.worlds[id]; + + // self.import_requirements.generate( + // snake, + // files, + // format!("{}_types.go", world.name.to_kebab_case()), + // ); + // self.src.push_str(&self.import_requirements.src); + + // self.src.push_str(&src); + + // if self.opts.gofmt { + // let mut child = std::process::Command::new("gofmt") + // .stdin(Stdio::piped()) + // .stdout(Stdio::piped()) + // .spawn() + // .expect("failed to spawn gofmt"); + // child + // .stdin + // .take() + // .unwrap() + // .write_all(self.src.as_bytes()) + // .expect("failed to write to gofmt"); + // self.src.as_mut_string().truncate(0); + // child + // .stdout + // .take() + // .unwrap() + // .read_to_string(self.src.as_mut_string()) + // .expect("failed to read from gofmt"); + // let status = child.wait().expect("failed to wait on gofmt"); + // assert!(status.success()); + // } + // files.push( + // &format!("{}.go", world.name.to_kebab_case()), + // self.src.as_bytes(), + // ); + + // let mut opts = wit_bindgen_c::Opts::default(); + // opts.no_sig_flattening = true; + // opts.no_object_file = true; + // opts.build() + // .generate(resolve, id, files) + // .expect("C generator should be infallible") + // } } fn avoid_keyword(s: &str) -> String { diff --git a/crates/go/src/path.rs b/crates/go/src/path.rs index 202ac5fcd..724e39cc1 100644 --- a/crates/go/src/path.rs +++ b/crates/go/src/path.rs @@ -1,5 +1,8 @@ use heck::ToSnakeCase; -use wit_bindgen_core::{wit_parser::{WorldKey, Resolve, PackageId}, Direction}; +use wit_bindgen_core::{ + wit_parser::{PackageId, Resolve, WorldKey}, + Direction, +}; pub(crate) trait GoPath { fn to_path(&self, resolve: &Resolve, direction: Direction) -> Vec; @@ -8,18 +11,19 @@ pub(crate) trait GoPath { impl GoPath for WorldKey { fn to_path(&self, resolve: &Resolve, direction: Direction) -> Vec { let mut path = Vec::new(); - if matches!(direction, Direction::Export) { - path.push("exports".to_string()); + match direction { + Direction::Import => path.push("imports".to_string()), + Direction::Export => path.push("exports".to_string()), } - match self { + match self { WorldKey::Name(n) => path.push(n.to_snake_case()), WorldKey::Interface(id) => { - let iface = &resolve.interfaces[*id]; - let pkg = iface.package.unwrap(); - let pkgname = resolve.packages[pkg].name.clone(); - path.push(pkgname.namespace.to_snake_case()); - path.push(name_package_module(resolve, pkg)); - path.push(iface.name.as_ref().unwrap().to_snake_case()); + let iface = &resolve.interfaces[*id]; + let pkg = iface.package.unwrap(); + let pkgname = resolve.packages[pkg].name.clone(); + path.push(pkgname.namespace.to_snake_case()); + path.push(name_package_module(resolve, pkg)); + path.push(iface.name.as_ref().unwrap().to_snake_case()); } } path diff --git a/crates/go/src/world.rs b/crates/go/src/world.rs new file mode 100644 index 000000000..6bacd577b --- /dev/null +++ b/crates/go/src/world.rs @@ -0,0 +1,191 @@ +use std::{collections::HashMap, mem}; + +use crate::{ + interface::{self, InterfaceGenerator}, + C_GEN_FILES_PATH, +}; +use heck::{ToSnakeCase, ToUpperCamelCase}; +use wit_bindgen_core::{ + wit_parser::{Resolve, WorldId}, + Direction, Files, Source, +}; + +/// Bookkeeping for the name of the world being generated and its ID. +#[derive(Default, Debug, Clone)] +pub struct TinyGoWorld { + name: String, + id: Option, +} + +impl TinyGoWorld { + pub fn from_world_id(id: WorldId, resolve: &Resolve) -> Self { + Self { + name: resolve.worlds[id].name.to_string(), + id: Some(id), + } + } + pub fn to_snake_case(&self) -> String { + self.name.to_snake_case() + } + pub fn to_upper_camel_case(&self) -> String { + self.name.to_upper_camel_case() + } + pub fn unwrap_id(&self) -> WorldId { + self.id.unwrap() + } + pub fn name(&self) -> &str { + &self.name + } +} + +/// Bookkeeping for the name of the package being generated +/// and each package corresponds to either an imported or exported interface +/// , or all imported functions and types from the world, or all +/// exported functions from the world. +#[derive(Default)] +pub struct Packages { + pub prefix_name: Option, + packages: HashMap, +} + +pub struct Package { + pub src: Source, + pub preamble: Source, + pub world: TinyGoWorld, + pub path: Vec, + pub c_files_path: String, + pub prefix_name: Option, +} + +impl Packages { + pub fn push( + &mut self, + name: &str, + src: Source, + preamble: Source, + world: &'_ TinyGoWorld, + module_path: Vec, + ) -> Option { + let prefix_name = self.prefix_name.clone(); + let c_files_path = get_c_files_path(&module_path); + let mut package = Package::new( + src, + preamble, + world.clone(), + module_path, + c_files_path, + prefix_name, + ); + + let old_package = self.packages.insert(name.to_owned(), package); + assert!(old_package.is_none()); + old_package + } + + pub fn finish(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) { + for (_, mut package) in self { + package.finish(resolve, id); + let mut path_name = package.path.join("/"); + path_name.push_str(".wit.go"); + files.push(&path_name, package.src.as_bytes()); + } + } +} + +fn get_c_files_path(module_path: &[String]) -> String { + // for each path in module_path, we will go to the parent directory + // and then join the path with the C_GEN_FILES_PATH + let mut path = String::new(); + for _ in 0..module_path.len() - 1 { + path.push_str("../"); + } + path.push_str(C_GEN_FILES_PATH); + path +} + +impl Package { + pub fn new( + src: Source, + preamble: Source, + world: TinyGoWorld, + path: Vec, + c_files_path: String, + prefix_name: Option, + ) -> Self { + Self { + src, + preamble, + world, + path, + c_files_path, + prefix_name, + } + } + + pub fn finish(&mut self, resolve: &Resolve, id: WorldId) { + let src = mem::take(&mut self.src); + + // generate the preamble + wit_bindgen_core::generated_preamble(&mut self.src, env!("CARGO_PKG_VERSION")); + + // generate the package name + assert!(self.path.len() > 1); + let snake = self.path[self.path.len() - 2].to_snake_case(); + self.src.push_str("package "); + self.src.push_str(&snake); + self.src.push_str("\n\n"); + + // use the Go exposed C API + let prefix_name = match self.prefix_name.take() { + Some(name) => name, + None => "main".to_owned(), + }; + self.src.push_str(&format!( + "import _ \"{prefix_name}/{C_GEN_FILES_PATH}\"\n\n" + )); + + // generate CGo preamble + self.src.push_str("// #cgo CFLAGS: -I"); + self.src.push_str(&self.c_files_path); + self.src.push_str("\n"); + + self.src.push_str("// #include \""); + self.src.push_str(self.world.to_snake_case().as_str()); + self.src.push_str(".h\"\n"); + + if self.preamble.len() > 0 { + self.src.append_src(&self.preamble); + } + self.src.push_str("import \"C\"\n"); + let world = &resolve.worlds[id]; + + self.src.push_str(&src); + } +} + +impl IntoIterator for Packages { + type Item = (String, Package); + type IntoIter = std::collections::hash_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.packages.into_iter() + } +} + +impl<'a> IntoIterator for &'a Packages { + type Item = (&'a String, &'a Package); + type IntoIter = std::collections::hash_map::Iter<'a, String, Package>; + + fn into_iter(self) -> Self::IntoIter { + self.packages.iter() + } +} + +impl<'a> IntoIterator for &'a mut Packages { + type Item = (&'a String, &'a mut Package); + type IntoIter = std::collections::hash_map::IterMut<'a, String, Package>; + + fn into_iter(self) -> Self::IntoIter { + self.packages.iter_mut() + } +} diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index a213dd91a..cdae0920a 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -268,21 +268,25 @@ fn tests(name: &str, dir_name: &str) -> Result> { drop(fs::remove_dir_all(&out_dir)); let mut files = Default::default(); - wit_bindgen_go::Opts::default() + let mut go_opts = wit_bindgen_go::Opts::default(); + go_opts.package_name = Some(format!("{}", snake)); + go_opts .build() .generate(&resolve, world, &mut files) .unwrap(); - let gen_dir = out_dir.join("gen"); + let gen_dir = out_dir.clone(); fs::create_dir_all(&gen_dir).unwrap(); for (file, contents) in files.iter() { let dst = gen_dir.join(file); + fs::create_dir_all(dst.parent().unwrap()).unwrap(); + println!("write {dst:?}"); fs::write(dst, contents).unwrap(); } for go_impl in &go { fs::copy(&go_impl, out_dir.join(format!("{snake}.go"))).unwrap(); } - let go_mod = format!("module wit_{snake}_go\n\ngo 1.20"); + let go_mod = format!("module {snake}\n\ngo 1.20"); fs::write(out_dir.join("go.mod"), go_mod).unwrap(); let out_wasm = out_dir.join("go.wasm"); diff --git a/tests/runtime/smoke/wasm.go b/tests/runtime/smoke/wasm.go index ff3c0c316..7c2955a83 100644 --- a/tests/runtime/smoke/wasm.go +++ b/tests/runtime/smoke/wasm.go @@ -1,18 +1,19 @@ package main import ( - . "wit_smoke_go/gen" + exports "smoke/exports" + smoke "smoke/imports/test/smoke" ) func init() { n := SmokeImpl{} - SetSmoke(n) + exports.SetSmoke(n) } type SmokeImpl struct{} func (s SmokeImpl) Thunk() { - TestSmokeImportsThunk() + smoke.Thunk() } func main() {}