Skip to content

Commit

Permalink
First steps of C++ codegen
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed Jul 12, 2023
1 parent 9589002 commit 287853e
Show file tree
Hide file tree
Showing 66 changed files with 557 additions and 9 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/re_types/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const SOURCE_HASH_PATH: &str = "./source_hash.txt";
const DEFINITIONS_DIR_PATH: &str = "./definitions";
const ENTRYPOINT_PATH: &str = "./definitions/rerun/archetypes.fbs";
const DOC_EXAMPLES_DIR_PATH: &str = "../../docs/code-examples";
const CPP_OUTPUT_DIR_PATH: &str = "../../rerun_cpp/src";
const RUST_OUTPUT_DIR_PATH: &str = ".";
const PYTHON_OUTPUT_DIR_PATH: &str = "../../rerun_py/rerun_sdk/rerun/_rerun2";

Expand Down Expand Up @@ -106,6 +107,8 @@ fn main() {
let (objects, arrow_registry) =
re_types_builder::generate_lang_agnostic(DEFINITIONS_DIR_PATH, ENTRYPOINT_PATH);

re_types_builder::generate_cpp_code(CPP_OUTPUT_DIR_PATH, &objects, &arrow_registry);

re_types_builder::generate_rust_code(RUST_OUTPUT_DIR_PATH, &objects, &arrow_registry);

// We need to run `cago fmt` several times because it is not idempotent!
Expand Down
2 changes: 1 addition & 1 deletion crates/re_types/source_hash.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This is a sha256 hash for all direct and indirect dependencies of this crate's build script.
# It can be safely removed at anytime to force the build script to run again.
# Check out build.rs to see how it's computed.
d86ab89fae71ae9a5f145017617c8bdb8f1f65355e5788eff6b2c88ee3a9081b
d3c5e74d40a0d77df3d63e680f229940694849a9ad5136012a63c0bd2944988a
1 change: 1 addition & 0 deletions crates/re_types_builder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ all-features = true
anyhow.workspace = true
arrow2.workspace = true
camino.workspace = true
clang-format = "0.1"
convert_case = "0.6"
flatbuffers = "23.0"
indent = "0.1"
Expand Down
144 changes: 144 additions & 0 deletions crates/re_types_builder/src/codegen/cpp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
use std::collections::BTreeSet;

use anyhow::Context as _;
use camino::Utf8PathBuf;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};

use crate::{codegen::AUTOGEN_WARNING, ArrowRegistry, Object, ObjectKind, Objects};

const NEWLINE_TOKEN: &str = "RE_TOKEN_NEWLINE";

pub struct CppCodeGenerator {
output_path: Utf8PathBuf,
}

impl CppCodeGenerator {
pub fn new(output_path: impl Into<Utf8PathBuf>) -> Self {
Self {
output_path: output_path.into(),
}
}

fn generate_folder(
&mut self,
objects: &Objects,
arrow_registry: &ArrowRegistry,
object_kind: ObjectKind,
folder_name: &str,
) -> BTreeSet<Utf8PathBuf> {
let mut filepaths = BTreeSet::default();

let folder_path = self.output_path.join(folder_name);
std::fs::create_dir_all(&folder_path)
.with_context(|| format!("{folder_path:?}"))
.unwrap();
for obj in objects.ordered_objects(object_kind.into()) {
let filename = obj.snake_case_name();
let (hpp, cpp) = generate_hpp_cpp(objects, arrow_registry, obj);
for (extension, tokens) in [("hpp", hpp), ("cpp", cpp)] {
let string = string_from_token_stream(obj, &tokens);
let filepath = folder_path.join(format!("{filename}.{extension}"));
write_file(&filepath, string);
filepaths.insert(filepath);
}
}

// Clean up old files:
for entry in std::fs::read_dir(folder_path).unwrap().flatten() {
let filepath = Utf8PathBuf::try_from(entry.path()).unwrap();
if !filepaths.contains(&filepath) {
std::fs::remove_file(filepath).ok();
}
}

filepaths
}
}

impl crate::CodeGenerator for CppCodeGenerator {
fn generate(
&mut self,
objects: &Objects,
arrow_registry: &ArrowRegistry,
) -> BTreeSet<Utf8PathBuf> {
let mut filepaths = BTreeSet::new();

for object_kind in ObjectKind::ALL {
let folder_name = object_kind.plural_snake_case();
filepaths.extend(self.generate_folder(
objects,
arrow_registry,
object_kind,
folder_name,
));
}

filepaths
}
}

