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