xref: /aosp_15_r20/external/bazelbuild-rules_go/go/runfiles/manifest.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	"bufio"
19	"fmt"
20	"os"
21	"path"
22	"path/filepath"
23	"strings"
24)
25
26// ManifestFile specifies the location of the runfile manifest file.  You can
27// pass this as an option to New.  If unset or empty, use the value of the
28// environmental variable RUNFILES_MANIFEST_FILE.
29type ManifestFile string
30
31func (f ManifestFile) new(sourceRepo SourceRepo) (*Runfiles, error) {
32	m, err := f.parse()
33	if err != nil {
34		return nil, err
35	}
36	r := &Runfiles{
37		impl:       m,
38		env:        manifestFileVar + "=" + string(f),
39		sourceRepo: string(sourceRepo),
40	}
41	err = r.loadRepoMapping()
42	return r, err
43}
44
45type manifest map[string]string
46
47func (f ManifestFile) parse() (manifest, error) {
48	r, err := os.Open(string(f))
49	if err != nil {
50		return nil, fmt.Errorf("runfiles: can’t open manifest file: %w", err)
51	}
52	defer r.Close()
53
54	s := bufio.NewScanner(r)
55	m := make(manifest)
56	for s.Scan() {
57		fields := strings.SplitN(s.Text(), " ", 2)
58		if len(fields) != 2 || fields[0] == "" {
59			return nil, fmt.Errorf("runfiles: bad manifest line %q in file %s", s.Text(), f)
60		}
61		m[fields[0]] = filepath.FromSlash(fields[1])
62	}
63
64	if err := s.Err(); err != nil {
65		return nil, fmt.Errorf("runfiles: error parsing manifest file %s: %w", f, err)
66	}
67
68	return m, nil
69}
70
71func (m manifest) path(s string) (string, error) {
72	r, ok := m[s]
73	if ok && r == "" {
74		return "", ErrEmpty
75	}
76	if ok {
77		return r, nil
78	}
79
80	// If path references a runfile that lies under a directory that itself is a
81	// runfile, then only the directory is listed in the manifest. Look up all
82	// prefixes of path in the manifest.
83	for prefix := s; prefix != ""; prefix, _ = path.Split(prefix) {
84		prefix = strings.TrimSuffix(prefix, "/")
85		if prefixMatch, ok := m[prefix]; ok {
86			return prefixMatch + filepath.FromSlash(strings.TrimPrefix(s, prefix)), nil
87		}
88	}
89
90	return "", os.ErrNotExist
91}
92