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 "regexp" 19*9bb1b549SSpandan Das "runtime" 20*9bb1b549SSpandan Das "sync" 21*9bb1b549SSpandan Das) 22*9bb1b549SSpandan Das 23*9bb1b549SSpandan Das// Rlocation returns the absolute path name of a runfile. The runfile name must be 24*9bb1b549SSpandan Das// a relative path, using the slash (not backslash) as directory separator. If 25*9bb1b549SSpandan Das// the runfiles manifest maps s to an empty name (indicating an empty runfile 26*9bb1b549SSpandan Das// not present in the filesystem), Rlocation returns an error that wraps ErrEmpty. 27*9bb1b549SSpandan Dasfunc Rlocation(path string) (string, error) { 28*9bb1b549SSpandan Das return RlocationFrom(path, CallerRepository()) 29*9bb1b549SSpandan Das} 30*9bb1b549SSpandan Das 31*9bb1b549SSpandan Dasfunc RlocationFrom(path string, sourceRepo string) (string, error) { 32*9bb1b549SSpandan Das r, err := g.get() 33*9bb1b549SSpandan Das if err != nil { 34*9bb1b549SSpandan Das return "", err 35*9bb1b549SSpandan Das } 36*9bb1b549SSpandan Das return r.WithSourceRepo(sourceRepo).Rlocation(path) 37*9bb1b549SSpandan Das} 38*9bb1b549SSpandan Das 39*9bb1b549SSpandan Das// Env returns additional environmental variables to pass to subprocesses. 40*9bb1b549SSpandan Das// Each element is of the form “key=value”. Pass these variables to 41*9bb1b549SSpandan Das// Bazel-built binaries so they can find their runfiles as well. See the 42*9bb1b549SSpandan Das// Runfiles example for an illustration of this. 43*9bb1b549SSpandan Das// 44*9bb1b549SSpandan Das// The return value is a newly-allocated slice; you can modify it at will. 45*9bb1b549SSpandan Dasfunc Env() ([]string, error) { 46*9bb1b549SSpandan Das r, err := g.get() 47*9bb1b549SSpandan Das if err != nil { 48*9bb1b549SSpandan Das return nil, err 49*9bb1b549SSpandan Das } 50*9bb1b549SSpandan Das return r.Env(), nil 51*9bb1b549SSpandan Das} 52*9bb1b549SSpandan Das 53*9bb1b549SSpandan Dasvar legacyExternalGeneratedFile = regexp.MustCompile(`^bazel-out[/][^/]+/bin/external/([^/]+)/`) 54*9bb1b549SSpandan Dasvar legacyExternalFile = regexp.MustCompile(`^external/([^/]+)/`) 55*9bb1b549SSpandan Das 56*9bb1b549SSpandan Das// CurrentRepository returns the canonical name of the Bazel repository that 57*9bb1b549SSpandan Das// contains the source file of the caller of CurrentRepository. 58*9bb1b549SSpandan Dasfunc CurrentRepository() string { 59*9bb1b549SSpandan Das return callerRepository(1) 60*9bb1b549SSpandan Das} 61*9bb1b549SSpandan Das 62*9bb1b549SSpandan Das// CallerRepository returns the canonical name of the Bazel repository that 63*9bb1b549SSpandan Das// contains the source file of the caller of the function that itself calls 64*9bb1b549SSpandan Das// CallerRepository. 65*9bb1b549SSpandan Dasfunc CallerRepository() string { 66*9bb1b549SSpandan Das return callerRepository(2) 67*9bb1b549SSpandan Das} 68*9bb1b549SSpandan Das 69*9bb1b549SSpandan Dasfunc callerRepository(skip int) string { 70*9bb1b549SSpandan Das _, file, _, _ := runtime.Caller(skip + 1) 71*9bb1b549SSpandan Das if match := legacyExternalGeneratedFile.FindStringSubmatch(file); match != nil { 72*9bb1b549SSpandan Das return match[1] 73*9bb1b549SSpandan Das } 74*9bb1b549SSpandan Das if match := legacyExternalFile.FindStringSubmatch(file); match != nil { 75*9bb1b549SSpandan Das return match[1] 76*9bb1b549SSpandan Das } 77*9bb1b549SSpandan Das // If a file is not in an external repository, it is in the main repository, 78*9bb1b549SSpandan Das // which has the empty string as its canonical name. 79*9bb1b549SSpandan Das return "" 80*9bb1b549SSpandan Das} 81*9bb1b549SSpandan Das 82*9bb1b549SSpandan Dastype global struct { 83*9bb1b549SSpandan Das once sync.Once 84*9bb1b549SSpandan Das runfiles *Runfiles 85*9bb1b549SSpandan Das err error 86*9bb1b549SSpandan Das} 87*9bb1b549SSpandan Das 88*9bb1b549SSpandan Dasfunc (g *global) get() (*Runfiles, error) { 89*9bb1b549SSpandan Das g.once.Do(g.init) 90*9bb1b549SSpandan Das return g.runfiles, g.err 91*9bb1b549SSpandan Das} 92*9bb1b549SSpandan Das 93*9bb1b549SSpandan Dasfunc (g *global) init() { 94*9bb1b549SSpandan Das g.runfiles, g.err = New() 95*9bb1b549SSpandan Das} 96*9bb1b549SSpandan Das 97*9bb1b549SSpandan Dasvar g global 98