Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions java/gazelle/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ go_library(
deps = [
"//java/gazelle/javaconfig",
"//java/gazelle/private/java",
"//java/gazelle/private/java_export_index",
"//java/gazelle/private/javaparser",
"//java/gazelle/private/logconfig",
"//java/gazelle/private/maven",
Expand Down
23 changes: 23 additions & 0 deletions java/gazelle/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func (jc *Configurer) KnownDirectives() []string {
javaconfig.JavaGenerateProto,
javaconfig.JavaMavenRepositoryName,
javaconfig.JavaAnnotationProcessorPlugin,
javaconfig.JavaResolveToJavaExports,
}
}

Expand Down Expand Up @@ -146,7 +147,29 @@ func (jc *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
jc.lang.logger.Fatal().Msgf("invalid value for directive %q: %q: couldn't parse annotation processor class-name: %v", javaconfig.JavaAnnotationProcessorPlugin, parts[1], err)
}
cfg.AddAnnotationProcessorPlugin(*annotationClassName, *processorClassName)

case javaconfig.JavaResolveToJavaExports:
if !cfg.CanSetResolveToJavaExports() {
jc.lang.logger.Fatal().
Msgf("Detected multiple attempts to initialize directive %q. Please only initialize it once for the entire repository.",
javaconfig.JavaResolveToJavaExports)
}
if rel != "" {
jc.lang.logger.Fatal().
Msgf("Enabling or disabling directive %q must be done from the root of the repository.",
javaconfig.JavaResolveToJavaExports)
}
switch d.Value {
case "true":
cfg.SetResolveToJavaExports(true)
case "false":
cfg.SetResolveToJavaExports(false)
default:
jc.lang.logger.Fatal().Msgf("invalid value for directive %q: %s: possible values are true/false",
javaconfig.JavaResolveToJavaExports, d.Value)
}
}

}
}

Expand Down
10 changes: 7 additions & 3 deletions java/gazelle/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes
}

if productionJavaFiles.Len() > 0 {
l.generateJavaLibrary(args.File, args.Rel, filepath.Base(args.Rel), productionJavaFiles.SortedSlice(), allPackageNames, nonLocalProductionJavaImports, nonLocalJavaExports, annotationProcessorClasses, false, javaLibraryKind, &res)
l.generateJavaLibrary(args.File, args.Rel, filepath.Base(args.Rel), productionJavaFiles.SortedSlice(), allPackageNames, nonLocalProductionJavaImports, nonLocalJavaExports, annotationProcessorClasses, false, javaLibraryKind, &res, cfg, args.Config.RepoName)
}

var testHelperJavaClasses *sorted_set.SortedSet[types.ClassName]
Expand Down Expand Up @@ -236,7 +236,7 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes
testJavaImportsWithHelpers.Add(tf.pkg)
srcs = append(srcs, tf.pathRelativeToBazelWorkspaceRoot)
}
l.generateJavaLibrary(args.File, args.Rel, filepath.Base(args.Rel), srcs, packages, testJavaImports, nonLocalJavaExports, annotationProcessorClasses, true, javaLibraryKind, &res)
l.generateJavaLibrary(args.File, args.Rel, filepath.Base(args.Rel), srcs, packages, testJavaImports, nonLocalJavaExports, annotationProcessorClasses, true, javaLibraryKind, &res, cfg, args.Config.RepoName)
}
}

Expand Down Expand Up @@ -471,7 +471,7 @@ func accumulateJavaFile(cfg *javaconfig.Config, testJavaFiles, testHelperJavaFil
}
}

