@@ -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+
1520var (
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
2559type 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
4377func 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+
102185func 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
190235func 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