// extractToolArgs extracts the build tool arguments from args. func extractToolArgs(args []string) toolArgs { if len(args) == 3 && args[0] == "/bin/bash" && args[1] == "-c" { actual, ok := shell.Split(args[2]) if !ok { log.Fatalf("Invalid shell-quoted argument %#q", args[2]) } args = actual } // Find the Go tool invocation, and return only that portion. var result toolArgs var wantArg *string inTool := false for _, arg := range args { // Discard arguments until the tool binary is found. if !inTool { if root := strings.TrimPrefix(arg, "GOROOT="); root != arg { result.goRoot = os.ExpandEnv(root) continue } else if ok, dir := isGoTool(arg); ok { inTool = true result.toolRoot = dir } else { continue } } result.fullArgs = append(result.fullArgs, arg) // Scan for important flags. if wantArg != nil { // capture argument for a previous flag *wantArg = arg wantArg = nil continue } if arg == "-p" { wantArg = &result.importPath } else if arg == "-o" { wantArg = &result.outputPath } else if arg == "-I" { wantArg = &result.includePath } else if !strings.HasPrefix(arg, "-") && strings.HasSuffix(arg, ".go") { result.sources = append(result.sources, arg) } } return result }
// parseBazelArgs extracts the compiler command line from the raw argument list // passed in by Bazel. The official Go rules currently pass in a response file // containing a shell script that we have to parse. func (c *Config) parseBazelArgs(ctx context.Context, args []string) (*bazelArgs, error) { if len(args) != 1 || filepath.Ext(args[0]) != ".params" { // This is some unusual case; assume the arguments are already parsed. return &bazelArgs{compile: args}, nil } // This is the expected case, a response file. result := &bazelArgs{paramsFile: args[0]} f, err := c.openFile(ctx, result.paramsFile) if err != nil { return nil, err } data, err := ioutil.ReadAll(f) f.Close() if err != nil { return nil, err } // Split up the response into lines, and split each line into commands // assuming a pipeline of the form "cmd1 && cmd2 && ...". // Bazel exports GOROOT and changes the working directory, both of which we // want for processing the compiler's argument list. var last []string for _, line := range strings.Split(string(data), "\n") { for _, cmd := range strings.Split(line, "&&") { trimmed := strings.TrimSpace(cmd) last, _ = shell.Split(trimmed) if m := rootVar.FindStringSubmatch(trimmed); m != nil { result.goRoot = m[1] } else if m := cdCommand.FindStringSubmatch(trimmed); m != nil { result.workDir = m[1] } } } result.compile = last return result, nil }