Skip to content

Commit

Permalink
Merge pull request aspect-build#1 from gonzojive/apparent-loads
Browse files Browse the repository at this point in the history
feat(gazelle/kotlin): Implement language.ModuleAwareLanguage.
  • Loading branch information
gonzojive authored Oct 6, 2024
2 parents c33d433 + 4937a11 commit 2873f18
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 26 deletions.
10 changes: 5 additions & 5 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ bazel_dep(name = "platforms", version = "0.0.10")
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
use_repo(go_deps, "com_github_bazel_contrib_rules_jvm", "com_github_bazelbuild_buildtools", "com_github_emirpasic_gods", "com_github_go_git_go_git_v5", "com_github_google_go_cmp", "com_github_rs_zerolog", "com_github_smacker_go_tree_sitter", "com_github_yargevad_filepathx", "net_starlark_go")
go_deps.archive_override(
path = "github.com/bazel-contrib/rules_jvm",
sha256 = "855a528d5231aa6cfa2d5bd7a2343f601a468f7af9671eecb091b0c540ce034c",
urls = ["http://localhost:8674/by-sha256/855a528d5231aa6cfa2d5bd7a2343f601a468f7af9671eecb091b0c540ce034c.zip"],
)
# go_deps.archive_override(
# path = "github.com/bazel-contrib/rules_jvm",
# sha256 = "855a528d5231aa6cfa2d5bd7a2343f601a468f7af9671eecb091b0c540ce034c",
# urls = ["http://localhost:8674/by-sha256/855a528d5231aa6cfa2d5bd7a2343f601a468f7af9671eecb091b0c540ce034c.zip"],
# )

go_sdk = use_extension("@io_bazel_rules_go//go:extensions.bzl", "go_sdk")

