Commit 4064a092 authored by Ryan Hunt's avatar Ryan Hunt
Browse files

Do a bunch of clean up and add some comments

parent 9d10cd96
......@@ -38,7 +38,9 @@ fn main() {
let root = env::var("CARGO_MANIFEST_DIR").unwrap();
let config = Config::from_root_or_default(&root);
Library::load(&root, &config)
Library::load_crate(Path::new(root),
"CRATE_NAME",
&config)
.generate().unwrap()
.write_to_file("bindings.h");
}
......@@ -59,8 +61,6 @@ See `compile-tests/` for some examples of rust source that can be handled.
## Future work
1. Add a validation step to catch common issues
2. Better support for types with fully specified names
3. Better support for finding dependencies managed by Cargo
4. Support for generating a FFI interface for a Struct+Impl
5. ...
1. Better support for types with fully specified names
2. Support for generating a FFI interface for a Struct+Impl
3. ...
......@@ -38,11 +38,17 @@ impl AnnotationSet {
pub fn parse(text: String) -> Result<AnnotationSet, String> {
let mut annotations = HashMap::new();
// Look at each line for an annotation
for line in text.lines().map(|x| x.trim_left_matches("///").trim()) {
// Skip lines that don't start with cbindgen
if !line.starts_with("cbindgen:") {
continue;
}
// Remove the "cbingen:" prefix
let annotation = &line[9..];
// Split the annotation in two
let parts: Vec<&str> = annotation.split("=")
.map(|x| x.trim())
.collect();
......@@ -51,13 +57,16 @@ impl AnnotationSet {
return Err(format!("couldn't parse {}", line));
}
// Grab the name that this annotation is modifying
let name = parts[0];
// If the annotation only has a name, assume it's setting a bool flag
if parts.len() == 1 {
annotations.insert(name.to_string(), AnnotationValue::Bool(true));
continue;
}
// Parse the value we're setting the name to
let value = parts[1];
if let Some(x) = parse_list(value) {
......@@ -111,6 +120,7 @@ impl AnnotationSet {
}
}
/// Parse lists like "[x, y, z]". This is not implemented efficiently or well.
fn parse_list(list: &str) -> Option<Vec<String>> {
if list.len() < 2 {
return None;
......
......@@ -3,10 +3,11 @@ use std::path::Path;
use std::process::Command;
use std::str::from_utf8;
type ExpandResult = Result<String, String>;
pub fn expand(manifest_path: &Path, crate_name: &str) -> ExpandResult {
/// Use rustc to expand and pretty print the crate into a single file,
/// removing any macros in the process.
pub fn expand(manifest_path: &Path, crate_name: &str) -> Result<String, String> {
let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
let mut cmd = Command::new(cargo);
cmd.arg("rustc");
cmd.arg("--manifest-path");
......
......@@ -35,6 +35,7 @@ pub struct Package {
pub name: String,
}
/// Parse the Cargo.toml for a given path
pub fn manifest(manifest_path: &Path) -> Result<Manifest, Error> {
let mut s = String::new();
let mut f = File::open(manifest_path)?;
......
......@@ -89,8 +89,7 @@ impl CDecl {
}
}
fn to_string(&self, ident: Option<&str>) -> String
{
fn to_string(&self, ident: Option<&str>) -> String {
// Build the left side (the type-specifier and type-qualifier),
// and then build the right side (the declarators), and then
// merge the result.
......@@ -169,11 +168,9 @@ impl CDecl {
}
}
pub fn write_func<F: Write>(out: &mut SourceWriter<F>, f: &Function)
{
pub fn write_func<F: Write>(out: &mut SourceWriter<F>, f: &Function) {
out.write(&CDecl::from_func(f).to_string(Some(&f.name)));
}
pub fn write_type<F: Write>(out: &mut SourceWriter<F>, t: &Type, ident: &str)
{
pub fn write_type<F: Write>(out: &mut SourceWriter<F>, t: &Type, ident: &str) {
out.write(&CDecl::from_type(t).to_string(Some(ident)));
}
......@@ -19,21 +19,6 @@ pub enum Language {
C,
}
/// A style of braces to use for generating code.
#[derive(Debug, Clone, PartialEq)]
pub enum Braces {
SameLine,
NextLine,
}
/// A type of layout to use when generating long lines of code.
#[derive(Debug, Clone, PartialEq)]
pub enum Layout {
Horizontal,
Vertical,
Auto,
}
impl FromStr for Language {
type Err = String;
......@@ -53,6 +38,16 @@ impl FromStr for Language {
}
}
}
deserialize_enum_str!(Language);
/// A style of braces to use for generating code.
#[derive(Debug, Clone, PartialEq)]
pub enum Braces {
SameLine,
NextLine,
}
impl FromStr for Braces {
type Err = String;
......@@ -66,6 +61,17 @@ impl FromStr for Braces {
}
}
}
deserialize_enum_str!(Braces);
/// A type of layout to use when generating long lines of code.
#[derive(Debug, Clone, PartialEq)]
pub enum Layout {
Horizontal,
Vertical,
Auto,
}
impl FromStr for Layout {
type Err = String;
......@@ -82,67 +88,8 @@ impl FromStr for Layout {
}
}
deserialize_enum_str!(Language);
deserialize_enum_str!(Braces);
deserialize_enum_str!(Layout);
/// A collection of settings to customize the generated bindings.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct Config {
/// Optional text to output at the beginning of the file
pub header: Option<String>,
/// Optional text to output at the end of the file
pub trailer: Option<String>,
/// Option name to use for an include guard
pub include_guard: Option<String>,
/// Optional text to output at major sections to deter manual editing
pub autogen_warning: Option<String>,
/// Include a comment with the version of cbindgen used to generate the file
pub include_version: bool,
/// The style to use for braces
pub braces: Braces,
/// The preferred length of a line, used for auto breaking function arguments
pub line_length: usize,
/// The amount of spaces in a tab
pub tab_width: usize,
/// The language to output bindings for
pub language: Language,
/// The names of crates to parse with `rustc --pretty=expanded`
pub expand: Vec<String>,
/// The configuration options for functions
#[serde(rename = "fn")]
pub function: FunctionConfig,
/// The configuration options for structs
#[serde(rename = "struct")]
pub structure: StructConfig,
/// The configuration options for enums
#[serde(rename = "enum")]
pub enumeration: EnumConfig,
}
impl Default for Config {
fn default() -> Config {
Config {
header: None,
trailer: None,
include_guard: None,
autogen_warning: None,
include_version: true,
braces: Braces::SameLine,
line_length: 100,
tab_width: 2,
language: Language::Cxx,
expand: Vec::new(),
function: FunctionConfig::default(),
structure: StructConfig::default(),
enumeration: EnumConfig::default(),
}
}
}
/// Settings to apply to generated functions.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
......@@ -170,6 +117,22 @@ impl Default for FunctionConfig {
}
}
impl FunctionConfig {
pub fn prefix(&self, annotations: &AnnotationSet) -> Option<String> {
if let Some(x) = annotations.atom("prefix") {
return x;
}
self.prefix.clone()
}
pub fn postfix(&self, annotations: &AnnotationSet) -> Option<String> {
if let Some(x) = annotations.atom("postfix") {
return x;
}
self.postfix.clone()
}
}
/// Settings to apply to generated structs.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
......@@ -206,6 +169,45 @@ impl Default for StructConfig {
}
}
impl StructConfig {
pub fn derive_eq(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-eq") {
return x;
}
self.derive_eq
}
pub fn derive_neq(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-neq") {
return x;
}
self.derive_neq
}
pub fn derive_lt(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-lt") {
return x;
}
self.derive_lt
}
pub fn derive_lte(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-lte") {
return x;
}
self.derive_lte
}
pub fn derive_gt(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-gt") {
return x;
}
self.derive_gt
}
pub fn derive_gte(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-gte") {
return x;
}
self.derive_gte
}
}
/// Settings to apply to generated enums.
#[derive( Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
......@@ -228,6 +230,72 @@ impl Default for EnumConfig {
}
}
impl EnumConfig {
pub fn add_sentinel(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("add-sentinel") {
return x;
}
self.add_sentinel
}
}
/// A collection of settings to customize the generated bindings.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct Config {
/// Optional text to output at the beginning of the file
pub header: Option<String>,
/// Optional text to output at the end of the file
pub trailer: Option<String>,
/// Option name to use for an include guard
pub include_guard: Option<String>,
/// Optional text to output at major sections to deter manual editing
pub autogen_warning: Option<String>,
/// Include a comment with the version of cbindgen used to generate the file
pub include_version: bool,
/// The style to use for braces
pub braces: Braces,
/// The preferred length of a line, used for auto breaking function arguments
pub line_length: usize,
/// The amount of spaces in a tab
pub tab_width: usize,
/// The language to output bindings for
pub language: Language,
/// The names of crates to parse with `rustc --pretty=expanded`
pub expand: Vec<String>,
/// The configuration options for functions
#[serde(rename = "fn")]
pub function: FunctionConfig,
/// The configuration options for structs
#[serde(rename = "struct")]
pub structure: StructConfig,
/// The configuration options for enums
#[serde(rename = "enum")]
pub enumeration: EnumConfig,
}
impl Default for Config {
fn default() -> Config {
Config {
header: None,
trailer: None,
include_guard: None,
autogen_warning: None,
include_version: true,
braces: Braces::SameLine,
line_length: 100,
tab_width: 2,
language: Language::Cxx,
expand: Vec::new(),
function: FunctionConfig::default(),
structure: StructConfig::default(),
enumeration: EnumConfig::default(),
}
}
}
impl Config {
pub fn from_file(file_name: &str) -> Result<Config, String> {
fn read(file_name: &str) -> io::Result<String> {
......@@ -256,67 +324,3 @@ impl Config {
}
}
}
impl FunctionConfig {
pub fn prefix(&self, annotations: &AnnotationSet) -> Option<String> {
if let Some(x) = annotations.atom("prefix") {
return x;
}
self.prefix.clone()
}
pub fn postfix(&self, annotations: &AnnotationSet) -> Option<String> {
if let Some(x) = annotations.atom("postfix") {
return x;
}
self.postfix.clone()
}
}
impl StructConfig {
pub fn derive_eq(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-eq") {
return x;
}
self.derive_eq
}
pub fn derive_neq(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-neq") {
return x;
}
self.derive_neq
}
pub fn derive_lt(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-lt") {
return x;
}
self.derive_lt
}
pub fn derive_lte(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-lte") {
return x;
}
self.derive_lte
}
pub fn derive_gt(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-gt") {
return x;
}
self.derive_gt
}
pub fn derive_gte(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-gte") {
return x;
}
self.derive_gte
}
}
impl EnumConfig {
pub fn add_sentinel(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("add-sentinel") {
return x;
}
self.add_sentinel
}
}
......@@ -39,6 +39,7 @@ pub enum PrimitiveType {
Float,
Double,
}
impl PrimitiveType {
fn maybe(path: &str) -> Option<PrimitiveType> {
match path {
......@@ -83,6 +84,7 @@ impl PrimitiveType {
true
}
}
impl fmt::Display for PrimitiveType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
......@@ -124,11 +126,12 @@ pub enum Type {
Array(Box<Type>, u64),
FuncPtr(Box<Type>, Vec<Type>),
}
impl Type {
pub fn convert(ty: &syn::Ty) -> ConvertResult<Option<Type>> {
pub fn load(ty: &syn::Ty) -> Result<Option<Type>, String> {
let converted = match ty {
&syn::Ty::Rptr(_, ref mut_ty) => {
let converted = try!(Type::convert(&mut_ty.ty));
let converted = Type::load(&mut_ty.ty)?;
let converted = match converted {
Some(converted) => converted,
......@@ -141,7 +144,7 @@ impl Type {
}
}
&syn::Ty::Ptr(ref mut_ty) => {
let converted = try!(Type::convert(&mut_ty.ty));
let converted = Type::load(&mut_ty.ty)?;
let converted = match converted {
Some(converted) => converted,
......@@ -154,7 +157,7 @@ impl Type {
}
}
&syn::Ty::Path(_, ref p) => {
let (name, generics) = try!(p.convert_to_generic_single_segment());
let (name, generics) = p.convert_to_generic_single_segment()?;
if name == "PhantomData" {
return Ok(None);
......@@ -169,7 +172,7 @@ impl Type {
}
}
&syn::Ty::Array(ref ty, syn::ConstExpr::Lit(syn::Lit::Int(sz, _))) => {
let converted = try!(Type::convert(ty));
let converted = Type::load(ty)?;
let converted = match converted {
Some(converted) => converted,
......@@ -179,9 +182,9 @@ impl Type {
Type::Array(Box::new(converted), sz)
},
&syn::Ty::BareFn(ref f) => {
let args = try!(f.inputs.iter()
.try_skip_map(|x| Type::convert(&x.ty)));
let ret = try!(f.output.as_type());
let args = f.inputs.iter()
.try_skip_map(|x| Type::load(&x.ty))?;
let ret = f.output.as_type()?;
Type::FuncPtr(Box::new(ret), args)
},
......@@ -281,6 +284,7 @@ impl Type {
}
}
}
impl Source for (String, Type) {
fn write<F: Write>(&self, _config: &Config, out: &mut SourceWriter<F>) {
cdecl::write_type(out, &self.1, &self.0);
......@@ -297,14 +301,14 @@ pub struct Function {
}
impl Function {
pub fn convert(name: String,
annotations: AnnotationSet,
decl: &syn::FnDecl,
extern_decl: bool) -> ConvertResult<Function>
pub fn load(name: String,
annotations: AnnotationSet,
decl: &syn::FnDecl,
extern_decl: bool) -> Result<Function, String>
{
let args = try!(decl.inputs.iter()
.try_skip_map(|x| x.as_ident_and_type()));
let ret = try!(decl.output.as_type());
let args = decl.inputs.iter()
.try_skip_map(|x| x.as_ident_and_type())?;
let ret = decl.output.as_type()?;
Ok(Function {
name: name,
......@@ -335,6 +339,7 @@ impl Function {
}
}
}
impl Source for Function {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
fn write_1<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
......@@ -352,6 +357,7 @@ impl Source for Function {
}
out.write(";");
}
fn write_2<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
let prefix = config.function.prefix(&func.annotations);
let postfix = config.function.postfix(&func.annotations);
......@@ -388,21 +394,21 @@ pub struct Struct {
}
impl Struct {
pub fn convert(name: String,
annotations: AnnotationSet,
decl: &syn::VariantData,
generics: &syn::Generics) -> ConvertResult<Struct>
pub fn load(name: String,
annotations: AnnotationSet,
decl: &syn::VariantData,
generics: &syn::Generics) -> Result<Struct, String>
{
let fields = match decl {
&syn::VariantData::Struct(ref fields) => {
try!(fields.iter()
.try_skip_map(|x| x.as_ident_and_type()))
fields.iter()
.try_skip_map(|x| x.as_ident_and_type())?
}
&syn::VariantData::Tuple(ref fields) => {
let mut out = Vec::new();
let mut current = 0;
for field in fields {
if let Some(x) = try!(Type::convert(&field.ty)) {
if let Some(x) = Type::load(&field.ty)? {
out.push((format!("{}", current), x));
current += 1;
}
......@@ -457,6 +463,7 @@ impl Struct {
}
}
}
impl Source for Struct {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
assert!(self.generic_params.is_empty());
......@@ -548,6 +555,7 @@ impl OpaqueStruct {
}
}
}
impl Source for OpaqueStruct {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
if config.language == Language::C {
......@@ -560,6 +568,15 @@ impl Source for OpaqueStruct {
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Repr {
None,
C,
U8,
U16,
U32,
}
#[derive(Debug, Clone)]
pub struct Enum {
pub name: String,
......@@ -569,10 +586,10 @@ pub struct Enum {
}