{"id":18244256,"url":"https://github.com/stackb/scala-gazelle","last_synced_at":"2025-10-16T11:33:49.232Z","repository":{"id":40346377,"uuid":"458001546","full_name":"stackb/scala-gazelle","owner":"stackb","description":"Experimental gazelle extension for scala","archived":false,"fork":false,"pushed_at":"2025-03-30T19:01:07.000Z","size":3592,"stargazers_count":12,"open_issues_count":7,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-30T20:18:54.405Z","etag":null,"topics":["bazel","gazelle","scala"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stackb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-11T01:10:53.000Z","updated_at":"2025-03-25T08:19:21.000Z","dependencies_parsed_at":"2023-12-18T03:28:39.785Z","dependency_job_id":"7f033642-b514-4316-ad16-56957eaa39c8","html_url":"https://github.com/stackb/scala-gazelle","commit_stats":{"total_commits":247,"total_committers":2,"mean_commits":123.5,"dds":0.2672064777327935,"last_synced_commit":"2e562eae2bcbf18f26f3d3ca2414e7d1970ed175"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackb%2Fscala-gazelle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackb%2Fscala-gazelle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackb%2Fscala-gazelle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackb%2Fscala-gazelle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stackb","download_url":"https://codeload.github.com/stackb/scala-gazelle/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247184975,"owners_count":20897867,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bazel","gazelle","scala"],"created_at":"2024-11-05T09:15:55.490Z","updated_at":"2025-10-16T11:33:49.209Z","avatar_url":"https://github.com/stackb.png","language":"Go","readme":"\n[![CI](https://github.com/stackb/scala-gazelle/actions/workflows/ci.yaml/badge.svg)](https://github.com/stackb/scala-gazelle/actions/workflows/ci.yaml) [![Go Reference](https://pkg.go.dev/badge/github.com/stackb/scala-gazelle.svg)](https://pkg.go.dev/github.com/stackb/scala-gazelle)\n\n\u003ctable border=\"0\"\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cimg src=\"https://upload.wikimedia.org/wikipedia/en/thumb/7/7d/Bazel_logo.svg/1920px-Bazel_logo.svg.png\" height=\"120\"/\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"https://www.scala-lang.org/resources/img/frontpage/scala-spiral.png\" width=\"100\" height=\"120\"/\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"https://user-images.githubusercontent.com/50580/141892423-5205bbfd-8487-442b-81c7-f56fa3d1f69e.jpeg\" width=\"130\" height=\"120\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003ebazel\u003c/td\u003e\n    \u003ctd\u003escala\u003c/td\u003e\n    \u003ctd\u003egazelle\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n- [Overview](#overview)\n- [Installation](#installation)\n  - [Primary Dependency](#primary-dependency)\n  - [Transitive Dependencies](#transitive-dependencies)\n  - [Patch bazel-gazelle](#patch-bazel-gazelle)\n  - [Gazelle Binary](#gazelle-binary)\n  - [Gazelle Rule](#gazelle-rule)\n- [Usage](#usage)\n- [Configuration](#configuration)\n  - [Rule Providers](#rule-providers)\n    - [Built-in Existing Rule Providers](#built-in-existing-rule-providers)\n    - [Custom Existing Rule Providers](#custom-existing-rule-providers)\n    - [Custom Rule Provider](#custom-rule-provider)\n  - [Symbol Providers](#symbol-providers)\n    - [`source`](#source)\n    - [`maven`](#maven)\n    - [`java`](#java)\n    - [`protobuf`](#protobuf)\n    - [Custom Symbol Provider](#custom-symbol-provider)\n    - [CanProvide](#canprovide)\n    - [Split Packages](#split-packages)\n  - [Conflict Resolution](#conflict-resolution)\n    - [Conflict Resolvers](#conflict-resolvers)\n    - [`scala_proto_package` conflict resolver](#scala_proto_package-conflict-resolver)\n    - [`predefined_label` conflict resolver](#predefined_label-conflict-resolver)\n    - [Custom conflict resolvers](#custom-conflict-resolvers)\n    - [Dependency List Cleanup](#dependency-list-cleanup)\n  - [Cache](#cache)\n  - [Profiling](#profiling)\n    - [CPU](#cpu)\n    - [Memory](#memory)\n  - [Directives](#directives)\n    - [`gazelle:scala_rule`](#gazellescala_rule)\n    - [`gazelle:resolve`](#gazelleresolve)\n    - [`gazelle:resolve_with`](#gazelleresolve_with)\n    - [`gazelle:resolve_kind_rewrite_name`](#gazelleresolve_kind_rewrite_name)\n    - [`gazelle:resolve_file_symbol_name`](#gazelleresolve_file_symbol_name)\n    - [`gazelle:scala_debug`](#gazellescala_debug)\n      - [`imports`](#imports)\n      - [`exports`](#exports)\n      - [`deps`](#deps)\n- [Import Resolution Procedure](#import-resolution-procedure)\n  - [How Required Imports are Calculated](#how-required-imports-are-calculated)\n    - [Rule](#rule)\n    - [File](#file)\n      - [Parsing](#parsing)\n      - [Name resolution](#name-resolution)\n  - [How Required Imports are Resolved](#how-required-imports-are-resolved)\n- [Help](#help)\n\n# Overview\n\nThis is an experimental gazelle extension for scala.  It has the following\ndesign characteristics:\n\n- It only works on scala rules that already exist in a `BUILD` file.  You are\n  responsible for manually creating `scala_library`, `scala_binary`, and\n  `scala_test` targets in their respective packages.\n- It only manages compile-time scala `deps`; you are responsible for\n  `runtime_deps`.\n- Existing scala rules are evaluated for the contents of their `srcs`. Globs are\n  interpreted the same as bazel starlark (unless there is a a bug 😱).\n- Source files named in the `srcs` are parsed for their import statements and\n  exportable symbols (classes, traits, objects, ...).\n- Dependencies are resolved by matching required imports against their providing\n  rule labels.  The resolution procedure is configurable.\n\n# Installation\n\n## Primary Dependency\n\nAdd `build_stack_scala_gazelle` as an external workspace:\n\n```bazel\n# Branch: master\n# Commit: cd4ba132018c2ac709bfda4560da394da2544490\n# Date: 2022-12-15 22:11:08 +0000 UTC\n# URL: https://github.com/stackb/scala-gazelle/commit/cd4ba132018c2ac709bfda4560da394da2544490\n# \n# Refactor MemoParser (#69)\n# \n# * Refactor MemoParser\n# * regen mocks\n# * Fix cache read/write\n# Size: 150152 (150 kB)\nhttp_archive(\n    name = \"build_stack_scala_gazelle\",\n    sha256 = \"a88095f943b5b382761efe300b098ae438a0083db844bea98efbcfaee6efa8bf\",\n    strip_prefix = \"scala-gazelle-cd4ba132018c2ac709bfda4560da394da2544490\",\n    urls = [\"https://github.com/stackb/scala-gazelle/archive/cd4ba132018c2ac709bfda4560da394da2544490.tar.gz\"],\n)\n```\n\n\u003e Update to latest, the version in the readme is probably out-of-date!\n\n## Transitive Dependencies\n\nDeclare transitive dependencies in your `WORKSPACE` as follows:\n\n```bazel\nload(\"@build_stack_scala_gazelle//:workspace_deps.bzl\", \"language_scala_deps\")\n\nlanguage_scala_deps()\n\nload(\"@build_stack_scala_gazelle//:go_repos.bzl\", build_stack_scala_gazelle_gazelle_extension_deps = \"gazelle_extension_deps\")\n\nbuild_stack_scala_gazelle_gazelle_extension_deps()\n```\n\n## Patch bazel-gazelle\n\nAt the time of this writing, scala-gazelle uses a feature from https://github.com/bazelbuild/bazel-gazelle/pull/1394.  To patch:\n\n```bazel\nhttp_archive(\n    name = \"bazel_gazelle\",\n    patch_args = [\"-p1\"],\n    patches = [\"@build_stack_scala_gazelle//third_party/bazelbuild/bazel-gazelle:pr-1394.patch\"],\n    sha256 = \"5ebc984c7be67a317175a9527ea1fb027c67f0b57bb0c990bac348186195f1ba\",\n    strip_prefix = \"bazel-gazelle-2d1002926dd160e4c787c1b7ecc60fb7d39b97dc\",\n    urls = [\"https://github.com/bazelbuild/bazel-gazelle/archive/2d1002926dd160e4c787c1b7ecc60fb7d39b97dc.tar.gz\"],\n)\n```\n\n## Gazelle Binary\n\nInclude the language/scala extension in your `gazelle_binary` rule.  For\nexample:\n\n```bazel\ngazelle_binary(\n    name = \"gazelle-scala\",\n    languages = [\n        \"@bazel_gazelle//language/proto:go_default_library\",\n        \"@bazel_gazelle//language/go:go_default_library\",\n        \"@build_stack_rules_proto//language/protobuf\",\n        \"@build_stack_scala_gazelle//language/scala\",\n    ],\n)\n```\n\n## Gazelle Rule\n\nReference the binary in the gazelle rule:\n\n```bazel\ngazelle(\n    name = \"gazelle\",\n    args = [...],\n    gazelle = \":gazelle-scala\",\n)\n```\n\nThe `args` and `data` are discussed below. \n\n# Usage\n\nInvoke gazelle as per typical usage:\n\n```sh\n$ bazel run //:gazelle\n```\n\n# Configuration\n\n## Rule Providers\n\nThe extension needs to know which rules it should manage (parse `srcs`/resolve\n`deps`).  This is done using `gazelle:scala_rule` directives.\n\n### Built-in Existing Rule Providers\n\nA preset catalog of providers are available out-of-the-box:\n\n- `@io_bazel_rules_scala//scala:scala.bzl%scala_binary`\n- `@io_bazel_rules_scala//scala:scala.bzl%scala_library`\n- `@io_bazel_rules_scala//scala:scala.bzl%scala_macro_library`\n- `@io_bazel_rules_scala//scala:scala.bzl%scala_test`\n\nTo enable a provider, instantiate a \"rule provider config\":\n\n```bazel\n# gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library\n```\n\n\u003e This reads as \"create a rule provider configuration named 'scala_library'\n\u003e whose provider implementation is registered under the name\n\u003e '@io_bazel_rules_scala//scala:scala.bzl%scala_library'\n\n### Custom Existing Rule Providers\n\nYou may have your own scala rule macros that look like a `scala_library` or\n`scala_binary`, but have their own rule kinds and loads.  To register these\nrules/macros as provider implementations, use the\n`-existing_scala_{type}_rule=LOAD%KIND` flag (where type is one of `binary|library|test`).  For example:\n\n```bazel\ngazelle(\n    name = \"gazelle\",\n    args = [\n        \"-existing_scala_library_rule=//bazel_tools:scala.bzl%scala_app\",\n        ...\n    ],\n    ...\n)\n```\n\n```bazel\n# gazelle:scala_rule scala_app implementation //bazel_tools:scala.bzl%scala_app\n```\n\n### Custom Rule Provider\n\nAn advanced use-case would involve writing your own `scalarule.Provider`\nimplementation.\n\nTo register it:\n\n```go\nimport \"github.com/stackb/scala-gazelle/pkg/scalarule\"\n\nfunc init() {\n  scalarule.GlobalProviderRegistry().RegisterProvider(\n    \"@foo//rules/scala.bzl:my_scala_library\",\n    newMyScalaLibrary(),\n  )\n}\n```\n\nEnable the rule provider configuration:\n\n```bazel\n# gazelle:scala_rule my_scala_library implementation @foo//rules/scala.bzl:my_scala_library\n```\n\n## Symbol Providers\n\nAt the core of the import resolution process is a trie structure where the keys\nof the trie are parts of an import statement and the values are\n`*resolver.Symbol` structs.\n\nFor example, for the import `io.grpc.Status`, the trie would contain the\nfollowing:\n\n- `io`: (`nil`)\n  - `grpc` (type `PACKAGE`, from `@maven//:io_grpc_grpc_core`)\n    - `Status` (type `CLASS`, from `@maven//:io_grpc_grpc_core`)\n\nWhen resolving the import `io.grpc.Status.ALREADY_EXISTS`, the longest prefix\nmatch would find the symbol `io.grpc.Status` `CLASS` and the label\n`@maven//:io_grpc_grpc_core` would be added to the rule `deps`.\n\nThe trie is populated by `resolver.SymbolProvider` implementations. Each\nimplementation provides symbols from a different data source.\n\nA symbol provider:\n\n- Have a canonical name.\n- Must be enabled with the `-scala_symbol_provider` flag.\n- Manage its own flags; check the provider source code for complete details.\n\n### `source`\n\nThe `source` provider is responsible for indexing importable symbols from\n`.scala` source files during the rule generation phase.\n\nSource files that are listed in the `srcs` of existing scala rules are parsed.\nThe discovered `object`, `class`, `trait` types are provided to the symbol trie\nsuch that they can be resolved by other rules.\n\nThe extension wouldn't do much without this provider, but it still needs to be\nenabled in `args`:\n\n```bazel\ngazelle(\n    name = \"gazelle\",\n    args = [\n        \"-scala_symbol_provider=source\",\n    ],\n)\n```\n\n### `maven`\n\nThis provider reads `maven_install.json` files that are produced from pinned\n`maven_install` repository rules.\n\nAs of https://github.com/bazelbuild/rules_jvm_external/pull/716 (`Add index of\npackages in jar files when pinning`), `@rules_jvm_external` indexes the package\nnames that jars provide.\n\nThe `maven` provider reads these package names and populates the trie\naccordingly.  Note that since only package names are known, maven dependency\nresolution via this mechanism alone is _coarse-grained_.\n\nTo configure the `maven` provider, use the `-maven_install_json_file` flag (can\nbe repeated if you have more than one `maven_install` rule):\n\n```bazel\ngazelle(\n    name = \"gazelle\",\n    args = [\n        \"-scala_symbol_provider=source\",\n        \"-scala_symbol_provider=maven\",\n        \"-maven_install_json_file=$(location //:maven_install.json)\",\n        \"-maven_install_json_file=$(location //:artifactory_install.json)\",\n    ],\n    data = [\n        \"//:maven_install.json\",\n        \"//:artifactory_install.json\",\n    ],\n)\n```\n\n### `java`\n\nThe `java` provider indexes symbols from java-related dependencies in the bazel\ngraph.  It relies on an index file produced by the `java_index` rule:\n\n```bazel\nload(\"@build_stack_scala_gazelle//rules:java_index.bzl\", \"java_index\")\n\njava_index(\n    name = \"java_index\",\n    deps = [\n        \"@maven//:io_grpc_grpc_context\",\n        \"@maven//:io_grpc_grpc_core\",\n    ],\n    out_json = \"java_index.json\",\n    out_proto = \"java_index.pb\",\n    platform_deps = [\"@bazel_tools//tools/jdk:platformclasspath\"],\n)\n```\n\n\u003e NOTE: Use `bazel build //:java_index --output_groups=json` to produce the JSON\n\u003e file if you want to inspect it.\n\nThe `deps` attribute names dependencies that you want indexed at a\n_fine-grained_ level.  Any label that provides `JavaInfo` will satisfy.\n\nThe `platform_deps` attribute is special: it indexes jars that are provided by\nthe platform and do not need to be resolved to a label in rule `deps`.  For\nexample, if you import `java.util.Map`, no additional bazel label is required to\nuse it. The `@bazel_tools//tools/jdk:platformclasspath` is the bazel rule that\nsupplies these symbols.  You can also add things like\n`@maven//:org_scala_lang_scala_library` or other toolchain-provided jars that\nnever need to be explicitly stated in scala rule `deps`.\n\nTo enable it:\n\n```bazel\ngazelle(\n    name = \"gazelle\",\n    args = [\n        \"-scala_symbol_provider=source\",\n        \"-scala_symbol_provider=java\",\n        \"-java_index_file=$(location //:java_index.pb)\",\n        # the flag order is significant: put fine-grained providers (java)\n        # before coarse-grained ones (maven)\n        \"-scala_symbol_provider=maven\",\n        ...\n    ],\n    data = [\n        \"//:java_index.pb\",\n    ],\n)\n```\n\n### `protobuf`\n\nThe `protobuf` providers works in conjuction with the\n[stackb/rules_proto](https://github.com/stackb/rules_proto) gazelle extension.\n\nThat extension parses proto files and supplies scala imports for proto\n`message`, `enum`, and `service` classes.\n\nTo resolve scala dependencies to protobuf rules, enable as follows:\n\n```bazel\ngazelle(\n    name = \"gazelle\",\n    args = [\n        \"-scala_symbol_provider=source\",\n        \"-scala_symbol_provider=protobuf\",\n        ...\n    ],\n)\n```\n\n\u003e TODO: provide an example repo showing the full configuration of these two\n\u003e extensions.\n\n### Custom Symbol Provider\n\nIf your organization has an additional database or mechanism for import\ntracking, you can implement the `resolver.SymbolProvider` interface and\nregister it with the global registry.\n\nFor example, if your organization uses https://github.com/johnynek/bazel-deps,\nyou might implement something like:\n\n```go\npackage provider\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\n\t\"github.com/bazelbuild/bazel-gazelle/config\"\n\t\"github.com/bazelbuild/bazel-gazelle/label\"\n\t\"github.com/bazelbuild/bazel-gazelle/rule\"\n\n\t\"github.com/stackb/scala-gazelle/pkg/collections\"\n\t\"github.com/stackb/scala-gazelle/pkg/resolver\"\n)\n\nfunc init() {\n  resolver.\n    GlobalSymbolProviderRegistry().\n    AddSymbolProvider(newBazelDepsProvider())\n}\n\n// bazelDepsProvider is a provider of symbols for the\n// johnynek/bazel-deps.\ntype bazelDepsProvider struct {\n\tbazelDepsYAMLFiles collections.StringSlice\n}\n\n// newBazelDepsProvider constructs a new provider.\nfunc newBazelDepsProvider() *bazelDepsProvider {\n\treturn \u0026bazelDepsProvider{}\n}\n\n// Name implements part of the resolver.SymbolProvider interface.\nfunc (p *bazelDepsProvider) Name() string {\n\treturn \"bazel-deps\"\n}\n\n// RegisterFlags implements part of the resolver.SymbolProvider interface.\nfunc (p *bazelDepsProvider) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {\n\tfs.Var(\u0026p.bazelDepsYAMLFiles, \"bazel_deps_yaml_file\", \"path to bazel_deps.yaml\")\n}\n\n// CheckFlags implements part of the resolver.SymbolProvider interface.\nfunc (p *bazelDepsProvider) CheckFlags(fs *flag.FlagSet, c *config.Config, scope resolver.Scope) error {\n\tfor _, filename := range p.bazelDepsYAMLFiles {\n\t\tif err := p.loadFile(c.WorkDir, filename, scope); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p *bazelDepsProvider) loadFile(dir string, filename string, scope resolver.Scope) error {\n\treturn fmt.Errorf(\"Implement me; Supply symbols to the given scope!\")\n}\n\n// CanProvide implements part of the resolver.SymbolProvider interface.\nfunc (p *bazelDepsProvider) CanProvide(dep *resolver.ImportLabel, knownRule func(from label.Label) (*rule.Rule, bool), from label.Label) bool {\n\tif dep.Label.Repo == \"bazel_deps\" {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// OnResolve implements part of the resolver.SymbolProvider interface.\nfunc (p *bazelDepsProvider) OnResolve() error {\n  return nil\n}\n\n// OnEnd implements part of the resolver.SymbolProvider interface.\nfunc (p *bazelDepsProvider) OnEnd() error {\n  return nil\n}\n```\n\n### CanProvide\n\nThe `resolver.Scope.CanProvide` function is used to determine if this provider\nis capable of providing a given dependency label.  When rule deps are resolved,\nthe existing deps list is cleared of those labels it can find a provider for.\nFor example, given the rule:\n\n```bazel\nscala_library(\n  name = \"lib\",\n  srcs = glob([\"*.scala\"]),\n  deps = [\n    \"//src/main/scala:scala\",\n    \"@foo//:scala\",\n    \"@maven//:com_google_gson_gson\",\n  ],\n)\n```\n\nThe configured providers are checked to see which labels can be re-resolved. So,\nthe intermediate state of the rule before deps resolution actually happens looks\nlike:\n\n```diff\nscala_library(\n  name = \"lib\",\n  srcs = glob([\"*.scala\"]),\n  deps = [\n-    \"//src/main/scala:scala\",  # can be resolved to a source rule - delete it!\n    \"@foo//:scala\",  # don't know anything about @foo - leave it alone!\n-    \"@maven//:com_google_gson_gson\",  # can be resolved by maven provider - delete it!\n  ],\n)\n```\n\nSo, if the scala-gazelle extension is not confident that a label can be\nre-resolved, it will leave the dependency alone, even without `# keep`\ndirectives.\n\n### Split Packages\n\nIssues can occur when more than one jar provides the same package name.  This\nsituation is known as a \"split package\".  The `io.grpc` namespace is a classic\nexample (see [discussion](https://github.com/grpc/grpc-java/issues/3522)).  The\n`io.grpc.Context` is in `@maven//:io_grpc_grpc_context`, but other classes like\n`io.grpc.Status` are in `@maven//:io_grpc_grpc_core`.  Both advertise the\npackage `io.grpc`.\n\nTo help avoid issues with split packages:\n\n- Use the `java` provider to supply fine-grained deps for selected artifacts.\n- Avoid wildcard imports that involve split packages.\n\n## Conflict Resolution\n\nWhen the symbol trie is populated from the enabled symbol providers, conflicts\ncan arise if the same symbol is put more than once under the same name.\n\nRather than ignoring the duplicate, additional symbols are stored on the `*resolver.Symbol.Conflicts` slice, which has this signature:\n\n```go\n// Symbol associates a name with the label that provides it, along with a type\n// classifier that says what kind of symbol it is.\ntype Symbol struct {\n\t// Type is the kind of symbol this is.\n\tType sppb.ImportType\n\t// Name is the fully-qualified import name.\n\tName string\n\t// Label is the bazel label where the symbol is provided from.\n\tLabel label.Label\n\t// Provider is the name of the provider that supplied the symbol.\n\tProvider string\n\t// Conflicts is a list of symbols provided by another provider or label.\n\tConflicts []*Symbol\n\t// Requires is a list of other symbols that are required by this one.\n\tRequires []*Symbol\n}\n```\n\nIf an import resolves to a symbol that carries a conflict, a warning is emitted.  Example:\n\n```\nUnresolved symbol conflict: CLASS \"com.google.protobuf.Empty\" has multiple providers!\n - Maybe add one of the following to //common/akka/grpc:BUILD.bazel:\n     # gazelle:resolve scala scala com.google.protobuf.Empty @protobufapis//google/protobuf:empty_proto_scala_library:\n     # gazelle:resolve scala scala com.google.protobuf.Empty @maven//:com_google_protobuf_protobuf_java:\n```\n\nAs the warning suggests, one way to suppress the warning is to add a `gazelle:resolve` directive indicating which rule should be chosen.\n\n### Conflict Resolvers\n\nAnother way to resolve the conflict is to use a `resolver.ConflictResolver` implementation, which has this signature:\n\n```go\n// ConflictResolver implementations are capable of applying a conflict\n// resolution strategy for conflicting resolved import symbols.\ntype ConflictResolver interface {  \n\t// ResolveConflict takes the context rule and imports, and the target symbol\n\t// with conflicts to resolve.\n\tResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol) (*Symbol, bool)\n}\n```\n\n### `scala_proto_package` conflict resolver\n\nAnother example:\n\n```\nUnresolved symbol conflict: PROTO_PACKAGE \"examples.helloworld.greeter.proto\" has multiple providers!\n - Maybe remove a wildcard import (if one exists)\n - Maybe add one of the following to @unity//examples/helloworld/greeter/server/scala:BUILD.bazel:\n     # gazelle:resolve scala scala examples.helloworld.greeter.proto //examples/helloworld/greeter/proto:examples_helloworld_greeter_proto_grpc_scala_library:\n     # gazelle:resolve scala scala examples.helloworld.greeter.proto //examples/helloworld/greeter/proto:examples_helloworld_greeter_proto_proto_scala_library:\n```\n\nIn this case, the conflict occurred because the package\n`examples.helloworld.greeter.proto` was resolved via a wildcard import\n`import examples.helloworld.greeter.proto._`.  Because that package is provided by\ntwo rules (one proto only, one grpc), we need to choose one.\n\nOne way to avoid this conflict is to remove the wildcard import and be explicit about which things are to be imported.\n\nAnother way is implemented by the `scala_proto_package` conflict resolver:\n  - if the rule is using any grpc symbols, choose the `examples_helloworld_greeter_proto_grpc_scala_library`.\n  - if the rule is not using any grpc, take the proto one, since we don't want unnecessary grpc deps when they aren't needed.\n\nTo use it, you need to register it with a flag and enable it with a directive:\n\n```bazel\ngazelle(\n    name = \"gazelle\",\n    args = [\n        \"-scala_conflict_resolver=scala_proto_package\",\n        ...\n    ],\n    ...\n)\n```\n\n```bazel\n# gazelle:resolve_conflicts +scala_proto_package\n```\n\n\u003e The `+` sign is an *intent modifier* and is optional in the positive case.\n\nTo turn off this strategy in a sub-package:\n\n```bazel\n# gazelle:resolve_conflicts -scala_proto_package\n```\n\n### `predefined_label` conflict resolver\n\nThe `predefined_label` conflict resolver prefers symbols that have no origin\nlabel.  For example, consider the following `java_index` rule:\n\n```python\njava_index(\n    name = \"java_index\",\n    out_json = \"java_index.json\",\n    out_proto = \"java_index.pb\",\n    platform_deps = [\n        \"@bazel_tools//tools/jdk:platformclasspath\",\n        \"@maven//:org_scala_lang_scala_library\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n```\n\nWhen the java provider reads this, it loads all the symbols from\n`@maven//:org_scala_lang_scala_library` and sets their label to `label.NoLabel`,\nwhich implies that this dependency `@maven//:org_scala_lang_scala_library` is\nnot needed in rule `deps` since the scala_library is already provided by the\ntoolchain / compiler.\n\nWhen choosing between the following conflict, it will choose the one without the\nlabel, thereby suppressing it in `deps`:\n\n```diff\ngazelle: conflicting symbols \"scala.runtime\":   \u0026resolver.Symbol{\n  \tType:     s\"PACKAGE\",\n  \tName:     \"scala.runtime\",\n- \tLabel:    s\"@maven//:org_scala_lang_scala_library\",\n+ \tLabel:    s\"//:\",\n- \tProvider: \"maven\",\n+ \tProvider: \"java\",\n  \t... // 1 ignored and 1 identical fields\n  }\n```\n\nTo use it, you need to register it with a flag and enable it with a directive:\n\n```bazel\ngazelle(\n    name = \"gazelle\",\n    args = [\n        \"-scala_conflict_resolver=predefined_label\",\n        ...\n    ],\n    ...\n)\n```\n\n```bazel\n# gazelle:resolve_conflicts predefined_label\n```\n\n### Custom conflict resolvers\n\nYou can implement your own conflict resolution strategies by implementing the `resolver.ConflictResolver` interface and registering it with the global registry:\n\n```go\npackage custom\n\nimport \"github.com/stackb/scala-gazelle/pkg/resolver\" \n\nfunc init() {\n  cr := \u0026customConflictResolver{}\n  resolver.GlobalConflictResolverRegistry().PutConflictResolver(cr.Name(), cr)\n}\n\ntype customConflictResolver struct {}\n\n...\n```\n\n### Dependency List Cleanup\n\nIn some cases it is necessary to visit the resolved dependency list for a rule\nand apply custom logic to cleanup labels.  The `resolver.DepsCleaner` interface\nshould be implemented and registered to a global cache, similar to how it is\ndone with \"Custom confict resolvers\".  \n\nTo use a deps cleaner, you'll need to do the following:\n\n- implement the `resolver.DepsCleaner` interface (see code for details).\n- register your implementation in an init function via `resolver.GlobalDepsCleanerRegistry().PutDepsCleaner(name, impl)` using a unique name (e.g. a hypothetical `proto_deps_cleaner`).\n- Make the deps cleaner available in your gazelle rule via arguments `--scala_deps_cleaner=proto_deps_cleaner`.\n- Enable the deps cleaner in a BUILD file via the directive `# gazelle:scala_deps_cleaner proto_deps_cleaner`.\n\n## Cache\n\nParsing scala source files for a large repository is expensive.  A cache can be\nenabled via the `-scala_gazelle_cache_file` flag.  If present, the extension\nwill read and write to this file.\n\n```bazel\ngazelle(\n    name = \"gazelle\",\n    args = [\n        \"-scala_gazelle_cache_file=${BUILD_WORKING_DIRECTORY}/.scala-gazelle-cache.pb\",\n    ],\n)\n```\n\nThe cache stores a sha256 hash of each source file; it will use cached state if\nthe hash matches the source file.\n\n\u003e - Environment variables are expanded.\n\u003e - To use a JSON cache (for example, to inspect it, change the extension to\n\u003e `.json`)\n\u003e - Bonus: the cache also records the total number of packages and enables a\n\u003e nice progress bar.\n\n## Profiling\n\nGazelle can be slow for large repositories.  To get a better sense of what's\ngoing on, cpu and memory profiling can be enabled:\n\n```bazel\ngazelle(\n    name = \"gazelle\",\n    args = [\n        \"-cpuprofile_file=./gazelle.cprof\",\n        \"-memprofile_file=./gazelle.mprof\",\n    ],\n)\n```\n\n### CPU\n\nUse `bazel run @go_sdk//:bin/go -- tool pprof ./gazelle.cprof` to analyze it\n(try the  commands `top10` or `web`).\n\n### Memory\n\nUse `bazel run @go_sdk//:bin/go -- tool mprof ./gazelle.mprof` to analyze it\n(try commands `top10` or `web`)\n\n## Directives\n\nThis extension supports the following directives:\n\n### `gazelle:scala_rule`\n\nInstantiates a named rule provider configuration (enabled by default once\ninstantiated):\n\n```bazel\n# gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library\n```\n\nTo enable/disable the configuration in a subpackage:\n\n```bazel\n# gazelle:scala_rule scala_library enabled false\n# gazelle:scala_rule scala_library enabled true\n```\n\n### `gazelle:resolve`\n\nThis is the core gazelle directive not implemented here but is applicable to\nthis one.\n\nUse something like the following to override dependency resolution to a\nhard-coded label:\n\n```bazel\n# gazelle:resolve scala scala.util @maven//:org_scala_lang_scala_library\n```\n\n### `gazelle:resolve_with`\n\nUse this directive to co-resolve dependencies that, while not explicitly stated\nin the source file, are needed for compilation.  Example:\n\n```bazel\n# gazelle:resolve_with scala com.typesafe.scalalogging.LazyLogging org.slf4j.Logger\n```\n\n\u003e This is referred to as an \"implicit\" dependency internally.\n\nThese are included transitively.\n\n### `gazelle:resolve_kind_rewrite_name`\n\nThe `resolve_kind_rewrite_name` is required for the following scenario:\n\n1. You have a custom existing rule implemented as a macro, for example\n   `my_scala_app`.\n2. The `my_scala_app` macro declares a \"real\" `scala_library` using a name like\n   `%{name}_lib`.\n\nIn this case the extension would parse a `my_scala_app` rule at\n`//src/main/scala/com/foo:scala`; other rules that import symbols from this rule\nwould resolve to `//src/main/scala/com/foo:scala`.  However, there is no such\nactual `scala_library` at `:scala`, it really should be\n`//src/main/scala/com/foo:scala_lib`.\n\nThis can be dealt with as follows:\n\n```bazel\n# gazelle:resolve_kind_rewrite_name my_scala_app %{name}_lib\n```\n\nThis tells the extension _\"if you find a rule with kind `my_scala_app`, rewrite\nthe label name to name + `\"_lib\"`, using the magic token `%{name}` as a\nplaceholder.\"_\n\n### `gazelle:resolve_file_symbol_name`\n\nThis directive can be used to resolve free names listed in a scala file against\nthe current file symbol scope.  To inspect the `names` of a file, take a look at the file parse cache.  For example:\n\n```json\n{\n    \"label\": \"//common/utils/logging/scala\",\n    \"kind\": \"scala_library\",\n    \"files\": [\n        {\n            \"filename\": \"src/LogField.scala\",\n            \"imports\": [\n                \"com.typesafe.scalalogging.LazyLogging\",\n                \"net.logstash.logback.marker.MapEntriesAppendingMarker\",\n                \"net.logstash.logback.marker.ObjectAppendingMarker\",\n                \"scala.jdk.CollectionConverters._\"\n            ],\n            \"packages\": [\n                \"common.utils.logging\"\n            ],\n            \"objects\": [\n                \"common.utils.logging.LogField\",\n                \"common.utils.logging.LogFields\"\n            ],\n            \"traits\": [\n                \"common.utils.logging.LogField\"\n            ],\n            \"names\": [\n                \"LazyLogging\",\n                \"LogField\",\n                \"LogFields\",\n                \"MapEntriesAppendingMarker\",\n                \"ObjectAppendingMarker\",\n                \"String\",\n                \"apply\",\n                \"fieldName\",\n                \"fieldValue\",\n                \"fieldValue.toString\",\n                \"name\",\n            ],\n            \"extends\": {\n                \"object trumid.common.utils.logging.LogField\": {\n                    \"classes\": [\n                        \"com.typesafe.scalalogging.LazyLogging\"\n                    ]\n                }\n            }\n        }\n    ],\n    \"sha256\": \"3ee80930372ea846ebb48e55eb76d55fed89b6af5f05d08f98b38045eb0464d6\",\n    \"parseTimeMillis\": \"3\"\n},\n```\n\nIn this case, if a dependency was missing from the `deps` list, but would be\ncorrected by resolving `ObjectAppendingMarker` (but not\n`MapEntriesAppendingMarker`, for example purposes), one could instruct the\nresolver to try and resolve it selectively via:\n\n```\n# gazelle:resolve_file_symbol_name LogField.scala +ObjectAppendingMarker -MapEntriesAppendingMarker\n```\n\n\n### `gazelle:scala_debug`\n\nThe `scala_debug` directive is a debugging aid that adds comments to the\ngenerated rules detailing what the symbols are and how they resolved.\n\n#### `imports`\n\nThis adds a list of comments to the `srcs` attribute detailing the required\nimports and how they resolved.  For example:\n\n```\n# gazelle:scala_debug imports\n```\n\nGenerates:\n\n```bazel\nscala_binary(\n    name = \"app\",\n    # import: ❌ AbstractServiceBase\u003cERROR\u003e symbol not found (EXTENDS of foo.allocation.Main)\n    # import: ✅ akka.NotUsed\u003cCLASS\u003e @maven//:com_typesafe_akka_akka_actor_2_12\u003cjarindex\u003e (DIRECT of BusinessFlows.scala)\n    # import: ✅ java.time.format.DateTimeFormatter\u003cCLASS\u003e NO-LABEL\u003cjava\u003e (DIRECT of RequestHandler.scala)\n    # import: ✅ scala.concurrent.ExecutionContext\u003cPACKAGE\u003e @maven//:org_scala_lang_scala_library\u003cmaven\u003e (DIRECT of RequestHandler.scala)\n    srcs = glob([\"src/main/**/*.scala\"]),\n    main_class = \"foo.allocation.Main\",\n)\n```\n\n#### `exports`\n\nThis adds a list of comments to the `srcs` attribute detailing the provided\nexports and how they resolved.  Example:\n\n```\n# gazelle:scala_debug exports\n```\n\n#### `deps`\n\nThis adds a suffix comment to each resolved item in `deps` and `exports` showing how the depency resolved.\n\n```\n# gazelle:scala_debug deps\n```\n\n# Import Resolution Procedure\n\n## How Required Imports are Calculated\n\n### Rule \n\nIf the rule has `main_class` attribute, that name is added to the imports (type\n`MAIN_CLASS`).\n\nThe remainder of rule imports are collected from file imports for all `.scala`\nsource files in the rule.\n\nOnce this initial set of imports are gathered, the transitive set of required\nsymbol are collected from:\n\n- `extends` clauses (type `EXTENDS`)\n- imports matching a `gazelle:resolve_with` directive (type `IMPLICIT`).\n\n### File \n\nThe imports for a file are collected as follows:\n\n#### Parsing\n\nThe `.scala` file is parsed:\n\n- Import statements are collected, including nested imports.\n- a set of *names* are collected by traversing the body of the AST.  Some of\n  these names are function calls, some of them are types, etc.\n\nSymbols named in import statements are added to imports (type `DIRECT`).\n\n#### Name resolution\n\nA trie of the symbols in scope for the file is built from:\n\n- the file package(s)\n- wildcard imports\n\n\"Names\" represent a variety of things in a scala file.  It might be a class\ninstatiation (e.g `new Foo()`), a static method call `doSomething()`, and other\nsimilar names.\n\nIn an earlier implementation of scala-gazelle, all *names* in the file were\ntested against the file scope and matching symbols are added to the imports\nlist (of type `RESOLVED_NAME`).\n\nThe drawback of that approach is that it was imprecise, potentially leading to\nfalse positive import resolutions (and unnecessary and/or incorrect `deps`\nlist).\n\nNow, name resolution is an opt-in feature using the gazelle directive `gazelle:resolve_file_symbol_name` directive.\n\n## How Required Imports are Resolved\n\nThe resolution procedure works as follows:\n\n1. Is the import named in a `gazelle:resolve` override?  If yes, stop ✅.\n2. Does the import satisfy a longest prefix match in the known import trie?  If\n   yes, stop ✅.\n3. Does the gazelle \"rule index\" and \"cross-resolve\" mechanism find a result for\n   the import?  If yes, stop ✅.\n4. No label was found.  Mark as `symbol not found` and move on ❌.\n\n# Help\n\nFor general help, please raise an [github\nissue](https://github.com/stackb/scala-gazelle/issues) or ask on the bazel slack\nin the `#gazelle` channel.\n\nIf you need dedicated help integrating `scala-gazelle` into your repository or\nwant additional features, please reach out to `pcj@stack.build` to assist on a\npart-time contractual basis.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackb%2Fscala-gazelle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstackb%2Fscala-gazelle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackb%2Fscala-gazelle/lists"}