diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go index 506462680..e3e68c99c 100644 --- a/cmd/sbox/sbox.go +++ b/cmd/sbox/sbox.go @@ -32,7 +32,7 @@ func main() { } } -var usage = "Usage: sbox -c --sandbox-path --output-root [...]\n" + +var usage = "Usage: sbox -c --sandbox-path --output-root [--depfile-out depFile] [...]\n" + "\n" + "Runs and moves each out of \n" + "If any file in is specified by absolute path, then must be specified as well,\n" + @@ -43,13 +43,18 @@ func usageError(violation string) error { } func run() error { - var outFiles []string + // the contents of the __SBOX_OUT_FILES__ variable + var outputsVarEntries []string + // all outputs + var allOutputs []string + args := os.Args[1:] var rawCommand string var sandboxesRoot string removeTempDir := true var outputRoot string + var depfile string for i := 0; i < len(args); i++ { arg := args[i] @@ -64,17 +69,20 @@ func run() error { i++ } else if arg == "--keep-out-dir" { removeTempDir = false + } else if arg == "--depfile-out" { + depfile = args[i+1] + i++ } else { - outFiles = append(outFiles, arg) + outputsVarEntries = append(outputsVarEntries, arg) } } - if len(rawCommand) == 0 { + if rawCommand == "" { return usageError("-c is required and must be non-empty") } - if outFiles == nil { + if len(outputsVarEntries) == 0 { return usageError("at least one output file must be given") } - if len(sandboxesRoot) == 0 { + if sandboxesRoot == "" { // In practice, the value of sandboxesRoot will mostly likely be at a fixed location relative to OUT_DIR, // and the sbox executable will most likely be at a fixed location relative to OUT_DIR too, so // the value of sandboxesRoot will most likely be at a fixed location relative to the sbox executable @@ -82,25 +90,39 @@ func run() error { // and by passing it as a parameter we don't need to duplicate its value return usageError("--sandbox-path is required and must be non-empty") } - - // Rewrite output file paths to be relative to output root - // This facilitates matching them up against the corresponding paths in the temporary directory in case they're absolute - for i, filePath := range outFiles { - if path.IsAbs(filePath) { - if len(outputRoot) == 0 { - return fmt.Errorf("Absolute path %s requires nonempty value for --output-root", filePath) - } - } - relativePath, err := filepath.Rel(outputRoot, filePath) - if err != nil { - return err - } - outFiles[i] = relativePath + if len(outputRoot) == 0 { + return usageError("--output-root is required and must be non-empty") } os.MkdirAll(sandboxesRoot, 0777) tempDir, err := ioutil.TempDir(sandboxesRoot, "sbox") + + // Rewrite output file paths to be relative to output root + // This facilitates matching them up against the corresponding paths in the temporary directory in case they're absolute + for i, filePath := range outputsVarEntries { + relativePath, err := filepath.Rel(outputRoot, filePath) + if err != nil { + return err + } + outputsVarEntries[i] = relativePath + } + + allOutputs = append([]string(nil), outputsVarEntries...) + + if depfile != "" { + sandboxedDepfile, err := filepath.Rel(outputRoot, depfile) + if err != nil { + return err + } + allOutputs = append(allOutputs, sandboxedDepfile) + if !strings.Contains(rawCommand, "__SBOX_DEPFILE__") { + return fmt.Errorf("the --depfile-out argument only makes sense if the command contains the text __SBOX_DEPFILE__") + } + rawCommand = strings.Replace(rawCommand, "__SBOX_DEPFILE__", filepath.Join(tempDir, sandboxedDepfile), -1) + + } + if err != nil { return fmt.Errorf("Failed to create temp dir: %s", err) } @@ -122,7 +144,7 @@ func run() error { if strings.Contains(rawCommand, "__SBOX_OUT_FILES__") { // expands into a space-separated list of output files to be generated into the sandbox directory tempOutPaths := []string{} - for _, outputPath := range outFiles { + for _, outputPath := range outputsVarEntries { tempOutPath := path.Join(tempDir, outputPath) tempOutPaths = append(tempOutPaths, tempOutPath) } @@ -130,8 +152,12 @@ func run() error { rawCommand = strings.Replace(rawCommand, "__SBOX_OUT_FILES__", pathsText, -1) } - for _, filePath := range outFiles { - os.MkdirAll(path.Join(tempDir, filepath.Dir(filePath)), 0777) + for _, filePath := range allOutputs { + dir := path.Join(tempDir, filepath.Dir(filePath)) + err = os.MkdirAll(dir, 0777) + if err != nil { + return err + } } commandDescription := rawCommand @@ -150,7 +176,7 @@ func run() error { // validate that all files are created properly var outputErrors []error - for _, filePath := range outFiles { + for _, filePath := range allOutputs { tempPath := filepath.Join(tempDir, filePath) fileInfo, err := os.Stat(tempPath) if err != nil { @@ -168,7 +194,7 @@ func run() error { return fmt.Errorf("mismatch between declared and actual outputs in sbox command (%s):\n%v", commandDescription, outputErrors) } // the created files match the declared files; now move them - for _, filePath := range outFiles { + for _, filePath := range allOutputs { tempPath := filepath.Join(tempDir, filePath) destPath := filePath if len(outputRoot) != 0 { diff --git a/genrule/genrule.go b/genrule/genrule.go index 03e10ba43..6af18eeba 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -16,7 +16,6 @@ package genrule import ( "fmt" - "path" "strings" "github.com/google/blueprint" @@ -139,6 +138,17 @@ func (g *Module) DepsMutator(ctx android.BottomUpMutatorContext) { } } +// Given an output file, returns an expression for the corresponding file path within the sandbox +func sandboxPathForOutput(ctx android.ModuleContext, outputFile string) (relative string, err error) { + var relativePath string + basedir := ctx.AConfig().BuildDir() + relativePath, err = filepath.Rel(basedir, outputFile) + if err != nil { + return "", err + } + return filepath.Join("__SBOX_OUT_DIR__", relativePath), nil +} + func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 { ctx.ModuleErrorf("at least one `tools` or `tool_files` is required") @@ -209,6 +219,8 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { } } + referencedDepfile := false + rawCommand, err := android.Expand(g.properties.Cmd, func(name string) (string, error) { switch name { case "location": @@ -222,20 +234,17 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { case "out": return "__SBOX_OUT_FILES__", nil case "depfile": + referencedDepfile = true if !g.properties.Depfile { return "", fmt.Errorf("$(depfile) used without depfile property") } - return "${depfile}", nil + return "__SBOX_DEPFILE__", nil case "genDir": - genPath := android.PathForModuleGen(ctx, "").String() - var relativePath string - var err error - outputPath := android.PathForOutput(ctx).String() - relativePath, err = filepath.Rel(outputPath, genPath) + path, err := sandboxPathForOutput(ctx, android.PathForModuleGen(ctx, "").String()) if err != nil { - panic(err) + return "", err } - return path.Join("__SBOX_OUT_DIR__", relativePath), nil + return path, nil default: if strings.HasPrefix(name, "location ") { label := strings.TrimSpace(strings.TrimPrefix(name, "location ")) @@ -249,6 +258,10 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { } }) + if g.properties.Depfile && !referencedDepfile { + ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd") + } + if err != nil { ctx.PropertyErrorf("cmd", "%s", err.Error()) return @@ -260,7 +273,11 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { // recall that Sprintf replaces percent sign expressions, whereas dollar signs expressions remain as written, // to be replaced later by ninja_strings.go - sandboxCommand := fmt.Sprintf("$sboxCmd --sandbox-path %s --output-root %s -c %q $allouts", sandboxPath, buildDir, rawCommand) + depfilePlaceholder := "" + if g.properties.Depfile { + depfilePlaceholder = "$depfileArgs" + } + sandboxCommand := fmt.Sprintf("$sboxCmd --sandbox-path %s --output-root %s -c %q %s $allouts", sandboxPath, buildDir, rawCommand, depfilePlaceholder) ruleParams := blueprint.RuleParams{ Command: sandboxCommand, @@ -269,7 +286,7 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { args := []string{"allouts"} if g.properties.Depfile { ruleParams.Deps = blueprint.DepsGCC - args = append(args, "depfile") + args = append(args, "depfileArgs") } g.rule = ctx.Rule(pctx, "generator", ruleParams, args...) @@ -289,6 +306,11 @@ func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask desc += " " + task.out[0].Base() } + var depFile android.ModuleGenPath + if g.properties.Depfile { + depFile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d") + } + params := android.BuildParams{ Rule: g.rule, Description: "generate", @@ -301,9 +323,10 @@ func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask }, } if g.properties.Depfile { - depfile := android.GenPathWithExt(ctx, "", task.out[0], task.out[0].Ext()+".d") - params.Depfile = depfile + params.Depfile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d") + params.Args["depfileArgs"] = "--depfile-out " + depFile.String() } + ctx.Build(pctx, params) for _, outputFile := range task.out {