123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- /* Copyright 2017 The Bazel Authors. All rights reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package repos
- import (
- "fmt"
- "os"
- "path/filepath"
- "sort"
- "strings"
- "github.com/bazelbuild/bazel-gazelle/internal/rule"
- )
- // Repo describes an external repository rule declared in a Bazel
- // WORKSPACE file.
- type Repo struct {
- // Name is the value of the "name" attribute of the repository rule.
- Name string
- // GoPrefix is the portion of the Go import path for the root of this
- // repository. Usually the same as Remote.
- GoPrefix string
- // Commit is the revision at which a repository is checked out (for example,
- // a Git commit id).
- Commit string
- // Tag is the name of the version at which a repository is checked out.
- Tag string
- // Remote is the URL the repository can be cloned or checked out from.
- Remote string
- // VCS is the version control system used to check out the repository.
- // May also be "http" for HTTP archives.
- VCS string
- }
- type byName []Repo
- func (s byName) Len() int { return len(s) }
- func (s byName) Less(i, j int) bool { return s[i].Name < s[j].Name }
- func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
- type lockFileFormat int
- const (
- unknownFormat lockFileFormat = iota
- depFormat
- moduleFormat
- )
- var lockFileParsers = map[lockFileFormat]func(string) ([]Repo, error){
- depFormat: importRepoRulesDep,
- moduleFormat: importRepoRulesModules,
- }
- // ImportRepoRules reads the lock file of a vendoring tool and returns
- // a list of equivalent repository rules that can be merged into a WORKSPACE
- // file. The format of the file is inferred from its basename. Currently,
- // only Gopkg.lock is supported.
- func ImportRepoRules(filename string) ([]*rule.Rule, error) {
- format := getLockFileFormat(filename)
- if format == unknownFormat {
- return nil, fmt.Errorf(`%s: unrecognized lock file format. Expected "Gopkg.lock"`, filename)
- }
- parser := lockFileParsers[format]
- repos, err := parser(filename)
- if err != nil {
- return nil, fmt.Errorf("error parsing %q: %v", filename, err)
- }
- sort.Stable(byName(repos))
- rules := make([]*rule.Rule, 0, len(repos))
- for _, repo := range repos {
- rules = append(rules, GenerateRule(repo))
- }
- return rules, nil
- }
- func getLockFileFormat(filename string) lockFileFormat {
- switch filepath.Base(filename) {
- case "Gopkg.lock":
- return depFormat
- case "go.mod":
- return moduleFormat
- default:
- return unknownFormat
- }
- }
- // GenerateRule returns a repository rule for the given repository that can
- // be written in a WORKSPACE file.
- func GenerateRule(repo Repo) *rule.Rule {
- r := rule.NewRule("go_repository", repo.Name)
- if repo.Commit != "" {
- r.SetAttr("commit", repo.Commit)
- }
- if repo.Tag != "" {
- r.SetAttr("tag", repo.Tag)
- }
- r.SetAttr("importpath", repo.GoPrefix)
- if repo.Remote != "" {
- r.SetAttr("remote", repo.Remote)
- }
- if repo.VCS != "" {
- r.SetAttr("vcs", repo.VCS)
- }
- return r
- }
- // FindExternalRepo attempts to locate the directory where Bazel has fetched
- // the external repository with the given name. An error is returned if the
- // repository directory cannot be located.
- func FindExternalRepo(repoRoot, name string) (string, error) {
- // See https://docs.bazel.build/versions/master/output_directories.html
- // for documentation on Bazel directory layout.
- // We expect the bazel-out symlink in the workspace root directory to point to
- // <output-base>/execroot/<workspace-name>/bazel-out
- // We expect the external repository to be checked out at
- // <output-base>/external/<name>
- // Note that users can change the prefix for most of the Bazel symlinks with
- // --symlink_prefix, but this does not include bazel-out.
- externalPath := strings.Join([]string{repoRoot, "bazel-out", "..", "..", "..", "external", name}, string(os.PathSeparator))
- cleanPath, err := filepath.EvalSymlinks(externalPath)
- if err != nil {
- return "", err
- }
- st, err := os.Stat(cleanPath)
- if err != nil {
- return "", err
- }
- if !st.IsDir() {
- return "", fmt.Errorf("%s: not a directory", externalPath)
- }
- return cleanPath, nil
- }
- // ListRepositories extracts metadata about repositories declared in a
- // WORKSPACE file.
- //
- // The set of repositories returned is necessarily incomplete, since we don't
- // evaluate the file, and repositories may be declared in macros in other files.
- func ListRepositories(workspace *rule.File) []Repo {
- var repos []Repo
- for _, r := range workspace.Rules {
- name := r.Name()
- if name == "" {
- continue
- }
- var repo Repo
- switch r.Kind() {
- case "go_repository":
- // TODO(jayconrod): extract other fields needed by go_repository.
- // Currently, we don't use the result of this function to produce new
- // go_repository rules, so it doesn't matter.
- goPrefix := r.AttrString("importpath")
- revision := r.AttrString("commit")
- remote := r.AttrString("remote")
- vcs := r.AttrString("vcs")
- if goPrefix == "" {
- continue
- }
- repo = Repo{
- Name: name,
- GoPrefix: goPrefix,
- Commit: revision,
- Remote: remote,
- VCS: vcs,
- }
- // TODO(jayconrod): infer from {new_,}git_repository, {new_,}http_archive,
- // local_repository.
- default:
- continue
- }
- repos = append(repos, repo)
- }
- // TODO(jayconrod): look for directives that describe repositories that
- // aren't declared in the top-level of WORKSPACE (e.g., behind a macro).
- return repos
- }
|