Skip to content

Commit

Permalink
Exposing diplomat-tool through a library (#348)
Browse files Browse the repository at this point in the history
  • Loading branch information
robertbastian authored Oct 21, 2023
1 parent 5091e17 commit 354307d
Show file tree
Hide file tree
Showing 10 changed files with 257 additions and 238 deletions.
1 change: 1 addition & 0 deletions tool/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct FileMap {
}

impl FileMap {
#[allow(dead_code)]
pub fn new(files: HashMap<String, String>) -> Self {
FileMap {
files: RefCell::new(files),
Expand Down
4 changes: 2 additions & 2 deletions tool/src/cpp/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use colored::*;
use diplomat_core::Env;
use std::fmt::Write;
use std::fs;
use std::path::PathBuf;
use std::path::Path;
use std::{collections::HashMap, fmt};

use diplomat_core::ast;
Expand All @@ -14,7 +14,7 @@ use crate::docs_util::{CppRst, FromMarkdown};
/// Generate RST-formatted Sphinx docs for all FFI types.
pub fn gen_docs(
env: &Env,
library_config_path: &Option<PathBuf>,
library_config_path: Option<&Path>,
outs: &mut HashMap<String, String>,
docs_url_gen: &ast::DocsUrlGenerator,
) -> fmt::Result {
Expand Down
4 changes: 2 additions & 2 deletions tool/src/cpp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
use std::fmt;
use std::fmt::Write;
use std::fs;
use std::path::PathBuf;
use std::path::Path;

use diplomat_core::ast;
use diplomat_core::Env;
Expand Down Expand Up @@ -52,7 +52,7 @@ fn render_header(typ_name: &ast::Ident, headers: &[String]) -> String {

pub fn gen_bindings(
env: &Env,
library_config_path: &Option<PathBuf>,
library_config_path: Option<&Path>,
docs_url_gen: &ast::DocsUrlGenerator,
outs: &mut HashMap<String, String>,
) -> fmt::Result {
Expand Down
4 changes: 2 additions & 2 deletions tool/src/cpp/test_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ macro_rules! test_file {

let mut out_texts = std::collections::HashMap::new();

crate::cpp::gen_bindings(&env, &None, &Default::default(), &mut out_texts).unwrap();
crate::cpp::gen_bindings(&env, None, &Default::default(), &mut out_texts).unwrap();

out_texts.retain(|k, _| !k.ends_with(".h"));
out_texts.remove("diplomat_runtime.hpp");
Expand Down Expand Up @@ -58,7 +58,7 @@ macro_rules! test_file_using_library_config {
use std::path::PathBuf;
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("src/cpp/configs/mfbt.toml");
crate::cpp::gen_bindings(&env, &Some(path), &Default::default(), &mut out_texts).unwrap();
crate::cpp::gen_bindings(&env, Some(&path), &Default::default(), &mut out_texts).unwrap();

out_texts.retain(|k, _| !k.ends_with(".h"));
out_texts.remove("diplomat_runtime.hpp");
Expand Down
1 change: 1 addition & 0 deletions tool/src/cpp2/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ impl<'tcx> Cpp2Formatter<'tcx> {
self.c.fmt_ptr(ident, mutability)
}

#[allow(dead_code)]
pub fn fmt_optional<'a>(&self, ident: &'a str) -> Cow<'a, str> {
format!("std::optional<{ident}>").into()
}
Expand Down
4 changes: 2 additions & 2 deletions tool/src/dotnet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::HashMap;
use std::fmt;
use std::fmt::Write;
use std::fs;
use std::path::PathBuf;
use std::path::Path;

use colored::*;
use diplomat_core::Env;
Expand All @@ -26,7 +26,7 @@ const SCOPE_CLOSING: &str = "}";

pub fn gen_bindings(
env: &Env,
library_config_path: &Option<PathBuf>,
library_config_path: Option<&Path>,
docs_url_gen: &diplomat_core::ast::DocsUrlGenerator,
outs: &mut HashMap<String, String>,
) -> fmt::Result {
Expand Down
1 change: 1 addition & 0 deletions tool/src/js/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ where
}

/// An `fmt::Display` type returned by [`iife`].
#[allow(clippy::upper_case_acronyms)]
pub struct IIFE<F>(F)
where
F: Fn(Indented<Formatter>) -> Result;
Expand Down
214 changes: 213 additions & 1 deletion tool/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,225 @@
// Enable once https://github.com/rust-lang/rust/issues/89554 is stable
// #![deny(non_exhaustive_omitted_patterns)] // diplomat_core uses non_exhaustive a lot; we should never miss its patterns

#[doc(hidden)]
pub mod c;
#[doc(hidden)]
pub mod c2;
#[doc(hidden)]
pub mod common;
#[doc(hidden)]
pub mod cpp;
#[doc(hidden)]
pub mod cpp2;
mod docs_util;
#[doc(hidden)]
pub mod dotnet;
#[doc(hidden)]
pub mod js;

mod docs_util;
mod layout;
mod util;

use colored::*;
use core::panic;
use diplomat_core::{ast, hir};
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::Path;

pub use ast::DocsUrlGenerator;

pub fn gen(
entry: &Path,
target_language: &str,
out_folder: &Path,
docs_out_folder: Option<&Path>,
docs_url_gen: &ast::DocsUrlGenerator,
library_config: Option<&Path>,
silent: bool,
) -> std::io::Result<()> {
// Check that user-provided paths exist. Exit early with a nice error message
// if anything doesn't exist.
exit_if_path_missing(
entry,
if entry.file_name().map(|e| e == "lib.rs").unwrap_or_default() {
"Could not find the lib.rs file to process."
} else {
"The entry file does not exist."
},
);
exit_if_path_missing(out_folder, "The out folder does not exist.");
if let Some(docs_out_folder) = docs_out_folder {
exit_if_path_missing(docs_out_folder, "The docs folder does not exist.");
}
if let Some(library_config) = library_config {
exit_if_path_missing(
library_config,
"The library configuration file does not exist.",
);
}

let lib_file = syn_inline_mod::parse_and_inline_modules(entry);
let diplomat_file = ast::File::from(&lib_file);
let env = diplomat_file.all_types();

let errors = diplomat_file.check_validity(&env);
if !errors.is_empty() {
for e in errors {
eprintln!("{e}");
}
panic!();
}

let mut out_texts: HashMap<String, String> = HashMap::new();

let mut errors_found = false;

match target_language {
"js" => js::gen_bindings(&env, &mut out_texts, Some(docs_url_gen)).unwrap(),
"c" => c::gen_bindings(&env, &mut out_texts).unwrap(),
"cpp" => {
c::gen_bindings(&env, &mut out_texts).unwrap();
cpp::gen_bindings(&env, library_config, docs_url_gen, &mut out_texts).unwrap()
}
"dotnet" => {
dotnet::gen_bindings(&env, library_config, docs_url_gen, &mut out_texts).unwrap()
}
"c2" | "cpp-c2" | "cpp2" => {
let mut attr_validator = hir::BasicAttributeValidator::new(target_language);

if target_language == "c2" {
attr_validator.other_backend_names.push("c".into());
} else {
attr_validator.other_backend_names.push("cpp".into());
// C backends cannot rename types using backend attributes
// In the future we may add a c_rename attribute
attr_validator.support.renaming = true;
}

attr_validator.support.disabling = true;
// cpp-c2 is a testing backend, we're not going to treat it as a real c/cpp backend
// since the ast-cpp backend doesn't know about attributes.

let tcx = match hir::TypeContext::from_ast(&env, attr_validator) {
Ok(context) => context,
Err(e) => {
for err in e {
eprintln!("Lowering error: {err}");
}
std::process::exit(1);
}
};
let files = common::FileMap::default();
let mut context = c2::CContext::new(&tcx, files);
context.run();

let errors = context.errors.take_all();

if !errors.is_empty() {
eprintln!("Found errors whilst generating {target_language}:");
for error in errors {
eprintln!("\t{}: {}", error.0, error.1);
}
errors_found = true;
}

out_texts = context.files.take_files();

if target_language == "cpp-c2" {
cpp::gen_bindings(&env, library_config, docs_url_gen, &mut out_texts).unwrap()
}
if target_language == "cpp2" {
let files = common::FileMap::default();
let mut context = cpp2::Cpp2Context::new(&tcx, files);
context.run();
out_texts.extend(context.files.take_files());

let errors = context.errors.take_all();

if !errors.is_empty() {
eprintln!("Found errors whilst generating {target_language}:");
for error in errors {
eprintln!("\t{}: {}", error.0, error.1);
}
errors_found = true;
}
}
}
o => panic!("Unknown target: {}", o),
}

if errors_found {
eprintln!("Not generating files due to errors");
// Eventually this should use eyre or something
std::process::exit(1);
}

if !silent {
println!(
"{}",
format!("Generating {} bindings:", target_language)
.green()
.bold()
);
}

for (subpath, text) in out_texts {
let out_path = out_folder.join(subpath);
let mut out_file = File::create(&out_path)?;
out_file.write_all(text.as_bytes())?;
if !silent {
println!("{}", format!(" {}", out_path.display()).dimmed());
}
}

if let Some(docs_out_folder) = docs_out_folder {
if !silent {
println!(
"{}",
format!("Generating {} docs:", target_language)
.green()
.bold()
);
}

let mut docs_out_texts: HashMap<String, String> = HashMap::new();

match target_language {
"js" => js::docs::gen_docs(&env, &mut docs_out_texts, docs_url_gen).unwrap(),
"cpp" | "cpp-c2" => {
cpp::docs::gen_docs(&env, library_config, &mut docs_out_texts, docs_url_gen)
.unwrap()
}
"c" => todo!("Docs generation for C"),
"dotnet" => todo!("Docs generation for .NET?"),
o => panic!("Unknown target: {}", o),
}

for (subpath, text) in docs_out_texts {
let out_path = docs_out_folder.join(subpath);
let mut out_file = File::create(&out_path)?;
out_file.write_all(text.as_bytes())?;
if !silent {
println!("{}", format!(" {}", out_path.display()).dimmed());
}
}
}

Ok(())
}

/// Provide nice error messages if a folder doesn't exist.
fn exit_if_path_missing(path: &Path, message: &str) {
if !path.exists() {
let current_dir = std::env::current_dir().expect("Filed to load current directory.");
eprintln!(
"{}{}\n{}",
"Error: ".red().bold(),
message,
format!("{}", Path::new(&current_dir).join(path).display()).red()
);
std::process::exit(1);
}
}
Loading

0 comments on commit 354307d

Please sign in to comment.