Skip to content

Commit c86323d

Browse files
authored
C++: Support separation of imported interfaces to libraries (#1469)
* support separation of imported interfaces to libraries * oh, the test name has to be unique across folders
1 parent 4e7b231 commit c86323d

File tree

5 files changed

+100
-19
lines changed

5 files changed

+100
-19
lines changed

crates/cpp/src/lib.rs

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ struct Cpp {
109109
import_prefix: Option<String>,
110110
}
111111

112+
#[cfg(feature = "clap")]
113+
fn parse_with(s: &str) -> Result<(String, String), String> {
114+
let (k, v) = s.split_once('=').ok_or_else(|| {
115+
format!("expected string of form `<key>=<value>[,<key>=<value>...]`; got `{s}`")
116+
})?;
117+
Ok((k.to_string(), v.to_string()))
118+
}
119+
112120
#[derive(Default, Debug, Clone)]
113121
#[cfg_attr(feature = "clap", derive(clap::Args))]
114122
pub struct Opts {
@@ -165,6 +173,14 @@ pub struct Opts {
165173
/// Where to place output files
166174
#[cfg_attr(feature = "clap", arg(skip))]
167175
out_dir: Option<PathBuf>,
176+
177+
/// Importing wit interface from custom include
178+
///
179+
/// Argument must be of the form `k=v` and this option can be passed
180+
/// multiple times or one option can be comma separated, for example
181+
/// `k1=v1,k2=v2`.
182+
#[cfg_attr(feature = "clap", arg(long, value_parser = parse_with, value_delimiter = ','))]
183+
pub with: Vec<(String, String)>,
168184
}
169185

170186
/// Supported API styles for the generated bindings.
@@ -498,29 +514,46 @@ impl WorldGenerator for Cpp {
498514
id: InterfaceId,
499515
_files: &mut Files,
500516
) -> anyhow::Result<()> {
501-
if let Some(prefix) = self
502-
.interface_prefixes
503-
.get(&(Direction::Import, name.clone()))
504-
{
505-
self.import_prefix = Some(prefix.clone());
506-
}
507-
508-
let store = self.start_new_file(None);
509517
self.imported_interfaces.insert(id);
510-
let wasm_import_module = resolve.name_world_key(name);
511-
let binding = Some(name);
512-
let mut r#gen = self.interface(resolve, binding, true, Some(wasm_import_module));
513-
r#gen.interface = Some(id);
514-
r#gen.types(id);
515-
let namespace = namespace(resolve, &TypeOwner::Interface(id), false, &r#gen.r#gen.opts);
516518

517-
for (_name, func) in resolve.interfaces[id].functions.iter() {
518-
if matches!(func.kind, FunctionKind::Freestanding) {
519-
r#gen.r#gen.h_src.change_namespace(&namespace);
520-
r#gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestImport);
519+
let full_name = resolve.name_world_key(name);
520+
match self.opts.with.iter().find(|e| e.0 == full_name) {
521+
None => {
522+
if let Some(prefix) = self
523+
.interface_prefixes
524+
.get(&(Direction::Import, name.clone()))
525+
{
526+
self.import_prefix = Some(prefix.clone());
527+
}
528+
529+
let store = self.start_new_file(None);
530+
let wasm_import_module = resolve.name_world_key(name);
531+
let binding = Some(name);
532+
let mut r#gen = self.interface(resolve, binding, true, Some(wasm_import_module));
533+
r#gen.interface = Some(id);
534+
r#gen.types(id);
535+
let namespace =
536+
namespace(resolve, &TypeOwner::Interface(id), false, &r#gen.r#gen.opts);
537+
538+
for (_name, func) in resolve.interfaces[id].functions.iter() {
539+
if matches!(func.kind, FunctionKind::Freestanding) {
540+
r#gen.r#gen.h_src.change_namespace(&namespace);
541+
r#gen.generate_function(
542+
func,
543+
&TypeOwner::Interface(id),
544+
AbiVariant::GuestImport,
545+
);
546+
}
547+
}
548+
self.finish_file(&namespace, store);
549+
}
550+
Some((_, val)) => {
551+
let with_quotes = format!("\"{val}\"");
552+
if !self.includes.contains(&with_quotes) {
553+
self.includes.push(with_quotes);
554+
}
521555
}
522556
}
523-
self.finish_file(&namespace, store);
524557
let _ = self.import_prefix.take();
525558
Ok(())
526559
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include <wit.h>
2+
3+
namespace my {
4+
namespace inline_ {
5+
namespace foo {
6+
struct Msg {
7+
wit::string field;
8+
};
9+
}
10+
}
11+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//@ args = '--with my:inline/foo=alien.h'
2+
3+
#include <runner_cpp.h>
4+
5+
void exports::runner::Run() {
6+
auto msg = my::inline_::foo::Msg { wit::string::from_view("hello") };
7+
my::inline_::bar::Bar(msg);
8+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <assert.h>
2+
#include <test_cpp.h>
3+
4+
void exports::my::inline_::bar::Bar(::my::inline_::foo::Msg m) {
5+
assert(m.field.get_view() == "hello");
6+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package my:inline;
2+
3+
interface foo {
4+
record msg {
5+
field: string,
6+
}
7+
}
8+
9+
interface bar {
10+
use foo.{msg};
11+
12+
bar: func(m: msg);
13+
}
14+
15+
world test {
16+
export bar;
17+
}
18+
19+
world runner {
20+
import bar;
21+
22+
export run: func();
23+
}

0 commit comments

Comments
 (0)