1- use miette:: { miette, IntoDiagnostic } ;
1+ use miette:: { miette, Context , IntoDiagnostic } ;
22use runfiles:: Runfiles ;
33use which:: which;
44// Depended on out of rules_rust
55use std:: env:: { self , current_exe} ;
66use std:: ffi:: OsStr ;
77use std:: fs;
8+ use std:: hash:: { DefaultHasher , Hash , Hasher } ;
89use std:: os:: unix:: process:: CommandExt ;
910use std:: path:: { Path , PathBuf } ;
1011use std:: process:: Command ;
@@ -187,14 +188,29 @@ fn find_actual_interpreter(executable: impl AsRef<Path>, cfg: &PyCfg) -> miette:
187188 Ok ( actual_interpreter_path. to_owned ( ) )
188189 }
189190 InterpreterConfig :: Runfiles { rpath, repo } => {
190- let r = Runfiles :: create ( executable) . unwrap ( ) ;
191- if let Some ( interpreter) = r. rlocation_from ( rpath. as_str ( ) , & repo) {
192- Ok ( PathBuf :: from ( interpreter) )
191+ if let Ok ( r) = Runfiles :: create ( executable) {
192+ if let Some ( interpreter) = r. rlocation_from ( rpath. as_str ( ) , & repo) {
193+ Ok ( PathBuf :: from ( interpreter) )
194+ } else {
195+ miette:: bail!( format!(
196+ "Unable to identify an interpreter for venv {:?}" ,
197+ cfg. interpreter,
198+ ) ) ;
199+ }
193200 } else {
201+ for candidate in [
202+ PathBuf :: from ( "external" ) . join ( & rpath) ,
203+ PathBuf :: from ( "bazel-out/k8-fastbuild/bin/external" ) . join ( & rpath) ,
204+ PathBuf :: from ( "." ) . join ( & rpath) ,
205+ PathBuf :: from ( "bazel-out/k8-fastbuild/bin" ) . join ( & rpath) ,
206+ ] {
207+ if candidate. exists ( ) {
208+ return Ok ( candidate) ;
209+ }
210+ }
194211 miette:: bail!( format!(
195- "Unable to identify an interpreter for venv {:?}" ,
196- cfg. interpreter,
197- ) ) ;
212+ "Unable to initialize runfiles and unable to identify action layout interpreter"
213+ ) )
198214 }
199215 }
200216 }
@@ -305,7 +321,9 @@ fn main() -> miette::Result<()> {
305321 #[ cfg( feature = "debug" ) ]
306322 eprintln ! ( "[aspect] {:?}" , venv_interpreter) ;
307323
308- let actual_interpreter = find_actual_interpreter ( & executable, & venv_config) ?;
324+ let actual_interpreter = find_actual_interpreter ( & executable, & venv_config) ?
325+ . canonicalize ( )
326+ . into_diagnostic ( ) ?;
309327
310328 #[ cfg( feature = "debug" ) ]
311329 eprintln ! (
@@ -325,23 +343,24 @@ fn main() -> miette::Result<()> {
325343
326344 let venv_bin = ( & venv_root) . join ( "bin" ) ;
327345 // TODO(arrdem|myrrlyn): PATHSEP is : on Unix and ; on Windows
328- let mut path_segments = env:: var ( "PATH" )
329- . into_diagnostic ( ) ? // if the variable is unset or not-utf-8, quit
330- . split ( ":" ) // break into individual entries
331- . filter ( |& p| !p. is_empty ( ) ) // skip over `::`, which is possible
332- . map ( ToOwned :: to_owned) // we're dropping the big string, so own the fragments
333- . collect :: < Vec < _ > > ( ) ; // and save them.
334- let need_venv_in_path = path_segments
335- . iter ( )
336- . find ( |& p| OsStr :: new ( p) == & venv_bin)
337- . is_none ( ) ;
338- if need_venv_in_path {
339- // append to back
340- path_segments. push ( venv_bin. to_string_lossy ( ) . into_owned ( ) ) ;
341- // then move venv_bin to the front of PATH
342- path_segments. rotate_right ( 1 ) ;
343- // and write into the child environment. this avoids an empty PATH causing us to write `{venv_bin}:` with a trailing colon
344- cmd. env ( "PATH" , path_segments. join ( ":" ) ) ;
346+ if let Ok ( path) = env:: var ( "PATH" ) {
347+ let mut path_segments = path
348+ . split ( ":" ) // break into individual entries
349+ . filter ( |& p| !p. is_empty ( ) ) // skip over `::`, which is possible
350+ . map ( ToOwned :: to_owned) // we're dropping the big string, so own the fragments
351+ . collect :: < Vec < _ > > ( ) ; // and save them.
352+ let need_venv_in_path = path_segments
353+ . iter ( )
354+ . find ( |& p| OsStr :: new ( p) == & venv_bin)
355+ . is_none ( ) ;
356+ if need_venv_in_path {
357+ // append to back
358+ path_segments. push ( venv_bin. to_string_lossy ( ) . into_owned ( ) ) ;
359+ // then move venv_bin to the front of PATH
360+ path_segments. rotate_right ( 1 ) ;
361+ // and write into the child environment. this avoids an empty PATH causing us to write `{venv_bin}:` with a trailing colon
362+ cmd. env ( "PATH" , path_segments. join ( ":" ) ) ;
363+ }
345364 }
346365
347366 // Set the executable pointer for MacOS, but we do it consistently
@@ -360,10 +379,39 @@ fn main() -> miette::Result<()> {
360379 // the home = property in the pyvenv.cfg being wrong because we don't
361380 // (currently) have a good way to map the interpreter rlocation to a
362381 // relative path.
363- cmd. env (
364- "PYTHONHOME" ,
365- & actual_interpreter. parent ( ) . unwrap ( ) . parent ( ) . unwrap ( ) ,
366- ) ;
382+ let home = & actual_interpreter
383+ . parent ( )
384+ . unwrap ( )
385+ . parent ( )
386+ . unwrap ( )
387+ . canonicalize ( )
388+ . into_diagnostic ( )
389+ . wrap_err ( "Failed to canonicalize the interpreter home" ) ?;
390+
391+ #[ cfg( feature = "debug" ) ]
392+ eprintln ! ( "Setting PYTHONHOME to {home:?} for {actual_interpreter:?}" ) ;
393+ cmd. env ( "PYTHONHOME" , home) ;
394+
395+ let mut hasher = DefaultHasher :: new ( ) ;
396+ venv_interpreter. to_str ( ) . unwrap ( ) . hash ( & mut hasher) ;
397+ home. to_str ( ) . unwrap ( ) . hash ( & mut hasher) ;
398+
399+ cmd. env ( "ASPECT_PY_VALIDITY" , format ! ( "{}" , hasher. finish( ) ) ) ;
400+
401+ // For the future, we could read, validate and reuse the env state.
402+ //
403+ // if let Ok(home) = env::var("PYTHONHOME") {
404+ // if let Ok(executable) = env::var("PYTHONEXECUTABLE") {
405+ // if let Ok(checksum) = env::var("ASPECT_PY_VALIDITY") {
406+ // let mut hasher = DefaultHasher::new();
407+ // executable.hash(&mut hasher);
408+ // home.hash(&mut hasher);
409+ // if checksum == format!("{}", hasher.finish()) {
410+ // return Ok(PathBuf::from(home).join("bin/python3"));
411+ // }
412+ // }
413+ // }
414+ // }
367415
368416 // And punt
369417 let err = cmd. exec ( ) ;
0 commit comments