From 595d3df5ee3d0b7fee2a3145c4635d70b283249a Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Sat, 22 Aug 2026 23:46:51 +0200 Subject: [PATCH 1/5] Change compiler so it can output multiple files. Also add some html docs scaffolding --- compiler/dd-cli/src/main.rs | 75 ++++++++---- compiler/dd-codegen/Cargo.toml | 4 + compiler/dd-codegen/src/docs.rs | 17 +++ compiler/dd-codegen/src/lib.rs | 23 +++- compiler/dd-codegen/src/rust.rs | 21 ++++ compiler/dd-core/Cargo.toml | 5 - compiler/dd-core/src/lib.rs | 173 ++++++++-------------------- compiler/dd-macros/src/lib.rs | 18 +-- compiler/dd-wasm/src/lib.rs | 44 ++++++- tests/ui/src/lib.rs | 12 +- tests/ui/src/main.rs | 12 +- website/pages/playground/index.html | 1 + website/pages/playground/index.ts | 3 +- 13 files changed, 236 insertions(+), 172 deletions(-) create mode 100644 compiler/dd-codegen/src/docs.rs diff --git a/compiler/dd-cli/src/main.rs b/compiler/dd-cli/src/main.rs index 4b25bf3d..a88d2599 100644 --- a/compiler/dd-cli/src/main.rs +++ b/compiler/dd-cli/src/main.rs @@ -27,9 +27,16 @@ struct BuildArgs { /// Path to the input file. #[arg(short = 's', long = "source", value_name = "FILE", global = true)] source_path: Option, - /// Path to output location. Any existing file is overwritten. If not provided, the output is written to stdout. + /// Path to output location. Any existing file is overwritten. + /// If no output file or target folder is provided and only one file is generated, the output is written to stdout. + /// + /// Can only be used with targets that generate a single file. #[arg(short = 'o', long = "output", value_name = "FILE", global = true)] output_path: Option, + /// Path to target folder location. Any existing file in the folder is overwritten. + /// If no output file or target folder is provided and only one file is generated, the output is written to stdout. + #[arg(short = 't', long = "target", value_name = "FOLDER", global = true)] + target_folder: Option, #[command(flatten)] options: CompileOptions, } @@ -83,11 +90,16 @@ fn build(args: BuildArgs) -> Result { let Some(source_path) = args.source_path else { return Err(DynError::new("no source path provided")); }; + if args.output_path.is_some() && args.target_folder.is_some() { + return Err(DynError::new( + "both single file output path and target folder are specified. Both cannot be used at the same time. Pick one", + )); + } let source = std::fs::read_to_string(&source_path) .with_message(|| format!("Failed to open input file at: {:?}", source_path.display()))?; - let (output, diagnostics) = device_driver_core::compile(&source, args.options) + let (output_files, diagnostics) = device_driver_core::compile(&source, args.options) .with_message(|| "internal compilation error")?; let diagnostics_has_error = diagnostics.has_error(); @@ -110,26 +122,40 @@ fn build(args: BuildArgs) -> Result { return Ok(ExitCode::FAILURE); } - let output_writer: &mut dyn Write = match &args.output_path { - Some(path) => &mut std::fs::File::create(path).with_message(|| { - format!( - "could not create the output file at: {:?}. Does its directory exist?", - path.display() - ) - })?, - None => &mut std::io::stdout().lock(), - }; + if let Some(output_folder) = args.target_folder.as_ref() { + std::fs::create_dir_all(output_folder).with_message(|| "could not create target folder")?; - let mut output_writer = std::io::BufWriter::new(output_writer); - output_writer - .write_all(output.as_bytes()) - .with_message(|| { - format!( - "could not write output to {}", - args.output_path - .map_or_else(|| "stdout".into(), |path| format!("{:?}", path.display())) - ) - })?; + for file in output_files { + let file_path = output_folder.join(file.name); + std::fs::write(&file_path, file.contents) + .with_message(|| format!("could not write output to {}", file_path.display()))?; + } + } else if let [output_file] = output_files.as_slice() { + let output_writer: &mut dyn Write = match &args.output_path { + Some(path) => &mut std::fs::File::create(path).with_message(|| { + format!( + "could not create the output file at: {:?}. Does its directory exist?", + path.display() + ) + })?, + None => &mut std::io::stdout().lock(), + }; + + let mut output_writer = std::io::BufWriter::new(output_writer); + output_writer + .write_all(output_file.contents.as_bytes()) + .with_message(|| { + format!( + "could not write output to {}", + args.output_path + .map_or_else(|| "stdout".into(), |path| format!("{:?}", path.display())) + ) + })?; + } else { + return Err(DynError::new( + "more than one file is generated, but no target folder is given", + )); + } Ok(ExitCode::SUCCESS) } @@ -174,6 +200,13 @@ fn gen_docs(args: GenDocsArgs) -> Result { .to_string(), ) .with_message(|| "writing rust-help")?; + std::fs::write( + cli_folder.join("docs-help.txt"), + device_driver_core::DocsCodegenOptions::command() + .render_long_help() + .to_string(), + ) + .with_message(|| "writing docs-help")?; device_driver_core::gen_docs(&args.output_path).map(|()| ExitCode::SUCCESS) } diff --git a/compiler/dd-codegen/Cargo.toml b/compiler/dd-codegen/Cargo.toml index b018e3b5..8bf61506 100644 --- a/compiler/dd-codegen/Cargo.toml +++ b/compiler/dd-codegen/Cargo.toml @@ -15,9 +15,13 @@ readme = "README.md" [dependencies] device-driver-common.workspace = true device-driver-lir.workspace = true +device-driver-diagnostics.workspace = true convert_case.workspace = true clap.workspace = true itertools.workspace = true +syn.workspace = true + +prettyplease = { version = "0.3.0" } askama = { version = "0.16.0", default-features = false, features = ["derive", "alloc"] } diff --git a/compiler/dd-codegen/src/docs.rs b/compiler/dd-codegen/src/docs.rs new file mode 100644 index 00000000..b1e180f0 --- /dev/null +++ b/compiler/dd-codegen/src/docs.rs @@ -0,0 +1,17 @@ +use clap::Parser; +use device_driver_diagnostics::DynError; +use device_driver_lir::model::Driver; + +use crate::File; + +#[derive(Parser, Debug, Clone, Default)] +#[command(no_binary_name = true, bin_name = "")] +pub struct DocsCodegenOptions {} + +pub fn codegen( + codegen_options: &DocsCodegenOptions, + lir_driver: &Driver, + source: &str, +) -> Result, DynError> { + todo!() +} diff --git a/compiler/dd-codegen/src/lib.rs b/compiler/dd-codegen/src/lib.rs index 45fce2fc..ad3fad60 100644 --- a/compiler/dd-codegen/src/lib.rs +++ b/compiler/dd-codegen/src/lib.rs @@ -1,15 +1,20 @@ use clap::Subcommand; +use device_driver_diagnostics::DynError; use device_driver_lir::model::Driver; use itertools::Itertools; +pub use crate::docs::DocsCodegenOptions; pub use crate::rust::RustCodegenOptions; +mod docs; mod rust; #[derive(Debug, Clone, Subcommand)] pub enum Target { /// Generate Rust code Rust(RustCodegenOptions), + /// Generate a documentation website + Docs(DocsCodegenOptions), } impl Target { @@ -18,6 +23,9 @@ impl Target { Target::Rust(_) => { "compile_error!(\"The device driver input has errors that need to be solved!\");" } + Target::Docs(_) => { + "