Skip to content

Commit

Permalink
Add java_generate_proto directive (#181)
Browse files Browse the repository at this point in the history
Addresses part 1 of #180
  • Loading branch information
meowcakes authored May 22, 2023
1 parent b48fbad commit d72ba4c
Show file tree
Hide file tree
Showing 15 changed files with 180 additions and 50 deletions.
12 changes: 12 additions & 0 deletions java/gazelle/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func (jc *Configurer) KnownDirectives() []string {
javaconfig.JavaModuleGranularityDirective,
javaconfig.JavaTestFileSuffixes,
javaconfig.JavaTestMode,
javaconfig.JavaGenerateProto,
}
}

Expand Down Expand Up @@ -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)
}
}
}
}
Expand Down
106 changes: 56 additions & 50 deletions java/gazelle/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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" })
Expand Down Expand Up @@ -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]) {
Expand Down
16 changes: 16 additions & 0 deletions java/gazelle/javaconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -75,6 +81,7 @@ type Config struct {

extensionEnabled bool
isModuleRoot bool
generateProto bool
mavenInstallFile string
moduleGranularity string
repoRoot string
Expand All @@ -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,
Expand All @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:java_generate_proto false
Original file line number Diff line number Diff line change
@@ -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"],
)
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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; }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:java_generate_proto true
Original file line number Diff line number Diff line change
@@ -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"],
)
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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; }

0 comments on commit d72ba4c

Please sign in to comment.