fn string_from_token_stream(obj: &Object, token_stream: &TokenStream) -> String {
let mut code = String::new();
code.push_str(&format!("// {AUTOGEN_WARNING}\n"));
if let Some(relative_path) = obj.relative_filepath() {
code.push_str(&format!("// Based on {relative_path:?}"));
}

code.push('\n');
code.push_str(
&token_stream
.to_string()
.replace(&format!("{NEWLINE_TOKEN:?}"), "\n"),
);
code.push('\n');

// clang_format has a bit of an ugly API: https://github.com/KDAB/clang-format-rs/issues/3
clang_format::CLANG_FORMAT_STYLE
.set(clang_format::ClangFormatStyle::File)
.ok();
code = clang_format::clang_format(&code).expect("Failed to run clang-format");

code
}

fn write_file(filepath: &Utf8PathBuf, code: String) {
if let Ok(existing) = std::fs::read_to_string(filepath) {
if existing == code {
// Don't touch the timestamp unnecessarily
return;
}
}

std::fs::write(filepath, code)
.with_context(|| format!("{filepath}"))
.unwrap();
}

fn generate_hpp_cpp(
_objects: &Objects,
_arrow_registry: &ArrowRegistry,
obj: &crate::Object,
) -> (TokenStream, TokenStream) {
let obj_kind_ident = format_ident!("{}", obj.kind.plural_snake_case());

let pascal_case_name = &obj.name;
let pascal_case_ident = format_ident!("{pascal_case_name}");
let snake_case_name = obj.snake_case_name();

let hash = quote! { # };
let header_file_name = format!("{snake_case_name}.hpp");

let hpp = quote! {
#hash pragma once #NEWLINE_TOKEN #NEWLINE_TOKEN

namespace rr {
namespace #obj_kind_ident {
struct #pascal_case_ident { };
}
}
};
let cpp = quote! { #hash include #header_file_name };

(hpp, cpp)
}
2 changes: 2 additions & 0 deletions crates/re_types_builder/src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ pub const AUTOGEN_WARNING: &str =
mod common;
use self::common::{get_documentation, StringExt};

mod cpp;
mod python;
mod rust;

pub use self::cpp::CppCodeGenerator;
pub use self::python::PythonCodeGenerator;
pub use self::rust::RustCodeGenerator;
10 changes: 7 additions & 3 deletions crates/re_types_builder/src/codegen/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl CodeGenerator for PythonCodeGenerator {
) -> BTreeSet<Utf8PathBuf> {
let mut filepaths = BTreeSet::new();

let datatypes_path = self.pkg_path.join("datatypes");
let datatypes_path = self.pkg_path.join(ObjectKind::Datatype.plural_snake_case());
let datatype_overrides = load_overrides(&datatypes_path);
std::fs::create_dir_all(&datatypes_path)
.with_context(|| format!("{datatypes_path:?}"))
Expand All @@ -109,7 +109,9 @@ impl CodeGenerator for PythonCodeGenerator {
.0,
);

let components_path = self.pkg_path.join("components");
let components_path = self
.pkg_path
.join(ObjectKind::Component.plural_snake_case());
let component_overrides = load_overrides(&components_path);
std::fs::create_dir_all(&components_path)
.with_context(|| format!("{components_path:?}"))
Expand All @@ -126,7 +128,9 @@ impl CodeGenerator for PythonCodeGenerator {
.0,
);

let archetypes_path = self.pkg_path.join("archetypes");
let archetypes_path = self
.pkg_path
.join(ObjectKind::Archetype.plural_snake_case());
let archetype_overrides = load_overrides(&archetypes_path);
std::fs::create_dir_all(&archetypes_path)
.with_context(|| format!("{archetypes_path:?}"))
Expand Down
29 changes: 28 additions & 1 deletion crates/re_types_builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ mod codegen;
mod objects;

pub use self::arrow_registry::{ArrowRegistry, LazyDatatype, LazyField};
pub use self::codegen::{CodeGenerator, PythonCodeGenerator, RustCodeGenerator};
pub use self::codegen::{CodeGenerator, CppCodeGenerator, PythonCodeGenerator, RustCodeGenerator};
pub use self::objects::{
Attributes, Docs, ElementType, Object, ObjectField, ObjectKind, Objects, Type,
};
Expand Down Expand Up @@ -252,6 +252,33 @@ pub fn generate_lang_agnostic(
(objects, arrow_registry)
}

/// Generates C++ code.
///
/// Panics on error.
///
/// - `output_path`: path to the root of the output.
///
/// E.g.:
/// ```no_run
/// let (object, arrow_registry) = re_types_builder::generate_lang_agnostic(
/// "./definitions",
/// "./definitions/rerun/archetypes.fbs",
/// );
/// re_types_builder::generate_cpp_code(
/// ".",
/// &objects,
/// &arrow_registry,
/// );
/// ```
pub fn generate_cpp_code(
output_path: impl AsRef<Utf8Path>,
objects: &Objects,
arrow_registry: &ArrowRegistry,
) {
let mut gen = CppCodeGenerator::new(output_path.as_ref());
let _filepaths = gen.generate(objects, arrow_registry);
}

/// Generates Rust code.
///
/// Panics on error.
Expand Down
25 changes: 25 additions & 0 deletions crates/re_types_builder/src/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ pub enum ObjectKind {
}

impl ObjectKind {
pub const ALL: [Self; 3] = [Self::Datatype, Self::Component, Self::Archetype];

// TODO(#2364): use an attr instead of the path
pub fn from_pkg_name(pkg_name: impl AsRef<str>) -> Self {
let pkg_name = pkg_name.as_ref().replace(".testing", "");
Expand All @@ -220,6 +222,14 @@ impl ObjectKind {
panic!("unknown package {pkg_name:?}");
}
}

pub fn plural_snake_case(&self) -> &'static str {
match self {
ObjectKind::Datatype => "datatypes",
ObjectKind::Component => "components",
ObjectKind::Archetype => "archetypes",
}
}
}

/// A high-level representation of a flatbuffers object's documentation.
Expand Down Expand Up @@ -596,6 +606,21 @@ impl Object {
.try_get::<String>(&self.fqname, crate::ATTR_TRANSPARENT)
.is_some()
}

/// Try to find the relative file path of the `.fbs` soruce file.

Check warning on line 610 in crates/re_types_builder/src/objects.rs

View workflow job for this annotation

GitHub Actions / Checks / Spell Check

"soruce" should be "source" or "spruce".
pub fn relative_filepath(&self) -> Option<&Utf8Path> {
std::env::var("CARGO_MANIFEST_DIR")
.ok()
.and_then(|manifest_dir| self.filepath.strip_prefix(manifest_dir).ok())
}

/// The snake-case filename of the object, e.g. `point2d`.
pub fn snake_case_name(&self) -> String {
Utf8PathBuf::from(&self.virtpath)
.file_stem()
.unwrap()
.to_owned()
}
}

