From d72ba4c4eb8ee061ce8ef26dfb81d4e2ff43ebea Mon Sep 17 00:00:00 2001 From: Rogan Morrow Date: Mon, 22 May 2023 21:26:08 +1000 Subject: [PATCH] Add java_generate_proto directive (#181) Addresses part 1 of https://github.com/bazel-contrib/rules_jvm/issues/180 --- java/gazelle/configure.go | 12 ++ java/gazelle/generate.go | 106 +++++++++--------- java/gazelle/javaconfig/config.go | 16 +++ .../src/main/proto/example/hello/BUILD.in | 0 .../src/main/proto/example/hello/BUILD.out | 0 .../src/main/proto/example/hello/book.proto | 0 .../src/main/proto/example/hello/hello.proto | 0 .../src/main/proto/example/hello/BUILD.in | 1 + .../src/main/proto/example/hello/BUILD.out | 13 +++ .../src/main/proto/example/hello/book.proto | 12 ++ .../src/main/proto/example/hello/hello.proto | 16 +++ .../src/main/proto/example/hello/BUILD.in | 1 + .../src/main/proto/example/hello/BUILD.out | 25 +++++ .../src/main/proto/example/hello/book.proto | 12 ++ .../src/main/proto/example/hello/hello.proto | 16 +++ 15 files changed, 180 insertions(+), 50 deletions(-) rename java/gazelle/testdata/proto/{ => default}/src/main/proto/example/hello/BUILD.in (100%) rename java/gazelle/testdata/proto/{ => default}/src/main/proto/example/hello/BUILD.out (100%) rename java/gazelle/testdata/proto/{ => default}/src/main/proto/example/hello/book.proto (100%) rename java/gazelle/testdata/proto/{ => default}/src/main/proto/example/hello/hello.proto (100%) create mode 100644 java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/BUILD.in create mode 100644 java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/BUILD.out create mode 100644 java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/book.proto create mode 100644 java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/hello.proto create mode 100644 java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/BUILD.in create mode 100644 java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/BUILD.out create mode 100644 java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/book.proto create mode 100644 java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/hello.proto diff --git a/java/gazelle/configure.go b/java/gazelle/configure.go index a6519b17..7e5ddd98 100644 --- a/java/gazelle/configure.go +++ b/java/gazelle/configure.go @@ -56,6 +56,7 @@ func (jc *Configurer) KnownDirectives() []string { javaconfig.JavaModuleGranularityDirective, javaconfig.JavaTestFileSuffixes, javaconfig.JavaTestMode, + javaconfig.JavaGenerateProto, } } @@ -107,6 +108,17 @@ func (jc *Configurer) Configure(c *config.Config, rel string, f *rule.File) { case javaconfig.JavaTestMode: cfg.SetTestMode(d.Value) + + case javaconfig.JavaGenerateProto: + switch d.Value { + case "true": + cfg.SetGenerateProto(true) + case "false": + cfg.SetGenerateProto(false) + default: + jc.lang.logger.Fatal().Msgf("invalid value for directive %q: %s: possible values are true/false", + javaconfig.JavaGenerateProto, d.Value) + } } } } diff --git a/java/gazelle/generate.go b/java/gazelle/generate.go index d56482b0..cea42db0 100644 --- a/java/gazelle/generate.go +++ b/java/gazelle/generate.go @@ -50,58 +50,10 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes return res } - var protoRuleNames []string - protoPackages := make(map[string]proto.Package) - protoFileInfo := make(map[string]proto.FileInfo) - for _, r := range args.OtherGen { - if r.Kind() != "proto_library" { - continue - } - pkg := r.PrivateAttr(proto.PackageKey).(proto.Package) - protoPackages[r.Name()] = pkg - for name, info := range pkg.Files { - protoFileInfo[name] = info - } - protoRuleNames = append(protoRuleNames, r.Name()) - } - sort.Strings(protoRuleNames) - isModule := cfg.ModuleGranularity() == "module" - for _, protoRuleName := range protoRuleNames { - protoPackage := protoPackages[protoRuleName] - - jplName := strings.TrimSuffix(protoRuleName, "_proto") + "_java_proto" - jglName := strings.TrimSuffix(protoRuleName, "_proto") + "_java_grpc" - jlName := strings.TrimSuffix(protoRuleName, "_proto") + "_java_library" - - rjpl := rule.NewRule("java_proto_library", jplName) - rjpl.SetAttr("deps", []string{":" + protoRuleName}) - res.Gen = append(res.Gen, rjpl) - res.Imports = append(res.Imports, types.ResolveInput{}) - - if protoPackage.HasServices { - r := rule.NewRule("java_grpc_library", jglName) - r.SetAttr("srcs", []string{":" + protoRuleName}) - r.SetAttr("deps", []string{":" + jplName}) - res.Gen = append(res.Gen, r) - res.Imports = append(res.Imports, types.ResolveInput{}) - } - - rjl := rule.NewRule("java_library", jlName) - rjl.SetAttr("visibility", []string{"//:__subpackages__"}) - var exports []string - if protoPackage.HasServices { - exports = append(exports, ":"+jglName) - } - rjl.SetAttr("exports", append(exports, ":"+jplName)) - packageName := types.NewPackageName(protoPackage.Options["java_package"]) - log.Debug().Str("pkg", packageName.Name).Msg("adding the proto import statement") - rjl.SetPrivateAttr(packagesKey, []types.ResolvableJavaPackage{*types.NewResolvableJavaPackage(packageName, false, false)}) - res.Gen = append(res.Gen, rjl) - res.Imports = append(res.Imports, types.ResolveInput{ - PackageNames: sorted_set.NewSortedSetFn([]types.PackageName{packageName}, types.PackageNameLess), - }) + if cfg.GenerateProto() { + generateProtoLibraries(args, log, &res) } javaFilenamesRelativeToPackage := filterStrSlice(args.RegularFiles, func(f string) bool { return filepath.Ext(f) == ".java" }) @@ -351,6 +303,60 @@ func (l javaLang) collectRuntimeDeps(kind, name string, file *rule.File) *sorted return runtimeDeps } +func generateProtoLibraries(args language.GenerateArgs, log zerolog.Logger, res *language.GenerateResult) { + var protoRuleNames []string + protoPackages := make(map[string]proto.Package) + protoFileInfo := make(map[string]proto.FileInfo) + for _, r := range args.OtherGen { + if r.Kind() != "proto_library" { + continue + } + pkg := r.PrivateAttr(proto.PackageKey).(proto.Package) + protoPackages[r.Name()] = pkg + for name, info := range pkg.Files { + protoFileInfo[name] = info + } + protoRuleNames = append(protoRuleNames, r.Name()) + } + sort.Strings(protoRuleNames) + + for _, protoRuleName := range protoRuleNames { + protoPackage := protoPackages[protoRuleName] + + jplName := strings.TrimSuffix(protoRuleName, "_proto") + "_java_proto" + jglName := strings.TrimSuffix(protoRuleName, "_proto") + "_java_grpc" + jlName := strings.TrimSuffix(protoRuleName, "_proto") + "_java_library" + + rjpl := rule.NewRule("java_proto_library", jplName) + rjpl.SetAttr("deps", []string{":" + protoRuleName}) + res.Gen = append(res.Gen, rjpl) + res.Imports = append(res.Imports, types.ResolveInput{}) + + if protoPackage.HasServices { + r := rule.NewRule("java_grpc_library", jglName) + r.SetAttr("srcs", []string{":" + protoRuleName}) + r.SetAttr("deps", []string{":" + jplName}) + res.Gen = append(res.Gen, r) + res.Imports = append(res.Imports, types.ResolveInput{}) + } + + rjl := rule.NewRule("java_library", jlName) + rjl.SetAttr("visibility", []string{"//:__subpackages__"}) + var exports []string + if protoPackage.HasServices { + exports = append(exports, ":"+jglName) + } + rjl.SetAttr("exports", append(exports, ":"+jplName)) + packageName := types.NewPackageName(protoPackage.Options["java_package"]) + log.Debug().Str("pkg", packageName.Name).Msg("adding the proto import statement") + rjl.SetPrivateAttr(packagesKey, []types.ResolvableJavaPackage{*types.NewResolvableJavaPackage(packageName, false, false)}) + res.Gen = append(res.Gen, rjl) + res.Imports = append(res.Imports, types.ResolveInput{ + PackageNames: sorted_set.NewSortedSetFn([]types.PackageName{packageName}, types.PackageNameLess), + }) + } +} + // We exclude intra-target imports because otherwise we'd get self-dependencies come resolve time. // toExports is optional and may be nil. All other parameters are required and must be non-nil. func addNonLocalImportsAndExports(toImports *sorted_set.SortedSet[types.PackageName], toExports *sorted_set.SortedSet[types.PackageName], fromImportedClasses *sorted_set.SortedSet[types.ClassName], fromPackages *sorted_set.SortedSet[types.PackageName], fromExportedClasses *sorted_set.SortedSet[types.ClassName], pkg types.PackageName, localClasses *sorted_set.SortedSet[string]) { diff --git a/java/gazelle/javaconfig/config.go b/java/gazelle/javaconfig/config.go index 4f94302e..da9cf481 100644 --- a/java/gazelle/javaconfig/config.go +++ b/java/gazelle/javaconfig/config.go @@ -37,6 +37,11 @@ const ( // JavaTestMode allows user to choose from per file test or per directory test suite. JavaTestMode = "java_test_mode" + + // JavaGenerateProto tells the code generator whether to generate `java_proto_library` (and `java_library`) + // rules when a `proto_library` rule is present. + // Can be either "true" or "false". Defaults to "true". + JavaGenerateProto = "java_generate_proto" ) // Configs is an extension of map[string]*Config. It provides finding methods @@ -50,6 +55,7 @@ func (c *Config) NewChild() *Config { parent: c, extensionEnabled: c.extensionEnabled, isModuleRoot: false, + generateProto: true, mavenInstallFile: c.mavenInstallFile, moduleGranularity: c.moduleGranularity, repoRoot: c.repoRoot, @@ -75,6 +81,7 @@ type Config struct { extensionEnabled bool isModuleRoot bool + generateProto bool mavenInstallFile string moduleGranularity string repoRoot string @@ -94,6 +101,7 @@ func New(repoRoot string) *Config { return &Config{ extensionEnabled: true, isModuleRoot: false, + generateProto: true, mavenInstallFile: "maven_install.json", moduleGranularity: "package", repoRoot: repoRoot, @@ -118,6 +126,14 @@ func (c Config) IsModuleRoot() bool { return c.isModuleRoot } +func (c *Config) GenerateProto() bool { + return c.generateProto +} + +func (c *Config) SetGenerateProto(generate bool) { + c.generateProto = generate +} + func (c Config) MavenInstallFile() string { return filepath.Join(c.repoRoot, c.mavenInstallFile) } diff --git a/java/gazelle/testdata/proto/src/main/proto/example/hello/BUILD.in b/java/gazelle/testdata/proto/default/src/main/proto/example/hello/BUILD.in similarity index 100% rename from java/gazelle/testdata/proto/src/main/proto/example/hello/BUILD.in rename to java/gazelle/testdata/proto/default/src/main/proto/example/hello/BUILD.in diff --git a/java/gazelle/testdata/proto/src/main/proto/example/hello/BUILD.out b/java/gazelle/testdata/proto/default/src/main/proto/example/hello/BUILD.out similarity index 100% rename from java/gazelle/testdata/proto/src/main/proto/example/hello/BUILD.out rename to java/gazelle/testdata/proto/default/src/main/proto/example/hello/BUILD.out diff --git a/java/gazelle/testdata/proto/src/main/proto/example/hello/book.proto b/java/gazelle/testdata/proto/default/src/main/proto/example/hello/book.proto similarity index 100% rename from java/gazelle/testdata/proto/src/main/proto/example/hello/book.proto rename to java/gazelle/testdata/proto/default/src/main/proto/example/hello/book.proto diff --git a/java/gazelle/testdata/proto/src/main/proto/example/hello/hello.proto b/java/gazelle/testdata/proto/default/src/main/proto/example/hello/hello.proto similarity index 100% rename from java/gazelle/testdata/proto/src/main/proto/example/hello/hello.proto rename to java/gazelle/testdata/proto/default/src/main/proto/example/hello/hello.proto diff --git a/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/BUILD.in b/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/BUILD.in new file mode 100644 index 00000000..ad5d473f --- /dev/null +++ b/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/BUILD.in @@ -0,0 +1 @@ +# gazelle:java_generate_proto false diff --git a/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/BUILD.out b/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/BUILD.out new file mode 100644 index 00000000..48533ca9 --- /dev/null +++ b/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/BUILD.out @@ -0,0 +1,13 @@ +load("@rules_proto//proto:defs.bzl", "proto_library") + +# gazelle:java_generate_proto false + +proto_library( + name = "example_hello_proto", + srcs = [ + "book.proto", + "hello.proto", + ], + visibility = ["//visibility:public"], + deps = ["//example/hello:hello_proto"], +) diff --git a/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/book.proto b/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/book.proto new file mode 100644 index 00000000..5ce0a5df --- /dev/null +++ b/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/book.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package example.hello; + +option java_outer_classname = "HelloProto"; +option java_multiple_files = true; +option java_package = "com.example.hello"; + +message Book { + string title = 1; + string author = 2; +} diff --git a/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/hello.proto b/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/hello.proto new file mode 100644 index 00000000..198bad18 --- /dev/null +++ b/java/gazelle/testdata/proto/generate_false/src/main/proto/example/hello/hello.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package example.hello; + +import "example/hello/book.proto"; + +option java_outer_classname = "HelloProto"; +option java_multiple_files = true; +option java_package = "com.example.hello"; + +message BarRequest { + string name = 1; + Book book = 2; +} + +message BarResponse { string message = 1; } diff --git a/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/BUILD.in b/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/BUILD.in new file mode 100644 index 00000000..a40071ea --- /dev/null +++ b/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/BUILD.in @@ -0,0 +1 @@ +# gazelle:java_generate_proto true diff --git a/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/BUILD.out b/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/BUILD.out new file mode 100644 index 00000000..73607aee --- /dev/null +++ b/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/BUILD.out @@ -0,0 +1,25 @@ +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@rules_java//java:defs.bzl", "java_library", "java_proto_library") + +# gazelle:java_generate_proto true + +proto_library( + name = "example_hello_proto", + srcs = [ + "book.proto", + "hello.proto", + ], + visibility = ["//visibility:public"], + deps = ["//example/hello:hello_proto"], +) + +java_proto_library( + name = "example_hello_java_proto", + deps = [":example_hello_proto"], +) + +java_library( + name = "example_hello_java_library", + visibility = ["//:__subpackages__"], + exports = [":example_hello_java_proto"], +) diff --git a/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/book.proto b/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/book.proto new file mode 100644 index 00000000..5ce0a5df --- /dev/null +++ b/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/book.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package example.hello; + +option java_outer_classname = "HelloProto"; +option java_multiple_files = true; +option java_package = "com.example.hello"; + +message Book { + string title = 1; + string author = 2; +} diff --git a/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/hello.proto b/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/hello.proto new file mode 100644 index 00000000..198bad18 --- /dev/null +++ b/java/gazelle/testdata/proto/generate_true/src/main/proto/example/hello/hello.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package example.hello; + +import "example/hello/book.proto"; + +option java_outer_classname = "HelloProto"; +option java_multiple_files = true; +option java_package = "com.example.hello"; + +message BarRequest { + string name = 1; + Book book = 2; +} + +message BarResponse { string message = 1; }