do some clean up

This commit is contained in:
Jacob Lifshay 2025-10-16 04:32:56 -07:00
parent 676c1e3b7d
commit a565be1b09
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
19 changed files with 1203 additions and 811 deletions

View file

@ -44,7 +44,7 @@ struct ExtraArgs {
impl ToArgs for ExtraArgs { impl ToArgs for ExtraArgs {
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
let Self { clock_frequency } = self; let Self { clock_frequency } = self;
args.write_arg(format_args!("--clock-frequency={clock_frequency}")); args.write_arg(format!("--clock-frequency={clock_frequency}"));
} }
} }

View file

@ -4,9 +4,9 @@
use crate::{ use crate::{
build::graph::JobGraph, build::graph::JobGraph,
bundle::{Bundle, BundleType}, bundle::{Bundle, BundleType},
intern::{Intern, Interned}, intern::{Intern, InternSlice, Interned},
module::Module, module::Module,
util::job_server::AcquiredJob, util::{job_server::AcquiredJob, os_str_strip_prefix},
}; };
use clap::ArgAction; use clap::ArgAction;
use serde::{ use serde::{
@ -18,9 +18,11 @@ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
borrow::Cow, borrow::Cow,
cmp::Ordering, cmp::Ordering,
ffi::OsStr, ffi::{OsStr, OsString},
fmt, fmt,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
io::Write,
marker::PhantomData,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{Arc, OnceLock}, sync::{Arc, OnceLock},
}; };
@ -46,46 +48,14 @@ pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> {
.chain(verilog::built_in_job_kinds()) .chain(verilog::built_in_job_kinds())
} }
#[track_caller]
fn intern_known_utf8_path_buf(v: PathBuf) -> Interned<str> {
let Ok(v) = v.into_os_string().into_string().map(str::intern_owned) else {
unreachable!("known to be valid UTF-8");
};
v
}
#[track_caller]
fn intern_known_utf8_str(v: impl AsRef<OsStr>) -> Interned<str> {
let Some(v) = v.as_ref().to_str().map(str::intern) else {
unreachable!("known to be valid UTF-8");
};
v
}
#[track_caller]
fn interned_known_utf8_method<F: FnOnce(&Path) -> &OsStr>(
v: impl AsRef<str>,
f: F,
) -> Interned<str> {
intern_known_utf8_str(f(v.as_ref().as_ref()))
}
#[track_caller]
fn interned_known_utf8_path_buf_method<F: FnOnce(&Path) -> PathBuf>(
v: impl AsRef<str>,
f: F,
) -> Interned<str> {
intern_known_utf8_path_buf(f(v.as_ref().as_ref()))
}
#[derive(Clone, Hash, PartialEq, Eq, Debug)] #[derive(Clone, Hash, PartialEq, Eq, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub enum JobItem { pub enum JobItem {
Path { Path {
path: Interned<str>, path: Interned<Path>,
}, },
DynamicPaths { DynamicPaths {
paths: Vec<Interned<str>>, paths: Vec<Interned<Path>>,
source_job_name: Interned<str>, source_job_name: Interned<str>,
}, },
} }
@ -105,7 +75,7 @@ impl JobItem {
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[non_exhaustive] #[non_exhaustive]
pub enum JobItemName { pub enum JobItemName {
Path { path: Interned<str> }, Path { path: Interned<Path> },
DynamicPaths { source_job_name: Interned<str> }, DynamicPaths { source_job_name: Interned<str> },
} }
@ -122,7 +92,7 @@ impl JobItemName {
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum JobItemNameRef<'a> { enum JobItemNameRef<'a> {
Path { path: &'a str }, Path { path: &'a Path },
DynamicPaths { source_job_name: &'a str }, DynamicPaths { source_job_name: &'a str },
} }
@ -146,119 +116,191 @@ impl Ord for JobItemName {
pub trait WriteArgs: pub trait WriteArgs:
for<'a> Extend<&'a str> for<'a> Extend<&'a str>
+ for<'a> Extend<&'a OsStr>
+ for<'a> Extend<&'a Path>
+ for<'a> Extend<Cow<'a, str>>
+ for<'a> Extend<Cow<'a, OsStr>>
+ for<'a> Extend<Cow<'a, Path>>
+ Extend<String> + Extend<String>
+ Extend<OsString>
+ Extend<PathBuf>
+ Extend<Interned<str>> + Extend<Interned<str>>
+ for<'a> Extend<fmt::Arguments<'a>> + Extend<Interned<OsStr>>
+ Extend<Interned<Path>>
{ {
fn write_args(&mut self, args: impl IntoIterator<Item: fmt::Display>) { fn write_display_args(&mut self, args: impl IntoIterator<Item: fmt::Display>) {
self.extend(args.into_iter().map(|v| format!("{v}"))); self.extend(args.into_iter().map(|v| v.to_string()));
} }
fn write_string_args(&mut self, args: impl IntoIterator<Item = String>) { fn write_owned_args(&mut self, args: impl IntoIterator<Item: Into<OsString>>) {
self.extend(args); self.extend(args.into_iter().map(Into::<OsString>::into))
} }
fn write_str_args<'a>(&mut self, args: impl IntoIterator<Item = &'a str>) { fn write_args<'a>(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>);
self.extend(args); fn write_interned_args(&mut self, args: impl IntoIterator<Item: Into<Interned<OsStr>>>) {
self.extend(args.into_iter().map(Into::<Interned<OsStr>>::into))
} }
fn write_interned_args(&mut self, args: impl IntoIterator<Item = Interned<str>>) { fn write_display_arg(&mut self, arg: impl fmt::Display) {
self.extend(args); self.write_display_args([arg]);
} }
fn write_arg(&mut self, arg: impl fmt::Display) { fn write_owned_arg(&mut self, arg: impl Into<OsString>) {
self.extend([format_args!("{arg}")]); self.extend([arg.into()]);
} }
fn write_string_arg(&mut self, arg: String) { fn write_arg(&mut self, arg: impl AsRef<OsStr>) {
self.extend([arg]); self.extend([arg.as_ref()]);
} }
fn write_str_arg(&mut self, arg: &str) { /// writes `--{name}={value}`
self.extend([arg]); fn write_long_option_eq(&mut self, name: impl AsRef<OsStr>, value: impl AsRef<OsStr>) {
let name = name.as_ref();
let value = value.as_ref();
let mut option =
OsString::with_capacity(name.len().saturating_add(value.len()).saturating_add(3));
option.push("--");
option.push(name);
option.push("=");
option.push(value);
self.write_owned_arg(option);
} }
fn write_interned_arg(&mut self, arg: Interned<str>) { fn write_interned_arg(&mut self, arg: impl Into<Interned<OsStr>>) {
self.extend([arg]); self.extend([arg.into()]);
} }
/// finds the first option that is `--{option_name}={value}` and returns `value` /// finds the first option that is `--{option_name}={value}` and returns `value`
fn get_long_option_eq(&self, option_name: impl AsRef<str>) -> Option<&str>; fn get_long_option_eq(&self, option_name: impl AsRef<str>) -> Option<&OsStr>;
} }
pub struct ArgsWriter<T>(pub Vec<T>); pub trait ArgsWriterArg:
AsRef<OsStr>
+ From<Interned<OsStr>>
+ for<'a> From<Cow<'a, OsStr>>
+ for<'a> From<&'a OsStr>
+ From<OsString>
{
}
impl<T> Default for ArgsWriter<T> { impl ArgsWriterArg for Interned<OsStr> {}
impl ArgsWriterArg for OsString {}
pub struct ArgsWriter<A: ArgsWriterArg>(pub Vec<A>);
impl<A: ArgsWriterArg> Default for ArgsWriter<A> {
fn default() -> Self { fn default() -> Self {
Self(Default::default()) Self(Default::default())
} }
} }
impl<T: AsRef<str>> ArgsWriter<T> { impl<A: ArgsWriterArg> ArgsWriter<A> {
fn get_long_option_eq_helper(&self, option_name: &str) -> Option<&str> { fn get_long_option_eq_helper(&self, option_name: &str) -> Option<&OsStr> {
self.0.iter().find_map(|arg| { self.0.iter().find_map(|arg| {
arg.as_ref() os_str_strip_prefix(arg.as_ref(), "--")
.strip_prefix("--") .and_then(|arg| os_str_strip_prefix(arg, option_name))
.and_then(|arg| arg.strip_prefix(option_name)) .and_then(|arg| os_str_strip_prefix(arg, "="))
.and_then(|arg| arg.strip_prefix("="))
}) })
} }
} }
impl<'a, W> Extend<fmt::Arguments<'a>> for ArgsWriter<W> impl<'a, A: ArgsWriterArg> Extend<&'a str> for ArgsWriter<A> {
where
Self: Extend<String>,
{
fn extend<T: IntoIterator<Item = fmt::Arguments<'a>>>(&mut self, iter: T) {
self.extend(iter.into_iter().map(|v| v.to_string()))
}
}
impl<'a> Extend<&'a str> for ArgsWriter<Interned<str>> {
fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
self.extend(iter.into_iter().map(str::intern)) self.extend(iter.into_iter().map(AsRef::<OsStr>::as_ref))
} }
} }
impl Extend<String> for ArgsWriter<Interned<str>> { impl<'a, A: ArgsWriterArg> Extend<&'a OsStr> for ArgsWriter<A> {
fn extend<T: IntoIterator<Item = &'a OsStr>>(&mut self, iter: T) {
self.0.extend(iter.into_iter().map(Into::into))
}
}
impl<'a, A: ArgsWriterArg> Extend<&'a Path> for ArgsWriter<A> {
fn extend<T: IntoIterator<Item = &'a Path>>(&mut self, iter: T) {
self.extend(iter.into_iter().map(AsRef::<OsStr>::as_ref))
}
}
impl<A: ArgsWriterArg> Extend<String> for ArgsWriter<A> {
fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
self.extend(iter.into_iter().map(str::intern_owned)) self.extend(iter.into_iter().map(OsString::from))
} }
} }
impl Extend<Interned<str>> for ArgsWriter<String> { impl<A: ArgsWriterArg> Extend<OsString> for ArgsWriter<A> {
fn extend<T: IntoIterator<Item = OsString>>(&mut self, iter: T) {
self.0.extend(iter.into_iter().map(Into::into))
}
}
impl<A: ArgsWriterArg> Extend<PathBuf> for ArgsWriter<A> {
fn extend<T: IntoIterator<Item = PathBuf>>(&mut self, iter: T) {
self.extend(iter.into_iter().map(OsString::from))
}
}
impl<A: ArgsWriterArg> Extend<Interned<str>> for ArgsWriter<A> {
fn extend<T: IntoIterator<Item = Interned<str>>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = Interned<str>>>(&mut self, iter: T) {
self.extend(iter.into_iter().map(String::from)) self.extend(iter.into_iter().map(Interned::<OsStr>::from))
} }
} }
impl<'a> Extend<&'a str> for ArgsWriter<String> { impl<A: ArgsWriterArg> Extend<Interned<OsStr>> for ArgsWriter<A> {
fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = Interned<OsStr>>>(&mut self, iter: T) {
self.extend(iter.into_iter().map(String::from)) self.0.extend(iter.into_iter().map(Into::into))
} }
} }
impl<I> Extend<I> for ArgsWriter<I> { impl<A: ArgsWriterArg> Extend<Interned<Path>> for ArgsWriter<A> {
fn extend<T: IntoIterator<Item = I>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = Interned<Path>>>(&mut self, iter: T) {
self.0.extend(iter); self.extend(iter.into_iter().map(Interned::<OsStr>::from))
} }
} }
impl WriteArgs for ArgsWriter<String> { impl<'a, A: ArgsWriterArg> Extend<Cow<'a, str>> for ArgsWriter<A> {
fn get_long_option_eq(&self, option_name: impl AsRef<str>) -> Option<&str> { fn extend<T: IntoIterator<Item = Cow<'a, str>>>(&mut self, iter: T) {
self.get_long_option_eq_helper(option_name.as_ref()) self.0.extend(iter.into_iter().map(|v| {
match v {
Cow::Borrowed(v) => Cow::<OsStr>::Borrowed(v.as_ref()),
Cow::Owned(v) => Cow::Owned(v.into()),
}
.into()
}))
} }
} }
impl WriteArgs for ArgsWriter<Interned<str>> { impl<'a, A: ArgsWriterArg> Extend<Cow<'a, OsStr>> for ArgsWriter<A> {
fn get_long_option_eq(&self, option_name: impl AsRef<str>) -> Option<&str> { fn extend<T: IntoIterator<Item = Cow<'a, OsStr>>>(&mut self, iter: T) {
self.0.extend(iter.into_iter().map(Into::into))
}
}
impl<'a, A: ArgsWriterArg> Extend<Cow<'a, Path>> for ArgsWriter<A> {
fn extend<T: IntoIterator<Item = Cow<'a, Path>>>(&mut self, iter: T) {
self.0.extend(iter.into_iter().map(|v| {
match v {
Cow::Borrowed(v) => Cow::<OsStr>::Borrowed(v.as_ref()),
Cow::Owned(v) => Cow::Owned(v.into()),
}
.into()
}))
}
}
impl<A: ArgsWriterArg> WriteArgs for ArgsWriter<A> {
fn write_args<'a>(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) {
self.0.extend(args.into_iter().map(|v| v.as_ref().into()))
}
fn get_long_option_eq(&self, option_name: impl AsRef<str>) -> Option<&OsStr> {
self.get_long_option_eq_helper(option_name.as_ref()) self.get_long_option_eq_helper(option_name.as_ref())
} }
} }
pub trait ToArgs: clap::Args + 'static + Send + Sync + Hash + Eq + fmt::Debug + Clone { pub trait ToArgs: clap::Args + 'static + Send + Sync + Hash + Eq + fmt::Debug + Clone {
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)); fn to_args(&self, args: &mut (impl WriteArgs + ?Sized));
fn to_interned_args(&self) -> Interned<[Interned<str>]> { fn to_interned_args(&self) -> Interned<[Interned<OsStr>]> {
Intern::intern_owned(self.to_interned_args_vec()) Intern::intern_owned(self.to_interned_args_vec())
} }
fn to_interned_args_vec(&self) -> Vec<Interned<str>> { fn to_interned_args_vec(&self) -> Vec<Interned<OsStr>> {
let mut retval = ArgsWriter::default(); let mut retval = ArgsWriter::default();
self.to_args(&mut retval); self.to_args(&mut retval);
retval.0 retval.0
} }
fn to_string_args(&self) -> Vec<String> { fn to_os_string_args(&self) -> Vec<OsString> {
let mut retval = ArgsWriter::default(); let mut retval = ArgsWriter::default();
self.to_args(&mut retval); self.to_args(&mut retval);
retval.0 retval.0
@ -361,6 +403,15 @@ pub struct JobAndDependencies<K: JobKind> {
pub dependencies: <K::Dependencies as JobDependencies>::JobsAndKinds, pub dependencies: <K::Dependencies as JobDependencies>::JobsAndKinds,
} }
impl<K: JobKind> JobAndDependencies<K> {
pub fn get_job<J, Position>(&self) -> &J
where
Self: GetJob<J, Position>,
{
GetJob::get_job(self)
}
}
impl<K: JobKind> Clone for JobAndDependencies<K> impl<K: JobKind> Clone for JobAndDependencies<K>
where where
K::Job: Clone, K::Job: Clone,
@ -608,41 +659,44 @@ impl JobParams {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct CommandParams { pub struct CommandParams {
pub command_line: Interned<[Interned<str>]>, pub command_line: Interned<[Interned<OsStr>]>,
pub current_dir: Option<Interned<str>>, pub current_dir: Option<Interned<Path>>,
} }
impl CommandParams { impl CommandParams {
fn to_unix_shell_line<W: ?Sized + fmt::Write>( fn to_unix_shell_line<E>(
self, self,
output: &mut W, output: &mut String,
mut escape_arg: impl FnMut(&str, &mut W) -> fmt::Result, mut escape_arg: impl FnMut(&OsStr, &mut String) -> Result<(), E>,
) -> fmt::Result { ) -> Result<(), E> {
let Self { let Self {
command_line, command_line,
current_dir, current_dir,
} = self; } = self;
let mut end = None; let mut end = None;
let mut separator = if let Some(current_dir) = current_dir { let mut separator = if let Some(current_dir) = current_dir {
output.write_str("(cd ")?; output.push_str("(cd ");
end = Some(")"); end = Some(")");
if !current_dir.starts_with(|ch: char| { if !current_dir
ch.is_ascii_alphanumeric() || matches!(ch, '/' | '\\' | '.') .as_os_str()
}) { .as_encoded_bytes()
output.write_str("-- ")?; .first()
.is_some_and(|ch| ch.is_ascii_alphanumeric() || matches!(ch, b'/' | b'\\' | b'.'))
{
output.push_str("-- ");
} }
escape_arg(&current_dir, output)?; escape_arg(current_dir.as_ref(), output)?;
"; exec -- " "; exec -- "
} else { } else {
"" ""
}; };
for arg in command_line { for arg in command_line {
output.write_str(separator)?; output.push_str(separator);
separator = " "; separator = " ";
escape_arg(&arg, output)?; escape_arg(&arg, output)?;
} }
if let Some(end) = end { if let Some(end) = end {
output.write_str(end)?; output.push_str(end);
} }
Ok(()) Ok(())
} }
@ -999,7 +1053,7 @@ trait DynJobArgsTrait: 'static + Send + Sync + fmt::Debug {
fn eq_dyn(&self, other: &dyn DynJobArgsTrait) -> bool; fn eq_dyn(&self, other: &dyn DynJobArgsTrait) -> bool;
fn hash_dyn(&self, state: &mut dyn Hasher); fn hash_dyn(&self, state: &mut dyn Hasher);
fn kind(&self) -> DynJobKind; fn kind(&self) -> DynJobKind;
fn to_args_extend_vec(&self, args: Vec<Interned<str>>) -> Vec<Interned<str>>; fn to_args_extend_vec(&self, args: Vec<Interned<OsStr>>) -> Vec<Interned<OsStr>>;
fn clone_into_arc(&self) -> Arc<dyn DynJobArgsTrait>; fn clone_into_arc(&self) -> Arc<dyn DynJobArgsTrait>;
fn update_from_arg_matches( fn update_from_arg_matches(
&mut self, &mut self,
@ -1041,7 +1095,7 @@ impl<K: JobKind> DynJobArgsTrait for DynJobArgsInner<K> {
DynJobKind::new(self.0.kind) DynJobKind::new(self.0.kind)
} }
fn to_args_extend_vec(&self, args: Vec<Interned<str>>) -> Vec<Interned<str>> { fn to_args_extend_vec(&self, args: Vec<Interned<OsStr>>) -> Vec<Interned<OsStr>> {
let mut writer = ArgsWriter(args); let mut writer = ArgsWriter(args);
self.0.args.to_args(&mut writer); self.0.args.to_args(&mut writer);
writer.0 writer.0
@ -1101,10 +1155,10 @@ impl DynJobArgs {
pub fn kind(&self) -> DynJobKind { pub fn kind(&self) -> DynJobKind {
DynJobArgsTrait::kind(&*self.0) DynJobArgsTrait::kind(&*self.0)
} }
pub fn to_args_vec(&self) -> Vec<Interned<str>> { pub fn to_args_vec(&self) -> Vec<Interned<OsStr>> {
self.to_args_extend_vec(Vec::new()) self.to_args_extend_vec(Vec::new())
} }
pub fn to_args_extend_vec(&self, args: Vec<Interned<str>>) -> Vec<Interned<str>> { pub fn to_args_extend_vec(&self, args: Vec<Interned<OsStr>>) -> Vec<Interned<OsStr>> {
DynJobArgsTrait::to_args_extend_vec(&*self.0, args) DynJobArgsTrait::to_args_extend_vec(&*self.0, args)
} }
fn make_mut(&mut self) -> &mut dyn DynJobArgsTrait { fn make_mut(&mut self) -> &mut dyn DynJobArgsTrait {
@ -1329,8 +1383,8 @@ impl DynJob {
#[track_caller] #[track_caller]
pub fn internal_command_params_with_program_prefix( pub fn internal_command_params_with_program_prefix(
&self, &self,
internal_program_prefix: &[Interned<str>], internal_program_prefix: &[Interned<OsStr>],
extra_args: &[Interned<str>], extra_args: &[Interned<OsStr>],
) -> CommandParams { ) -> CommandParams {
let mut command_line = internal_program_prefix.to_vec(); let mut command_line = internal_program_prefix.to_vec();
let command_line = match RunSingleJob::try_add_subcommand(self, &mut command_line) { let command_line = match RunSingleJob::try_add_subcommand(self, &mut command_line) {
@ -1346,7 +1400,7 @@ impl DynJob {
} }
} }
#[track_caller] #[track_caller]
pub fn internal_command_params(&self, extra_args: &[Interned<str>]) -> CommandParams { pub fn internal_command_params(&self, extra_args: &[Interned<OsStr>]) -> CommandParams {
self.internal_command_params_with_program_prefix( self.internal_command_params_with_program_prefix(
&[program_name_for_internal_jobs()], &[program_name_for_internal_jobs()],
extra_args, extra_args,
@ -1355,8 +1409,8 @@ impl DynJob {
#[track_caller] #[track_caller]
pub fn command_params_with_internal_program_prefix( pub fn command_params_with_internal_program_prefix(
&self, &self,
internal_program_prefix: &[Interned<str>], internal_program_prefix: &[Interned<OsStr>],
extra_args: &[Interned<str>], extra_args: &[Interned<OsStr>],
) -> CommandParams { ) -> CommandParams {
match self.external_command_params() { match self.external_command_params() {
Some(v) => v, Some(v) => v,
@ -1365,7 +1419,7 @@ impl DynJob {
} }
} }
#[track_caller] #[track_caller]
pub fn command_params(&self, extra_args: &[Interned<str>]) -> CommandParams { pub fn command_params(&self, extra_args: &[Interned<OsStr>]) -> CommandParams {
self.command_params_with_internal_program_prefix( self.command_params_with_internal_program_prefix(
&[program_name_for_internal_jobs()], &[program_name_for_internal_jobs()],
extra_args, extra_args,
@ -1490,14 +1544,16 @@ impl RunSingleJob {
pub const SUBCOMMAND_NAME: &'static str = "run-single-job"; pub const SUBCOMMAND_NAME: &'static str = "run-single-job";
fn try_add_subcommand( fn try_add_subcommand(
job: &DynJob, job: &DynJob,
subcommand_line: &mut Vec<Interned<str>>, subcommand_line: &mut Vec<Interned<OsStr>>,
) -> serde_json::Result<()> { ) -> serde_json::Result<()> {
let mut json = job.serialize_to_json_ascii()?; let mut json = job.serialize_to_json_ascii()?;
json.insert_str(0, "--json="); json.insert_str(0, "--json=");
subcommand_line.extend([ subcommand_line.extend([
Self::SUBCOMMAND_NAME.intern(), Interned::<OsStr>::from(Self::SUBCOMMAND_NAME.intern()),
Intern::intern_owned(format!("--name={}", job.kind().name())), format!("--name={}", job.kind().name())
Intern::intern_owned(json), .intern_deref()
.into(),
json.intern_deref().into(),
]); ]);
Ok(()) Ok(())
} }
@ -1623,10 +1679,11 @@ impl RunBuild for Completions {
F: FnOnce(NoArgs) -> eyre::Result<JobParams>, F: FnOnce(NoArgs) -> eyre::Result<JobParams>,
{ {
let Self::Completions { shell } = self; let Self::Completions { shell } = self;
let bin_name = cmd let bin_name = cmd.get_bin_name().map(str::intern).unwrap_or_else(|| {
.get_bin_name() program_name_for_internal_jobs()
.map(str::intern) .to_interned_str()
.unwrap_or_else(|| program_name_for_internal_jobs()); .expect("program name is invalid UTF-8")
});
clap_complete::aot::generate( clap_complete::aot::generate(
shell, shell,
&mut cmd, &mut cmd,
@ -1667,6 +1724,7 @@ pub enum BuildCli<Extra: ToArgs = NoArgs> {
RunSingleJob(RunSingleJob<Extra>), RunSingleJob(RunSingleJob<Extra>),
#[clap(flatten)] #[clap(flatten)]
Completions(Completions), Completions(Completions),
#[cfg(unix)]
#[clap(flatten)] #[clap(flatten)]
CreateUnixShellScript(CreateUnixShellScript<Extra>), CreateUnixShellScript(CreateUnixShellScript<Extra>),
} }
@ -1680,11 +1738,13 @@ impl<Extra: ToArgs> RunBuild<Extra> for BuildCli<Extra> {
BuildCli::Job(v) => v.run(make_params, cmd), BuildCli::Job(v) => v.run(make_params, cmd),
BuildCli::RunSingleJob(v) => v.run(make_params, cmd), BuildCli::RunSingleJob(v) => v.run(make_params, cmd),
BuildCli::Completions(v) => v.run(|NoArgs {}| unreachable!(), cmd), BuildCli::Completions(v) => v.run(|NoArgs {}| unreachable!(), cmd),
#[cfg(unix)]
BuildCli::CreateUnixShellScript(v) => v.run(make_params, cmd), BuildCli::CreateUnixShellScript(v) => v.run(make_params, cmd),
} }
} }
} }
#[cfg(unix)]
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Subcommand)] #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Subcommand)]
enum CreateUnixShellScriptInner<Extra: ToArgs> { enum CreateUnixShellScriptInner<Extra: ToArgs> {
CreateUnixShellScript { CreateUnixShellScript {
@ -1717,16 +1777,17 @@ impl<Extra: ToArgs> RunBuild<Extra> for CreateUnixShellScript<Extra> {
let (job, dependencies) = args.args_to_jobs(dependencies_args, &params)?; let (job, dependencies) = args.args_to_jobs(dependencies_args, &params)?;
let mut job_graph = JobGraph::new(); let mut job_graph = JobGraph::new();
job_graph.add_jobs([job].into_iter().chain(dependencies)); job_graph.add_jobs([job].into_iter().chain(dependencies));
println!( std::io::stdout().write_all(
"{}", job_graph
job_graph.to_unix_shell_script_with_internal_program_prefix( .to_unix_shell_script_with_internal_program_prefix(
&[cmd &[cmd
.get_bin_name() .get_bin_name()
.map(str::intern) .map(|v| OsStr::new(v).intern())
.unwrap_or_else(|| program_name_for_internal_jobs())], .unwrap_or_else(|| program_name_for_internal_jobs())],
&extra_args, &extra_args,
) )
); .as_bytes(),
)?;
Ok(()) Ok(())
} }
} }
@ -1749,6 +1810,7 @@ impl<Extra: ToArgs> clap::FromArgMatches for CreateUnixShellScript<Extra> {
} }
} }
#[cfg(unix)]
impl<Extra: ToArgs> clap::Subcommand for CreateUnixShellScript<Extra> { impl<Extra: ToArgs> clap::Subcommand for CreateUnixShellScript<Extra> {
fn augment_subcommands(cmd: clap::Command) -> clap::Command { fn augment_subcommands(cmd: clap::Command) -> clap::Command {
CreateUnixShellScriptInner::<Extra>::augment_subcommands(cmd) CreateUnixShellScriptInner::<Extra>::augment_subcommands(cmd)
@ -1912,10 +1974,14 @@ impl<Extra: ToArgs> RunBuild<Extra> for AnyJobSubcommand<Extra> {
} }
} }
pub fn program_name_for_internal_jobs() -> Interned<str> { pub fn program_name_for_internal_jobs() -> Interned<OsStr> {
static PROGRAM_NAME: OnceLock<Interned<str>> = OnceLock::new(); static PROGRAM_NAME: OnceLock<Interned<OsStr>> = OnceLock::new();
*PROGRAM_NAME *PROGRAM_NAME.get_or_init(|| {
.get_or_init(|| str::intern_owned(std::env::args().next().expect("can't get program name"))) std::env::args_os()
.next()
.expect("can't get program name")
.intern_deref()
})
} }
#[derive(clap::Args, PartialEq, Eq, Hash, Debug, Clone)] #[derive(clap::Args, PartialEq, Eq, Hash, Debug, Clone)]
@ -1923,7 +1989,7 @@ pub fn program_name_for_internal_jobs() -> Interned<str> {
pub struct CreateOutputDirArgs { pub struct CreateOutputDirArgs {
/// the directory to put the generated main output file and associated files in /// the directory to put the generated main output file and associated files in
#[arg(short, long, value_hint = clap::ValueHint::DirPath)] #[arg(short, long, value_hint = clap::ValueHint::DirPath)]
pub output: Option<String>, pub output: Option<PathBuf>,
#[arg(long, env = "FAYALITE_KEEP_TEMP_DIR")] #[arg(long, env = "FAYALITE_KEEP_TEMP_DIR")]
pub keep_temp_dir: bool, pub keep_temp_dir: bool,
} }
@ -1935,17 +2001,17 @@ impl ToArgs for CreateOutputDirArgs {
keep_temp_dir, keep_temp_dir,
} = self; } = self;
if let Some(output) = output { if let Some(output) = output {
args.write_arg(format_args!("--output={output}")); args.write_long_option_eq("output", output);
} }
if *keep_temp_dir { if *keep_temp_dir {
args.write_str_arg("--keep-temp-dir"); args.write_arg("--keep-temp-dir");
} }
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateOutputDir { pub struct CreateOutputDir {
output_dir: Interned<str>, output_dir: Interned<Path>,
#[serde(skip)] #[serde(skip)]
temp_dir: Option<Arc<TempDir>>, temp_dir: Option<Arc<TempDir>>,
} }
@ -1998,20 +2064,7 @@ impl JobKind for CreateOutputDirJobKind {
// we create the temp dir here rather than in run so other // we create the temp dir here rather than in run so other
// jobs can have their paths based on the chosen temp dir // jobs can have their paths based on the chosen temp dir
let temp_dir = TempDir::new()?; let temp_dir = TempDir::new()?;
let output_dir = temp_dir let output_dir = temp_dir.path().intern();
.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()
),
)
})?
.intern();
let temp_dir = if keep_temp_dir { let temp_dir = if keep_temp_dir {
// use TempDir::into_path() to no longer automatically delete the temp dir // use TempDir::into_path() to no longer automatically delete the temp dir
let temp_dir_path = temp_dir.into_path(); let temp_dir_path = temp_dir.into_path();
@ -2041,8 +2094,8 @@ impl JobKind for CreateOutputDirJobKind {
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
[JobItemName::Path { [JobItemName::Path {
path: job.output_dir, path: job.output_dir,
}][..] }]
.intern() .intern_slice()
} }
fn name(self) -> Interned<str> { fn name(self) -> Interned<str> {
@ -2052,12 +2105,12 @@ impl JobKind for CreateOutputDirJobKind {
fn external_command_params(self, job: &Self::Job) -> Option<CommandParams> { fn external_command_params(self, job: &Self::Job) -> Option<CommandParams> {
Some(CommandParams { Some(CommandParams {
command_line: [ command_line: [
"mkdir".intern(), "mkdir".intern().into(),
"-p".intern(), "-p".intern().into(),
"--".intern(), "--".intern().into(),
job.output_dir, job.output_dir.into(),
][..] ]
.intern(), .intern_slice(),
current_dir: None, current_dir: None,
}) })
} }
@ -2084,10 +2137,10 @@ impl JobKind for CreateOutputDirJobKind {
} }
impl CreateOutputDir { impl CreateOutputDir {
pub fn output_dir(&self) -> Interned<str> { pub fn output_dir(&self) -> Interned<Path> {
self.output_dir self.output_dir
} }
fn compare_key(&self) -> (&str, bool) { fn compare_key(&self) -> (&Path, bool) {
let Self { let Self {
output_dir, output_dir,
temp_dir, temp_dir,
@ -2105,7 +2158,7 @@ pub struct BaseJobArgs {
pub create_output_dir_args: CreateOutputDirArgs, pub create_output_dir_args: CreateOutputDirArgs,
/// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo /// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo
#[arg(long)] #[arg(long)]
pub file_stem: Option<String>, pub file_stem: Option<OsString>,
/// run commands even if their results are already cached /// run commands even if their results are already cached
#[arg(long, env = Self::RUN_EVEN_IF_CACHED_ENV_NAME)] #[arg(long, env = Self::RUN_EVEN_IF_CACHED_ENV_NAME)]
pub run_even_if_cached: bool, pub run_even_if_cached: bool,
@ -2113,7 +2166,7 @@ pub struct BaseJobArgs {
impl BaseJobArgs { impl BaseJobArgs {
pub const RUN_EVEN_IF_CACHED_ENV_NAME: &'static str = "FAYALITE_RUN_EVEN_IF_CACHED"; pub const RUN_EVEN_IF_CACHED_ENV_NAME: &'static str = "FAYALITE_RUN_EVEN_IF_CACHED";
pub fn from_output_dir_and_env(output: String) -> Self { pub fn from_output_dir_and_env(output: PathBuf) -> Self {
Self { Self {
create_output_dir_args: CreateOutputDirArgs { create_output_dir_args: CreateOutputDirArgs {
output: Some(output), output: Some(output),
@ -2134,10 +2187,10 @@ impl ToArgs for BaseJobArgs {
} = self; } = self;
create_output_dir_args.to_args(args); create_output_dir_args.to_args(args);
if let Some(file_stem) = file_stem { if let Some(file_stem) = file_stem {
args.write_arg(format_args!("--file-stem={file_stem}")); args.write_long_option_eq("file-stem", file_stem);
} }
if *run_even_if_cached { if *run_even_if_cached {
args.write_str_arg("--run-even-if-cached"); args.write_arg("--run-even-if-cached");
} }
} }
} }
@ -2147,21 +2200,21 @@ pub struct BaseJob {
/// rather than having CreateOutputDir be a normal dependency, it's nested in BaseJob to avoid a cyclic dependency /// rather than having CreateOutputDir be a normal dependency, it's nested in BaseJob to avoid a cyclic dependency
#[serde(flatten)] #[serde(flatten)]
create_output_dir: CreateOutputDir, create_output_dir: CreateOutputDir,
file_stem: Interned<str>, file_stem: Interned<OsStr>,
run_even_if_cached: bool, run_even_if_cached: bool,
} }
impl BaseJob { impl BaseJob {
pub fn output_dir(&self) -> Interned<str> { pub fn output_dir(&self) -> Interned<Path> {
self.create_output_dir.output_dir() self.create_output_dir.output_dir()
} }
pub fn file_stem(&self) -> Interned<str> { pub fn file_stem(&self) -> Interned<OsStr> {
self.file_stem self.file_stem
} }
pub fn file_with_ext(&self, ext: &str) -> Interned<str> { pub fn file_with_ext(&self, ext: impl AsRef<OsStr>) -> Interned<Path> {
let mut retval = std::path::Path::new(&self.output_dir()).join(self.file_stem()); let mut retval = self.output_dir().join(self.file_stem());
retval.set_extension(ext); retval.set_extension(ext);
intern_known_utf8_path_buf(retval) retval.intern_deref()
} }
pub fn run_even_if_cached(&self) -> bool { pub fn run_even_if_cached(&self) -> bool {
self.run_even_if_cached self.run_even_if_cached
@ -2195,8 +2248,8 @@ impl JobKind for BaseJobKind {
}; };
let create_output_dir = create_output_dir_args.args_to_jobs((), params)?.job.job; let create_output_dir = create_output_dir_args.args_to_jobs((), params)?.job.job;
let file_stem = file_stem let file_stem = file_stem
.map(Intern::intern_owned) .map(Intern::intern_deref)
.unwrap_or(params.main_module().name()); .unwrap_or(params.main_module().name().into());
Ok(JobAndDependencies { Ok(JobAndDependencies {
job: JobAndKind { job: JobAndKind {
kind: BaseJobKind, kind: BaseJobKind,
@ -2241,64 +2294,91 @@ impl JobKind for BaseJobKind {
} }
} }
pub trait GetBaseJob { pub trait GetJob<J, Position> {
fn base_job(&self) -> &BaseJob; fn get_job(this: &Self) -> &J;
} }
impl<T: ?Sized + GetBaseJob> GetBaseJob for &'_ T { impl<J, Position, T: ?Sized + GetJob<J, Position>> GetJob<J, Position> for &'_ T {
fn base_job(&self) -> &BaseJob { fn get_job(this: &Self) -> &J {
T::base_job(self) T::get_job(this)
} }
} }
impl<T: ?Sized + GetBaseJob> GetBaseJob for &'_ mut T { impl<J, Position, T: ?Sized + GetJob<J, Position>> GetJob<J, Position> for &'_ mut T {
fn base_job(&self) -> &BaseJob { fn get_job(this: &Self) -> &J {
T::base_job(self) T::get_job(this)
} }
} }
impl<T: ?Sized + GetBaseJob> GetBaseJob for Box<T> { impl<J, Position, T: ?Sized + GetJob<J, Position>> GetJob<J, Position> for Box<T> {
fn base_job(&self) -> &BaseJob { fn get_job(this: &Self) -> &J {
T::base_job(self) T::get_job(this)
} }
} }
impl GetBaseJob for BaseJob { pub struct GetJobPositionDependencies<T>(PhantomData<T>);
fn base_job(&self) -> &BaseJob {
self impl<T> Default for GetJobPositionDependencies<T> {
fn default() -> Self {
Self(Default::default())
} }
} }
impl GetBaseJob for JobAndKind<BaseJobKind> { impl<T> fmt::Debug for GetJobPositionDependencies<T> {
fn base_job(&self) -> &BaseJob { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
&self.job write!(
f,
"GetJobPositionDependencies<{}>",
std::any::type_name::<T>()
)
} }
} }
impl GetBaseJob for JobAndDependencies<BaseJobKind> { impl<T> Hash for GetJobPositionDependencies<T> {
fn base_job(&self) -> &BaseJob { fn hash<H: Hasher>(&self, _state: &mut H) {}
&self.job.job }
impl<T> Ord for GetJobPositionDependencies<T> {
fn cmp(&self, _other: &Self) -> Ordering {
Ordering::Equal
} }
} }
impl<K: JobKind> GetBaseJob for JobAndDependencies<K> impl<T> PartialOrd for GetJobPositionDependencies<T> {
where fn partial_cmp(&self, _other: &Self) -> Option<Ordering> {
K::Dependencies: JobDependencies, Some(Ordering::Equal)
<K::Dependencies as JobDependencies>::JobsAndKinds: GetBaseJob, }
}
impl<T> Eq for GetJobPositionDependencies<T> {}
impl<T> PartialEq for GetJobPositionDependencies<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T> Clone for GetJobPositionDependencies<T> {
fn clone(&self) -> Self {
Self(PhantomData)
}
}
impl<T> Copy for GetJobPositionDependencies<T> {}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct GetJobPositionJob;
impl<J, Position, K: JobKind<Dependencies: JobDependencies<JobsAndKinds: GetJob<J, Position>>>>
GetJob<J, GetJobPositionDependencies<Position>> for JobAndDependencies<K>
{ {
fn base_job(&self) -> &BaseJob { fn get_job(this: &Self) -> &J {
self.dependencies.base_job() GetJob::get_job(&this.dependencies)
} }
} }
impl<T: GetBaseJob, U> GetBaseJob for (T, U) { impl<K: JobKind> GetJob<K::Job, GetJobPositionJob> for JobAndDependencies<K> {
fn base_job(&self) -> &BaseJob { fn get_job(this: &Self) -> &K::Job {
self.0.base_job() &this.job.job
}
}
impl<T: GetBaseJob, U, V> GetBaseJob for (T, U, V) {
fn base_job(&self) -> &BaseJob {
self.0.base_job()
} }
} }

View file

@ -3,9 +3,9 @@
use crate::{ use crate::{
build::{ build::{
ArgsWriter, CommandParams, GetBaseJob, JobAndDependencies, JobAndKind, ArgsWriter, BaseJob, CommandParams, GetJob, JobAndDependencies, JobAndKind,
JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, JobKindAndArgs, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, JobKindAndArgs,
JobParams, ToArgs, WriteArgs, intern_known_utf8_path_buf, JobParams, ToArgs, WriteArgs,
}, },
intern::{Intern, Interned}, intern::{Intern, Interned},
util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8}, util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8},
@ -55,6 +55,25 @@ impl MaybeUtf8 {
MaybeUtf8::Binary(v) => v, MaybeUtf8::Binary(v) => v,
} }
} }
pub fn as_os_str(&self) -> &OsStr {
#![allow(unreachable_code)]
#[cfg(unix)]
{
return std::os::unix::ffi::OsStrExt::from_bytes(self.as_bytes());
}
#[cfg(target_os = "wasi")]
{
return std::os::wasi::ffi::OsStrExt::from_bytes(self.as_bytes());
}
// implementing WTF-8 is too much of a pain so don't have a special case for windows
if let Ok(s) = str::from_utf8(self.as_bytes()) {
return OsStr::new(s);
}
panic!("invalid UTF-8 conversion to OsStr is not implemented on this platform");
}
pub fn as_path(&self) -> &Path {
Path::new(self.as_os_str())
}
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -107,31 +126,80 @@ impl From<String> for MaybeUtf8 {
} }
} }
impl From<PathBuf> for MaybeUtf8 {
fn from(value: PathBuf) -> Self {
Self::from(value.into_os_string().into_encoded_bytes())
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
#[serde(rename = "File")]
pub struct ExternalJobCacheV2File<'a> {
pub name: MaybeUtf8,
pub contents: Cow<'a, MaybeUtf8>,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct ExternalJobCacheV2Files(pub BTreeMap<PathBuf, MaybeUtf8>);
impl Serialize for ExternalJobCacheV2Files {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_seq(
self.0
.iter()
.map(|(name, contents)| ExternalJobCacheV2File {
name: name.clone().into(),
contents: Cow::Borrowed(contents),
}),
)
}
}
impl<'de> Deserialize<'de> for ExternalJobCacheV2Files {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self(
Vec::deserialize(deserializer)?
.into_iter()
.map(|ExternalJobCacheV2File { name, contents }| {
(name.as_path().to_path_buf(), contents.into_owned())
})
.collect(),
))
}
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[serde(rename = "ExternalJobCache")] #[serde(rename = "ExternalJobCache")]
pub struct ExternalJobCacheV2 { pub struct ExternalJobCacheV2 {
pub version: ExternalJobCacheVersion, pub version: ExternalJobCacheVersion,
pub inputs_hash: blake3::Hash, pub inputs_hash: blake3::Hash,
pub stdout_stderr: String, pub stdout_stderr: String,
pub result: Result<BTreeMap<String, MaybeUtf8>, String>, pub result: Result<ExternalJobCacheV2Files, String>,
} }
impl ExternalJobCacheV2 { impl ExternalJobCacheV2 {
fn read_from_file(cache_json_path: Interned<str>) -> eyre::Result<Self> { fn read_from_file(cache_json_path: Interned<Path>) -> eyre::Result<Self> {
let cache_str = std::fs::read_to_string(&*cache_json_path) let cache_str = std::fs::read_to_string(&*cache_json_path)
.wrap_err_with(|| format!("can't read {cache_json_path}"))?; .wrap_err_with(|| format!("can't read {cache_json_path:?}"))?;
serde_json::from_str(&cache_str).wrap_err_with(|| format!("can't decode {cache_json_path}")) serde_json::from_str(&cache_str)
.wrap_err_with(|| format!("can't decode {cache_json_path:?}"))
} }
fn write_to_file(&self, cache_json_path: Interned<str>) -> eyre::Result<()> { fn write_to_file(&self, cache_json_path: Interned<Path>) -> eyre::Result<()> {
let cache_str = serde_json::to_string_pretty(&self).expect("serialization can't fail"); let cache_str = serde_json::to_string_pretty(&self).expect("serialization can't fail");
std::fs::write(&*cache_json_path, cache_str) std::fs::write(&*cache_json_path, cache_str)
.wrap_err_with(|| format!("can't write {cache_json_path}")) .wrap_err_with(|| format!("can't write {cache_json_path:?}"))
} }
} }
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct ExternalJobCaching { pub struct ExternalJobCaching {
cache_json_path: Interned<str>, cache_json_path: Interned<Path>,
run_even_if_cached: bool, run_even_if_cached: bool,
} }
@ -148,8 +216,8 @@ impl JobCacheHasher {
self.hash_size(bytes.len()); self.hash_size(bytes.len());
self.0.update(bytes); self.0.update(bytes);
} }
fn hash_sized_str(&mut self, s: &str) { fn hash_sized_os_str(&mut self, s: &OsStr) {
self.hash_sized_bytes(s.as_bytes()); self.hash_sized_bytes(s.as_encoded_bytes());
} }
fn hash_iter<F: FnMut(&mut Self, I::Item), I: IntoIterator<IntoIter: ExactSizeIterator>>( fn hash_iter<F: FnMut(&mut Self, I::Item), I: IntoIterator<IntoIter: ExactSizeIterator>>(
&mut self, &mut self,
@ -193,8 +261,8 @@ fn write_file_atomically_no_clobber<F: FnOnce() -> C, C: AsRef<[u8]>>(
} }
impl ExternalJobCaching { impl ExternalJobCaching {
pub fn get_cache_dir_from_output_dir(output_dir: &str) -> PathBuf { pub fn get_cache_dir_from_output_dir(output_dir: impl AsRef<Path>) -> PathBuf {
Path::join(output_dir.as_ref(), ".fayalite-job-cache") output_dir.as_ref().join(".fayalite-job-cache")
} }
pub fn make_cache_dir( pub fn make_cache_dir(
cache_dir: impl AsRef<Path>, cache_dir: impl AsRef<Path>,
@ -218,19 +286,18 @@ impl ExternalJobCaching {
}) })
} }
pub fn new( pub fn new(
output_dir: &str, output_dir: impl AsRef<Path>,
application_name: &str, application_name: &str,
json_file_stem: &str, json_file_stem: impl AsRef<OsStr>,
run_even_if_cached: bool, run_even_if_cached: bool,
) -> std::io::Result<Self> { ) -> std::io::Result<Self> {
let cache_dir = Self::get_cache_dir_from_output_dir(output_dir); let cache_dir = Self::get_cache_dir_from_output_dir(output_dir);
Self::make_cache_dir(&cache_dir, application_name)?; Self::make_cache_dir(&cache_dir, application_name)?;
let mut cache_json_path = cache_dir; let mut cache_json_path = cache_dir;
cache_json_path.push(json_file_stem); cache_json_path.push(json_file_stem.as_ref());
cache_json_path.set_extension("json"); cache_json_path.set_extension("json");
let cache_json_path = intern_known_utf8_path_buf(cache_json_path);
Ok(Self { Ok(Self {
cache_json_path, cache_json_path: Path::intern_owned(cache_json_path),
run_even_if_cached, run_even_if_cached,
}) })
} }
@ -249,7 +316,7 @@ impl ExternalJobCaching {
fn run_from_cache( fn run_from_cache(
self, self,
inputs_hash: blake3::Hash, inputs_hash: blake3::Hash,
output_file_paths: impl IntoIterator<Item = Interned<str>>, output_file_paths: impl IntoIterator<Item = Interned<Path>>,
) -> Result<Result<(), String>, ()> { ) -> Result<Result<(), String>, ()> {
if self.run_even_if_cached { if self.run_even_if_cached {
return Err(()); return Err(());
@ -269,7 +336,7 @@ impl ExternalJobCaching {
match result { match result {
Ok(outputs) => { Ok(outputs) => {
for output_file_path in output_file_paths { for output_file_path in output_file_paths {
let Some(output_data) = outputs.get(&*output_file_path) else { let Some(output_data) = outputs.0.get(&*output_file_path) else {
if let Ok(true) = std::fs::exists(&*output_file_path) { if let Ok(true) = std::fs::exists(&*output_file_path) {
// assume the existing file is the correct one // assume the existing file is the correct one
continue; continue;
@ -290,7 +357,7 @@ impl ExternalJobCaching {
} }
} }
fn make_command( fn make_command(
command_line: Interned<[Interned<str>]>, command_line: Interned<[Interned<OsStr>]>,
) -> eyre::Result<std::process::Command> { ) -> eyre::Result<std::process::Command> {
ensure!(!command_line.is_empty(), "command line must not be empty"); ensure!(!command_line.is_empty(), "command line must not be empty");
let mut cmd = std::process::Command::new(&*command_line[0]); let mut cmd = std::process::Command::new(&*command_line[0]);
@ -300,26 +367,26 @@ impl ExternalJobCaching {
} }
pub fn run<F: FnOnce(std::process::Command) -> eyre::Result<()>>( pub fn run<F: FnOnce(std::process::Command) -> eyre::Result<()>>(
self, self,
command_line: Interned<[Interned<str>]>, command_line: Interned<[Interned<OsStr>]>,
input_file_paths: impl IntoIterator<Item = Interned<str>>, input_file_paths: impl IntoIterator<Item = Interned<Path>>,
output_file_paths: impl IntoIterator<Item = Interned<str>> + Clone, output_file_paths: impl IntoIterator<Item = Interned<Path>> + Clone,
run_fn: F, run_fn: F,
) -> eyre::Result<()> { ) -> eyre::Result<()> {
let mut hasher = JobCacheHasher::default(); let mut hasher = JobCacheHasher::default();
hasher.hash_iter(command_line.iter(), |hasher, arg| { hasher.hash_iter(command_line.iter(), |hasher, arg| {
hasher.hash_sized_str(arg) hasher.hash_sized_os_str(arg)
}); });
let mut input_file_paths = let mut input_file_paths =
Vec::<&str>::from_iter(input_file_paths.into_iter().map(Interned::into_inner)); Vec::<&Path>::from_iter(input_file_paths.into_iter().map(Interned::into_inner));
input_file_paths.sort_unstable(); input_file_paths.sort_unstable();
input_file_paths.dedup(); input_file_paths.dedup();
hasher.try_hash_iter( hasher.try_hash_iter(
&input_file_paths, &input_file_paths,
|hasher, input_file_path| -> eyre::Result<()> { |hasher, input_file_path| -> eyre::Result<()> {
hasher.hash_sized_str(input_file_path); hasher.hash_sized_os_str(input_file_path.as_ref());
hasher.hash_sized_bytes( hasher.hash_sized_bytes(
&std::fs::read(input_file_path).wrap_err_with(|| { &std::fs::read(input_file_path).wrap_err_with(|| {
format!("can't read job input file: {input_file_path}") format!("can't read job input file: {input_file_path:?}")
})?, })?,
); );
Ok(()) Ok(())
@ -338,7 +405,7 @@ impl ExternalJobCaching {
let mut stdout_stderr = String::new(); let mut stdout_stderr = String::new();
let result = std::thread::scope(|scope| { let result = std::thread::scope(|scope| {
std::thread::Builder::new() std::thread::Builder::new()
.name(format!("stdout:{}", command_line[0])) .name(format!("stdout:{}", command_line[0].display()))
.spawn_scoped(scope, || { .spawn_scoped(scope, || {
let _ = streaming_read_utf8(std::io::BufReader::new(pipe_reader), |s| { let _ = streaming_read_utf8(std::io::BufReader::new(pipe_reader), |s| {
stdout_stderr.push_str(s); stdout_stderr.push_str(s);
@ -358,17 +425,19 @@ impl ExternalJobCaching {
inputs_hash, inputs_hash,
stdout_stderr, stdout_stderr,
result: match &result { result: match &result {
Ok(()) => Ok(Result::from_iter(output_file_paths.into_iter().map( Ok(()) => Ok(ExternalJobCacheV2Files(Result::from_iter(
|output_file_path: Interned<str>| -> eyre::Result<_> { output_file_paths.into_iter().map(
let output_file_path = &*output_file_path; |output_file_path: Interned<Path>| -> eyre::Result<_> {
Ok(( let output_file_path = &*output_file_path;
String::from(output_file_path), Ok((
MaybeUtf8::from(std::fs::read(output_file_path).wrap_err_with( PathBuf::from(output_file_path),
|| format!("can't read job output file: {output_file_path}"), MaybeUtf8::from(std::fs::read(output_file_path).wrap_err_with(
)?), || format!("can't read job output file: {output_file_path:?}"),
)) )?),
}, ))
))?), },
),
)?)),
Err(e) => Err(format!("{e:#}")), Err(e) => Err(format!("{e:#}")),
}, },
} }
@ -377,9 +446,9 @@ impl ExternalJobCaching {
} }
pub fn run_maybe_cached<F: FnOnce(std::process::Command) -> eyre::Result<()>>( pub fn run_maybe_cached<F: FnOnce(std::process::Command) -> eyre::Result<()>>(
this: Option<Self>, this: Option<Self>,
command_line: Interned<[Interned<str>]>, command_line: Interned<[Interned<OsStr>]>,
input_file_paths: impl IntoIterator<Item = Interned<str>>, input_file_paths: impl IntoIterator<Item = Interned<Path>>,
output_file_paths: impl IntoIterator<Item = Interned<str>> + Clone, output_file_paths: impl IntoIterator<Item = Interned<Path>> + Clone,
run_fn: F, run_fn: F,
) -> eyre::Result<()> { ) -> eyre::Result<()> {
match this { match this {
@ -437,31 +506,22 @@ fn parse_which_result(
which_result: which::Result<PathBuf>, which_result: which::Result<PathBuf>,
program_name: impl Into<OsString>, program_name: impl Into<OsString>,
program_path_arg_name: impl FnOnce() -> String, program_path_arg_name: impl FnOnce() -> String,
) -> Result<Interned<str>, ResolveProgramPathError> { ) -> Result<Interned<Path>, ResolveProgramPathError> {
let which_result = match which_result { let which_result = match which_result {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(inner) => {
return Err(ResolveProgramPathError { return Err(ResolveProgramPathError {
inner: ResolveProgramPathErrorInner::Which(e), inner,
program_name: program_name.into(), program_name: program_name.into(),
program_path_arg_name: program_path_arg_name(), program_path_arg_name: program_path_arg_name(),
}); });
} }
}; };
Ok(str::intern_owned( Ok(which_result.intern_deref())
which_result
.into_os_string()
.into_string()
.map_err(|_| ResolveProgramPathError {
inner: ResolveProgramPathErrorInner::NotValidUtf8,
program_name: program_name.into(),
program_path_arg_name: program_path_arg_name(),
})?,
))
} }
impl clap::builder::TypedValueParser for ExternalProgramPathValueParser { impl clap::builder::TypedValueParser for ExternalProgramPathValueParser {
type Value = Interned<str>; type Value = Interned<Path>;
fn parse_ref( fn parse_ref(
&self, &self,
@ -495,34 +555,10 @@ pub struct ExternalCommandArgs<T: ExternalCommand> {
pub additional_args: T::AdditionalArgs, pub additional_args: T::AdditionalArgs,
} }
#[derive(Clone)]
enum ResolveProgramPathErrorInner {
Which(which::Error),
NotValidUtf8,
}
impl fmt::Debug for ResolveProgramPathErrorInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Which(v) => v.fmt(f),
Self::NotValidUtf8 => f.write_str("NotValidUtf8"),
}
}
}
impl fmt::Display for ResolveProgramPathErrorInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Which(v) => v.fmt(f),
Self::NotValidUtf8 => f.write_str("path is not valid UTF-8"),
}
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ResolveProgramPathError { pub struct ResolveProgramPathError {
inner: ResolveProgramPathErrorInner, inner: which::Error,
program_name: std::ffi::OsString, program_name: OsString,
program_path_arg_name: String, program_path_arg_name: String,
} }
@ -546,7 +582,7 @@ pub fn resolve_program_path(
program_name: Option<&OsStr>, program_name: Option<&OsStr>,
default_program_name: impl AsRef<OsStr>, default_program_name: impl AsRef<OsStr>,
program_path_env_var_name: Option<&OsStr>, program_path_env_var_name: Option<&OsStr>,
) -> Result<Interned<str>, ResolveProgramPathError> { ) -> Result<Interned<Path>, ResolveProgramPathError> {
let default_program_name = default_program_name.as_ref(); let default_program_name = default_program_name.as_ref();
let owned_program_name; let owned_program_name;
let program_name = if let Some(program_name) = program_name { let program_name = if let Some(program_name) = program_name {
@ -564,7 +600,7 @@ pub fn resolve_program_path(
impl<T: ExternalCommand> ExternalCommandArgs<T> { impl<T: ExternalCommand> ExternalCommandArgs<T> {
pub fn with_resolved_program_path( pub fn with_resolved_program_path(
program_path: Interned<str>, program_path: Interned<Path>,
additional_args: T::AdditionalArgs, additional_args: T::AdditionalArgs,
) -> Self { ) -> Self {
Self::new( Self::new(
@ -602,7 +638,7 @@ impl<T: ExternalCommand> ToArgs for ExternalCommandArgs<T> {
} = *self; } = *self;
program_path.to_args(args); program_path.to_args(args);
if run_even_if_cached { if run_even_if_cached {
args.write_arg(format_args!("--{}", T::run_even_if_cached_arg_name())); args.write_display_arg(format_args!("--{}", T::run_even_if_cached_arg_name()));
} }
additional_args.to_args(args); additional_args.to_args(args);
} }
@ -613,13 +649,13 @@ struct ExternalCommandJobParams {
command_params: CommandParams, command_params: CommandParams,
inputs: Interned<[JobItemName]>, inputs: Interned<[JobItemName]>,
outputs: Interned<[JobItemName]>, outputs: Interned<[JobItemName]>,
output_paths: Interned<[Interned<str>]>, output_paths: Interned<[Interned<Path>]>,
} }
impl ExternalCommandJobParams { impl ExternalCommandJobParams {
fn new<T: ExternalCommand>(job: &ExternalCommandJob<T>) -> Self { fn new<T: ExternalCommand>(job: &ExternalCommandJob<T>) -> Self {
let output_paths = T::output_paths(job); let output_paths = T::output_paths(job);
let mut command_line = ArgsWriter(vec![job.program_path]); let mut command_line = ArgsWriter(vec![job.program_path.as_interned_os_str()]);
T::command_line_args(job, &mut command_line); T::command_line_args(job, &mut command_line);
Self { Self {
command_params: CommandParams { command_params: CommandParams {
@ -639,8 +675,8 @@ impl ExternalCommandJobParams {
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct ExternalCommandJob<T: ExternalCommand> { pub struct ExternalCommandJob<T: ExternalCommand> {
additional_job_data: T::AdditionalJobData, additional_job_data: T::AdditionalJobData,
program_path: Interned<str>, program_path: Interned<Path>,
output_dir: Interned<str>, output_dir: Interned<Path>,
run_even_if_cached: bool, run_even_if_cached: bool,
#[serde(skip)] #[serde(skip)]
params_cache: OnceLock<ExternalCommandJobParams>, params_cache: OnceLock<ExternalCommandJobParams>,
@ -722,10 +758,10 @@ impl<T: ExternalCommand> ExternalCommandJob<T> {
pub fn additional_job_data(&self) -> &T::AdditionalJobData { pub fn additional_job_data(&self) -> &T::AdditionalJobData {
&self.additional_job_data &self.additional_job_data
} }
pub fn program_path(&self) -> Interned<str> { pub fn program_path(&self) -> Interned<Path> {
self.program_path self.program_path
} }
pub fn output_dir(&self) -> Interned<str> { pub fn output_dir(&self) -> Interned<Path> {
self.output_dir self.output_dir
} }
pub fn run_even_if_cached(&self) -> bool { pub fn run_even_if_cached(&self) -> bool {
@ -741,7 +777,7 @@ impl<T: ExternalCommand> ExternalCommandJob<T> {
pub fn inputs(&self) -> Interned<[JobItemName]> { pub fn inputs(&self) -> Interned<[JobItemName]> {
self.params().inputs self.params().inputs
} }
pub fn output_paths(&self) -> Interned<[Interned<str>]> { pub fn output_paths(&self) -> Interned<[Interned<Path>]> {
self.params().output_paths self.params().output_paths
} }
pub fn outputs(&self) -> Interned<[JobItemName]> { pub fn outputs(&self) -> Interned<[JobItemName]> {
@ -751,12 +787,12 @@ impl<T: ExternalCommand> ExternalCommandJob<T> {
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ExternalProgramPath<T: ExternalProgramTrait> { pub struct ExternalProgramPath<T: ExternalProgramTrait> {
program_path: Interned<str>, program_path: Interned<Path>,
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: ExternalProgramTrait> ExternalProgramPath<T> { impl<T: ExternalProgramTrait> ExternalProgramPath<T> {
pub fn with_resolved_program_path(program_path: Interned<str>) -> Self { pub fn with_resolved_program_path(program_path: Interned<Path>) -> Self {
Self { Self {
program_path, program_path,
_phantom: PhantomData, _phantom: PhantomData,
@ -780,7 +816,7 @@ impl<T: ExternalProgramTrait> ExternalProgramPath<T> {
_phantom: PhantomData, _phantom: PhantomData,
}) })
} }
pub fn program_path(&self) -> Interned<str> { pub fn program_path(&self) -> Interned<Path> {
self.program_path self.program_path
} }
} }
@ -874,8 +910,8 @@ impl<T: ExternalProgramTrait> ToArgs for ExternalProgramPath<T> {
program_path, program_path,
_phantom: _, _phantom: _,
} = self; } = self;
if args.get_long_option_eq(program_path_arg_name) != Some(&**program_path) { if args.get_long_option_eq(program_path_arg_name) != Some(program_path.as_os_str()) {
args.write_arg(format_args!("--{program_path_arg_name}={program_path}")); args.write_long_option_eq(program_path_arg_name, program_path);
} }
} }
} }
@ -953,7 +989,8 @@ pub trait ExternalCommand: 'static + Send + Sync + Hash + Eq + fmt::Debug + Size
+ fmt::Debug + fmt::Debug
+ Serialize + Serialize
+ DeserializeOwned; + DeserializeOwned;
type Dependencies: JobDependencies<JobsAndKinds: GetBaseJob>; type BaseJobPosition;
type Dependencies: JobDependencies<JobsAndKinds: GetJob<BaseJob, Self::BaseJobPosition>>;
type ExternalProgram: ExternalProgramTrait; type ExternalProgram: ExternalProgramTrait;
fn dependencies() -> Self::Dependencies; fn dependencies() -> Self::Dependencies;
fn args_to_jobs( fn args_to_jobs(
@ -964,9 +1001,9 @@ pub trait ExternalCommand: 'static + Send + Sync + Hash + Eq + fmt::Debug + Size
<Self::Dependencies as JobDependencies>::JobsAndKinds, <Self::Dependencies as JobDependencies>::JobsAndKinds,
)>; )>;
fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]>; fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]>;
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]>; fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]>;
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W); fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W);
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>>; fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>>;
fn job_kind_name() -> Interned<str>; fn job_kind_name() -> Interned<str>;
fn args_group_id() -> clap::Id { fn args_group_id() -> clap::Id {
Interned::into_inner(Self::job_kind_name()).into() Interned::into_inner(Self::job_kind_name()).into()
@ -1006,7 +1043,7 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> {
}, },
} = args.args; } = args.args;
let (additional_job_data, dependencies) = T::args_to_jobs(args, params)?; let (additional_job_data, dependencies) = T::args_to_jobs(args, params)?;
let base_job = dependencies.base_job(); let base_job = GetJob::<BaseJob, _>::get_job(&dependencies);
let job = ExternalCommandJob { let job = ExternalCommandJob {
additional_job_data, additional_job_data,
program_path, program_path,

View file

@ -8,12 +8,12 @@ use crate::{
ToArgs, WriteArgs, ToArgs, WriteArgs,
}, },
firrtl::{ExportOptions, FileBackend}, firrtl::{ExportOptions, FileBackend},
intern::{Intern, Interned}, intern::{Intern, InternSlice, Interned},
util::job_server::AcquiredJob, util::job_server::AcquiredJob,
}; };
use clap::Args; use clap::Args;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::{Path, PathBuf};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
pub struct FirrtlJobKind; pub struct FirrtlJobKind;
@ -43,11 +43,11 @@ impl Firrtl {
fn make_firrtl_file_backend(&self) -> FileBackend { fn make_firrtl_file_backend(&self) -> FileBackend {
FileBackend { FileBackend {
dir_path: PathBuf::from(&*self.base.output_dir()), dir_path: PathBuf::from(&*self.base.output_dir()),
top_fir_file_stem: Some(String::from(&*self.base.file_stem())), top_fir_file_stem: Some(self.base.file_stem().into()),
circuit_name: None, circuit_name: None,
} }
} }
pub fn firrtl_file(&self) -> Interned<str> { pub fn firrtl_file(&self) -> Interned<Path> {
self.base.file_with_ext("fir") self.base.file_with_ext("fir")
} }
} }
@ -69,7 +69,7 @@ impl JobKind for FirrtlJobKind {
params, params,
|_kind, FirrtlArgs { export_options }, dependencies| { |_kind, FirrtlArgs { export_options }, dependencies| {
Ok(Firrtl { Ok(Firrtl {
base: dependencies.job.job.clone(), base: dependencies.get_job::<BaseJob, _>().clone(),
export_options, export_options,
}) })
}, },
@ -79,15 +79,15 @@ impl JobKind for FirrtlJobKind {
fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> { fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
[JobItemName::Path { [JobItemName::Path {
path: job.base.output_dir(), path: job.base.output_dir(),
}][..] }]
.intern() .intern_slice()
} }
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
[JobItemName::Path { [JobItemName::Path {
path: job.firrtl_file(), path: job.firrtl_file(),
}][..] }]
.intern() .intern_slice()
} }
fn name(self) -> Interned<str> { fn name(self) -> Interned<str> {

View file

@ -3,23 +3,26 @@
use crate::{ use crate::{
build::{ build::{
CommandParams, DynJobKind, GetBaseJob, JobAndDependencies, JobArgsAndDependencies, BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, JobAndDependencies,
JobDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind,
WriteArgs, JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
external::{ external::{
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
}, },
interned_known_utf8_method, verilog::{UnadjustedVerilog, VerilogDialect, VerilogJob, VerilogJobKind},
verilog::{VerilogDialect, VerilogJob, VerilogJobKind},
}, },
intern::{Intern, Interned}, intern::{Intern, InternSlice, Interned},
module::NameId, module::NameId,
util::job_server::AcquiredJob, util::job_server::AcquiredJob,
}; };
use clap::{Args, ValueEnum}; use clap::{Args, ValueEnum};
use eyre::Context; use eyre::Context;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::{
ffi::{OsStr, OsString},
fmt::{self, Write},
path::Path,
};
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Default, Deserialize, Serialize)] #[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Default, Deserialize, Serialize)]
#[non_exhaustive] #[non_exhaustive]
@ -52,7 +55,7 @@ impl fmt::Display for FormalMode {
#[non_exhaustive] #[non_exhaustive]
pub struct FormalArgs { pub struct FormalArgs {
#[arg(long = "sby-extra-arg", value_name = "ARG")] #[arg(long = "sby-extra-arg", value_name = "ARG")]
pub sby_extra_args: Vec<String>, pub sby_extra_args: Vec<OsString>,
#[arg(long, default_value_t)] #[arg(long, default_value_t)]
pub formal_mode: FormalMode, pub formal_mode: FormalMode,
#[arg(long, default_value_t = Self::DEFAULT_DEPTH)] #[arg(long, default_value_t = Self::DEFAULT_DEPTH)]
@ -60,7 +63,7 @@ pub struct FormalArgs {
#[arg(long, default_value = Self::DEFAULT_SOLVER)] #[arg(long, default_value = Self::DEFAULT_SOLVER)]
pub formal_solver: String, pub formal_solver: String,
#[arg(long = "smtbmc-extra-arg", value_name = "ARG")] #[arg(long = "smtbmc-extra-arg", value_name = "ARG")]
pub smtbmc_extra_args: Vec<String>, pub smtbmc_extra_args: Vec<OsString>,
} }
impl FormalArgs { impl FormalArgs {
@ -77,21 +80,17 @@ impl ToArgs for FormalArgs {
formal_solver, formal_solver,
smtbmc_extra_args, smtbmc_extra_args,
} = self; } = self;
args.extend( for arg in sby_extra_args {
sby_extra_args args.write_long_option_eq("sby-extra-arg", arg);
.iter() }
.map(|v| format!("--sby-extra-arg={v}")), args.write_display_args([
);
args.extend([
format_args!("--formal-mode={formal_mode}"), format_args!("--formal-mode={formal_mode}"),
format_args!("--formal-depth={formal_depth}"), format_args!("--formal-depth={formal_depth}"),
format_args!("--formal-solver={formal_solver}"), format_args!("--formal-solver={formal_solver}"),
]); ]);
args.extend( for arg in smtbmc_extra_args {
smtbmc_extra_args args.write_long_option_eq("smtbmc-extra-arg", arg);
.iter() }
.map(|v| format!("--smtbmc-extra-arg={v}")),
);
} }
} }
@ -100,18 +99,18 @@ pub struct WriteSbyFileJobKind;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct WriteSbyFileJob { pub struct WriteSbyFileJob {
sby_extra_args: Interned<[Interned<str>]>, sby_extra_args: Interned<[Interned<OsStr>]>,
formal_mode: FormalMode, formal_mode: FormalMode,
formal_depth: u64, formal_depth: u64,
formal_solver: Interned<str>, formal_solver: Interned<str>,
smtbmc_extra_args: Interned<[Interned<str>]>, smtbmc_extra_args: Interned<[Interned<OsStr>]>,
sby_file: Interned<str>, sby_file: Interned<Path>,
output_dir: Interned<str>, output_dir: Interned<Path>,
main_verilog_file: Interned<str>, main_verilog_file: Interned<Path>,
} }
impl WriteSbyFileJob { impl WriteSbyFileJob {
pub fn sby_extra_args(&self) -> Interned<[Interned<str>]> { pub fn sby_extra_args(&self) -> Interned<[Interned<OsStr>]> {
self.sby_extra_args self.sby_extra_args
} }
pub fn formal_mode(&self) -> FormalMode { pub fn formal_mode(&self) -> FormalMode {
@ -123,24 +122,24 @@ impl WriteSbyFileJob {
pub fn formal_solver(&self) -> Interned<str> { pub fn formal_solver(&self) -> Interned<str> {
self.formal_solver self.formal_solver
} }
pub fn smtbmc_extra_args(&self) -> Interned<[Interned<str>]> { pub fn smtbmc_extra_args(&self) -> Interned<[Interned<OsStr>]> {
self.smtbmc_extra_args self.smtbmc_extra_args
} }
pub fn sby_file(&self) -> Interned<str> { pub fn sby_file(&self) -> Interned<Path> {
self.sby_file self.sby_file
} }
pub fn output_dir(&self) -> Interned<str> { pub fn output_dir(&self) -> Interned<Path> {
self.output_dir self.output_dir
} }
pub fn main_verilog_file(&self) -> Interned<str> { pub fn main_verilog_file(&self) -> Interned<Path> {
self.main_verilog_file self.main_verilog_file
} }
fn write_sby<W: ?Sized + fmt::Write>( fn write_sby(
&self, &self,
output: &mut W, output: &mut OsString,
additional_files: &[Interned<str>], additional_files: &[Interned<Path>],
main_module_name_id: NameId, main_module_name_id: NameId,
) -> Result<eyre::Result<()>, fmt::Error> { ) -> eyre::Result<()> {
let Self { let Self {
sby_extra_args: _, sby_extra_args: _,
formal_mode, formal_mode,
@ -160,23 +159,21 @@ impl WriteSbyFileJob {
\n\ \n\
[engines]\n\ [engines]\n\
smtbmc {formal_solver} -- --" smtbmc {formal_solver} -- --"
)?; )
.expect("writing to OsString can't fail");
for i in smtbmc_extra_args { for i in smtbmc_extra_args {
output.write_str(" ")?; output.push(" ");
output.write_str(i)?; output.push(i);
} }
output.write_str( output.push(
"\n\ "\n\
\n\ \n\
[script]\n", [script]\n",
)?; );
let all_verilog_files = for verilog_file in VerilogJob::all_verilog_files(*main_verilog_file, additional_files)? {
match VerilogJob::all_verilog_files(*main_verilog_file, additional_files) { output.push("read_verilog -sv -formal \"");
Ok(v) => v, output.push(verilog_file);
Err(e) => return Ok(Err(e)), output.push("\"\n");
};
for verilog_file in all_verilog_files {
writeln!(output, "read_verilog -sv -formal \"{verilog_file}\"")?;
} }
let circuit_name = crate::firrtl::get_circuit_name(main_module_name_id); let circuit_name = crate::firrtl::get_circuit_name(main_module_name_id);
// workaround for wires disappearing -- set `keep` on all wires // workaround for wires disappearing -- set `keep` on all wires
@ -186,8 +183,9 @@ impl WriteSbyFileJob {
proc\n\ proc\n\
setattr -set keep 1 w:\\*\n\ setattr -set keep 1 w:\\*\n\
prep", prep",
)?; )
Ok(Ok(())) .expect("writing to OsString can't fail");
Ok(())
} }
} }
@ -219,18 +217,16 @@ impl JobKind for WriteSbyFileJobKind {
formal_solver, formal_solver,
smtbmc_extra_args, smtbmc_extra_args,
} = args; } = args;
let base_job = dependencies.get_job::<BaseJob, _>();
Ok(WriteSbyFileJob { Ok(WriteSbyFileJob {
sby_extra_args: sby_extra_args.into_iter().map(str::intern_owned).collect(), sby_extra_args: sby_extra_args.into_iter().map(Interned::from).collect(),
formal_mode, formal_mode,
formal_depth, formal_depth,
formal_solver: str::intern_owned(formal_solver), formal_solver: formal_solver.intern_deref(),
smtbmc_extra_args: smtbmc_extra_args smtbmc_extra_args: smtbmc_extra_args.into_iter().map(Interned::from).collect(),
.into_iter() sby_file: base_job.file_with_ext("sby"),
.map(str::intern_owned) output_dir: base_job.output_dir(),
.collect(), main_verilog_file: dependencies.get_job::<VerilogJob, _>().main_verilog_file(),
sby_file: dependencies.base_job().file_with_ext("sby"),
output_dir: dependencies.base_job().output_dir(),
main_verilog_file: dependencies.job.job.main_verilog_file(),
}) })
}) })
} }
@ -238,12 +234,12 @@ impl JobKind for WriteSbyFileJobKind {
fn inputs(self, _job: &Self::Job) -> Interned<[JobItemName]> { fn inputs(self, _job: &Self::Job) -> Interned<[JobItemName]> {
[JobItemName::DynamicPaths { [JobItemName::DynamicPaths {
source_job_name: VerilogJobKind.name(), source_job_name: VerilogJobKind.name(),
}][..] }]
.intern() .intern_slice()
} }
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
[JobItemName::Path { path: job.sby_file }][..].intern() [JobItemName::Path { path: job.sby_file }].intern_slice()
} }
fn name(self) -> Interned<str> { fn name(self) -> Interned<str> {
@ -266,18 +262,16 @@ impl JobKind for WriteSbyFileJobKind {
unreachable!(); unreachable!();
}; };
let additional_files = VerilogJob::unwrap_additional_files(additional_files); let additional_files = VerilogJob::unwrap_additional_files(additional_files);
let mut contents = String::new(); let mut contents = OsString::new();
match job.write_sby( job.write_sby(
&mut contents, &mut contents,
additional_files, additional_files,
params.main_module().name_id(), params.main_module().name_id(),
) { )?;
Ok(result) => result?, let path = job.sby_file;
Err(fmt::Error) => unreachable!("writing to String can't fail"), std::fs::write(path, contents.as_encoded_bytes())
} .wrap_err_with(|| format!("writing {path:?} failed"))?;
std::fs::write(job.sby_file, contents) Ok(vec![JobItem::Path { path }])
.wrap_err_with(|| format!("writing {} failed", job.sby_file))?;
Ok(vec![JobItem::Path { path: job.sby_file }])
} }
fn subcommand_hidden(self) -> bool { fn subcommand_hidden(self) -> bool {
@ -289,7 +283,7 @@ impl JobKind for WriteSbyFileJobKind {
pub struct Formal { pub struct Formal {
#[serde(flatten)] #[serde(flatten)]
write_sby_file: WriteSbyFileJob, write_sby_file: WriteSbyFileJob,
sby_file_name: Interned<str>, sby_file_name: Interned<OsStr>,
} }
impl fmt::Debug for Formal { impl fmt::Debug for Formal {
@ -342,6 +336,11 @@ impl ToArgs for FormalAdditionalArgs {
impl ExternalCommand for Formal { impl ExternalCommand for Formal {
type AdditionalArgs = FormalAdditionalArgs; type AdditionalArgs = FormalAdditionalArgs;
type AdditionalJobData = Formal; type AdditionalJobData = Formal;
type BaseJobPosition = GetJobPositionDependencies<
GetJobPositionDependencies<
GetJobPositionDependencies<<UnadjustedVerilog as ExternalCommand>::BaseJobPosition>,
>,
>;
type Dependencies = JobKindAndDependencies<WriteSbyFileJobKind>; type Dependencies = JobKindAndDependencies<WriteSbyFileJobKind>;
type ExternalProgram = Symbiyosys; type ExternalProgram = Symbiyosys;
@ -358,11 +357,13 @@ impl ExternalCommand for Formal {
)> { )> {
args.args_to_jobs_external_simple(params, |args, dependencies| { args.args_to_jobs_external_simple(params, |args, dependencies| {
let FormalAdditionalArgs {} = args.additional_args; let FormalAdditionalArgs {} = args.additional_args;
let write_sby_file = dependencies.get_job::<WriteSbyFileJob, _>().clone();
Ok(Formal { Ok(Formal {
write_sby_file: dependencies.job.job.clone(), sby_file_name: write_sby_file
sby_file_name: interned_known_utf8_method(dependencies.job.job.sby_file(), |v| { .sby_file()
v.file_name().expect("known to have file name") .interned_file_name()
}), .expect("known to have file name"),
write_sby_file,
}) })
}) })
} }
@ -378,22 +379,22 @@ impl ExternalCommand for Formal {
JobItemName::DynamicPaths { JobItemName::DynamicPaths {
source_job_name: VerilogJobKind.name(), source_job_name: VerilogJobKind.name(),
}, },
][..] ]
.intern() .intern_slice()
} }
fn output_paths(_job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]> { fn output_paths(_job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> {
Interned::default() Interned::default()
} }
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) { fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
// args.write_str_arg("-j1"); // sby seems not to respect job count in parallel mode // args.write_str_arg("-j1"); // sby seems not to respect job count in parallel mode
args.write_str_arg("-f"); args.write_arg("-f");
args.write_interned_arg(job.additional_job_data().sby_file_name); args.write_interned_arg(job.additional_job_data().sby_file_name);
args.write_interned_args(job.additional_job_data().write_sby_file.sby_extra_args()); args.write_interned_args(job.additional_job_data().write_sby_file.sby_extra_args());
} }
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>> { fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>> {
Some(job.output_dir()) Some(job.output_dir())
} }

View file

@ -16,9 +16,12 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error, ser::Se
use std::{ use std::{
cell::OnceCell, cell::OnceCell,
collections::{BTreeMap, BTreeSet, VecDeque}, collections::{BTreeMap, BTreeSet, VecDeque},
convert::Infallible,
ffi::OsStr,
fmt::{self, Write}, fmt::{self, Write},
panic, panic,
rc::Rc, rc::Rc,
str::Utf8Error,
sync::mpsc, sync::mpsc,
thread::{self, ScopedJoinHandle}, thread::{self, ScopedJoinHandle},
}; };
@ -138,8 +141,8 @@ impl<'a> fmt::Display for EscapeForUnixShell<'a> {
} }
impl<'a> EscapeForUnixShell<'a> { impl<'a> EscapeForUnixShell<'a> {
pub fn new(s: &'a str) -> Self { pub fn new(s: &'a (impl ?Sized + AsRef<OsStr>)) -> Self {
Self::from_bytes(s.as_bytes()) Self::from_bytes(s.as_ref().as_encoded_bytes())
} }
fn make_prefix(bytes: &[u8]) -> [u8; 3] { fn make_prefix(bytes: &[u8]) -> [u8; 3] {
let mut prefix = [0; 3]; let mut prefix = [0; 3];
@ -262,7 +265,7 @@ pub enum UnixMakefileEscapeKind {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct EscapeForUnixMakefile<'a> { pub struct EscapeForUnixMakefile<'a> {
s: &'a str, s: &'a OsStr,
kind: UnixMakefileEscapeKind, kind: UnixMakefileEscapeKind,
} }
@ -274,9 +277,13 @@ impl<'a> fmt::Debug for EscapeForUnixMakefile<'a> {
impl<'a> fmt::Display for EscapeForUnixMakefile<'a> { impl<'a> fmt::Display for EscapeForUnixMakefile<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.do_write(f, fmt::Write::write_str, fmt::Write::write_char, |_, _| { self.do_write(
Ok(()) f,
}) fmt::Write::write_str,
fmt::Write::write_char,
|_, _| Ok(()),
|_| unreachable!("already checked that the input causes no UTF-8 errors"),
)
} }
} }
@ -287,6 +294,7 @@ impl<'a> EscapeForUnixMakefile<'a> {
write_str: impl Fn(&mut S, &str) -> Result<(), E>, write_str: impl Fn(&mut S, &str) -> Result<(), E>,
write_char: impl Fn(&mut S, char) -> Result<(), E>, write_char: impl Fn(&mut S, char) -> Result<(), E>,
add_variable: impl Fn(&mut S, &'static str) -> Result<(), E>, add_variable: impl Fn(&mut S, &'static str) -> Result<(), E>,
utf8_error: impl Fn(Utf8Error) -> E,
) -> Result<(), E> { ) -> Result<(), E> {
let escape_recipe_char = |c| match c { let escape_recipe_char = |c| match c {
'$' => write_str(state, "$$"), '$' => write_str(state, "$$"),
@ -296,24 +304,30 @@ impl<'a> EscapeForUnixMakefile<'a> {
_ => write_char(state, c), _ => write_char(state, c),
}; };
match self.kind { match self.kind {
UnixMakefileEscapeKind::NonRecipe => self.s.chars().try_for_each(|c| match c { UnixMakefileEscapeKind::NonRecipe => str::from_utf8(self.s.as_encoded_bytes())
'=' => { .map_err(&utf8_error)?
add_variable(state, "EQUALS = =")?; .chars()
write_str(state, "$(EQUALS)") .try_for_each(|c| match c {
} '=' => {
';' => panic!("can't escape a semicolon (;) for Unix Makefile"), add_variable(state, "EQUALS = =")?;
'$' => write_str(state, "$$"), write_str(state, "$(EQUALS)")
'\\' | ' ' | '#' | ':' | '%' | '*' | '?' | '[' | ']' | '~' => { }
write_char(state, '\\')?; ';' => panic!("can't escape a semicolon (;) for Unix Makefile"),
write_char(state, c) '$' => write_str(state, "$$"),
} '\\' | ' ' | '#' | ':' | '%' | '*' | '?' | '[' | ']' | '~' => {
'\0'..='\x1F' | '\x7F' => { write_char(state, '\\')?;
panic!("can't escape a control character for Unix Makefile: {c:?}"); write_char(state, c)
} }
_ => write_char(state, c), '\0'..='\x1F' | '\x7F' => {
}), panic!("can't escape a control character for Unix Makefile: {c:?}");
}
_ => write_char(state, c),
}),
UnixMakefileEscapeKind::RecipeWithoutShellEscaping => { UnixMakefileEscapeKind::RecipeWithoutShellEscaping => {
self.s.chars().try_for_each(escape_recipe_char) str::from_utf8(self.s.as_encoded_bytes())
.map_err(&utf8_error)?
.chars()
.try_for_each(escape_recipe_char)
} }
UnixMakefileEscapeKind::RecipeWithShellEscaping => { UnixMakefileEscapeKind::RecipeWithShellEscaping => {
EscapeForUnixShell::new(self.s).try_for_each(escape_recipe_char) EscapeForUnixShell::new(self.s).try_for_each(escape_recipe_char)
@ -321,21 +335,23 @@ impl<'a> EscapeForUnixMakefile<'a> {
} }
} }
pub fn new( pub fn new(
s: &'a str, s: &'a (impl ?Sized + AsRef<OsStr>),
kind: UnixMakefileEscapeKind, kind: UnixMakefileEscapeKind,
needed_variables: &mut BTreeSet<&'static str>, needed_variables: &mut BTreeSet<&'static str>,
) -> Self { ) -> Result<Self, Utf8Error> {
let s = s.as_ref();
let retval = Self { s, kind }; let retval = Self { s, kind };
let Ok(()) = retval.do_write( retval.do_write(
needed_variables, needed_variables,
|_, _| Ok(()), |_, _| Ok(()),
|_, _| Ok(()), |_, _| Ok(()),
|needed_variables, variable| -> Result<(), std::convert::Infallible> { |needed_variables, variable| {
needed_variables.insert(variable); needed_variables.insert(variable);
Ok(()) Ok(())
}, },
); |e| e,
retval )?;
Ok(retval)
} }
} }
@ -473,7 +489,7 @@ impl JobGraph {
Err(e) => panic!("error: {e}"), Err(e) => panic!("error: {e}"),
} }
} }
pub fn to_unix_makefile(&self, extra_args: &[Interned<str>]) -> String { pub fn to_unix_makefile(&self, extra_args: &[Interned<OsStr>]) -> Result<String, Utf8Error> {
self.to_unix_makefile_with_internal_program_prefix( self.to_unix_makefile_with_internal_program_prefix(
&[program_name_for_internal_jobs()], &[program_name_for_internal_jobs()],
extra_args, extra_args,
@ -481,9 +497,9 @@ impl JobGraph {
} }
pub fn to_unix_makefile_with_internal_program_prefix( pub fn to_unix_makefile_with_internal_program_prefix(
&self, &self,
internal_program_prefix: &[Interned<str>], internal_program_prefix: &[Interned<OsStr>],
extra_args: &[Interned<str>], extra_args: &[Interned<OsStr>],
) -> String { ) -> Result<String, Utf8Error> {
let mut retval = String::new(); let mut retval = String::new();
let mut needed_variables = BTreeSet::new(); let mut needed_variables = BTreeSet::new();
let mut phony_targets = BTreeSet::new(); let mut phony_targets = BTreeSet::new();
@ -502,10 +518,10 @@ impl JobGraph {
retval, retval,
"{} ", "{} ",
EscapeForUnixMakefile::new( EscapeForUnixMakefile::new(
&path, &str::from_utf8(path.as_os_str().as_encoded_bytes())?,
UnixMakefileEscapeKind::NonRecipe, UnixMakefileEscapeKind::NonRecipe,
&mut needed_variables &mut needed_variables
) )?
); );
} }
JobItemName::DynamicPaths { source_job_name } => { JobItemName::DynamicPaths { source_job_name } => {
@ -516,7 +532,7 @@ impl JobGraph {
&source_job_name, &source_job_name,
UnixMakefileEscapeKind::NonRecipe, UnixMakefileEscapeKind::NonRecipe,
&mut needed_variables &mut needed_variables
) )?
); );
phony_targets.insert(Interned::into_inner(source_job_name)); phony_targets.insert(Interned::into_inner(source_job_name));
} }
@ -535,10 +551,10 @@ impl JobGraph {
retval, retval,
" {}", " {}",
EscapeForUnixMakefile::new( EscapeForUnixMakefile::new(
&path, &str::from_utf8(path.as_os_str().as_encoded_bytes())?,
UnixMakefileEscapeKind::NonRecipe, UnixMakefileEscapeKind::NonRecipe,
&mut needed_variables &mut needed_variables
) )?
); );
} }
JobItemName::DynamicPaths { source_job_name } => { JobItemName::DynamicPaths { source_job_name } => {
@ -549,7 +565,7 @@ impl JobGraph {
&source_job_name, &source_job_name,
UnixMakefileEscapeKind::NonRecipe, UnixMakefileEscapeKind::NonRecipe,
&mut needed_variables &mut needed_variables
) )?
); );
phony_targets.insert(Interned::into_inner(source_job_name)); phony_targets.insert(Interned::into_inner(source_job_name));
} }
@ -558,17 +574,17 @@ impl JobGraph {
retval.push_str("\n\t"); retval.push_str("\n\t");
job.command_params_with_internal_program_prefix(internal_program_prefix, extra_args) job.command_params_with_internal_program_prefix(internal_program_prefix, extra_args)
.to_unix_shell_line(&mut retval, |arg, output| { .to_unix_shell_line(&mut retval, |arg, output| {
write!( write_str!(
output, output,
"{}", "{}",
EscapeForUnixMakefile::new( EscapeForUnixMakefile::new(
arg, arg,
UnixMakefileEscapeKind::RecipeWithShellEscaping, UnixMakefileEscapeKind::RecipeWithShellEscaping,
&mut needed_variables &mut needed_variables
) )?
) );
}) Ok(())
.expect("writing to String never fails"); })?;
retval.push_str("\n\n"); retval.push_str("\n\n");
} }
if !phony_targets.is_empty() { if !phony_targets.is_empty() {
@ -581,7 +597,7 @@ impl JobGraph {
phony_target, phony_target,
UnixMakefileEscapeKind::NonRecipe, UnixMakefileEscapeKind::NonRecipe,
&mut needed_variables &mut needed_variables
) )?
); );
} }
retval.push_str("\n"); retval.push_str("\n");
@ -592,9 +608,9 @@ impl JobGraph {
&String::from_iter(needed_variables.into_iter().map(|v| format!("{v}\n"))), &String::from_iter(needed_variables.into_iter().map(|v| format!("{v}\n"))),
); );
} }
retval Ok(retval)
} }
pub fn to_unix_shell_script(&self, extra_args: &[Interned<str>]) -> String { pub fn to_unix_shell_script(&self, extra_args: &[Interned<OsStr>]) -> String {
self.to_unix_shell_script_with_internal_program_prefix( self.to_unix_shell_script_with_internal_program_prefix(
&[program_name_for_internal_jobs()], &[program_name_for_internal_jobs()],
extra_args, extra_args,
@ -602,8 +618,8 @@ impl JobGraph {
} }
pub fn to_unix_shell_script_with_internal_program_prefix( pub fn to_unix_shell_script_with_internal_program_prefix(
&self, &self,
internal_program_prefix: &[Interned<str>], internal_program_prefix: &[Interned<OsStr>],
extra_args: &[Interned<str>], extra_args: &[Interned<OsStr>],
) -> String { ) -> String {
let mut retval = String::from( let mut retval = String::from(
"#!/bin/sh\n\ "#!/bin/sh\n\
@ -613,11 +629,12 @@ impl JobGraph {
let JobGraphNode::Job(job) = &self.graph[node_id] else { let JobGraphNode::Job(job) = &self.graph[node_id] else {
continue; continue;
}; };
job.command_params_with_internal_program_prefix(internal_program_prefix, extra_args) let Ok(()) = job
.to_unix_shell_line(&mut retval, |arg, output| { .command_params_with_internal_program_prefix(internal_program_prefix, extra_args)
write!(output, "{}", EscapeForUnixShell::new(&arg)) .to_unix_shell_line(&mut retval, |arg, output| -> Result<(), Infallible> {
}) write_str!(output, "{}", EscapeForUnixShell::new(&arg));
.expect("writing to String never fails"); Ok(())
});
retval.push_str("\n"); retval.push_str("\n");
} }
retval retval

View file

@ -4,18 +4,18 @@
use crate::{ use crate::{
annotations::Annotation, annotations::Annotation,
build::{ build::{
CommandParams, DynJobKind, GetBaseJob, JobAndDependencies, JobArgsAndDependencies, BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, JobAndDependencies,
JobDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, ToArgs, WriteArgs, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind,
JobKindAndDependencies, ToArgs, WriteArgs,
external::{ external::{
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
}, },
interned_known_utf8_method, interned_known_utf8_path_buf_method,
vendor::xilinx::{XdcIOStandardAnnotation, XdcLocationAnnotation}, vendor::xilinx::{XdcIOStandardAnnotation, XdcLocationAnnotation},
verilog::{VerilogDialect, VerilogJob, VerilogJobKind}, verilog::{UnadjustedVerilog, VerilogDialect, VerilogJob, VerilogJobKind},
}, },
bundle::Bundle, bundle::Bundle,
firrtl::{ScalarizedModuleABI, ScalarizedModuleABIAnnotations, ScalarizedModuleABIPort}, firrtl::{ScalarizedModuleABI, ScalarizedModuleABIAnnotations, ScalarizedModuleABIPort},
intern::{Intern, Interned}, intern::{Intern, InternSlice, Interned},
module::{Module, NameId}, module::{Module, NameId},
prelude::JobParams, prelude::JobParams,
util::job_server::AcquiredJob, util::job_server::AcquiredJob,
@ -23,7 +23,12 @@ use crate::{
use clap::ValueEnum; use clap::ValueEnum;
use eyre::Context; use eyre::Context;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fmt, ops::ControlFlow}; use std::{
ffi::{OsStr, OsString},
fmt::{self, Write},
ops::ControlFlow,
path::{Path, PathBuf},
};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
pub struct YosysNextpnrXrayWriteYsFileJobKind; pub struct YosysNextpnrXrayWriteYsFileJobKind;
@ -39,52 +44,52 @@ impl ToArgs for YosysNextpnrXrayWriteYsFileArgs {
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct YosysNextpnrXrayWriteYsFile { pub struct YosysNextpnrXrayWriteYsFile {
main_verilog_file: Interned<str>, main_verilog_file: Interned<Path>,
ys_file: Interned<str>, ys_file: Interned<Path>,
json_file: Interned<str>, json_file: Interned<Path>,
json_file_name: Interned<str>, json_file_name: Interned<OsStr>,
} }
impl YosysNextpnrXrayWriteYsFile { impl YosysNextpnrXrayWriteYsFile {
pub fn main_verilog_file(&self) -> Interned<str> { pub fn main_verilog_file(&self) -> Interned<Path> {
self.main_verilog_file self.main_verilog_file
} }
pub fn ys_file(&self) -> Interned<str> { pub fn ys_file(&self) -> Interned<Path> {
self.ys_file self.ys_file
} }
pub fn json_file(&self) -> Interned<str> { pub fn json_file(&self) -> Interned<Path> {
self.json_file self.json_file
} }
pub fn json_file_name(&self) -> Interned<str> { pub fn json_file_name(&self) -> Interned<OsStr> {
self.json_file_name self.json_file_name
} }
fn write_ys<W: ?Sized + fmt::Write>( fn write_ys(
&self, &self,
output: &mut W, output: &mut OsString,
additional_files: &[Interned<str>], additional_files: &[Interned<Path>],
main_module_name_id: NameId, main_module_name_id: NameId,
) -> Result<eyre::Result<()>, fmt::Error> { ) -> eyre::Result<()> {
let Self { let Self {
main_verilog_file, main_verilog_file,
ys_file: _, ys_file: _,
json_file: _, json_file: _,
json_file_name, json_file_name,
} = self; } = self;
let all_verilog_files = for verilog_file in VerilogJob::all_verilog_files(*main_verilog_file, additional_files)? {
match VerilogJob::all_verilog_files(*main_verilog_file, additional_files) { output.push("read_verilog -sv \"");
Ok(v) => v, output.push(verilog_file);
Err(e) => return Ok(Err(e)), output.push("\"\n");
};
for verilog_file in all_verilog_files {
writeln!(output, "read_verilog -sv \"{verilog_file}\"")?;
} }
let circuit_name = crate::firrtl::get_circuit_name(main_module_name_id); let circuit_name = crate::firrtl::get_circuit_name(main_module_name_id);
writeln!( writeln!(
output, output,
"synth_xilinx -flatten -abc9 -nobram -arch xc7 -top {circuit_name}" "synth_xilinx -flatten -abc9 -nobram -arch xc7 -top {circuit_name}"
)?; )
writeln!(output, "write_json \"{json_file_name}\"")?; .expect("writing to OsString can't fail");
Ok(Ok(())) output.push("write_json \"");
output.push(json_file_name);
output.push("\"\n");
Ok(())
} }
} }
@ -110,14 +115,16 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind {
.get_or_insert(VerilogDialect::Yosys); .get_or_insert(VerilogDialect::Yosys);
args.args_to_jobs_simple(params, |_kind, args, dependencies| { args.args_to_jobs_simple(params, |_kind, args, dependencies| {
let YosysNextpnrXrayWriteYsFileArgs {} = args; let YosysNextpnrXrayWriteYsFileArgs {} = args;
let json_file = dependencies.base_job().file_with_ext("json"); let base_job = dependencies.get_job::<BaseJob, _>();
let verilog_job = dependencies.get_job::<VerilogJob, _>();
let json_file = base_job.file_with_ext("json");
Ok(YosysNextpnrXrayWriteYsFile { Ok(YosysNextpnrXrayWriteYsFile {
main_verilog_file: dependencies.job.job.main_verilog_file(), main_verilog_file: verilog_job.main_verilog_file(),
ys_file: dependencies.base_job().file_with_ext("ys"), ys_file: base_job.file_with_ext("ys"),
json_file, json_file,
json_file_name: interned_known_utf8_method(json_file, |v| { json_file_name: json_file
v.file_name().expect("known to have file name") .interned_file_name()
}), .expect("known to have file name"),
}) })
}) })
} }
@ -125,12 +132,12 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind {
fn inputs(self, _job: &Self::Job) -> Interned<[JobItemName]> { fn inputs(self, _job: &Self::Job) -> Interned<[JobItemName]> {
[JobItemName::DynamicPaths { [JobItemName::DynamicPaths {
source_job_name: VerilogJobKind.name(), source_job_name: VerilogJobKind.name(),
}][..] }]
.intern() .intern_slice()
} }
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
[JobItemName::Path { path: job.ys_file }][..].intern() [JobItemName::Path { path: job.ys_file }].intern_slice()
} }
fn name(self) -> Interned<str> { fn name(self) -> Interned<str> {
@ -153,18 +160,16 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind {
unreachable!(); unreachable!();
}; };
let additional_files = VerilogJob::unwrap_additional_files(additional_files); let additional_files = VerilogJob::unwrap_additional_files(additional_files);
let mut contents = String::new(); let mut contents = OsString::new();
match job.write_ys( job.write_ys(
&mut contents, &mut contents,
additional_files, additional_files,
params.main_module().name_id(), params.main_module().name_id(),
) { )?;
Ok(result) => result?, let path = job.ys_file;
Err(fmt::Error) => unreachable!("writing to String can't fail"), std::fs::write(path, contents.as_encoded_bytes())
} .wrap_err_with(|| format!("writing {path:?} failed"))?;
std::fs::write(job.ys_file, contents) Ok(vec![JobItem::Path { path }])
.wrap_err_with(|| format!("writing {} failed", job.ys_file))?;
Ok(vec![JobItem::Path { path: job.ys_file }])
} }
fn subcommand_hidden(self) -> bool { fn subcommand_hidden(self) -> bool {
@ -185,7 +190,7 @@ impl ToArgs for YosysNextpnrXraySynthArgs {
pub struct YosysNextpnrXraySynth { pub struct YosysNextpnrXraySynth {
#[serde(flatten)] #[serde(flatten)]
write_ys_file: YosysNextpnrXrayWriteYsFile, write_ys_file: YosysNextpnrXrayWriteYsFile,
ys_file_name: Interned<str>, ys_file_name: Interned<OsStr>,
} }
impl fmt::Debug for YosysNextpnrXraySynth { impl fmt::Debug for YosysNextpnrXraySynth {
@ -211,19 +216,19 @@ impl fmt::Debug for YosysNextpnrXraySynth {
} }
impl YosysNextpnrXraySynth { impl YosysNextpnrXraySynth {
pub fn main_verilog_file(&self) -> Interned<str> { pub fn main_verilog_file(&self) -> Interned<Path> {
self.write_ys_file.main_verilog_file() self.write_ys_file.main_verilog_file()
} }
pub fn ys_file(&self) -> Interned<str> { pub fn ys_file(&self) -> Interned<Path> {
self.write_ys_file.ys_file() self.write_ys_file.ys_file()
} }
pub fn ys_file_name(&self) -> Interned<str> { pub fn ys_file_name(&self) -> Interned<OsStr> {
self.ys_file_name self.ys_file_name
} }
pub fn json_file(&self) -> Interned<str> { pub fn json_file(&self) -> Interned<Path> {
self.write_ys_file.json_file() self.write_ys_file.json_file()
} }
pub fn json_file_name(&self) -> Interned<str> { pub fn json_file_name(&self) -> Interned<OsStr> {
self.write_ys_file.json_file_name() self.write_ys_file.json_file_name()
} }
} }
@ -240,6 +245,11 @@ impl ExternalProgramTrait for Yosys {
impl ExternalCommand for YosysNextpnrXraySynth { impl ExternalCommand for YosysNextpnrXraySynth {
type AdditionalArgs = YosysNextpnrXraySynthArgs; type AdditionalArgs = YosysNextpnrXraySynthArgs;
type AdditionalJobData = Self; type AdditionalJobData = Self;
type BaseJobPosition = GetJobPositionDependencies<
GetJobPositionDependencies<
GetJobPositionDependencies<<UnadjustedVerilog as ExternalCommand>::BaseJobPosition>,
>,
>;
type Dependencies = JobKindAndDependencies<YosysNextpnrXrayWriteYsFileJobKind>; type Dependencies = JobKindAndDependencies<YosysNextpnrXrayWriteYsFileJobKind>;
type ExternalProgram = Yosys; type ExternalProgram = Yosys;
@ -258,9 +268,12 @@ impl ExternalCommand for YosysNextpnrXraySynth {
let YosysNextpnrXraySynthArgs {} = args.additional_args; let YosysNextpnrXraySynthArgs {} = args.additional_args;
Ok(Self { Ok(Self {
write_ys_file: dependencies.job.job.clone(), write_ys_file: dependencies.job.job.clone(),
ys_file_name: interned_known_utf8_method(dependencies.job.job.ys_file(), |v| { ys_file_name: dependencies
v.file_name().expect("known to have file name") .job
}), .job
.ys_file()
.interned_file_name()
.expect("known to have file name"),
}) })
}) })
} }
@ -276,20 +289,20 @@ impl ExternalCommand for YosysNextpnrXraySynth {
JobItemName::DynamicPaths { JobItemName::DynamicPaths {
source_job_name: VerilogJobKind.name(), source_job_name: VerilogJobKind.name(),
}, },
][..] ]
.intern() .intern_slice()
} }
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]> { fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> {
[job.additional_job_data().json_file()][..].intern() [job.additional_job_data().json_file()].intern_slice()
} }
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) { fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
args.write_str_arg("-s"); args.write_arg("-s");
args.write_interned_arg(job.additional_job_data().ys_file_name()); args.write_interned_arg(job.additional_job_data().ys_file_name());
} }
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>> { fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>> {
Some(job.output_dir()) Some(job.output_dir())
} }
@ -317,8 +330,8 @@ impl ToArgs for YosysNextpnrXrayWriteXdcFileArgs {
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct YosysNextpnrXrayWriteXdcFile { pub struct YosysNextpnrXrayWriteXdcFile {
firrtl_export_options: crate::firrtl::ExportOptions, firrtl_export_options: crate::firrtl::ExportOptions,
output_dir: Interned<str>, output_dir: Interned<Path>,
xdc_file: Interned<str>, xdc_file: Interned<Path>,
} }
struct WriteXdcContentsError(eyre::Report); struct WriteXdcContentsError(eyre::Report);
@ -424,10 +437,11 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
.export_options; .export_options;
args.args_to_jobs_simple(params, |_kind, args, dependencies| { args.args_to_jobs_simple(params, |_kind, args, dependencies| {
let YosysNextpnrXrayWriteXdcFileArgs {} = args; let YosysNextpnrXrayWriteXdcFileArgs {} = args;
let base_job = dependencies.get_job::<BaseJob, _>();
Ok(YosysNextpnrXrayWriteXdcFile { Ok(YosysNextpnrXrayWriteXdcFile {
firrtl_export_options, firrtl_export_options,
output_dir: dependencies.base_job().output_dir(), output_dir: base_job.output_dir(),
xdc_file: dependencies.base_job().file_with_ext("xdc"), xdc_file: base_job.file_with_ext("xdc"),
}) })
}) })
} }
@ -435,12 +449,12 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> { fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
[JobItemName::Path { [JobItemName::Path {
path: job.output_dir, path: job.output_dir,
}][..] }]
.intern() .intern_slice()
} }
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
[JobItemName::Path { path: job.xdc_file }][..].intern() [JobItemName::Path { path: job.xdc_file }].intern_slice()
} }
fn name(self) -> Interned<str> { fn name(self) -> Interned<str> {
@ -611,7 +625,7 @@ impl fmt::Display for Device {
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
pub struct YosysNextpnrXrayRunNextpnrArgs { pub struct YosysNextpnrXrayRunNextpnrArgs {
#[arg(long, env = "CHIPDB_DIR", value_hint = clap::ValueHint::DirPath)] #[arg(long, env = "CHIPDB_DIR", value_hint = clap::ValueHint::DirPath)]
pub nextpnr_xilinx_chipdb_dir: String, pub nextpnr_xilinx_chipdb_dir: PathBuf,
#[arg(long)] #[arg(long)]
pub device: Device, pub device: Device,
#[arg(long, default_value_t = 0)] #[arg(long, default_value_t = 0)]
@ -625,42 +639,43 @@ impl ToArgs for YosysNextpnrXrayRunNextpnrArgs {
device, device,
nextpnr_xilinx_seed, nextpnr_xilinx_seed,
} = self; } = self;
args.write_args([ args.write_long_option_eq("nextpnr-xilinx-chipdb-dir", nextpnr_xilinx_chipdb_dir);
format_args!("--nextpnr-xilinx-chipdb-dir={nextpnr_xilinx_chipdb_dir}"), args.write_long_option_eq("device", device.as_str());
format_args!("--device={device}"), args.write_display_arg(format_args!("--nextpnr-xilinx-seed={nextpnr_xilinx_seed}"));
format_args!("--nextpnr-xilinx-seed={nextpnr_xilinx_seed}"),
]);
} }
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct YosysNextpnrXrayRunNextpnr { pub struct YosysNextpnrXrayRunNextpnr {
nextpnr_xilinx_chipdb_dir: Interned<str>, nextpnr_xilinx_chipdb_dir: Interned<Path>,
device: Device, device: Device,
nextpnr_xilinx_seed: i32, nextpnr_xilinx_seed: i32,
xdc_file: Interned<str>, xdc_file: Interned<Path>,
xdc_file_name: Interned<str>, xdc_file_name: Interned<OsStr>,
json_file: Interned<str>, json_file: Interned<Path>,
json_file_name: Interned<str>, json_file_name: Interned<OsStr>,
routed_json_file: Interned<str>, routed_json_file: Interned<Path>,
routed_json_file_name: Interned<str>, routed_json_file_name: Interned<OsStr>,
fasm_file: Interned<str>, fasm_file: Interned<Path>,
fasm_file_name: Interned<str>, fasm_file_name: Interned<OsStr>,
} }
impl YosysNextpnrXrayRunNextpnr { impl YosysNextpnrXrayRunNextpnr {
fn chipdb_file(&self) -> Interned<str> { fn chipdb_file(&self) -> Interned<Path> {
interned_known_utf8_path_buf_method(self.nextpnr_xilinx_chipdb_dir, |chipdb_dir| { let mut retval = self
let mut retval = chipdb_dir.join(self.device.xray_device()); .nextpnr_xilinx_chipdb_dir
retval.set_extension("bin"); .join(self.device.xray_device());
retval retval.set_extension("bin");
}) retval.intern_deref()
} }
} }
impl ExternalCommand for YosysNextpnrXrayRunNextpnr { impl ExternalCommand for YosysNextpnrXrayRunNextpnr {
type AdditionalArgs = YosysNextpnrXrayRunNextpnrArgs; type AdditionalArgs = YosysNextpnrXrayRunNextpnrArgs;
type AdditionalJobData = Self; type AdditionalJobData = Self;
type BaseJobPosition = GetJobPositionDependencies<
GetJobPositionDependencies<<YosysNextpnrXraySynth as ExternalCommand>::BaseJobPosition>,
>;
type Dependencies = JobKindAndDependencies<YosysNextpnrXrayWriteXdcFileJobKind>; type Dependencies = JobKindAndDependencies<YosysNextpnrXrayWriteXdcFileJobKind>;
type ExternalProgram = NextpnrXilinx; type ExternalProgram = NextpnrXilinx;
@ -681,37 +696,30 @@ impl ExternalCommand for YosysNextpnrXrayRunNextpnr {
device, device,
nextpnr_xilinx_seed, nextpnr_xilinx_seed,
} = args.additional_args; } = args.additional_args;
let xdc_file = dependencies.job.job.xdc_file; let base_job = dependencies.get_job::<BaseJob, _>();
let routed_json_file = dependencies.base_job().file_with_ext("routed.json"); let write_xdc_file = dependencies.get_job::<YosysNextpnrXrayWriteXdcFile, _>();
let fasm_file = dependencies.base_job().file_with_ext("fasm"); let synth = dependencies.get_job::<ExternalCommandJob<YosysNextpnrXraySynth>, _>();
let routed_json_file = base_job.file_with_ext("routed.json");
let fasm_file = base_job.file_with_ext("fasm");
Ok(Self { Ok(Self {
nextpnr_xilinx_chipdb_dir: str::intern_owned(nextpnr_xilinx_chipdb_dir), nextpnr_xilinx_chipdb_dir: nextpnr_xilinx_chipdb_dir.intern_deref(),
device, device,
nextpnr_xilinx_seed, nextpnr_xilinx_seed,
xdc_file, xdc_file: write_xdc_file.xdc_file,
xdc_file_name: interned_known_utf8_method(xdc_file, |v| { xdc_file_name: write_xdc_file
v.file_name().expect("known to have file name") .xdc_file
}), .interned_file_name()
json_file: dependencies .expect("known to have file name"),
.dependencies json_file: synth.additional_job_data().json_file(),
.job json_file_name: synth.additional_job_data().json_file_name(),
.job
.additional_job_data()
.json_file(),
json_file_name: dependencies
.dependencies
.job
.job
.additional_job_data()
.json_file_name(),
routed_json_file, routed_json_file,
routed_json_file_name: interned_known_utf8_method(routed_json_file, |v| { routed_json_file_name: routed_json_file
v.file_name().expect("known to have file name") .interned_file_name()
}), .expect("known to have file name"),
fasm_file, fasm_file,
fasm_file_name: interned_known_utf8_method(fasm_file, |v| { fasm_file_name: fasm_file
v.file_name().expect("known to have file name") .interned_file_name()
}), .expect("known to have file name"),
}) })
}) })
} }
@ -724,16 +732,16 @@ impl ExternalCommand for YosysNextpnrXrayRunNextpnr {
JobItemName::Path { JobItemName::Path {
path: job.additional_job_data().xdc_file, path: job.additional_job_data().xdc_file,
}, },
][..] ]
.intern() .intern_slice()
} }
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]> { fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> {
[ [
job.additional_job_data().routed_json_file, job.additional_job_data().routed_json_file,
job.additional_job_data().fasm_file, job.additional_job_data().fasm_file,
][..] ]
.intern() .intern_slice()
} }
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) { fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
@ -745,17 +753,15 @@ impl ExternalCommand for YosysNextpnrXrayRunNextpnr {
fasm_file_name, fasm_file_name,
.. ..
} = job.additional_job_data(); } = job.additional_job_data();
args.write_args([ args.write_long_option_eq("chipdb", job_data.chipdb_file());
format_args!("--chipdb={}", job_data.chipdb_file()), args.write_long_option_eq("xdc", xdc_file_name);
format_args!("--xdc={xdc_file_name}"), args.write_long_option_eq("json", json_file_name);
format_args!("--json={json_file_name}"), args.write_long_option_eq("write", routed_json_file_name);
format_args!("--write={routed_json_file_name}"), args.write_long_option_eq("fasm", fasm_file_name);
format_args!("--fasm={fasm_file_name}"), args.write_display_arg(format_args!("--seed={nextpnr_xilinx_seed}"));
format_args!("--seed={nextpnr_xilinx_seed}"),
]);
} }
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>> { fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>> {
Some(job.output_dir()) Some(job.output_dir())
} }
@ -780,47 +786,48 @@ impl ExternalProgramTrait for Xcfasm {
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
pub struct YosysNextpnrXrayArgs { pub struct YosysNextpnrXrayArgs {
#[arg(long, env = "DB_DIR", value_hint = clap::ValueHint::DirPath)] #[arg(long, env = "DB_DIR", value_hint = clap::ValueHint::DirPath)]
pub prjxray_db_dir: String, pub prjxray_db_dir: PathBuf,
} }
impl ToArgs for YosysNextpnrXrayArgs { impl ToArgs for YosysNextpnrXrayArgs {
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
let Self { prjxray_db_dir } = self; let Self { prjxray_db_dir } = self;
args.write_arg(format_args!("--prjxray-db-dir={prjxray_db_dir}")); args.write_long_option_eq("prjxray-db-dir", prjxray_db_dir);
} }
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct YosysNextpnrXray { pub struct YosysNextpnrXray {
prjxray_db_dir: Interned<str>, prjxray_db_dir: Interned<Path>,
device: Device, device: Device,
fasm_file: Interned<str>, fasm_file: Interned<Path>,
fasm_file_name: Interned<str>, fasm_file_name: Interned<OsStr>,
frames_file: Interned<str>, frames_file: Interned<Path>,
frames_file_name: Interned<str>, frames_file_name: Interned<OsStr>,
bit_file: Interned<str>, bit_file: Interned<Path>,
bit_file_name: Interned<str>, bit_file_name: Interned<OsStr>,
} }
impl YosysNextpnrXray { impl YosysNextpnrXray {
fn db_root(&self) -> Interned<str> { fn db_root(&self) -> Interned<Path> {
interned_known_utf8_path_buf_method(self.prjxray_db_dir, |prjxray_db_dir| { self.prjxray_db_dir
prjxray_db_dir.join(self.device.xray_family()) .join(self.device.xray_family())
}) .intern_deref()
} }
fn part_file(&self) -> Interned<str> { fn part_file(&self) -> Interned<Path> {
interned_known_utf8_path_buf_method(self.prjxray_db_dir, |prjxray_db_dir| { let mut retval = self.prjxray_db_dir.join(self.device.xray_family());
let mut retval = prjxray_db_dir.join(self.device.xray_family()); retval.push(self.device.xray_part());
retval.push(self.device.xray_part()); retval.push("part.yaml");
retval.push("part.yaml"); retval.intern_deref()
retval
})
} }
} }
impl ExternalCommand for YosysNextpnrXray { impl ExternalCommand for YosysNextpnrXray {
type AdditionalArgs = YosysNextpnrXrayArgs; type AdditionalArgs = YosysNextpnrXrayArgs;
type AdditionalJobData = Self; type AdditionalJobData = Self;
type BaseJobPosition = GetJobPositionDependencies<
<YosysNextpnrXrayRunNextpnr as ExternalCommand>::BaseJobPosition,
>;
type Dependencies = JobKindAndDependencies<ExternalCommandJobKind<YosysNextpnrXrayRunNextpnr>>; type Dependencies = JobKindAndDependencies<ExternalCommandJobKind<YosysNextpnrXrayRunNextpnr>>;
type ExternalProgram = Xcfasm; type ExternalProgram = Xcfasm;
@ -837,21 +844,22 @@ impl ExternalCommand for YosysNextpnrXray {
)> { )> {
args.args_to_jobs_external_simple(params, |args, dependencies| { args.args_to_jobs_external_simple(params, |args, dependencies| {
let YosysNextpnrXrayArgs { prjxray_db_dir } = args.additional_args; let YosysNextpnrXrayArgs { prjxray_db_dir } = args.additional_args;
let frames_file = dependencies.base_job().file_with_ext("frames"); let base_job = dependencies.get_job::<BaseJob, _>();
let bit_file = dependencies.base_job().file_with_ext("bit"); let frames_file = base_job.file_with_ext("frames");
let bit_file = base_job.file_with_ext("bit");
Ok(Self { Ok(Self {
prjxray_db_dir: str::intern_owned(prjxray_db_dir), prjxray_db_dir: prjxray_db_dir.intern_deref(),
device: dependencies.job.job.additional_job_data().device, device: dependencies.job.job.additional_job_data().device,
fasm_file: dependencies.job.job.additional_job_data().fasm_file, fasm_file: dependencies.job.job.additional_job_data().fasm_file,
fasm_file_name: dependencies.job.job.additional_job_data().fasm_file_name, fasm_file_name: dependencies.job.job.additional_job_data().fasm_file_name,
frames_file, frames_file,
frames_file_name: interned_known_utf8_method(frames_file, |v| { frames_file_name: frames_file
v.file_name().expect("known to have file name") .interned_file_name()
}), .expect("known to have file name"),
bit_file, bit_file,
bit_file_name: interned_known_utf8_method(bit_file, |v| { bit_file_name: bit_file
v.file_name().expect("known to have file name") .interned_file_name()
}), .expect("known to have file name"),
}) })
}) })
} }
@ -859,16 +867,16 @@ impl ExternalCommand for YosysNextpnrXray {
fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]> { fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]> {
[JobItemName::Path { [JobItemName::Path {
path: job.additional_job_data().fasm_file, path: job.additional_job_data().fasm_file,
}][..] }]
.intern() .intern_slice()
} }
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]> { fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> {
[ [
job.additional_job_data().frames_file, job.additional_job_data().frames_file,
job.additional_job_data().bit_file, job.additional_job_data().bit_file,
][..] ]
.intern() .intern_slice()
} }
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) { fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
@ -879,18 +887,16 @@ impl ExternalCommand for YosysNextpnrXray {
bit_file_name, bit_file_name,
.. ..
} = job.additional_job_data(); } = job.additional_job_data();
args.write_args([ args.write_arg("--sparse");
format_args!("--sparse"), args.write_long_option_eq("db-root", job_data.db_root());
format_args!("--db-root={}", job_data.db_root()), args.write_long_option_eq("part", device.xray_part());
format_args!("--part={}", device.xray_part()), args.write_long_option_eq("part_file", job_data.part_file());
format_args!("--part_file={}", job_data.part_file()), args.write_long_option_eq("fn_in", fasm_file_name);
format_args!("--fn_in={fasm_file_name}"), args.write_long_option_eq("frm_out", frames_file_name);
format_args!("--frm_out={frames_file_name}"), args.write_long_option_eq("bit_out", bit_file_name);
format_args!("--bit_out={bit_file_name}"),
]);
} }
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>> { fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>> {
Some(job.output_dir()) Some(job.output_dir())
} }

View file

@ -3,22 +3,25 @@
use crate::{ use crate::{
build::{ build::{
CommandParams, DynJobKind, GetBaseJob, JobAndDependencies, JobArgsAndDependencies, BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GetJobPositionJob,
JobDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind,
WriteArgs, JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
external::{ external::{
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
}, },
firrtl::FirrtlJobKind, firrtl::{Firrtl, FirrtlJobKind},
interned_known_utf8_method, interned_known_utf8_path_buf_method,
}, },
intern::{Intern, Interned}, intern::{Intern, InternSlice, Interned},
util::job_server::AcquiredJob, util::job_server::AcquiredJob,
}; };
use clap::Args; use clap::Args;
use eyre::{Context, bail}; use eyre::{Context, bail};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fmt, mem}; use std::{
ffi::{OsStr, OsString},
fmt, mem,
path::Path,
};
/// based on [LLVM Circt's recommended lowering options][lowering-options] /// based on [LLVM Circt's recommended lowering options][lowering-options]
/// ///
@ -70,7 +73,7 @@ impl VerilogDialect {
#[non_exhaustive] #[non_exhaustive]
pub struct UnadjustedVerilogArgs { pub struct UnadjustedVerilogArgs {
#[arg(long = "firtool-extra-arg", value_name = "ARG")] #[arg(long = "firtool-extra-arg", value_name = "ARG")]
pub firtool_extra_args: Vec<String>, pub firtool_extra_args: Vec<OsString>,
/// adapt the generated Verilog for a particular toolchain /// adapt the generated Verilog for a particular toolchain
#[arg(long)] #[arg(long)]
pub verilog_dialect: Option<VerilogDialect>, pub verilog_dialect: Option<VerilogDialect>,
@ -85,16 +88,14 @@ impl ToArgs for UnadjustedVerilogArgs {
verilog_dialect, verilog_dialect,
verilog_debug, verilog_debug,
} = *self; } = *self;
args.extend( for arg in firtool_extra_args {
firtool_extra_args args.write_long_option_eq("firtool-extra-arg", arg);
.iter() }
.map(|arg| format!("--firtool-extra-arg={arg}")),
);
if let Some(verilog_dialect) = verilog_dialect { if let Some(verilog_dialect) = verilog_dialect {
args.write_arg(format_args!("--verilog-dialect={verilog_dialect}")); args.write_long_option_eq("verilog-dialect", verilog_dialect.as_str());
} }
if verilog_debug { if verilog_debug {
args.write_str_arg("--verilog-debug"); args.write_arg("--verilog-debug");
} }
} }
} }
@ -110,23 +111,23 @@ impl ExternalProgramTrait for Firtool {
#[derive(Clone, PartialEq, Eq, Hash, Debug, Deserialize, Serialize)] #[derive(Clone, PartialEq, Eq, Hash, Debug, Deserialize, Serialize)]
pub struct UnadjustedVerilog { pub struct UnadjustedVerilog {
firrtl_file: Interned<str>, firrtl_file: Interned<Path>,
firrtl_file_name: Interned<str>, firrtl_file_name: Interned<OsStr>,
unadjusted_verilog_file: Interned<str>, unadjusted_verilog_file: Interned<Path>,
unadjusted_verilog_file_name: Interned<str>, unadjusted_verilog_file_name: Interned<OsStr>,
firtool_extra_args: Interned<[Interned<str>]>, firtool_extra_args: Interned<[Interned<OsStr>]>,
verilog_dialect: Option<VerilogDialect>, verilog_dialect: Option<VerilogDialect>,
verilog_debug: bool, verilog_debug: bool,
} }
impl UnadjustedVerilog { impl UnadjustedVerilog {
pub fn firrtl_file(&self) -> Interned<str> { pub fn firrtl_file(&self) -> Interned<Path> {
self.firrtl_file self.firrtl_file
} }
pub fn unadjusted_verilog_file(&self) -> Interned<str> { pub fn unadjusted_verilog_file(&self) -> Interned<Path> {
self.unadjusted_verilog_file self.unadjusted_verilog_file
} }
pub fn firtool_extra_args(&self) -> Interned<[Interned<str>]> { pub fn firtool_extra_args(&self) -> Interned<[Interned<OsStr>]> {
self.firtool_extra_args self.firtool_extra_args
} }
pub fn verilog_dialect(&self) -> Option<VerilogDialect> { pub fn verilog_dialect(&self) -> Option<VerilogDialect> {
@ -140,6 +141,7 @@ impl UnadjustedVerilog {
impl ExternalCommand for UnadjustedVerilog { impl ExternalCommand for UnadjustedVerilog {
type AdditionalArgs = UnadjustedVerilogArgs; type AdditionalArgs = UnadjustedVerilogArgs;
type AdditionalJobData = UnadjustedVerilog; type AdditionalJobData = UnadjustedVerilog;
type BaseJobPosition = GetJobPositionDependencies<GetJobPositionJob>;
type Dependencies = JobKindAndDependencies<FirrtlJobKind>; type Dependencies = JobKindAndDependencies<FirrtlJobKind>;
type ExternalProgram = Firtool; type ExternalProgram = Firtool;
@ -165,21 +167,18 @@ impl ExternalCommand for UnadjustedVerilog {
.job .job
.job .job
.file_with_ext("unadjusted.v"); .file_with_ext("unadjusted.v");
let firrtl_job = dependencies.get_job::<Firrtl, _>();
Ok(UnadjustedVerilog { Ok(UnadjustedVerilog {
firrtl_file: dependencies.job.job.firrtl_file(), firrtl_file: firrtl_job.firrtl_file(),
firrtl_file_name: interned_known_utf8_method( firrtl_file_name: firrtl_job
dependencies.job.job.firrtl_file(), .firrtl_file()
|v| v.file_name().expect("known to have file name"), .interned_file_name()
), .expect("known to have file name"),
unadjusted_verilog_file, unadjusted_verilog_file,
unadjusted_verilog_file_name: interned_known_utf8_method( unadjusted_verilog_file_name: unadjusted_verilog_file
unadjusted_verilog_file, .interned_file_name()
|v| v.file_name().expect("known to have file name"), .expect("known to have file name"),
), firtool_extra_args: firtool_extra_args.into_iter().map(Interned::from).collect(),
firtool_extra_args: firtool_extra_args
.into_iter()
.map(str::intern_owned)
.collect(),
verilog_dialect, verilog_dialect,
verilog_debug, verilog_debug,
}) })
@ -189,12 +188,12 @@ impl ExternalCommand for UnadjustedVerilog {
fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]> { fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]> {
[JobItemName::Path { [JobItemName::Path {
path: job.additional_job_data().firrtl_file, path: job.additional_job_data().firrtl_file,
}][..] }]
.intern() .intern_slice()
} }
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]> { fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> {
[job.additional_job_data().unadjusted_verilog_file][..].intern() [job.additional_job_data().unadjusted_verilog_file].intern_slice()
} }
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) { fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
@ -208,18 +207,18 @@ impl ExternalCommand for UnadjustedVerilog {
verilog_debug, verilog_debug,
} = *job.additional_job_data(); } = *job.additional_job_data();
args.write_interned_arg(firrtl_file_name); args.write_interned_arg(firrtl_file_name);
args.write_str_arg("-o"); args.write_arg("-o");
args.write_interned_arg(unadjusted_verilog_file_name); args.write_interned_arg(unadjusted_verilog_file_name);
if verilog_debug { if verilog_debug {
args.write_str_args(["-g", "--preserve-values=all"]); args.write_args(["-g", "--preserve-values=all"]);
} }
if let Some(dialect) = verilog_dialect { if let Some(dialect) = verilog_dialect {
args.write_str_args(dialect.firtool_extra_args().iter().copied()); args.write_args(dialect.firtool_extra_args().iter().copied());
} }
args.write_interned_args(firtool_extra_args); args.write_interned_args(firtool_extra_args);
} }
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>> { fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>> {
Some(job.output_dir()) Some(job.output_dir())
} }
@ -251,23 +250,23 @@ impl ToArgs for VerilogJobArgs {
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct VerilogJob { pub struct VerilogJob {
output_dir: Interned<str>, output_dir: Interned<Path>,
unadjusted_verilog_file: Interned<str>, unadjusted_verilog_file: Interned<Path>,
main_verilog_file: Interned<str>, main_verilog_file: Interned<Path>,
} }
impl VerilogJob { impl VerilogJob {
pub fn output_dir(&self) -> Interned<str> { pub fn output_dir(&self) -> Interned<Path> {
self.output_dir self.output_dir
} }
pub fn unadjusted_verilog_file(&self) -> Interned<str> { pub fn unadjusted_verilog_file(&self) -> Interned<Path> {
self.unadjusted_verilog_file self.unadjusted_verilog_file
} }
pub fn main_verilog_file(&self) -> Interned<str> { pub fn main_verilog_file(&self) -> Interned<Path> {
self.main_verilog_file self.main_verilog_file
} }
#[track_caller] #[track_caller]
pub fn unwrap_additional_files(additional_files: &JobItem) -> &[Interned<str>] { pub fn unwrap_additional_files(additional_files: &JobItem) -> &[Interned<Path>] {
match additional_files { match additional_files {
JobItem::DynamicPaths { JobItem::DynamicPaths {
paths, paths,
@ -277,31 +276,31 @@ impl VerilogJob {
} }
} }
pub fn all_verilog_files( pub fn all_verilog_files(
main_verilog_file: Interned<str>, main_verilog_file: Interned<Path>,
additional_files: &[Interned<str>], additional_files: &[Interned<Path>],
) -> eyre::Result<Interned<[Interned<str>]>> { ) -> eyre::Result<Interned<[Interned<Path>]>> {
let mut retval = Vec::with_capacity(additional_files.len().saturating_add(1)); let mut retval = Vec::with_capacity(additional_files.len().saturating_add(1));
for verilog_file in [main_verilog_file].iter().chain(additional_files) { for verilog_file in [main_verilog_file].iter().chain(additional_files) {
if !(verilog_file.ends_with(".v") || verilog_file.ends_with(".sv")) { if !["v", "sv"]
.iter()
.any(|extension| verilog_file.extension() == Some(extension.as_ref()))
{
continue; continue;
} }
let verilog_file = std::path::absolute(verilog_file) let verilog_file = std::path::absolute(verilog_file).wrap_err_with(|| {
.and_then(|v| { format!("converting {verilog_file:?} to an absolute path failed")
v.into_os_string().into_string().map_err(|_| { })?;
std::io::Error::new(std::io::ErrorKind::Other, "path is not valid UTF-8") if verilog_file
}) .as_os_str()
}) .as_encoded_bytes()
.wrap_err_with(|| { .iter()
format!("converting {verilog_file:?} to an absolute path failed") .any(|&ch| (ch != b' ' && ch != b'\t' && ch.is_ascii_whitespace()) || ch == b'"')
})?; {
if verilog_file.contains(|ch: char| {
(ch != ' ' && ch != '\t' && ch.is_ascii_whitespace()) || ch == '"'
}) {
bail!("verilog file path contains characters that aren't permitted"); bail!("verilog file path contains characters that aren't permitted");
} }
retval.push(str::intern_owned(verilog_file)); retval.push(verilog_file.intern_deref());
} }
Ok(Intern::intern_owned(retval)) Ok(retval.intern_slice())
} }
} }
@ -320,14 +319,15 @@ impl JobKind for VerilogJobKind {
) -> eyre::Result<JobAndDependencies<Self>> { ) -> eyre::Result<JobAndDependencies<Self>> {
args.args_to_jobs_simple(params, |_kind, args, dependencies| { args.args_to_jobs_simple(params, |_kind, args, dependencies| {
let VerilogJobArgs {} = args; let VerilogJobArgs {} = args;
let base_job = dependencies.get_job::<BaseJob, _>();
Ok(VerilogJob { Ok(VerilogJob {
output_dir: dependencies.base_job().output_dir(), output_dir: base_job.output_dir(),
unadjusted_verilog_file: dependencies unadjusted_verilog_file: dependencies
.job .job
.job .job
.additional_job_data() .additional_job_data()
.unadjusted_verilog_file(), .unadjusted_verilog_file(),
main_verilog_file: dependencies.base_job().file_with_ext("v"), main_verilog_file: base_job.file_with_ext("v"),
}) })
}) })
} }
@ -335,8 +335,8 @@ impl JobKind for VerilogJobKind {
fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> { fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
[JobItemName::Path { [JobItemName::Path {
path: job.unadjusted_verilog_file, path: job.unadjusted_verilog_file,
}][..] }]
.intern() .intern_slice()
} }
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
@ -347,8 +347,8 @@ impl JobKind for VerilogJobKind {
JobItemName::DynamicPaths { JobItemName::DynamicPaths {
source_job_name: self.name(), source_job_name: self.name(),
}, },
][..] ]
.intern() .intern_slice()
} }
fn name(self) -> Interned<str> { fn name(self) -> Interned<str> {
@ -384,8 +384,7 @@ impl JobKind for VerilogJobKind {
); );
}; };
input = rest; input = rest;
let next_file_name = let next_file_name = job.output_dir.join(next_file_name).intern_deref();
interned_known_utf8_path_buf_method(job.output_dir, |v| v.join(next_file_name));
additional_outputs.push(next_file_name); additional_outputs.push(next_file_name);
(chunk, Some(next_file_name)) (chunk, Some(next_file_name))
} else { } else {

View file

@ -7,7 +7,7 @@ use crate::{
ops::{ArrayLiteral, BundleLiteral, ExprPartialEq}, ops::{ArrayLiteral, BundleLiteral, ExprPartialEq},
}, },
int::{Bool, DynSize}, int::{Bool, DynSize},
intern::{Intern, Interned}, intern::{Intern, InternSlice, Interned},
sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
@ -549,7 +549,7 @@ macro_rules! impl_tuples {
type FilledBuilder = TupleBuilder<($(Expr<$T>,)*)>; type FilledBuilder = TupleBuilder<($(Expr<$T>,)*)>;
fn fields(&self) -> Interned<[BundleField]> { fn fields(&self) -> Interned<[BundleField]> {
let ($($var,)*) = self; let ($($var,)*) = self;
[$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*][..].intern() [$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*].intern_slice()
} }
} }
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) { impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) {
@ -580,7 +580,7 @@ macro_rules! impl_tuples {
$(let $var = $var.to_expr();)* $(let $var = $var.to_expr();)*
let ty = ($(Expr::ty($var),)*); let ty = ($(Expr::ty($var),)*);
let field_values = [$(Expr::canonical($var)),*]; let field_values = [$(Expr::canonical($var)),*];
BundleLiteral::new(ty, field_values[..].intern()).to_expr() BundleLiteral::new(ty, field_values.intern_slice()).to_expr()
} }
} }
impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> { impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> {
@ -590,7 +590,7 @@ macro_rules! impl_tuples {
let ($($var,)*) = self.0; let ($($var,)*) = self.0;
let ty = ($(Expr::ty($var),)*); let ty = ($(Expr::ty($var),)*);
let field_values = [$(Expr::canonical($var)),*]; let field_values = [$(Expr::canonical($var)),*];
BundleLiteral::new(ty, field_values[..].intern()).to_expr() BundleLiteral::new(ty, field_values.intern_slice()).to_expr()
} }
} }
impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<CanonicalType> for ($($T,)*) { impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<CanonicalType> for ($($T,)*) {

View file

@ -49,6 +49,7 @@ use std::{
cmp::Ordering, cmp::Ordering,
collections::{BTreeMap, VecDeque}, collections::{BTreeMap, VecDeque},
error::Error, error::Error,
ffi::OsString,
fmt::{self, Write}, fmt::{self, Write},
fs, fs,
hash::Hash, hash::Hash,
@ -2452,7 +2453,7 @@ impl<T: ?Sized + FileBackendTrait> FileBackendTrait for &'_ mut T {
pub struct FileBackend { pub struct FileBackend {
pub dir_path: PathBuf, pub dir_path: PathBuf,
pub circuit_name: Option<String>, pub circuit_name: Option<String>,
pub top_fir_file_stem: Option<String>, pub top_fir_file_stem: Option<OsString>,
} }
impl FileBackend { impl FileBackend {
@ -2501,7 +2502,7 @@ impl FileBackendTrait for FileBackend {
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
let top_fir_file_stem = self let top_fir_file_stem = self
.top_fir_file_stem .top_fir_file_stem
.get_or_insert_with(|| circuit_name.clone()); .get_or_insert_with(|| circuit_name.clone().into());
self.circuit_name = Some(circuit_name); self.circuit_name = Some(circuit_name);
let mut path = self.dir_path.join(top_fir_file_stem); let mut path = self.dir_path.join(top_fir_file_stem);
if let Some(parent) = path.parent().filter(|v| !v.as_os_str().is_empty()) { if let Some(parent) = path.parent().filter(|v| !v.as_os_str().is_empty()) {
@ -2777,7 +2778,7 @@ impl ToArgs for ExportOptions {
__private: ExportOptionsPrivate(()), __private: ExportOptionsPrivate(()),
} = *self; } = *self;
if !simplify_memories { if !simplify_memories {
args.write_str_arg("--no-simplify-memories"); args.write_arg("--no-simplify-memories");
} }
let simplify_enums = simplify_enums.map(|v| { let simplify_enums = simplify_enums.map(|v| {
clap::ValueEnum::to_possible_value(&v).expect("there are no skipped variants") clap::ValueEnum::to_possible_value(&v).expect("there are no skipped variants")
@ -2786,7 +2787,7 @@ impl ToArgs for ExportOptions {
None => OptionSimplifyEnumsKindValueParser::NONE_NAME, None => OptionSimplifyEnumsKindValueParser::NONE_NAME,
Some(v) => v.get_name(), Some(v) => v.get_name(),
}; };
args.write_arg(format_args!("--simplify-enums={simplify_enums}")); args.write_long_option_eq("simplify-enums", simplify_enums);
} }
} }

View file

@ -8,7 +8,7 @@ use crate::{
ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd}, ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd},
}, },
int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType}, int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType},
intern::{Intern, Interned}, intern::{Intern, InternSlice, Interned},
phantom_const::PhantomConst, phantom_const::PhantomConst,
sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType}, sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType},
source_location::SourceLocation, source_location::SourceLocation,
@ -112,8 +112,8 @@ impl BundleType for UIntInRangeMaskType {
flipped: false, flipped: false,
ty: range.canonical(), ty: range.canonical(),
}, },
][..] ]
.intern() .intern_slice()
} }
} }
@ -409,8 +409,8 @@ macro_rules! define_uint_in_range_type {
flipped: false, flipped: false,
ty: range.canonical(), ty: range.canonical(),
}, },
][..] ]
.intern() .intern_slice()
} }
} }

View file

@ -9,13 +9,13 @@ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
borrow::{Borrow, Cow}, borrow::{Borrow, Cow},
cmp::Ordering, cmp::Ordering,
ffi::OsStr, ffi::{OsStr, OsString},
fmt, fmt,
hash::{BuildHasher, Hash, Hasher}, hash::{BuildHasher, Hash, Hasher},
iter::FusedIterator, iter::FusedIterator,
marker::PhantomData, marker::PhantomData,
ops::Deref, ops::Deref,
path::Path, path::{Path, PathBuf},
sync::{Mutex, RwLock}, sync::{Mutex, RwLock},
}; };
@ -289,15 +289,266 @@ impl InternedCompare for BitSlice {
} }
} }
impl InternedCompare for str { /// Safety: `as_bytes` and `from_bytes_unchecked` must return the same pointer as the input.
type InternedCompareKey = PtrEqWithMetadata<Self>; /// all values returned by `as_bytes` must be valid to pass to `from_bytes_unchecked`.
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { /// `into_bytes` must return the exact same thing as `as_bytes`.
PtrEqWithMetadata(this) /// `Interned<Self>` must contain the exact same references as `Interned<[u8]>`,
/// so they can be safely interconverted without needing re-interning.
unsafe trait InternStrLike: ToOwned {
fn as_bytes(this: &Self) -> &[u8];
fn into_bytes(this: Self::Owned) -> Vec<u8>;
/// Safety: `bytes` must be a valid sequence of bytes for this type. All UTF-8 sequences are valid.
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self;
}
macro_rules! impl_intern_str_like {
($ty:ty, owned = $Owned:ty) => {
impl InternedCompare for $ty {
type InternedCompareKey = PtrEqWithMetadata<[u8]>;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
PtrEqWithMetadata(InternStrLike::as_bytes(this))
}
}
impl Intern for $ty {
fn intern(&self) -> Interned<Self> {
Self::intern_cow(Cow::Borrowed(self))
}
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self> {
Interned::cast_unchecked(
<[u8]>::intern_cow(match this {
Cow::Borrowed(v) => Cow::Borrowed(<Self as InternStrLike>::as_bytes(v)),
Cow::Owned(v) => {
// verify $Owned is correct
let v: $Owned = v;
Cow::Owned(<Self as InternStrLike>::into_bytes(v))
}
}),
// Safety: guaranteed safe because we got the bytes from `as_bytes`/`into_bytes`
|v| unsafe { <Self as InternStrLike>::from_bytes_unchecked(v) },
)
}
}
impl Default for Interned<$ty> {
fn default() -> Self {
// Safety: safe because the empty sequence is valid UTF-8
unsafe { <$ty as InternStrLike>::from_bytes_unchecked(&[]) }.intern()
}
}
impl<'de> Deserialize<'de> for Interned<$ty> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Cow::<'de, $ty>::deserialize(deserializer).map(Intern::intern_cow)
}
}
impl From<$Owned> for Interned<$ty> {
fn from(v: $Owned) -> Self {
v.intern_deref()
}
}
impl From<Interned<$ty>> for $Owned {
fn from(v: Interned<$ty>) -> Self {
Interned::into_inner(v).into()
}
}
impl From<Interned<$ty>> for Box<$ty> {
fn from(v: Interned<$ty>) -> Self {
Interned::into_inner(v).into()
}
}
};
}
// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `str`
unsafe impl InternStrLike for str {
fn as_bytes(this: &Self) -> &[u8] {
this.as_bytes()
}
fn into_bytes(this: Self::Owned) -> Vec<u8> {
this.into_bytes()
}
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
// Safety: `bytes` is guaranteed UTF-8 by the caller
unsafe { str::from_utf8_unchecked(bytes) }
}
}
impl_intern_str_like!(str, owned = String);
// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `OsStr`
unsafe impl InternStrLike for OsStr {
fn as_bytes(this: &Self) -> &[u8] {
this.as_encoded_bytes()
}
fn into_bytes(this: Self::Owned) -> Vec<u8> {
this.into_encoded_bytes()
}
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
// Safety: `bytes` is guaranteed valid for `OsStr` by the caller
unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }
}
}
impl_intern_str_like!(OsStr, owned = OsString);
// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `OsStr`
unsafe impl InternStrLike for Path {
fn as_bytes(this: &Self) -> &[u8] {
this.as_os_str().as_encoded_bytes()
}
fn into_bytes(this: Self::Owned) -> Vec<u8> {
this.into_os_string().into_encoded_bytes()
}
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
// Safety: `bytes` is guaranteed valid for `OsStr` by the caller
unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(bytes)) }
}
}
impl_intern_str_like!(Path, owned = PathBuf);
impl Interned<str> {
pub fn from_utf8(v: Interned<[u8]>) -> Result<Self, std::str::Utf8Error> {
Interned::try_cast_unchecked(v, str::from_utf8)
}
pub fn as_interned_bytes(self) -> Interned<[u8]> {
Interned::cast_unchecked(self, str::as_bytes)
}
pub fn as_interned_os_str(self) -> Interned<OsStr> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
pub fn as_interned_path(self) -> Interned<Path> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
}
impl From<Interned<str>> for Interned<OsStr> {
fn from(value: Interned<str>) -> Self {
value.as_interned_os_str()
}
}
impl From<Interned<str>> for Interned<Path> {
fn from(value: Interned<str>) -> Self {
value.as_interned_path()
}
}
impl Interned<OsStr> {
pub fn as_interned_encoded_bytes(self) -> Interned<[u8]> {
Interned::cast_unchecked(self, OsStr::as_encoded_bytes)
}
pub fn to_interned_str(self) -> Option<Interned<str>> {
Interned::try_cast_unchecked(self, |v| v.to_str().ok_or(())).ok()
}
pub fn display(self) -> std::ffi::os_str::Display<'static> {
Self::into_inner(self).display()
}
pub fn as_interned_path(self) -> Interned<Path> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
}
impl From<Interned<OsStr>> for Interned<Path> {
fn from(value: Interned<OsStr>) -> Self {
value.as_interned_path()
}
}
impl Interned<Path> {
pub fn as_interned_os_str(self) -> Interned<OsStr> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
pub fn to_interned_str(self) -> Option<Interned<str>> {
Interned::try_cast_unchecked(self, |v| v.to_str().ok_or(())).ok()
}
pub fn display(self) -> std::path::Display<'static> {
Self::into_inner(self).display()
}
pub fn interned_file_name(self) -> Option<Interned<OsStr>> {
Some(self.file_name()?.intern())
}
}
impl From<Interned<Path>> for Interned<OsStr> {
fn from(value: Interned<Path>) -> Self {
value.as_interned_os_str()
}
}
pub trait InternSlice: Sized {
type Element: 'static + Send + Sync + Clone + Hash + Eq;
fn intern_slice(self) -> Interned<[Self::Element]>;
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for Box<[T]> {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.into_vec().intern_slice()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for Vec<T> {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.intern_deref()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for &'_ [T] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.intern()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for &'_ mut [T] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.intern()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for [T; N] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
(&self).intern_slice()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for Box<[T; N]> {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
let this: Box<[T]> = self;
this.intern_slice()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for &'_ [T; N] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
let this: &[T] = self;
this.intern()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for &'_ mut [T; N] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
let this: &[T] = self;
this.intern()
} }
} }
pub trait Intern: Any + Send + Sync { pub trait Intern: Any + Send + Sync {
fn intern(&self) -> Interned<Self>; fn intern(&self) -> Interned<Self>;
fn intern_deref(self) -> Interned<Self::Target>
where
Self: Sized + Deref<Target: Intern + ToOwned<Owned = Self>>,
{
Self::Target::intern_owned(self)
}
fn intern_sized(self) -> Interned<Self> fn intern_sized(self) -> Interned<Self>
where where
Self: Clone, Self: Clone,
@ -318,6 +569,30 @@ pub trait Intern: Any + Send + Sync {
} }
} }
impl<T: ?Sized + Intern + ToOwned> From<Cow<'_, T>> for Interned<T> {
fn from(value: Cow<'_, T>) -> Self {
Intern::intern_cow(value)
}
}
impl<T: ?Sized + Intern> From<&'_ T> for Interned<T> {
fn from(value: &'_ T) -> Self {
Intern::intern(value)
}
}
impl<T: Intern + Clone> From<T> for Interned<T> {
fn from(value: T) -> Self {
Intern::intern_sized(value)
}
}
impl<T: ?Sized + 'static + Send + Sync + ToOwned> From<Interned<T>> for Cow<'_, T> {
fn from(value: Interned<T>) -> Self {
Cow::Borrowed(Interned::into_inner(value))
}
}
struct InternerState<T: ?Sized + 'static + Send + Sync> { struct InternerState<T: ?Sized + 'static + Send + Sync> {
table: HashTable<&'static T>, table: HashTable<&'static T>,
hasher: DefaultBuildHasher, hasher: DefaultBuildHasher,
@ -383,12 +658,6 @@ impl Interner<BitSlice> {
} }
} }
impl Interner<str> {
fn intern_str(&self, value: Cow<'_, str>) -> Interned<str> {
self.intern(|value| value.into_owned().leak(), value)
}
}
pub struct Interned<T: ?Sized + 'static + Send + Sync> { pub struct Interned<T: ?Sized + 'static + Send + Sync> {
inner: &'static T, inner: &'static T,
} }
@ -418,9 +687,9 @@ forward_fmt_trait!(Pointer);
forward_fmt_trait!(UpperExp); forward_fmt_trait!(UpperExp);
forward_fmt_trait!(UpperHex); forward_fmt_trait!(UpperHex);
impl<T: ?Sized + 'static + Send + Sync> AsRef<T> for Interned<T> { impl<T: ?Sized + 'static + Send + Sync + AsRef<U>, U: ?Sized> AsRef<U> for Interned<T> {
fn as_ref(&self) -> &T { fn as_ref(&self) -> &U {
self T::as_ref(self)
} }
} }
@ -498,19 +767,25 @@ where
String: FromIterator<I>, String: FromIterator<I>,
{ {
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
str::intern_owned(FromIterator::from_iter(iter)) String::from_iter(iter).intern_deref()
} }
} }
impl AsRef<OsStr> for Interned<str> { impl<I> FromIterator<I> for Interned<Path>
fn as_ref(&self) -> &OsStr { where
str::as_ref(self) PathBuf: FromIterator<I>,
{
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
PathBuf::from_iter(iter).intern_deref()
} }
} }
impl AsRef<Path> for Interned<str> { impl<I> FromIterator<I> for Interned<OsStr>
fn as_ref(&self) -> &Path { where
str::as_ref(self) OsString: FromIterator<I>,
{
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
OsString::from_iter(iter).intern_deref()
} }
} }
@ -550,24 +825,12 @@ impl<T: 'static + Clone + Send + Sync> From<Interned<[T]>> for Box<[T]> {
} }
} }
impl From<Interned<str>> for String {
fn from(value: Interned<str>) -> Self {
String::from(&*value)
}
}
impl<I> Default for Interned<[I]> impl<I> Default for Interned<[I]>
where where
[I]: Intern, [I]: Intern,
{ {
fn default() -> Self { fn default() -> Self {
[][..].intern() Intern::intern(&[])
}
}
impl Default for Interned<str> {
fn default() -> Self {
"".intern()
} }
} }
@ -698,15 +961,6 @@ impl<'de> Deserialize<'de> for Interned<BitSlice> {
} }
} }
impl<'de> Deserialize<'de> for Interned<str> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
String::deserialize(deserializer).map(Intern::intern_owned)
}
}
impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for T { impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for T {
fn intern(&self) -> Interned<Self> { fn intern(&self) -> Interned<Self> {
Self::intern_cow(Cow::Borrowed(self)) Self::intern_cow(Cow::Borrowed(self))
@ -767,26 +1021,6 @@ impl Intern for BitSlice {
} }
} }
impl Intern for str {
fn intern(&self) -> Interned<Self> {
Self::intern_cow(Cow::Borrowed(self))
}
fn intern_owned(this: <Self as ToOwned>::Owned) -> Interned<Self>
where
Self: ToOwned,
{
Self::intern_cow(Cow::Owned(this))
}
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self>
where
Self: ToOwned,
{
Interner::get().intern_str(this)
}
}
pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy { pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy {
type InputRef<'a>: 'a + Send + Sync + Hash + Copy; type InputRef<'a>: 'a + Send + Sync + Hash + Copy;
type InputOwned: 'static + Send + Sync; type InputOwned: 'static + Send + Sync;

View file

@ -10,7 +10,7 @@ use crate::{
}, },
hdl, hdl,
int::UInt, int::UInt,
intern::{Intern, Interned, Memoize}, intern::{Intern, InternSlice, Interned, Memoize},
memory::{DynPortType, Mem, MemPort}, memory::{DynPortType, Mem, MemPort},
module::{ module::{
Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire,
@ -620,7 +620,7 @@ fn match_int_tag(
block, block,
Block { Block {
memories: Default::default(), memories: Default::default(),
stmts: [Stmt::from(retval)][..].intern(), stmts: [Stmt::from(retval)].intern_slice(),
}, },
], ],
}; };

View file

@ -12,7 +12,9 @@ use crate::{
}, },
}, },
int::BoolOrIntType, int::BoolOrIntType,
intern::{Intern, Interned, InternedCompare, PtrEqWithTypeId, SupportsPtrEqWithTypeId}, intern::{
Intern, InternSlice, Interned, InternedCompare, PtrEqWithTypeId, SupportsPtrEqWithTypeId,
},
module::{ module::{
ModuleIO, ModuleIO,
transform::visit::{Fold, Folder, Visit, Visitor}, transform::visit::{Fold, Folder, Visit, Visitor},
@ -262,7 +264,7 @@ impl_trace_decl! {
}), }),
Instance(TraceInstance { Instance(TraceInstance {
fn children(self) -> _ { fn children(self) -> _ {
[self.instance_io.into(), self.module.into()][..].intern() [self.instance_io.into(), self.module.into()].intern_slice()
} }
name: Interned<str>, name: Interned<str>,
instance_io: TraceBundle, instance_io: TraceBundle,
@ -282,7 +284,7 @@ impl_trace_decl! {
}), }),
MemPort(TraceMemPort { MemPort(TraceMemPort {
fn children(self) -> _ { fn children(self) -> _ {
[self.bundle.into()][..].intern() [self.bundle.into()].intern_slice()
} }
name: Interned<str>, name: Interned<str>,
bundle: TraceBundle, bundle: TraceBundle,
@ -290,7 +292,7 @@ impl_trace_decl! {
}), }),
Wire(TraceWire { Wire(TraceWire {
fn children(self) -> _ { fn children(self) -> _ {
[*self.child][..].intern() [*self.child].intern_slice()
} }
name: Interned<str>, name: Interned<str>,
child: Interned<TraceDecl>, child: Interned<TraceDecl>,
@ -298,7 +300,7 @@ impl_trace_decl! {
}), }),
Reg(TraceReg { Reg(TraceReg {
fn children(self) -> _ { fn children(self) -> _ {
[*self.child][..].intern() [*self.child].intern_slice()
} }
name: Interned<str>, name: Interned<str>,
child: Interned<TraceDecl>, child: Interned<TraceDecl>,
@ -306,7 +308,7 @@ impl_trace_decl! {
}), }),
ModuleIO(TraceModuleIO { ModuleIO(TraceModuleIO {
fn children(self) -> _ { fn children(self) -> _ {
[*self.child][..].intern() [*self.child].intern_slice()
} }
name: Interned<str>, name: Interned<str>,
child: Interned<TraceDecl>, child: Interned<TraceDecl>,

View file

@ -14,7 +14,7 @@ use crate::{
}, },
}, },
int::BoolOrIntType, int::BoolOrIntType,
intern::{Intern, Interned, Memoize}, intern::{Intern, InternSlice, Interned, Memoize},
memory::PortKind, memory::PortKind,
module::{ module::{
AnnotatedModuleIO, Block, ExternModuleBody, Id, InstantiatedModule, ModuleBody, NameId, AnnotatedModuleIO, Block, ExternModuleBody, Id, InstantiatedModule, ModuleBody, NameId,
@ -3950,8 +3950,8 @@ impl Compiler {
[Cond { [Cond {
body: CondBody::IfTrue { cond }, body: CondBody::IfTrue { cond },
source_location: reg.source_location(), source_location: reg.source_location(),
}][..] }]
.intern(), .intern_slice(),
lhs, lhs,
init, init,
reg.source_location(), reg.source_location(),

View file

@ -14,7 +14,6 @@ use crate::{
module::Module, module::Module,
util::HashMap, util::HashMap,
}; };
use eyre::eyre;
use serde::Deserialize; use serde::Deserialize;
use std::{ use std::{
fmt::Write, fmt::Write,
@ -107,12 +106,7 @@ fn make_assert_formal_args(
) -> eyre::Result<JobArgsAndDependencies<ExternalCommandJobKind<Formal>>> { ) -> eyre::Result<JobArgsAndDependencies<ExternalCommandJobKind<Formal>>> {
let args = JobKindAndArgs { let args = JobKindAndArgs {
kind: BaseJobKind, kind: BaseJobKind,
args: BaseJobArgs::from_output_dir_and_env( args: BaseJobArgs::from_output_dir_and_env(get_assert_formal_target_path(&test_name)),
get_assert_formal_target_path(&test_name)
.into_os_string()
.into_string()
.map_err(|_| eyre!("path is not valid UTF-8"))?,
),
}; };
let dependencies = JobArgsAndDependencies { let dependencies = JobArgsAndDependencies {
args, args,

View file

@ -38,9 +38,9 @@ pub(crate) use misc::chain;
pub use misc::{ pub use misc::{
BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter,
SerdeJsonEscapeIf, SerdeJsonEscapeIfFormatter, SerdeJsonEscapeIfTest, SerdeJsonEscapeIf, SerdeJsonEscapeIfFormatter, SerdeJsonEscapeIfTest,
SerdeJsonEscapeIfTestResult, interned_bit, iter_eq_by, serialize_to_json_ascii, SerdeJsonEscapeIfTestResult, interned_bit, iter_eq_by, os_str_strip_prefix,
serialize_to_json_ascii_pretty, serialize_to_json_ascii_pretty_with_indent, slice_range, os_str_strip_suffix, serialize_to_json_ascii, serialize_to_json_ascii_pretty,
try_slice_range, serialize_to_json_ascii_pretty_with_indent, slice_range, try_slice_range,
}; };
pub mod job_server; pub mod job_server;

View file

@ -4,6 +4,7 @@ use crate::intern::{Intern, Interned};
use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView}; use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView};
use std::{ use std::{
cell::Cell, cell::Cell,
ffi::OsStr,
fmt::{self, Debug, Write}, fmt::{self, Debug, Write},
io, io,
ops::{Bound, Range, RangeBounds}, ops::{Bound, Range, RangeBounds},
@ -564,3 +565,23 @@ pub fn serialize_to_json_ascii_pretty_with_indent<T: serde::Serialize + ?Sized>(
serde_json::ser::PrettyFormatter::with_indent(indent.as_bytes()), serde_json::ser::PrettyFormatter::with_indent(indent.as_bytes()),
) )
} }
pub fn os_str_strip_prefix<'a>(os_str: &'a OsStr, prefix: impl AsRef<str>) -> Option<&'a OsStr> {
os_str
.as_encoded_bytes()
.strip_prefix(prefix.as_ref().as_bytes())
.map(|bytes| {
// Safety: we removed a UTF-8 prefix so bytes starts with a valid boundary
unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }
})
}
pub fn os_str_strip_suffix<'a>(os_str: &'a OsStr, suffix: impl AsRef<str>) -> Option<&'a OsStr> {
os_str
.as_encoded_bytes()
.strip_suffix(suffix.as_ref().as_bytes())
.map(|bytes| {
// Safety: we removed a UTF-8 suffix so bytes ends with a valid boundary
unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }
})
}

View file

@ -156,7 +156,7 @@ note: required by a bound in `intern_sized`
| |
| pub trait Intern: Any + Send + Sync { | pub trait Intern: Any + Send + Sync {
| ^^^^ required by this bound in `Intern::intern_sized` | ^^^^ required by this bound in `Intern::intern_sized`
| fn intern(&self) -> Interned<Self>; ...
| fn intern_sized(self) -> Interned<Self> | fn intern_sized(self) -> Interned<Self>
| ------------ required by a bound in this associated function | ------------ required by a bound in this associated function
help: consider dereferencing here help: consider dereferencing here
@ -188,7 +188,7 @@ note: required by a bound in `intern_sized`
| |
| pub trait Intern: Any + Send + Sync { | pub trait Intern: Any + Send + Sync {
| ^^^^ required by this bound in `Intern::intern_sized` | ^^^^ required by this bound in `Intern::intern_sized`
| fn intern(&self) -> Interned<Self>; ...
| fn intern_sized(self) -> Interned<Self> | fn intern_sized(self) -> Interned<Self>
| ------------ required by a bound in this associated function | ------------ required by a bound in this associated function
help: consider dereferencing here help: consider dereferencing here
@ -255,7 +255,7 @@ note: required by a bound in `intern_sized`
| |
| pub trait Intern: Any + Send + Sync { | pub trait Intern: Any + Send + Sync {
| ^^^^ required by this bound in `Intern::intern_sized` | ^^^^ required by this bound in `Intern::intern_sized`
| fn intern(&self) -> Interned<Self>; ...
| fn intern_sized(self) -> Interned<Self> | fn intern_sized(self) -> Interned<Self>
| ------------ required by a bound in this associated function | ------------ required by a bound in this associated function
help: consider dereferencing here help: consider dereferencing here