func (l javaLang) generateJavaLibrary(file *rule.File, pathToPackageRelativeToBazelWorkspace string, name string, srcsRelativeToBazelWorkspace []string, packages, imports *sorted_set.SortedSet[types.PackageName], exports *sorted_set.SortedSet[types.PackageName], annotationProcessorClasses *sorted_set.SortedSet[types.ClassName], testonly bool, javaLibraryRuleKind string, res *language.GenerateResult) {
func (l javaLang) generateJavaLibrary(file *rule.File, pathToPackageRelativeToBazelWorkspace, name string, srcsRelativeToBazelWorkspace []string, packages, imports, exports *sorted_set.SortedSet[types.PackageName], annotationProcessorClasses *sorted_set.SortedSet[types.ClassName], testonly bool, javaLibraryRuleKind string, res *language.GenerateResult, cfg *javaconfig.Config, repoName string) {
const ruleKind = "java_library"
r := rule.NewRule(ruleKind, name)

Expand Down Expand Up @@ -508,6 +508,10 @@ func (l javaLang) generateJavaLibrary(file *rule.File, pathToPackageRelativeToBa
AnnotationProcessors: annotationProcessorClasses,
}
res.Imports = append(res.Imports, resolveInput)

if cfg.ResolveToJavaExports() {
l.javaExportIndex.RecordRuleWithResolveInput(repoName, file, r, resolveInput)
}
}

func (l javaLang) generateJavaBinary(file *rule.File, m types.ClassName, libName string, testonly bool, res *language.GenerateResult) {
Expand Down
22 changes: 22 additions & 0 deletions java/gazelle/javaconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ const (
// JavaAnnotationProcessorPlugin tells the code generator about specific java_plugin targets needed to process
// specific annotations.
JavaAnnotationProcessorPlugin = "java_annotation_processor_plugin"

// JavaResolveToJavaExports tells the code generator to favour resolving dependencies to java_exports where possible.
// If enabled, generated libraries will try to depend on java_exports targets that export a given package, instead of the underlying library.
// This allows monorepos to closely match a traditional Gradle/Maven model where subprojects are published in jars.
// Can be either "true" or "false". Defaults to "true".
// Inherited by children packages, can only be set at the root of the repository.
JavaResolveToJavaExports = "java_resolve_to_java_exports"
)

// Configs is an extension of map[string]*Config. It provides finding methods
Expand All @@ -75,6 +82,7 @@ func (c *Config) NewChild() *Config {
extensionEnabled: c.extensionEnabled,
isModuleRoot: false,
generateProto: true,
resolveToJavaExports: c.resolveToJavaExports,
mavenInstallFile: c.mavenInstallFile,
moduleGranularity: c.moduleGranularity,
repoRoot: c.repoRoot,
Expand Down Expand Up @@ -105,6 +113,7 @@ type Config struct {
extensionEnabled bool
isModuleRoot bool
generateProto bool
resolveToJavaExports *types.LateInit[bool]
mavenInstallFile string
moduleGranularity string
repoRoot string
Expand All @@ -128,6 +137,7 @@ func New(repoRoot string) *Config {
extensionEnabled: true,
isModuleRoot: false,
generateProto: true,
resolveToJavaExports: types.NewLateInit[bool](true),
mavenInstallFile: "maven_install.json",
moduleGranularity: "package",
repoRoot: repoRoot,
Expand Down Expand Up @@ -294,6 +304,18 @@ func (c *Config) AddAnnotationProcessorPlugin(annotationClass types.ClassName, p
c.annotationProcessorFullQualifiedClassToPluginClass[fullyQualifiedAnnotationClass].Add(processorClass)
}

func (c *Config) ResolveToJavaExports() bool {
return c.resolveToJavaExports.Value()
}

func (c *Config) CanSetResolveToJavaExports() bool {
return !c.resolveToJavaExports.IsInitialized()
}

func (c *Config) SetResolveToJavaExports(resolve bool) {
c.resolveToJavaExports.Initialize(resolve)
}

func equalStringSlices(l, r []string) bool {
if len(l) != len(r) {
return false
Expand Down
38 changes: 37 additions & 1 deletion java/gazelle/lang.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"context"
"os"

"github.com/bazel-contrib/rules_jvm/java/gazelle/javaconfig"
"github.com/bazel-contrib/rules_jvm/java/gazelle/private/java"
"github.com/bazel-contrib/rules_jvm/java/gazelle/private/java_export_index"
"github.com/bazel-contrib/rules_jvm/java/gazelle/private/javaparser"
"github.com/bazel-contrib/rules_jvm/java/gazelle/private/logconfig"
"github.com/bazel-contrib/rules_jvm/java/gazelle/private/maven"
Expand All @@ -31,6 +33,9 @@ type javaLang struct {
// Key is the path to the java package from the Bazel workspace root.
javaPackageCache map[string]*java.Package

// javaExportIndex holds information about java_export targets and which symbols they make available.
javaExportIndex *java_export_index.JavaExportIndex

// hasHadErrors triggers the extension to fail at destroy time.
//
// this is used to return != 0 when some errors during the generation were
Expand Down Expand Up @@ -64,6 +69,7 @@ func NewLanguage() language.Language {
logger: logger,
javaLogLevel: javaLevel,
javaPackageCache: make(map[string]*java.Package),
javaExportIndex: java_export_index.NewJavaExportIndex(languageName, logger),
}

l.logger = l.logger.Hook(shutdownServerOnFatalLogHook{
Expand Down Expand Up @@ -112,11 +118,25 @@ var javaLibraryKind = rule.KindInfo{
},
}

var javaExportKind = rule.KindInfo{
NonEmptyAttrs: map[string]bool{
"deps": true,
"exports": true,
"runtime_deps": true,
},
ResolveAttrs: map[string]bool{
"deps": true,
"exports": true,
"runtime_deps": true,
},
}

func (l javaLang) Kinds() map[string]rule.KindInfo {
kinds := map[string]rule.KindInfo{
"java_binary": kindWithRuntimeDeps,
"java_junit5_test": kindWithRuntimeDeps,
"java_library": javaLibraryKind,
"java_export": javaExportKind,
"java_test": kindWithRuntimeDeps,
"java_test_suite": kindWithRuntimeDeps,
"java_proto_library": kindWithoutRuntimeDeps,
Expand Down Expand Up @@ -152,6 +172,7 @@ var baseJavaLoads = []rule.LoadInfo{
Symbols: []string{
"java_junit5_test",
"java_test_suite",
"java_export",
},
},
}
Expand Down Expand Up @@ -183,10 +204,25 @@ func (l javaLang) Loads() []rule.LoadInfo {
return loads
}

func (l javaLang) Fix(c *config.Config, f *rule.File) {}
func (l javaLang) Fix(c *config.Config, f *rule.File) {

// We can't put this code in `GenerateRule`, because it doesn't parse the BUILD file at that point,
// so we can't identify the `java_export`s already in the file.
// And we can't do it at `Imports()` time, because we need to hook into `DoneGeneratingRules`
// to know when to populate l.javaExportIndex.
packageConfig := c.Exts[languageName].(javaconfig.Configs)[f.Pkg]
if packageConfig != nil && packageConfig.ResolveToJavaExports() {
for _, r := range f.Rules {
if r.Kind() == "java_export" {
l.javaExportIndex.RecordJavaExport(c.RepoName, r, f)
}
}
}
}

func (l javaLang) DoneGeneratingRules() {
l.parser.ServerManager().Shutdown()
l.javaExportIndex.FinalizeIndex()
}

func (l javaLang) AfterResolvingDeps(_ context.Context) {
Expand Down
2 changes: 1 addition & 1 deletion java/gazelle/private/java/java.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/bazel-contrib/rules_jvm/java/gazelle/private/types"
)

// IsTestPackage tries to detect if the directory would contain test files of not.
// IsTestPackage tries to detect if the directory would contain test files or not.
// It assumes dir is a forward-slashed package name, not a possibly-back-slashed filepath.
func IsTestPackage(pkg string) bool {
if strings.HasPrefix(pkg, "javatests/") {
Expand Down
15 changes: 15 additions & 0 deletions java/gazelle/private/java_export_index/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
load("@rules_go//go:def.bzl", "go_library")

go_library(
name = "java_export_index",
srcs = ["java_export_index.go"],
importpath = "github.com/bazel-contrib/rules_jvm/java/gazelle/private/java_export_index",
visibility = ["//visibility:public"],
deps = [
"//java/gazelle/private/sorted_set",
"//java/gazelle/private/types",
"@bazel_gazelle//label",
"@bazel_gazelle//rule",
"@com_github_rs_zerolog//:zerolog",
],
)
Loading
Loading