Skip to content

Commit 0132f9f

Browse files
committed
Add starlark_bundle rule
1 parent aa5f356 commit 0132f9f

6 files changed

Lines changed: 182 additions & 30 deletions

File tree

language/starlarkbundle/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go_library(
44
name = "starlarkbundle",
55
srcs = [
66
"language.go",
7+
"starlark_bundle.go",
78
"starlark_library.go",
89
],
910
importpath = "github.com/stackb/rules_proto/language/starlarkbundle",

language/starlarkbundle/language.go

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"flag"
2626
"log"
2727
"maps"
28+
"strings"
2829

2930
"github.com/bazelbuild/bazel-gazelle/config"
3031
"github.com/bazelbuild/bazel-gazelle/label"
@@ -35,13 +36,15 @@ import (
3536
)
3637

3738
const (
38-
languageName = "starlark_bundle"
39-
starlarkBundleRootDirectiveName = "starlark_bundle_root"
39+
languageName = "starlark_bundle"
40+
starlarkBundleRootDirectiveName = "starlark_bundle_root"
41+
starlarkBundleExcludeDirectiveName = "starlark_bundle_exclude"
4042
)
4143

4244
type starlarkBundleLang struct {
43-
starlarkBundleRoot *string
44-
starlarkLibraries map[label.Label]*rule.Rule
45+
starlarkBundleRoot *string
46+
starlarkBundleExcludeDirs []string
47+
starlarkLibraries map[label.Label]*rule.Rule
4548
}
4649

4750
// NewLanguage is called by Gazelle to install this language extension in a
@@ -62,25 +65,31 @@ func (*starlarkBundleLang) Name() string {
6265
// The following methods are implemented to satisfy the
6366
// https://pkg.go.dev/github.com/bazelbuild/bazel-gazelle/resolve?tab=doc#Resolver
6467
// interface, but are otherwise unused.
65-
func (l *starlarkBundleLang) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {
68+
func (ext *starlarkBundleLang) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {
6669
}
6770

6871
func (*starlarkBundleLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
6972
return nil
7073
}
7174

7275
func (*starlarkBundleLang) KnownDirectives() []string {
73-
return []string{starlarkBundleRootDirectiveName}
76+
return []string{
77+
starlarkBundleRootDirectiveName,
78+
starlarkBundleExcludeDirectiveName,
79+
}
7480
}
7581

76-
func (l *starlarkBundleLang) Configure(c *config.Config, rel string, f *rule.File) {
82+
func (ext *starlarkBundleLang) Configure(c *config.Config, rel string, f *rule.File) {
7783
if f != nil {
7884
for _, d := range f.Directives {
79-
if d.Key == starlarkBundleRootDirectiveName {
80-
if l.starlarkBundleRoot != nil {
81-
log.Fatalf("gazelle:%s should only be set once (refusing to override %q with %q)", starlarkBundleRootDirectiveName, *l.starlarkBundleRoot, d.Value)
85+
switch d.Key {
86+
case starlarkBundleRootDirectiveName:
87+
if ext.starlarkBundleRoot != nil {
88+
log.Fatalf("gazelle:%s should only be set once (refusing to override %q with %q)", starlarkBundleRootDirectiveName, *ext.starlarkBundleRoot, d.Value)
8289
}
83-
l.starlarkBundleRoot = &rel
90+
ext.starlarkBundleRoot = &rel
91+
case starlarkBundleExcludeDirectiveName:
92+
ext.starlarkBundleExcludeDirs = append(ext.starlarkBundleExcludeDirs, d.Value)
8493
}
8594
}
8695
}
@@ -92,6 +101,7 @@ func (l *starlarkBundleLang) Configure(c *config.Config, rel string, f *rule.Fil
92101
func (*starlarkBundleLang) Kinds() map[string]rule.KindInfo {
93102
kinds := map[string]rule.KindInfo{}
94103
maps.Copy(kinds, starlarkLibraryKindInfo)
104+
maps.Copy(kinds, starlarkBundleKindInfo)
95105
return kinds
96106
}
97107

@@ -101,6 +111,7 @@ func (*starlarkBundleLang) Kinds() map[string]rule.KindInfo {
101111
func (*starlarkBundleLang) Loads() []rule.LoadInfo {
102112
return []rule.LoadInfo{
103113
starlarkLibraryLoadInfo,
114+
starlarkBundleLoadInfo,
104115
}
105116
}
106117

@@ -115,10 +126,12 @@ func (*starlarkBundleLang) Fix(c *config.Config, f *rule.File) {
115126
//
116127
// If nil is returned, the rule will not be indexed. If any non-nil slice is
117128
// returned, including an empty slice, the rule will be indexed.
118-
func (b *starlarkBundleLang) Imports(c *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec {
129+
func (ext *starlarkBundleLang) Imports(c *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec {
119130
switch r.Kind() {
120131
case starlarkLibraryKind:
121132
return starlarkLibraryImports(c, r, f)
133+
case starlarkBundleKind:
134+
return starlarkBundleImports(c, r, f)
122135
}
123136
return nil
124137
}
@@ -138,10 +151,12 @@ func (*starlarkBundleLang) Embeds(r *rule.Rule, from label.Label) []label.Label
138151
// Resolve generates a "deps" attribute (or the appropriate language-specific
139152
// equivalent) for each import according to language-specific rules and
140153
// heuristics.
141-
func (*starlarkBundleLang) Resolve(c *config.Config, ix *resolve.RuleIndex, rc *repo.RemoteCache, r *rule.Rule, importsRaw interface{}, from label.Label) {
154+
func (ext *starlarkBundleLang) Resolve(c *config.Config, ix *resolve.RuleIndex, rc *repo.RemoteCache, r *rule.Rule, importsRaw interface{}, from label.Label) {
142155
switch r.Kind() {
143156
case starlarkLibraryKind:
144-
starlarkLibraryResolve(c, ix, rc, r, importsRaw, from)
157+
starlarkLibraryResolve(c, ix, r, importsRaw, from)
158+
case starlarkBundleKind:
159+
starlarkBundleResolve(r, ext.starlarkLibraries)
145160
}
146161
}
147162

@@ -157,13 +172,19 @@ func (*starlarkBundleLang) Resolve(c *config.Config, ix *resolve.RuleIndex, rc *
157172
//
158173
// Any non-fatal errors this function encounters should be logged using
159174
// log.Print.
160-
func (l *starlarkBundleLang) GenerateRules(args language.GenerateArgs) (result language.GenerateResult) {
161-
if l.starlarkBundleRoot == nil {
175+
func (ext *starlarkBundleLang) GenerateRules(args language.GenerateArgs) (result language.GenerateResult) {
176+
if ext.starlarkBundleRoot == nil {
162177
return
163178
}
179+
for _, root := range ext.starlarkBundleExcludeDirs {
180+
if strings.HasPrefix(args.Rel, root) {
181+
return
182+
}
183+
}
164184

165185
for _, r := range []language.GenerateResult{
166-
starlarkLibararyGenerate(args),
186+
starlarkLibraryGenerate(args, ext.starlarkLibraries),
187+
starlarkBundleGenerate(args, ext.starlarkBundleRoot),
167188
} {
168189
result.Gen = append(result.Gen, r.Gen...)
169190
result.Imports = append(result.Imports, r.Imports...)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* Copyright 2020 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+
// Package symbol generates a `starlark_bundle` target for every `.bzl` file in
17+
// each package. At the root of the module, a single starlark_bundle is
18+
// populated with deps that include all other symbol_libraries.
19+
//
20+
// The original code for this gazelle extension started from
21+
// https://github.com/bazelbuild/bazel-skylib/blob/main/gazelle/bzl/gazelle.go.
22+
package starlarkbundle
23+
24+
import (
25+
"sort"
26+
27+
"github.com/bazelbuild/bazel-gazelle/config"
28+
"github.com/bazelbuild/bazel-gazelle/label"
29+
"github.com/bazelbuild/bazel-gazelle/language"
30+
"github.com/bazelbuild/bazel-gazelle/resolve"
31+
"github.com/bazelbuild/bazel-gazelle/rule"
32+
)
33+
34+
const (
35+
starlarkBundleKind = "starlark_bundle"
36+
)
37+
38+
var starlarkBundleKindInfo = map[string]rule.KindInfo{
39+
starlarkBundleKind: {
40+
NonEmptyAttrs: map[string]bool{"deps": true},
41+
ResolveAttrs: map[string]bool{"deps": true},
42+
},
43+
}
44+
45+
var starlarkBundleLoadInfo = rule.LoadInfo{
46+
Name: "@build_stack_rules_proto//rules:starlark_bundle.bzl",
47+
Symbols: []string{starlarkBundleKind},
48+
}
49+
50+
func starlarkBundleRule() (*rule.Rule, []string) {
51+
r := rule.NewRule(starlarkBundleKind, starlarkBundleKind)
52+
53+
r.SetAttr("visibility", []string{"//visibility:public"})
54+
55+
return r, []string{}
56+
}
57+
58+
func starlarkBundleImports(_ *config.Config, _ *rule.Rule, _ *rule.File) []resolve.ImportSpec {
59+
return nil
60+
}
61+
62+
func starlarkBundleResolve(r *rule.Rule, starlarkLibraries map[label.Label]*rule.Rule) {
63+
deps := make([]string, 0, len(starlarkLibraries))
64+
for dep := range starlarkLibraries {
65+
deps = append(deps, dep.String())
66+
}
67+
68+
sort.Strings(deps)
69+
if len(deps) > 0 {
70+
r.SetAttr("deps", deps)
71+
}
72+
}
73+
74+
func starlarkBundleGenerate(args language.GenerateArgs, root *string) (result language.GenerateResult) {
75+
if root == nil || *root != args.Rel {
76+
return result
77+
}
78+
79+
r, loads := starlarkBundleRule()
80+
81+
return language.GenerateResult{
82+
Gen: []*rule.Rule{r},
83+
Imports: []any{loads},
84+
}
85+
}

language/starlarkbundle/starlark_library.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import (
3333
"github.com/bazelbuild/bazel-gazelle/label"
3434
"github.com/bazelbuild/bazel-gazelle/language"
3535
"github.com/bazelbuild/bazel-gazelle/pathtools"
36-
"github.com/bazelbuild/bazel-gazelle/repo"
3736
"github.com/bazelbuild/bazel-gazelle/resolve"
3837
"github.com/bazelbuild/bazel-gazelle/rule"
3938
"github.com/bazelbuild/buildtools/build"
@@ -42,6 +41,7 @@ import (
4241
const (
4342
starlarkLibraryKind = "starlark_library"
4443
fileType = ".bzl"
44+
visibilityPublic = "//visibility:public"
4545
)
4646

4747
var ignoreSuffix = suffixes{
@@ -73,14 +73,16 @@ func (s suffixes) Matches(test string) bool {
7373
}
7474

7575
func starlarkLibraryRule(args language.GenerateArgs, f string) (*rule.Rule, []string) {
76-
name := strings.TrimSuffix(f, fileType)
76+
// prefix with 'lib' in case someone is also using bzl_library gazelle
77+
// extension
78+
name := "lib" + strings.TrimSuffix(f, fileType)
7779
r := rule.NewRule(starlarkLibraryKind, name)
7880

7981
r.SetAttr("srcs", []string{f})
8082

8183
shouldSetVisibility := args.File == nil || !args.File.HasDefaultVisibility()
8284
if shouldSetVisibility {
83-
vis := checkInternalVisibility(args.Rel, "//visibility:public")
85+
vis := checkInternalVisibility(args.Rel, visibilityPublic)
8486
r.SetAttr("visibility", []string{vis})
8587
}
8688

@@ -96,7 +98,7 @@ func starlarkLibraryRule(args language.GenerateArgs, f string) (*rule.Rule, []st
9698
return r, loads
9799
}
98100

99-
func starlarkLibraryImports(c *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec {
101+
func starlarkLibraryImports(_ *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec {
100102
srcs := r.AttrStrings("srcs")
101103
imports := make([]resolve.ImportSpec, 0, len(srcs))
102104

@@ -115,7 +117,7 @@ func starlarkLibraryImports(c *config.Config, r *rule.Rule, f *rule.File) []reso
115117
return imports
116118
}
117119

118-
func starlarkLibraryResolve(c *config.Config, ix *resolve.RuleIndex, rc *repo.RemoteCache, r *rule.Rule, importsRaw interface{}, from label.Label) {
120+
func starlarkLibraryResolve(c *config.Config, ix *resolve.RuleIndex, r *rule.Rule, importsRaw interface{}, from label.Label) {
119121
imports := importsRaw.([]string)
120122

121123
r.DelAttr("deps")
@@ -175,7 +177,7 @@ func starlarkLibraryResolve(c *config.Config, ix *resolve.RuleIndex, rc *repo.Re
175177
}
176178
}
177179

178-
func starlarkLibararyGenerate(args language.GenerateArgs) language.GenerateResult {
180+
func starlarkLibraryGenerate(args language.GenerateArgs, starlarkLibraries map[label.Label]*rule.Rule) language.GenerateResult {
179181
var rules []*rule.Rule
180182
var imports []any
181183

@@ -186,12 +188,18 @@ func starlarkLibararyGenerate(args language.GenerateArgs) language.GenerateResul
186188
r, loads := starlarkLibraryRule(args, f)
187189
rules = append(rules, r)
188190
imports = append(imports, loads)
191+
192+
// populate the map so the bundle rule can use them later.
193+
if isVisibilityPublic(r.AttrStrings("visibility")) {
194+
from := label.New(args.Config.RepoName, args.Rel, r.Name())
195+
starlarkLibraries[from] = r
196+
}
189197
}
190198

191199
return language.GenerateResult{
192200
Gen: rules,
193201
Imports: imports,
194-
Empty: generateEmpty(args),
202+
Empty: starlarkLibraryEmptyRules(args),
195203
}
196204
}
197205

@@ -221,12 +229,16 @@ func isBzlSourceFile(f string) bool {
221229
return strings.HasSuffix(f, fileType) && !ignoreSuffix.Matches(f)
222230
}
223231

224-
// generateEmpty generates the list of rules that don't need to exist in the
225-
// BUILD file any more. For each symbol_library rule in args.File that only has
226-
// srcs that aren't in args.RegularFiles or args.GenFiles, add a symbol_library
227-
// with no srcs or deps. That will let Gazelle delete symbol_library rules after
228-
// the corresponding .bzl files are deleted.
229-
func generateEmpty(args language.GenerateArgs) []*rule.Rule {
232+
func isVisibilityPublic(vis []string) bool {
233+
return len(vis) == 1 && vis[0] == visibilityPublic
234+
}
235+
236+
// starlarkLibraryEmptyRules generates the list of rules that don't need to
237+
// exist in the BUILD file any more. For each symbol_library rule in args.File
238+
// that only has srcs that aren't in args.RegularFiles or args.GenFiles, add a
239+
// symbol_library with no srcs or deps. That will let Gazelle delete
240+
// symbol_library rules after the corresponding .bzl files are deleted.
241+
func starlarkLibraryEmptyRules(args language.GenerateArgs) []*rule.Rule {
230242
var ret []*rule.Rule
231243
if args.File == nil {
232244
return ret

rules/private/proto_repository_tools_srcs.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ PROTO_REPOSITORY_TOOLS_SRCS = [
4545
"@build_stack_rules_proto//language/protobuf:protobuf.go",
4646
"@build_stack_rules_proto//language/starlarkbundle:BUILD.bazel",
4747
"@build_stack_rules_proto//language/starlarkbundle:language.go",
48+
"@build_stack_rules_proto//language/starlarkbundle:starlark_bundle.go",
4849
"@build_stack_rules_proto//language/starlarkbundle:starlark_library.go",
4950
"@build_stack_rules_proto//pkg:BUILD.bazel",
5051
"@build_stack_rules_proto//pkg/goldentest:BUILD.bazel",

rules/starlark_bundle.bzl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""starlark_bundle.bzl aggreagates the transitive sources of dependencies
2+
3+
Rule is needed because bzl_library strictly requires srcs.
4+
"""
5+
6+
load("@bazel_skylib//:bzl_library.bzl", "StarlarkLibraryInfo")
7+
8+
def _starlark_bundle_impl(ctx):
9+
deps = [d[StarlarkLibraryInfo] for d in ctx.attr.deps]
10+
transitive_srcs = depset(transitive = [d.transitive_srcs for d in deps])
11+
12+
return [
13+
DefaultInfo(
14+
files = transitive_srcs,
15+
),
16+
StarlarkLibraryInfo(
17+
srcs = [],
18+
transitive_srcs = transitive_srcs,
19+
),
20+
]
21+
22+
starlark_bundle = rule(
23+
implementation = _starlark_bundle_impl,
24+
attrs = {
25+
"deps": attr.label_list(
26+
doc = "list of starlark_library or bzl library rules",
27+
providers = [StarlarkLibraryInfo],
28+
),
29+
},
30+
doc = "",
31+
provides = [DefaultInfo, StarlarkLibraryInfo],
32+
)

0 commit comments

Comments
 (0)