1010 */
1111
1212use aml:: { AmlContext , DebugVerbosity } ;
13- use clap:: { Arg , ArgAction } ;
13+ use clap:: { Arg , ArgGroup , ArgAction } ;
1414use 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+
2332fn 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
135205struct Logger ;
0 commit comments