diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index cc2f8e98b..b34850b85 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -109,6 +109,14 @@ struct Cpp { import_prefix: Option, } +#[cfg(feature = "clap")] +fn parse_with(s: &str) -> Result<(String, String), String> { + let (k, v) = s.split_once('=').ok_or_else(|| { + format!("expected string of form `=[,=...]`; got `{s}`") + })?; + Ok((k.to_string(), v.to_string())) +} + #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Args))] pub struct Opts { @@ -165,6 +173,14 @@ pub struct Opts { /// Where to place output files #[cfg_attr(feature = "clap", arg(skip))] out_dir: Option, + + /// Importing wit interface from custom include + /// + /// Argument must be of the form `k=v` and this option can be passed + /// multiple times or one option can be comma separated, for example + /// `k1=v1,k2=v2`. + #[cfg_attr(feature = "clap", arg(long, value_parser = parse_with, value_delimiter = ','))] + pub with: Vec<(String, String)>, } /// Supported API styles for the generated bindings. @@ -498,29 +514,46 @@ impl WorldGenerator for Cpp { id: InterfaceId, _files: &mut Files, ) -> anyhow::Result<()> { - if let Some(prefix) = self - .interface_prefixes - .get(&(Direction::Import, name.clone())) - { - self.import_prefix = Some(prefix.clone()); - } - - let store = self.start_new_file(None); self.imported_interfaces.insert(id); - let wasm_import_module = resolve.name_world_key(name); - let binding = Some(name); - let mut r#gen = self.interface(resolve, binding, true, Some(wasm_import_module)); - r#gen.interface = Some(id); - r#gen.types(id); - let namespace = namespace(resolve, &TypeOwner::Interface(id), false, &r#gen.r#gen.opts); - for (_name, func) in resolve.interfaces[id].functions.iter() { - if matches!(func.kind, FunctionKind::Freestanding) { - r#gen.r#gen.h_src.change_namespace(&namespace); - r#gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestImport); + let full_name = resolve.name_world_key(name); + match self.opts.with.iter().find(|e| e.0 == full_name) { + None => { + if let Some(prefix) = self + .interface_prefixes + .get(&(Direction::Import, name.clone())) + { + self.import_prefix = Some(prefix.clone()); + } + + let store = self.start_new_file(None); + let wasm_import_module = resolve.name_world_key(name); + let binding = Some(name); + let mut r#gen = self.interface(resolve, binding, true, Some(wasm_import_module)); + r#gen.interface = Some(id); + r#gen.types(id); + let namespace = + namespace(resolve, &TypeOwner::Interface(id), false, &r#gen.r#gen.opts); + + for (_name, func) in resolve.interfaces[id].functions.iter() { + if matches!(func.kind, FunctionKind::Freestanding) { + r#gen.r#gen.h_src.change_namespace(&namespace); + r#gen.generate_function( + func, + &TypeOwner::Interface(id), + AbiVariant::GuestImport, + ); + } + } + self.finish_file(&namespace, store); + } + Some((_, val)) => { + let with_quotes = format!("\"{val}\""); + if !self.includes.contains(&with_quotes) { + self.includes.push(with_quotes); + } } } - self.finish_file(&namespace, store); let _ = self.import_prefix.take(); Ok(()) } diff --git a/tests/runtime/cpp/cpp-with/cpp/alien.h b/tests/runtime/cpp/cpp-with/cpp/alien.h new file mode 100644 index 000000000..7c70aec1b --- /dev/null +++ b/tests/runtime/cpp/cpp-with/cpp/alien.h @@ -0,0 +1,11 @@ +#include + +namespace my { + namespace inline_ { + namespace foo { + struct Msg { + wit::string field; + }; + } + } +} diff --git a/tests/runtime/cpp/cpp-with/runner.cpp b/tests/runtime/cpp/cpp-with/runner.cpp new file mode 100644 index 000000000..c96275d64 --- /dev/null +++ b/tests/runtime/cpp/cpp-with/runner.cpp @@ -0,0 +1,8 @@ +//@ args = '--with my:inline/foo=alien.h' + +#include + +void exports::runner::Run() { + auto msg = my::inline_::foo::Msg { wit::string::from_view("hello") }; + my::inline_::bar::Bar(msg); +} diff --git a/tests/runtime/cpp/cpp-with/test.cpp b/tests/runtime/cpp/cpp-with/test.cpp new file mode 100644 index 000000000..355addd92 --- /dev/null +++ b/tests/runtime/cpp/cpp-with/test.cpp @@ -0,0 +1,6 @@ +#include +#include + +void exports::my::inline_::bar::Bar(::my::inline_::foo::Msg m) { + assert(m.field.get_view() == "hello"); +} diff --git a/tests/runtime/cpp/cpp-with/test.wit b/tests/runtime/cpp/cpp-with/test.wit new file mode 100644 index 000000000..a608e40cc --- /dev/null +++ b/tests/runtime/cpp/cpp-with/test.wit @@ -0,0 +1,23 @@ +package my:inline; + +interface foo { + record msg { + field: string, + } +} + +interface bar { + use foo.{msg}; + + bar: func(m: msg); +} + +world test { + export bar; +} + +world runner { + import bar; + + export run: func(); +}