Expand Down
61 changes: 59 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,60 @@
A Kotlin gazelle plugin, etracted from https://github.com/aspect-build/aspect-cli
A Kotlin plugin for [bazel's gazelle
tool](https://github.com/bazelbuild/bazel-gazelle/tree/master) for automatically
generating BUILD.bazel files from Kotlin source code.

This repository is only for debugging. It is not intended for usage.
### Fork status
This code was extracted from https://github.com/aspect-build/aspect-cli. I would
like to merge it upstream depending on how the conversation goes with the
original authors. See https://github.com/aspect-build/aspect-cli/issues/750 for
discussion.


# Usage


# Dependency resolution

Gazelle must resolve what Bazel labels (like `@foo//bar/baz`) a Kotlin file depends on.
The rules followed by this plugin are as follows:

1. The dependencies of a Kotlin file are determined from the explicit imports in
the file. Fully-qualified identifiers present elsewhere in the file are not
considered when determining dependencies.

2. The resolution algorithm (as implemented in `gazelle/kotlin/resolver.go`)

3. An identifier matches a [Maven
artifact](https://maven.apache.org/repositories/artifacts.html) if one of the
packages declared as an export of that artifact is a package prefix

4. If the import to be resolved is in the library index, the import will be resolved to that library. If `-index=true`, Gazelle builds an index of library rules in the current repository before starting dependency resolution, and this is how most dependencies are resolved.

1. For Kotlin, the match is based on the importpath attribute.

2. For proto, the match is based on the srcs attribute.


# Terminology

**identifier** is used to refer to what the Kotlin spec calls an
[identifier](https://kotlinlang.org/spec/syntax-and-grammar.html#grammar-rule-identifier).
This is a fully-qualified or partially-qualified name. Fully-qualified
identifiers are used in `package` and `import` statements in Kotlin files.

**parent identifier**: In this document and in the code base, "parent identifier" means
an identifier with the last dot-delimited component removed. Sometimes it may be loosely
used to refer to all the ancestor identifiers as well (parent, parent of parent, etc.).

# Directives

Many [gazelle directives](https://github.com/bazelbuild/bazel-gazelle#directives) are generic
and will apply the the behavior of the Kotlin plugin.

The Kotlin plugin has additional directives for configuring behavior:

## gazelle:java_maven_install_file

Specifies where the `maven_install.json` file is located.

This directive is defined by the [rules_jvm gazelle plugin](), and the Kotlin
plugins hares logic with the Java plugin to parse it.
14 changes: 11 additions & 3 deletions gazelle/kotlin/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
load("@gazelle//:def.bzl", "gazelle_binary")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
load("//gazelle:gazelle.bzl", "gazelle_generation_test")
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@bazel_skylib//lib:sets.bzl", "sets")

# Exclude all test data
# gazelle:exclude tests/
Expand Down Expand Up @@ -49,15 +51,21 @@ gazelle_binary(
# A separate generation test for each tests/* test case
[
gazelle_generation_test(
name = "%s_test" % test_workspace.replace("/WORKSPACE", "").replace("tests/", ""),
name = "%s_test" % test_dir.replace("tests/", ""),
size = "small",
dir = test_workspace.replace("/WORKSPACE", ""),
dir = test_dir,
env = {
"ASPECT_CLI_LOG_DEBUG": "TRACE",
},
gazelle_binary = "gazelle_kotlin_binary",
)
for test_workspace in glob(["tests/**/WORKSPACE"])
for test_dir in sets.to_list(sets.make([
paths.dirname(p)
for p in glob([
"tests/**/WORKSPACE",
"tests/**/MODULE.bazel",
])
]))
]

go_test(
Expand Down
67 changes: 51 additions & 16 deletions gazelle/kotlin/language.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package gazelle

import (
"fmt"

jvm_maven "github.com/bazel-contrib/rules_jvm/java/gazelle/private/maven"
"github.com/bazelbuild/bazel-gazelle/config"
"github.com/bazelbuild/bazel-gazelle/language"
Expand All @@ -11,14 +13,24 @@ import (
const LanguageName = "kotlin"

const (
KtJvmLibrary = "kt_jvm_library"
KtJvmBinary = "kt_jvm_binary"
RulesKotlinRepositoryName = "io_bazel_rules_kotlin"
KtJvmLibrary = "kt_jvm_library"
KtJvmBinary = "kt_jvm_binary"
// rulesKotlinWorkspaceBasedRepositoryName is the canonical repository name of the
// rules_kotlin repository for WORKSPACE-based projects.
rulesKotlinWorkspaceBasedRepositoryName = "io_bazel_rules_kotlin"

// rulesKotlinModuleName is the name of [rules_kotlin bzlmod module].
//
// [rules_kotlin bzlmod module]: https://registry.bazel.build/modules/rules_kotlin.
rulesKotlinModuleName = "rules_kotlin"
)

var sourceRuleKinds = treeset.NewWithStringComparator(KtJvmLibrary)

var _ language.Language = (*kotlinLang)(nil)
var (
_ language.Language = (*kotlinLang)(nil)
_ language.ModuleAwareLanguage = (*kotlinLang)(nil)
)

// The Gazelle extension for TypeScript rules.
// TypeScript satisfies the language.Language interface including the
Expand Down Expand Up @@ -62,22 +74,45 @@ var kotlinKinds = map[string]rule.KindInfo{
},
}

var kotlinLoads = []rule.LoadInfo{
{
Name: "@" + RulesKotlinRepositoryName + "//kotlin:jvm.bzl",
Symbols: []string{
KtJvmLibrary,
KtJvmBinary,
},
},
}

func (*kotlinLang) Kinds() map[string]rule.KindInfo {
return kotlinKinds
}

func (*kotlinLang) Loads() []rule.LoadInfo {
return kotlinLoads
func (l *kotlinLang) Loads() []rule.LoadInfo {
return l.ApparentLoads(func(moduleName string) string {
switch moduleName {
case rulesKotlinModuleName:
return rulesKotlinWorkspaceBasedRepositoryName
default:
panic(fmt.Errorf("unexpected module name %q", moduleName))
}
})
}

// ApparentLoads implements [language.ModuleAwareLanguage].
func (kt *kotlinLang) ApparentLoads(moduleToApparentName func(string) string) []rule.LoadInfo {
// Note from [language.ModuleAwareLanguage]:
//
// The moduleToApparentName argument is a function that resolves a given
// Bazel module name to the apparent repository name configured for this
// module in the MODULE.bazel file, or the empty string if there is no such
// module or the MODULE.bazel file doesn't exist. Languages should use the
// non-empty value returned by this function to form the repository part of
// the load statements they return and fall back to using the legacy
// WORKSPACE name otherwise.
rulesKotlinRepo := moduleToApparentName(rulesKotlinModuleName)
if rulesKotlinRepo == "" {
rulesKotlinRepo = rulesKotlinWorkspaceBasedRepositoryName
}
return []rule.LoadInfo{
{
Name: "@" + rulesKotlinRepo + "//kotlin:jvm.bzl",
Symbols: []string{
KtJvmLibrary,
KtJvmBinary,
},
},
}
}

func (*kotlinLang) Fix(c *config.Config, f *rule.File) {}
Empty file.
9 changes: 9 additions & 0 deletions gazelle/kotlin/tests/simple_file_bzlmod/BUILD.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")

kt_jvm_library(
name = "root",
srcs = [
"lib.kt",
"libs.kts",
],
)
21 changes: 21 additions & 0 deletions gazelle/kotlin/tests/simple_file_bzlmod/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module(
name = "simple",
version = "0.0.1",
)

bazel_dep(name = "rules_kotlin", version = "1.9.6")
bazel_dep(name = "rules_jvm_external", version = "6.4")

maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")

maven.install(
artifacts = [
"com.google.truth:truth:1.1.2",
"junit:junit:4.13.2",
],
lock_file = "//:maven_install.json",
repositories = [
"https://repo1.maven.org/maven2",
],
)
use_repo(maven, "maven")
Empty file.
5 changes: 5 additions & 0 deletions gazelle/kotlin/tests/simple_file_bzlmod/lib.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Hello World Program

fun hello() {
println("Hello world!")
}
5 changes: 5 additions & 0 deletions gazelle/kotlin/tests/simple_file_bzlmod/libs.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Hello World Program

fun hello() {
println("Hello world!")
}

0 comments on commit 2873f18

Please sign in to comment.