WIP converting from cli.rs to build/*.rs
This commit is contained in:
parent
ecc5ffd9eb
commit
90aed1615e
7 changed files with 313 additions and 27 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -309,7 +309,6 @@ dependencies = [
|
|||
"jobslot",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"os_pipe",
|
||||
"petgraph",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -514,16 +513,6 @@ version = "1.19.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.8.1"
|
||||
|
|
|
@ -29,7 +29,6 @@ indexmap = { version = "2.5.0", features = ["serde"] }
|
|||
jobslot = "0.2.19"
|
||||
num-bigint = "0.4.6"
|
||||
num-traits = "0.2.16"
|
||||
os_pipe = "1.2.1"
|
||||
petgraph = "0.8.1"
|
||||
prettyplease = "0.2.20"
|
||||
proc-macro2 = "1.0.83"
|
||||
|
|
|
@ -25,7 +25,6 @@ hashbrown.workspace = true
|
|||
jobslot.workspace = true
|
||||
num-bigint.workspace = true
|
||||
num-traits.workspace = true
|
||||
os_pipe.workspace = true
|
||||
petgraph.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
|
|
|
@ -7,7 +7,6 @@ use crate::{
|
|||
module::Module,
|
||||
util::{HashMap, HashSet, job_server::AcquiredJob},
|
||||
};
|
||||
use clap::{FromArgMatches, Subcommand};
|
||||
use hashbrown::hash_map::Entry;
|
||||
use petgraph::{
|
||||
algo::{DfsSpace, kosaraju_scc, toposort},
|
||||
|
@ -29,8 +28,10 @@ use std::{
|
|||
sync::{Arc, OnceLock, RwLock, RwLockWriteGuard, mpsc},
|
||||
thread::{self, ScopedJoinHandle},
|
||||
};
|
||||
use tempfile::TempDir;
|
||||
|
||||
pub mod external;
|
||||
pub mod firrtl;
|
||||
|
||||
macro_rules! write_str {
|
||||
($s:expr, $($rest:tt)*) => {
|
||||
|
@ -92,8 +93,6 @@ impl Ord for JobItemName {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct CommandLine {}
|
||||
|
||||
pub trait JobKind: 'static + Send + Sync + Hash + Eq + fmt::Debug {
|
||||
type Job: 'static + Send + Sync + Hash + Eq + fmt::Debug;
|
||||
fn inputs_and_direct_dependencies<'a>(
|
||||
|
@ -779,6 +778,7 @@ impl JobKind for DynJobKind {
|
|||
enum JobGraphNode {
|
||||
Job(DynJob),
|
||||
Item {
|
||||
#[allow(dead_code, reason = "name used for debugging")]
|
||||
name: JobItemName,
|
||||
source_job: Option<DynJob>,
|
||||
},
|
||||
|
@ -1551,7 +1551,7 @@ impl<Job: InternalJobTrait> JobKind for InternalJobKind<Job> {
|
|||
}
|
||||
|
||||
fn from_arg_matches(&self, matches: &mut clap::ArgMatches) -> clap::error::Result<Self::Job> {
|
||||
InternalJob::<Job>::from_arg_matches_mut(matches)
|
||||
clap::FromArgMatches::from_arg_matches_mut(matches)
|
||||
}
|
||||
|
||||
fn to_command_line(&self, job: &Self::Job) -> Interned<[Interned<str>]> {
|
||||
|
@ -1568,11 +1568,11 @@ impl<Job: InternalJobTrait> JobKind for InternalJobKind<Job> {
|
|||
command_line: Interned<[Interned<str>]>,
|
||||
) -> clap::error::Result<Self::Job> {
|
||||
let cmd = clap::Command::new(Interned::into_inner(program_name_for_internal_jobs()));
|
||||
let mut matches = InternalJob::<Job>::augment_subcommands(cmd)
|
||||
let mut matches = <InternalJob<Job> as clap::Subcommand>::augment_subcommands(cmd)
|
||||
.subcommand_required(true)
|
||||
.arg_required_else_help(true)
|
||||
.try_get_matches_from(command_line.iter().map(|arg| &**arg))?;
|
||||
InternalJob::<Job>::from_arg_matches_mut(&mut matches)
|
||||
<InternalJob<Job> as clap::FromArgMatches>::from_arg_matches_mut(&mut matches)
|
||||
}
|
||||
|
||||
fn run(
|
||||
|
@ -1657,3 +1657,202 @@ impl<Job: InternalJobTrait> clap::Subcommand for InternalJob<Job> {
|
|||
*name == *Job::subcommand_name()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::Args)]
|
||||
#[clap(id = "OutputDir")]
|
||||
struct OutputDirArgs {
|
||||
/// the directory to put the generated main output file and associated files in
|
||||
#[arg(short, long, value_hint = clap::ValueHint::DirPath, required = true)]
|
||||
output: Option<String>,
|
||||
#[arg(long, env = "FAYALITE_KEEP_TEMP_DIR")]
|
||||
keep_temp_dir: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OutputDir {
|
||||
output: String,
|
||||
temp_dir: Option<Arc<TempDir>>,
|
||||
keep_temp_dir: bool,
|
||||
}
|
||||
|
||||
impl Eq for OutputDir {}
|
||||
|
||||
impl AsRef<str> for OutputDir {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.path()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<std::path::Path> for OutputDir {
|
||||
fn as_ref(&self) -> &std::path::Path {
|
||||
self.path().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputDir {
|
||||
pub fn path(&self) -> &str {
|
||||
&self.output
|
||||
}
|
||||
pub fn new(output: String) -> Self {
|
||||
Self {
|
||||
output,
|
||||
temp_dir: None,
|
||||
keep_temp_dir: false,
|
||||
}
|
||||
}
|
||||
pub fn with_keep_temp_dir(output: String, keep_temp_dir: bool) -> Self {
|
||||
Self {
|
||||
output,
|
||||
temp_dir: None,
|
||||
keep_temp_dir,
|
||||
}
|
||||
}
|
||||
pub fn temp(keep_temp_dir: bool) -> std::io::Result<Self> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
let output = String::from(temp_dir.path().as_os_str().to_str().ok_or_else(|| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidFilename,
|
||||
format!(
|
||||
"temporary directory path is not valid UTF-8: {:?}",
|
||||
temp_dir.path()
|
||||
),
|
||||
)
|
||||
})?);
|
||||
let temp_dir = if keep_temp_dir {
|
||||
println!(
|
||||
"created temporary directory: {}",
|
||||
temp_dir.into_path().display()
|
||||
);
|
||||
None
|
||||
} else {
|
||||
Some(Arc::new(temp_dir))
|
||||
};
|
||||
Ok(Self {
|
||||
output,
|
||||
temp_dir,
|
||||
keep_temp_dir,
|
||||
})
|
||||
}
|
||||
pub fn to_args(&self) -> Vec<Interned<str>> {
|
||||
let Self {
|
||||
output,
|
||||
temp_dir: _,
|
||||
keep_temp_dir,
|
||||
} = self;
|
||||
let mut retval = Vec::new();
|
||||
retval.push(str::intern_owned(format!("--output={output}")));
|
||||
if *keep_temp_dir {
|
||||
retval.push("--keep-temp-dir".intern());
|
||||
}
|
||||
retval
|
||||
}
|
||||
fn compare_key(&self) -> (&str, bool, bool) {
|
||||
let Self {
|
||||
output,
|
||||
temp_dir,
|
||||
keep_temp_dir,
|
||||
} = self;
|
||||
(output, temp_dir.is_some(), *keep_temp_dir)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for OutputDir {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.compare_key() == other.compare_key()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for OutputDir {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.compare_key().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<OutputDirArgs> for OutputDir {
|
||||
type Error = clap::Error;
|
||||
|
||||
fn try_from(value: OutputDirArgs) -> Result<Self, Self::Error> {
|
||||
let OutputDirArgs {
|
||||
output,
|
||||
keep_temp_dir,
|
||||
} = value;
|
||||
match output {
|
||||
Some(output) => Ok(Self::with_keep_temp_dir(output, keep_temp_dir)),
|
||||
None => Ok(Self::temp(keep_temp_dir)?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl clap::FromArgMatches for OutputDir {
|
||||
fn from_arg_matches(matches: &clap::ArgMatches) -> clap::error::Result<Self> {
|
||||
OutputDirArgs::from_arg_matches(matches)?.try_into()
|
||||
}
|
||||
|
||||
fn from_arg_matches_mut(matches: &mut clap::ArgMatches) -> clap::error::Result<Self> {
|
||||
OutputDirArgs::from_arg_matches_mut(matches)?.try_into()
|
||||
}
|
||||
|
||||
fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> clap::error::Result<()> {
|
||||
*self = Self::from_arg_matches(matches)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_from_arg_matches_mut(
|
||||
&mut self,
|
||||
matches: &mut clap::ArgMatches,
|
||||
) -> clap::error::Result<()> {
|
||||
*self = Self::from_arg_matches_mut(matches)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl clap::Args for OutputDir {
|
||||
fn group_id() -> Option<clap::Id> {
|
||||
OutputDirArgs::group_id()
|
||||
}
|
||||
|
||||
fn augment_args(cmd: clap::Command) -> clap::Command {
|
||||
OutputDirArgs::augment_args(cmd)
|
||||
}
|
||||
|
||||
fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
|
||||
OutputDirArgs::augment_args_for_update(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::Parser, Debug, Clone, Hash, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub struct BaseArgs {
|
||||
/// the directory to put the generated main output file and associated files in
|
||||
#[command(flatten)]
|
||||
pub output: OutputDir,
|
||||
/// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo
|
||||
#[arg(long)]
|
||||
pub file_stem: Option<String>,
|
||||
pub module_name: String,
|
||||
}
|
||||
|
||||
impl BaseArgs {
|
||||
pub fn to_args(&self) -> Vec<Interned<str>> {
|
||||
let Self {
|
||||
output,
|
||||
file_stem,
|
||||
module_name,
|
||||
} = self;
|
||||
let mut retval = output.to_args();
|
||||
if let Some(file_stem) = file_stem {
|
||||
retval.push(str::intern_owned(format!("--file-stem={file_stem}")));
|
||||
}
|
||||
retval.push(str::intern(module_name));
|
||||
retval
|
||||
}
|
||||
pub fn file_with_ext(&self, ext: &str) -> String {
|
||||
let mut retval = std::path::Path::new(self.output.path())
|
||||
.join(self.file_stem.as_ref().unwrap_or(&self.module_name));
|
||||
retval.set_extension(ext);
|
||||
retval
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.expect("known to be UTF-8")
|
||||
}
|
||||
}
|
||||
|
|
82
crates/fayalite/src/build/firrtl.rs
Normal file
82
crates/fayalite/src/build/firrtl.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
build::{BaseArgs, DynJob, InternalJobTrait, JobItem, JobItemName},
|
||||
firrtl::{ExportOptions, FileBackend},
|
||||
intern::{Intern, Interned},
|
||||
util::job_server::AcquiredJob,
|
||||
};
|
||||
use clap::Parser;
|
||||
use std::{borrow::Cow, collections::BTreeMap};
|
||||
|
||||
#[derive(Parser, Debug, Clone, Hash, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub struct FirrtlArgs {
|
||||
#[command(flatten)]
|
||||
pub base: BaseArgs,
|
||||
#[command(flatten)]
|
||||
pub export_options: ExportOptions,
|
||||
}
|
||||
|
||||
impl FirrtlArgs {
|
||||
fn make_firrtl_file_backend(&self) -> FileBackend {
|
||||
FileBackend {
|
||||
dir_path: self.base.output.path().into(),
|
||||
top_fir_file_stem: self.base.file_stem.clone(),
|
||||
circuit_name: None,
|
||||
}
|
||||
}
|
||||
pub fn firrtl_file(&self) -> String {
|
||||
self.base.file_with_ext("fir")
|
||||
}
|
||||
}
|
||||
|
||||
impl InternalJobTrait for FirrtlArgs {
|
||||
fn subcommand_name() -> Interned<str> {
|
||||
"firrtl".intern()
|
||||
}
|
||||
|
||||
fn to_args(&self) -> Vec<Interned<str>> {
|
||||
let Self {
|
||||
base,
|
||||
export_options,
|
||||
} = self;
|
||||
let mut retval = base.to_args();
|
||||
retval.extend(export_options.to_args());
|
||||
retval
|
||||
}
|
||||
|
||||
fn inputs_and_direct_dependencies<'a>(
|
||||
&'a self,
|
||||
) -> Cow<'a, BTreeMap<JobItemName, Option<DynJob>>> {
|
||||
Cow::Owned(BTreeMap::from_iter([(
|
||||
JobItemName::Module {
|
||||
name: str::intern(&self.base.module_name),
|
||||
},
|
||||
None,
|
||||
)]))
|
||||
}
|
||||
|
||||
fn outputs(&self) -> Interned<[JobItemName]> {
|
||||
[JobItemName::File {
|
||||
path: str::intern_owned(self.firrtl_file()),
|
||||
}][..]
|
||||
.intern()
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
inputs: &[JobItem],
|
||||
_acquired_job: &mut AcquiredJob,
|
||||
) -> eyre::Result<Vec<JobItem>> {
|
||||
let [JobItem::Module { value: module }] = inputs else {
|
||||
panic!("wrong inputs, expected a single `Module`");
|
||||
};
|
||||
assert_eq!(*module.name(), *self.base.module_name);
|
||||
crate::firrtl::export(self.make_firrtl_file_backend(), module, self.export_options)?;
|
||||
Ok(vec![JobItem::File {
|
||||
path: str::intern_owned(self.firrtl_file()),
|
||||
}])
|
||||
}
|
||||
}
|
|
@ -102,7 +102,7 @@ impl BaseArgs {
|
|||
mut captured_output: Option<&mut String>,
|
||||
) -> io::Result<process::ExitStatus> {
|
||||
if self.redirect_output_for_rust_test || captured_output.is_some() {
|
||||
let (reader, writer) = os_pipe::pipe()?;
|
||||
let (reader, writer) = io::pipe()?;
|
||||
let mut reader = io::BufReader::new(reader);
|
||||
command.stderr(writer.try_clone()?);
|
||||
command.stdout(writer); // must not leave writer around after spawning child
|
||||
|
|
|
@ -1206,9 +1206,7 @@ impl<'a> Exporter<'a> {
|
|||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::Reset(_) => Ok(format!("asUInt({value_str})")),
|
||||
CanonicalType::PhantomConst(_) => Ok("UInt<0>(0)".into()),
|
||||
CanonicalType::DynSimOnly(_) => {
|
||||
Err(FirrtlError::SimOnlyValuesAreNotPermitted.into())
|
||||
}
|
||||
CanonicalType::DynSimOnly(_) => Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()),
|
||||
}
|
||||
}
|
||||
fn expr_cast_bits_to_bundle(
|
||||
|
@ -1429,9 +1427,7 @@ impl<'a> Exporter<'a> {
|
|||
definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}"));
|
||||
return Ok(retval.to_string());
|
||||
}
|
||||
CanonicalType::DynSimOnly(_) => {
|
||||
Err(FirrtlError::SimOnlyValuesAreNotPermitted.into())
|
||||
}
|
||||
CanonicalType::DynSimOnly(_) => Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()),
|
||||
}
|
||||
}
|
||||
fn expr_unary<T: Type>(
|
||||
|
@ -2758,7 +2754,7 @@ pub struct ExportOptions {
|
|||
#[clap(long = "no-simplify-memories", action = clap::ArgAction::SetFalse)]
|
||||
pub simplify_memories: bool,
|
||||
#[clap(long, value_parser = OptionSimplifyEnumsKindValueParser, default_value = "replace-with-bundle-of-uints")]
|
||||
pub simplify_enums: std::option::Option<SimplifyEnumsKind>,
|
||||
pub simplify_enums: std::option::Option<SimplifyEnumsKind>, // use std::option::Option instead of Option to avoid clap mis-parsing
|
||||
#[doc(hidden)]
|
||||
#[clap(skip = ExportOptionsPrivate(()))]
|
||||
/// `#[non_exhaustive]` except allowing struct update syntax
|
||||
|
@ -2772,6 +2768,28 @@ impl fmt::Debug for ExportOptions {
|
|||
}
|
||||
|
||||
impl ExportOptions {
|
||||
pub fn to_args(&self) -> Vec<Interned<str>> {
|
||||
let Self {
|
||||
simplify_memories,
|
||||
simplify_enums,
|
||||
__private: ExportOptionsPrivate(()),
|
||||
} = self;
|
||||
let mut retval = Vec::new();
|
||||
if !*simplify_memories {
|
||||
retval.push("--no-simplify-memories".intern());
|
||||
}
|
||||
let simplify_enums = simplify_enums.map(|v| {
|
||||
clap::ValueEnum::to_possible_value(&v).expect("there are no skipped variants")
|
||||
});
|
||||
let simplify_enums = match &simplify_enums {
|
||||
None => OptionSimplifyEnumsKindValueParser::NONE_NAME,
|
||||
Some(v) => v.get_name(),
|
||||
};
|
||||
retval.push(str::intern_owned(format!(
|
||||
"--simplify-enums={simplify_enums}"
|
||||
)));
|
||||
retval
|
||||
}
|
||||
fn debug_fmt(
|
||||
&self,
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue