Skip to content

Commit 88af418

Browse files
committed
checkpoint: bazel_lib as starlark_repository actually builds!
1 parent 0132f9f commit 88af418

13 files changed

+772
-88
lines changed

extensions/starlark_repository.bzl

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""proto_repostitory.bzl provides the starlark_repository rule."""
2+
3+
# Copyright 2014 The Bazel Authors. All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
load("@bazel_features//:features.bzl", "bazel_features")
18+
load("@build_stack_rules_proto//rules/proto:starlark_repository.bzl", "starlark_repository_attrs", starlark_repository_repo_rule = "starlark_repository")
19+
20+
def _extension_metadata(
21+
module_ctx,
22+
*,
23+
root_module_direct_deps = None,
24+
root_module_direct_dev_deps = None,
25+
reproducible = False):
26+
"""returns the module_ctx.extension_metadata in a bazel-version-aware way
27+
28+
This function was copied from the bazel-gazelle repository.
29+
"""
30+
31+
if not hasattr(module_ctx, "extension_metadata"):
32+
return None
33+
metadata_kwargs = {}
34+
if bazel_features.external_deps.extension_metadata_has_reproducible:
35+
metadata_kwargs["reproducible"] = reproducible
36+
return module_ctx.extension_metadata(
37+
root_module_direct_deps = root_module_direct_deps,
38+
root_module_direct_dev_deps = root_module_direct_dev_deps,
39+
**metadata_kwargs
40+
)
41+
42+
def _starlark_repository_impl(module_ctx):
43+
# named_repos is a dict<K,V> where V is the kwargs for the actual
44+
# "starlark_repository" repo rule and K is the tag.name (the name given by the
45+
# MODULE.bazel author)
46+
named_archives = {}
47+
48+
# iterate all the module tags and gather a list of named_archives.
49+
#
50+
# TODO(pcj): what is the best practice for version selection here? Do I need
51+
# to check if module.is_root and handle that differently?
52+
#
53+
for module in module_ctx.modules:
54+
for tag in module.tags.archive:
55+
kwargs = {
56+
attr: getattr(tag, attr)
57+
for attr in _starlark_repository_archive_attrs.keys()
58+
if hasattr(tag, attr)
59+
}
60+
named_archives[tag.name] = kwargs
61+
62+
# declare a repository rule foreach one
63+
for apparent_name, kwargs in named_archives.items():
64+
starlark_repository_repo_rule(
65+
apparent_name = apparent_name,
66+
**kwargs
67+
)
68+
69+
return _extension_metadata(
70+
module_ctx,
71+
reproducible = True,
72+
)
73+
74+
_starlark_repository_archive_attrs = starlark_repository_attrs | {
75+
"name": attr.string(
76+
doc = "The repo name.",
77+
mandatory = True,
78+
),
79+
}
80+
_starlark_repository_archive_attrs.pop("apparent_name")
81+
82+
starlark_repository = module_extension(
83+
implementation = _starlark_repository_impl,
84+
tag_classes = dict(
85+
archive = tag_class(
86+
doc = "declare an http_archive repository that is post-processed by a custom version of gazelle that includes the 'protobuf' language",
87+
attrs = _starlark_repository_archive_attrs,
88+
),
89+
),
90+
)

language/starlarkbundle/BUILD.bazel

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
load("@io_bazel_rules_go//go:def.bzl", "go_library")
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
22

33
go_library(
44
name = "starlarkbundle",
55
srcs = [
66
"language.go",
7+
"lifecycle.go",
78
"starlark_bundle.go",
89
"starlark_library.go",
910
],
@@ -20,3 +21,16 @@ go_library(
2021
"@com_github_bazelbuild_buildtools//build:go_default_library",
2122
],
2223
)
24+
25+
go_test(
26+
name = "starlarkbundle_test",
27+
srcs = [
28+
"lifecycle_test.go",
29+
"starlark_library_test.go",
30+
],
31+
embed = [":starlarkbundle"],
32+
deps = [
33+
"@bazel_gazelle//config:go_default_library",
34+
"@com_github_bazelbuild_buildtools//build:go_default_library",
35+
],
36+
)

language/starlarkbundle/language.go

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ package starlarkbundle
2323

2424
import (
2525
"flag"
26+
"io"
2627
"log"
2728
"maps"
29+
"os"
2830
"strings"
2931

3032
"github.com/bazelbuild/bazel-gazelle/config"
@@ -36,22 +38,29 @@ import (
3638
)
3739

3840
const (
39-
languageName = "starlark_bundle"
40-
starlarkBundleRootDirectiveName = "starlark_bundle_root"
41-
starlarkBundleExcludeDirectiveName = "starlark_bundle_exclude"
41+
languageName = "starlark_bundle"
42+
starlarkBundleRootDirectiveName = "starlark_bundle_root"
43+
starlarkBundleExcludeDirectiveName = "starlark_bundle_exclude"
44+
starlarkBundleLogFileDirectiveName = "starlark_bundle_log_file"
45+
starlarkModuleDependencyDirectiveName = "module_dependency"
4246
)
4347

4448
type starlarkBundleLang struct {
4549
starlarkBundleRoot *string
4650
starlarkBundleExcludeDirs []string
51+
moduleDeps map[string]string
4752
starlarkLibraries map[label.Label]*rule.Rule
53+
logFile string
54+
logWriter *os.File
55+
logger *log.Logger
4856
}
4957

5058
// NewLanguage is called by Gazelle to install this language extension in a
5159
// binary.
5260
func NewLanguage() language.Language {
5361
return &starlarkBundleLang{
54-
starlarkLibraries: make(map[label.Label]*rule.Rule),
62+
starlarkLibraries: map[label.Label]*rule.Rule{},
63+
moduleDeps: map[string]string{},
5564
}
5665
}
5766

@@ -66,21 +75,32 @@ func (*starlarkBundleLang) Name() string {
6675
// https://pkg.go.dev/github.com/bazelbuild/bazel-gazelle/resolve?tab=doc#Resolver
6776
// interface, but are otherwise unused.
6877
func (ext *starlarkBundleLang) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {
78+
fs.StringVar(&ext.logFile, "starlark_bundle_log", "", "path to log file for starlark_bundle extension")
6979
}
7080

71-
func (*starlarkBundleLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
81+
func (ext *starlarkBundleLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
82+
if ext.logFile != "" {
83+
ext.createLogger(c, ext.logFile)
84+
ext.logf("CheckFlags: log file initialized from flag: %s", ext.logFile)
85+
}
86+
if ext.logger == nil {
87+
ext.logger = log.New(io.Discard, "", 0)
88+
}
7289
return nil
7390
}
7491

7592
func (*starlarkBundleLang) KnownDirectives() []string {
7693
return []string{
7794
starlarkBundleRootDirectiveName,
7895
starlarkBundleExcludeDirectiveName,
96+
starlarkBundleLogFileDirectiveName,
97+
starlarkModuleDependencyDirectiveName,
7998
}
8099
}
81100

82101
func (ext *starlarkBundleLang) Configure(c *config.Config, rel string, f *rule.File) {
83102
if f != nil {
103+
ext.logf("Configure: processing %d directives in %s", len(f.Directives), rel)
84104
for _, d := range f.Directives {
85105
switch d.Key {
86106
case starlarkBundleRootDirectiveName:
@@ -90,11 +110,37 @@ func (ext *starlarkBundleLang) Configure(c *config.Config, rel string, f *rule.F
90110
ext.starlarkBundleRoot = &rel
91111
case starlarkBundleExcludeDirectiveName:
92112
ext.starlarkBundleExcludeDirs = append(ext.starlarkBundleExcludeDirs, d.Value)
113+
case starlarkModuleDependencyDirectiveName:
114+
nameVersion := strings.Fields(d.Value)
115+
if len(nameVersion) != 2 {
116+
log.Fatalf("malformed directive %s, should be NAME VERSION: %s", starlarkModuleDependencyDirectiveName, d.Value)
117+
}
118+
ext.moduleDeps[nameVersion[0]] = nameVersion[1]
119+
ext.logf("Added module dependency: %q -> %q", nameVersion[0], nameVersion[1])
120+
case starlarkBundleLogFileDirectiveName:
121+
ext.createLogger(c, d.Value)
93122
}
94123
}
95124
}
96125
}
97126

127+
func (ext *starlarkBundleLang) createLogger(c *config.Config, logFile string) {
128+
if logFile != "" {
129+
f, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
130+
if err == nil {
131+
ext.logWriter = f
132+
ext.logger = log.New(f, "", log.LstdFlags)
133+
} else {
134+
log.Fatalf("attempting to open log file: %v", err)
135+
}
136+
}
137+
if false && ext.logger == nil {
138+
ext.logger = log.New(io.Discard, "", 0)
139+
}
140+
141+
ext.logf("Log initialized")
142+
}
143+
98144
// Kinds returns a map of maps rule names (kinds) and information on how to
99145
// match and merge attributes that may be found in rules of those kinds. All
100146
// kinds of rules generated for this language may be found here.
@@ -154,7 +200,7 @@ func (*starlarkBundleLang) Embeds(r *rule.Rule, from label.Label) []label.Label
154200
func (ext *starlarkBundleLang) Resolve(c *config.Config, ix *resolve.RuleIndex, rc *repo.RemoteCache, r *rule.Rule, importsRaw interface{}, from label.Label) {
155201
switch r.Kind() {
156202
case starlarkLibraryKind:
157-
starlarkLibraryResolve(c, ix, r, importsRaw, from)
203+
starlarkLibraryResolve(c, ix, r, importsRaw, from, ext)
158204
case starlarkBundleKind:
159205
starlarkBundleResolve(r, ext.starlarkLibraries)
160206
}
@@ -182,8 +228,10 @@ func (ext *starlarkBundleLang) GenerateRules(args language.GenerateArgs) (result
182228
}
183229
}
184230

231+
ext.logf("GenerateRules: visiting %s", args.Rel)
232+
185233
for _, r := range []language.GenerateResult{
186-
starlarkLibraryGenerate(args, ext.starlarkLibraries),
234+
starlarkLibraryGenerate(args, ext.starlarkLibraries, ext),
187235
starlarkBundleGenerate(args, ext.starlarkBundleRoot),
188236
} {
189237
result.Gen = append(result.Gen, r.Gen...)
@@ -193,3 +241,26 @@ func (ext *starlarkBundleLang) GenerateRules(args language.GenerateArgs) (result
193241

194242
return
195243
}
244+
245+
func (ext *starlarkBundleLang) getModuleDependencyRepoName(repo string) (string, bool) {
246+
ext.logf("getModuleDependencyRepoName called with repo=%q", repo)
247+
if repo == "" {
248+
ext.logf(" returning early for empty repo: @")
249+
return "@", true
250+
}
251+
if mappedRepo, ok := ext.moduleDeps[repo]; ok {
252+
ext.logf(" found mapping: %q -> %q", repo, mappedRepo)
253+
return mappedRepo, true
254+
} else {
255+
ext.logf(" unknown module dependency: %q (known deps: %v)", repo, ext.moduleDeps)
256+
log.Fatalf("unknown module dependency: %q", repo)
257+
return repo, false
258+
}
259+
}
260+
261+
// logf logs a message using the extension's logger if configured
262+
func (ext *starlarkBundleLang) logf(format string, args ...any) {
263+
if ext.logger != nil {
264+
ext.logger.Printf(format, args...)
265+
}
266+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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 starlarkbundle
17+
18+
import (
19+
"context"
20+
)
21+
22+
// Before implements part of the language.LifecycleManager interface.
23+
func (ext *starlarkBundleLang) Before(context.Context) {
24+
ext.logf("Lifecycle: Before() called")
25+
}
26+
27+
// DoneGeneratingRules implements part of the language.FinishableLanguage interface.
28+
func (ext *starlarkBundleLang) DoneGeneratingRules() {
29+
ext.logf("Lifecycle: DoneGeneratingRules() called")
30+
}
31+
32+
// AfterResolvingDeps implements part of the language.LifecycleManager interface.
33+
// This is where we flush and close the log file.
34+
func (ext *starlarkBundleLang) AfterResolvingDeps(context.Context) {
35+
ext.logf("Lifecycle: AfterResolvingDeps() called - flushing and closing log file")
36+
if ext.logWriter != nil {
37+
// Sync flushes any buffered data to disk
38+
_ = ext.logWriter.Sync()
39+
// Close the file
40+
_ = ext.logWriter.Close()
41+
ext.logWriter = nil
42+
}
43+
}

0 commit comments

Comments
 (0)