mirror of
https://github.com/topjohnwu/cxx.git
synced 2025-02-21 16:41:01 +00:00
Adding library for high-level code generators.
The intention here is to provide a way for high level code generators to convert a Rust TokenStream into C++ bindings code without the need to write files to disk or invoke an external command.
This commit is contained in:
parent
291a8b87a1
commit
9fc0846261
@ -32,7 +32,7 @@ rustversion = "1.0"
|
||||
trybuild = { version = "1.0.32", features = ["diff"] }
|
||||
|
||||
[workspace]
|
||||
members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"]
|
||||
members = ["demo-rs", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@ -1,4 +1,5 @@
|
||||
This directory contains CXX's C++ code generator. This code generator has two
|
||||
This directory contains CXX's C++ code generator. This code generator has three
|
||||
public frontends, one a command-line application (binary) in the *cmd* directory
|
||||
and the other a library intended to be used from a build.rs in the *build*
|
||||
directory.
|
||||
a library intended to be used from a build.rs in the *build* directory, and
|
||||
a library intended to be used from arbitrary other places (e.g. higher-level
|
||||
code generators) in the *lib* directory.
|
||||
|
21
gen/lib/Cargo.toml
Normal file
21
gen/lib/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "cxx-gen"
|
||||
version = "0.3.4"
|
||||
authors = ["David Tolnay <dtolnay@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "C++ code generator for integrating `cxx` crate into higher level tools."
|
||||
repository = "https://github.com/dtolnay/cxx"
|
||||
keywords = ["ffi"]
|
||||
categories = ["development-tools::ffi"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
cc = "1.0.49"
|
||||
codespan-reporting = "0.9"
|
||||
proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] }
|
||||
quote = { version = "1.0", default-features = false }
|
||||
syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
1
gen/lib/LICENSE-APACHE
Symbolic link
1
gen/lib/LICENSE-APACHE
Symbolic link
@ -0,0 +1 @@
|
||||
../../LICENSE-APACHE
|
1
gen/lib/LICENSE-MIT
Symbolic link
1
gen/lib/LICENSE-MIT
Symbolic link
@ -0,0 +1 @@
|
||||
../../LICENSE-MIT
|
1
gen/lib/src/gen
Symbolic link
1
gen/lib/src/gen
Symbolic link
@ -0,0 +1 @@
|
||||
../../src
|
17
gen/lib/src/lib.rs
Normal file
17
gen/lib/src/lib.rs
Normal file
@ -0,0 +1,17 @@
|
||||
//! The CXX code generator for constructing and compiling C++ code.
|
||||
//!
|
||||
//! This is intended to be embedded into higher-level code generators.
|
||||
|
||||
mod gen;
|
||||
mod syntax;
|
||||
|
||||
use crate::gen::Opt;
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
pub use crate::gen::{Error, Result, GeneratedCode};
|
||||
|
||||
/// Generate C++ bindings code from a Rust token stream. This should be a Rust
|
||||
/// token stream which somewhere contains a `#[cxx::bridge] mod {}`.
|
||||
pub fn generate_header_and_cc(rust_source: TokenStream) -> Result<GeneratedCode> {
|
||||
gen::do_generate_from_tokens(rust_source, Opt::default())
|
||||
}
|
1
gen/lib/src/syntax
Symbolic link
1
gen/lib/src/syntax
Symbolic link
@ -0,0 +1 @@
|
||||
../../../syntax
|
@ -11,13 +11,18 @@ use std::ops::Range;
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
|
||||
pub(super) type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum Error {
|
||||
pub enum Error {
|
||||
/// No `#[cxx::bridge]` module could be found.
|
||||
NoBridgeMod,
|
||||
/// `#[cxx::bridge]` was attached to something other than
|
||||
/// an inline module.
|
||||
OutOfLineMod,
|
||||
/// An IO error occurred when reading Rust code.
|
||||
Io(io::Error),
|
||||
/// A syntax error occurred when parsing Rust code.
|
||||
Syn(syn::Error),
|
||||
}
|
||||
|
||||
|
@ -10,20 +10,23 @@ mod write;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use self::error::{format_err, Error, Result};
|
||||
use self::error::format_err;
|
||||
pub use self::error::{Error, Result};
|
||||
use crate::syntax::namespace::Namespace;
|
||||
use crate::syntax::report::Errors;
|
||||
use crate::syntax::{self, check, Types};
|
||||
use proc_macro2::TokenStream;
|
||||
use std::clone::Clone;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use syn::Item;
|
||||
use syn::{File, Item};
|
||||
|
||||
struct Input {
|
||||
namespace: Namespace,
|
||||
module: Vec<Item>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Clone)]
|
||||
pub(super) struct Opt {
|
||||
/// Any additional headers to #include
|
||||
pub include: Vec<String>,
|
||||
@ -32,6 +35,14 @@ pub(super) struct Opt {
|
||||
pub cxx_impl_annotations: Option<String>,
|
||||
}
|
||||
|
||||
/// Results of code generation.
|
||||
pub struct GeneratedCode {
|
||||
/// The bytes of a C++ header file.
|
||||
pub header: Vec<u8>,
|
||||
/// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
|
||||
pub cxx: Vec<u8>,
|
||||
}
|
||||
|
||||
pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec<u8> {
|
||||
let header = false;
|
||||
generate_from_path(path, opt, header)
|
||||
@ -42,21 +53,43 @@ pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec<u8> {
|
||||
generate_from_path(path, opt, header)
|
||||
}
|
||||
|
||||
pub(super) fn do_generate_from_tokens(
|
||||
tokens: TokenStream,
|
||||
opt: Opt,
|
||||
) -> std::result::Result<GeneratedCode, Error> {
|
||||
let syntax = syn::parse2::<File>(tokens)?;
|
||||
match generate(syntax, opt, true, true) {
|
||||
Ok((Some(header), Some(cxx))) => Ok(GeneratedCode { header, cxx } ),
|
||||
Err(err) => Err(err),
|
||||
_ => panic!("Unexpected generation"),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec<u8> {
|
||||
let source = match fs::read_to_string(path) {
|
||||
Ok(source) => source,
|
||||
Err(err) => format_err(path, "", Error::Io(err)),
|
||||
};
|
||||
match generate(&source, opt, header) {
|
||||
let syntax = match syn::parse_file(&source) {
|
||||
Ok(out) => out,
|
||||
Err(err) => format_err(path, "", Error::Syn(err)),
|
||||
};
|
||||
match generate(syntax, opt, header, !header) {
|
||||
Ok((Some(hdr), None)) => hdr,
|
||||
Ok((None, Some(cxx))) => cxx,
|
||||
Err(err) => format_err(path, &source, err),
|
||||
_ => panic!("Unexpected generation"),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate(source: &str, opt: Opt, header: bool) -> Result<Vec<u8>> {
|
||||
fn generate(
|
||||
syntax: File,
|
||||
opt: Opt,
|
||||
gen_header: bool,
|
||||
gen_cxx: bool,
|
||||
) -> Result<(Option<Vec<u8>>, Option<Vec<u8>>)> {
|
||||
proc_macro2::fallback::force();
|
||||
let ref mut errors = Errors::new();
|
||||
let syntax = syn::parse_file(&source)?;
|
||||
let bridge = find::find_bridge_mod(syntax)?;
|
||||
let ref namespace = bridge.namespace;
|
||||
let ref apis = syntax::parse_items(errors, bridge.module);
|
||||
@ -64,6 +97,18 @@ fn generate(source: &str, opt: Opt, header: bool) -> Result<Vec<u8>> {
|
||||
errors.propagate()?;
|
||||
check::typecheck(errors, namespace, apis, types);
|
||||
errors.propagate()?;
|
||||
let out = write::gen(namespace, apis, types, opt, header);
|
||||
Ok(out.content())
|
||||
// Some callers may wish to generate both header and C++
|
||||
// from the same token stream to avoid parsing twice. But others
|
||||
// only need to generate one or the other.
|
||||
let hdr = if gen_header {
|
||||
Some(write::gen(namespace, apis, types, opt.clone(), true).content())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let cxx = if gen_cxx {
|
||||
Some(write::gen(namespace, apis, types, opt, false).content())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok((hdr, cxx))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user