Skip to content

Commit 6e421db

Browse files
authored
Add CompilerCommand::link_override (#412)
[capnpc] add `crate_provides()` to specify that an imported schema is provided by another crate
1 parent 3bbb520 commit 6e421db

File tree

17 files changed

+149
-4
lines changed

17 files changed

+149
-4
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ members = [
1919
"async-byte-channel",
2020
"benchmark",
2121
"capnpc/test",
22+
"capnpc/test/external-crate",
2223
"capnpc/test-edition-2015",
2324
"capnpc/test-edition-2018",
2425
"capnpc/test-edition-2021",

capnpc/src/codegen.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub struct CodeGenerationCommand {
3838
default_parent_module: Vec<String>,
3939
raw_code_generator_request_path: Option<PathBuf>,
4040
capnp_root: String,
41+
crates_provide_map: HashMap<u64, String>,
4142
}
4243

4344
impl Default for CodeGenerationCommand {
@@ -47,6 +48,7 @@ impl Default for CodeGenerationCommand {
4748
default_parent_module: Vec::new(),
4849
raw_code_generator_request_path: None,
4950
capnp_root: "::capnp".into(),
51+
crates_provide_map: HashMap::new(),
5052
}
5153
}
5254
}
@@ -93,6 +95,19 @@ impl CodeGenerationCommand {
9395
self
9496
}
9597

98+
/// Sets the crate provides map.
99+
///
100+
/// # Arguments
101+
///
102+
/// - `map` - A map from capnp file id to the crate name that provides the
103+
/// corresponding generated code.
104+
///
105+
/// See [`crate::CompilerCommand::crate_provides`] for more details.
106+
pub fn crates_provide_map(&mut self, map: HashMap<u64, String>) -> &mut Self {
107+
self.crates_provide_map = map;
108+
self
109+
}
110+
96111
/// Generates Rust code according to a `schema_capnp::code_generator_request` read from `inp`.
97112
pub fn run<T>(&mut self, inp: T) -> ::capnp::Result<()>
98113
where
@@ -210,6 +225,8 @@ impl<'a> GeneratorContext<'a> {
210225
capnp_root: code_generation_command.capnp_root.clone(),
211226
};
212227

228+
let crates_provide = &code_generation_command.crates_provide_map;
229+
213230
for node in ctx.request.get_nodes()? {
214231
ctx.node_map.insert(node.get_id(), node);
215232
ctx.node_parents.insert(node.get_id(), node.get_scope_id());
@@ -240,8 +257,15 @@ impl<'a> GeneratorContext<'a> {
240257
"{}_capnp",
241258
path_to_stem_string(importpath)?.replace('-', "_")
242259
);
260+
let parent_module_scope = if let Some(krate) = crates_provide.get(&import.get_id())
261+
{
262+
vec![format!("::{krate}")]
263+
} else {
264+
default_parent_module_scope.clone()
265+
};
266+
243267
ctx.populate_scope_map(
244-
default_parent_module_scope.clone(),
268+
parent_module_scope,
245269
root_name,
246270
NameKind::Verbatim,
247271
import.get_id(),
@@ -284,7 +308,9 @@ impl<'a> GeneratorContext<'a> {
284308
if annotation.get_id() == NAME_ANNOTATION_ID {
285309
current_node_name = name_annotation_value(annotation)?.to_string();
286310
} else if annotation.get_id() == PARENT_MODULE_ANNOTATION_ID {
287-
ancestor_scope_names = vec!["crate".to_string()];
311+
let head = ancestor_scope_names[0].clone();
312+
ancestor_scope_names.clear();
313+
ancestor_scope_names.push(head);
288314
ancestor_scope_names.append(&mut get_parent_module(annotation)?);
289315
}
290316
}

capnpc/src/lib.rs

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ pub mod codegen;
7171
pub mod codegen_types;
7272
mod pointer_constants;
7373

74-
use std::path::{Path, PathBuf};
74+
use std::{
75+
collections::HashMap,
76+
path::{Path, PathBuf},
77+
};
7578

7679
// Copied from capnp/src/lib.rs, where this conversion lives behind the "std" feature flag,
7780
// which we don't want to depend on here.
@@ -119,6 +122,7 @@ pub struct CompilerCommand {
119122
output_path: Option<PathBuf>,
120123
default_parent_module: Vec<String>,
121124
raw_code_generator_request_path: Option<PathBuf>,
125+
crate_provides_map: HashMap<u64, String>,
122126
}
123127

124128
impl CompilerCommand {
@@ -156,6 +160,75 @@ impl CompilerCommand {
156160
self
157161
}
158162

163+
/// Specify that `crate_name` provides generated code for `files`.
164+
///
165+
/// This means that when your schema refers to types defined in `files` we
166+
/// will generate Rust code that uses identifiers in `crate_name`.
167+
///
168+
/// # Arguments
169+
///
170+
/// - `crate_name`: The Rust identifier of the crate
171+
/// - `files`: the Capnp file ids the crate provides generated code for
172+
///
173+
/// # When to use
174+
///
175+
/// You only need this when your generated code needs to refer to types in
176+
/// the external crate. If you just want to use an annotation and the
177+
/// argument to that annotation is a builtin type (e.g. `$Json.name`) this
178+
/// isn't necessary.
179+
///
180+
/// # Example
181+
///
182+
/// If you write a schema like so
183+
///
184+
/// ```capnp
185+
/// // my_schema.capnp
186+
///
187+
/// using Json = import "/capnp/compat/json.capnp";
188+
///
189+
/// struct Foo {
190+
/// value @0 :Json.Value;
191+
/// }
192+
/// ```
193+
///
194+
/// you'd look at [json.capnp][json.capnp] to see its capnp id.
195+
///
196+
/// ```capnp
197+
/// // json.capnp
198+
///
199+
/// # Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors ...
200+
/// @0x8ef99297a43a5e34;
201+
/// ```
202+
///
203+
/// If you want the `foo::Builder::get_value` method generated for your
204+
/// schema to return a `capnp_json::json_capnp::value::Reader` you'd add a
205+
/// dependency on `capnp_json` to your `Cargo.toml` and specify it provides
206+
/// `json.capnp` in your `build.rs`.
207+
///
208+
/// ```rust,no_run
209+
/// // build.rs
210+
///
211+
/// capnpc::CompilerCommand::new()
212+
/// .crate_provides("json_capnp", [0x8ef99297a43a5e34])
213+
/// .file("my_schema.capnp")
214+
/// .run()
215+
/// .unwrap();
216+
/// ```
217+
///
218+
/// [json.capnp]:
219+
/// https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/compat/json.capnp
220+
pub fn crate_provides(
221+
&mut self,
222+
crate_name: impl Into<String>,
223+
files: impl IntoIterator<Item = u64>,
224+
) -> &mut Self {
225+
let crate_name = crate_name.into();
226+
for file in files.into_iter() {
227+
self.crate_provides_map.insert(file, crate_name.clone());
228+
}
229+
self
230+
}
231+
159232
/// Adds the --no-standard-import flag, indicating that the default import paths of
160233
/// /usr/include and /usr/local/include should not bet included.
161234
pub fn no_standard_import(&mut self) -> &mut Self {
@@ -307,7 +380,8 @@ impl CompilerCommand {
307380
let mut code_generation_command = crate::codegen::CodeGenerationCommand::new();
308381
code_generation_command
309382
.output_directory(output_path)
310-
.default_parent_module(self.default_parent_module.clone());
383+
.default_parent_module(self.default_parent_module.clone())
384+
.crates_provide_map(self.crate_provides_map.clone());
311385
if let Some(raw_code_generator_request_path) = &self.raw_code_generator_request_path {
312386
code_generation_command
313387
.raw_code_generator_request_path(raw_code_generator_request_path.clone());

capnpc/test-edition-2015/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ capnpc = { path = "../" }
1616
[dependencies]
1717
capnp = { path = "../../capnp" }
1818
capnpc = { path = "../" }
19+
external-crate = { path = "../test/external-crate" }

capnpc/test-edition-2015/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
fn main() {
22
capnpc::CompilerCommand::new()
3+
.crate_provides("external_crate", [0xe6f94f52f7be8fe2])
34
.file("../test/test.capnp")
45
.src_prefix("../test/")
56
.run()

capnpc/test-edition-2015/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
extern crate capnp;
22
extern crate core;
3+
extern crate external_crate;
34

45
pub mod test_capnp {
56
include!(concat!(env!("OUT_DIR"), "/test_capnp.rs"));

capnpc/test-edition-2018/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ capnpc = { path = "../" }
1616
[dependencies]
1717
capnp = { path = "../../capnp" }
1818
capnpc = { path = "../" }
19+
external-crate = { path = "../test/external-crate" }

capnpc/test-edition-2018/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
fn main() {
22
capnpc::CompilerCommand::new()
3+
.crate_provides("external_crate", [0xe6f94f52f7be8fe2])
34
.file("../test/test.capnp")
45
.src_prefix("../test/")
56
.run()

capnpc/test-edition-2021/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ capnpc = { path = "../" }
1616
[dependencies]
1717
capnp = { path = "../../capnp" }
1818
capnpc = { path = "../" }
19+
external-crate = { path = "../test/external-crate" }

capnpc/test-edition-2021/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
fn main() {
22
capnpc::CompilerCommand::new()
3+
.crate_provides("external_crate", [0xe6f94f52f7be8fe2])
34
.file("../test/test.capnp")
45
.src_prefix("../test/")
56
.run()

capnpc/test/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ capnpc = { path = "../" }
1717
[dependencies]
1818
capnp = { path = "../../capnp" }
1919
capnpc = { path = "../" }
20+
external-crate = { path = "./external-crate" }

capnpc/test/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
fn main() {
22
capnpc::CompilerCommand::new()
3+
.crate_provides("external_crate", [0xe6f94f52f7be8fe2])
34
.file("test.capnp")
45
.file("in-submodule.capnp")
56
.file("in-other-submodule.capnp")

capnpc/test/external-crate/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "external-crate"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
capnp = { version = "0.17.1", path = "../../../capnp" }
10+
11+
[build-dependencies]
12+
capnpc = { version = "0.17.1", path = "../.." }

capnpc/test/external-crate/build.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn main() {
2+
capnpc::CompilerCommand::new()
3+
.file("external.capnp")
4+
.run()
5+
.expect("compiling schema");
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@0xe6f94f52f7be8fe2;
2+
3+
annotation annot (*) :Opts;
4+
5+
struct Opts {
6+
field @0 :Text;
7+
}

capnpc/test/external-crate/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod external_capnp {
2+
include!(concat!(env!("OUT_DIR"), "/external_capnp.rs"));
3+
}

capnpc/test/test.capnp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
@0x99d187209d25cee7;
2424

2525
using Rust = import "rust.capnp";
26+
using External = import "./external-crate/external.capnp";
27+
28+
# The test case is that this builds. This ensure we're able to refer to a struct
29+
# (external_capnp::opts) in the generated code.
30+
struct UseExternalAnnotation $External.annot(field = "foo") {
31+
field @0 :Text;
32+
}
2633

2734
struct FieldSubsetIndexesCorrectly {
2835
common @2 :Text;

0 commit comments

Comments
 (0)