@@ -9,12 +9,13 @@ use std::path::PathBuf;
99use std:: process:: ExitCode ;
1010
1111use aspect_config:: cli_version;
12- use axl_runtime:: engine:: task:: { AsTaskLike , FrozenTask , Task } ;
12+ use axl_runtime:: engine:: task:: { FrozenTask , Task } ;
1313use axl_runtime:: engine:: task_arg:: TaskArg ;
1414use axl_runtime:: engine:: task_args:: TaskArgs ;
1515use axl_runtime:: eval:: { AxlScriptEvaluator , EvaluatedAxlScript } ;
16- use axl_runtime:: module :: AXL_ROOT_MODULE_NAME ;
16+ use axl_runtime:: helpers :: normalize_abs_path_lexically ;
1717use axl_runtime:: module:: { AxlModuleEvaluator , DiskStore } ;
18+ use axl_runtime:: module:: { AXL_MODULE_FILE , AXL_ROOT_MODULE_NAME } ;
1819use clap:: { Arg , ArgAction , Command } ;
1920use miette:: { miette, IntoDiagnostic } ;
2021use starlark:: values:: ValueLike ;
@@ -64,7 +65,7 @@ async fn main() -> miette::Result<ExitCode> {
6465 . evaluate ( AXL_ROOT_MODULE_NAME . to_string ( ) , repo_root. clone ( ) )
6566 . into_diagnostic ( ) ?;
6667
67- // Expand all modules (including the builtin @aspect module) to the disk store and return the module roots on disk.
68+ // Expand all module deps (including the builtin @aspect module) to the disk store and return the module roots on disk.
6869 // This results in a Vec of (String, PathBuf) such as
6970 // [
7071 // ( "aspect", "/Users/username/Library/Caches/axl/deps/27e6d838c365a7c5d79674a7b6c7ec7b8d22f686dbcc8088a8d1454a6489a9ae/aspect" ),
@@ -76,24 +77,23 @@ async fn main() -> miette::Result<ExitCode> {
7677 . await
7778 . into_diagnostic ( ) ?;
7879
79- // Gather all tasks from use_task calls in the repository root axl module
80- let mut use_tasks = vec ! [ (
80+ // Gather evaluated root and deps modules into modules vec
81+ let mut modules = vec ! [ (
8182 module_store. module_name,
8283 module_store. module_root,
8384 module_store. tasks. take( ) ,
8485 ) ] ;
8586
86- // Gather all tasks from use_task calls in the axl module deps
8787 for ( name, root) in module_roots {
8888 let module_store = module_eval. evaluate ( name, root) . into_diagnostic ( ) ?;
89- use_tasks . push ( (
89+ modules . push ( (
9090 module_store. module_name ,
9191 module_store. module_root ,
9292 module_store. tasks . take ( ) ,
9393 ) )
9494 }
9595
96- let deps_path = disk_store. deps_path ( ) ;
96+ let axl_deps_root = disk_store. deps_path ( ) ;
9797
9898 // Get the default search paths given the current working directory and the repository root
9999 let search_paths = get_default_axl_search_paths ( & current_work_dir, & repo_root) ;
@@ -109,6 +109,110 @@ async fn main() -> miette::Result<ExitCode> {
109109 let out = spawn_blocking ( move || {
110110 let _enter = espan. enter ( ) ;
111111
112+ // Evaluate all scripts to find tasks and configs. The order of task discovery will be load bearing in the future
113+ // when task overloading is supported
114+ // 1. repository axl_sources
115+ // 2. use_task in the root module
116+ // 3. auto_use_tasks from the @aspect built-in module (if not overloaded by an dep in the root MODULE.aspect)
117+ // 4. auto_use_tasks from axl module deps in the root MODULE.aspect
118+ let mut scripts: HashMap < String , EvaluatedAxlScript > = HashMap :: new ( ) ;
119+ let mut tasks: Vec < ( String , String ) > = Vec :: new ( ) ;
120+ let mut configs: Vec < String > = Vec :: new ( ) ;
121+ let script_eval = AxlScriptEvaluator :: new (
122+ AXL_ROOT_MODULE_NAME . to_string ( ) ,
123+ repo_root. clone ( ) ,
124+ axl_deps_root. clone ( ) ,
125+ ) ;
126+ for path in axl_sources. iter ( ) {
127+ let rel_path = path
128+ . strip_prefix ( & repo_root)
129+ . map ( |p| p. to_path_buf ( ) )
130+ . into_diagnostic ( ) ?;
131+ let mut has_config = false ;
132+ let mut has_tasks = false ;
133+ if path. ends_with ( ".aspect/config.axl" ) {
134+ configs. push ( path. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) ) ;
135+ has_config = true ;
136+ }
137+ let script = script_eval. eval ( & rel_path) . into_diagnostic ( ) ?;
138+ for symbol in script. module . names ( ) {
139+ if let Some ( task_val) = script. module . get ( symbol. as_str ( ) ) {
140+ if task_val. downcast_ref :: < Task > ( ) . is_none ( )
141+ && task_val. downcast_ref :: < FrozenTask > ( ) . is_none ( )
142+ {
143+ continue ;
144+ }
145+ tasks. push ( (
146+ symbol. as_str ( ) . to_string ( ) ,
147+ path. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) ,
148+ ) ) ;
149+ has_tasks = true ;
150+ }
151+ }
152+ if has_config || has_tasks {
153+ scripts
154+ . entry ( path. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) )
155+ . or_insert ( script) ;
156+ }
157+ }
158+ for ( module_name, module_root, use_tasks) in modules. iter ( ) {
159+ let script_eval = AxlScriptEvaluator :: new (
160+ module_name. clone ( ) ,
161+ module_root. clone ( ) ,
162+ axl_deps_root. clone ( ) ,
163+ ) ;
164+ for ( rel_path, symbol) in use_tasks {
165+ let script = script_eval
166+ . eval ( & PathBuf :: from ( & rel_path) )
167+ . into_diagnostic ( ) ?;
168+ let path =
169+ normalize_abs_path_lexically ( & module_root. join ( & rel_path) ) . expect ( "TODO" ) ;
170+ if let Some ( task_val) = script. module . get ( symbol. as_str ( ) ) {
171+ if task_val. downcast_ref :: < Task > ( ) . is_none ( )
172+ && task_val. downcast_ref :: < FrozenTask > ( ) . is_none ( )
173+ {
174+ return Err ( miette ! (
175+ "invalid use_task({:?}, {:?}) call in @{} module at {}/{}" ,
176+ rel_path,
177+ symbol,
178+ module_name,
179+ module_root. display( ) ,
180+ AXL_MODULE_FILE
181+ ) ) ;
182+ } ;
183+ tasks. push ( (
184+ symbol. clone ( ) ,
185+ path. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) ,
186+ ) ) ;
187+ } else {
188+ return Err ( miette ! (
189+ "task symbol {:?} not found in @{} module use_task({:?}, {:?}) at {}/{}" ,
190+ symbol,
191+ module_name,
192+ rel_path,
193+ symbol,
194+ module_root. display( ) ,
195+ AXL_MODULE_FILE
196+ ) ) ;
197+ }
198+ scripts
199+ . entry ( path. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) )
200+ . or_insert ( script) ;
201+ }
202+ }
203+
204+ // Call config.axl config() functions
205+ // TODO: pass tasks to the config function and allow them to be mutated
206+ for path in configs. iter ( ) {
207+ let script = scripts
208+ . get ( path)
209+ . expect ( & format ! ( "expected to find {:?} script" , path) ) ;
210+ script. execute_config ( "config" ) . into_diagnostic ( ) ?;
211+ }
212+
213+ // Iterate through tasks after any config mutations and create the command with make_command_from_task
214+ let mut tree = CommandTree :: default ( ) ;
215+
112216 // TODO: add .about()
113217 let cmd = Command :: new ( "aspect" )
114218 // set binary name to "aspect" in help
@@ -134,97 +238,23 @@ async fn main() -> miette::Result<ExitCode> {
134238 . display_order ( BUILTIN_COMMAND_DISPLAY_ORDER ) ,
135239 ) ;
136240
137- // Collect tasks into tree
138- let mut tree = CommandTree :: default ( ) ;
139- let mut tasks: HashMap < String , EvaluatedAxlScript > = HashMap :: new ( ) ;
140-
141- // First gather tasks from use_task calls in axl modules
142- for ( module_name, module_root, use_tasks) in use_tasks {
143- let te = AxlScriptEvaluator :: new ( module_root. clone ( ) , deps_path. clone ( ) ) ;
144-
145- for ( relative_path, symbol) in use_tasks {
146- let path = module_root. join ( & relative_path) ;
147- let script = te. eval ( & PathBuf :: from ( & relative_path) ) . into_diagnostic ( ) ?;
148- if let Some ( task_val) = script. module . get ( symbol. as_str ( ) ) {
149- let def = if let Some ( task) = task_val. downcast_ref :: < Task > ( ) {
150- task. as_task ( )
151- } else if let Some ( task) = task_val. downcast_ref :: < FrozenTask > ( ) {
152- task. as_task ( )
153- } else {
154- return Err ( miette ! (
155- "invalid use_task({}, {}) call in {} at {:?}" ,
156- relative_path,
157- symbol,
158- module_name,
159- module_root
160- ) ) ;
161- } ;
162-
163- let name = if def. name ( ) . is_empty ( ) {
164- symbol. clone ( )
165- } else {
166- def. name ( ) . clone ( )
167- } ;
168- let rel_path = & path
169- . strip_prefix ( & module_root)
170- . expect ( "failed make path relative" )
171- . as_os_str ( )
172- . to_str ( )
173- . expect ( "failed to encode path" ) ;
174- let group = def. group ( ) ;
175- let defined_in = format ! ( "@{}/{}" , module_name, rel_path) ;
176- let cmd = make_command_from_task ( & name, & defined_in, & path, & symbol, def) ;
177- tree. insert ( & name, & group, & group, & path, cmd)
178- . into_diagnostic ( ) ?;
179- tasks. insert ( path. to_str ( ) . unwrap ( ) . to_string ( ) , script) ;
180- }
181- }
182- }
183-
184- // Next gather tasks from axl sources in the repository
185- let te = AxlScriptEvaluator :: new ( repo_root. clone ( ) , deps_path. clone ( ) ) ;
186- for path in axl_sources. iter ( ) {
187- let rel_path = path
188- . strip_prefix ( & repo_root)
189- . map ( |p| p. to_path_buf ( ) )
241+ for task in tasks. iter ( ) {
242+ let symbol = & task. 0 ;
243+ let path = & task. 1 ;
244+ let script = scripts
245+ . get ( path)
246+ . expect ( & format ! ( "expected to find {:?} script" , path) ) ;
247+ let def = script. task_definition ( symbol) . into_diagnostic ( ) ?;
248+ let name = if def. name ( ) . is_empty ( ) {
249+ symbol
250+ } else {
251+ def. name ( )
252+ } ;
253+ let group = def. group ( ) ;
254+ let defined_in = format ! ( "@{}/{}" , script. module_name, script. module_subpath) ;
255+ let cmd = make_command_from_task ( name, & defined_in, path, symbol, def) ;
256+ tree. insert ( name, group, group, path, cmd)
190257 . into_diagnostic ( ) ?;
191-
192- let script = te. eval ( & rel_path) . into_diagnostic ( ) ?;
193-
194- ' inner: for symbol in script. module . names ( ) {
195- if let Some ( task_val) = script. module . get ( symbol. as_str ( ) ) {
196- let def = if let Some ( task) = task_val. downcast_ref :: < Task > ( ) {
197- task. as_task ( )
198- } else if let Some ( task) = task_val. downcast_ref :: < FrozenTask > ( ) {
199- task. as_task ( )
200- } else {
201- continue ' inner;
202- } ;
203-
204- let name = if def. name ( ) . is_empty ( ) {
205- symbol. as_str ( ) . to_string ( )
206- } else {
207- def. name ( ) . clone ( )
208- } ;
209- let group = def. group ( ) ;
210- let defined_in = path
211- . strip_prefix ( & repo_root)
212- . expect ( "failed make path relative" )
213- . as_os_str ( )
214- . to_str ( )
215- . expect ( "failed to encode path" ) ;
216- let cmd = make_command_from_task (
217- & name,
218- defined_in,
219- path,
220- & symbol. as_str ( ) . to_string ( ) ,
221- def,
222- ) ;
223- tree. insert ( & name, & group, & group, & path, cmd)
224- . into_diagnostic ( ) ?;
225- }
226- }
227- tasks. insert ( path. to_str ( ) . unwrap ( ) . to_string ( ) , script) ;
228258 }
229259
230260 // Turn the command tree into a command with subcommands.
@@ -256,7 +286,7 @@ async fn main() -> miette::Result<ExitCode> {
256286 let ( name, cmdargs) = cmd;
257287 let task_path = tree. get_task_path ( & cmdargs) ;
258288 let task_symbol = tree. get_task_symbol ( & cmdargs) ;
259- let task_script = tasks . get ( & task_path) . unwrap ( ) ;
289+ let task_script = scripts . get ( & task_path) . unwrap ( ) ;
260290 let def = task_script
261291 . task_definition ( & task_symbol)
262292 . into_diagnostic ( ) ?;
0 commit comments