@@ -17,10 +17,10 @@
use std::fmt::Write;
-use crate::spec::{OutFileDef, StructDef, Typ};
+use crate::spec::{EnumDef, OutFileDef, StructDef, Typ};
use convert_case::{Case, Casing};
-use log::{debug, trace};
+use log::{debug, error, trace};
/// An abstract indentation level. 0 is no indentation, 1 is [`INDENT_WIDTH`]
/// and so on.
@@ -31,7 +31,7 @@ struct Indentation(usize);
const INDENT_WIDTH: usize = 4;
/// Create a C-compatible struct field. Without the terminating semicolon.
-fn structfield(typ: &Typ, name: &str) -> String {
+fn structfield(filedef: &OutFileDef, typ: &Typ, name: &str) -> String {
match typ {
Typ::Ptr(x) => {
let t: &Typ = x;
@@ -51,7 +51,20 @@ fn structfield(typ: &Typ, name: &str) -> String {
)
}
Typ::Struct(x) => format!("struct {x} {name}"),
- Typ::Array(x, len) => format!("{}{name}[{len}]", structfield(x, "")),
+ Typ::Enum(x) => {
+ // C can't use an enum as a field and fix its width. Look for its
+ // underlying layout and use that type instead.
+ let Some(e) = filedef.enums.iter().find(|y| *x == y.name) else {
+ error!("Can't find enum {x}. Typo?");
+ std::process::exit(1);
+ };
+ format!(
+ "{} /* See {} */",
+ structfield(filedef, &e.typ, name),
+ e.name
+ )
+ }
+ Typ::Array(x, len) => format!("{}{name}[{len}]", structfield(filedef, x, "")),
Typ::U8 => format!("uint8_t {name}"),
Typ::U16 => format!("uint16_t {name}"),
Typ::U32 => format!("uint32_t {name}"),
@@ -83,7 +96,7 @@ fn comment(out: &mut String, comment: &str, ind: Indentation) {
}
/// Write a C-compatible struct onto `out`
-fn structgen(out: &mut String, def: &StructDef) {
+fn structgen(out: &mut String, filedef: &OutFileDef, def: &StructDef) {
debug!("struct {}", def.name);
comment(out, &def.description, Indentation(0));
@@ -92,7 +105,33 @@ fn structgen(out: &mut String, def: &StructDef) {
trace!(" field {} type={:?}", f.name, f.typ);
comment(out, &f.description, Indentation(1));
- writeln!(out, " {};", structfield(&f.typ, &f.name),).unwrap();
+ writeln!(out, " {};", structfield(filedef, &f.typ, &f.name),).unwrap();
+ }
+ writeln!(out, "}};").unwrap();
+ writeln!(out).unwrap();
+}
+
+/// Write a C-compatible enum onto `out`
+///
+/// This is a generator for the enum _type_, not an instantiation of a bitmap
+/// in a struct field. Use [`structfield`] for that.
+fn enumgen(out: &mut String, def: &EnumDef) {
+ debug!("enum {}", def.name);
+
+ comment(out, &def.description, Indentation(0));
+ writeln!(out, "enum {} {{", def.name).unwrap();
+ for f in &def.variants {
+ trace!(" variant {}={}", f.name, f.value);
+
+ comment(out, &f.description, Indentation(1));
+ writeln!(
+ out,
+ " {}_{} = {},",
+ def.name.from_case(Case::Snake).to_case(Case::UpperSnake),
+ f.name.from_case(Case::Snake).to_case(Case::UpperSnake),
+ f.value
+ )
+ .unwrap();
}
writeln!(out, "}};").unwrap();
writeln!(out).unwrap();
@@ -117,8 +156,12 @@ pub fn parse(filedef: &OutFileDef) -> String {
writeln!(out, "#ifndef __XEN_AUTOGEN_{name}_H").unwrap();
writeln!(out, "#define __XEN_AUTOGEN_{name}_H\n").unwrap();
+ for def in &filedef.enums {
+ enumgen(&mut out, def);
+ }
+
for def in &filedef.structs {
- structgen(&mut out, def);
+ structgen(&mut out, filedef, def);
}
writeln!(out, "#endif /* __XEN_AUTOGEN_{name}_H */\n").unwrap();
@@ -28,6 +28,7 @@ use log::{debug, info};
#[derive(Debug, serde::Deserialize, PartialEq)]
#[serde(rename_all = "lowercase", tag = "tag", content = "args")]
pub enum Typ {
+ Enum(String),
Struct(String),
U8,
U16,
@@ -66,11 +67,47 @@ pub struct FieldDef {
pub typ: Typ,
}
+/// Description of a lang-agnostic enumerated type.
+#[derive(Debug, serde::Deserialize)]
+pub struct EnumDef {
+ /// snake-cased name of this enumeration.
+ ///
+ /// Must be converted to whatever is idiomatic in the target language.
+ pub name: String,
+ /// Description of what the type is for.
+ ///
+ /// Must be turned into documentation in the autogenerated file.
+ pub description: String,
+ /// Width of the type given as an equivalent primitive unsigned integer
+ /// of the same width.
+ pub typ: Typ,
+ /// List of variants present in this enum.
+ ///
+ /// The backend must export all of these under the same namespace if
+ /// possible.
+ pub variants: Vec<VariantDef>,
+}
+
+/// A lang-agnostic description of a single variant of an enumerated type.
+#[derive(Debug, serde::Deserialize)]
+pub struct VariantDef {
+ /// Name of this variant. Depending on the backend, the name might be
+ /// prefixed by the name of its type (as is commonly done in C).
+ pub name: String,
+ /// Meaning of this variant in the context of its type.
+ pub description: String,
+ /// Actual value associated with this variant. Must be explicit to enable
+ /// deprecation of variants.
+ pub value: u64,
+}
+
/// A language-agnostic specification.
#[derive(Debug, serde::Deserialize)]
struct InFileDef {
/// List of structs described in this input specification.
structs: Option<Vec<StructDef>>,
+ /// List of lang-agnostic enumerated descriptions.
+ enums: Option<Vec<EnumDef>>,
}
/// Description of an abstract output (i.e: `.rs`, `.h`, etc).
@@ -82,6 +119,10 @@ pub struct OutFileDef {
pub name: String,
/// List of structs described by all input spec files merged on this file.
pub structs: Vec<StructDef>,
+ /// List of enumerated descriptions.
+ ///
+ /// Implementation is lang-specific.
+ pub enums: Vec<EnumDef>,
}
impl OutFileDef {
@@ -97,6 +138,7 @@ impl OutFileDef {
let mut ret = Self {
name,
structs: Vec::new(),
+ enums: Vec::new(),
};
for entry in from_ioerr(dir.read_dir())? {
@@ -107,6 +149,9 @@ impl OutFileDef {
if let Some(structs) = filedef.structs {
ret.structs.extend(structs);
}
+ if let Some(enums) = filedef.enums {
+ ret.enums.extend(enums);
+ }
}
Ok(ret)
Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com> --- tools/rust/xenbindgen/src/c_lang.rs | 57 +++++++++++++++++++++++++---- tools/rust/xenbindgen/src/spec.rs | 45 +++++++++++++++++++++++ 2 files changed, 95 insertions(+), 7 deletions(-)