| /* Copyright 2019 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. |
| */ |
| |
| // Command generate_repo_config takes in a build config file such as |
| // WORKSPACE and generates a stripped version of the file. The generated |
| // config file should contain only the information relevant to gazelle for |
| // dependency resolution, so go_repository rules with importpath |
| // and name defined, plus any directives. |
| // |
| // This command is used by the go_repository_config rule to generate a repo |
| // config file used by all go_repository rules. A list of macro files is |
| // printed to stdout to be read by the go_repository_config rule. |
| package main |
| |
| import ( |
| "bytes" |
| "flag" |
| "fmt" |
| "log" |
| "os" |
| "path/filepath" |
| "sort" |
| "strings" |
| |
| "github.com/bazelbuild/bazel-gazelle/repo" |
| "github.com/bazelbuild/bazel-gazelle/rule" |
| ) |
| |
| const ( |
| goRepoRuleKind = "go_repository" |
| httpArchiveRuleKind = "http_archive" |
| ) |
| |
| var ( |
| configSource = flag.String("config_source", "", "a file that is read to learn about external repositories") |
| configDest = flag.String("config_dest", "", "destination file for the generated repo config") |
| ) |
| |
| type byName []*rule.Rule |
| |
| 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] } |
| |
| func main() { |
| log.SetFlags(0) |
| log.SetPrefix("generate_repo_config: ") |
| |
| flag.Parse() |
| if *configDest == "" { |
| log.Fatal("-config_dest must be set") |
| } |
| if *configSource == "" { |
| log.Fatal("-config_source must be set") |
| } |
| if flag.NArg() != 0 { |
| log.Fatal("generate_repo_config does not accept positional arguments") |
| } |
| files, err := generateRepoConfig(*configDest, *configSource) |
| if err != nil { |
| log.Fatal(err) |
| } |
| for _, f := range files { |
| fmt.Fprintln(os.Stdout, f) |
| } |
| } |
| |
| func generateRepoConfig(configDest, configSource string) ([]string, error) { |
| var buf bytes.Buffer |
| buf.WriteString("# Code generated by generate_repo_config.go; DO NOT EDIT.\n") |
| |
| sourceFile, err := rule.LoadWorkspaceFile(configSource, "") |
| if err != nil { |
| return nil, err |
| } |
| repos, repoFileMap, err := repo.ListRepositories(sourceFile) |
| if err != nil { |
| return nil, err |
| } |
| sort.Stable(byName(repos)) |
| |
| seenFile := make(map[*rule.File]bool) |
| var sortedFiles []*rule.File |
| for _, f := range repoFileMap { |
| if !seenFile[f] { |
| seenFile[f] = true |
| sortedFiles = append(sortedFiles, f) |
| } |
| } |
| sort.SliceStable(sortedFiles, func(i, j int) bool { |
| if cmp := strings.Compare(sortedFiles[i].Path, sortedFiles[j].Path); cmp != 0 { |
| return cmp < 0 |
| } |
| return sortedFiles[i].DefName < sortedFiles[j].DefName |
| }) |
| |
| destFile := rule.EmptyFile(configDest, "") |
| for _, rsrc := range repos { |
| var rdst *rule.Rule |
| if rsrc.Kind() == goRepoRuleKind { |
| rdst = rule.NewRule(goRepoRuleKind, rsrc.Name()) |
| rdst.SetAttr("importpath", rsrc.AttrString("importpath")) |
| if namingConvention := rsrc.AttrString("build_naming_convention"); namingConvention != "" { |
| rdst.SetAttr("build_naming_convention", namingConvention) |
| } |
| } else if rsrc.Kind() == httpArchiveRuleKind && rsrc.Name() == "io_bazel_rules_go" { |
| rdst = rule.NewRule(httpArchiveRuleKind, "io_bazel_rules_go") |
| rdst.SetAttr("urls", rsrc.AttrStrings("urls")) |
| } |
| if rdst != nil { |
| rdst.Insert(destFile) |
| } |
| } |
| |
| buf.WriteString("\n") |
| buf.Write(destFile.Format()) |
| if err := os.WriteFile(configDest, buf.Bytes(), 0o666); err != nil { |
| return nil, err |
| } |
| |
| files := make([]string, 0, len(sortedFiles)) |
| for _, m := range sortedFiles { |
| // We have to trim the configSource file path from the repo files returned. |
| // This is safe/required because repo.ListRepositories(sourceFile) is called |
| // with the sourcefile as the workspace, so the source file location is always |
| // prepended to the macro file paths. |
| // TODO: https://github.com/bazelbuild/bazel-gazelle/issues/1068 |
| f, err := filepath.Rel(filepath.Dir(configSource), m.Path) |
| if err != nil { |
| return nil, err |
| } |
| files = append(files, f) |
| } |
| |
| return files, nil |
| } |