Skip to content

Commit

Permalink
generate structured rust module tree
Browse files Browse the repository at this point in the history
  • Loading branch information
timbod7 committed Mar 15, 2023
1 parent 476e831 commit 6064473
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 10 deletions.
19 changes: 18 additions & 1 deletion rust/compiler/src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use clap::{Args, Parser, Subcommand};
use clap::{Args, Parser};
use std::path::PathBuf;

pub mod ast;
Expand Down Expand Up @@ -74,6 +74,19 @@ pub struct RustOpts {
#[clap(flatten)]
pub output: OutputOpts,

/// Generate the runtime code
#[arg(long)]
pub include_runtime: bool,

/// The module where the code is generated, relative to crate root
#[arg(long, value_name="RSMODULE", default_value_t={"adl".to_string()})]
pub module: String,

/// The module where the runtime is located, relative to crate root
#[arg(long, value_name="RSMODULE", default_value_t={"adlrt".to_string()})]
pub runtime_module: String,

#[arg(value_name="ADLMODULE")]
pub modules: Vec<String>,
}

Expand All @@ -90,5 +103,9 @@ pub struct OutputOpts {
/// writes generated code to the specified directory
#[arg(long, short='O', value_name="DIR")]
pub outdir: PathBuf,

/// write a manifest file recording generated files
#[arg(long, value_name="FILE")]
pub manifest: Option<PathBuf>,
}

49 changes: 44 additions & 5 deletions rust/compiler/src/cli/rust.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::RustOpts;
use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use std::path::PathBuf;

Expand All @@ -25,27 +26,65 @@ pub fn rust(opts: &RustOpts) -> anyhow::Result<()> {
.map(|mn| resolver.get_module(&mn).unwrap())
.collect();

let writer = TreeWriter::new(opts.output.outdir.clone());
let mut writer = TreeWriter::new(
opts.output.outdir.clone(),
opts.output.manifest.clone(),
)?;

for m in modules {
let path = path_from_module_name(m.name.to_owned());
let code = generate_code(m).unwrap();
let path = path_from_module_name(opts, m.name.to_owned());
let code = gen_module(m).unwrap();
writer.write(path.as_path(), code)?;
}

gen_rs_mod_files(opts, &resolver, &mut writer)?;

Ok(())
}

fn path_from_module_name(mname: adlast::ModuleName) -> PathBuf {
fn path_from_module_name(opts: &RustOpts, mname: adlast::ModuleName) -> PathBuf {
let mut path = PathBuf::new();
path.push(opts.module.clone());
for el in mname.split(".") {
path.push(el);
}
path.set_extension("rs");
return path;
}

fn generate_code(m: &Module1) -> anyhow::Result<String> {
/// Generate the tree of mod.rs files that link together the generated code
fn gen_rs_mod_files(opts: &RustOpts, resolver: &Resolver, writer: &mut TreeWriter) -> anyhow::Result<()> {

// build a map of parent rust modules and their children
let mut modfiles: HashMap<Vec<String>,HashSet<String>> = HashMap::new();
for m in resolver.get_module_names() {
let msplit: Vec<&str> = m.split(".").collect();
for i in 0..msplit.len() {
let rsmod = msplit.get(i).unwrap();
let parent = &msplit[0..i];
let parent: Vec<String> = parent.iter().map(|m| m.to_string()).collect();
let e = modfiles.entry(parent).or_default();
e.insert(rsmod.to_string());
}
}

for (rsmod,children) in modfiles {
let mut path = PathBuf::new();
path.push(opts.module.clone());
for el in rsmod {
path.push(el);
}
path.push("mod.rs");
let lines: Vec<String> = children
.iter()
.map( |m| format!("pub mod {};", m))
.collect();
writer.write(&path, lines.join("\n"))?
};
Ok(())
}

fn gen_module(m: &Module1) -> anyhow::Result<String> {
let mut out = String::new();

for d in m.decls.values() {
Expand Down
83 changes: 79 additions & 4 deletions rust/compiler/src/processing/writer.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,97 @@
use std::path::{Path, PathBuf};
use std::{path::{Path, PathBuf}, collections::HashSet};
use std::{fs,io};
use std::io::{Write,BufRead};

pub struct TreeWriter {
pub root_dir: PathBuf,
pub manifest_file: Option<PathBuf>,
pub manifest: Manifest,
}

impl TreeWriter {
pub fn new(root_dir: PathBuf) -> Self {
TreeWriter { root_dir }
pub fn new(root_dir: PathBuf, manifest_file: Option<PathBuf>) -> io::Result<Self> {

let manifest = HashSet::new();
Ok(TreeWriter { root_dir, manifest_file, manifest})
}

/// Write a file to the specifed path, creating parent directories as required
pub fn write(&self, path: &Path, content: String) -> Result<(), std::io::Error> {
pub fn write(&mut self, path: &Path, content: String) -> io::Result<()> {
let mut file_path = self.root_dir.clone();
file_path.push(path);
log::info!("writing {}", file_path.display());
let dir_path = file_path.parent().unwrap();
std::fs::create_dir_all(dir_path)?;
std::fs::write(file_path, &content)?;

self.manifest.insert(path.to_owned());
Ok(())
}

pub fn close(&self) -> io::Result<()> {

if let Some(to_file) = &self.manifest_file {

// Read old manifest
let old_manifest =
if to_file.exists() {
TreeWriter::read_manifest(to_file)?
} else {
HashSet::new()
};

// Write the new one
log::info!("writing manifest {}", to_file.display());
TreeWriter::write_manifest(to_file, &self.manifest)?;

// Remove any files left from the old manifest
for f in old_manifest.difference(&self.manifest) {
let mut file_path = self.root_dir.clone();
file_path.push(f);
log::info!("removing {}", file_path.display());
}
}
Ok(())
}

fn read_manifest(from_file: &Path) -> io::Result<Manifest> {
let file = fs::File::open(from_file)?;
let lines = io::BufReader::new(file).lines();
let mut manifest : Manifest = HashSet::new();
for line in lines {
let s = line?;
let s = s.trim();
if ! s.starts_with("#") {
manifest.insert(PathBuf::from(s));
}
}
Ok(manifest)
}

fn write_manifest(to_file: &Path, manifest: &Manifest) -> io::Result<()> {
let dir_path = to_file.parent().unwrap();
fs::create_dir_all(dir_path)?;
let mut file = fs::OpenOptions::new()
.write(true)
.create(true)
.open(to_file)?;
file.write(b"# manifest @generated by the adl compiler\n")?;
let mut manifest: Vec<&PathBuf> = manifest.into_iter().collect();
manifest.sort();
for m in manifest {
file.write(m.to_str().unwrap().as_bytes())?;
file.write(b"\n")?;
}
Ok(())
}
}

impl Drop for TreeWriter {
fn drop(&mut self) {
self.close().unwrap();
}
}


type Manifest = HashSet<PathBuf>;

5 changes: 5 additions & 0 deletions rust/compiler/tests/test1/rs-output/.adl-manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# manifest @generated by the adl compiler
adl/mod.rs
adl/sys/annotations.rs
adl/sys/mod.rs
adl/test1.rs
2 changes: 2 additions & 0 deletions rust/compiler/tests/test1/rs-output/adl/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod test1;
pub mod sys;
4 changes: 4 additions & 0 deletions rust/compiler/tests/test1/rs-output/adl/sys/annotations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
struct SerializedWithInternalTag {
tag: TYPE;
}

1 change: 1 addition & 0 deletions rust/compiler/tests/test1/rs-output/adl/sys/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod annotations;
Empty file.

0 comments on commit 6064473

Please sign in to comment.