/// Properties specific to either structs or unions, but not both.
Expand Down
4 changes: 0 additions & 4 deletions rerun_cpp/example/main.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
#include <iostream>

#define RERUN_WITH_ARROW 1

#include <loguru.hpp>
#include <rerun.hpp>

Expand Down
3 changes: 3 additions & 0 deletions rerun_cpp/src/archetypes/fuzzy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// NOTE: This file was autogenerated by re_types_builder; DO NOT EDIT.
// Based on "definitions/rerun/testing/archetypes/fuzzy.fbs"
#include "fuzzy.hpp"
9 changes: 9 additions & 0 deletions rerun_cpp/src/archetypes/fuzzy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// NOTE: This file was autogenerated by re_types_builder; DO NOT EDIT.
// Based on "definitions/rerun/testing/archetypes/fuzzy.fbs"
#pragma once

namespace rr {
namespace archetypes {
struct AffixFuzzer1 {};
} // namespace archetypes
} // namespace rr
3 changes: 3 additions & 0 deletions rerun_cpp/src/archetypes/points2d.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// NOTE: This file was autogenerated by re_types_builder; DO NOT EDIT.
// Based on "definitions/rerun/archetypes/points2d.fbs"
#include "points2d.hpp"
9 changes: 9 additions & 0 deletions rerun_cpp/src/archetypes/points2d.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// NOTE: This file was autogenerated by re_types_builder; DO NOT EDIT.
// Based on "definitions/rerun/archetypes/points2d.fbs"
#pragma once

namespace rr {
namespace archetypes {
struct Points2D {};
} // namespace archetypes
} // namespace rr
3 changes: 3 additions & 0 deletions rerun_cpp/src/archetypes/transform3d.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// NOTE: This file was autogenerated by re_types_builder; DO NOT EDIT.
// Based on "definitions/rerun/archetypes/transform3d.fbs"
#include "transform3d.hpp"
9 changes: 9 additions & 0 deletions rerun_cpp/src/archetypes/transform3d.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// NOTE: This file was autogenerated by re_types_builder; DO NOT EDIT.
// Based on "definitions/rerun/archetypes/transform3d.fbs"
#pragma once

namespace rr {
namespace archetypes {
struct Transform3D {};
} // namespace archetypes
} // namespace rr
3 changes: 3 additions & 0 deletions rerun_cpp/src/components/class_id.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// NOTE: This file was autogenerated by re_types_builder; DO NOT EDIT.
// Based on "definitions/rerun/components/class_id.fbs"
#include "class_id.hpp"
9 changes: 9 additions & 0 deletions rerun_cpp/src/components/class_id.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// NOTE: This file was autogenerated by re_types_builder; DO NOT EDIT.
// Based on "definitions/rerun/components/class_id.fbs"
#pragma once

namespace rr {
namespace components {
struct ClassId {};
} // namespace components
} // namespace rr
Loading

0 comments on commit 287853e

Please sign in to comment.