1*9bb1b549SSpandan Das// Copyright 2020, 2021 Google LLC 2*9bb1b549SSpandan Das// 3*9bb1b549SSpandan Das// Licensed under the Apache License, Version 2.0 (the "License"); 4*9bb1b549SSpandan Das// you may not use this file except in compliance with the License. 5*9bb1b549SSpandan Das// You may obtain a copy of the License at 6*9bb1b549SSpandan Das// 7*9bb1b549SSpandan Das// https://www.apache.org/licenses/LICENSE-2.0 8*9bb1b549SSpandan Das// 9*9bb1b549SSpandan Das// Unless required by applicable law or agreed to in writing, software 10*9bb1b549SSpandan Das// distributed under the License is distributed on an "AS IS" BASIS, 11*9bb1b549SSpandan Das// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9bb1b549SSpandan Das// See the License for the specific language governing permissions and 13*9bb1b549SSpandan Das// limitations under the License. 14*9bb1b549SSpandan Das 15*9bb1b549SSpandan Daspackage runfiles 16*9bb1b549SSpandan Das 17*9bb1b549SSpandan Dasimport ( 18*9bb1b549SSpandan Das "bufio" 19*9bb1b549SSpandan Das "fmt" 20*9bb1b549SSpandan Das "os" 21*9bb1b549SSpandan Das "path" 22*9bb1b549SSpandan Das "path/filepath" 23*9bb1b549SSpandan Das "strings" 24*9bb1b549SSpandan Das) 25*9bb1b549SSpandan Das 26*9bb1b549SSpandan Das// ManifestFile specifies the location of the runfile manifest file. You can 27*9bb1b549SSpandan Das// pass this as an option to New. If unset or empty, use the value of the 28*9bb1b549SSpandan Das// environmental variable RUNFILES_MANIFEST_FILE. 29*9bb1b549SSpandan Dastype ManifestFile string 30*9bb1b549SSpandan Das 31*9bb1b549SSpandan Dasfunc (f ManifestFile) new(sourceRepo SourceRepo) (*Runfiles, error) { 32*9bb1b549SSpandan Das m, err := f.parse() 33*9bb1b549SSpandan Das if err != nil { 34*9bb1b549SSpandan Das return nil, err 35*9bb1b549SSpandan Das } 36*9bb1b549SSpandan Das r := &Runfiles{ 37*9bb1b549SSpandan Das impl: m, 38*9bb1b549SSpandan Das env: manifestFileVar + "=" + string(f), 39*9bb1b549SSpandan Das sourceRepo: string(sourceRepo), 40*9bb1b549SSpandan Das } 41*9bb1b549SSpandan Das err = r.loadRepoMapping() 42*9bb1b549SSpandan Das return r, err 43*9bb1b549SSpandan Das} 44*9bb1b549SSpandan Das 45*9bb1b549SSpandan Dastype manifest map[string]string 46*9bb1b549SSpandan Das 47*9bb1b549SSpandan Dasfunc (f ManifestFile) parse() (manifest, error) { 48*9bb1b549SSpandan Das r, err := os.Open(string(f)) 49*9bb1b549SSpandan Das if err != nil { 50*9bb1b549SSpandan Das return nil, fmt.Errorf("runfiles: can’t open manifest file: %w", err) 51*9bb1b549SSpandan Das } 52*9bb1b549SSpandan Das defer r.Close() 53*9bb1b549SSpandan Das 54*9bb1b549SSpandan Das s := bufio.NewScanner(r) 55*9bb1b549SSpandan Das m := make(manifest) 56*9bb1b549SSpandan Das for s.Scan() { 57*9bb1b549SSpandan Das fields := strings.SplitN(s.Text(), " ", 2) 58*9bb1b549SSpandan Das if len(fields) != 2 || fields[0] == "" { 59*9bb1b549SSpandan Das return nil, fmt.Errorf("runfiles: bad manifest line %q in file %s", s.Text(), f) 60*9bb1b549SSpandan Das } 61*9bb1b549SSpandan Das m[fields[0]] = filepath.FromSlash(fields[1]) 62*9bb1b549SSpandan Das } 63*9bb1b549SSpandan Das 64*9bb1b549SSpandan Das if err := s.Err(); err != nil { 65*9bb1b549SSpandan Das return nil, fmt.Errorf("runfiles: error parsing manifest file %s: %w", f, err) 66*9bb1b549SSpandan Das } 67*9bb1b549SSpandan Das 68*9bb1b549SSpandan Das return m, nil 69*9bb1b549SSpandan Das} 70*9bb1b549SSpandan Das 71*9bb1b549SSpandan Dasfunc (m manifest) path(s string) (string, error) { 72*9bb1b549SSpandan Das r, ok := m[s] 73*9bb1b549SSpandan Das if ok && r == "" { 74*9bb1b549SSpandan Das return "", ErrEmpty 75*9bb1b549SSpandan Das } 76*9bb1b549SSpandan Das if ok { 77*9bb1b549SSpandan Das return r, nil 78*9bb1b549SSpandan Das } 79*9bb1b549SSpandan Das 80*9bb1b549SSpandan Das // If path references a runfile that lies under a directory that itself is a 81*9bb1b549SSpandan Das // runfile, then only the directory is listed in the manifest. Look up all 82*9bb1b549SSpandan Das // prefixes of path in the manifest. 83*9bb1b549SSpandan Das for prefix := s; prefix != ""; prefix, _ = path.Split(prefix) { 84*9bb1b549SSpandan Das prefix = strings.TrimSuffix(prefix, "/") 85*9bb1b549SSpandan Das if prefixMatch, ok := m[prefix]; ok { 86*9bb1b549SSpandan Das return prefixMatch + filepath.FromSlash(strings.TrimPrefix(s, prefix)), nil 87*9bb1b549SSpandan Das } 88*9bb1b549SSpandan Das } 89*9bb1b549SSpandan Das 90*9bb1b549SSpandan Das return "", os.ErrNotExist 91*9bb1b549SSpandan Das} 92