1*9bb1b549SSpandan Das// Copyright 2018 The Bazel Authors. 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// http://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 bazel 16*9bb1b549SSpandan Das 17*9bb1b549SSpandan Dasimport ( 18*9bb1b549SSpandan Das "bytes" 19*9bb1b549SSpandan Das "errors" 20*9bb1b549SSpandan Das "fmt" 21*9bb1b549SSpandan Das "io/ioutil" 22*9bb1b549SSpandan Das "os" 23*9bb1b549SSpandan Das "path" 24*9bb1b549SSpandan Das "path/filepath" 25*9bb1b549SSpandan Das "runtime" 26*9bb1b549SSpandan Das "sort" 27*9bb1b549SSpandan Das "strings" 28*9bb1b549SSpandan Das "sync" 29*9bb1b549SSpandan Das) 30*9bb1b549SSpandan Das 31*9bb1b549SSpandan Dasconst ( 32*9bb1b549SSpandan Das RUNFILES_MANIFEST_FILE = "RUNFILES_MANIFEST_FILE" 33*9bb1b549SSpandan Das RUNFILES_DIR = "RUNFILES_DIR" 34*9bb1b549SSpandan Das) 35*9bb1b549SSpandan Das 36*9bb1b549SSpandan Das// Runfile returns an absolute path to the file named by "path", which 37*9bb1b549SSpandan Das// should be a relative path from the workspace root to the file within 38*9bb1b549SSpandan Das// the bazel workspace. 39*9bb1b549SSpandan Das// 40*9bb1b549SSpandan Das// Runfile may be called from tests invoked with 'bazel test' and 41*9bb1b549SSpandan Das// binaries invoked with 'bazel run'. On Windows, 42*9bb1b549SSpandan Das// only tests invoked with 'bazel test' are supported. 43*9bb1b549SSpandan Das// 44*9bb1b549SSpandan Das// Deprecated: Use github.com/bazelbuild/rules_go/go/runfiles instead for 45*9bb1b549SSpandan Das// cross-platform support matching the behavior of the Bazel-provided runfiles 46*9bb1b549SSpandan Das// libraries. 47*9bb1b549SSpandan Dasfunc Runfile(path string) (string, error) { 48*9bb1b549SSpandan Das // Search in working directory 49*9bb1b549SSpandan Das if _, err := os.Stat(path); err == nil { 50*9bb1b549SSpandan Das return filepath.Abs(path) 51*9bb1b549SSpandan Das } 52*9bb1b549SSpandan Das 53*9bb1b549SSpandan Das if err := ensureRunfiles(); err != nil { 54*9bb1b549SSpandan Das return "", err 55*9bb1b549SSpandan Das } 56*9bb1b549SSpandan Das 57*9bb1b549SSpandan Das // Search manifest if we have one. 58*9bb1b549SSpandan Das if entry, ok := runfiles.index.GetIgnoringWorkspace(path); ok { 59*9bb1b549SSpandan Das return entry.Path, nil 60*9bb1b549SSpandan Das } 61*9bb1b549SSpandan Das 62*9bb1b549SSpandan Das if strings.HasPrefix(path, "../") || strings.HasPrefix(path, "external/") { 63*9bb1b549SSpandan Das pathParts := strings.Split(path, "/") 64*9bb1b549SSpandan Das if len(pathParts) >= 3 { 65*9bb1b549SSpandan Das workspace := pathParts[1] 66*9bb1b549SSpandan Das pathInsideWorkspace := strings.Join(pathParts[2:], "/") 67*9bb1b549SSpandan Das if path := runfiles.index.Get(workspace, pathInsideWorkspace); path != "" { 68*9bb1b549SSpandan Das return path, nil 69*9bb1b549SSpandan Das } 70*9bb1b549SSpandan Das } 71*9bb1b549SSpandan Das } 72*9bb1b549SSpandan Das 73*9bb1b549SSpandan Das // Search the main workspace. 74*9bb1b549SSpandan Das if runfiles.workspace != "" { 75*9bb1b549SSpandan Das mainPath := filepath.Join(runfiles.dir, runfiles.workspace, path) 76*9bb1b549SSpandan Das if _, err := os.Stat(mainPath); err == nil { 77*9bb1b549SSpandan Das return mainPath, nil 78*9bb1b549SSpandan Das } 79*9bb1b549SSpandan Das } 80*9bb1b549SSpandan Das 81*9bb1b549SSpandan Das // Search other workspaces. 82*9bb1b549SSpandan Das for _, w := range runfiles.workspaces { 83*9bb1b549SSpandan Das workPath := filepath.Join(runfiles.dir, w, path) 84*9bb1b549SSpandan Das if _, err := os.Stat(workPath); err == nil { 85*9bb1b549SSpandan Das return workPath, nil 86*9bb1b549SSpandan Das } 87*9bb1b549SSpandan Das } 88*9bb1b549SSpandan Das 89*9bb1b549SSpandan Das return "", fmt.Errorf("Runfile %s: could not locate file", path) 90*9bb1b549SSpandan Das} 91*9bb1b549SSpandan Das 92*9bb1b549SSpandan Das// FindBinary returns an absolute path to the binary built from a go_binary 93*9bb1b549SSpandan Das// rule in the given package with the given name. FindBinary is similar to 94*9bb1b549SSpandan Das// Runfile, but it accounts for varying configurations and file extensions, 95*9bb1b549SSpandan Das// which may cause the binary to have different paths on different platforms. 96*9bb1b549SSpandan Das// 97*9bb1b549SSpandan Das// FindBinary may be called from tests invoked with 'bazel test' and 98*9bb1b549SSpandan Das// binaries invoked with 'bazel run'. On Windows, 99*9bb1b549SSpandan Das// only tests invoked with 'bazel test' are supported. 100*9bb1b549SSpandan Dasfunc FindBinary(pkg, name string) (string, bool) { 101*9bb1b549SSpandan Das if err := ensureRunfiles(); err != nil { 102*9bb1b549SSpandan Das return "", false 103*9bb1b549SSpandan Das } 104*9bb1b549SSpandan Das 105*9bb1b549SSpandan Das // If we've gathered a list of runfiles, either by calling ListRunfiles or 106*9bb1b549SSpandan Das // parsing the manifest on Windows, just use that instead of searching 107*9bb1b549SSpandan Das // directories. Return the first match. The manifest on Windows may contain 108*9bb1b549SSpandan Das // multiple entries for the same file. 109*9bb1b549SSpandan Das if runfiles.list != nil { 110*9bb1b549SSpandan Das if runtime.GOOS == "windows" { 111*9bb1b549SSpandan Das name += ".exe" 112*9bb1b549SSpandan Das } 113*9bb1b549SSpandan Das for _, entry := range runfiles.list { 114*9bb1b549SSpandan Das if path.Base(entry.ShortPath) != name { 115*9bb1b549SSpandan Das continue 116*9bb1b549SSpandan Das } 117*9bb1b549SSpandan Das pkgDir := path.Dir(path.Dir(entry.ShortPath)) 118*9bb1b549SSpandan Das if pkgDir == "." { 119*9bb1b549SSpandan Das pkgDir = "" 120*9bb1b549SSpandan Das } 121*9bb1b549SSpandan Das if pkgDir != pkg { 122*9bb1b549SSpandan Das continue 123*9bb1b549SSpandan Das } 124*9bb1b549SSpandan Das return entry.Path, true 125*9bb1b549SSpandan Das } 126*9bb1b549SSpandan Das return "", false 127*9bb1b549SSpandan Das } 128*9bb1b549SSpandan Das 129*9bb1b549SSpandan Das dir, err := Runfile(pkg) 130*9bb1b549SSpandan Das if err != nil { 131*9bb1b549SSpandan Das return "", false 132*9bb1b549SSpandan Das } 133*9bb1b549SSpandan Das var found string 134*9bb1b549SSpandan Das stopErr := errors.New("stop") 135*9bb1b549SSpandan Das err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 136*9bb1b549SSpandan Das if err != nil { 137*9bb1b549SSpandan Das return err 138*9bb1b549SSpandan Das } 139*9bb1b549SSpandan Das if info.IsDir() { 140*9bb1b549SSpandan Das return nil 141*9bb1b549SSpandan Das } 142*9bb1b549SSpandan Das base := filepath.Base(path) 143*9bb1b549SSpandan Das stem := strings.TrimSuffix(base, ".exe") 144*9bb1b549SSpandan Das if stem != name { 145*9bb1b549SSpandan Das return nil 146*9bb1b549SSpandan Das } 147*9bb1b549SSpandan Das if runtime.GOOS != "windows" { 148*9bb1b549SSpandan Das if st, err := os.Stat(path); err != nil { 149*9bb1b549SSpandan Das return err 150*9bb1b549SSpandan Das } else if st.Mode()&0111 == 0 { 151*9bb1b549SSpandan Das return nil 152*9bb1b549SSpandan Das } 153*9bb1b549SSpandan Das } 154*9bb1b549SSpandan Das if stem == name { 155*9bb1b549SSpandan Das found = path 156*9bb1b549SSpandan Das return stopErr 157*9bb1b549SSpandan Das } 158*9bb1b549SSpandan Das return nil 159*9bb1b549SSpandan Das }) 160*9bb1b549SSpandan Das if err == stopErr { 161*9bb1b549SSpandan Das return found, true 162*9bb1b549SSpandan Das } else { 163*9bb1b549SSpandan Das return "", false 164*9bb1b549SSpandan Das } 165*9bb1b549SSpandan Das} 166*9bb1b549SSpandan Das 167*9bb1b549SSpandan Das// A RunfileEntry describes a runfile. 168*9bb1b549SSpandan Dastype RunfileEntry struct { 169*9bb1b549SSpandan Das // Workspace is the bazel workspace the file came from. For example, 170*9bb1b549SSpandan Das // this would be "io_bazel_rules_go" for a file in rules_go. 171*9bb1b549SSpandan Das Workspace string 172*9bb1b549SSpandan Das 173*9bb1b549SSpandan Das // ShortPath is a relative, slash-separated path from the workspace root 174*9bb1b549SSpandan Das // to the file. For non-binary files, this may be passed to Runfile 175*9bb1b549SSpandan Das // to locate a file. 176*9bb1b549SSpandan Das ShortPath string 177*9bb1b549SSpandan Das 178*9bb1b549SSpandan Das // Path is an absolute path to the file. 179*9bb1b549SSpandan Das Path string 180*9bb1b549SSpandan Das} 181*9bb1b549SSpandan Das 182*9bb1b549SSpandan Das// ListRunfiles returns a list of available runfiles. 183*9bb1b549SSpandan Dasfunc ListRunfiles() ([]RunfileEntry, error) { 184*9bb1b549SSpandan Das if err := ensureRunfiles(); err != nil { 185*9bb1b549SSpandan Das return nil, err 186*9bb1b549SSpandan Das } 187*9bb1b549SSpandan Das 188*9bb1b549SSpandan Das if runfiles.list == nil && runfiles.dir != "" { 189*9bb1b549SSpandan Das runfiles.listOnce.Do(func() { 190*9bb1b549SSpandan Das var list []RunfileEntry 191*9bb1b549SSpandan Das haveWorkspaces := strings.HasSuffix(runfiles.dir, ".runfiles") && runfiles.workspace != "" 192*9bb1b549SSpandan Das 193*9bb1b549SSpandan Das err := filepath.Walk(runfiles.dir, func(path string, info os.FileInfo, err error) error { 194*9bb1b549SSpandan Das if err != nil { 195*9bb1b549SSpandan Das return err 196*9bb1b549SSpandan Das } 197*9bb1b549SSpandan Das rel, _ := filepath.Rel(runfiles.dir, path) 198*9bb1b549SSpandan Das rel = filepath.ToSlash(rel) 199*9bb1b549SSpandan Das if rel == "." { 200*9bb1b549SSpandan Das return nil 201*9bb1b549SSpandan Das } 202*9bb1b549SSpandan Das 203*9bb1b549SSpandan Das var workspace, shortPath string 204*9bb1b549SSpandan Das if haveWorkspaces { 205*9bb1b549SSpandan Das if i := strings.IndexByte(rel, '/'); i < 0 { 206*9bb1b549SSpandan Das return nil 207*9bb1b549SSpandan Das } else { 208*9bb1b549SSpandan Das workspace, shortPath = rel[:i], rel[i+1:] 209*9bb1b549SSpandan Das } 210*9bb1b549SSpandan Das } else { 211*9bb1b549SSpandan Das workspace, shortPath = "", rel 212*9bb1b549SSpandan Das } 213*9bb1b549SSpandan Das 214*9bb1b549SSpandan Das list = append(list, RunfileEntry{Workspace: workspace, ShortPath: shortPath, Path: path}) 215*9bb1b549SSpandan Das return nil 216*9bb1b549SSpandan Das }) 217*9bb1b549SSpandan Das if err != nil { 218*9bb1b549SSpandan Das runfiles.err = err 219*9bb1b549SSpandan Das return 220*9bb1b549SSpandan Das } 221*9bb1b549SSpandan Das runfiles.list = list 222*9bb1b549SSpandan Das }) 223*9bb1b549SSpandan Das } 224*9bb1b549SSpandan Das return runfiles.list, runfiles.err 225*9bb1b549SSpandan Das} 226*9bb1b549SSpandan Das 227*9bb1b549SSpandan Das// TestWorkspace returns the name of the Bazel workspace for this test. 228*9bb1b549SSpandan Das// TestWorkspace returns an error if the TEST_WORKSPACE environment variable 229*9bb1b549SSpandan Das// was not set or SetDefaultTestWorkspace was not called. 230*9bb1b549SSpandan Dasfunc TestWorkspace() (string, error) { 231*9bb1b549SSpandan Das if err := ensureRunfiles(); err != nil { 232*9bb1b549SSpandan Das return "", err 233*9bb1b549SSpandan Das } 234*9bb1b549SSpandan Das if runfiles.workspace != "" { 235*9bb1b549SSpandan Das return runfiles.workspace, nil 236*9bb1b549SSpandan Das } 237*9bb1b549SSpandan Das return "", errors.New("TEST_WORKSPACE not set and SetDefaultTestWorkspace not called") 238*9bb1b549SSpandan Das} 239*9bb1b549SSpandan Das 240*9bb1b549SSpandan Das// SetDefaultTestWorkspace allows you to set a fake value for the 241*9bb1b549SSpandan Das// environment variable TEST_WORKSPACE if it is not defined. This is useful 242*9bb1b549SSpandan Das// when running tests on the command line and not through Bazel. 243*9bb1b549SSpandan Dasfunc SetDefaultTestWorkspace(w string) { 244*9bb1b549SSpandan Das ensureRunfiles() 245*9bb1b549SSpandan Das runfiles.workspace = w 246*9bb1b549SSpandan Das} 247*9bb1b549SSpandan Das 248*9bb1b549SSpandan Das// RunfilesPath return the path to the runfiles tree. 249*9bb1b549SSpandan Das// It will return an error if there is no runfiles tree, for example because 250*9bb1b549SSpandan Das// the executable is run on Windows or was not invoked with 'bazel test' 251*9bb1b549SSpandan Das// or 'bazel run'. 252*9bb1b549SSpandan Dasfunc RunfilesPath() (string, error) { 253*9bb1b549SSpandan Das if err := ensureRunfiles(); err != nil { 254*9bb1b549SSpandan Das return "", err 255*9bb1b549SSpandan Das } 256*9bb1b549SSpandan Das if runfiles.dir == "" { 257*9bb1b549SSpandan Das if runtime.GOOS == "windows" { 258*9bb1b549SSpandan Das return "", errors.New("RunfilesPath: no runfiles directory on windows") 259*9bb1b549SSpandan Das } else { 260*9bb1b549SSpandan Das return "", errors.New("could not locate runfiles directory") 261*9bb1b549SSpandan Das } 262*9bb1b549SSpandan Das } 263*9bb1b549SSpandan Das if runfiles.workspace == "" { 264*9bb1b549SSpandan Das return "", errors.New("could not locate runfiles workspace") 265*9bb1b549SSpandan Das } 266*9bb1b549SSpandan Das return filepath.Join(runfiles.dir, runfiles.workspace), nil 267*9bb1b549SSpandan Das} 268*9bb1b549SSpandan Das 269*9bb1b549SSpandan Dasvar runfiles = struct { 270*9bb1b549SSpandan Das once, listOnce sync.Once 271*9bb1b549SSpandan Das 272*9bb1b549SSpandan Das // list is a list of known runfiles, either loaded from the manifest 273*9bb1b549SSpandan Das // or discovered by walking the runfile directory. 274*9bb1b549SSpandan Das list []RunfileEntry 275*9bb1b549SSpandan Das 276*9bb1b549SSpandan Das // index maps runfile short paths to absolute paths. 277*9bb1b549SSpandan Das index index 278*9bb1b549SSpandan Das 279*9bb1b549SSpandan Das // dir is a path to the runfile directory. Typically this is a directory 280*9bb1b549SSpandan Das // named <target>.runfiles, with a subdirectory for each workspace. 281*9bb1b549SSpandan Das dir string 282*9bb1b549SSpandan Das 283*9bb1b549SSpandan Das // workspace is workspace where the binary or test was built. 284*9bb1b549SSpandan Das workspace string 285*9bb1b549SSpandan Das 286*9bb1b549SSpandan Das // workspaces is a list of other workspace names. 287*9bb1b549SSpandan Das workspaces []string 288*9bb1b549SSpandan Das 289*9bb1b549SSpandan Das // err is set when there is an error loading runfiles, for example, 290*9bb1b549SSpandan Das // parsing the manifest. 291*9bb1b549SSpandan Das err error 292*9bb1b549SSpandan Das}{} 293*9bb1b549SSpandan Das 294*9bb1b549SSpandan Dastype index struct { 295*9bb1b549SSpandan Das indexWithWorkspace map[indexKey]*RunfileEntry 296*9bb1b549SSpandan Das indexIgnoringWorksapce map[string]*RunfileEntry 297*9bb1b549SSpandan Das} 298*9bb1b549SSpandan Das 299*9bb1b549SSpandan Dasfunc newIndex() index { 300*9bb1b549SSpandan Das return index{ 301*9bb1b549SSpandan Das indexWithWorkspace: make(map[indexKey]*RunfileEntry), 302*9bb1b549SSpandan Das indexIgnoringWorksapce: make(map[string]*RunfileEntry), 303*9bb1b549SSpandan Das } 304*9bb1b549SSpandan Das} 305*9bb1b549SSpandan Das 306*9bb1b549SSpandan Dasfunc (i *index) Put(entry *RunfileEntry) { 307*9bb1b549SSpandan Das i.indexWithWorkspace[indexKey{ 308*9bb1b549SSpandan Das workspace: entry.Workspace, 309*9bb1b549SSpandan Das shortPath: entry.ShortPath, 310*9bb1b549SSpandan Das }] = entry 311*9bb1b549SSpandan Das i.indexIgnoringWorksapce[entry.ShortPath] = entry 312*9bb1b549SSpandan Das} 313*9bb1b549SSpandan Das 314*9bb1b549SSpandan Dasfunc (i *index) Get(workspace string, shortPath string) string { 315*9bb1b549SSpandan Das entry := i.indexWithWorkspace[indexKey{ 316*9bb1b549SSpandan Das workspace: workspace, 317*9bb1b549SSpandan Das shortPath: shortPath, 318*9bb1b549SSpandan Das }] 319*9bb1b549SSpandan Das if entry == nil { 320*9bb1b549SSpandan Das return "" 321*9bb1b549SSpandan Das } 322*9bb1b549SSpandan Das return entry.Path 323*9bb1b549SSpandan Das} 324*9bb1b549SSpandan Das 325*9bb1b549SSpandan Dasfunc (i *index) GetIgnoringWorkspace(shortPath string) (*RunfileEntry, bool) { 326*9bb1b549SSpandan Das entry, ok := i.indexIgnoringWorksapce[shortPath] 327*9bb1b549SSpandan Das return entry, ok 328*9bb1b549SSpandan Das} 329*9bb1b549SSpandan Das 330*9bb1b549SSpandan Dastype indexKey struct { 331*9bb1b549SSpandan Das workspace string 332*9bb1b549SSpandan Das shortPath string 333*9bb1b549SSpandan Das} 334*9bb1b549SSpandan Das 335*9bb1b549SSpandan Dasfunc ensureRunfiles() error { 336*9bb1b549SSpandan Das runfiles.once.Do(initRunfiles) 337*9bb1b549SSpandan Das return runfiles.err 338*9bb1b549SSpandan Das} 339*9bb1b549SSpandan Das 340*9bb1b549SSpandan Dasfunc initRunfiles() { 341*9bb1b549SSpandan Das manifest := os.Getenv("RUNFILES_MANIFEST_FILE") 342*9bb1b549SSpandan Das if manifest != "" { 343*9bb1b549SSpandan Das // On Windows, Bazel doesn't create a symlink tree of runfiles because 344*9bb1b549SSpandan Das // Windows doesn't support symbolic links by default. Instead, runfile 345*9bb1b549SSpandan Das // locations are written to a manifest file. 346*9bb1b549SSpandan Das runfiles.index = newIndex() 347*9bb1b549SSpandan Das data, err := ioutil.ReadFile(manifest) 348*9bb1b549SSpandan Das if err != nil { 349*9bb1b549SSpandan Das runfiles.err = err 350*9bb1b549SSpandan Das return 351*9bb1b549SSpandan Das } 352*9bb1b549SSpandan Das lineno := 0 353*9bb1b549SSpandan Das for len(data) > 0 { 354*9bb1b549SSpandan Das i := bytes.IndexByte(data, '\n') 355*9bb1b549SSpandan Das var line []byte 356*9bb1b549SSpandan Das if i < 0 { 357*9bb1b549SSpandan Das line = data 358*9bb1b549SSpandan Das data = nil 359*9bb1b549SSpandan Das } else { 360*9bb1b549SSpandan Das line = data[:i] 361*9bb1b549SSpandan Das data = data[i+1:] 362*9bb1b549SSpandan Das } 363*9bb1b549SSpandan Das lineno++ 364*9bb1b549SSpandan Das 365*9bb1b549SSpandan Das // Only TrimRight newlines. Do not TrimRight() completely, because that would remove spaces too. 366*9bb1b549SSpandan Das // This is necessary in order to have at least one space in every manifest line. 367*9bb1b549SSpandan Das // Some manifest entries don't have any path after this space, namely the "__init__.py" entries. 368*9bb1b549SSpandan Das // original comment sourced from: https://github.com/bazelbuild/bazel/blob/09c621e4cf5b968f4c6cdf905ab142d5961f9ddc/src/test/py/bazel/runfiles_test.py#L225 369*9bb1b549SSpandan Das line = bytes.TrimRight(line, "\r\n") 370*9bb1b549SSpandan Das if len(line) == 0 { 371*9bb1b549SSpandan Das continue 372*9bb1b549SSpandan Das } 373*9bb1b549SSpandan Das 374*9bb1b549SSpandan Das spaceIndex := bytes.IndexByte(line, ' ') 375*9bb1b549SSpandan Das if spaceIndex < 0 { 376*9bb1b549SSpandan Das runfiles.err = fmt.Errorf( 377*9bb1b549SSpandan Das "error parsing runfiles manifest: %s:%d: no space: '%s'", manifest, lineno, line) 378*9bb1b549SSpandan Das return 379*9bb1b549SSpandan Das } 380*9bb1b549SSpandan Das shortPath := string(line[0:spaceIndex]) 381*9bb1b549SSpandan Das abspath := "" 382*9bb1b549SSpandan Das if len(line) > spaceIndex+1 { 383*9bb1b549SSpandan Das abspath = string(line[spaceIndex+1:]) 384*9bb1b549SSpandan Das } 385*9bb1b549SSpandan Das 386*9bb1b549SSpandan Das entry := RunfileEntry{ShortPath: shortPath, Path: abspath} 387*9bb1b549SSpandan Das if i := strings.IndexByte(entry.ShortPath, '/'); i >= 0 { 388*9bb1b549SSpandan Das entry.Workspace = entry.ShortPath[:i] 389*9bb1b549SSpandan Das entry.ShortPath = entry.ShortPath[i+1:] 390*9bb1b549SSpandan Das } 391*9bb1b549SSpandan Das if strings.HasPrefix(entry.ShortPath, "external/") { 392*9bb1b549SSpandan Das entry.ShortPath = entry.ShortPath[len("external/"):] 393*9bb1b549SSpandan Das if i := strings.IndexByte(entry.ShortPath, '/'); i >= 0 { 394*9bb1b549SSpandan Das entry.Workspace = entry.ShortPath[:i] 395*9bb1b549SSpandan Das entry.ShortPath = entry.ShortPath[i+1:] 396*9bb1b549SSpandan Das } 397*9bb1b549SSpandan Das } 398*9bb1b549SSpandan Das 399*9bb1b549SSpandan Das runfiles.list = append(runfiles.list, entry) 400*9bb1b549SSpandan Das runfiles.index.Put(&entry) 401*9bb1b549SSpandan Das } 402*9bb1b549SSpandan Das } 403*9bb1b549SSpandan Das 404*9bb1b549SSpandan Das runfiles.workspace = os.Getenv("TEST_WORKSPACE") 405*9bb1b549SSpandan Das 406*9bb1b549SSpandan Das if dir := os.Getenv("RUNFILES_DIR"); dir != "" { 407*9bb1b549SSpandan Das runfiles.dir = dir 408*9bb1b549SSpandan Das } else if dir = os.Getenv("TEST_SRCDIR"); dir != "" { 409*9bb1b549SSpandan Das runfiles.dir = dir 410*9bb1b549SSpandan Das } else if runtime.GOOS != "windows" { 411*9bb1b549SSpandan Das dir, err := os.Getwd() 412*9bb1b549SSpandan Das if err != nil { 413*9bb1b549SSpandan Das runfiles.err = fmt.Errorf("error locating runfiles dir: %v", err) 414*9bb1b549SSpandan Das return 415*9bb1b549SSpandan Das } 416*9bb1b549SSpandan Das 417*9bb1b549SSpandan Das parent := filepath.Dir(dir) 418*9bb1b549SSpandan Das if strings.HasSuffix(parent, ".runfiles") { 419*9bb1b549SSpandan Das runfiles.dir = parent 420*9bb1b549SSpandan Das if runfiles.workspace == "" { 421*9bb1b549SSpandan Das runfiles.workspace = filepath.Base(dir) 422*9bb1b549SSpandan Das } 423*9bb1b549SSpandan Das } else { 424*9bb1b549SSpandan Das runfiles.err = errors.New("could not locate runfiles directory") 425*9bb1b549SSpandan Das return 426*9bb1b549SSpandan Das } 427*9bb1b549SSpandan Das } 428*9bb1b549SSpandan Das 429*9bb1b549SSpandan Das if runfiles.dir != "" { 430*9bb1b549SSpandan Das fis, err := ioutil.ReadDir(runfiles.dir) 431*9bb1b549SSpandan Das if err != nil { 432*9bb1b549SSpandan Das runfiles.err = fmt.Errorf("could not open runfiles directory: %v", err) 433*9bb1b549SSpandan Das return 434*9bb1b549SSpandan Das } 435*9bb1b549SSpandan Das for _, fi := range fis { 436*9bb1b549SSpandan Das if fi.IsDir() { 437*9bb1b549SSpandan Das runfiles.workspaces = append(runfiles.workspaces, fi.Name()) 438*9bb1b549SSpandan Das } 439*9bb1b549SSpandan Das } 440*9bb1b549SSpandan Das sort.Strings(runfiles.workspaces) 441*9bb1b549SSpandan Das } 442*9bb1b549SSpandan Das} 443