1 package constants
2
3 import (
4 "fmt"
5 "path/filepath"
6 "slices"
7 "strings"
8
9 "github.com/bazelbuild/bazel-gazelle/language"
10 "github.com/bazelbuild/buildtools/build"
11 bzlFile "github.com/bazelbuild/buildtools/file"
12 )
13
14 type BazelConstMap map[string]string
15
16 type constantSource struct {
17 Module string
18 Filepath string
19 Constants []string
20 }
21
22 func (cs constantSource) LoadBazelConstMap(cm BazelConstMap) (BazelConstMap, error) {
23 moduleFileBytes, _, err := bzlFile.ReadFile(cs.Filepath)
24 if err != nil {
25 return cm, fmt.Errorf("error reading loaded file %s: err: %w", cs.Filepath, err)
26 }
27
28 moduleBzlFile, err := build.ParseBzl(cs.Filepath, moduleFileBytes)
29 if err != nil {
30 return cm, fmt.Errorf("error parsing file %s: %w", cs.Filepath, err)
31 }
32
33 for _, stmt := range moduleBzlFile.Stmt {
34 if assignStmt, isAssign := stmt.(*build.AssignExpr); isAssign {
35 var constName string
36 var constValue string
37 if ident, ok := assignStmt.LHS.(*build.Ident); ok {
38 if !slices.Contains(cs.Constants, ident.Name) {
39 continue
40 }
41 constName = ident.Name
42 }
43
44
45 if val, ok := assignStmt.RHS.(*build.StringExpr); ok {
46 constValue = val.Value
47 }
48
49
50 cm[constName] = constValue
51 }
52 }
53 return cm, nil
54 }
55
56
57
58 func ResolveConstants(args language.GenerateArgs, f *build.File, constMap BazelConstMap) (BazelConstMap, error) {
59 constantSources := []constantSource{}
60 for _, s := range f.Stmt {
61 switch stmt := s.(type) {
62 case *build.LoadStmt:
63 cs := constantSource{
64 Module: stmt.Module.Value,
65 Filepath: labelToPath(args, stmt.Module.Value),
66 }
67 for _, from := range stmt.From {
68 if from.Name != strings.ToUpper(from.Name) {
69
70 continue
71 }
72 cs.Constants = append(cs.Constants, from.Name)
73 }
74 if len(cs.Constants) == 0 {
75
76 continue
77 }
78 constantSources = append(constantSources, cs)
79 case *build.AssignExpr:
80
81 constName, constValue, canAssign := assignExprVals(stmt)
82 if canAssign {
83 constMap[constName] = constValue
84 }
85 }
86 }
87
88 for _, constSource := range constantSources {
89 constMap, err := constSource.LoadBazelConstMap(constMap)
90 if err != nil {
91 return constMap, err
92 }
93 }
94
95 return constMap, nil
96 }
97
98 func ResolveAttr(rule *build.Rule, key string, constMap BazelConstMap) string {
99 attr := rule.Attr(key)
100 if reg, ok := attr.(*build.Ident); ok {
101 return constMap[reg.Name]
102 }
103 if reg, ok := attr.(*build.StringExpr); ok {
104 return reg.Value
105 }
106 return ""
107 }
108
109 func labelToPath(args language.GenerateArgs, label string) string {
110 if strings.HasPrefix(label, ":") {
111 label = strings.TrimPrefix(label, ":")
112 return filepath.Join(args.Dir, label)
113 }
114 label = strings.TrimPrefix(label, "//")
115 label = strings.ReplaceAll(label, ":", "/")
116 return filepath.Join(args.Config.RepoRoot, label)
117 }
118
119
120
121 func assignExprVals(assign *build.AssignExpr) (string, string, bool) {
122 var constName string
123 var constValue string
124 switch ident := assign.LHS.(type) {
125 case *build.Ident:
126 constName = ident.Name
127 default:
128
129 return "", "", false
130 }
131
132
133 switch val := assign.RHS.(type) {
134 case *build.StringExpr:
135 constValue = val.Value
136 return constName, constValue, true
137 default:
138
139 return "", "", false
140 }
141 }
142
View as plain text