github.com/bazelbuild/bazel-gazelle@v0.36.1-0.20240520142334-61b277ba6fed/cmd/generate_repo_config/generate_repo_config.go (about) 1 /* Copyright 2019 The Bazel Authors. All rights reserved. 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 http://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 */ 15 16 // Command generate_repo_config takes in a build config file such as 17 // WORKSPACE and generates a stripped version of the file. The generated 18 // config file should contain only the information relevant to gazelle for 19 // dependency resolution, so go_repository rules with importpath 20 // and name defined, plus any directives. 21 // 22 // This command is used by the go_repository_config rule to generate a repo 23 // config file used by all go_repository rules. A list of macro files is 24 // printed to stdout to be read by the go_repository_config rule. 25 package main 26 27 import ( 28 "bytes" 29 "flag" 30 "fmt" 31 "log" 32 "os" 33 "path/filepath" 34 "sort" 35 "strings" 36 37 "github.com/bazelbuild/bazel-gazelle/repo" 38 "github.com/bazelbuild/bazel-gazelle/rule" 39 ) 40 41 const ( 42 goRepoRuleKind = "go_repository" 43 httpArchiveRuleKind = "http_archive" 44 ) 45 46 var ( 47 configSource = flag.String("config_source", "", "a file that is read to learn about external repositories") 48 configDest = flag.String("config_dest", "", "destination file for the generated repo config") 49 ) 50 51 type byName []*rule.Rule 52 53 func (s byName) Len() int { return len(s) } 54 func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() } 55 func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 56 57 func main() { 58 log.SetFlags(0) 59 log.SetPrefix("generate_repo_config: ") 60 61 flag.Parse() 62 if *configDest == "" { 63 log.Fatal("-config_dest must be set") 64 } 65 if *configSource == "" { 66 log.Fatal("-config_source must be set") 67 } 68 if flag.NArg() != 0 { 69 log.Fatal("generate_repo_config does not accept positional arguments") 70 } 71 files, err := generateRepoConfig(*configDest, *configSource) 72 if err != nil { 73 log.Fatal(err) 74 } 75 for _, f := range files { 76 fmt.Fprintln(os.Stdout, f) 77 } 78 } 79 80 func generateRepoConfig(configDest, configSource string) ([]string, error) { 81 var buf bytes.Buffer 82 buf.WriteString("# Code generated by generate_repo_config.go; DO NOT EDIT.\n") 83 84 sourceFile, err := rule.LoadWorkspaceFile(configSource, "") 85 if err != nil { 86 return nil, err 87 } 88 repos, repoFileMap, err := repo.ListRepositories(sourceFile) 89 if err != nil { 90 return nil, err 91 } 92 sort.Stable(byName(repos)) 93 94 seenFile := make(map[*rule.File]bool) 95 var sortedFiles []*rule.File 96 for _, f := range repoFileMap { 97 if !seenFile[f] { 98 seenFile[f] = true 99 sortedFiles = append(sortedFiles, f) 100 } 101 } 102 sort.SliceStable(sortedFiles, func(i, j int) bool { 103 if cmp := strings.Compare(sortedFiles[i].Path, sortedFiles[j].Path); cmp != 0 { 104 return cmp < 0 105 } 106 return sortedFiles[i].DefName < sortedFiles[j].DefName 107 }) 108 109 destFile := rule.EmptyFile(configDest, "") 110 for _, rsrc := range repos { 111 var rdst *rule.Rule 112 if rsrc.Kind() == goRepoRuleKind { 113 rdst = rule.NewRule(goRepoRuleKind, rsrc.Name()) 114 rdst.SetAttr("importpath", rsrc.AttrString("importpath")) 115 if namingConvention := rsrc.AttrString("build_naming_convention"); namingConvention != "" { 116 rdst.SetAttr("build_naming_convention", namingConvention) 117 } 118 } else if rsrc.Kind() == httpArchiveRuleKind && rsrc.Name() == "io_bazel_rules_go" { 119 rdst = rule.NewRule(httpArchiveRuleKind, "io_bazel_rules_go") 120 rdst.SetAttr("urls", rsrc.AttrStrings("urls")) 121 } 122 if rdst != nil { 123 rdst.Insert(destFile) 124 } 125 } 126 127 buf.WriteString("\n") 128 buf.Write(destFile.Format()) 129 if err := os.WriteFile(configDest, buf.Bytes(), 0o666); err != nil { 130 return nil, err 131 } 132 133 files := make([]string, 0, len(sortedFiles)) 134 for _, m := range sortedFiles { 135 // We have to trim the configSource file path from the repo files returned. 136 // This is safe/required because repo.ListRepositories(sourceFile) is called 137 // with the sourcefile as the workspace, so the source file location is always 138 // prepended to the macro file paths. 139 // TODO: https://github.com/bazelbuild/bazel-gazelle/issues/1068 140 f, err := filepath.Rel(filepath.Dir(configSource), m.Path) 141 if err != nil { 142 return nil, err 143 } 144 files = append(files, f) 145 } 146 147 return files, nil 148 }