package main import ( "bufio" "io" "log" "os" "path/filepath" "regexp" "syscall" ) // The Python stub template that Bazel generates for every py_binary() // always starts with '#!/usr/bin/env python'. This means that even if // build actions provide their own copy of Python as part of the input // root, the remote execution environment must also provide a copy for // evaluating the stub template. // // This tool is a drop-in replacement for /usr/bin/python that is only // capable of accepting Python stub templates, executing them with the // copy of Python that is stored in the input root. It obtains the path // of the Python interpreter by extracting the PYTHON_BINARY constant // declared in the stub template. func extractPythonBinary(r io.Reader) string { s := bufio.NewScanner(r) pattern := regexp.MustCompile("^PYTHON_BINARY\\s*=\\s*'(.*)'$") for s.Scan() { if match := pattern.FindStringSubmatch(s.Text()); match != nil { return match[1] } } if err := s.Err(); err != nil { log.Fatal("Failed to read line from Python stub template: ", err) } log.Fatal("Python stub template does not contain a valid PYTHON_BINARY declaration") return "" } func main() { if len(os.Args) < 2 { log.Fatal("Fake Python can only be used to run Python stub templates generated by Bazel") } // Extract the "PYTHON_BINARY = '...'" line from the Python stub // template that was generated by Bazel. f, err := os.Open(os.Args[1]) if err != nil { log.Fatalf("Cannot open Python stub template %#v: %s", os.Args[1], err) } pythonBinary := extractPythonBinary(f) f.Close() // Determine the path inside the runfiles directory at which the // Python interpreter is placed. Tests have RUNFILES_DIR set, // while build actions do not. Look up the runfiles directory // manually if needed. runfilesDir, ok := os.LookupEnv("RUNFILES_DIR") if !ok { runfilesDir = os.Args[1] + ".runfiles" } pythonPath := filepath.Join(runfilesDir, pythonBinary) // Execute the Python stub template using the copy of Python // that it was going to use to run the actual program. log.Fatalf( "Failed to execute %#v: %s", pythonPath, syscall.Exec( pythonPath, append([]string{pythonPath}, os.Args[1:]...), os.Environ())) }