Skip to content

Commit 0b3a35d

Browse files
rw-vancIsaacWoods
authored andcommitted
aml_tester: Add positional file arguments, in-order parsing and shared namespace
1 parent d8140ca commit 0b3a35d

File tree

1 file changed

+134
-64
lines changed

1 file changed

+134
-64
lines changed

aml_tester/src/main.rs

Lines changed: 134 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,25 @@
1010
*/
1111

1212
use aml::{AmlContext, DebugVerbosity};
13-
use clap::{Arg, ArgAction};
13+
use clap::{Arg, ArgGroup, ArgAction};
1414
use std::{
1515
ffi::OsStr,
1616
fs::{self, File},
1717
io::{Read, Write},
18-
ops::Not,
19-
path::Path,
18+
path::{Path, PathBuf},
2019
process::Command,
20+
collections::HashSet,
2121
};
2222

23+
enum CompilationOutcome {
24+
Ignored,
25+
IsAml(PathBuf),
26+
Newer(PathBuf),
27+
NotCompiled(PathBuf),
28+
Failed(PathBuf),
29+
Succeeded(PathBuf),
30+
}
31+
2332
fn main() -> std::io::Result<()> {
2433
log::set_logger(&Logger).unwrap();
2534
log::set_max_level(log::LevelFilter::Trace);
@@ -28,36 +37,102 @@ fn main() -> std::io::Result<()> {
2837
.version("v0.1.0")
2938
.author("Isaac Woods")
3039
.about("Compiles and tests ASL files")
31-
.arg(Arg::new("path").short('p').long("path").required(true).action(ArgAction::Set).value_name("DIR"))
32-
.arg(Arg::new("no_compile").long("no-compile").action(ArgAction::SetTrue))
40+
.arg(Arg::new("no_compile").long("no-compile").action(ArgAction::SetTrue).help("Don't compile asl to aml"))
41+
.arg(Arg::new("reset").long("reset").action(ArgAction::SetTrue).help("Clear namespace after each file"))
42+
.arg(Arg::new("path").short('p').long("path").required(false).action(ArgAction::Set).value_name("DIR"))
43+
.arg(Arg::new("files").action(ArgAction::Append).value_name("FILE.{asl,aml}"))
44+
.group(ArgGroup::new("files_list").args(["path", "files"]).required(true))
3345
.get_matches();
3446

35-
let dir_path = Path::new(matches.get_one::<String>("path").unwrap());
36-
println!("Running tests in directory: {:?}", dir_path);
47+
// Get an initial list of files - may not work correctly on non-UTF8 OsString
48+
let files: Vec<String> = if matches.contains_id("path") {
49+
let dir_path = Path::new(matches.get_one::<String>("path").unwrap());
50+
println!("Running tests in directory: {:?}", dir_path);
51+
fs::read_dir(dir_path)?.filter_map(| entry | if entry.is_ok() {
52+
Some(entry.unwrap().path().to_string_lossy().to_string())
53+
} else {
54+
None
55+
}).collect()
56+
} else {
57+
matches.get_many::<String>("files").unwrap_or_default().map(| name | name.to_string()).collect()
58+
};
59+
60+
// Make sure all files exist, propagate error if it occurs
61+
files.iter().fold(Ok(()), | result: std::io::Result<()>, file | {
62+
let path = Path::new(file);
63+
if !path.is_file() {
64+
println!("Not a regular file: {}", file);
65+
// Get the io error if there is one
66+
path.metadata()?;
67+
}
68+
result
69+
})?;
70+
71+
// Make sure we have the ability to compile ASL -> AML, if user wants it
72+
let user_wants_compile = !matches.get_flag("no_compile");
73+
let can_compile = user_wants_compile &&
74+
// Test if `iasl` is installed, so we can give a good error later if it's not
75+
match Command::new("iasl").arg("-v").status() {
76+
Ok(exit_status) if exit_status.success() => true,
77+
Ok(exit_status) => {
78+
panic!("`iasl` exited with unsuccessful status: {:?}", exit_status);
79+
},
80+
Err(_) => false,
81+
};
3782

38-
if !matches.get_flag("no_compile") {
39-
let (passed, failed) = compile_asl_files(dir_path)?;
40-
println!("Compiled {} ASL files: {} passed, {} failed.", passed + failed, passed, failed);
83+
let compiled_files: Vec<CompilationOutcome> = files.iter().map(| name | resolve_and_compile(name, can_compile).unwrap()).collect();
84+
85+
// Check if compilation should have happened but did not
86+
if user_wants_compile && compiled_files.iter().any(| outcome | matches!(outcome, CompilationOutcome::NotCompiled(_))) {
87+
panic!("`iasl` is not installed, but we want to compile some ASL files! Pass --no-compile, or install `iasl`");
88+
}
89+
// Report compilation results
90+
if user_wants_compile {
91+
let (passed, failed) = compiled_files.iter()
92+
.fold((0, 0), | (passed, failed), outcome | match outcome {
93+
CompilationOutcome::Succeeded(_) => (passed + 1, failed),
94+
CompilationOutcome::Failed(_) => (passed, failed + 1),
95+
_ => (passed, failed),
96+
});
97+
if passed + failed > 0 {
98+
println!("Compiled {} ASL files: {} passed, {} failed.", passed + failed, passed, failed);
99+
}
41100
}
42101

43-
/*
44-
* Now, we find all the AML files in the directory, and try to compile them with the `aml`
45-
* parser.
46-
*/
47-
let aml_files = fs::read_dir(dir_path)?
48-
.filter(|entry| entry.is_ok() && entry.as_ref().unwrap().path().extension() == Some(OsStr::new("aml")))
49-
.map(Result::unwrap);
102+
// Make a list of the files we have processed, and skip them if we see them again
103+
let mut dedup_list: HashSet<PathBuf> = HashSet::new();
104+
105+
// Filter down to the final list of AML files
106+
let aml_files = compiled_files.iter()
107+
.filter_map(| outcome | match outcome {
108+
CompilationOutcome::IsAml(path) => Some(path.clone()),
109+
CompilationOutcome::Newer(path) => Some(path.clone()),
110+
CompilationOutcome::Succeeded(path) => Some(path.clone()),
111+
CompilationOutcome::Ignored | CompilationOutcome::Failed(_) | CompilationOutcome::NotCompiled(_) => None,
112+
})
113+
.filter(| path | if dedup_list.contains(path) {
114+
false
115+
} else {
116+
dedup_list.insert(path.clone());
117+
true
118+
});
119+
120+
let user_wants_reset = matches.get_flag("reset");
121+
let mut context = AmlContext::new(Box::new(Handler), DebugVerbosity::None);
50122

51123
let (passed, failed) = aml_files.fold((0, 0), |(passed, failed), file_entry| {
52-
print!("Testing AML file: {:?}... ", file_entry.path());
124+
print!("Testing AML file: {:?}... ", file_entry);
53125
std::io::stdout().flush().unwrap();
54126

55-
let mut file = File::open(file_entry.path()).unwrap();
127+
let mut file = File::open(file_entry).unwrap();
56128
let mut contents = Vec::new();
57129
file.read_to_end(&mut contents).unwrap();
58130

59131
const AML_TABLE_HEADER_LENGTH: usize = 36;
60-
let mut context = AmlContext::new(Box::new(Handler), DebugVerbosity::None);
132+
133+
if user_wants_reset {
134+
context = AmlContext::new(Box::new(Handler), DebugVerbosity::None);
135+
}
61136

62137
match context.parse_table(&contents[AML_TABLE_HEADER_LENGTH..]) {
63138
Ok(()) => {
@@ -78,58 +153,53 @@ fn main() -> std::io::Result<()> {
78153
Ok(())
79154
}
80155

81-
fn compile_asl_files(dir_path: &Path) -> std::io::Result<(u32, u32)> {
82-
let mut asl_files = fs::read_dir(dir_path)?
83-
.filter(|entry| entry.is_ok() && entry.as_ref().unwrap().path().extension() == Some(OsStr::new("asl")))
84-
.map(Result::unwrap)
85-
.peekable();
156+
/// Determine what to do with this file - ignore, compile and parse, or just parse.
157+
/// If ".aml" does not exist, or if ".asl" is newer, compiles the file.
158+
/// If the ".aml" file is newer, indicate it is ready to parse.
159+
fn resolve_and_compile(name: &str, can_compile: bool) -> std::io::Result<CompilationOutcome> {
160+
let path = PathBuf::from(name);
86161

87-
if !asl_files.peek().is_none() {
88-
// Test if `iasl` is installed, so we can give a good error if it's not
89-
match Command::new("iasl").arg("-v").status() {
90-
Ok(exit_status) => if exit_status.success().not() {
91-
panic!("`iasl` exited with unsuccessfull status: {:?}", exit_status);
92-
},
93-
Err(_) => panic!("`iasl` is not installed, but we want to compile some ASL files! Pass --no-compile, or install `iasl`"),
94-
}
162+
// If this file is aml and it exists, it's ready for parsing
163+
// metadata() will error if the file does not exist
164+
if path.extension() == Some(OsStr::new("aml")) && path.metadata()?.is_file() {
165+
return Ok(CompilationOutcome::IsAml(path));
95166
}
96167

97-
let mut passed = 0;
98-
let mut failed = 0;
99-
100-
for file in asl_files {
101-
let aml_path = file.path().with_extension(OsStr::new("aml"));
102-
103-
/*
104-
* Check if an AML path with a matching last-modified date exists. If it
105-
* does, we don't need to compile the ASL file again.
106-
*/
107-
if aml_path.is_file() {
108-
let asl_last_modified = file.metadata()?.modified()?;
109-
let aml_last_modified = aml_path.metadata()?.modified()?;
110-
111-
if asl_last_modified <= aml_last_modified {
112-
continue;
113-
}
114-
}
168+
// If this file is not asl, it's not interesting. Error if the file does not exist.
169+
if path.extension() != Some(OsStr::new("asl")) || !path.metadata()?.is_file() {
170+
return Ok(CompilationOutcome::Ignored);
171+
}
115172

116-
// Compile the ASL file using `iasl`
117-
println!("Compiling file: {}", file.path().to_str().unwrap());
118-
let output = Command::new("iasl").arg(file.path()).output()?;
173+
let aml_path = path.with_extension("aml");
119174

120-
if output.status.success() {
121-
passed += 1;
122-
} else {
123-
failed += 1;
124-
println!(
125-
"Failed to compile ASL file: {}. Output from iasl:\n {}",
126-
file.path().to_str().unwrap(),
127-
String::from_utf8_lossy(&output.stderr)
128-
);
175+
if aml_path.is_file() {
176+
let asl_last_modified = path.metadata()?.modified()?;
177+
let aml_last_modified = aml_path.metadata()?.modified()?;
178+
// If the aml is more recent than the asl, use the existing aml
179+
// Otherwise continue to compilation
180+
if asl_last_modified <= aml_last_modified {
181+
return Ok(CompilationOutcome::Newer(aml_path))
129182
}
130183
}
131184

132-
Ok((passed, failed))
185+
if !can_compile {
186+
return Ok(CompilationOutcome::NotCompiled(path));
187+
}
188+
189+
// Compile the ASL file using `iasl`
190+
println!("Compiling file: {}", name);
191+
let output = Command::new("iasl").arg(name).output()?;
192+
193+
if !output.status.success() {
194+
println!(
195+
"Failed to compile ASL file: {}. Output from iasl:\n {}",
196+
name,
197+
String::from_utf8_lossy(&output.stderr)
198+
);
199+
Ok(CompilationOutcome::Failed(path))
200+
} else {
201+
Ok(CompilationOutcome::Succeeded(aml_path))
202+
}
133203
}
134204

135205
struct Logger;

0 commit comments

Comments
 (0)