github.com/distbuild/reclient@v0.0.0-20240401075343-3de72e395564/internal/pkg/inputprocessor/action/javac/flagsparser.go (about) 1 // Copyright 2023 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package javac 16 17 import ( 18 "context" 19 "fmt" 20 "path/filepath" 21 "strings" 22 23 "github.com/bazelbuild/reclient/internal/pkg/inputprocessor/args" 24 "github.com/bazelbuild/reclient/internal/pkg/inputprocessor/flags" 25 "github.com/bazelbuild/reclient/internal/pkg/rsp" 26 ) 27 28 // parseFlags is used to transform a javac command into a CommandFlags structure. 29 func parseFlags(ctx context.Context, command []string, workingDir, execRoot string) (*flags.CommandFlags, error) { 30 numArgs := len(command) 31 if numArgs < 2 { 32 return nil, fmt.Errorf("insufficient number of arguments in command: %v", command) 33 } 34 35 res := &flags.CommandFlags{ 36 ExecutablePath: command[0], 37 WorkingDirectory: workingDir, 38 ExecRoot: execRoot, 39 } 40 s := args.Scanner{ 41 Args: command[1:], 42 Flags: map[string]int{ 43 "-bootclasspath": 1, 44 "-classpath": 1, 45 "-processorpath": 1, 46 "-d": 1, 47 "-s": 1, 48 }, 49 Joined: []args.PrefixOption{ 50 {"--system=", 0}, 51 {"-Aroom.schemaLocation=", 0}, 52 }, 53 } 54 for s.HasNext() { 55 if err := handleJavacFlags(res, &s); err != nil { 56 return nil, err 57 } 58 } 59 return res, nil 60 } 61 62 func handleJavacFlags(cmdFlags *flags.CommandFlags, scanner *args.Scanner) error { 63 nextRes := scanner.ReadNextFlag() 64 // A temporary CommandFlags structure is used to collect the various dependencies, flags and outputs in the handleArgFunc, 65 // and then the results of that are copied into the CommandFlags structure passed in (cmdFlags). 66 // This is done because if a flag is being processed that is in a rsp file, we don't want to add that flag to 67 // the Flags list; otherwise, the flags will show up on the remote bot twice, one time in the cmd itself, 68 // the second time inside the rsp file. Perhaps a cleaner option is to add a 69 // flag to the handleArgFunc that indicates if the argument is from the command line or from inside a rsp file. 70 f := &flags.CommandFlags{ 71 ExecRoot: cmdFlags.ExecRoot, 72 WorkingDirectory: cmdFlags.WorkingDirectory, 73 } 74 handleArgFunc := func(sc *args.Scanner) error { 75 curr := sc.CurResult 76 flag, args, values := curr.NormalizedKey, curr.Args, curr.Values 77 switch flag { 78 case "-bootclasspath", "-classpath", "-processorpath": 79 deps := strings.Split(values[0], ":") 80 for _, d := range deps { 81 // Exclude empty strings and . strings from dependencies. 82 if d == "" || d == "." { 83 continue 84 } 85 f.Dependencies = append(f.Dependencies, d) 86 } 87 case "--system=", "-Aroom.schemaLocation=": 88 f.Dependencies = append(f.Dependencies, values[0]) 89 case "-d", "-s": 90 f.OutputDirPaths = append(f.OutputDirPaths, values[0]) 91 case "": 92 f.Dependencies = append(f.Dependencies, args[0]) 93 } 94 95 for _, arg := range args { 96 f.Flags = append(f.Flags, &flags.Flag{Value: arg}) 97 } 98 return nil 99 } 100 // Check if this is a rsp file that needs processing or just a normal flag. 101 if strings.HasPrefix(nextRes.Args[0], "@") { 102 rspFile := nextRes.Args[0][1:] 103 cmdFlags.TargetFilePaths = append(cmdFlags.TargetFilePaths, rspFile) 104 if !filepath.IsAbs(rspFile) { 105 rspFile = filepath.Join(cmdFlags.ExecRoot, cmdFlags.WorkingDirectory, rspFile) 106 } 107 cmdFlags.Flags = append(cmdFlags.Flags, &flags.Flag{Value: nextRes.Args[0]}) 108 if err := rsp.ParseWithFunc(rspFile, *scanner, handleArgFunc); err != nil { 109 return err 110 } 111 // We don't want to pass along the flags that were in the rsp file, just the 112 // rsp file itself as a flag. 113 } else { 114 if err := handleArgFunc(scanner); err != nil { 115 return err 116 } 117 // We want to pass along flags that were on the command line. 118 cmdFlags.Flags = append(cmdFlags.Flags, f.Flags...) 119 } 120 cmdFlags.Dependencies = append(cmdFlags.Dependencies, f.Dependencies...) 121 cmdFlags.OutputDirPaths = append(cmdFlags.OutputDirPaths, f.OutputDirPaths...) 122 cmdFlags.OutputFilePaths = append(cmdFlags.OutputFilePaths, f.OutputFilePaths...) 123 return nil 124 }