Skip to content

Commit 4a6a36f

Browse files
Add support for typescript rules.
1 parent 17ca790 commit 4a6a36f

1 file changed

Lines changed: 105 additions & 55 deletions

File tree

main.go

Lines changed: 105 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,66 @@ import (
1212
"github.com/bazelbuild/buildtools/build"
1313
)
1414

15+
const (
16+
goProtoLibrary = "go_proto_library"
17+
tsProtoLibrary = "ts_proto_library"
18+
)
19+
1520
var (
1621
dirs = flag.String("dirs", "", "Bazel workspaces to process")
1722

1823
githubRepoRe = regexp.MustCompile(`^github.com/(.+?)/(.+?)/`)
1924
)
2025

21-
type goRule struct {
22-
name, protoRuleName, importPath string
26+
type languageProtoRule struct {
27+
kind, name, protoRuleName, importPath string
28+
}
29+
30+
func (r *languageProtoRule) getLinkAndTarget(workspaceRoot, protoFile string) (string, string, error) {
31+
protoFileRelPath := strings.TrimPrefix(protoFile, workspaceRoot)
32+
switch r.kind {
33+
case goProtoLibrary:
34+
workspaceRelativePath := githubRepoRe.ReplaceAllLiteralString(r.importPath, "")
35+
if workspaceRelativePath == r.importPath {
36+
return "", "", fmt.Errorf("could not figure out workspace relative path for import %q", r.importPath)
37+
}
38+
39+
protoFileBasename := filepath.Base(protoFile)
40+
41+
linkSrcDir := filepath.Join(workspaceRoot, workspaceRelativePath)
42+
if err := os.MkdirAll(linkSrcDir, 0700); err != nil {
43+
return "", "", fmt.Errorf("could not make directory %q: %v", linkSrcDir, err)
44+
}
45+
linkSrcFile := strings.TrimSuffix(protoFileBasename, ".proto") + ".pb.go"
46+
linkSrc := filepath.Join(linkSrcDir, linkSrcFile)
47+
48+
genProtoAbsPath := filepath.Join(workspaceRoot, "bazel-bin", filepath.Dir(protoFileRelPath), r.name+"_", r.importPath, linkSrcFile)
49+
50+
return linkSrc, genProtoAbsPath, nil
51+
case tsProtoLibrary:
52+
linkSrc := filepath.Join(workspaceRoot, filepath.Dir(protoFileRelPath), r.name + ".d.ts")
53+
genProtoAbsPath := filepath.Join(workspaceRoot, "bazel-bin", filepath.Dir(protoFileRelPath), r.name+".d.ts")
54+
return linkSrc, genProtoAbsPath, nil
55+
}
56+
return "", "", fmt.Errorf("unknown proto rule kind %q", r.kind)
2357
}
2458

2559
type parsedBuildFile struct {
26-
protoFileToRule map[string]string
27-
protoRuleToGoRule map[string]goRule
60+
protoFileToRule map[string]string
61+
protoRuleToLangProtoRules map[string][]languageProtoRule
2862
}
2963

30-
func (b *parsedBuildFile) getGoRuleForProto(protoFile string) (*goRule, bool) {
64+
func (b *parsedBuildFile) getLangProtoRulesForProto(protoFile string) ([]languageProtoRule, bool) {
3165
basename := filepath.Base(protoFile)
3266
protoRule, ok := b.protoFileToRule[basename]
3367
if !ok {
3468
return nil, false
3569
}
36-
goRule, ok := b.protoRuleToGoRule[protoRule]
70+
langRules, ok := b.protoRuleToLangProtoRules[protoRule]
3771
if !ok {
3872
return nil, false
3973
}
40-
return &goRule, true
74+
return langRules, true
4175
}
4276

4377
func parseBuildFile(buildFilePath string) (*parsedBuildFile, error) {
@@ -66,31 +100,43 @@ func parseBuildFile(buildFilePath string) (*parsedBuildFile, error) {
66100
}
67101
}
68102

69-
protoRuleToGoRule := make(map[string]goRule)
103+
protoRuleToLangProtoRules := make(map[string][]languageProtoRule)
70104

71-
goProtoRules := buildFile.Rules("go_proto_library")
105+
goProtoRules := buildFile.Rules("")
72106
for _, r := range goProtoRules {
107+
if r.Kind() != goProtoLibrary && r.Kind() != tsProtoLibrary {
108+
continue
109+
}
110+
73111
protoRule := r.AttrString("proto")
74112
if protoRule == "" {
75113
return nil, fmt.Errorf("%s: go proto rule %q missing proto attribute", buildFilePath, r.Name())
76114
}
77115
if !strings.HasPrefix(protoRule, ":") {
78116
return nil, fmt.Errorf("%s: go proto rule %q has unsupported proto reference: %s", buildFilePath, r.Name(), protoRule)
79117
}
80-
importPath := r.AttrString("importpath")
81-
if importPath == "" {
82-
return nil, fmt.Errorf("%s: go proto rule %q missing importpath attribute", buildFilePath, r.Name())
118+
119+
importPath := ""
120+
if r.Kind() == goProtoLibrary {
121+
importPath = r.AttrString("importpath")
122+
if importPath == "" {
123+
return nil, fmt.Errorf("%s: go proto rule %q missing importpath attribute", buildFilePath, r.Name())
124+
}
83125
}
84-
protoRuleToGoRule[protoRule[1:]] = goRule{
126+
127+
protoRuleName := protoRule[1:]
128+
langProtoRule := languageProtoRule{
129+
kind: r.Kind(),
85130
name: r.Name(),
86131
protoRuleName: protoRule[1:],
87132
importPath: importPath,
88133
}
134+
protoRuleToLangProtoRules[protoRuleName] = append(protoRuleToLangProtoRules[protoRuleName], langProtoRule)
89135
}
90136

91137
return &parsedBuildFile{
92-
protoFileToRule: protoFileToRule,
93-
protoRuleToGoRule: protoRuleToGoRule,
138+
protoFileToRule: protoFileToRule,
139+
protoRuleToLangProtoRules: protoRuleToLangProtoRules,
94140
}, nil
95141
}
96142

@@ -99,6 +145,43 @@ type result struct {
99145
upToDate int
100146
}
101147

148+
func processProtoFile(workspaceRoot string, protoFile string, buildFile *parsedBuildFile, result *result) error {
149+
langRules, ok := buildFile.getLangProtoRulesForProto(protoFile)
150+
if !ok {
151+
return fmt.Errorf("could not figure out go proto rule for %q", protoFile)
152+
}
153+
154+
for _, langRule := range langRules {
155+
link, linkTarget, err := langRule.getLinkAndTarget(workspaceRoot, protoFile)
156+
if err != nil {
157+
return err
158+
}
159+
160+
s, err := os.Lstat(link)
161+
if err == nil {
162+
if s.Mode()&os.ModeSymlink == 0 {
163+
return fmt.Errorf("%s already exists and is not a symlink", link)
164+
}
165+
existingTarget, err := os.Readlink(link)
166+
if err != nil {
167+
return fmt.Errorf("could not read symlink %q: %v", link, err)
168+
}
169+
// cautious for now but we should probably just overwrite the symlink
170+
if existingTarget != linkTarget {
171+
return fmt.Errorf("symlink %s already exists and points to a different location", link)
172+
}
173+
result.upToDate++
174+
} else {
175+
if err := os.Symlink(linkTarget, link); err != nil {
176+
return fmt.Errorf("could not create symlink from %q to %q: %v", linkTarget, link, err)
177+
}
178+
fmt.Printf("Created symlink for %s\n", protoFile)
179+
result.created++
180+
}
181+
}
182+
return nil
183+
}
184+
102185
func processWorkspace(workspaceRoot string) (*result, error) {
103186
fmt.Printf("Processing directory %s\n", workspaceRoot)
104187

@@ -141,55 +224,22 @@ func processWorkspace(workspaceRoot string) (*result, error) {
141224
buildFiles[buildFilePath] = buildFile
142225
}
143226

144-
goRule, ok := buildFile.getGoRuleForProto(protoFile)
145-
if !ok {
146-
return nil, fmt.Errorf("could not figure out go proto rule for %q", protoFile)
147-
}
148-
workspaceRelativePath := githubRepoRe.ReplaceAllLiteralString(goRule.importPath, "")
149-
if workspaceRelativePath == goRule.importPath {
150-
return nil, fmt.Errorf("could not figure out workspace relative path for import %s", goRule.importPath)
227+
if err := processProtoFile(workspaceRoot, protoFile, buildFile, result); err != nil {
228+
return nil, err
151229
}
152230

153-
protoFileBasename := filepath.Base(protoFile)
154-
155-
linkSrcDir := filepath.Join(workspaceRoot, workspaceRelativePath)
156-
if err := os.MkdirAll(linkSrcDir, 0700); err != nil {
157-
return nil, fmt.Errorf("could not make directory %q: %v", linkSrcDir, err)
158-
}
159-
linkSrcFile := strings.TrimSuffix(protoFileBasename, ".proto") + ".pb.go"
160-
linkSrc := filepath.Join(linkSrcDir, linkSrcFile)
161-
162-
protoFileRelPath := strings.TrimPrefix(protoFile, workspaceRoot)
163-
genProtoAbsPath := filepath.Join(workspaceRoot, "bazel-bin", filepath.Dir(protoFileRelPath), goRule.name+"_", goRule.importPath, linkSrcFile)
164-
165-
s, err := os.Lstat(linkSrc)
166-
if err == nil {
167-
if s.Mode()&os.ModeSymlink == 0 {
168-
return nil, fmt.Errorf("%s already exists and is not a symlink", linkSrc)
169-
}
170-
existingTarget, err := os.Readlink(linkSrc)
171-
if err != nil {
172-
return nil, fmt.Errorf("could not read symlink %q: %v", linkSrc, err)
173-
}
174-
// cautious for now but we should probably just overwrite the symlink
175-
if existingTarget != genProtoAbsPath {
176-
return nil, fmt.Errorf("symlink %s already exists and points to a different location", linkSrc)
177-
}
178-
result.upToDate++
179-
} else {
180-
if err := os.Symlink(genProtoAbsPath, linkSrc); err != nil {
181-
return nil, fmt.Errorf("could not create symlink from %q to %q: %v", genProtoAbsPath, linkSrc, err)
182-
}
183-
fmt.Printf("Created symlink for %s\n", protoFile)
184-
result.created++
185-
}
186231
}
187232
return result, nil
188233
}
189234

190235
func main() {
191236
flag.Parse()
192237

238+
if *dirs == "" {
239+
fmt.Printf("Please specify --dirs")
240+
os.Exit(1)
241+
}
242+
193243
for _, dir := range strings.Split(*dirs, ",") {
194244
result, err := processWorkspace(dir)
195245
if err != nil {

0 commit comments

Comments
 (0)