xref: /aosp_15_r20/external/bazelbuild-rules_go/go/runfiles/manifest.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
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