sbox: best-effort copy output files on failure Error messages printed by failing commands may reference output files that were created by the command, for example printing a command line to copy and paste to update a baseline file. Copy output files in the sandbox to their final locations, ignoring missing files, so that the messages are valid. Bug: 185516277 Test: m out/soong/.intermediates/frameworks/base/system-api-stubs-docs-non-updatable/android_common/metalava/api_lint.timestamp with lint error Change-Id: I604a11c9b54e409ca5bc5c016cd04b3133f74a60
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go index 7bd0868..9736ff6 100644 --- a/cmd/sbox/sbox.go +++ b/cmd/sbox/sbox.go
@@ -230,7 +230,7 @@ } // Copy in any files specified by the manifest. - err = copyFiles(command.CopyBefore, "", tempDir) + err = copyFiles(command.CopyBefore, "", tempDir, false) if err != nil { return "", err } @@ -276,6 +276,14 @@ } err = cmd.Run() + if err != nil { + // The command failed, do a best effort copy of output files out of the sandbox. This is + // especially useful for linters with baselines that print an error message on failure + // with a command to copy the output lint errors to the new baseline. Use a copy instead of + // a move to leave the sandbox intact for manual inspection + copyFiles(command.CopyAfter, tempDir, "", true) + } + if exit, ok := err.(*exec.ExitError); ok && !exit.Success() { return "", fmt.Errorf("sbox command failed with err:\n%s\n%w\n", commandDescription, err) } else if err != nil { @@ -351,12 +359,13 @@ return missingOutputErrors } -// copyFiles copies files in or out of the sandbox. -func copyFiles(copies []*sbox_proto.Copy, fromDir, toDir string) error { +// copyFiles copies files in or out of the sandbox. If allowFromNotExists is true then errors +// caused by a from path not existing are ignored. +func copyFiles(copies []*sbox_proto.Copy, fromDir, toDir string, allowFromNotExists bool) error { for _, copyPair := range copies { fromPath := joinPath(fromDir, copyPair.GetFrom()) toPath := joinPath(toDir, copyPair.GetTo()) - err := copyOneFile(fromPath, toPath, copyPair.GetExecutable()) + err := copyOneFile(fromPath, toPath, copyPair.GetExecutable(), allowFromNotExists) if err != nil { return fmt.Errorf("error copying %q to %q: %w", fromPath, toPath, err) } @@ -364,8 +373,9 @@ return nil } -// copyOneFile copies a file. -func copyOneFile(from string, to string, executable bool) error { +// copyOneFile copies a file and its permissions. If forceExecutable is true it adds u+x to the +// permissions. If allowFromNotExists is true it returns nil if the from path doesn't exist. +func copyOneFile(from string, to string, forceExecutable, allowFromNotExists bool) error { err := os.MkdirAll(filepath.Dir(to), 0777) if err != nil { return err @@ -373,11 +383,14 @@ stat, err := os.Stat(from) if err != nil { + if os.IsNotExist(err) && allowFromNotExists { + return nil + } return err } perm := stat.Mode() - if executable { + if forceExecutable { perm = perm | 0100 // u+x } @@ -454,7 +467,7 @@ to := applyPathMappings(rspFile.PathMappings, from) // Copy the file into the sandbox. - err := copyOneFile(from, joinPath(toDir, to), false) + err := copyOneFile(from, joinPath(toDir, to), false, false) if err != nil { return err }