char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
7e18c89
main
1use std:: collections::{ HashMap , HashSet }; 2use std:: path:: PathBuf ; 3use std:: sync:: Arc ; 4use std:: sync:: atomic::{ AtomicBool , Ordering }; 5 6use serde::{ Deserialize , Serialize }; 7 8use crate :: cache:: Cache ; 9use crate :: env:: EnvLoader ; 10use crate :: fingerprint:: Fingerprinter ; 11use crate :: graph::{ Action , Graph , shell_quote}; 12use crate ::{ EnvMap , Error , Reporter , Result }; 13 14# [ derive ( Clone , Default )] 15pub struct CancelToken ( Arc < AtomicBool >); 16 17impl CancelToken { 18pub fn new () ->Self { 19CancelToken ( Arc :: new ( AtomicBool :: new ( false ))) 20} 21pub fn cancel ( & self ) { 22self . 0 . store ( true , Ordering :: SeqCst ); 23} 24pub fn is_cancelled ( & self ) ->bool { 25self . 0 . load ( Ordering :: SeqCst ) 26} 27} 28 29pub struct RunOptions { 30pub root : PathBuf , 31pub cache : Option < Cache >, 32pub force : bool , 33pub extra_args : Vec < String >, 34pub env : EnvMap , 35pub reporter : Box < dyn Reporter >, 36pub cancelled : CancelToken , 37} 38 39impl RunOptions { 40pub fn new ( root : PathBuf ) ->Self { 41RunOptions { 42 root, 43cache : None , 44force : false , 45extra_args : Vec :: new (), 46env : EnvMap :: new (), 47reporter : Box :: new ( crate :: NoopReporter ), 48cancelled : CancelToken :: new (), 49} 50} 51} 52 53# [ derive ( Debug , Clone , PartialEq , Serialize , Deserialize )] 54pub struct ReadyTask { 55pub name : String , 56pub action : Action , 57/// Extra arguments; non-empty only for the run's target. 58pub args : Vec < String >, 59pub env : EnvMap , 60} 61 62impl ReadyTask { 63pub fn display ( & self ) ->String { 64let mut text =self . action . display (); 65for argin & self . args { 66 text. push ( ' ' ); 67 text. push_str ( & shell_quote ( arg)); 68} 69 text 70} 71} 72 73# [ derive ( Debug , Clone , PartialEq , Default , Serialize , Deserialize )] 74pub struct FailedTask { 75pub name : String , 76pub code : u8 , 77} 78 79# [ derive ( Debug , Clone , PartialEq , Default , Serialize , Deserialize )] 80pub struct Report { 81pub code : u8 , 82pub completed : Vec < String >, 83pub failed : Vec < FailedTask >, 84pub cached : Vec < String >, 85} 86 87impl Report { 88pub fn ok ( & self ) ->bool { 89self . code ==0 90} 91} 92 93# [ derive ( Debug , Serialize , Deserialize )] 94pub enum Step { 95Ready ( Vec < ReadyTask >), 96Done ( Report ), 97} 98 99struct Frame { 100name : String , 101claim : usize , 102parent : Option < usize >, 103dep_index : usize , 104env : EnvMap , 105ancestors : Vec < String >, 106dep_outcomes : Vec < Option < u8 >>, 107active_children : HashSet < usize >, 108deps_started : bool , 109waiting_claim : Option < usize >, 110dispatched : bool , 111surfaced : bool , 112done : bool , 113} 114 115struct Claim { 116owner : Option < usize >, 117outcome : Option < u8 >, 118fingerprint : Option < String >, 119/// Whether the task's own action was dispatched (vs. completing 120/// because a dependency failed). Only dispatched tasks are failures. 121dispatched : bool , 122} 123 124pub struct Execution { 125graph : Graph , 126root : PathBuf , 127cache : Option < Cache >, 128force : bool , 129extra_args : Vec < String >, 130base_env : EnvMap , 131reporter : Box < dyn Reporter >, 132cancelled : CancelToken , 133env : EnvLoader , 134fingerprinter : Fingerprinter , 135frames : Vec < Frame >, 136claims : Vec < Claim >, 137claim_by_name : HashMap < String , usize >, 138root_frame : usize , 139completed : Vec < String >, 140cached : Vec < String >, 141} 142 143impl Execution { 144pub fn new ( graph : Graph , target : & str , opts : RunOptions ) ->Result < Self > { 145 graph. get ( target) ?; 146let root = opts. root . clone (); 147let mut execution =Execution { 148fingerprinter : Fingerprinter :: new ( graph. clone (), root. clone ()), 149env : EnvLoader :: new ( root. clone ()), 150 graph, 151 root, 152cache : opts. cache , 153force : opts. force , 154extra_args : opts. extra_args , 155base_env : opts. env , 156reporter : opts. reporter , 157cancelled : opts. cancelled , 158frames : Vec :: new (), 159claims : Vec :: new (), 160claim_by_name : HashMap :: new (), 161root_frame : 0 , 162completed : Vec :: new (), 163cached : Vec :: new (), 164}; 165let base = execution. base_env . clone (); 166let root_frame = execution. create_frame ( None , 0 , target, & base, Vec :: new ()) ?; 167 execution. root_frame = root_frame; 168Ok ( execution) 169} 170 171fn create_frame ( 172& mut self , 173parent : Option < usize >, 174dep_index : usize , 175name : & str , 176inherited_env : & EnvMap , 177ancestors : Vec < String >, 178) ->Result < usize > { 179if let Some ( start) = ancestors. iter (). position ( |a| a == name) { 180let mut cycle = ancestors[ start..]. to_vec (); 181 cycle. push ( name. to_owned ()); 182return Err ( Error :: Cycle ( cycle. join ( " -> " ))); 183} 184let task =self . graph . get ( name) ?. clone (); 185let env =self . env . effective ( & task, inherited_env) ?; 186let id =self . frames . len (); 187let ( claim, waiting_claim) =match self . claim_by_name . get ( name) { 188Some ( & claim) =>( claim, Some ( claim)), 189None =>{ 190let claim =self . claims . len (); 191self . claims . push ( Claim { 192owner : Some ( id), 193outcome : None , 194fingerprint : None , 195dispatched : false , 196}); 197self . claim_by_name . insert ( name. to_owned (), claim); 198( claim, None ) 199} 200}; 201self . frames . push ( Frame { 202name : name. to_owned (), 203 claim, 204 parent, 205 dep_index, 206 env, 207 ancestors, 208dep_outcomes : vec! [ None ; task. dependencies . len ()], 209active_children : HashSet :: new (), 210deps_started : false , 211 waiting_claim, 212dispatched : false , 213surfaced : false , 214done : false , 215}); 216Ok ( id) 217} 218 219fn complete ( & mut self , frame_id : usize , code : u8 ) { 220let ( claim, parent, dep_index) ={ 221let frame =& mut self . frames [ frame_id]; 222if frame. done { 223return ; 224} 225 frame. done =true ; 226( frame. claim , frame. parent , frame. dep_index ) 227}; 228if self . claims [ claim]. owner ==Some ( frame_id) &&self . claims [ claim]. outcome . is_none () { 229self . claims [ claim]. outcome =Some ( code); 230let name =self . frames [ frame_id]. name . clone (); 231self . completed . push ( name); 232} 233if let Some ( parent) = parent{ 234self . frames [ parent]. dep_outcomes [ dep_index] =Some ( code); 235self . frames [ parent]. active_children . remove ( & frame_id); 236} 237} 238 239/// Advances every frame whose dependencies have resolved, marking 240/// cached tasks complete and dispatchable tasks ready. Returns whether 241/// anything changed. 242fn advance ( & mut self ) ->Result < bool > { 243let mut progressed =false ; 244let mut index =0 ; 245while index <self . frames . len () { 246let frame =& self . frames [ index]; 247if frame. done || frame. dispatched { 248 index +=1 ; 249continue ; 250} 251// A frame borrowing another frame's in-flight execution 252// completes with that execution's outcome. 253if let Some ( claim) = frame. waiting_claim { 254if let Some ( code) =self . claims [ claim]. outcome { 255self . complete ( index, code); 256 progressed =true ; 257} 258 index +=1 ; 259continue ; 260} 261let task =self . graph . get ( & frame. name ) ?. clone (); 262 263// Start dependencies. 264if !frame. deps_started || !task. parallel && frame. active_children . is_empty () { 265let next =if task. parallel { 266if frame. deps_started { 267None 268} else { 269Some ( 0 ..task. dependencies . len ()) 270} 271} else { 272// Sequential: a failed dependency ends the task now. 273if let Some ( code) = frame. dep_outcomes . iter (). flatten (). find ( |code|** code !=0 ) 274{ 275let code =* code; 276self . complete ( index, code); 277 progressed =true ; 278 index +=1 ; 279continue ; 280} 281let started = frame. dep_outcomes . iter (). filter ( |o| o. is_some ()). count () 282 + frame. active_children . len (); 283if started < task. dependencies . len () { 284Some ( started..started +1 ) 285} else { 286None 287} 288}; 289if let Some ( range) = next{ 290self . frames [ index]. deps_started =true ; 291let env =self . frames [ index]. env . clone (); 292let mut child_ancestors =self . frames [ index]. ancestors . clone (); 293 child_ancestors. push ( self . frames [ index]. name . clone ()); 294for dep_indexin range{ 295let dependency = task. dependencies [ dep_index]. clone (); 296let child =self . create_frame ( 297Some ( index), 298 dep_index, 299& dependency, 300& env, 301 child_ancestors. clone (), 302) ?; 303self . frames [ index]. active_children . insert ( child); 304} 305 progressed =true ; 306 index +=1 ; 307continue ; 308} 309} 310if !self . frames [ index]. active_children . is_empty () { 311 index +=1 ; 312continue ; 313} 314if task. parallel 315 &&let Some ( _failed) = frame. dep_outcomes . iter (). flatten (). find ( |c|** c !=0 ) 316{ 317self . complete ( index, 1 ); 318 progressed =true ; 319 index +=1 ; 320continue ; 321} 322if !task. parallel 323 &&let Some ( code) = frame. dep_outcomes . iter (). flatten (). find ( |c|** c !=0 ) 324{ 325let code =* code; 326self . complete ( index, code); 327 progressed =true ; 328 index +=1 ; 329continue ; 330} 331 332// All dependencies succeeded: resolve this task's own action. 333match task. action { 334Action :: None =>{ 335self . complete ( index, 0 ); 336 progressed =true ; 337} 338 _ =>{ 339if task. files . is_some () { 340let args: Vec < String > =if index ==self . root_frame { 341self . extra_args . clone () 342} else { 343Vec :: new () 344}; 345let env =self . frames [ index]. env . clone (); 346let name =self . frames [ index]. name . clone (); 347let fingerprint =self . fingerprinter . fingerprint ( & name, & args, & env) ?; 348self . claims [ self . frames [ index]. claim ]. fingerprint = 349Some ( fingerprint. clone ()); 350let hit = !self . force 351 &&self . cache . as_ref (). is_some_and ( |cache|{ 352 cache 353. manifest_matches ( & self . root , & task, & fingerprint) 354. unwrap_or ( false ) 355}); 356if hit{ 357self . reporter . cached ( & name); 358self . cached . push ( name); 359self . complete ( index, 0 ); 360 progressed =true ; 361 index +=1 ; 362continue ; 363} 364} 365self . frames [ index]. dispatched =true ; 366let claim =self . frames [ index]. claim ; 367self . claims [ claim]. dispatched =true ; 368 progressed =true ; 369} 370} 371 index +=1 ; 372} 373Ok ( progressed) 374} 375 376pub fn step ( & mut self ) ->Result < Step > { 377// Advance until nothing else resolves without driver input. 378loop { 379if !self . advance () ?{ 380break ; 381} 382} 383if self . frames [ self . root_frame ]. done { 384return Ok ( Step :: Done ( self . report ( self . claim_code ()))); 385} 386if self . cancelled . is_cancelled () { 387return Ok ( Step :: Done ( self . report ( 1 ))); 388} 389let mut ready =Vec :: new (); 390let root_name =self . frames [ self . root_frame ]. name . clone (); 391for framein self . frames . iter_mut () { 392if frame. dispatched && !frame. surfaced && !frame. done { 393 frame. surfaced =true ; 394 ready. push ( ReadyTask { 395name : frame. name . clone (), 396action : self . graph . get ( & frame. name ). unwrap (). action . clone (), 397args : if frame. name == root_name{ 398self . extra_args . clone () 399} else { 400Vec :: new () 401}, 402env : frame. env . clone (), 403}); 404} 405} 406Ok ( Step :: Ready ( ready)) 407} 408 409/// Records the outcome of a task handed out by `step`. On success the 410/// task's outputs are committed to the cache. 411pub fn finish ( & mut self , name : & str , code : u8 ) { 412let Some ( frame_id) =self 413. frames 414. iter () 415. position ( |f| f. name == name && f. dispatched && !f. done ) 416else { 417return ; 418}; 419if code ==0 && !self . cancelled . is_cancelled () { 420let fingerprint =self . claims [ self . frames [ frame_id]. claim ]. fingerprint . clone (); 421if let ( Some ( cache), Some ( fingerprint)) =( & self . cache , fingerprint) { 422let task =self . graph . get ( name). unwrap (). clone (); 423if let Err ( error) = 424 cache. record_at ( & self . root , & task, & fingerprint, &* self . reporter , name) 425{ 426self . reporter . cache_error ( name, & error. to_string ()); 427} 428} 429} 430self . complete ( frame_id, code); 431} 432 433fn claim_code ( & self ) ->u8 { 434let claim =self . frames [ self . root_frame ]. claim ; 435self . claims [ claim]. outcome . unwrap_or ( 1 ) 436} 437 438fn report ( & self , code : u8 ) ->Report { 439let failed =self 440. claims 441. iter () 442. enumerate () 443. filter_map ( |( index, claim) |{ 444let code = claim. outcome ?; 445if code ==0 || !claim. dispatched { 446return None ; 447} 448let name =self 449. claim_by_name 450. iter () 451. find ( |& ( _, & claim_index) | claim_index == index) 452. map ( |( name, _) | name. clone ()) ?; 453Some ( FailedTask { name, code}) 454}) 455. collect (); 456Report { 457 code, 458completed : self . completed . clone (), 459 failed, 460cached : self . cached . clone (), 461} 462} 463}