Skip to content

Commit

Permalink
Add CompilerCommand::link_override (#412)
Browse files Browse the repository at this point in the history
[capnpc] add `crate_provides()` to specify that an imported schema is provided by another crate
  • Loading branch information
dzfranklin authored Jun 22, 2023
1 parent 3bbb520 commit 6e421db
Show file tree
Hide file tree
Showing 17 changed files with 149 additions and 4 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ members = [
"async-byte-channel",
"benchmark",
"capnpc/test",
"capnpc/test/external-crate",
"capnpc/test-edition-2015",
"capnpc/test-edition-2018",
"capnpc/test-edition-2021",
Expand Down
30 changes: 28 additions & 2 deletions capnpc/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub struct CodeGenerationCommand {
default_parent_module: Vec<String>,
raw_code_generator_request_path: Option<PathBuf>,
capnp_root: String,
crates_provide_map: HashMap<u64, String>,
}

impl Default for CodeGenerationCommand {
Expand All @@ -47,6 +48,7 @@ impl Default for CodeGenerationCommand {
default_parent_module: Vec::new(),
raw_code_generator_request_path: None,
capnp_root: "::capnp".into(),
crates_provide_map: HashMap::new(),
}
}
}
Expand Down Expand Up @@ -93,6 +95,19 @@ impl CodeGenerationCommand {
self
}

/// Sets the crate provides map.
///
/// # Arguments
///
/// - `map` - A map from capnp file id to the crate name that provides the
/// corresponding generated code.
///
/// See [`crate::CompilerCommand::crate_provides`] for more details.
pub fn crates_provide_map(&mut self, map: HashMap<u64, String>) -> &mut Self {
self.crates_provide_map = map;
self
}

/// Generates Rust code according to a `schema_capnp::code_generator_request` read from `inp`.
pub fn run<T>(&mut self, inp: T) -> ::capnp::Result<()>
where
Expand Down Expand Up @@ -210,6 +225,8 @@ impl<'a> GeneratorContext<'a> {
capnp_root: code_generation_command.capnp_root.clone(),
};

let crates_provide = &code_generation_command.crates_provide_map;

for node in ctx.request.get_nodes()? {
ctx.node_map.insert(node.get_id(), node);
ctx.node_parents.insert(node.get_id(), node.get_scope_id());
Expand Down Expand Up @@ -240,8 +257,15 @@ impl<'a> GeneratorContext<'a> {
"{}_capnp",
path_to_stem_string(importpath)?.replace('-', "_")
);
let parent_module_scope = if let Some(krate) = crates_provide.get(&import.get_id())
{
vec![format!("::{krate}")]
} else {
default_parent_module_scope.clone()
};

ctx.populate_scope_map(
default_parent_module_scope.clone(),
parent_module_scope,
root_name,
NameKind::Verbatim,
import.get_id(),
Expand Down Expand Up @@ -284,7 +308,9 @@ impl<'a> GeneratorContext<'a> {
if annotation.get_id() == NAME_ANNOTATION_ID {
current_node_name = name_annotation_value(annotation)?.to_string();
} else if annotation.get_id() == PARENT_MODULE_ANNOTATION_ID {
ancestor_scope_names = vec!["crate".to_string()];
let head = ancestor_scope_names[0].clone();
ancestor_scope_names.clear();
ancestor_scope_names.push(head);
ancestor_scope_names.append(&mut get_parent_module(annotation)?);
}
}
Expand Down
78 changes: 76 additions & 2 deletions capnpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ pub mod codegen;
pub mod codegen_types;
mod pointer_constants;

use std::path::{Path, PathBuf};
use std::{
collections::HashMap,
path::{Path, PathBuf},
};

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

impl CompilerCommand {
Expand Down Expand Up @@ -156,6 +160,75 @@ impl CompilerCommand {
self
}

/// Specify that `crate_name` provides generated code for `files`.
///
/// This means that when your schema refers to types defined in `files` we
/// will generate Rust code that uses identifiers in `crate_name`.
///
/// # Arguments
///
/// - `crate_name`: The Rust identifier of the crate
/// - `files`: the Capnp file ids the crate provides generated code for
///
/// # When to use
///
/// You only need this when your generated code needs to refer to types in
/// the external crate. If you just want to use an annotation and the
/// argument to that annotation is a builtin type (e.g. `$Json.name`) this
/// isn't necessary.
///
/// # Example
///
/// If you write a schema like so
///
/// ```capnp
/// // my_schema.capnp
///
/// using Json = import "/capnp/compat/json.capnp";
///
/// struct Foo {
/// value @0 :Json.Value;
/// }
/// ```
///
/// you'd look at [json.capnp][json.capnp] to see its capnp id.
///
/// ```capnp
/// // json.capnp
///
/// # Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors ...
/// @0x8ef99297a43a5e34;
/// ```
///
/// If you want the `foo::Builder::get_value` method generated for your
/// schema to return a `capnp_json::json_capnp::value::Reader` you'd add a
/// dependency on `capnp_json` to your `Cargo.toml` and specify it provides
/// `json.capnp` in your `build.rs`.
///
/// ```rust,no_run
/// // build.rs
///
/// capnpc::CompilerCommand::new()
/// .crate_provides("json_capnp", [0x8ef99297a43a5e34])
/// .file("my_schema.capnp")
/// .run()
/// .unwrap();
/// ```
///
/// [json.capnp]:
/// https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/compat/json.capnp
pub fn crate_provides(
&mut self,
crate_name: impl Into<String>,
files: impl IntoIterator<Item = u64>,
) -> &mut Self {
let crate_name = crate_name.into();
for file in files.into_iter() {
self.crate_provides_map.insert(file, crate_name.clone());
}
self
}

/// Adds the --no-standard-import flag, indicating that the default import paths of
/// /usr/include and /usr/local/include should not bet included.
pub fn no_standard_import(&mut self) -> &mut Self {
Expand Down Expand Up @@ -307,7 +380,8 @@ impl CompilerCommand {
let mut code_generation_command = crate::codegen::CodeGenerationCommand::new();
code_generation_command
.output_directory(output_path)
.default_parent_module(self.default_parent_module.clone());
.default_parent_module(self.default_parent_module.clone())
.crates_provide_map(self.crate_provides_map.clone());
if let Some(raw_code_generator_request_path) = &self.raw_code_generator_request_path {
code_generation_command
.raw_code_generator_request_path(raw_code_generator_request_path.clone());
Expand Down
1 change: 1 addition & 0 deletions capnpc/test-edition-2015/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ capnpc = { path = "../" }
[dependencies]
capnp = { path = "../../capnp" }
capnpc = { path = "../" }
external-crate = { path = "../test/external-crate" }
1 change: 1 addition & 0 deletions capnpc/test-edition-2015/build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
fn main() {
capnpc::CompilerCommand::new()
.crate_provides("external_crate", [0xe6f94f52f7be8fe2])
.file("../test/test.capnp")
.src_prefix("../test/")
.run()
Expand Down
1 change: 1 addition & 0 deletions capnpc/test-edition-2015/test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
extern crate capnp;
extern crate core;
extern crate external_crate;

pub mod test_capnp {
include!(concat!(env!("OUT_DIR"), "/test_capnp.rs"));
Expand Down
1 change: 1 addition & 0 deletions capnpc/test-edition-2018/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ capnpc = { path = "../" }
[dependencies]
capnp = { path = "../../capnp" }
capnpc = { path = "../" }
external-crate = { path = "../test/external-crate" }
1 change: 1 addition & 0 deletions capnpc/test-edition-2018/build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
fn main() {
capnpc::CompilerCommand::new()
.crate_provides("external_crate", [0xe6f94f52f7be8fe2])
.file("../test/test.capnp")
.src_prefix("../test/")
.run()
Expand Down
1 change: 1 addition & 0 deletions capnpc/test-edition-2021/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ capnpc = { path = "../" }
[dependencies]
capnp = { path = "../../capnp" }
capnpc = { path = "../" }
external-crate = { path = "../test/external-crate" }
1 change: 1 addition & 0 deletions capnpc/test-edition-2021/build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
fn main() {
capnpc::CompilerCommand::new()
.crate_provides("external_crate", [0xe6f94f52f7be8fe2])
.file("../test/test.capnp")
.src_prefix("../test/")
.run()
Expand Down
1 change: 1 addition & 0 deletions capnpc/test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ capnpc = { path = "../" }
[dependencies]
capnp = { path = "../../capnp" }
capnpc = { path = "../" }
external-crate = { path = "./external-crate" }
1 change: 1 addition & 0 deletions capnpc/test/build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
fn main() {
capnpc::CompilerCommand::new()
.crate_provides("external_crate", [0xe6f94f52f7be8fe2])
.file("test.capnp")
.file("in-submodule.capnp")
.file("in-other-submodule.capnp")
Expand Down
12 changes: 12 additions & 0 deletions capnpc/test/external-crate/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "external-crate"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
capnp = { version = "0.17.1", path = "../../../capnp" }

[build-dependencies]
capnpc = { version = "0.17.1", path = "../.." }
6 changes: 6 additions & 0 deletions capnpc/test/external-crate/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fn main() {
capnpc::CompilerCommand::new()
.file("external.capnp")
.run()
.expect("compiling schema");
}
7 changes: 7 additions & 0 deletions capnpc/test/external-crate/external.capnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@0xe6f94f52f7be8fe2;

annotation annot (*) :Opts;

struct Opts {
field @0 :Text;
}
3 changes: 3 additions & 0 deletions capnpc/test/external-crate/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod external_capnp {
include!(concat!(env!("OUT_DIR"), "/external_capnp.rs"));
}
7 changes: 7 additions & 0 deletions capnpc/test/test.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
@0x99d187209d25cee7;

using Rust = import "rust.capnp";
using External = import "./external-crate/external.capnp";

# The test case is that this builds. This ensure we're able to refer to a struct
# (external_capnp::opts) in the generated code.
struct UseExternalAnnotation $External.annot(field = "foo") {
field @0 :Text;
}

struct FieldSubsetIndexesCorrectly {
common @2 :Text;
Expand Down

0 comments on commit 6e421db

Please sign in to comment.