github.com/distbuild/reclient@v0.0.0-20240401075343-3de72e395564/internal/pkg/inputprocessor/action/r8/preprocessor.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 r8 performs include processing of r8 actions. 16 package r8 17 18 import ( 19 "bufio" 20 "context" 21 "fmt" 22 "os" 23 "path/filepath" 24 "strings" 25 26 "github.com/bazelbuild/reclient/internal/pkg/inputprocessor" 27 28 "github.com/bazelbuild/remote-apis-sdks/go/pkg/cache" 29 "github.com/bazelbuild/remote-apis-sdks/go/pkg/command" 30 ) 31 32 const ( 33 // includePrefix is used in flag files to indicate that another flag file is included. 34 includePrefix = "-include " 35 ) 36 37 var ( 38 r8Cache = cache.SingleFlight{} 39 ) 40 41 // Preprocessor is the context for processing tool type actions. 42 type Preprocessor struct { 43 *inputprocessor.BasePreprocessor 44 } 45 46 // ParseFlags parses the commands flags and populates the ActionSpec object with inferred 47 // information. 48 func (p *Preprocessor) ParseFlags() error { 49 f, err := parseFlags(p.Ctx, p.Options.Cmd, p.Options.WorkingDir, p.Options.ExecRoot) 50 if err != nil { 51 p.Err = fmt.Errorf("flag parsing failed. %v", err) 52 return p.Err 53 } 54 p.Flags = f 55 p.FlagsToActionSpec() 56 return nil 57 } 58 59 // ComputeSpec computes any further action specification that is not immediately inferrable 60 // from flags or toolchain configuration. 61 func (p *Preprocessor) ComputeSpec() error { 62 s := &inputprocessor.ActionSpec{InputSpec: &command.InputSpec{}} 63 64 // Add including flag files. 65 for _, dep := range p.Flags.Dependencies { 66 if filepath.Ext(dep) == ".txt" || filepath.Ext(dep) == ".flags" { 67 depIncludes, err := p.includesInFlagsFile(p.Ctx, p.Options.ExecRoot, p.Options.WorkingDir, dep) 68 if err != nil { 69 return err 70 } 71 s.InputSpec.Inputs = append(s.InputSpec.Inputs, depIncludes...) 72 } 73 } 74 p.AppendSpec(s) 75 76 // Add output directory as virtual input. 77 s, err := p.Spec() 78 if err != nil { 79 return err 80 } 81 var vi []*command.VirtualInput 82 for _, od := range s.OutputDirectories { 83 vi = append(vi, &command.VirtualInput{Path: od, IsEmptyDirectory: true}) 84 } 85 p.AppendSpec(&inputprocessor.ActionSpec{ 86 InputSpec: &command.InputSpec{VirtualInputs: vi}, 87 }) 88 return nil 89 } 90 91 func (p *Preprocessor) includesInFlagsFile(ctx context.Context, execRoot, workingDir, f string) ([]string, error) { 92 compute := func() (interface{}, error) { 93 file, err := os.Open(filepath.Join(execRoot, workingDir, f)) 94 if err != nil { 95 return nil, err 96 } 97 defer file.Close() 98 99 var includes []string 100 scanner := bufio.NewScanner(file) 101 for scanner.Scan() { 102 if !strings.HasPrefix(scanner.Text(), includePrefix) { 103 continue 104 } 105 path := scanner.Text()[len(includePrefix):] 106 includes = append(includes, filepath.Join(filepath.Dir(f), path)) 107 } 108 109 if err := scanner.Err(); err != nil { 110 return nil, err 111 } 112 return includes, nil 113 } 114 files, err := r8Cache.LoadOrStore(f, compute) 115 if err != nil { 116 return nil, err 117 } 118 v, ok := files.([]string) 119 if !ok { 120 return nil, fmt.Errorf("unexpected type stored in the cache: %v", files) 121 } 122 var includes []string 123 for _, inc := range v { 124 includes = append(includes, inc) 125 subIncludes, err := p.includesInFlagsFile(ctx, execRoot, workingDir, inc) 126 if err != nil { 127 return nil, err 128 } 129 includes = append(includes, subIncludes...) 130 } 131 return includes, nil 132 }