├── .bazelrc ├── .bazelversion ├── .github └── workflows │ ├── ci.bazelrc │ ├── ci.yaml │ ├── format.yaml │ ├── release.yml │ └── workspace_snippet.sh ├── .gitignore ├── BUILD.bazel ├── LICENSE ├── Makefile ├── README.md ├── WORKSPACE ├── blaze └── worker │ ├── BUILD.bazel │ ├── worker_protocol.pb.go │ └── worker_protocol.proto ├── build └── stack │ └── gazelle │ └── scala │ ├── autokeep │ ├── BUILD.bazel │ ├── autokeep.pb.go │ └── autokeep.proto │ ├── cache │ ├── BUILD.bazel │ ├── cache.pb.go │ └── cache.proto │ ├── jarindex │ ├── BUILD.bazel │ ├── jarindex.pb.go │ └── jarindex.proto │ └── parse │ ├── BUILD.bazel │ ├── compiler.pb.go │ ├── compiler.proto │ ├── compiler_grpc.pb.go │ ├── file.pb.go │ ├── file.proto │ ├── import.pb.go │ ├── import.proto │ ├── parser.pb.go │ ├── parser.proto │ ├── parser_grpc.pb.go │ ├── rule.pb.go │ ├── rule.proto │ ├── symbol.pb.go │ └── symbol.proto ├── cmd ├── autokeep │ ├── BUILD.bazel │ ├── autokeep.bzl │ └── autokeep.go ├── jarindexer │ ├── BUILD.bazel │ ├── JarIndexer.java │ ├── JarIndexerTest.java │ ├── README.md │ └── testdata │ │ └── indexer │ │ ├── Main.java │ │ └── golden.json ├── mergeindex │ ├── BUILD.bazel │ └── mergeindex.go ├── scalafileextract │ ├── BUILD.bazel │ └── scalafileextract.go ├── scalafilemerge │ ├── BUILD.bazel │ └── scalafilemerge.go ├── semanticdbextract │ ├── BUILD.bazel │ └── semanticdbextract.go ├── semanticdbmerge │ ├── BUILD.bazel │ └── semanticdbmerge.go └── wildcardimportfixer │ ├── BUILD.bazel │ └── main.go ├── docs └── architecture │ ├── BUILD.bazel │ ├── README.md │ ├── components.png │ ├── components.puml │ ├── sequence.png │ └── sequence.puml ├── go.mod ├── go.sum ├── go_repos.bzl ├── language ├── files │ ├── BUILD.bazel │ ├── diff_test.go │ └── files.go └── scala │ ├── BUILD.bazel │ ├── cache.go │ ├── cleanup.go │ ├── configure.go │ ├── conflict_resolver_registry.go │ ├── coverage.go │ ├── coverage_test.go │ ├── cross_resolve.go │ ├── deps_cleaner_registry.go │ ├── diff_test.go │ ├── environment.go │ ├── existing_scala_rule.go │ ├── existing_scala_rule_test.go │ ├── fix.go │ ├── flags.go │ ├── flags_test.go │ ├── generate.go │ ├── golden_test.go │ ├── imports.go │ ├── kinds.go │ ├── known_rule_registry.go │ ├── language.go │ ├── language_test.go │ ├── lifecycle.go │ ├── loads.go │ ├── loads_test.go │ ├── package_marker_rule.go │ ├── progress.go │ ├── resolve.go │ ├── scala.WORKSPACE │ ├── scala_package.go │ ├── scala_package_test.go │ ├── scala_rule.go │ ├── scala_rule_test.go │ ├── scope.go │ ├── symbol_provider_registry.go │ ├── symbol_resolver.go │ └── testdata │ ├── java_provider │ ├── .bazelrc │ ├── .gazelle.args │ ├── BUILD.in │ ├── BUILD.out │ ├── Main.scala │ ├── WORKSPACE │ └── javaindex.json │ ├── maven_provider │ ├── .bazelrc │ ├── .gazelle.args │ ├── BUILD.in │ ├── BUILD.out │ ├── Main.scala │ ├── WORKSPACE │ ├── atlassian-public_install.json │ └── maven_install.json │ ├── override_provider │ ├── .bazelrc │ ├── .gazelle.args │ ├── BUILD.in │ ├── BUILD.out │ ├── Main.scala │ ├── WORKSPACE │ └── maven_install.json │ ├── protobuf_provider │ ├── .bazelrc │ ├── .gazelle.args │ ├── BUILD.in │ ├── BUILD.out │ ├── Main.scala │ ├── WORKSPACE │ └── proto │ │ ├── BUILD.in │ │ ├── BUILD.out │ │ └── customer.proto │ ├── resolve_kind_rewrite_name │ ├── .bazelrc │ ├── .gazelle.args │ ├── BUILD.in │ ├── BUILD.out │ ├── WORKSPACE │ ├── app │ │ ├── App.scala │ │ ├── BUILD.in │ │ └── BUILD.out │ ├── lib │ │ ├── BUILD.in │ │ ├── BUILD.out │ │ └── Helper.scala │ └── rules │ │ ├── BUILD.in │ │ ├── BUILD.out │ │ └── scala.bzl │ ├── scala_fileset │ ├── .bazelrc │ ├── .gazelle.args │ ├── BUILD.in │ ├── BUILD.out │ ├── Main.scala │ ├── WORKSPACE │ ├── maven_install.json │ └── src │ │ ├── BUILD.in │ │ ├── BUILD.out │ │ └── a │ │ └── b │ │ └── c │ │ ├── Lib.scala │ │ ├── LibTest.scala │ │ └── Main.scala │ └── source_provider │ ├── .bazelrc │ ├── .gazelle.args │ ├── BUILD.in │ ├── BUILD.out │ ├── Main.scala │ ├── WORKSPACE │ ├── maven_install.json │ └── src │ ├── BUILD.in │ ├── BUILD.out │ └── a │ └── b │ └── c │ ├── Lib.scala │ ├── LibTest.scala │ └── Main.scala ├── maven_install.json ├── pkg ├── autokeep │ ├── BUILD.bazel │ ├── deps.go │ ├── deps_test.go │ ├── scanner.go │ └── scanner_test.go ├── bazel │ ├── BUILD.bazel │ └── bazel.go ├── collections │ ├── BUILD.bazel │ ├── box_type.go │ ├── debugging.go │ ├── fs.go │ ├── intent.go │ ├── net.go │ ├── path_trie.go │ ├── sha256.go │ ├── slice.go │ ├── string_slice.go │ ├── string_stack.go │ └── uint32stack.go ├── glob │ ├── BUILD.bazel │ ├── collect.go │ ├── glob.go │ └── glob_test.go ├── jarindex │ ├── BUILD.bazel │ ├── merge.go │ └── merge_test.go ├── maven │ ├── BUILD.bazel │ ├── config.go │ ├── coordinate.go │ ├── multiset.go │ └── resolver.go ├── parser │ ├── BUILD.bazel │ ├── assets.go │ ├── assets_test.go │ ├── exec.go │ ├── exec_test.go │ ├── memo_parser.go │ ├── mocks │ │ ├── BUILD.bazel │ │ └── Parser.go │ ├── package.json │ ├── parser.go │ ├── scalameta_parser.go │ ├── scalameta_parser.mjs │ ├── scalameta_parser_test.go │ └── testdata │ │ ├── Main.scala │ │ └── Main.scala.golden.json ├── procutil │ ├── BUILD.bazel │ ├── cmd.go │ ├── env.go │ └── paths.go ├── protobuf │ ├── BUILD.bazel │ └── io.go ├── provider │ ├── BUILD.bazel │ ├── README.md │ ├── java_provider.go │ ├── java_provider_test.go │ ├── maven_provider.go │ ├── maven_provider_test.go │ ├── protobuf_provider.go │ ├── protobuf_provider_test.go │ ├── semanticdb_provider.go │ ├── semanticdb_provider_test.go │ ├── source_provider.go │ ├── source_provider_test.go │ └── testdata │ │ ├── DockerKit.scala │ │ ├── DockerKit.scala.golden.json │ │ ├── FullyQualified.scala │ │ ├── FullyQualified.scala.golden.json │ │ ├── GreeterClient.scala │ │ ├── GreeterClient.scala.golden.json │ │ ├── PostgresAccess.scala │ │ ├── PostgresAccess.scala.golden.json │ │ ├── UserId.scala │ │ ├── UserId.scala.golden.json │ │ ├── javaindex.json │ │ └── maven_install.json ├── resolver │ ├── BUILD.bazel │ ├── chain_scope.go │ ├── chain_scope_test.go │ ├── chain_symbol_resolver.go │ ├── conflict_resolver.go │ ├── conflict_resolver_registry.go │ ├── cross_symbol_resolver.go │ ├── deps_cleaner.go │ ├── deps_cleaner_registry.go │ ├── global_conflict_resolver_registry.go │ ├── global_deps_cleaner_registry.go │ ├── global_symbol_provider_registry.go │ ├── import.go │ ├── import_map.go │ ├── import_map_test.go │ ├── known_rule_registry.go │ ├── label_name_rewrite_spec.go │ ├── memo_symbol_resolver.go │ ├── mocks │ │ ├── BUILD.bazel │ │ ├── ConflictResolver.go │ │ ├── Scope.go │ │ ├── SymbolProvider.go │ │ ├── SymbolResolver.go │ │ ├── Universe.go │ │ └── symbol_capturer.go │ ├── override_symbol_resolver.go │ ├── predefined_label_conflict_resolver.go │ ├── predefined_label_conflict_resolver_test.go │ ├── preferred_deps_conflict_resolver.go │ ├── preferred_deps_conflict_resolver_test.go │ ├── scala_grpc_zio_conflict_resolver.go │ ├── scala_grpc_zio_conflict_resolver_test.go │ ├── scala_proto_package_conflict_resolver.go │ ├── scala_proto_package_conflict_resolver_test.go │ ├── scala_scope.go │ ├── scala_scope_test.go │ ├── scala_symbol_resolver.go │ ├── scala_symbol_resolver_test.go │ ├── scope.go │ ├── scope_map_test.go │ ├── scope_symbol_resolver.go │ ├── symbol.go │ ├── symbol_map.go │ ├── symbol_provider.go │ ├── symbol_provider_registry.go │ ├── symbol_resolver.go │ ├── symbol_test.go │ ├── trie_scope.go │ ├── trie_scope_test.go │ ├── trim_prefix_scope.go │ └── universe.go ├── scalaconfig │ ├── BUILD.bazel │ ├── config.go │ └── config_test.go ├── scalafiles │ ├── BUILD.bazel │ ├── scala_files.go │ └── scala_fileset.go ├── scalarule │ ├── BUILD.bazel │ ├── config.go │ ├── global_provider_registry.go │ ├── mocks │ │ ├── BUILD.bazel │ │ └── ProviderRegistry.go │ ├── package.go │ ├── provider.go │ ├── provider_registry.go │ ├── rule.go │ ├── rule_provider.go │ └── rule_resolver.go ├── semanticdb │ ├── BUILD.bazel │ ├── README.md │ ├── globalscope.go │ ├── io.go │ ├── semanticdb.go │ ├── semanticdb_index.go │ ├── semanticdb_test.go │ ├── testdata │ │ ├── stringlib.jar.textdocuments.json │ │ └── testdata │ │ │ └── stringlib.jar.textdocuments.json.golden │ │ │ ├── common │ │ │ └── utils │ │ │ │ └── string │ │ │ │ └── src │ │ │ │ └── package.scala.file.json │ │ │ └── trumid │ │ │ └── common │ │ │ └── utils │ │ │ └── string │ │ │ └── src │ │ │ └── package.scala.file.json │ ├── visitor.go │ └── visitor_test.go ├── starlarkeval │ ├── BUILD.bazel │ ├── convert_ast.go │ ├── convert_value.go │ └── interpreter.go ├── testutil │ ├── BUILD.bazel │ ├── test_logger.go │ └── testutil.go └── wildcardimport │ ├── BUILD.bazel │ ├── fixer.go │ ├── fixer_test.go │ ├── scanner.go │ ├── scanner_test.go │ └── text_file.go ├── rules ├── BUILD.bazel ├── artifacts.bzl ├── java_index.bzl ├── java_indexer_aspect.bzl ├── package_filegroup.bzl ├── providers.bzl ├── scala_files.bzl └── semanticdb_index.bzl ├── scala └── meta │ └── semanticdb │ ├── BUILD.bazel │ ├── semanticdb.pb.go │ └── semanticdb.proto ├── scalapb ├── BUILD.bazel ├── scalapb.pb.go └── scalapb.proto ├── third_party └── bazelbuild │ └── bazel-gazelle │ ├── BUILD.bazel │ └── pr-1394.patch ├── tools └── plantuml │ ├── BUILD.bazel │ ├── Main.java │ └── plantuml.bzl └── workspace_deps.bzl /.bazelrc: -------------------------------------------------------------------------------- 1 | # Keep until @io_bazel_rules_scala is upgraded 2 | build --incompatible_java_common_parameters=false 3 | 4 | query --output=label_kind 5 | -------------------------------------------------------------------------------- /.bazelversion: -------------------------------------------------------------------------------- 1 | 6.4.0 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.bazelrc: -------------------------------------------------------------------------------- 1 | # This file contains Bazel settings to apply on CI only. 2 | # It is referenced with a --bazelrc option in the call to bazel in ci.yaml 3 | # Debug where options came from 4 | build --announce_rc 5 | # Don't rely on test logs being easily accessible from the test runner, 6 | # though it makes the log noisier. 7 | test --test_output=errors 8 | # This directory is configured in GitHub actions to be persisted between runs. 9 | build --disk_cache=$HOME/.cache/bazel 10 | build --repository_cache=$HOME/.cache/bazel-repo 11 | # Allows tests to run bazelisk-in-bazel, since this is the cache folder used 12 | test --test_env=XDG_CACHE_HOME 13 | # Keep until @io_bazel_rules_scala is upgraded 14 | build --incompatible_java_common_parameters=false 15 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # Controls when the action will run. 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the master branch 6 | push: 7 | branches: [master] 8 | pull_request: 9 | branches: [master] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | jobs: 15 | test: 16 | # The type of runner that the job will run on 17 | runs-on: ubuntu-latest 18 | 19 | # Steps represent a sequence of tasks that will be executed as part of the job 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - uses: bazelbuild/setup-bazelisk@v3 24 | 25 | - name: Mount bazel action cache 26 | uses: actions/cache@v4 27 | if: always() 28 | with: 29 | path: "~/.cache/bazel" 30 | key: bazel 31 | 32 | - name: Mount bazel repo cache 33 | uses: actions/cache@v4 34 | if: always() 35 | with: 36 | path: "~/.cache/bazel-repo" 37 | key: bazel-repo 38 | 39 | - name: bazel test 40 | env: 41 | # Bazelisk will download bazel to here, ensure it is cached between runs. 42 | XDG_CACHE_HOME: ~/.cache/bazel-repo 43 | run: >- 44 | bazel 45 | --bazelrc=.github/workflows/ci.bazelrc 46 | --bazelrc=.bazelrc 47 | test 48 | //cmd/jarindexer/... 49 | //cmd/mergeindex/... 50 | //language/scala/... 51 | //pkg/... 52 | -------------------------------------------------------------------------------- /.github/workflows/format.yaml: -------------------------------------------------------------------------------- 1 | name: Format 2 | 3 | # Controls when the action will run. 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the main branch 6 | push: 7 | branches: [main] 8 | pull_request: 9 | branches: [main] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | jobs: 15 | test: 16 | # The type of runner that the job will run on 17 | runs-on: ubuntu-latest 18 | 19 | # Steps represent a sequence of tasks that will be executed as part of the job 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - uses: bazelbuild/setup-bazelisk@v3 24 | 25 | - name: Mount bazel action cache 26 | uses: actions/cache@v2 27 | with: 28 | path: "~/.cache/bazel" 29 | key: bazel 30 | 31 | - name: Mount bazel repo cache 32 | uses: actions/cache@v2 33 | with: 34 | path: "~/.cache/bazel-repo" 35 | key: bazel-repo 36 | 37 | - name: Run format 38 | env: 39 | # Bazelisk will download bazel to here, ensure it is cached between runs. 40 | XDG_CACHE_HOME: ~/.cache/bazel-repo 41 | run: ./tools/format.sh 42 | 43 | - name: Check no changes 44 | run: bash -c "if [[ $(git status --porcelain | wc -l) -gt 0 ]]; then echo >&2 'Please run \`./tools/format.sh\` - Found differences after formatting:' && git diff && exit 1; fi" 45 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Cut a release whenever a new tag is pushed to the repo. 2 | # You should use an annotated tag, like `git tag -a v1.2.3` 3 | # and put the release notes into the commit message for the tag. 4 | name: Release 5 | 6 | on: 7 | push: 8 | tags: 9 | - "v*.*.*" 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | - name: bazel test //... 18 | env: 19 | # Bazelisk will download bazel to here 20 | XDG_CACHE_HOME: ~/.cache/bazel-repo 21 | run: bazel --bazelrc=.github/workflows/ci.bazelrc --bazelrc=.bazelrc test //... 22 | - name: Prepare workspace snippet 23 | run: .github/workflows/workspace_snippet.sh ${{ env.GITHUB_REF_NAME }} > release_notes.txt 24 | - name: Release 25 | uses: softprops/action-gh-release@v1 26 | with: 27 | prerelease: true 28 | # Use GH feature to populate the changelog automatically 29 | generate_release_notes: true 30 | body_path: release_notes.txt 31 | -------------------------------------------------------------------------------- /.github/workflows/workspace_snippet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eufo pipefail 3 | 4 | # Set by GH actions, see 5 | # https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables 6 | TAG=${GITHUB_REF_NAME} 7 | PREFIX="rules_jvm-${TAG:1}" 8 | SHA=$(git archive --format=tar --prefix=${PREFIX}/ ${TAG} | gzip | shasum -a 256 | awk '{print $1}') 9 | 10 | cat << EOF 11 | WORKSPACE snippet: 12 | \`\`\`starlark 13 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 14 | http_archive( 15 | name = "contrib_rules_jvm", 16 | sha256 = "${SHA}", 17 | strip_prefix = "${PREFIX}", 18 | url = "https://github.com/bazel-contrib/rules_jvm/archive/refs/tags/${TAG}.tar.gz", 19 | ) 20 | 21 | # Fetches the contrib_rules_jvm dependencies. 22 | # If you want to have a different version of some dependency, 23 | # you should fetch it *before* calling this. 24 | load("@contrib_rules_jvm//:repositories.bzl", "contrib_rules_jvm_deps") 25 | 26 | contrib_rules_jvm_deps() 27 | 28 | # Now ensure that the downloaded deps are properly configured 29 | load("@contrib_rules_jvm//:setup.bzl", "contrib_rules_jvm_setup") 30 | 31 | contrib_rules_jvm_setup() 32 | \`\`\` 33 | EOF 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /pkg/parser/node.exe 2 | /bazel-* 3 | /.vscode/ 4 | /examples/ -------------------------------------------------------------------------------- /blaze/worker/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@rules_proto//proto:defs.bzl", "proto_library") 4 | load("@build_stack_rules_proto//rules:proto_compiled_sources.bzl", "proto_compiled_sources") 5 | 6 | proto_library( 7 | name = "worker_protocol_proto", 8 | srcs = ["worker_protocol.proto"], 9 | visibility = ["//visibility:public"], 10 | ) 11 | 12 | proto_compiled_sources( 13 | name = "worker_protocol_go_compiled_sources", 14 | srcs = ["worker_protocol.pb.go"], 15 | output_mappings = ["worker_protocol.pb.go=github.com/stackb/scala-gazelle/blaze/worker/worker_protocol/worker_protocol.pb.go"], 16 | plugins = ["@build_stack_rules_proto//plugin/golang/protobuf:protoc-gen-go"], 17 | proto = "worker_protocol_proto", 18 | ) 19 | 20 | go_library( 21 | name = "worker", 22 | srcs = ["worker_protocol.pb.go"], 23 | importpath = "github.com/stackb/scala-gazelle/blaze/worker", 24 | visibility = ["//visibility:public"], 25 | deps = [ 26 | "@org_golang_google_protobuf//reflect/protoreflect", 27 | "@org_golang_google_protobuf//runtime/protoimpl", 28 | ], 29 | ) 30 | 31 | package_filegroup( 32 | name = "filegroup", 33 | srcs = [ 34 | "BUILD.bazel", 35 | "worker_protocol.pb.go", 36 | "worker_protocol.proto", 37 | ], 38 | visibility = ["//visibility:public"], 39 | ) 40 | -------------------------------------------------------------------------------- /build/stack/gazelle/scala/autokeep/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@rules_proto//proto:defs.bzl", "proto_library") 4 | load("@build_stack_rules_proto//rules:proto_compiled_sources.bzl", "proto_compiled_sources") 5 | 6 | proto_library( 7 | name = "autokeep_proto", 8 | srcs = ["autokeep.proto"], 9 | visibility = ["//visibility:public"], 10 | deps = ["//build/stack/gazelle/scala/parse:parse_proto"], 11 | ) 12 | 13 | proto_compiled_sources( 14 | name = "autokeep_go_compiled_sources", 15 | srcs = ["autokeep.pb.go"], 16 | output_mappings = ["autokeep.pb.go=github.com/stackb/scala-gazelle/build/stack/gazelle/scala/autokeep/autokeep.pb.go"], 17 | plugins = ["@build_stack_rules_proto//plugin/golang/protobuf:protoc-gen-go"], 18 | proto = "autokeep_proto", 19 | ) 20 | 21 | go_library( 22 | name = "autokeep", 23 | srcs = ["autokeep.pb.go"], 24 | importpath = "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/autokeep", 25 | visibility = ["//visibility:public"], 26 | deps = [ 27 | "//build/stack/gazelle/scala/parse", 28 | "@org_golang_google_protobuf//reflect/protoreflect", 29 | "@org_golang_google_protobuf//runtime/protoimpl", 30 | ], 31 | ) 32 | 33 | package_filegroup( 34 | name = "filegroup", 35 | srcs = [ 36 | "BUILD.bazel", 37 | "autokeep.pb.go", 38 | "autokeep.proto", 39 | ], 40 | visibility = ["//visibility:public"], 41 | ) 42 | -------------------------------------------------------------------------------- /build/stack/gazelle/scala/autokeep/autokeep.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package build.stack.gazelle.scala.autokeep; 4 | 5 | import "build/stack/gazelle/scala/parse/rule.proto"; 6 | 7 | option go_package = "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/autokeep;autokeep"; 8 | option java_package = "build.stack.gazelle.scala.autokeep"; 9 | option java_multiple_files = true; 10 | 11 | message Diagnostics { 12 | repeated ScalacError scalac_errors = 1; 13 | } 14 | 15 | message ScalacError { 16 | string rule_label = 1; 17 | string build_file = 2; 18 | oneof error { 19 | MissingSymbol missing_symbol = 3; 20 | NotAMemberOfPackage not_a_member_of_package = 4; 21 | BuildozerUnusedDep buildozer_unused_dep = 5; 22 | } 23 | } 24 | 25 | message MissingSymbol { 26 | string source_file = 1; 27 | string symbol = 2; 28 | string required_by = 3; 29 | } 30 | 31 | message NotAMemberOfPackage { 32 | string package_name = 1; 33 | string symbol = 2; 34 | } 35 | 36 | message BuildozerUnusedDep { 37 | string rule_label = 1; 38 | string unused_dep = 2; 39 | } 40 | 41 | message RuleDeps { 42 | string label = 1; 43 | string build_file = 2; 44 | repeated string deps = 3; 45 | } 46 | 47 | message DeltaDeps { 48 | repeated RuleDeps add = 1; 49 | repeated RuleDeps remove = 2; 50 | } 51 | -------------------------------------------------------------------------------- /build/stack/gazelle/scala/cache/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@rules_proto//proto:defs.bzl", "proto_library") 4 | load("@build_stack_rules_proto//rules:proto_compiled_sources.bzl", "proto_compiled_sources") 5 | 6 | proto_library( 7 | name = "cache_proto", 8 | srcs = ["cache.proto"], 9 | visibility = ["//visibility:public"], 10 | deps = ["//build/stack/gazelle/scala/parse:parse_proto"], 11 | ) 12 | 13 | proto_compiled_sources( 14 | name = "cache_go_compiled_sources", 15 | srcs = ["cache.pb.go"], 16 | output_mappings = ["cache.pb.go=github.com/stackb/scala-gazelle/build/stack/gazelle/scala/cache/cache.pb.go"], 17 | plugins = ["@build_stack_rules_proto//plugin/golang/protobuf:protoc-gen-go"], 18 | proto = "cache_proto", 19 | ) 20 | 21 | go_library( 22 | name = "cache", 23 | srcs = ["cache.pb.go"], 24 | importpath = "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/cache", 25 | visibility = ["//visibility:public"], 26 | deps = [ 27 | "//build/stack/gazelle/scala/parse", 28 | "@org_golang_google_protobuf//reflect/protoreflect", 29 | "@org_golang_google_protobuf//runtime/protoimpl", 30 | ], 31 | ) 32 | 33 | package_filegroup( 34 | name = "filegroup", 35 | srcs = [ 36 | "BUILD.bazel", 37 | "cache.pb.go", 38 | "cache.proto", 39 | ], 40 | visibility = ["//visibility:public"], 41 | ) 42 | -------------------------------------------------------------------------------- /build/stack/gazelle/scala/cache/cache.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package build.stack.gazelle.scala.cache; 4 | 5 | import "build/stack/gazelle/scala/parse/rule.proto"; 6 | 7 | option go_package = "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/cache;cache"; 8 | option java_package = "build.stack.gazelle.scala.cache"; 9 | option java_multiple_files = true; 10 | 11 | // Cache represents the scala gazelle file parse rule cache. 12 | message Cache { 13 | // package_count is the number of packages visited 14 | // during the generation phase. 15 | int32 package_count = 1; 16 | // rules is the list of parsed rules. 17 | repeated build.stack.gazelle.scala.parse.Rule rules = 2; 18 | // key is a string that is persisted in the cache file. If the key changes, 19 | // the cache is evicted. 20 | string key = 3; 21 | } 22 | 23 | // Resolved imports is a mapping between a fully-qualified scala import type and 24 | // the bazel label that provides it. 25 | message ResolvedImports { 26 | map imports = 1; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /build/stack/gazelle/scala/parse/compiler.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package build.stack.gazelle.scala.parse; 4 | 5 | import "build/stack/gazelle/scala/parse/file.proto"; 6 | 7 | option go_package = "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse;parse"; 8 | option java_package = "build.stack.gazelle.scala.parse"; 9 | option java_multiple_files = true; 10 | 11 | // CompileRequest holds a list of filenames to be parsed. 12 | message CompileRequest { 13 | string dir = 1; 14 | // list of files relative to dir. 15 | repeated string filenames = 2; 16 | } 17 | 18 | // Severity of the diagnostic. 19 | enum Severity { 20 | SEVERITY_UNKNOWN = 0; 21 | INFO = 1; 22 | WARN = 2; 23 | ERROR = 3; 24 | } 25 | 26 | // Diagnostic is similar structure of what is emitted by the scala compiler 27 | // (scalac). 28 | message Diagnostic { 29 | // severity level 30 | Severity severity = 1; 31 | // absolute path to file 32 | string source = 2; 33 | // line number 34 | int32 line = 3; 35 | // detail message 36 | string message = 4; 37 | } 38 | 39 | // CompileResponse holds the collected diagnostics. 40 | message CompileResponse { 41 | // list of diagnostics 42 | repeated Diagnostic diagnostics = 1; 43 | // error is populated when a top-level parse error occurs that is 44 | // independent from a single file. 45 | string error = 2; 46 | // elapsed_millis is the total time spent on the request. 47 | int64 elapsed_millis = 3; 48 | } 49 | 50 | // Compiler is a service that can compile scala files. 51 | service Compiler { 52 | // Compile is a unary rpc function that takes a single request and returns a 53 | // single response. 54 | rpc Compile(CompileRequest) returns (CompileResponse) {} 55 | } 56 | -------------------------------------------------------------------------------- /build/stack/gazelle/scala/parse/file.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package build.stack.gazelle.scala.parse; 4 | 5 | option go_package = "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse;parse"; 6 | option java_package = "build.stack.gazelle.scala.parse"; 7 | option java_multiple_files = true; 8 | 9 | // FileSet represents a set of Files 10 | message FileSet { 11 | repeated File files = 1; 12 | } 13 | 14 | // File represents a parsed file named in a scala rule.srcs list. 15 | message File { 16 | // filename is the (workspace relative) source filename 17 | string filename = 1; 18 | // semantic_imports is a list of types used by the file, typically 19 | // discovered using semanticdb. 20 | repeated string semantic_imports = 2; 21 | // imports is a list of required imports. 22 | repeated string imports = 3; 23 | // packages is a list of provided top-level classes. 24 | repeated string packages = 4; 25 | // classes is a list of provided top-level classes. 26 | repeated string classes = 5; 27 | // objects is a list of provided top-level classes. 28 | repeated string objects = 6; 29 | // traits is a list of provided top-level classes. 30 | repeated string traits = 7; 31 | // types is a list of provided top-level types (in package objects). 32 | repeated string types = 8; 33 | // vals is a list of provided top-level vals (in package objects). 34 | repeated string vals = 9; 35 | // names is a list of simple function calls. In practice these look like 36 | // constructor invocations. 37 | repeated string names = 10; 38 | // extends is a mapping from the base type to a list of symbol names. 39 | map extends = 11; 40 | // error is a string assigned when a parse error occurs 41 | string error = 13; 42 | // tree is a JSON string representing the parse tree. This field is only 43 | // populated when specifically requested during parsing. 44 | string tree = 14; 45 | } 46 | 47 | // ClassList represents a set of files. 48 | message ClassList { 49 | repeated string classes = 1; 50 | } 51 | -------------------------------------------------------------------------------- /build/stack/gazelle/scala/parse/parser.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package build.stack.gazelle.scala.parse; 4 | 5 | import "build/stack/gazelle/scala/parse/file.proto"; 6 | 7 | option go_package = "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse;parse"; 8 | option java_package = "build.stack.gazelle.scala.parse"; 9 | option java_multiple_files = true; 10 | 11 | // ParseRequest holds a list of filenames to be parsed. 12 | message ParseRequest { 13 | // filenames is the file list. 14 | repeated string filenames = 1; 15 | // if true, files parsed should return the raw parse tree. 16 | bool want_parse_tree = 2; 17 | } 18 | 19 | // ParseResponse holds the parsed file data. 20 | message ParseResponse { 21 | // files is file parse result list. 22 | repeated build.stack.gazelle.scala.parse.File files = 1; 23 | // error is populated when a top-level parse error occurs that is 24 | // independent from a single file. 25 | string error = 2; 26 | // elapsed_millis is the total time spent on the request. 27 | int64 elapsed_millis = 3; 28 | } 29 | 30 | // Parser is a service that can parse scala files. 31 | service Parser { 32 | // Parse is a unary rpc function that takes a single request and returns a 33 | // single response. 34 | rpc Parse(ParseRequest) returns (ParseResponse) {} 35 | } 36 | -------------------------------------------------------------------------------- /build/stack/gazelle/scala/parse/rule.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package build.stack.gazelle.scala.parse; 4 | 5 | import "build/stack/gazelle/scala/parse/file.proto"; 6 | 7 | option go_package = "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse;parse"; 8 | option java_package = "build.stack.gazelle.scala.parse"; 9 | option java_multiple_files = true; 10 | 11 | // Rule represents a set of files with a label and rule kind. 12 | message Rule { 13 | // label is the bazel label. 14 | string label = 1; 15 | // kind is the kind of rule named by label. 16 | string kind = 2; 17 | // files is the list of files in the rule 18 | repeated build.stack.gazelle.scala.parse.File files = 3; 19 | // sha256 is a combined hash of the files 20 | string sha256 = 4; 21 | // parse_time_millis is the time taken to collect filesnames and parse the files. 22 | int64 parse_time_millis = 5; 23 | } 24 | 25 | // RuleSet represents a set of Rule(s). 26 | message RuleSet { 27 | repeated Rule rules = 1; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /build/stack/gazelle/scala/parse/symbol.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package build.stack.gazelle.scala.parse; 4 | 5 | option go_package = "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse;parse"; 6 | option java_package = "build.stack.gazelle.scala.parse"; 7 | option java_multiple_files = true; 8 | 9 | // Symbol represents a 10 | message Symbol { 11 | // Type of the symbol 12 | SymbolType type = 1; 13 | // the fully-qualified name of the symbol. 14 | string name = 2; 15 | } 16 | 17 | // SymbolType describes the possible kinds of symbols. 18 | enum SymbolType { 19 | SYMBOL_TYPE_UNKNOWN = 0; 20 | // Package type 21 | SYMBOL_PACKAGE = 1; 22 | // JVM class type 23 | SYMBOL_CLASS = 2; 24 | // JVM interface type 25 | SYMBOL_INTERFACE = 3; 26 | // Scala trait type 27 | SYMBOL_TRAIT = 4; 28 | // Scala object type 29 | SYMBOL_OBJECT = 5; 30 | // Scala val type 31 | SYMBOL_VALUE = 7; 32 | // OVERRIDE signals that the import was configured via a 'gazelle:resolve' 33 | // override directive and hence we don't know the underlying type of thing. 34 | SYMBOL_OVERRIDE = 8; 35 | // CROSS_RESOLVE signals that the import was resolved via gazelle's underlying 36 | // cross resolver system. We don't know the underlying type here either. 37 | SYMBOL_CROSS_RESOLVE = 9; 38 | // PLATFORM imports include stdlib'ish things like java.lang.String. We 39 | // don't need a dep for them (and we don't know the underlying type). 40 | SYMBOL_PLATFORM = 10; 41 | // Scala type type 42 | SYMBOL_TYPE = 11; 43 | // Protobuf Enum 44 | SYMBOL_PROTO_ENUM = 12; 45 | // Protobuf Enum Field 46 | SYMBOL_PROTO_ENUM_FIELD = 13; 47 | // Protobuf Message 48 | SYMBOL_PROTO_MESSAGE = 14; 49 | // Protobuf Service 50 | SYMBOL_PROTO_SERVICE = 15; 51 | // Protobuf Package 52 | SYMBOL_PROTO_PACKAGE = 16; 53 | } 54 | -------------------------------------------------------------------------------- /cmd/autokeep/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 3 | 4 | go_library( 5 | name = "autokeep_lib", 6 | srcs = ["autokeep.go"], 7 | importpath = "github.com/stackb/scala-gazelle/cmd/autokeep", 8 | visibility = ["//visibility:private"], 9 | deps = ["//pkg/autokeep"], 10 | ) 11 | 12 | go_binary( 13 | name = "autokeep", 14 | embed = [":autokeep_lib"], 15 | visibility = ["//visibility:public"], 16 | ) 17 | 18 | package_filegroup( 19 | name = "filegroup", 20 | srcs = [ 21 | "BUILD.bazel", 22 | "autokeep.bzl", 23 | "autokeep.go", 24 | ], 25 | visibility = ["//visibility:public"], 26 | ) 27 | -------------------------------------------------------------------------------- /cmd/autokeep/autokeep.bzl: -------------------------------------------------------------------------------- 1 | "autokeep.bzl provides a rule to run autokeep" 2 | script_content = """#!/usr/bin/env bash 3 | {autokeep_path} \ 4 | --cache_file={cache_file} \ 5 | --only_rules={only_rules} \ 6 | 7 | """ 8 | 9 | def autokeep_impl(ctx): 10 | script = ctx.outputs.executable 11 | ctx.actions.write( 12 | output = script, 13 | is_executable = True, 14 | content = script_content.format( 15 | autokeep_path = ctx.executable._tool.short_path, 16 | cache_file = ctx.file.cache_file.short_path, 17 | only_rules = ",".join(ctx.attr.only_rules), 18 | ), 19 | ) 20 | 21 | runfiles = ctx.runfiles([ 22 | script, 23 | ctx.file.cache_file, 24 | ctx.executable._tool, 25 | ], collect_data = True, collect_default = True) 26 | 27 | return [ 28 | DefaultInfo( 29 | files = depset([script]), 30 | runfiles = runfiles, 31 | ), 32 | ] 33 | 34 | autokeep = rule( 35 | implementation = autokeep_impl, 36 | attrs = { 37 | "cache_file": attr.label( 38 | doc = "the scala-gazelle cache file to read", 39 | mandatory = True, 40 | allow_single_file = True, 41 | ), 42 | "only_rules": attr.string_list( 43 | doc = "list of rules to limit autokeep processing to", 44 | ), 45 | "_tool": attr.label( 46 | default = "//cmd/autokeep", 47 | executable = True, 48 | cfg = "exec", 49 | ), 50 | }, 51 | executable = True, 52 | ) 53 | -------------------------------------------------------------------------------- /cmd/jarindexer/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | 3 | # gazelle:exclude testdata 4 | 5 | java_library( 6 | name = "jarindexer", 7 | srcs = ["JarIndexer.java"], 8 | visibility = ["//visibility:public"], 9 | deps = [ 10 | "//build/stack/gazelle/scala/jarindex:jarindex_java_library", 11 | "@classgraph_jar//jar", 12 | "@protobuf_java_jar//jar", 13 | ], 14 | ) 15 | 16 | java_binary( 17 | name = "jarindexer_bin", 18 | main_class = "JarIndexer", 19 | visibility = ["//visibility:public"], 20 | runtime_deps = [ 21 | ":jarindexer", 22 | "//build/stack/gazelle/scala/jarindex:jarindex_java_library", 23 | "@classgraph_jar//jar", 24 | ], 25 | ) 26 | 27 | java_test( 28 | name = "jarindexer_test", 29 | srcs = ["JarIndexerTest.java"], 30 | data = [ 31 | ":jarindexer_jar", 32 | ] + glob(["testdata/**/*"]), 33 | test_class = "JarIndexerTest", 34 | deps = [ 35 | ":jarindexer", 36 | "//build/stack/gazelle/scala/jarindex:jarindex_java_library", 37 | "@com_google_protobuf//:protobuf_java", 38 | "@com_google_protobuf//:protobuf_java_util", 39 | ], 40 | ) 41 | 42 | genrule( 43 | name = "jarindexer_jar", 44 | srcs = [":jarindexer"], 45 | outs = ["testdata/indexer/indexer.jar"], 46 | cmd = "cp $(location :jarindexer) $@", 47 | executable = False, 48 | ) 49 | 50 | package_filegroup( 51 | name = "filegroup", 52 | srcs = [ 53 | "BUILD.bazel", 54 | "JarIndexer.java", 55 | "JarIndexerTest.java", 56 | "README.md", 57 | ], 58 | visibility = ["//visibility:public"], 59 | ) 60 | -------------------------------------------------------------------------------- /cmd/jarindexer/README.md: -------------------------------------------------------------------------------- 1 | # jarindexer 2 | 3 | The jarindexer tool is used to build an index of packages/classes/symbols within a jar file. 4 | -------------------------------------------------------------------------------- /cmd/jarindexer/testdata/indexer/Main.java: -------------------------------------------------------------------------------- 1 | 2 | public class Main { 3 | public static void main(String[] args) { 4 | System.out.println("Hello, world!"); 5 | } 6 | } -------------------------------------------------------------------------------- /cmd/jarindexer/testdata/indexer/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "jarFiles": [{ 3 | "filename": "testdata/indexer/indexer.jar", 4 | "label": "//fake:label", 5 | "kind": "java_library", 6 | "files": [{ 7 | "name": "JarIndexer", 8 | "symbols": ["build.stack.gazelle.scala.jarindex.ClassFile", "build.stack.gazelle.scala.jarindex.JarFile", "build.stack.gazelle.scala.jarindex.JarIndex", "io.github.classgraph.ClassInfo", "io.github.classgraph.MethodParameterInfo", "io.github.classgraph.MethodTypeSignature", "io.github.classgraph.TypeParameter", "io.github.classgraph.TypeSignature", "java.lang.String", "java.nio.file.Path", "java.util.Collection", "java.util.Set"] 9 | }] 10 | }] 11 | } -------------------------------------------------------------------------------- /cmd/mergeindex/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 3 | 4 | go_library( 5 | name = "mergeindex_lib", 6 | srcs = ["mergeindex.go"], 7 | importpath = "github.com/stackb/scala-gazelle/cmd/mergeindex", 8 | visibility = ["//visibility:public"], 9 | deps = [ 10 | "//build/stack/gazelle/scala/jarindex", 11 | "//pkg/collections", 12 | "//pkg/jarindex", 13 | "//pkg/protobuf", 14 | ], 15 | ) 16 | 17 | go_binary( 18 | name = "mergeindex", 19 | embed = [":mergeindex_lib"], 20 | visibility = ["//visibility:public"], 21 | ) 22 | 23 | package_filegroup( 24 | name = "filegroup", 25 | srcs = [ 26 | "BUILD.bazel", 27 | "mergeindex.go", 28 | ], 29 | visibility = ["//visibility:public"], 30 | ) 31 | -------------------------------------------------------------------------------- /cmd/scalafileextract/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 3 | 4 | go_library( 5 | name = "scalafileextract_lib", 6 | srcs = ["scalafileextract.go"], 7 | importpath = "github.com/stackb/scala-gazelle/cmd/scalafileextract", 8 | visibility = ["//visibility:private"], 9 | deps = [ 10 | "//blaze/worker", 11 | "//build/stack/gazelle/scala/parse", 12 | "//pkg/collections", 13 | "//pkg/parser", 14 | "//pkg/protobuf", 15 | ], 16 | ) 17 | 18 | go_binary( 19 | name = "scalafileextract", 20 | embed = [":scalafileextract_lib"], 21 | visibility = ["//visibility:public"], 22 | ) 23 | 24 | package_filegroup( 25 | name = "filegroup", 26 | srcs = [ 27 | "BUILD.bazel", 28 | "scalafileextract.go", 29 | ], 30 | visibility = ["//visibility:public"], 31 | ) 32 | -------------------------------------------------------------------------------- /cmd/scalafilemerge/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 3 | 4 | go_library( 5 | name = "scalafilemerge_lib", 6 | srcs = ["scalafilemerge.go"], 7 | importpath = "github.com/stackb/scala-gazelle/cmd/scalafilemerge", 8 | visibility = ["//visibility:private"], 9 | deps = [ 10 | "//build/stack/gazelle/scala/parse", 11 | "//pkg/collections", 12 | "//pkg/protobuf", 13 | ], 14 | ) 15 | 16 | go_binary( 17 | name = "scalafilemerge", 18 | embed = [":scalafilemerge_lib"], 19 | visibility = ["//visibility:public"], 20 | ) 21 | 22 | package_filegroup( 23 | name = "filegroup", 24 | srcs = [ 25 | "BUILD.bazel", 26 | "scalafilemerge.go", 27 | ], 28 | visibility = ["//visibility:public"], 29 | ) 30 | -------------------------------------------------------------------------------- /cmd/semanticdbextract/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 3 | 4 | go_library( 5 | name = "semanticdbextract_lib", 6 | srcs = ["semanticdbextract.go"], 7 | importpath = "github.com/stackb/scala-gazelle/cmd/semanticdbextract", 8 | visibility = ["//visibility:private"], 9 | deps = [ 10 | "//pkg/collections", 11 | "//pkg/protobuf", 12 | "//pkg/semanticdb", 13 | "//scala/meta/semanticdb", 14 | ], 15 | ) 16 | 17 | go_binary( 18 | name = "semanticdbextract", 19 | embed = [":semanticdbextract_lib"], 20 | visibility = ["//visibility:public"], 21 | ) 22 | 23 | package_filegroup( 24 | name = "filegroup", 25 | srcs = [ 26 | "BUILD.bazel", 27 | "semanticdbextract.go", 28 | ], 29 | visibility = ["//visibility:public"], 30 | ) 31 | -------------------------------------------------------------------------------- /cmd/semanticdbmerge/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 3 | 4 | go_library( 5 | name = "semanticdbmerge_lib", 6 | srcs = ["semanticdbmerge.go"], 7 | importpath = "github.com/stackb/scala-gazelle/cmd/semanticdbmerge", 8 | visibility = ["//visibility:private"], 9 | deps = [ 10 | "//build/stack/gazelle/scala/parse", 11 | "//pkg/collections", 12 | "//pkg/protobuf", 13 | "//pkg/semanticdb", 14 | "//scala/meta/semanticdb", 15 | ], 16 | ) 17 | 18 | go_binary( 19 | name = "semanticdbmerge", 20 | embed = [":semanticdbmerge_lib"], 21 | visibility = ["//visibility:public"], 22 | ) 23 | 24 | package_filegroup( 25 | name = "filegroup", 26 | srcs = [ 27 | "BUILD.bazel", 28 | "semanticdbmerge.go", 29 | ], 30 | visibility = ["//visibility:public"], 31 | ) 32 | -------------------------------------------------------------------------------- /cmd/wildcardimportfixer/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 3 | 4 | go_library( 5 | name = "wildcardimportfixer_lib", 6 | srcs = ["main.go"], 7 | importpath = "github.com/stackb/scala-gazelle/cmd/wildcardimportfixer", 8 | visibility = ["//visibility:private"], 9 | deps = ["//pkg/wildcardimport"], 10 | ) 11 | 12 | go_binary( 13 | name = "wildcardimportfixer", 14 | embed = [":wildcardimportfixer_lib"], 15 | visibility = ["//visibility:public"], 16 | ) 17 | 18 | package_filegroup( 19 | name = "filegroup", 20 | srcs = [ 21 | "BUILD.bazel", 22 | "main.go", 23 | ], 24 | visibility = ["//visibility:public"], 25 | ) 26 | -------------------------------------------------------------------------------- /cmd/wildcardimportfixer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/stackb/scala-gazelle/pkg/wildcardimport" 10 | ) 11 | 12 | const ( 13 | executableName = "wildcardimportfixer" 14 | ) 15 | 16 | type config struct { 17 | ruleLabel string 18 | targetFilename string 19 | importPrefix string 20 | bazelExe string 21 | } 22 | 23 | func main() { 24 | log.SetPrefix(executableName + ": ") 25 | log.SetFlags(0) // don't print timestamps 26 | 27 | cfg, err := parseFlags(os.Args[1:]) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | if err := run(cfg); err != nil { 33 | log.Fatalln("ERROR:", err) 34 | } 35 | 36 | } 37 | 38 | func parseFlags(args []string) (*config, error) { 39 | cfg := new(config) 40 | 41 | fs := flag.NewFlagSet(executableName, flag.ExitOnError) 42 | fs.StringVar(&cfg.ruleLabel, "rule_label", "", "the rule label to iteratively build") 43 | fs.StringVar(&cfg.targetFilename, "target_filename", "", "the scala file to fix") 44 | fs.StringVar(&cfg.importPrefix, "import_prefix", "", "the scala import prefix to set") 45 | fs.StringVar(&cfg.bazelExe, "bazel_executable", "bazel", "the path to the bazel executable") 46 | fs.Usage = func() { 47 | fmt.Fprintf(flag.CommandLine.Output(), "usage: %s OPTIONS", executableName) 48 | fs.PrintDefaults() 49 | } 50 | if err := fs.Parse(args); err != nil { 51 | return nil, err 52 | } 53 | 54 | if cfg.ruleLabel == "" { 55 | log.Fatal("-rule_label is required") 56 | } 57 | 58 | return cfg, nil 59 | } 60 | 61 | func run(cfg *config) error { 62 | 63 | var err error 64 | 65 | fixer := wildcardimport.NewFixer(&wildcardimport.FixerOptions{ 66 | BazelExecutable: cfg.bazelExe, 67 | }) 68 | 69 | symbols, err := fixer.Fix(cfg.ruleLabel, cfg.targetFilename, cfg.importPrefix) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | log.Println("FIXED", cfg.targetFilename, symbols) 75 | 76 | return nil 77 | } 78 | -------------------------------------------------------------------------------- /docs/architecture/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("//tools/plantuml:plantuml.bzl", "plantuml_diagram") 3 | 4 | plantuml_diagram( 5 | name = "sequence", 6 | src = "sequence.puml", 7 | dst = "sequence.png", 8 | ) 9 | 10 | plantuml_diagram( 11 | name = "components", 12 | src = "components.puml", 13 | dst = "components.png", 14 | ) 15 | 16 | package_filegroup( 17 | name = "filegroup", 18 | srcs = [ 19 | "BUILD.bazel", 20 | "README.md", 21 | "components.png", 22 | "components.puml", 23 | "sequence.png", 24 | "sequence.puml", 25 | ], 26 | visibility = ["//visibility:public"], 27 | ) 28 | -------------------------------------------------------------------------------- /docs/architecture/README.md: -------------------------------------------------------------------------------- 1 | # Architecture 2 | 3 | ## Components 4 | 5 | ![components](./components.png) 6 | 7 | ## Sequence 8 | 9 | ![sequence](./sequence.png) -------------------------------------------------------------------------------- /docs/architecture/components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/docs/architecture/components.png -------------------------------------------------------------------------------- /docs/architecture/components.puml: -------------------------------------------------------------------------------- 1 | 2 | @startuml components 3 | 4 | skinparam Shadowing false 5 | 6 | frame "gazelle" { 7 | 8 | frame "protobuf" as C { 9 | [GlobalResolver] #f8c8a4 10 | } 11 | 12 | frame "scala" as S { 13 | database Scope { 14 | 15 | } 16 | [MavenProvider] #ff6f61 17 | [JavaProvider] #ff6f61 18 | component SourceProvider { 19 | [ScalaMetaParser] #f4a6a6 20 | [Imports] #f4a6a6 21 | [SemanticDBProvider] #fa8072 22 | } 23 | [ProtobufProvider] #f8c8a4 24 | [Resolver] 25 | } 26 | 27 | } 28 | 29 | frame "WORKSPACE Rules" { 30 | [maven_install] #ff6f61 31 | [maven_install.json] as MavenInstallJson #ff6f61 32 | } 33 | 34 | frame "BUILD Rules" { 35 | [proto_scala_library] #f8c8a4 36 | [java_index] #ff6f61 37 | [scala_library] #f4a6a6 38 | 39 | [jar_index.json] as JarIndexJson #ff6f61 40 | [scala.jar] as ScalaJar #fa8072 41 | [App.scala] as AppScala #f4a6a6 42 | } 43 | 44 | proto_scala_library --> GlobalResolver 45 | java_index --> JarIndexJson: "@maven//:com_google_json" 46 | JarIndexJson --> JavaProvider 47 | maven_install --> MavenInstallJson 48 | MavenInstallJson --> MavenProvider 49 | scala_library --> AppScala: srcs 50 | AppScala --> ScalaMetaParser 51 | ScalaMetaParser --> Imports 52 | SemanticDBProvider --> Imports 53 | scala_library --> ScalaJar: scalac 54 | ScalaJar --> SemanticDBProvider: META-INF/semanticdb 55 | 56 | MavenProvider --> Scope: com.google.gson 57 | ProtobufProvider --> Scope: myapp.proto.UserMessage 58 | JavaProvider --> Scope: java.lang.*, com.google.gson.GSON 59 | Imports --> Scope: myapp.util.Helper 60 | 61 | GlobalResolver --> ProtobufProvider 62 | Scope --> Resolver 63 | 64 | ' Resolve --> Resolver 65 | ' Export --> CSVFile: write 66 | ' CSVFile ..> [Topic]: " notify file created" 67 | ' [Topic] -> [Subscription]: notify 68 | ' [Subscription] ..> EventGridHandler: " send file created event" 69 | ' EventGridHandler --> [ProcessCSV]: " start" 70 | ' [ProcessCSV] <.. CSVFile: "read" 71 | ' [ProcessCSV] --> JSONFile: "\n write" 72 | ' [FunctionApp] - [API] 73 | ' [HttpClient] <..> [API]: " request/response" 74 | ' [CostExportClient] ..> Export: " create" 75 | 76 | @enduml -------------------------------------------------------------------------------- /docs/architecture/sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/docs/architecture/sequence.png -------------------------------------------------------------------------------- /docs/architecture/sequence.puml: -------------------------------------------------------------------------------- 1 | @startuml sequence 2 | 3 | title scala-gazelle run sequence 4 | 5 | skinparam Shadowing false 6 | 7 | hide footbox 8 | 9 | participant Gazelle as G 10 | participant Scala as S 11 | participant Protobuf as P 12 | 13 | == Configuration == 14 | 15 | == Generation == 16 | 17 | == Resolve == 18 | 19 | == Cleanup == 20 | 21 | == Emit == 22 | 23 | @enduml -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/stackb/scala-gazelle 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/amenzhinsky/go-memexec v0.7.0 7 | github.com/bazelbuild/bazel-gazelle v0.28.1-0.20221114044302-2d1002926dd1 8 | github.com/bazelbuild/buildtools v0.0.0-20221004120235-7186f635531b 9 | github.com/bazelbuild/rules_go v0.35.0 10 | github.com/bmatcuk/doublestar/v4 v4.2.0 11 | github.com/davecgh/go-spew v1.1.1 12 | github.com/dghubble/trie v0.0.0-20220811160003-18e0eff3ca7b 13 | github.com/google/go-cmp v0.5.9 14 | github.com/pcj/mobyprogress v0.0.0-20221114203314-669a7801d484 15 | github.com/stackb/rules_proto v0.0.0-20221020023827-830238a0c071 16 | github.com/stretchr/testify v1.7.0 17 | go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd 18 | google.golang.org/grpc v1.42.0 19 | google.golang.org/protobuf v1.28.1 20 | ) 21 | 22 | require ( 23 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect 24 | github.com/docker/go-units v0.5.0 // indirect 25 | github.com/emicklei/proto v1.9.0 // indirect 26 | github.com/golang/protobuf v1.5.2 // indirect 27 | github.com/mattn/go-colorable v0.1.13 // indirect 28 | github.com/mattn/go-isatty v0.0.19 // indirect 29 | github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect 30 | github.com/morikuni/aec v1.0.0 // indirect 31 | github.com/pmezard/go-difflib v1.0.0 // indirect 32 | github.com/rs/zerolog v1.34.0 // indirect 33 | github.com/stretchr/objx v0.1.0 // indirect 34 | golang.org/x/net v0.1.0 // indirect 35 | golang.org/x/sys v0.12.0 // indirect 36 | golang.org/x/text v0.4.0 // indirect 37 | golang.org/x/time v0.2.0 // indirect 38 | golang.org/x/tools v0.2.0 // indirect 39 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect 40 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect 41 | ) 42 | 43 | replace github.com/bazelbuild/bazel-gazelle => github.com/pcj/bazel-gazelle v0.0.0-20221215190317-f045255d3e6a 44 | -------------------------------------------------------------------------------- /language/files/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 3 | load("@bazel_gazelle//:def.bzl", "gazelle_binary") 4 | 5 | go_library( 6 | name = "files", 7 | srcs = ["files.go"], 8 | importpath = "github.com/stackb/scala-gazelle/language/files", 9 | visibility = ["//visibility:public"], 10 | deps = [ 11 | "@bazel_gazelle//config:go_default_library", 12 | "@bazel_gazelle//label:go_default_library", 13 | "@bazel_gazelle//language:go_default_library", 14 | "@bazel_gazelle//repo:go_default_library", 15 | "@bazel_gazelle//resolve:go_default_library", 16 | "@bazel_gazelle//rule:go_default_library", 17 | ], 18 | ) 19 | 20 | package_filegroup( 21 | name = "filegroup", 22 | srcs = [ 23 | "BUILD.bazel", 24 | "diff_test.go", 25 | "files.go", 26 | ], 27 | visibility = ["//visibility:public"], 28 | ) 29 | 30 | genrule( 31 | name = "gazelle_tool", 32 | outs = ["gazelle"], 33 | cmd = "cp $(location :gazelle-scala) $@", 34 | executable = True, 35 | tools = [":gazelle-scala"], 36 | ) 37 | 38 | gazelle_binary( 39 | name = "gazelle-scala", 40 | languages = [ 41 | "@bazel_gazelle//language/go", 42 | "@bazel_gazelle//language/proto", 43 | ":files", 44 | ], 45 | visibility = ["//visibility:public"], 46 | ) 47 | 48 | go_test( 49 | name = "files_test", 50 | srcs = ["diff_test.go"], 51 | data = [":gazelle"] + glob(["testdata/**"]), 52 | embed = [":files"], 53 | deps = [ 54 | "//pkg/testutil", 55 | "@bazel_gazelle//testtools:go_default_library", 56 | ], 57 | ) 58 | -------------------------------------------------------------------------------- /language/scala/cache.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "github.com/bazelbuild/bazel-gazelle/label" 8 | scpb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/cache" 9 | "github.com/stackb/scala-gazelle/pkg/parser" 10 | "github.com/stackb/scala-gazelle/pkg/protobuf" 11 | ) 12 | 13 | const debugCache = false 14 | 15 | func (sl *scalaLang) readScalaRuleCacheFile() error { 16 | t1 := time.Now() 17 | 18 | if err := protobuf.ReadFile(sl.cacheFileFlagValue, &sl.cache); err != nil { 19 | return err 20 | } 21 | 22 | if sl.cacheKeyFlagValue != sl.cache.Key { 23 | if debugCache { 24 | log.Printf("scala-gazelle cache invalidated! (want %q, got %q)", sl.cacheFileFlagValue, sl.cache.Key) 25 | } 26 | sl.cache = scpb.Cache{} 27 | return nil 28 | } 29 | 30 | parser.SortRules(sl.cache.Rules) 31 | 32 | for _, rule := range sl.cache.Rules { 33 | from, err := label.Parse(rule.Label) 34 | if err != nil { 35 | return err 36 | } 37 | if err := sl.parser.LoadScalaRule(from, rule); err != nil { 38 | return err 39 | } 40 | } 41 | 42 | if debugCache { 43 | t2 := time.Since(t1).Round(1 * time.Millisecond) 44 | log.Printf("Read cache %s (%d rules) %v", sl.cacheFileFlagValue, len(sl.cache.Rules), t2) 45 | } 46 | 47 | return nil 48 | } 49 | 50 | func (sl *scalaLang) writeScalaRuleCacheFile() error { 51 | sl.cache.PackageCount = int32(len(sl.packages)) 52 | sl.cache.Rules = sl.parser.ScalaRules() 53 | sl.cache.Key = sl.cacheKeyFlagValue 54 | 55 | if debugCache { 56 | log.Printf("Wrote scala-gazelle cache %s (%d rules)", sl.cacheFileFlagValue, len(sl.cache.Rules)) 57 | } 58 | 59 | return protobuf.WriteFile(sl.cacheFileFlagValue, &sl.cache) 60 | } 61 | -------------------------------------------------------------------------------- /language/scala/configure.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/config" 7 | "github.com/bazelbuild/bazel-gazelle/rule" 8 | "github.com/stackb/scala-gazelle/pkg/scalaconfig" 9 | ) 10 | 11 | // Configure implements part of the language.Language interface 12 | func (sl *scalaLang) Configure(c *config.Config, rel string, f *rule.File) { 13 | sc := scalaconfig.GetOrCreate(sl.logger, sl, c, rel) 14 | if f != nil { 15 | if err := sc.ParseDirectives(f.Directives); err != nil { 16 | log.Fatalf("parsing directives in package %q: %v", rel, err) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /language/scala/conflict_resolver_registry.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/stackb/scala-gazelle/pkg/resolver" 7 | ) 8 | 9 | // GetConflictResolver implements part of the resolver.ConflictResolverRegistry 10 | // interface. 11 | func (sl *scalaLang) GetConflictResolver(name string) (resolver.ConflictResolver, bool) { 12 | r, ok := sl.conflictResolvers[name] 13 | return r, ok 14 | } 15 | 16 | // PutConflictResolver implements part of the resolver.ConflictResolverRegistry 17 | // interface. 18 | func (sl *scalaLang) PutConflictResolver(name string, r resolver.ConflictResolver) error { 19 | if _, ok := sl.conflictResolvers[name]; ok { 20 | return fmt.Errorf("duplicate conflict resolver: %s", name) 21 | } 22 | sl.conflictResolvers[name] = r 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /language/scala/coverage.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | "strings" 7 | 8 | "github.com/stackb/scala-gazelle/pkg/procutil" 9 | ) 10 | 11 | type packageRuleCoverage struct { 12 | // managed represents the total number of rules that are managed by 13 | // scala-gazelle (actual number of rules that we provided deps for) 14 | managed int 15 | // total represents the total number of rules in a package that we have a 16 | // RuleProvider for. 17 | total int 18 | // kinds represents the kinds of rules covered, and how many each has 19 | kinds map[string]int 20 | } 21 | 22 | func (sl *scalaLang) reportCoverage(printf func(format string, v ...any)) { 23 | if !sl.existingScalaRuleCoverageFlagValue { 24 | return 25 | } 26 | 27 | kindTotals := make(map[string]int) 28 | 29 | var managed int 30 | var total int 31 | for _, pkg := range sl.packages { 32 | managed += pkg.ruleCoverage.managed 33 | total += pkg.ruleCoverage.total 34 | for k, v := range pkg.ruleCoverage.kinds { 35 | kindTotals[k] += v 36 | } 37 | } 38 | 39 | kinds := make([]string, 0, len(kindTotals)) 40 | for kind := range kindTotals { 41 | kinds = append(kinds, fmt.Sprintf("%s: %d", kind, kindTotals[kind])) 42 | } 43 | sort.Strings(kinds) 44 | totals := strings.Join(kinds, ", ") 45 | 46 | var percent float32 47 | if total > 0 { 48 | percent = float32(managed) / float32(total) * 100 49 | } 50 | 51 | if procutil.LookupBoolEnv(SCALA_GAZELLE_SHOW_COVERAGE, true) { 52 | printf("scala-gazelle coverage is %0.1f%% (%d/%d) %s", percent, managed, total, totals) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /language/scala/coverage_test.go: -------------------------------------------------------------------------------- 1 | package scala 2 | -------------------------------------------------------------------------------- /language/scala/cross_resolve.go: -------------------------------------------------------------------------------- 1 | package scala 2 | -------------------------------------------------------------------------------- /language/scala/deps_cleaner_registry.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/stackb/scala-gazelle/pkg/resolver" 7 | ) 8 | 9 | // GetDepsCleaner implements part of the resolver.DepsCleanerRegistry 10 | // interface. 11 | func (sl *scalaLang) GetDepsCleaner(name string) (resolver.DepsCleaner, bool) { 12 | r, ok := sl.depsCleaners[name] 13 | return r, ok 14 | } 15 | 16 | // PutDepsCleaner implements part of the resolver.DepsCleanerRegistry 17 | // interface. 18 | func (sl *scalaLang) PutDepsCleaner(name string, r resolver.DepsCleaner) error { 19 | if _, ok := sl.depsCleaners[name]; ok { 20 | return fmt.Errorf("duplicate conflict resolver: %s", name) 21 | } 22 | sl.depsCleaners[name] = r 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /language/scala/environment.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import "github.com/stackb/scala-gazelle/pkg/procutil" 4 | 5 | const ( 6 | SCALA_GAZELLE_LOG_FILE = procutil.EnvVar("SCALA_GAZELLE_LOG_FILE") 7 | SCALA_GAZELLE_SHOW_COVERAGE = procutil.EnvVar("SCALA_GAZELLE_SHOW_COVERAGE") 8 | SCALA_GAZELLE_SHOW_PROGRESS = procutil.EnvVar("SCALA_GAZELLE_SHOW_PROGRESS") 9 | TEST_TMPDIR = procutil.EnvVar("TEST_TMPDIR") 10 | ) 11 | -------------------------------------------------------------------------------- /language/scala/existing_scala_rule_test.go: -------------------------------------------------------------------------------- 1 | package scala 2 | -------------------------------------------------------------------------------- /language/scala/fix.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/config" 5 | "github.com/bazelbuild/bazel-gazelle/rule" 6 | ) 7 | 8 | // Fix implements part of the language.Language interface 9 | func (sl *scalaLang) Fix(c *config.Config, f *rule.File) { 10 | } 11 | -------------------------------------------------------------------------------- /language/scala/generate.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "github.com/bazelbuild/bazel-gazelle/config" 8 | "github.com/bazelbuild/bazel-gazelle/label" 9 | "github.com/bazelbuild/bazel-gazelle/language" 10 | "github.com/stackb/scala-gazelle/pkg/scalaconfig" 11 | ) 12 | 13 | const debugGenerate = false 14 | 15 | // GenerateRules implements part of the language.Language interface 16 | func (sl *scalaLang) GenerateRules(args language.GenerateArgs) (result language.GenerateResult) { 17 | now := time.Now() 18 | 19 | sl.logger.Debug().Msgf("visiting directory %s", args.Rel) 20 | 21 | sc := scalaconfig.Get(args.Config) 22 | if args.File == nil && !sc.GenerateBuildFiles() { 23 | return 24 | } 25 | 26 | if sl.wantProgress && sl.cache.PackageCount > 0 { 27 | writeGenerateProgress(sl.progress, len(sl.packages), int(sl.cache.PackageCount)) 28 | } 29 | 30 | logger := sl.logger.With().Str("rel", args.Rel).Logger() 31 | pkg := newScalaPackage(logger, args, sc, sl.ruleProviderRegistry, sl.parser, sl) 32 | sl.packages[args.Rel] = pkg 33 | sl.remainingPackages++ 34 | 35 | rules := pkg.Rules() 36 | for _, r := range rules { 37 | from := label.Label{Pkg: args.Rel, Name: r.Name()} 38 | sl.PutKnownRule(from, r) 39 | } 40 | 41 | rules = append(rules, generatePackageMarkerRule(len(sl.packages), pkg)) 42 | 43 | imports := make([]interface{}, len(rules)) 44 | for i, r := range rules { 45 | imports[i] = r.PrivateAttr(config.GazelleImportsKey) 46 | } 47 | 48 | if debugGenerate { 49 | t2 := time.Since(now).Round(1 * time.Millisecond) 50 | if len(rules) > 1 { 51 | log.Printf("Visited %q (%d rules, %v)", args.Rel, len(rules)-1, t2) 52 | } 53 | } 54 | 55 | logger.Debug().Msgf("generated %d rules in %v", len(rules), time.Since(now)) 56 | 57 | return language.GenerateResult{ 58 | Gen: rules, 59 | Imports: imports, 60 | Empty: pkg.Empty(), 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /language/scala/golden_test.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/stackb/rules_proto/pkg/goldentest" 8 | "github.com/stackb/scala-gazelle/pkg/collections" 9 | ) 10 | 11 | func TestScala(t *testing.T) { 12 | if val, ok := os.LookupEnv("SCALA_GAZELLE_DEBUG_PROCESS"); false && ok && (val == "1" || val == "true") { 13 | collections.PrintProcessIdForDelveAndWait() 14 | } 15 | goldentest.FromDir("language/scala", 16 | goldentest.WithOnlyTests( 17 | "java_provider", 18 | "maven_provider", 19 | "override_provider", 20 | "protobuf_provider", 21 | "resolve_kind_rewrite_name", 22 | "source_provider", 23 | "scala_fileset", 24 | ), 25 | ).Run(t, "gazelle") 26 | } 27 | -------------------------------------------------------------------------------- /language/scala/imports.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/bazelbuild/bazel-gazelle/label" 10 | scpb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/cache" 11 | "github.com/stackb/scala-gazelle/pkg/protobuf" 12 | ) 13 | 14 | func (sl *scalaLang) writeResolvedImportsMapFile(filename string) error { 15 | imports := &scpb.ResolvedImports{ 16 | Imports: make(map[string]string), 17 | } 18 | 19 | for _, sym := range sl.globalScope.GetSymbols("") { 20 | dep := "NO_LABEL" 21 | if sym.Label != label.NoLabel { 22 | dep = sym.Label.String() 23 | } 24 | imports.Imports[sym.Name] = dep 25 | } 26 | 27 | if filepath.Ext(filename) == ".txt" { 28 | f, err := os.Create(filename) 29 | if err != nil { 30 | return fmt.Errorf("create: %w", err) 31 | } 32 | for k, v := range imports.Imports { 33 | fmt.Fprintln(f, k, v) 34 | } 35 | if err := f.Close(); err != nil { 36 | return fmt.Errorf("close: %w", err) 37 | } 38 | } else { 39 | if err := protobuf.WriteFile(filename, imports); err != nil { 40 | return err 41 | } 42 | } 43 | 44 | log.Printf("Wrote scala-gazelle import map: %s", filename) 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /language/scala/kinds.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/rule" 7 | ) 8 | 9 | // Kinds implements part of the language.Language interface 10 | func (sl *scalaLang) Kinds() map[string]rule.KindInfo { 11 | kinds := make(map[string]rule.KindInfo) 12 | kinds[packageMarkerRuleKind] = rule.KindInfo{ 13 | ResolveAttrs: map[string]bool{"deps": true}, 14 | } 15 | 16 | for _, name := range sl.ruleProviderRegistry.ProviderNames() { 17 | if provider, ok := sl.ruleProviderRegistry.LookupProvider(name); ok { 18 | kinds[provider.Name()] = provider.KindInfo() 19 | } else { 20 | log.Fatal("rule provider not found:", name) 21 | } 22 | } 23 | 24 | return kinds 25 | } 26 | -------------------------------------------------------------------------------- /language/scala/known_rule_registry.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/label" 7 | "github.com/bazelbuild/bazel-gazelle/rule" 8 | ) 9 | 10 | // GetKnownRule implements part of the 11 | // resolver.KnownRuleRegistry interface. 12 | func (sl *scalaLang) GetKnownRule(from label.Label) (*rule.Rule, bool) { 13 | r, ok := sl.knownRules[from] 14 | return r, ok 15 | } 16 | 17 | // PutKnownRule implements part of the 18 | // resolver.KnownRuleRegistry interface. 19 | func (sl *scalaLang) PutKnownRule(from label.Label, r *rule.Rule) error { 20 | if _, ok := sl.knownRules[from]; ok { 21 | return fmt.Errorf("duplicate known rule: %s", from) 22 | } 23 | sl.knownRules[from] = r 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /language/scala/language_test.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import "fmt" 4 | 5 | func ExampleLanguage_Name() { 6 | lang := NewLanguage() 7 | fmt.Println(lang.Name()) 8 | // output: 9 | // scala 10 | } 11 | 12 | func ExampleLanguage_KnownDirectives() { 13 | lang := NewLanguage() 14 | for _, d := range lang.KnownDirectives() { 15 | fmt.Println(d) 16 | } 17 | // output: 18 | // resolve_conflicts 19 | // resolve_file_symbol_name 20 | // resolve_glob 21 | // resolve_kind_rewrite_name 22 | // resolve_with 23 | // scala_debug 24 | // scala_log_level 25 | // scala_deps_cleaner 26 | // scala_keep_unknown_deps 27 | // scala_fix_wildcard_imports 28 | // scala_generate_build_files 29 | // scala_rule 30 | } 31 | -------------------------------------------------------------------------------- /language/scala/lifecycle.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/stackb/scala-gazelle/pkg/resolver" 7 | ) 8 | 9 | // onResolve is called when gazelle transitions from the generate phase to the 10 | // resolve phase 11 | func (sl *scalaLang) onResolve() { 12 | sl.phaseTransition("resolve") 13 | 14 | for _, provider := range sl.symbolProviders { 15 | if err := provider.OnResolve(); err != nil { 16 | log.Fatalf("provider.OnResolve transition error %s: %v", provider.Name(), err) 17 | } 18 | } 19 | 20 | // assign final readonly scala-specific scope 21 | if scalaScope, err := resolver.NewScalaScope(sl.globalScope); err != nil { 22 | sl.logger.Printf("warning: setting up global resolver scope: %v", err) 23 | } else { 24 | sl.globalScope = scalaScope 25 | } 26 | 27 | if sl.cacheFileFlagValue != "" { 28 | if err := sl.writeScalaRuleCacheFile(); err != nil { 29 | log.Fatalf("failed to write cache: %v", err) 30 | } 31 | } 32 | } 33 | 34 | // onEnd is called when the last rule has been resolved. 35 | func (sl *scalaLang) onEnd() { 36 | sl.phaseTransition("end") 37 | 38 | for _, provider := range sl.symbolProviders { 39 | if err := provider.OnEnd(); err != nil { 40 | log.Fatalf("provider.OnEnd transition error %s: %v", provider.Name(), err) 41 | } 42 | } 43 | 44 | sl.cleanup() 45 | sl.dumpResolvedImportMap() 46 | sl.reportCoverage(log.Printf) 47 | sl.stopCpuProfiling() 48 | sl.stopMemoryProfiling() 49 | 50 | if sl.logFile != nil { 51 | sl.logFile.Close() 52 | } 53 | } 54 | 55 | func (sl *scalaLang) phaseTransition(phase string) { 56 | sl.logger.Debug().Msgf("transitioning to phase: %s", phase) 57 | } 58 | -------------------------------------------------------------------------------- /language/scala/loads.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "log" 5 | "sort" 6 | 7 | "github.com/bazelbuild/bazel-gazelle/rule" 8 | ) 9 | 10 | // Loads implements part of the language.Language interface 11 | func (sl *scalaLang) Loads() []rule.LoadInfo { 12 | // Merge symbols 13 | symbolsByLoadName := make(map[string][]string) 14 | 15 | for _, name := range sl.ruleProviderRegistry.ProviderNames() { 16 | provider, ok := sl.ruleProviderRegistry.LookupProvider(name) 17 | if !ok { 18 | log.Fatalf("unknown rule provider: %q", name) 19 | } 20 | load := provider.LoadInfo() 21 | symbolsByLoadName[load.Name] = append(symbolsByLoadName[load.Name], load.Symbols...) 22 | } 23 | 24 | // Ensure names are sorted otherwise order of load statements can be 25 | // non-deterministic 26 | keys := make([]string, 0) 27 | for name := range symbolsByLoadName { 28 | keys = append(keys, name) 29 | } 30 | sort.Strings(keys) 31 | 32 | // Build final load list 33 | loads := make([]rule.LoadInfo, 0) 34 | for _, name := range keys { 35 | symbols := symbolsByLoadName[name] 36 | sort.Strings(symbols) 37 | loads = append(loads, rule.LoadInfo{ 38 | Name: name, 39 | Symbols: symbols, 40 | }) 41 | } 42 | return loads 43 | } 44 | 45 | func fullyQualifiedLoadName(loads []*rule.Load, kind string) string { 46 | for _, load := range loads { 47 | for _, pair := range load.SymbolPairs() { 48 | // when there is no aliasing, pair.From == pair.To, so this covers 49 | // both cases (aliases and not). 50 | if pair.From == pair.To && pair.From == kind { 51 | return load.Name() + "%" + pair.From 52 | } 53 | if pair.To == kind { 54 | return load.Name() + "%" + pair.From 55 | } 56 | } 57 | } 58 | // no match, just return the kind (e.g. native.java_binary) 59 | return kind 60 | } 61 | -------------------------------------------------------------------------------- /language/scala/loads_test.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import "fmt" 4 | 5 | func ExampleLanguage_Loads() { 6 | lang := NewLanguage() 7 | for _, info := range lang.Loads() { 8 | fmt.Printf("%+v\n", info) 9 | } 10 | // output: 11 | // {Name:@build_stack_scala_gazelle//rules:scala_files.bzl Symbols:[scala_files scala_fileset] After:[]} 12 | // {Name:@build_stack_scala_gazelle//rules:semanticdb_index.bzl Symbols:[semanticdb_index] After:[]} 13 | // {Name:@io_bazel_rules_scala//scala:scala.bzl Symbols:[scala_binary scala_library scala_macro_library scala_test] After:[]} 14 | } 15 | -------------------------------------------------------------------------------- /language/scala/package_marker_rule.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/rule" 5 | "github.com/pcj/mobyprogress" 6 | ) 7 | 8 | const packageMarkerRuleKind = "filegroup" 9 | const packageMarkerRuleName = "package_marker" 10 | 11 | // generatePackageMarkerRule creates a dummy rule that forces gazelle to run the 12 | // resolve phase at least once per package; used for tracking progress during 13 | // the resolve phase. 14 | func generatePackageMarkerRule(pkgNum int, pkg *scalaPackage) *rule.Rule { 15 | r := rule.NewRule(packageMarkerRuleKind, packageMarkerRuleName) 16 | r.SetAttr("srcs", []string{}) 17 | r.SetPrivateAttr("n", pkgNum) 18 | r.SetPrivateAttr("pkg", pkg) 19 | return r 20 | } 21 | 22 | // resolvePackageMarkerRule is called when a package marker rule is resolved. 23 | func resolvePackageMarkerRule(output mobyprogress.Output, r *rule.Rule, total int, wantProgress bool) { 24 | current := r.PrivateAttr("n").(int) 25 | if wantProgress { 26 | writeResolveProgress(output, current, total, current == total) 27 | } 28 | 29 | pkg := r.PrivateAttr("pkg").(*scalaPackage) 30 | pkg.Finalize() 31 | 32 | r.Delete() 33 | } 34 | -------------------------------------------------------------------------------- /language/scala/progress.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "github.com/pcj/mobyprogress" 5 | ) 6 | 7 | func writeParseProgress(output mobyprogress.Output, message string) { 8 | output.WriteProgress(mobyprogress.Progress{ 9 | ID: "parse", 10 | Action: "parse", 11 | Message: message, 12 | }) 13 | } 14 | 15 | func writeGenerateProgress(output mobyprogress.Output, current, total int) { 16 | output.WriteProgress(mobyprogress.Progress{ 17 | ID: "walk", 18 | Action: "generating rules", 19 | Current: int64(current), 20 | Total: int64(total), 21 | Units: "packages", 22 | }) 23 | } 24 | 25 | func writeResolveProgress(output mobyprogress.Output, current, total int, lastUpdate bool) { 26 | output.WriteProgress(mobyprogress.Progress{ 27 | ID: "resolve", 28 | Action: "resolving dependencies", 29 | Current: int64(current), 30 | Total: int64(total), 31 | Units: "packages", 32 | LastUpdate: lastUpdate, 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /language/scala/resolve.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/config" 5 | "github.com/bazelbuild/bazel-gazelle/label" 6 | "github.com/bazelbuild/bazel-gazelle/repo" 7 | "github.com/bazelbuild/bazel-gazelle/resolve" 8 | "github.com/bazelbuild/bazel-gazelle/rule" 9 | ) 10 | 11 | // Imports implements part of the language.Language interface 12 | func (sl *scalaLang) Imports(c *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec { 13 | from := label.New("", f.Pkg, r.Name()) 14 | 15 | pkg, ok := sl.packages[from.Pkg] 16 | if !ok { 17 | return nil 18 | } 19 | 20 | provider := pkg.ruleProvider(r) 21 | if provider == nil { 22 | return nil 23 | } 24 | 25 | return provider.Imports(c, r, f) 26 | } 27 | 28 | // Embeds implements part of the language.Language interface 29 | func (*scalaLang) Embeds(r *rule.Rule, from label.Label) []label.Label { return nil } 30 | 31 | // Resolve implements part of the language.Language interface 32 | func (sl *scalaLang) Resolve( 33 | c *config.Config, 34 | ix *resolve.RuleIndex, 35 | rc *repo.RemoteCache, 36 | r *rule.Rule, 37 | importsRaw interface{}, 38 | from label.Label, 39 | ) { 40 | // gazelle supplies the 'from' label fully-qualified (label.Repo is set to 41 | // the current workspace name). However, all the symbols provided are using 42 | // the default workspace, so normalize it here without the repoName to make 43 | // matching simpler. 44 | if from.Repo == c.RepoName { 45 | from.Repo = "" 46 | } 47 | 48 | if !sl.isResolvePhase { 49 | sl.isResolvePhase = true 50 | sl.onResolve() 51 | } 52 | 53 | pkg, ok := sl.packages[from.Pkg] 54 | if !ok { 55 | return 56 | } 57 | 58 | if r.Kind() == packageMarkerRuleKind { 59 | resolvePackageMarkerRule(sl.progress, r, len(sl.packages), sl.wantProgress) 60 | sl.remainingPackages-- 61 | } else { 62 | pkg.Resolve(c, ix, rc, r, importsRaw, from) 63 | } 64 | 65 | if sl.remainingPackages == 0 { 66 | sl.onEnd() 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /language/scala/scala.WORKSPACE: -------------------------------------------------------------------------------- 1 | local_repository( 2 | name = "build_stack_scala_gazelle", 3 | path = "../build_stack_scala_gazelle", 4 | ) 5 | 6 | load("@build_stack_scala_gazelle//:workspace_deps.bzl", "workspace_deps") 7 | 8 | workspace_deps() 9 | 10 | load("@rules_jvm_external//:defs.bzl", "maven_install") 11 | 12 | maven_install( 13 | name = "maven", 14 | artifacts = [ 15 | "xml-apis:xml-apis:1.4.01", 16 | ], 17 | generate_compat_repositories = True, 18 | # maven_install_json = "//:maven_install.json", 19 | repositories = [ 20 | "https://repo.maven.apache.org/maven2/", 21 | ], 22 | version_conflict_policy = "pinned", 23 | ) 24 | 25 | maven_install( 26 | name = "atlassian", 27 | artifacts = [ 28 | "aopalliance:aopalliance:1.0", 29 | ], 30 | generate_compat_repositories = True, 31 | # maven_install_json = "//:atlassian_install.json", 32 | repositories = [ 33 | "https://packages.atlassian.com/maven-external", 34 | ], 35 | version_conflict_policy = "pinned", 36 | ) 37 | 38 | load("@maven//:compat.bzl", "compat_repositories") 39 | 40 | compat_repositories() 41 | 42 | # @atlassian-public workspace used to differentiate deps with @maven 43 | maven_install( 44 | name = "atlassian-public", 45 | artifacts = [ 46 | "junit:junit:4.9", 47 | ], 48 | repositories = ["https://packages.atlassian.com/maven-external"], 49 | ) 50 | 51 | # ---------------------------------------------------- 52 | # Scala 53 | # ---------------------------------------------------- 54 | 55 | load("@io_bazel_rules_scala//:scala_config.bzl", "scala_config") 56 | 57 | scala_config(scala_version = "2.13.2") 58 | 59 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_repositories") 60 | 61 | scala_repositories() 62 | 63 | load("@io_bazel_rules_scala//scala:toolchains.bzl", "scala_register_toolchains") 64 | 65 | scala_register_toolchains() 66 | -------------------------------------------------------------------------------- /language/scala/scala_package_test.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/bazelbuild/bazel-gazelle/config" 8 | "github.com/bazelbuild/bazel-gazelle/rule" 9 | "github.com/google/go-cmp/cmp" 10 | "github.com/rs/zerolog" 11 | "github.com/stretchr/testify/mock" 12 | 13 | sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" 14 | "github.com/stackb/scala-gazelle/pkg/resolver/mocks" 15 | "github.com/stackb/scala-gazelle/pkg/scalaconfig" 16 | ) 17 | 18 | func TestScalaPackageParseRule(t *testing.T) { 19 | for name, tc := range map[string]struct { 20 | rule *rule.Rule 21 | attrName string 22 | wantFiles []*sppb.File 23 | wantErr string 24 | }{ 25 | "degenerate": { 26 | rule: rule.NewRule("scala_library", "somelib"), 27 | wantErr: "rule has no source files", 28 | wantFiles: nil, 29 | }, 30 | } { 31 | t.Run(name, func(t *testing.T) { 32 | logger := zerolog.New(os.Stderr) 33 | universe := mocks.NewUniverse(t) 34 | scope := mocks.NewScope(t) 35 | scope. 36 | On("PutSymbol", mock.Anything). 37 | Maybe(). 38 | Return(nil) 39 | 40 | cfg := scalaconfig.New( 41 | logger, 42 | universe, 43 | config.New(), 44 | "", 45 | ) 46 | 47 | pkg := scalaPackage{ 48 | cfg: cfg, 49 | logger: logger, 50 | } 51 | 52 | var gotErr string 53 | got, gotError := pkg.ParseRule(tc.rule, tc.attrName) 54 | if gotError != nil { 55 | gotErr = gotError.Error() 56 | } 57 | 58 | if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" { 59 | t.Errorf("error (-want +got):\n%s", diff) 60 | } 61 | if diff := cmp.Diff(tc.wantFiles, got.Files()); diff != "" { 62 | t.Errorf("(-want +got):\n%s", diff) 63 | } 64 | }) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /language/scala/scope.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" 5 | "github.com/stackb/scala-gazelle/pkg/resolver" 6 | ) 7 | 8 | // GetScope implements part of the resolver.Scope interface. 9 | func (sl *scalaLang) GetScope(imp string) (resolver.Scope, bool) { 10 | return sl.globalScope.GetScope(imp) 11 | } 12 | 13 | // GetSymbol implements part of the resolver.Scope interface. 14 | func (sl *scalaLang) GetSymbol(imp string) (*resolver.Symbol, bool) { 15 | return sl.globalScope.GetSymbol(imp) 16 | } 17 | 18 | // GetSymbols implements part of the resolver.Scope interface. 19 | func (sl *scalaLang) GetSymbols(prefix string) []*resolver.Symbol { 20 | return sl.globalScope.GetSymbols(prefix) 21 | } 22 | 23 | // PutSymbol implements part of the resolver.Scope interface. 24 | func (sl *scalaLang) PutSymbol(symbol *resolver.Symbol) error { 25 | // collect package symbols and put them in a separate container that 26 | // resolves only if everything else fails. 27 | if symbol.Type == sppb.ImportType_PACKAGE { 28 | if err := sl.globalPackages.PutSymbol(symbol); err != nil { 29 | return err 30 | } 31 | return sl.globalPackages.PutSymbol(symbol) 32 | } 33 | return sl.globalScope.PutSymbol(symbol) 34 | } 35 | 36 | // String implements the fmt.Stringer interface. 37 | func (sl *scalaLang) String() string { 38 | return sl.globalScope.String() 39 | } 40 | -------------------------------------------------------------------------------- /language/scala/symbol_provider_registry.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import "github.com/stackb/scala-gazelle/pkg/resolver" 4 | 5 | // SymbolProviders implements part of the 6 | // resolver.SymbolProviderRegistry interface. 7 | func (sl *scalaLang) SymbolProviders() []resolver.SymbolProvider { 8 | return resolver.GlobalSymbolProviderRegistry().SymbolProviders() 9 | } 10 | 11 | // AddSymbolProvider implements part of the 12 | // resolver.SymbolProviderRegistry interface. 13 | func (sl *scalaLang) AddSymbolProvider(provider resolver.SymbolProvider) error { 14 | sl.logger.Debug().Msgf("adding symbol provider: %s", provider.Name()) 15 | return resolver.GlobalSymbolProviderRegistry().AddSymbolProvider(provider) 16 | } 17 | -------------------------------------------------------------------------------- /language/scala/symbol_resolver.go: -------------------------------------------------------------------------------- 1 | package scala 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/config" 5 | "github.com/bazelbuild/bazel-gazelle/label" 6 | "github.com/bazelbuild/bazel-gazelle/resolve" 7 | 8 | "github.com/stackb/scala-gazelle/pkg/resolver" 9 | ) 10 | 11 | // newUniverseResolver constructs the top-level symbol resolver. 12 | func newUniverseResolver(scope resolver.Scope, packages resolver.Scope) resolver.SymbolResolver { 13 | chain := resolver.NewChainSymbolResolver( 14 | // override resolver is the least performant! 15 | resolver.NewMemoSymbolResolver(resolver.NewOverrideSymbolResolver(scalaLangName)), 16 | resolver.NewScopeSymbolResolver(scope), 17 | resolver.NewCrossSymbolResolver(scalaLangName), 18 | resolver.NewScalaSymbolResolver(resolver.NewScopeSymbolResolver(packages)), 19 | ) 20 | scala := resolver.NewScalaSymbolResolver(chain) 21 | return resolver.NewMemoSymbolResolver(scala) 22 | } 23 | 24 | // ResolveSymbol implements the resolver.SymbolResolver interface. 25 | func (sl *scalaLang) ResolveSymbol(c *config.Config, ix *resolve.RuleIndex, from label.Label, lang string, imp string) (*resolver.Symbol, bool) { 26 | return sl.symbolResolver.ResolveSymbol(c, ix, from, lang, imp) 27 | } 28 | -------------------------------------------------------------------------------- /language/scala/testdata/java_provider/.bazelrc: -------------------------------------------------------------------------------- 1 | build --incompatible_java_common_parameters=false 2 | -------------------------------------------------------------------------------- /language/scala/testdata/java_provider/.gazelle.args: -------------------------------------------------------------------------------- 1 | -scala_symbol_provider=source 2 | -scala_symbol_provider=java 3 | -javaindex_file=./javaindex.json 4 | -------------------------------------------------------------------------------- /language/scala/testdata/java_provider/BUILD.in: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary") 2 | 3 | # gazelle:scala_rule scala_binary implementation @io_bazel_rules_scala//scala:scala.bzl%scala_binary 4 | # gazelle:scala_debug imports 5 | 6 | scala_binary( 7 | name = "app", 8 | srcs = ["Main.scala"], 9 | ) 10 | -------------------------------------------------------------------------------- /language/scala/testdata/java_provider/BUILD.out: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary") 2 | 3 | # gazelle:scala_rule scala_binary implementation @io_bazel_rules_scala//scala:scala.bzl%scala_binary 4 | # gazelle:scala_debug imports 5 | 6 | scala_binary( 7 | name = "app", 8 | # import: ✅ java.util.Map NO-LABEL (DIRECT of Main.scala) 9 | srcs = ["Main.scala"], 10 | ) 11 | -------------------------------------------------------------------------------- /language/scala/testdata/java_provider/Main.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | import java.util.Map 4 | 5 | object Main { 6 | } 7 | -------------------------------------------------------------------------------- /language/scala/testdata/java_provider/WORKSPACE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/language/scala/testdata/java_provider/WORKSPACE -------------------------------------------------------------------------------- /language/scala/testdata/maven_provider/.bazelrc: -------------------------------------------------------------------------------- 1 | build --incompatible_java_common_parameters=false 2 | -------------------------------------------------------------------------------- /language/scala/testdata/maven_provider/.gazelle.args: -------------------------------------------------------------------------------- 1 | -scala_symbol_provider=source 2 | -scala_symbol_provider=maven 3 | -maven_install_json_file=./maven_install.json 4 | -------------------------------------------------------------------------------- /language/scala/testdata/maven_provider/BUILD.in: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library") 2 | 3 | # gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library 4 | # gazelle:scala_debug imports dep_label_origin 5 | # gazelle:resolve_with scala javax.xml._ javax.inject 6 | # gazelle:scala_keep_unknown_deps true 7 | 8 | scala_library( 9 | name = "app", 10 | srcs = ["Main.scala"], 11 | deps = [ 12 | # junit is required for compilation but we haven't specified 13 | # that @atlassian-public deps should be managed by the scala-gazelle 14 | # extension (this is left alone despite no # keep directive because of 15 | # scala_keep_unknown_deps) 16 | "@atlassian-public//:junit_junit", 17 | ], 18 | ) 19 | -------------------------------------------------------------------------------- /language/scala/testdata/maven_provider/BUILD.out: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library") 2 | 3 | # gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library 4 | # gazelle:scala_debug imports dep_label_origin 5 | # gazelle:resolve_with scala javax.xml._ javax.inject 6 | # gazelle:scala_keep_unknown_deps true 7 | 8 | scala_library( 9 | name = "app", 10 | # import: ✅ javax.xml._ @maven//:xml_apis_xml_apis (DIRECT of Main.scala) 11 | # import: ❌ org.junit.rules.TemporaryFolder symbol not found (DIRECT of Main.scala) 12 | # import: ✅ javax.inject @maven//:javax_inject_javax_inject (IMPLICIT via "javax.xml._") 13 | srcs = ["Main.scala"], 14 | deps = [ 15 | # junit is required for compilation but we haven't specified 16 | # that @atlassian-public deps should be managed by the scala-gazelle 17 | # extension (this is left alone despite no # keep directive because of 18 | # scala_keep_unknown_deps) 19 | "@atlassian-public//:junit_junit", 20 | "@maven//:javax_inject_javax_inject", # IMPLICIT (maven javax.inject ) 21 | "@maven//:xml_apis_xml_apis", # DIRECT (maven javax.xml Main.scala) 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /language/scala/testdata/maven_provider/Main.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | import javax.xml._ 4 | import org.junit.rules.TemporaryFolder 5 | 6 | object Main { 7 | def main(args: Array[String]): Unit = { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /language/scala/testdata/maven_provider/WORKSPACE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/language/scala/testdata/maven_provider/WORKSPACE -------------------------------------------------------------------------------- /language/scala/testdata/override_provider/.bazelrc: -------------------------------------------------------------------------------- 1 | build --incompatible_java_common_parameters=false 2 | -------------------------------------------------------------------------------- /language/scala/testdata/override_provider/.gazelle.args: -------------------------------------------------------------------------------- 1 | -scala_symbol_provider=source 2 | -------------------------------------------------------------------------------- /language/scala/testdata/override_provider/BUILD.in: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library") 2 | 3 | # gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library 4 | # gazelle:resolve scala scala com.typesafe.scalalogging.LazyLogging @maven//:com_typesafe_scala_logging_scala_logging_2_12 5 | # gazelle:resolve scala scala org.slf4j.Logger @maven//:org_slf4j_slf4j_api 6 | # gazelle:resolve_with scala com.typesafe.scalalogging.LazyLogging org.slf4j.Logger 7 | # gazelle:scala_debug imports 8 | 9 | scala_library( 10 | name = "app", 11 | srcs = ["Main.scala"], 12 | deps = [ 13 | # demonstrate that scala-gazelle removes un-necessary 'keep' directives 14 | "@maven//:com_typesafe_scala_logging_scala_logging_2_12", # keep 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /language/scala/testdata/override_provider/BUILD.out: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library") 2 | 3 | # gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library 4 | # gazelle:resolve scala scala com.typesafe.scalalogging.LazyLogging @maven//:com_typesafe_scala_logging_scala_logging_2_12 5 | # gazelle:resolve scala scala org.slf4j.Logger @maven//:org_slf4j_slf4j_api 6 | # gazelle:resolve_with scala com.typesafe.scalalogging.LazyLogging org.slf4j.Logger 7 | # gazelle:scala_debug imports 8 | 9 | scala_library( 10 | name = "app", 11 | # import: ✅ com.typesafe.scalalogging.LazyLogging @maven//:com_typesafe_scala_logging_scala_logging_2_12 (DIRECT of Main.scala) 12 | # import: ✅ org.slf4j.Logger @maven//:org_slf4j_slf4j_api (IMPLICIT via "com.typesafe.scalalogging.LazyLogging") 13 | srcs = ["Main.scala"], 14 | deps = [ 15 | "@maven//:com_typesafe_scala_logging_scala_logging_2_12", # DIRECT 16 | "@maven//:org_slf4j_slf4j_api", # IMPLICIT 17 | ], 18 | ) 19 | -------------------------------------------------------------------------------- /language/scala/testdata/override_provider/Main.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | import com.typesafe.scalalogging.LazyLogging 4 | 5 | object Main {} 6 | -------------------------------------------------------------------------------- /language/scala/testdata/override_provider/WORKSPACE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/language/scala/testdata/override_provider/WORKSPACE -------------------------------------------------------------------------------- /language/scala/testdata/protobuf_provider/.bazelrc: -------------------------------------------------------------------------------- 1 | build --incompatible_java_common_parameters=false 2 | -------------------------------------------------------------------------------- /language/scala/testdata/protobuf_provider/.gazelle.args: -------------------------------------------------------------------------------- 1 | -scala_symbol_provider=source 2 | -scala_symbol_provider=protobuf 3 | -maven_install_json_file=./maven_install.json 4 | -------------------------------------------------------------------------------- /language/scala/testdata/protobuf_provider/BUILD.in: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library") 2 | 3 | # gazelle:proto_rule proto_compile implementation stackb:rules_proto:proto_compile 4 | # gazelle:proto_plugin protoc-gen-scala implementation scalapb:scalapb:protoc-gen-scala 5 | # gazelle:proto_rule proto_scala_library implementation stackb:rules_proto:proto_scala_library 6 | # gazelle:proto_rule proto_scala_library option --plugins=protoc-gen-scala 7 | # gazelle:proto_rule proto_scala_library visibility //visibility:public 8 | # gazelle:proto_language scala plugin protoc-gen-scala 9 | # gazelle:proto_language scala rule proto_compile 10 | # gazelle:proto_language scala rule proto_scala_library 11 | 12 | # gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library 13 | # gazelle:scala_debug imports 14 | 15 | scala_library( 16 | name = "app", 17 | srcs = ["Main.scala"], 18 | ) 19 | -------------------------------------------------------------------------------- /language/scala/testdata/protobuf_provider/BUILD.out: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library") 2 | 3 | # gazelle:proto_rule proto_compile implementation stackb:rules_proto:proto_compile 4 | # gazelle:proto_plugin protoc-gen-scala implementation scalapb:scalapb:protoc-gen-scala 5 | # gazelle:proto_rule proto_scala_library implementation stackb:rules_proto:proto_scala_library 6 | # gazelle:proto_rule proto_scala_library option --plugins=protoc-gen-scala 7 | # gazelle:proto_rule proto_scala_library visibility //visibility:public 8 | # gazelle:proto_language scala plugin protoc-gen-scala 9 | # gazelle:proto_language scala rule proto_compile 10 | # gazelle:proto_language scala rule proto_scala_library 11 | 12 | # gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library 13 | # gazelle:scala_debug imports 14 | 15 | scala_library( 16 | name = "app", 17 | # import: ❌ not.Exists symbol not found (DIRECT of Main.scala) 18 | # import: ✅ proto.Customer //proto:proto_proto_scala_library (DIRECT of Main.scala) 19 | srcs = ["Main.scala"], 20 | deps = [ 21 | "//proto:proto_proto_scala_library", # DIRECT 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /language/scala/testdata/protobuf_provider/Main.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | // proto.Customer should require //proto:proto_proto_scala_library 4 | import proto.Customer 5 | // not.Exists should be annotated as unresolved 6 | import not.Exists 7 | 8 | object Main { 9 | def main(args: Array[String]): Unit = { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /language/scala/testdata/protobuf_provider/WORKSPACE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/language/scala/testdata/protobuf_provider/WORKSPACE -------------------------------------------------------------------------------- /language/scala/testdata/protobuf_provider/proto/BUILD.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/language/scala/testdata/protobuf_provider/proto/BUILD.in -------------------------------------------------------------------------------- /language/scala/testdata/protobuf_provider/proto/BUILD.out: -------------------------------------------------------------------------------- 1 | load("@rules_proto//proto:defs.bzl", "proto_library") 2 | load("@build_stack_rules_proto//rules/scala:proto_scala_library.bzl", "proto_scala_library") 3 | load("@build_stack_rules_proto//rules:proto_compile.bzl", "proto_compile") 4 | 5 | proto_library( 6 | name = "proto_proto", 7 | srcs = ["customer.proto"], 8 | visibility = ["//visibility:public"], 9 | ) 10 | 11 | proto_compile( 12 | name = "proto_scala_compile", 13 | outputs = ["proto_scala.srcjar"], 14 | plugins = ["@build_stack_rules_proto//plugin/scalapb/scalapb:protoc-gen-scala"], 15 | proto = "proto_proto", 16 | ) 17 | 18 | proto_scala_library( 19 | name = "proto_proto_scala_library", 20 | srcs = ["proto_scala.srcjar"], 21 | visibility = ["//visibility:public"], 22 | ) 23 | -------------------------------------------------------------------------------- /language/scala/testdata/protobuf_provider/proto/customer.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package proto; 4 | 5 | message Customer { 6 | string name = 1; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/.bazelrc: -------------------------------------------------------------------------------- 1 | build --incompatible_java_common_parameters=false 2 | -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/.gazelle.args: -------------------------------------------------------------------------------- 1 | -scala_symbol_provider=source 2 | -existing_scala_library_rule=//rules:scala.bzl%scala_helper_library 3 | -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/BUILD.in: -------------------------------------------------------------------------------- 1 | # gazelle:scala_rule scala_helper_library implementation //rules:scala.bzl%scala_helper_library 2 | # gazelle:scala_rule scala_binary implementation @io_bazel_rules_scala//scala:scala.bzl%scala_binary 3 | # gazelle:resolve_kind_rewrite_name scala_helper_library %{name} %{name}_helper 4 | -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/BUILD.out: -------------------------------------------------------------------------------- 1 | # gazelle:scala_rule scala_helper_library implementation //rules:scala.bzl%scala_helper_library 2 | # gazelle:scala_rule scala_binary implementation @io_bazel_rules_scala//scala:scala.bzl%scala_binary 3 | # gazelle:resolve_kind_rewrite_name scala_helper_library %{name} %{name}_helper 4 | -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/WORKSPACE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/language/scala/testdata/resolve_kind_rewrite_name/WORKSPACE -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/app/App.scala: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import lib.Helper 4 | 5 | object App { 6 | def main(args: Array[String]): Unit = {} 7 | } 8 | -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/app/BUILD.in: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary", "scala_library", "scala_test") 2 | 3 | scala_binary( 4 | name = "app", 5 | main_class= "app.App", 6 | srcs = ["App.scala"], 7 | ) 8 | -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/app/BUILD.out: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary") 2 | 3 | scala_binary( 4 | name = "app", 5 | srcs = ["App.scala"], 6 | main_class = "app.App", 7 | deps = [ 8 | "//lib:lib_helper", # DIRECT 9 | ], 10 | ) 11 | -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/lib/BUILD.in: -------------------------------------------------------------------------------- 1 | load("//rules:scala.bzl", "scala_helper_library") 2 | 3 | scala_helper_library( 4 | name = "lib", 5 | srcs = ["Helper.scala"], 6 | ) 7 | -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/lib/BUILD.out: -------------------------------------------------------------------------------- 1 | load("//rules:scala.bzl", "scala_helper_library") 2 | 3 | scala_helper_library( 4 | name = "lib", 5 | srcs = ["Helper.scala"], 6 | ) 7 | -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/lib/Helper.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | object Helper { 4 | } 5 | -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/rules/BUILD.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/language/scala/testdata/resolve_kind_rewrite_name/rules/BUILD.in -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/rules/BUILD.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/language/scala/testdata/resolve_kind_rewrite_name/rules/BUILD.out -------------------------------------------------------------------------------- /language/scala/testdata/resolve_kind_rewrite_name/rules/scala.bzl: -------------------------------------------------------------------------------- 1 | """custom scala rules 2 | """ 3 | 4 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library") 5 | 6 | def scala_helper_library(**kwargs): 7 | """scala_helper_library is a contrived rule that helps demonstrate the resolve_kind_rewrite_name directive. 8 | 9 | Args: 10 | **kwargs: must contain 'name' and 'srcs' 11 | """ 12 | 13 | name = kwargs.pop("name") 14 | srcs = kwargs.pop("srcs") 15 | helper_name = name + "_helper" 16 | 17 | filegroup( 18 | name = name, 19 | srcs = srcs, 20 | ) 21 | 22 | scala_library( 23 | name = helper_name, 24 | srcs = srcs, 25 | ) 26 | -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/.bazelrc: -------------------------------------------------------------------------------- 1 | build --incompatible_java_common_parameters=false 2 | -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/.gazelle.args: -------------------------------------------------------------------------------- 1 | -scala_symbol_provider=source 2 | -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/BUILD.in: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:scala_files.bzl", "scala_fileset") 2 | 3 | # gazelle:resolve scala scala java.util.LinkedList @java//util 4 | # gazelle:scala_debug exports 5 | # gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library 6 | # gazelle:scala_rule scala_binary implementation @io_bazel_rules_scala//scala:scala.bzl%scala_binary 7 | # gazelle:scala_rule @build_stack_scala_gazelle//rules:scala_files.bzl%scala_files enabled true 8 | # gazelle:scala_rule @build_stack_scala_gazelle//rules:scala_files.bzl%scala_fileset enabled true 9 | 10 | scala_fileset( 11 | name = "scala_fileset", 12 | ) 13 | -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/BUILD.out: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:scala_files.bzl", "scala_files", "scala_fileset") 2 | 3 | # gazelle:resolve scala scala java.util.LinkedList @java//util 4 | # gazelle:scala_debug exports 5 | # gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library 6 | # gazelle:scala_rule scala_binary implementation @io_bazel_rules_scala//scala:scala.bzl%scala_binary 7 | # gazelle:scala_rule @build_stack_scala_gazelle//rules:scala_files.bzl%scala_files enabled true 8 | # gazelle:scala_rule @build_stack_scala_gazelle//rules:scala_files.bzl%scala_fileset enabled true 9 | 10 | scala_fileset( 11 | name = "scala_fileset", 12 | deps = [ 13 | "//:scala_files", 14 | "//src:scala_files", 15 | ], 16 | ) 17 | 18 | scala_files( 19 | name = "scala_files", 20 | srcs = ["Main.scala"], 21 | tags = ["manual"], 22 | visibility = ["//visibility:public"], 23 | ) 24 | -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/Main.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | object Main { 4 | def main(args: Array[String]): Unit = { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/WORKSPACE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/language/scala/testdata/scala_fileset/WORKSPACE -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/maven_install.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/src/BUILD.in: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary", "scala_library", "scala_test") 2 | 3 | scala_library( 4 | name = "lib", 5 | srcs = ["a/b/c/Lib.scala"], 6 | ) 7 | 8 | scala_binary( 9 | name = "app", 10 | main_class= "a.b.c.Main", 11 | srcs = ["a/b/c/Main.scala"], 12 | ) 13 | 14 | scala_test( 15 | name = "test", 16 | srcs = ["a/b/c/LibTest.scala"], 17 | deps = [":lib"], 18 | ) 19 | -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/src/BUILD.out: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:scala_files.bzl", "scala_files") 2 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary", "scala_library", "scala_test") 3 | 4 | scala_library( 5 | name = "lib", 6 | srcs = ["a/b/c/Lib.scala"], 7 | exports = [ 8 | "@java//util", # EXTENDS 9 | ], 10 | deps = [ 11 | "@java//util", # DIRECT 12 | ], 13 | ) 14 | 15 | scala_binary( 16 | name = "app", 17 | srcs = ["a/b/c/Main.scala"], 18 | main_class = "a.b.c.Main", 19 | deps = [ 20 | ":lib", # DIRECT 21 | ], 22 | ) 23 | 24 | scala_test( 25 | name = "test", 26 | srcs = ["a/b/c/LibTest.scala"], 27 | deps = [":lib"], 28 | ) 29 | 30 | scala_files( 31 | name = "scala_files", 32 | srcs = [ 33 | "a/b/c/Lib.scala", 34 | "a/b/c/Main.scala", 35 | ], 36 | tags = ["manual"], 37 | visibility = ["//visibility:public"], 38 | ) -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/src/a/b/c/Lib.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | import java.util.LinkedList; 4 | 5 | object Lib extends LinkedList { 6 | } 7 | -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/src/a/b/c/LibTest.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | import a.b.c.Lib 4 | 5 | class LibTest { 6 | } 7 | -------------------------------------------------------------------------------- /language/scala/testdata/scala_fileset/src/a/b/c/Main.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | import a.b.c.Lib 4 | 5 | object Main { 6 | def main(args: Array[String]): Unit = { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/.bazelrc: -------------------------------------------------------------------------------- 1 | build --incompatible_java_common_parameters=false 2 | -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/.gazelle.args: -------------------------------------------------------------------------------- 1 | -scala_symbol_provider=source 2 | -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/BUILD.in: -------------------------------------------------------------------------------- 1 | # gazelle:resolve scala scala java.util.LinkedList @java//util 2 | # gazelle:scala_debug exports 3 | # gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library 4 | # gazelle:scala_rule scala_binary implementation @io_bazel_rules_scala//scala:scala.bzl%scala_binary 5 | -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/BUILD.out: -------------------------------------------------------------------------------- 1 | # gazelle:resolve scala scala java.util.LinkedList @java//util 2 | # gazelle:scala_debug exports 3 | # gazelle:scala_rule scala_library implementation @io_bazel_rules_scala//scala:scala.bzl%scala_library 4 | # gazelle:scala_rule scala_binary implementation @io_bazel_rules_scala//scala:scala.bzl%scala_binary 5 | -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/Main.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | object Main { 4 | def main(args: Array[String]): Unit = { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/WORKSPACE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackb/scala-gazelle/49a650ca298f133d9479ff701ea16a7c97e457fe/language/scala/testdata/source_provider/WORKSPACE -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/maven_install.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/src/BUILD.in: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary", "scala_library", "scala_test") 2 | 3 | scala_library( 4 | name = "lib", 5 | srcs = ["a/b/c/Lib.scala"], 6 | ) 7 | 8 | scala_binary( 9 | name = "app", 10 | main_class= "a.b.c.Main", 11 | srcs = ["a/b/c/Main.scala"], 12 | ) 13 | 14 | scala_test( 15 | name = "test", 16 | srcs = ["a/b/c/LibTest.scala"], 17 | deps = [":lib"], 18 | ) 19 | -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/src/BUILD.out: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary", "scala_library", "scala_test") 2 | 3 | scala_library( 4 | name = "lib", 5 | srcs = ["a/b/c/Lib.scala"], 6 | exports = [ 7 | "@java//util", # EXTENDS 8 | ], 9 | deps = [ 10 | "@java//util", # DIRECT 11 | ], 12 | ) 13 | 14 | scala_binary( 15 | name = "app", 16 | srcs = ["a/b/c/Main.scala"], 17 | main_class = "a.b.c.Main", 18 | deps = [ 19 | ":lib", # DIRECT 20 | ], 21 | ) 22 | 23 | scala_test( 24 | name = "test", 25 | srcs = ["a/b/c/LibTest.scala"], 26 | deps = [":lib"], 27 | ) 28 | -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/src/a/b/c/Lib.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | import java.util.LinkedList; 4 | 5 | object Lib extends LinkedList { 6 | } 7 | -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/src/a/b/c/LibTest.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | import a.b.c.Lib 4 | 5 | class LibTest { 6 | } 7 | -------------------------------------------------------------------------------- /language/scala/testdata/source_provider/src/a/b/c/Main.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | import a.b.c.Lib 4 | 5 | object Main { 6 | def main(args: Array[String]): Unit = { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /pkg/autokeep/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 3 | 4 | go_library( 5 | name = "autokeep", 6 | srcs = [ 7 | "deps.go", 8 | "scanner.go", 9 | ], 10 | importpath = "github.com/stackb/scala-gazelle/pkg/autokeep", 11 | visibility = ["//visibility:public"], 12 | deps = [ 13 | "//build/stack/gazelle/scala/autokeep", 14 | "//build/stack/gazelle/scala/cache", 15 | "//pkg/protobuf", 16 | "@bazel_gazelle//label:go_default_library", 17 | "@bazel_gazelle//rule:go_default_library", 18 | "@com_github_bazelbuild_buildtools//build:go_default_library", 19 | ], 20 | ) 21 | 22 | go_test( 23 | name = "autokeep_test", 24 | srcs = [ 25 | "deps_test.go", 26 | "scanner_test.go", 27 | ], 28 | embed = [":autokeep"], 29 | deps = [ 30 | "//build/stack/gazelle/scala/autokeep", 31 | "//pkg/testutil", 32 | "@bazel_gazelle//testtools:go_default_library", 33 | "@com_github_google_go_cmp//cmp", 34 | "@com_github_google_go_cmp//cmp/cmpopts", 35 | ], 36 | ) 37 | 38 | package_filegroup( 39 | name = "filegroup", 40 | srcs = [ 41 | "BUILD.bazel", 42 | "deps.go", 43 | "deps_test.go", 44 | "scanner.go", 45 | "scanner_test.go", 46 | ], 47 | visibility = ["//visibility:public"], 48 | ) 49 | -------------------------------------------------------------------------------- /pkg/bazel/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | 4 | go_library( 5 | name = "bazel", 6 | srcs = ["bazel.go"], 7 | importpath = "github.com/stackb/scala-gazelle/pkg/bazel", 8 | visibility = ["//visibility:public"], 9 | deps = [ 10 | "//pkg/procutil", 11 | "@io_bazel_rules_go//go/tools/bazel:go_default_library", 12 | ], 13 | ) 14 | 15 | package_filegroup( 16 | name = "filegroup", 17 | srcs = [ 18 | "BUILD.bazel", 19 | "bazel.go", 20 | ], 21 | visibility = ["//visibility:public"], 22 | ) 23 | -------------------------------------------------------------------------------- /pkg/bazel/bazel.go: -------------------------------------------------------------------------------- 1 | package bazel 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "path" 7 | "regexp" 8 | 9 | "github.com/bazelbuild/rules_go/go/tools/bazel" 10 | "github.com/stackb/scala-gazelle/pkg/procutil" 11 | ) 12 | 13 | // the name of an environment variable at runtime 14 | const TEST_TMPDIR = "TEST_TMPDIR" 15 | 16 | var ( 17 | FindBinary = bazel.FindBinary 18 | ListRunfiles = bazel.ListRunfiles 19 | nonWordRe = regexp.MustCompile(`\W+`) 20 | ) 21 | 22 | func CleanupLabel(in string) string { 23 | return nonWordRe.ReplaceAllString(in, "_") 24 | } 25 | 26 | // NewTmpDir creates a new temporary directory in TestTmpDir(). 27 | func NewTmpDir(prefix string) (string, error) { 28 | if tmp, ok := os.LookupEnv(TEST_TMPDIR); ok { 29 | err := os.MkdirAll(path.Join(tmp, prefix), 0700) 30 | return tmp, err 31 | } 32 | return os.MkdirTemp("", prefix) 33 | } 34 | 35 | func ExecCommand(bazelExe, command string, labels ...string) ([]byte, int, error) { 36 | args := append([]string{command}, labels...) 37 | 38 | cmd := exec.Command(bazelExe, args...) 39 | cmd.Dir = GetBuildWorkspaceDirectory() 40 | 41 | // log.Println("🧱", cmd.String()) 42 | output, err := cmd.CombinedOutput() 43 | exitCode := procutil.CmdExitCode(cmd, err) 44 | 45 | return output, exitCode, err 46 | } 47 | 48 | func GetBuildWorkspaceDirectory() string { 49 | if bwd, ok := os.LookupEnv("BUILD_WORKSPACE_DIRECTORY"); ok { 50 | return bwd 51 | } else { 52 | return "." 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /pkg/collections/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | 4 | go_library( 5 | name = "collections", 6 | srcs = [ 7 | "box_type.go", 8 | "debugging.go", 9 | "fs.go", 10 | "intent.go", 11 | "net.go", 12 | "path_trie.go", 13 | "sha256.go", 14 | "slice.go", 15 | "string_slice.go", 16 | "string_stack.go", 17 | "uint32stack.go", 18 | ], 19 | importpath = "github.com/stackb/scala-gazelle/pkg/collections", 20 | visibility = ["//visibility:public"], 21 | deps = ["@com_github_dghubble_trie//:trie"], 22 | ) 23 | 24 | package_filegroup( 25 | name = "filegroup", 26 | srcs = [ 27 | "BUILD.bazel", 28 | "box_type.go", 29 | "debugging.go", 30 | "fs.go", 31 | "intent.go", 32 | "net.go", 33 | "path_trie.go", 34 | "sha256.go", 35 | "slice.go", 36 | "string_slice.go", 37 | "string_stack.go", 38 | "uint32stack.go", 39 | ], 40 | visibility = ["//visibility:public"], 41 | ) 42 | -------------------------------------------------------------------------------- /pkg/collections/box_type.go: -------------------------------------------------------------------------------- 1 | // copied from https://github.com/Tufin/asciitree; Apache 2 2 | package collections 3 | 4 | type boxType int 5 | 6 | const ( 7 | Regular boxType = iota 8 | Last 9 | AfterLast 10 | Between 11 | ) 12 | 13 | func (boxType boxType) String() string { 14 | switch boxType { 15 | case Regular: 16 | return "\u251c" // ├ 17 | case Last: 18 | return "\u2514" // └ 19 | case AfterLast: 20 | return " " 21 | case Between: 22 | return "\u2502" // │ 23 | default: 24 | panic("invalid box type") 25 | } 26 | } 27 | 28 | func getBoxType(index int, len int) boxType { 29 | if index+1 == len { 30 | return Last 31 | } else if index+1 > len { 32 | return AfterLast 33 | } 34 | return Regular 35 | } 36 | 37 | func getBoxTypeExternal(index int, len int) boxType { 38 | if index+1 == len { 39 | return AfterLast 40 | } 41 | return Between 42 | } 43 | 44 | func getBoxPadding(root bool, boxType boxType) string { 45 | if root { 46 | return "" 47 | } 48 | 49 | return boxType.String() + " " 50 | } 51 | -------------------------------------------------------------------------------- /pkg/collections/debugging.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func PrintProcessIdForDelveAndWait() { 9 | fmt.Printf("Debugging session requested (Process ID: %d)\n", os.Getpid()) 10 | fmt.Printf("dlv attach --headless --listen=:2345 %d\n", os.Getpid()) 11 | fmt.Println("Press ENTER to continue.") 12 | fmt.Scanln() 13 | } 14 | -------------------------------------------------------------------------------- /pkg/collections/intent.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import "strings" 4 | 5 | // Intent represents an action for an attribute name or "value" optionally 6 | // prefixed by a '+' or '-'. If the prefix is missing, the Intent is not 7 | // negative. 8 | type Intent struct { 9 | Value string 10 | Want bool 11 | } 12 | 13 | func ParseIntent(value string) *Intent { 14 | value = strings.TrimSpace(value) 15 | negative := strings.HasPrefix(value, "-") 16 | positive := strings.HasPrefix(value, "+") 17 | if negative || positive { 18 | value = value[1:] 19 | } 20 | return &Intent{Value: value, Want: !negative} 21 | } 22 | -------------------------------------------------------------------------------- /pkg/collections/net.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | const connectionAvailableDuration = 250 * time.Millisecond 12 | 13 | // WaitForConnectionAvailable pings a tcp connection every 250 milliseconds 14 | // until it connects and returns true. If it fails to connect by the timeout 15 | // deadline, returns false. 16 | func WaitForConnectionAvailable(host string, port int, timeout time.Duration, progress bool) bool { 17 | target := net.JoinHostPort(host, fmt.Sprintf("%d", port)) 18 | var wg sync.WaitGroup 19 | wg.Add(1) 20 | 21 | success := make(chan bool, 1) 22 | 23 | go func() { 24 | go func() { 25 | defer wg.Done() 26 | for { 27 | _, err := net.Dial("tcp", target) 28 | if err == nil { 29 | break 30 | } 31 | if progress { 32 | log.Println(err) 33 | } 34 | time.Sleep(connectionAvailableDuration) 35 | } 36 | }() 37 | wg.Wait() 38 | success <- true 39 | }() 40 | 41 | select { 42 | case <-success: 43 | return true 44 | case <-time.After(timeout): 45 | return false 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /pkg/collections/sha256.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "io" 7 | "os" 8 | ) 9 | 10 | // Sha256 computes the sha256 of the given reader 11 | func Sha256(in io.Reader) (string, error) { 12 | h := sha256.New() 13 | if _, err := io.Copy(h, in); err != nil { 14 | return "", err 15 | } 16 | return hex.EncodeToString(h.Sum(nil)), nil 17 | } 18 | 19 | // FileSha256 computes the sha256 hash of a file 20 | func FileSha256(filename string) (string, error) { 21 | f, err := os.Open(filename) 22 | if err != nil { 23 | return "", err 24 | } 25 | defer f.Close() 26 | return Sha256(f) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/collections/slice.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | func SliceRemoveIndex[T any](slice []T, i int) []T { 4 | // Create a new slice with capacity one less than original 5 | result := make([]T, 0, len(slice)-1) 6 | 7 | // Add all elements except the one at index i 8 | result = append(result, slice[:i]...) 9 | result = append(result, slice[i+1:]...) 10 | 11 | return result 12 | } 13 | 14 | func SliceInsertAt[T any](slice []T, i int, value T) []T { 15 | // Create a new slice with capacity one more than original 16 | result := make([]T, 0, len(slice)+1) 17 | 18 | // Add elements before index i 19 | result = append(result, slice[:i]...) 20 | 21 | // Add the new value 22 | result = append(result, value) 23 | 24 | // Add elements after index i 25 | result = append(result, slice[i:]...) 26 | 27 | return result 28 | } 29 | -------------------------------------------------------------------------------- /pkg/collections/string_slice.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | ) 7 | 8 | type StringSlice []string 9 | 10 | func (i *StringSlice) String() string { 11 | return strings.Join(*i, ",") 12 | } 13 | 14 | // Set implements the flag.Value interface. 15 | func (i *StringSlice) Set(value string) error { 16 | *i = append(*i, value) 17 | return nil 18 | } 19 | 20 | // DeduplicateAndSort removes duplicate entries and sorts the list 21 | func DeduplicateAndSort(in []string) (out []string) { 22 | if len(in) == 0 { 23 | return in 24 | } 25 | seen := make(map[string]bool) 26 | for _, v := range in { 27 | if seen[v] { 28 | continue 29 | } 30 | seen[v] = true 31 | out = append(out, v) 32 | } 33 | sort.Strings(out) 34 | return 35 | } 36 | -------------------------------------------------------------------------------- /pkg/collections/string_stack.go: -------------------------------------------------------------------------------- 1 | // stack of strings 2 | package collections 3 | 4 | type StringStack []string 5 | 6 | // IsEmpty checks if the stack is empty 7 | func (s *StringStack) IsEmpty() bool { 8 | return len(*s) == 0 9 | } 10 | 11 | // Push a new string onto the stack 12 | func (s *StringStack) Push(x string) { 13 | *s = append(*s, x) 14 | } 15 | 16 | // Pop: remove and return top element of stack, return false if stack is empty 17 | func (s *StringStack) Pop() (string, bool) { 18 | if s.IsEmpty() { 19 | return "", false 20 | } 21 | 22 | i := len(*s) - 1 23 | x := (*s)[i] 24 | *s = (*s)[:i] 25 | 26 | return x, true 27 | } 28 | 29 | // Peek: return top element of stack, return false if stack is empty 30 | func (s *StringStack) Peek() (string, bool) { 31 | if s.IsEmpty() { 32 | return "", false 33 | } 34 | 35 | i := len(*s) - 1 36 | x := (*s)[i] 37 | 38 | return x, true 39 | } 40 | -------------------------------------------------------------------------------- /pkg/collections/uint32stack.go: -------------------------------------------------------------------------------- 1 | // stack of integers 2 | package collections 3 | 4 | type UInt32Stack []uint32 5 | 6 | // IsEmpty checks if the stack is empty 7 | func (s *UInt32Stack) IsEmpty() bool { 8 | return len(*s) == 0 9 | } 10 | 11 | // Push a new integer onto the stack 12 | func (s *UInt32Stack) Push(x uint32) { 13 | *s = append(*s, x) 14 | } 15 | 16 | // Pop: remove and return top element of stack, return false if stack is empty 17 | func (s *UInt32Stack) Pop() (uint32, bool) { 18 | if s.IsEmpty() { 19 | return 0, false 20 | } 21 | 22 | i := len(*s) - 1 23 | x := (*s)[i] 24 | *s = (*s)[:i] 25 | 26 | return x, true 27 | } 28 | 29 | // Peek: return top element of stack, return false if stack is empty 30 | func (s *UInt32Stack) Peek() (uint32, bool) { 31 | if s.IsEmpty() { 32 | return 0, false 33 | } 34 | 35 | i := len(*s) - 1 36 | x := (*s)[i] 37 | 38 | return x, true 39 | } 40 | -------------------------------------------------------------------------------- /pkg/glob/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 3 | 4 | go_library( 5 | name = "glob", 6 | srcs = [ 7 | "collect.go", 8 | "glob.go", 9 | ], 10 | importpath = "github.com/stackb/scala-gazelle/pkg/glob", 11 | visibility = ["//visibility:public"], 12 | deps = [ 13 | "@bazel_gazelle//rule:go_default_library", 14 | "@com_github_bazelbuild_buildtools//build:go_default_library", 15 | "@com_github_bmatcuk_doublestar_v4//:doublestar", 16 | "@com_github_davecgh_go_spew//spew", 17 | ], 18 | ) 19 | 20 | go_test( 21 | name = "glob_test", 22 | srcs = ["glob_test.go"], 23 | embed = [":glob"], 24 | deps = [ 25 | "//pkg/bazel", 26 | "@bazel_gazelle//rule:go_default_library", 27 | "@bazel_gazelle//testtools:go_default_library", 28 | "@com_github_bazelbuild_buildtools//build:go_default_library", 29 | "@com_github_google_go_cmp//cmp", 30 | ], 31 | ) 32 | 33 | package_filegroup( 34 | name = "filegroup", 35 | srcs = [ 36 | "BUILD.bazel", 37 | "collect.go", 38 | "glob.go", 39 | "glob_test.go", 40 | ], 41 | visibility = ["//visibility:public"], 42 | ) 43 | -------------------------------------------------------------------------------- /pkg/glob/collect.go: -------------------------------------------------------------------------------- 1 | package glob 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/bazelbuild/bazel-gazelle/rule" 8 | "github.com/bazelbuild/buildtools/build" 9 | ) 10 | 11 | type collector struct { 12 | file *rule.File 13 | dir string 14 | srcs []string 15 | } 16 | 17 | func CollectFilenames(file *rule.File, dir string, expr build.Expr) ([]string, error) { 18 | c := collector{file: file, dir: dir} 19 | if err := c.fromExpr(expr); err != nil { 20 | return nil, err 21 | } 22 | return c.srcs, nil 23 | } 24 | 25 | // collectSourceFilesFromExpr returns a list of source files for the srcs 26 | // attribute. Each value is a repo-relative path. 27 | func (c *collector) fromExpr(expr build.Expr) (err error) { 28 | switch t := expr.(type) { 29 | case *build.StringExpr: 30 | c.srcs = append(c.srcs, t.Value) 31 | case *build.BinaryExpr: 32 | c.fromExpr(t.X) 33 | c.fromExpr(t.Y) 34 | case *build.ListExpr: 35 | // example: ["foo.scala", "bar.scala"] 36 | for _, item := range t.List { 37 | c.fromExpr(item) 38 | } 39 | case *build.CallExpr: 40 | // example: glob(["**/*.scala"]) 41 | if ident, ok := t.X.(*build.Ident); ok { 42 | switch ident.Name { 43 | case "glob": 44 | g := Parse(c.file, t) 45 | c.srcs = append(c.srcs, Apply(g, os.DirFS(c.dir))...) 46 | default: 47 | err = fmt.Errorf("not attempting to resolve function call %v(): consider making this simpler", ident.Name) 48 | } 49 | } else { 50 | err = fmt.Errorf("not attempting to resolve call expression %+v: consider making this simpler", t) 51 | } 52 | case *build.Ident: 53 | // example: srcs = LIST_OF_SOURCES 54 | var srcs []string 55 | srcs, err = globalStringList(c.file, t) 56 | if err != nil { 57 | err = fmt.Errorf("failed to resolve resolve identifier %q (consider inlining it): %w", t.Name, err) 58 | } 59 | c.srcs = append(c.srcs, srcs...) 60 | case nil: 61 | break 62 | default: 63 | err = fmt.Errorf("uninterpretable 'srcs' attribute type: %T", t) 64 | } 65 | 66 | return 67 | } 68 | -------------------------------------------------------------------------------- /pkg/jarindex/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 3 | 4 | go_library( 5 | name = "jarindex", 6 | srcs = ["merge.go"], 7 | importpath = "github.com/stackb/scala-gazelle/pkg/jarindex", 8 | visibility = ["//visibility:public"], 9 | deps = ["//build/stack/gazelle/scala/jarindex"], 10 | ) 11 | 12 | go_test( 13 | name = "jarindex_test", 14 | srcs = ["merge_test.go"], 15 | embed = [":jarindex"], 16 | deps = [ 17 | "//build/stack/gazelle/scala/jarindex", 18 | "@com_github_google_go_cmp//cmp", 19 | "@org_golang_google_protobuf//testing/protocmp", 20 | ], 21 | ) 22 | 23 | package_filegroup( 24 | name = "filegroup", 25 | srcs = [ 26 | "BUILD.bazel", 27 | "merge.go", 28 | "merge_test.go", 29 | ], 30 | visibility = ["//visibility:public"], 31 | ) 32 | -------------------------------------------------------------------------------- /pkg/maven/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | 4 | go_library( 5 | name = "maven", 6 | srcs = [ 7 | "config.go", 8 | "coordinate.go", 9 | "multiset.go", 10 | "resolver.go", 11 | ], 12 | importpath = "github.com/stackb/scala-gazelle/pkg/maven", 13 | visibility = ["//visibility:public"], 14 | deps = [ 15 | "//build/stack/gazelle/scala/parse", 16 | "//pkg/bazel", 17 | "//pkg/resolver", 18 | "@bazel_gazelle//label:go_default_library", 19 | ], 20 | ) 21 | 22 | package_filegroup( 23 | name = "filegroup", 24 | srcs = [ 25 | "BUILD.bazel", 26 | "config.go", 27 | "coordinate.go", 28 | "multiset.go", 29 | "resolver.go", 30 | ], 31 | visibility = ["//visibility:public"], 32 | ) 33 | -------------------------------------------------------------------------------- /pkg/maven/config.go: -------------------------------------------------------------------------------- 1 | package maven 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | ) 7 | 8 | type configFile struct { 9 | DependencyTree dependencyTree `json:"dependency_tree"` 10 | } 11 | 12 | func loadConfiguration(filename string) (*configFile, error) { 13 | f, err := os.Open(filename) 14 | if err != nil { 15 | return nil, err 16 | } 17 | defer f.Close() 18 | 19 | var c configFile 20 | if err := json.NewDecoder(f).Decode(&c); err != nil { 21 | return nil, err 22 | } 23 | 24 | return &c, nil 25 | } 26 | 27 | type dependencyTree struct { 28 | ConflictResolution map[string]string `json:"conflict_resolution"` 29 | Dependencies []Dep `json:"dependencies"` 30 | Version string `json:"version"` 31 | } 32 | 33 | type Dep struct { 34 | Coord string `json:"coord"` 35 | Dependencies []string `json:"dependencies"` 36 | DirectDependencies []string `json:"directDependencies"` 37 | File string `json:"file"` 38 | MirrorUrls []string `json:"mirror_urls,omitempty"` 39 | Packages []string `json:"packages"` 40 | Sha256 string `json:"sha256,omitempty"` 41 | URL string `json:"url,omitempty"` 42 | Exclusions []string `json:"exclusions,omitempty"` 43 | } 44 | -------------------------------------------------------------------------------- /pkg/maven/coordinate.go: -------------------------------------------------------------------------------- 1 | package maven 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type coordinate struct { 9 | GroupID string 10 | ArtifactID string 11 | Type string 12 | Classifier string 13 | Version string 14 | } 15 | 16 | func ParseCoordinate(s string) (*coordinate, error) { 17 | parts := strings.Split(s, ":") 18 | if len(parts) < 3 { 19 | return nil, fmt.Errorf("invalid Maven coordinate %q - needed at least 2 :s", s) 20 | } 21 | c := coordinate{ 22 | GroupID: parts[0], 23 | ArtifactID: parts[1], 24 | Version: parts[len(parts)-1], 25 | } 26 | if len(parts) > 3 { 27 | c.Type = parts[2] 28 | } 29 | if len(parts) > 4 { 30 | c.Classifier = parts[3] 31 | } 32 | return &c, nil 33 | } 34 | 35 | // ArtifactString returns a value which can be passed to the `artifact()` macro to find a dependency. 36 | func (c *coordinate) ArtifactString() string { 37 | parts := []string{ 38 | c.GroupID, 39 | c.ArtifactID, 40 | } 41 | if c.Classifier != "" { 42 | parts = append(parts, c.Classifier) 43 | } 44 | return strings.Join(parts, ":") 45 | } 46 | -------------------------------------------------------------------------------- /pkg/maven/multiset.go: -------------------------------------------------------------------------------- 1 | package maven 2 | 3 | import "sync" 4 | 5 | type stringSet = map[string]struct{} 6 | 7 | func newStringSet() stringSet { 8 | return make(map[string]struct{}) 9 | } 10 | 11 | type StringMultiSet struct { 12 | lock sync.RWMutex 13 | data map[string]stringSet 14 | } 15 | 16 | func NewStringMultiSet() *StringMultiSet { 17 | return &StringMultiSet{data: make(map[string]stringSet)} 18 | } 19 | 20 | func (m *StringMultiSet) Add(key, value string) { 21 | m.lock.Lock() 22 | defer m.lock.Unlock() 23 | 24 | if _, found := m.data[key]; !found { 25 | m.data[key] = newStringSet() 26 | } 27 | m.data[key][value] = struct{}{} 28 | } 29 | 30 | func (m *StringMultiSet) Get(key string) (map[string]struct{}, bool) { 31 | m.lock.RLock() 32 | defer m.lock.RUnlock() 33 | 34 | v, ok := m.data[key] 35 | return v, ok 36 | } 37 | -------------------------------------------------------------------------------- /pkg/parser/assets.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | _ "embed" 5 | ) 6 | 7 | //go:embed scalameta_parser.mjs 8 | var parserrMjs string 9 | 10 | //go:embed node_modules/scalameta-parsers/index.js 11 | var scalametaParsersIndexJs string 12 | 13 | //go:embed node.exe 14 | var nodeExe []byte 15 | -------------------------------------------------------------------------------- /pkg/parser/assets_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestEmbed(t *testing.T) { 8 | if len(parserrMjs) == 0 { 9 | t.Error("embedded scalameta_parser.mjs script is missing") 10 | } 11 | if len(scalametaParsersIndexJs) == 0 { 12 | t.Error("embedded node_modules/scalameta-parsers/index.js script is missing") 13 | } 14 | if len(nodeExe) == 0 { 15 | t.Errorf("embedded node.exe is missing: len %d", len(nodeExe)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pkg/parser/exec.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/amenzhinsky/go-memexec" 7 | "github.com/stackb/scala-gazelle/pkg/procutil" 8 | ) 9 | 10 | // ExecJS runs the embedded js runtime interpreter 11 | func ExecJS(dir string, args, env []string, in io.Reader, stdout, stderr io.Writer) (int, error) { 12 | exe, err := memexec.New(nodeExe) 13 | if err != nil { 14 | return 1, err 15 | } 16 | defer exe.Close() 17 | 18 | cmd := exe.Command(args...) 19 | cmd.Stdin = in 20 | cmd.Stdout = stdout 21 | cmd.Stderr = stderr 22 | cmd.Env = env 23 | cmd.Dir = dir 24 | err = cmd.Run() 25 | 26 | return procutil.CmdExitCode(cmd, err), err 27 | } 28 | -------------------------------------------------------------------------------- /pkg/parser/exec_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "testing" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestExecJS(t *testing.T) { 12 | for name, tc := range map[string]struct { 13 | dir string 14 | args []string 15 | in io.Reader 16 | env []string 17 | wantExitCode int 18 | wantStdout string 19 | wantStderr string 20 | }{ 21 | "version": { 22 | dir: ".", 23 | args: []string{"--version"}, 24 | wantStderr: "", 25 | wantStdout: "v19.1.0\n", 26 | wantExitCode: 0, 27 | }, 28 | } { 29 | t.Run(name, func(t *testing.T) { 30 | var stdout, stderr bytes.Buffer 31 | 32 | gotExitCode, err := ExecJS(tc.dir, tc.args, tc.env, tc.in, &stdout, &stderr) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | if diff := cmp.Diff(tc.wantExitCode, gotExitCode); diff != "" { 38 | t.Errorf("ExecJS exit code (-want +got):\n%s", diff) 39 | } 40 | 41 | if diff := cmp.Diff(tc.wantStdout, stdout.String()); diff != "" { 42 | t.Errorf("ExecJS stdout (-want +got):\n%s", diff) 43 | } 44 | 45 | if diff := cmp.Diff(tc.wantStderr, stderr.String()); diff != "" { 46 | t.Errorf("ExecJS stderr (-want +got):\n%s", diff) 47 | } 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/parser/mocks/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | 4 | go_library( 5 | name = "mocks", 6 | srcs = ["Parser.go"], 7 | importpath = "github.com/stackb/scala-gazelle/pkg/parser/mocks", 8 | visibility = ["//visibility:public"], 9 | deps = [ 10 | "//build/stack/gazelle/scala/parse", 11 | "@bazel_gazelle//label:go_default_library", 12 | "@com_github_stretchr_testify//mock", 13 | ], 14 | ) 15 | 16 | package_filegroup( 17 | name = "filegroup", 18 | srcs = [ 19 | "BUILD.bazel", 20 | "Parser.go", 21 | ], 22 | visibility = ["//visibility:public"], 23 | ) 24 | -------------------------------------------------------------------------------- /pkg/parser/mocks/Parser.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.16.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | label "github.com/bazelbuild/bazel-gazelle/label" 7 | mock "github.com/stretchr/testify/mock" 8 | 9 | parse "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" 10 | ) 11 | 12 | // Parser is an autogenerated mock type for the Parser type 13 | type Parser struct { 14 | mock.Mock 15 | } 16 | 17 | // LoadScalaRule provides a mock function with given fields: from, rule 18 | func (_m *Parser) LoadScalaRule(from label.Label, rule *parse.Rule) error { 19 | ret := _m.Called(from, rule) 20 | 21 | var r0 error 22 | if rf, ok := ret.Get(0).(func(label.Label, *parse.Rule) error); ok { 23 | r0 = rf(from, rule) 24 | } else { 25 | r0 = ret.Error(0) 26 | } 27 | 28 | return r0 29 | } 30 | 31 | // ParseScalaRule provides a mock function with given fields: kind, from, dir, srcs 32 | func (_m *Parser) ParseScalaRule(kind string, from label.Label, dir string, srcs ...string) (*parse.Rule, error) { 33 | _va := make([]interface{}, len(srcs)) 34 | for _i := range srcs { 35 | _va[_i] = srcs[_i] 36 | } 37 | var _ca []interface{} 38 | _ca = append(_ca, kind, from, dir) 39 | _ca = append(_ca, _va...) 40 | ret := _m.Called(_ca...) 41 | 42 | var r0 *parse.Rule 43 | if rf, ok := ret.Get(0).(func(string, label.Label, string, ...string) *parse.Rule); ok { 44 | r0 = rf(kind, from, dir, srcs...) 45 | } else { 46 | if ret.Get(0) != nil { 47 | r0 = ret.Get(0).(*parse.Rule) 48 | } 49 | } 50 | 51 | var r1 error 52 | if rf, ok := ret.Get(1).(func(string, label.Label, string, ...string) error); ok { 53 | r1 = rf(kind, from, dir, srcs...) 54 | } else { 55 | r1 = ret.Error(1) 56 | } 57 | 58 | return r0, r1 59 | } 60 | 61 | type mockConstructorTestingTNewParser interface { 62 | mock.TestingT 63 | Cleanup(func()) 64 | } 65 | 66 | // NewParser creates a new instance of Parser. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 67 | func NewParser(t mockConstructorTestingTNewParser) *Parser { 68 | mock := &Parser{} 69 | mock.Mock.Test(t) 70 | 71 | t.Cleanup(func() { mock.AssertExpectations(t) }) 72 | 73 | return mock 74 | } 75 | -------------------------------------------------------------------------------- /pkg/parser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsparse", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "scalameta-parsers": "^4.4.17" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pkg/parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/label" 5 | sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" 6 | ) 7 | 8 | // Parser abstracts a service that can parse scala files. It also supports a 9 | // load operation to facilitate a cache. 10 | type Parser interface { 11 | // LoadScalaRule loads the given rule state. 12 | LoadScalaRule(from label.Label, rule *sppb.Rule) error 13 | 14 | // ParseScalaRule is used to parse a list of source files. The srcs list 15 | // is expected to be relative to dir. 16 | ParseScalaRule(kind string, from label.Label, dir string, srcs ...string) (*sppb.Rule, error) 17 | } 18 | -------------------------------------------------------------------------------- /pkg/parser/testdata/Main.scala: -------------------------------------------------------------------------------- 1 | package a.b.c 2 | 3 | object Main { 4 | def main(args: Array[String]): Unit = { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /pkg/procutil/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 3 | 4 | go_library( 5 | name = "procutil", 6 | srcs = [ 7 | "cmd.go", 8 | "env.go", 9 | "paths.go", 10 | ], 11 | importpath = "github.com/stackb/scala-gazelle/pkg/procutil", 12 | visibility = ["//visibility:public"], 13 | ) 14 | 15 | package_filegroup( 16 | name = "filegroup", 17 | srcs = [ 18 | "BUILD.bazel", 19 | "cmd.go", 20 | "env.go", 21 | "paths.go", 22 | ], 23 | visibility = ["//visibility:public"], 24 | ) 25 | -------------------------------------------------------------------------------- /pkg/procutil/cmd.go: -------------------------------------------------------------------------------- 1 | package procutil 2 | 3 | import ( 4 | "os/exec" 5 | "syscall" 6 | ) 7 | 8 | func CmdExitCode(cmd *exec.Cmd, err error) int { 9 | if err == nil { 10 | // success, exitCode should be 0 if go is ok 11 | ws := cmd.ProcessState.Sys().(syscall.WaitStatus) 12 | return ws.ExitStatus() 13 | } 14 | 15 | // try to get the exit code 16 | if exitError, ok := err.(*exec.ExitError); ok { 17 | ws := exitError.Sys().(syscall.WaitStatus) 18 | return ws.ExitStatus() 19 | } 20 | 21 | // This will happen (in OSX) if `name` is not available in $PATH, 22 | // in this situation, exit code could not be get, and stderr will be 23 | // empty string very likely, so we use the default fail code, and format err 24 | // to string and set to stderr 25 | return -1 26 | } 27 | -------------------------------------------------------------------------------- /pkg/procutil/env.go: -------------------------------------------------------------------------------- 1 | package procutil 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | ) 7 | 8 | type EnvVar string 9 | 10 | func LookupBoolEnv(name EnvVar, defaultValue bool) bool { 11 | if val, ok := os.LookupEnv(string(name)); ok { 12 | switch strings.ToLower(val) { 13 | case "true", "1": 14 | return true 15 | case "false", "0": 16 | return false 17 | } 18 | } 19 | return defaultValue 20 | } 21 | 22 | func LookupEnv(name EnvVar) (string, bool) { 23 | return os.LookupEnv(string(name)) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/procutil/paths.go: -------------------------------------------------------------------------------- 1 | package procutil 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func DirExists(dirname string) bool { 8 | info, err := os.Stat(dirname) 9 | if os.IsNotExist(err) { 10 | return false 11 | } 12 | return info.IsDir() 13 | } 14 | 15 | func FileExists(dirname string) bool { 16 | info, err := os.Stat(dirname) 17 | if os.IsNotExist(err) { 18 | return false 19 | } 20 | return !info.IsDir() 21 | } 22 | -------------------------------------------------------------------------------- /pkg/protobuf/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | 4 | go_library( 5 | name = "protobuf", 6 | srcs = ["io.go"], 7 | importpath = "github.com/stackb/scala-gazelle/pkg/protobuf", 8 | visibility = ["//visibility:public"], 9 | deps = [ 10 | "@org_golang_google_protobuf//encoding/protojson", 11 | "@org_golang_google_protobuf//encoding/prototext", 12 | "@org_golang_google_protobuf//encoding/protowire", 13 | "@org_golang_google_protobuf//proto", 14 | "@org_golang_google_protobuf//reflect/protoreflect", 15 | ], 16 | ) 17 | 18 | package_filegroup( 19 | name = "filegroup", 20 | srcs = [ 21 | "BUILD.bazel", 22 | "io.go", 23 | ], 24 | visibility = ["//visibility:public"], 25 | ) 26 | -------------------------------------------------------------------------------- /pkg/provider/README.md: -------------------------------------------------------------------------------- 1 | # pkg/provider 2 | 3 | The `TestScalaSourceProviderParseScalaRule` test parses .scala files in the 4 | `testdata/` directory and compares the output to a similarly-named golden file. 5 | 6 | To regenerate the golden files, run the test with the `-update` flag: 7 | 8 | ``` 9 | bazel run //pkg/provider:provider_test -- -update 10 | ``` 11 | -------------------------------------------------------------------------------- /pkg/provider/semanticdb_provider_test.go: -------------------------------------------------------------------------------- 1 | package provider_test 2 | -------------------------------------------------------------------------------- /pkg/provider/testdata/FullyQualified.scala: -------------------------------------------------------------------------------- 1 | package foo 2 | 3 | object Main { 4 | def main(args: Array[String]): Unit = { 5 | val map: java.dx.Map[String,String] = null 6 | import sk.ygor.stackoverflow.q53326545.macros.ExampleMacro.methodName 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /pkg/provider/testdata/FullyQualified.scala.golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "//pkg/provider:testdata/FullyQualified.scala", 3 | "kind": "scala_library", 4 | "files": [ 5 | { 6 | "filename": "pkg/provider/testdata/FullyQualified.scala", 7 | "imports": [ 8 | "sk.ygor.stackoverflow.q53326545.macros.ExampleMacro.methodName" 9 | ], 10 | "packages": [ 11 | "foo" 12 | ], 13 | "objects": [ 14 | "foo.Main" 15 | ], 16 | "names": [ 17 | "Array", 18 | "Main", 19 | "Map", 20 | "String", 21 | "Unit", 22 | "java.dx.Map" 23 | ] 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /pkg/provider/testdata/GreeterClient.scala.golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "//pkg/provider:testdata/GreeterClient.scala", 3 | "kind": "scala_library", 4 | "files": [ 5 | { 6 | "filename": "pkg/provider/testdata/GreeterClient.scala", 7 | "imports": [ 8 | "akka.Done", 9 | "akka.NotUsed", 10 | "akka.actor.ActorSystem", 11 | "akka.grpc.GrpcClientSettings", 12 | "akka.stream.Materializer", 13 | "akka.stream.scaladsl.Source", 14 | "com.typesafe.scalalogging.LazyLogging", 15 | "examples.helloworld.greeter.proto.GreeterServiceClient", 16 | "examples.helloworld.greeter.proto.HelloReplyMessage", 17 | "examples.helloworld.greeter.proto.HelloRequest", 18 | "examples.helloworld.greeter.proto.MonitorHelloRequest", 19 | "scala.concurrent.Future", 20 | "scala.concurrent.duration._", 21 | "scala.util.Failure", 22 | "scala.util.Success" 23 | ], 24 | "packages": [ 25 | "examples.helloworld.greeter" 26 | ], 27 | "objects": [ 28 | "examples.helloworld.greeter.GreeterClient" 29 | ], 30 | "names": [ 31 | "ActorSystem", 32 | "Array", 33 | "Done", 34 | "Failure", 35 | "Future", 36 | "GreeterClient", 37 | "GreeterServiceClient", 38 | "GrpcClientSettings", 39 | "GrpcClientSettings.fromConfig", 40 | "HelloReplyMessage", 41 | "HelloRequest", 42 | "LazyLogging", 43 | "List", 44 | "Materializer", 45 | "MonitorHelloRequest", 46 | "NotUsed", 47 | "Some", 48 | "Source", 49 | "Source.tick", 50 | "String", 51 | "Success", 52 | "Unit" 53 | ], 54 | "extends": { 55 | "object examples.helloworld.greeter.GreeterClient": { 56 | "classes": [ 57 | "com.typesafe.scalalogging.LazyLogging" 58 | ] 59 | } 60 | } 61 | } 62 | ] 63 | } -------------------------------------------------------------------------------- /pkg/provider/testdata/PostgresAccess.scala: -------------------------------------------------------------------------------- 1 | package auth.dao 2 | 3 | object PostgresAccess extends common.postgres.PostgresAccess { 4 | override def dbConfigKey: String = "auth" 5 | } 6 | -------------------------------------------------------------------------------- /pkg/provider/testdata/PostgresAccess.scala.golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "//pkg/provider:testdata/PostgresAccess.scala", 3 | "kind": "scala_library", 4 | "files": [ 5 | { 6 | "filename": "pkg/provider/testdata/PostgresAccess.scala", 7 | "packages": [ 8 | "auth.dao" 9 | ], 10 | "objects": [ 11 | "auth.dao.PostgresAccess" 12 | ], 13 | "names": [ 14 | "PostgresAccess", 15 | "String", 16 | "common.postgres.PostgresAccess" 17 | ], 18 | "extends": { 19 | "object auth.dao.PostgresAccess": { 20 | "classes": [ 21 | "common.postgres.PostgresAccess" 22 | ] 23 | } 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /pkg/provider/testdata/UserId.scala: -------------------------------------------------------------------------------- 1 | package common.types 2 | 3 | /** Scala "Value" class for interacting with UserId values 4 | * Representative of conceptual ActorId/UserId primary key Int values 5 | * 6 | * @param value In general it should not be necessary or desirable to interact directly with this property. Exceptions 7 | * being Core Libraries, DB interactions, and legacy code (which should be updated opportunistically). 8 | */ 9 | case class UserId(value: Int) extends AnyVal 10 | 11 | object UserId { 12 | implicit val intTypeMapper: scalapb.TypeMapper[Int, UserId] = scalapb.TypeMapper[Int, UserId](UserId(_))(_.value) 13 | } 14 | -------------------------------------------------------------------------------- /pkg/provider/testdata/UserId.scala.golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "//pkg/provider:testdata/UserId.scala", 3 | "kind": "scala_library", 4 | "files": [ 5 | { 6 | "filename": "pkg/provider/testdata/UserId.scala", 7 | "packages": [ 8 | "common.types" 9 | ], 10 | "classes": [ 11 | "common.types.UserId" 12 | ], 13 | "objects": [ 14 | "common.types.UserId" 15 | ], 16 | "names": [ 17 | "AnyVal", 18 | "Int", 19 | "TypeMapper", 20 | "UserId", 21 | "scalapb.TypeMapper" 22 | ], 23 | "extends": { 24 | "class common.types.UserId": { 25 | "classes": [ 26 | "AnyVal" 27 | ] 28 | } 29 | } 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /pkg/provider/testdata/maven_install.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependency_tree": { 3 | "dependencies": [ 4 | { 5 | "coord": "xml-apis:xml-apis:1.4.01", 6 | "dependencies": [], 7 | "directDependencies": [], 8 | "exclusions": [ 9 | "log4j:log4j" 10 | ], 11 | "file": "v1/https/repo.maven.apache.org/maven2/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar", 12 | "mirror_urls": [ 13 | "https://repo.maven.apache.org/maven2/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar", 14 | "https://omnistac.jfrog.io/artifactory/libs-release/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar" 15 | ], 16 | "packages": [ 17 | "javax.xml", 18 | "javax.xml.datatype", 19 | "javax.xml.namespace", 20 | "javax.xml.parsers", 21 | "javax.xml.stream" 22 | ], 23 | "sha256": "a840968176645684bb01aed376e067ab39614885f9eee44abe35a5f20ebe7fad", 24 | "url": "https://repo.maven.apache.org/maven2/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar" 25 | } 26 | ], 27 | "version": "0.1.0" 28 | } 29 | } -------------------------------------------------------------------------------- /pkg/resolver/chain_scope.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // ChainScope implements Scope over a chain of registries. 9 | type ChainScope struct { 10 | chain []Scope 11 | } 12 | 13 | func NewChainScope(chain ...Scope) *ChainScope { 14 | return &ChainScope{ 15 | chain: chain, 16 | } 17 | } 18 | 19 | // PutSymbol is not supported and will panic. 20 | func (r *ChainScope) PutSymbol(known *Symbol) error { 21 | return fmt.Errorf("unsupported operation: PutSymbol") 22 | } 23 | 24 | // GetSymbol implements part of the Scope interface 25 | func (r *ChainScope) GetSymbol(imp string) (*Symbol, bool) { 26 | for _, next := range r.chain { 27 | if known, ok := next.GetSymbol(imp); ok { 28 | return known, true 29 | } 30 | } 31 | return nil, false 32 | } 33 | 34 | // GetScope implements part of the resolver.Scope interface. 35 | func (r *ChainScope) GetScope(imp string) (Scope, bool) { 36 | for _, next := range r.chain { 37 | if scope, ok := next.GetScope(imp); ok { 38 | return scope, true 39 | } 40 | } 41 | return nil, false 42 | } 43 | 44 | // GetSymbols implements part of the Scope interface 45 | func (r *ChainScope) GetSymbols(prefix string) []*Symbol { 46 | for _, next := range r.chain { 47 | if known := next.GetSymbols(prefix); len(known) > 0 { 48 | return known 49 | } 50 | } 51 | return nil 52 | } 53 | 54 | // String implements the fmt.Stringer interface 55 | func (r *ChainScope) String() string { 56 | var buf strings.Builder 57 | for i, next := range r.chain { 58 | buf.WriteString(fmt.Sprintf("--- layer %d ---\n", i)) 59 | buf.WriteString(next.String()) 60 | buf.WriteRune('\n') 61 | } 62 | return buf.String() 63 | } 64 | -------------------------------------------------------------------------------- /pkg/resolver/chain_scope_test.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/label" 7 | "github.com/google/go-cmp/cmp" 8 | sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" 9 | ) 10 | 11 | func TestChainScope(t *testing.T) { 12 | for name, tc := range map[string]struct { 13 | scopes []Scope 14 | name string 15 | want *Symbol 16 | }{ 17 | "degenerate": {}, 18 | "miss": { 19 | name: "examples.helloworld.greeter.GreeterServiceImpl", 20 | scopes: func() []Scope { 21 | root := NewTrieScope() 22 | root.PutSymbol( 23 | makeSymbol(sppb.ImportType_PROTO_PACKAGE, "examples.helloworld.greeter.proto", label.Label{Pkg: "examples/helloworld/greeter/proto", Name: "examples_helloworld_greeter_proto_grpc_scala_library"}), 24 | ) 25 | scope, _ := root.GetScope("examples.helloworld.greeter.proto") 26 | return []Scope{scope} 27 | }(), 28 | want: nil, 29 | }, 30 | } { 31 | t.Run(name, func(t *testing.T) { 32 | scope := NewChainScope(tc.scopes...) 33 | got, _ := scope.GetSymbol(tc.name) 34 | if diff := cmp.Diff(tc.want, got); diff != "" { 35 | t.Errorf("(-want +got):\n%s", diff) 36 | } 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/resolver/chain_symbol_resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/config" 5 | "github.com/bazelbuild/bazel-gazelle/label" 6 | "github.com/bazelbuild/bazel-gazelle/resolve" 7 | ) 8 | 9 | // ChainSymbolResolver implements SymbolResolver over a chain of resolvers. 10 | type ChainSymbolResolver struct { 11 | chain []SymbolResolver 12 | } 13 | 14 | func NewChainSymbolResolver(chain ...SymbolResolver) *ChainSymbolResolver { 15 | return &ChainSymbolResolver{ 16 | chain: chain, 17 | } 18 | } 19 | 20 | // ResolveSymbol implements the SymbolResolver interface 21 | func (r *ChainSymbolResolver) ResolveSymbol(c *config.Config, ix *resolve.RuleIndex, from label.Label, lang string, imp string) (*Symbol, bool) { 22 | for _, next := range r.chain { 23 | if sym, ok := next.ResolveSymbol(c, ix, from, lang, imp); ok { 24 | return sym, true 25 | } 26 | } 27 | return nil, false 28 | } 29 | -------------------------------------------------------------------------------- /pkg/resolver/conflict_resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/config" 7 | "github.com/bazelbuild/bazel-gazelle/rule" 8 | "github.com/rs/zerolog" 9 | ) 10 | 11 | // ConflictResolver implementations are capable of applying a conflict 12 | // resolution strategy for conflicting resolved import symbols. 13 | type ConflictResolver interface { 14 | // Name is the canonical name for the resolver 15 | Name() string 16 | // RegisterFlags configures the flags. RegisterFlags is called for all 17 | // resolvers whether they are enabled or not. A logger for the instance to 18 | // use is passed in as the final argument. 19 | RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config, logger zerolog.Logger) 20 | // CheckFlags asserts that the flags are correct. CheckFlags is only called 21 | // if the resolver is enabled. 22 | CheckFlags(fs *flag.FlagSet, c *config.Config) error 23 | // ResolveConflict takes the context rule and imports, and the target symbol 24 | // with conflicts to resolve. 25 | ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol) (*Symbol, bool) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/resolver/conflict_resolver_registry.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | // ConflictResolverRegistry is an index of known conflict resolvers keyed by their name. 4 | type ConflictResolverRegistry interface { 5 | // GetConflictResolver returns the named resolver. If not known `(nil, 6 | // false)` is returned. 7 | GetConflictResolver(name string) (ConflictResolver, bool) 8 | 9 | // PutConflictResolver adds the given known rule to the registry. It is an 10 | // error to attempt duplicate registration of the same rule twice. 11 | // Implementations should use the google.golang.org/grpc/status.Errorf for 12 | // error types. 13 | PutConflictResolver(name string, r ConflictResolver) error 14 | } 15 | -------------------------------------------------------------------------------- /pkg/resolver/cross_symbol_resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/config" 5 | "github.com/bazelbuild/bazel-gazelle/label" 6 | "github.com/bazelbuild/bazel-gazelle/resolve" 7 | 8 | sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" 9 | ) 10 | 11 | // CrossSymbolResolver implements Resolver using the resolve.RuleIndex (which uses the 12 | // gazelle cross resolver infrastructure). 13 | type CrossSymbolResolver struct { 14 | lang string 15 | } 16 | 17 | func NewCrossSymbolResolver(lang string) *CrossSymbolResolver { 18 | return &CrossSymbolResolver{lang} 19 | } 20 | 21 | // ResolveSymbol implements the SymbolResolver interface 22 | func (sr *CrossSymbolResolver) ResolveSymbol(c *config.Config, ix *resolve.RuleIndex, from label.Label, lang string, imp string) (*Symbol, bool) { 23 | matches := ix.FindRulesByImportWithConfig(c, resolve.ImportSpec{Lang: lang, Imp: imp}, sr.lang) 24 | 25 | if len(matches) == 0 { 26 | return nil, false 27 | } 28 | 29 | if len(matches) == 1 { 30 | return NewSymbol(sppb.ImportType_CROSS_RESOLVE, imp, "cross-resolve", matches[0].Label), true 31 | } 32 | 33 | sym := NewSymbol(sppb.ImportType_CROSS_RESOLVE, imp, "cross-resolve", matches[0].Label) 34 | for _, match := range matches[1:] { 35 | conflict := NewSymbol(sppb.ImportType_CROSS_RESOLVE, imp, "cross-resolve", match.Label) 36 | sym.Conflict(conflict) 37 | } 38 | 39 | return sym, true 40 | } 41 | -------------------------------------------------------------------------------- /pkg/resolver/deps_cleaner.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/config" 7 | "github.com/bazelbuild/bazel-gazelle/label" 8 | "github.com/bazelbuild/bazel-gazelle/rule" 9 | ) 10 | 11 | // DepsCleaner implementations are capable of applying some sort of cleanup 12 | // strategy on the post-resolved deps of a rule. 13 | type DepsCleaner interface { 14 | // Name is the canonical name for the resolver 15 | Name() string 16 | // RegisterFlags configures the flags. RegisterFlags is called for all 17 | // resolvers whether they are enabled or not. 18 | RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) 19 | // CheckFlags asserts that the flags are correct. CheckFlags is only called 20 | // if the resolver is enabled. 21 | CheckFlags(fs *flag.FlagSet, c *config.Config) error 22 | // CleanDeps takes the context rule and a map of labels that represent the 23 | // incoming deps. The cleaner implementation should assign the value under 24 | // the dep label as false if the dep is not wanted. 25 | CleanDeps(deps map[label.Label]bool, r *rule.Rule, from label.Label) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/resolver/deps_cleaner_registry.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | // DepsCleanerRegistry is an index of known conflict resolvers keyed by their name. 4 | type DepsCleanerRegistry interface { 5 | // GetDepsCleaner returns the named resolver. If not known `(nil, 6 | // false)` is returned. 7 | GetDepsCleaner(name string) (DepsCleaner, bool) 8 | 9 | // PutDepsCleaner adds the given known rule to the registry. It is an 10 | // error to attempt duplicate registration of the same rule twice. 11 | // Implementations should use the google.golang.org/grpc/status.Errorf for 12 | // error types. 13 | PutDepsCleaner(name string, r DepsCleaner) error 14 | } 15 | -------------------------------------------------------------------------------- /pkg/resolver/global_conflict_resolver_registry.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | var globalConflictResolvers = make(globalConflictResolverMap) 9 | 10 | // GlobalConflictResolverRegistry returns a default symbol provider registry. 11 | // Third-party gazelle extensions can append to this list and configure their 12 | // own implementations. 13 | func GlobalConflictResolverRegistry() ConflictResolverRegistry { 14 | return globalConflictResolvers 15 | } 16 | 17 | type globalConflictResolverMap map[string]ConflictResolver 18 | 19 | // GetConflictResolver implements part of the resolver.ConflictResolverRegistry 20 | // interface. 21 | func (r globalConflictResolverMap) GetConflictResolver(name string) (ConflictResolver, bool) { 22 | resolver, ok := r[name] 23 | return resolver, ok 24 | } 25 | 26 | // PutConflictResolver implements part of the resolver.ConflictResolverRegistry 27 | // interface. 28 | func (r globalConflictResolverMap) PutConflictResolver(name string, resolver ConflictResolver) error { 29 | if _, ok := r[name]; ok { 30 | return fmt.Errorf("duplicate ConflictResolver %q", name) 31 | } 32 | r[name] = resolver 33 | return nil 34 | } 35 | 36 | // GlobalConflictResolvers returns a sorted list of known conflict resolvers 37 | func GlobalConflictResolvers() []ConflictResolver { 38 | keys := make([]string, 0, len(globalConflictResolvers)) 39 | for k := range globalConflictResolvers { 40 | keys = append(keys, k) 41 | } 42 | sort.Strings(keys) 43 | resolvers := make([]ConflictResolver, len(keys)) 44 | for i, k := range keys { 45 | resolvers[i] = globalConflictResolvers[k] 46 | } 47 | return resolvers 48 | } 49 | -------------------------------------------------------------------------------- /pkg/resolver/global_deps_cleaner_registry.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | var globalDepsCleaners = make(globalDepsCleanerMap) 9 | 10 | // GlobalDepsCleanerRegistry returns a default deps cleaner registry. 11 | // Third-party gazelle extensions can append to this list and configure their 12 | // own implementations. 13 | func GlobalDepsCleanerRegistry() DepsCleanerRegistry { 14 | return globalDepsCleaners 15 | } 16 | 17 | type globalDepsCleanerMap map[string]DepsCleaner 18 | 19 | // GetDepsCleaner implements part of the resolver.DepsCleanerRegistry 20 | // interface. 21 | func (r globalDepsCleanerMap) GetDepsCleaner(name string) (DepsCleaner, bool) { 22 | resolver, ok := r[name] 23 | return resolver, ok 24 | } 25 | 26 | // PutDepsCleaner implements part of the resolver.DepsCleanerRegistry 27 | // interface. 28 | func (r globalDepsCleanerMap) PutDepsCleaner(name string, resolver DepsCleaner) error { 29 | if _, ok := r[name]; ok { 30 | return fmt.Errorf("duplicate DepsCleaner %q", name) 31 | } 32 | r[name] = resolver 33 | return nil 34 | } 35 | 36 | // GlobalDepsCleaners returns a sorted list of known conflict resolvers 37 | func GlobalDepsCleaners() []DepsCleaner { 38 | keys := make([]string, 0, len(globalDepsCleaners)) 39 | for k := range globalDepsCleaners { 40 | keys = append(keys, k) 41 | } 42 | sort.Strings(keys) 43 | resolvers := make([]DepsCleaner, len(keys)) 44 | for i, k := range keys { 45 | resolvers[i] = globalDepsCleaners[k] 46 | } 47 | return resolvers 48 | } 49 | -------------------------------------------------------------------------------- /pkg/resolver/global_symbol_provider_registry.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import "fmt" 4 | 5 | var globalSymbolProviders = &globalSymbolProviderRegistry{} 6 | 7 | // GlobalSymbolProviderRegistry returns a default symbol provider registry. 8 | // Third-party gazelle extensions can append to this list and configure their 9 | // own implementations. 10 | func GlobalSymbolProviderRegistry() SymbolProviderRegistry { 11 | return globalSymbolProviders 12 | } 13 | 14 | func GetNamedSymbolProviders(names []string) (want []SymbolProvider, err error) { 15 | all := GlobalSymbolProviderRegistry().SymbolProviders() 16 | for _, name := range names { 17 | found := false 18 | for _, provider := range all { 19 | if name == provider.Name() { 20 | want = append(want, provider) 21 | found = true 22 | break 23 | } 24 | } 25 | if !found { 26 | return nil, fmt.Errorf("resolver.SymbolProvider not found: %q", name) 27 | } 28 | } 29 | return 30 | } 31 | 32 | type globalSymbolProviderRegistry struct { 33 | providers []SymbolProvider 34 | } 35 | 36 | // SymbolProviders implements part of the 37 | // resolver.SymbolProviderRegistry interface. 38 | func (r *globalSymbolProviderRegistry) SymbolProviders() []SymbolProvider { 39 | return r.providers 40 | } 41 | 42 | // AddSymbolProvider implements part of the 43 | // resolver.SymbolProviderRegistry interface. 44 | func (r *globalSymbolProviderRegistry) AddSymbolProvider(provider SymbolProvider) error { 45 | for _, p := range r.providers { 46 | if p.Name() == provider.Name() { 47 | return fmt.Errorf("duplicate resolver.SymbolProvider %q", p.Name()) 48 | } 49 | } 50 | r.providers = append(r.providers, provider) 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /pkg/resolver/known_rule_registry.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/label" 5 | "github.com/bazelbuild/bazel-gazelle/rule" 6 | ) 7 | 8 | // KnownRuleRegistry is an index of known rules keyed by their label. 9 | type KnownRuleRegistry interface { 10 | // GetKnownRule does a lookup of the given label and returns the 11 | // known rule. If not known `(nil, false)` is returned. 12 | GetKnownRule(from label.Label) (*rule.Rule, bool) 13 | 14 | // PutKnownRule adds the given known rule to the registry. It is an 15 | // error to attempt duplicate registration of the same rule twice. 16 | // Implementations should use the google.golang.org/grpc/status.Errorf for 17 | // error types. 18 | PutKnownRule(from label.Label, r *rule.Rule) error 19 | } 20 | -------------------------------------------------------------------------------- /pkg/resolver/label_name_rewrite_spec.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/label" 7 | ) 8 | 9 | // LabelNameRewriteSpec is a specification to rewrite a label name. For 10 | // example, a bazel macro like `custom_scala_library` might be implemented such 11 | // that the macro instantiates a "real" scala_library rule as `name + "_lib"`. 12 | // In this case, we don't want to resolve the actual macro name but rather the 13 | // lib name. Given this example, it would be 14 | // `LabelNameRewriteSpec{Src:"%{name}", Dst:"%{name}_lib"}`, where `%{name}` is 15 | // a special token used as a placeholder for the label.Name. 16 | type LabelNameRewriteSpec struct { 17 | // Src is the label name pattern to match 18 | Src string 19 | // Dst is the label name pattern to rewrite 20 | Dst string 21 | } 22 | 23 | func (m *LabelNameRewriteSpec) Rewrite(from label.Label) label.Label { 24 | if !(m.Src == from.Name || m.Src == "%{name}") { 25 | return from 26 | } 27 | return label.Label{Repo: from.Repo, Pkg: from.Pkg, Name: strings.ReplaceAll(m.Dst, "%{name}", from.Name)} 28 | } 29 | -------------------------------------------------------------------------------- /pkg/resolver/memo_symbol_resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/config" 5 | "github.com/bazelbuild/bazel-gazelle/label" 6 | "github.com/bazelbuild/bazel-gazelle/resolve" 7 | ) 8 | 9 | // MemoSymbolResolver implements SymbolResolver, memoizing results. 10 | type MemoSymbolResolver struct { 11 | next SymbolResolver 12 | cache map[string]*Symbol 13 | } 14 | 15 | func NewMemoSymbolResolver(next SymbolResolver) *MemoSymbolResolver { 16 | return &MemoSymbolResolver{ 17 | next: next, 18 | cache: make(map[string]*Symbol), 19 | } 20 | } 21 | 22 | // ResolveSymbol implements the SymbolResolver interface 23 | func (r *MemoSymbolResolver) ResolveSymbol(c *config.Config, ix *resolve.RuleIndex, from label.Label, lang string, imp string) (*Symbol, bool) { 24 | if sym, ok := r.cache[imp]; ok { 25 | return sym, true 26 | } 27 | if sym, ok := r.next.ResolveSymbol(c, ix, from, lang, imp); ok { 28 | r.cache[imp] = sym 29 | return sym, true 30 | } 31 | return nil, false 32 | } 33 | -------------------------------------------------------------------------------- /pkg/resolver/mocks/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | 4 | go_library( 5 | name = "mocks", 6 | testonly = True, 7 | srcs = [ 8 | "ConflictResolver.go", 9 | "Scope.go", 10 | "SymbolProvider.go", 11 | "SymbolResolver.go", 12 | "Universe.go", 13 | "symbol_capturer.go", 14 | ], 15 | importpath = "github.com/stackb/scala-gazelle/pkg/resolver/mocks", 16 | visibility = ["//visibility:public"], 17 | deps = [ 18 | "//pkg/resolver", 19 | "@bazel_gazelle//config:go_default_library", 20 | "@bazel_gazelle//label:go_default_library", 21 | "@bazel_gazelle//resolve:go_default_library", 22 | "@bazel_gazelle//rule:go_default_library", 23 | "@com_github_bazelbuild_buildtools//build:go_default_library", 24 | "@com_github_rs_zerolog//:zerolog", 25 | "@com_github_stretchr_testify//mock", 26 | ], 27 | ) 28 | 29 | package_filegroup( 30 | name = "filegroup", 31 | srcs = [ 32 | "BUILD.bazel", 33 | "ConflictResolver.go", 34 | "Scope.go", 35 | "SymbolProvider.go", 36 | "SymbolResolver.go", 37 | "Universe.go", 38 | "symbol_capturer.go", 39 | ], 40 | visibility = ["//visibility:public"], 41 | ) 42 | -------------------------------------------------------------------------------- /pkg/resolver/mocks/SymbolResolver.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | config "github.com/bazelbuild/bazel-gazelle/config" 7 | label "github.com/bazelbuild/bazel-gazelle/label" 8 | 9 | mock "github.com/stretchr/testify/mock" 10 | 11 | resolve "github.com/bazelbuild/bazel-gazelle/resolve" 12 | 13 | resolver "github.com/stackb/scala-gazelle/pkg/resolver" 14 | ) 15 | 16 | // SymbolResolver is an autogenerated mock type for the SymbolResolver type 17 | type SymbolResolver struct { 18 | mock.Mock 19 | } 20 | 21 | // ResolveSymbol provides a mock function with given fields: c, ix, from, lang, sym 22 | func (_m *SymbolResolver) ResolveSymbol(c *config.Config, ix *resolve.RuleIndex, from label.Label, lang string, sym string) (*resolver.Symbol, bool) { 23 | ret := _m.Called(c, ix, from, lang, sym) 24 | 25 | if len(ret) == 0 { 26 | panic("no return value specified for ResolveSymbol") 27 | } 28 | 29 | var r0 *resolver.Symbol 30 | var r1 bool 31 | if rf, ok := ret.Get(0).(func(*config.Config, *resolve.RuleIndex, label.Label, string, string) (*resolver.Symbol, bool)); ok { 32 | return rf(c, ix, from, lang, sym) 33 | } 34 | if rf, ok := ret.Get(0).(func(*config.Config, *resolve.RuleIndex, label.Label, string, string) *resolver.Symbol); ok { 35 | r0 = rf(c, ix, from, lang, sym) 36 | } else { 37 | if ret.Get(0) != nil { 38 | r0 = ret.Get(0).(*resolver.Symbol) 39 | } 40 | } 41 | 42 | if rf, ok := ret.Get(1).(func(*config.Config, *resolve.RuleIndex, label.Label, string, string) bool); ok { 43 | r1 = rf(c, ix, from, lang, sym) 44 | } else { 45 | r1 = ret.Get(1).(bool) 46 | } 47 | 48 | return r0, r1 49 | } 50 | 51 | // NewSymbolResolver creates a new instance of SymbolResolver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 52 | // The first argument is typically a *testing.T value. 53 | func NewSymbolResolver(t interface { 54 | mock.TestingT 55 | Cleanup(func()) 56 | }) *SymbolResolver { 57 | mock := &SymbolResolver{} 58 | mock.Mock.Test(t) 59 | 60 | t.Cleanup(func() { mock.AssertExpectations(t) }) 61 | 62 | return mock 63 | } 64 | -------------------------------------------------------------------------------- /pkg/resolver/mocks/symbol_capturer.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "testing" 5 | 6 | resolver "github.com/stackb/scala-gazelle/pkg/resolver" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | type SymbolCapturer struct { 11 | Registry *Scope 12 | Got []*resolver.Symbol 13 | } 14 | 15 | func (k *SymbolCapturer) capture(symbol *resolver.Symbol) bool { 16 | k.Got = append(k.Got, symbol) 17 | return true 18 | } 19 | 20 | func NewSymbolsCapturer(t *testing.T) *SymbolCapturer { 21 | c := &SymbolCapturer{ 22 | Registry: NewScope(t), 23 | } 24 | 25 | c.Registry. 26 | On("PutSymbol", mock.MatchedBy(c.capture)). 27 | Maybe(). 28 | Return(nil) 29 | 30 | return c 31 | } 32 | -------------------------------------------------------------------------------- /pkg/resolver/override_symbol_resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/config" 5 | "github.com/bazelbuild/bazel-gazelle/label" 6 | "github.com/bazelbuild/bazel-gazelle/resolve" 7 | 8 | sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" 9 | ) 10 | 11 | const overrideProviderName = "override" 12 | 13 | // OverrideSymbolResolver implements Resolver for gazelle:resolve directives. 14 | type OverrideSymbolResolver struct { 15 | lang string 16 | } 17 | 18 | func NewOverrideSymbolResolver(lang string) *OverrideSymbolResolver { 19 | return &OverrideSymbolResolver{lang} 20 | } 21 | 22 | // ResolveSymbol implements the SymbolResolver interface 23 | func (sr *OverrideSymbolResolver) ResolveSymbol(c *config.Config, ix *resolve.RuleIndex, from label.Label, lang string, imp string) (*Symbol, bool) { 24 | if to, ok := resolve.FindRuleWithOverride(c, resolve.ImportSpec{Lang: lang, Imp: imp}, sr.lang); ok { 25 | return NewSymbol(sppb.ImportType_OVERRIDE, imp, overrideProviderName, to), true 26 | } 27 | return nil, false 28 | } 29 | -------------------------------------------------------------------------------- /pkg/resolver/predefined_label_conflict_resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/config" 7 | "github.com/bazelbuild/bazel-gazelle/label" 8 | "github.com/bazelbuild/bazel-gazelle/rule" 9 | "github.com/rs/zerolog" 10 | ) 11 | 12 | func init() { 13 | cr := &PredefinedLabelConflictResolver{} 14 | GlobalConflictResolverRegistry().PutConflictResolver(cr.Name(), cr) 15 | } 16 | 17 | // PredefinedLabelConflictResolver implements a strategy where 18 | type PredefinedLabelConflictResolver struct { 19 | } 20 | 21 | // RegisterFlags implements part of the resolver.ConflictResolver interface. 22 | func (s *PredefinedLabelConflictResolver) Name() string { 23 | return "predefined_label" 24 | } 25 | 26 | // RegisterFlags implements part of the resolver.ConflictResolver interface. 27 | func (s *PredefinedLabelConflictResolver) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config, logger zerolog.Logger) { 28 | } 29 | 30 | // CheckFlags implements part of the resolver.ConflictResolver interface. 31 | func (s *PredefinedLabelConflictResolver) CheckFlags(fs *flag.FlagSet, c *config.Config) error { 32 | return nil 33 | } 34 | 35 | // ResolveConflict implements part of the resolver.ConflictResolver interface. 36 | // This implementation chooses symbols that have symbol.Label == label.NoLabel, 37 | // which is the scenario when a symbol is provided by the platform / compiler, 38 | // like "java.lang.String". 39 | func (s *PredefinedLabelConflictResolver) ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol) (*Symbol, bool) { 40 | if symbol.Label == label.NoLabel { 41 | return symbol, true 42 | } 43 | for _, sym := range symbol.Conflicts { 44 | if sym.Label == label.NoLabel { 45 | return sym, true 46 | } 47 | } 48 | return nil, false 49 | } 50 | -------------------------------------------------------------------------------- /pkg/resolver/predefined_label_conflict_resolver_test.go: -------------------------------------------------------------------------------- 1 | package resolver_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/label" 7 | "github.com/bazelbuild/bazel-gazelle/rule" 8 | "github.com/google/go-cmp/cmp" 9 | "github.com/stackb/scala-gazelle/pkg/resolver" 10 | "github.com/stackb/scala-gazelle/pkg/resolver/mocks" 11 | ) 12 | 13 | func TestPredefinedLabelConflictResolver(t *testing.T) { 14 | for name, tc := range map[string]struct { 15 | symbol resolver.Symbol 16 | rule rule.Rule 17 | imports resolver.ImportMap 18 | imp resolver.Import 19 | name string 20 | want *resolver.Symbol 21 | wantOk bool 22 | }{ 23 | "degenerate": { 24 | symbol: resolver.Symbol{ 25 | Name: "foo.Bar", 26 | Label: label.Label{Pkg: "foo", Name: "bar"}, 27 | }, 28 | }, 29 | "takes conflict without a label": { 30 | symbol: resolver.Symbol{ 31 | Name: "foo.Bar", 32 | Label: label.Label{Pkg: "foo", Name: "bar"}, 33 | Conflicts: []*resolver.Symbol{ 34 | { 35 | Name: "foo.Bar", 36 | Label: label.NoLabel, 37 | }, 38 | }, 39 | }, 40 | want: &resolver.Symbol{ 41 | Name: "foo.Bar", 42 | Label: label.NoLabel, 43 | }, 44 | wantOk: true, 45 | }, 46 | } { 47 | t.Run(name, func(t *testing.T) { 48 | universe := mocks.NewUniverse(t) 49 | resolver := resolver.PredefinedLabelConflictResolver{} 50 | got, gotOk := resolver.ResolveConflict(universe, &tc.rule, tc.imports, &tc.imp, &tc.symbol) 51 | if diff := cmp.Diff(tc.wantOk, gotOk); diff != "" { 52 | t.Errorf("ok (-want +got):\n%s", diff) 53 | } 54 | if diff := cmp.Diff(tc.want, got); diff != "" { 55 | t.Errorf("(-want +got):\n%s", diff) 56 | } 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pkg/resolver/preferred_deps_conflict_resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/config" 7 | "github.com/bazelbuild/bazel-gazelle/label" 8 | "github.com/bazelbuild/bazel-gazelle/rule" 9 | "github.com/rs/zerolog" 10 | ) 11 | 12 | func NewPreferredDepsConflictResolver(name string, preferred map[string]label.Label) *PreferredDepsConflictResolver { 13 | return &PreferredDepsConflictResolver{name, preferred} 14 | } 15 | 16 | // PreferredDepsConflictResolver implements a strategy where 17 | type PreferredDepsConflictResolver struct { 18 | name string 19 | preferred map[string]label.Label 20 | } 21 | 22 | // RegisterFlags implements part of the resolver.ConflictResolver interface. 23 | func (s *PreferredDepsConflictResolver) Name() string { 24 | return s.name 25 | } 26 | 27 | // RegisterFlags implements part of the resolver.ConflictResolver interface. 28 | func (s *PreferredDepsConflictResolver) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config, logger zerolog.Logger) { 29 | } 30 | 31 | // CheckFlags implements part of the resolver.ConflictResolver interface. 32 | func (s *PreferredDepsConflictResolver) CheckFlags(fs *flag.FlagSet, c *config.Config) error { 33 | return nil 34 | } 35 | 36 | // ResolveConflict implements part of the resolver.ConflictResolver interface. 37 | // This implementation uses the preferred package map to try and match the 38 | // symbol.Name against the preferred package key. If found, the matching dep 39 | // Label is used to select the correct one from the conflict list. 40 | func (s *PreferredDepsConflictResolver) ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol) (*Symbol, bool) { 41 | if want, ok := s.preferred[symbol.Name]; ok { 42 | if symbol.Label == want { 43 | return symbol, true 44 | } 45 | for _, sym := range symbol.Conflicts { 46 | if sym.Label == want { 47 | return sym, true 48 | } 49 | } 50 | } 51 | return nil, false 52 | } 53 | -------------------------------------------------------------------------------- /pkg/resolver/scala_symbol_resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/config" 7 | "github.com/bazelbuild/bazel-gazelle/label" 8 | "github.com/bazelbuild/bazel-gazelle/resolve" 9 | ) 10 | 11 | // ScalaSymbolResolver implements SymbolResolver for scala imports. Patterns 12 | // like '_root_.' or "._" are stripped. 13 | type ScalaSymbolResolver struct { 14 | next SymbolResolver 15 | } 16 | 17 | // NewScalaSymbolResolver constructs a new resolver that chains to the given 18 | // resolver. 19 | func NewScalaSymbolResolver(next SymbolResolver) *ScalaSymbolResolver { 20 | return &ScalaSymbolResolver{next} 21 | } 22 | 23 | // ResolveSymbol implements the SymbolResolver interface 24 | func (sr *ScalaSymbolResolver) ResolveSymbol(c *config.Config, ix *resolve.RuleIndex, from label.Label, lang string, imp string) (*Symbol, bool) { 25 | imp = strings.TrimPrefix(imp, "_root_.") 26 | imp = strings.TrimSuffix(imp, "._") 27 | return sr.next.ResolveSymbol(c, ix, from, lang, imp) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/resolver/scope.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import "fmt" 4 | 5 | // Scope is an index of known symbols. 6 | type Scope interface { 7 | fmt.Stringer 8 | 9 | // GetScope returns a scope for th symbol under the given prefix. 10 | GetScope(name string) (Scope, bool) 11 | 12 | // GetSymbol does a lookup of the given import symbol and returns the 13 | // known import. If not known `(nil, false)` is returned. 14 | GetSymbol(name string) (*Symbol, bool) 15 | 16 | // GetSymbols does a lookup of the given prefix and returns the 17 | // symbols. 18 | GetSymbols(prefix string) []*Symbol 19 | 20 | // PutSymbol adds the given known import to the registry. It is an 21 | // error to attempt duplicate registration of the same import twice. 22 | PutSymbol(known *Symbol) error 23 | } 24 | -------------------------------------------------------------------------------- /pkg/resolver/scope_map_test.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/google/go-cmp/cmp" 7 | ) 8 | 9 | func TestScopeMapAdd(t *testing.T) { 10 | for name, tc := range map[string]struct { 11 | symbol Symbol 12 | want bool 13 | }{ 14 | "degenerate": {}, 15 | "not added": { 16 | symbol: Symbol{Name: "nope"}, 17 | want: false, 18 | }, 19 | "added": { 20 | symbol: Symbol{Name: "a.b"}, 21 | want: true, 22 | }, 23 | } { 24 | t.Run(name, func(t *testing.T) { 25 | scope := make(SymbolMap) 26 | got := scope.Add(&tc.symbol) 27 | 28 | if diff := cmp.Diff(tc.want, got); diff != "" { 29 | t.Errorf("(-want +got):\n%s", diff) 30 | } 31 | }) 32 | } 33 | } 34 | 35 | func TestSymbolBasename(t *testing.T) { 36 | type result struct { 37 | Basename string 38 | Ok bool 39 | } 40 | for name, tc := range map[string]struct { 41 | name string 42 | want result 43 | }{ 44 | "degenerate": {}, 45 | "typical": { 46 | name: "com.foo.Bar", 47 | want: result{"Bar", true}, 48 | }, 49 | "no dot": { 50 | name: "com_foo_Bar", 51 | }, 52 | } { 53 | t.Run(name, func(t *testing.T) { 54 | basename, ok := symbolBasename(tc.name) 55 | got := result{basename, ok} 56 | 57 | if diff := cmp.Diff(tc.want, got); diff != "" { 58 | t.Errorf("(-want +got):\n%s", diff) 59 | } 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/resolver/scope_symbol_resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/config" 5 | "github.com/bazelbuild/bazel-gazelle/label" 6 | "github.com/bazelbuild/bazel-gazelle/resolve" 7 | ) 8 | 9 | // ScopeSymbolResolver implements Resolver for symbols. 10 | type ScopeSymbolResolver struct { 11 | scope Scope 12 | } 13 | 14 | func NewScopeSymbolResolver(scope Scope) *ScopeSymbolResolver { 15 | return &ScopeSymbolResolver{scope: scope} 16 | } 17 | 18 | // ResolveSymbol implements the ImportResolver interface 19 | func (sr *ScopeSymbolResolver) ResolveSymbol(c *config.Config, ix *resolve.RuleIndex, from label.Label, lang string, symbol string) (*Symbol, bool) { 20 | if sym, ok := sr.scope.GetSymbol(symbol); ok { 21 | return sym, true 22 | } 23 | return nil, false 24 | } 25 | -------------------------------------------------------------------------------- /pkg/resolver/symbol_map.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import "strings" 4 | 5 | // SymbolMap is a map of symbols that are in a scope. For the import 6 | // 'com.foo.Bar', the map key is 'Bar' and the map value is the known import 7 | // for it. 8 | type SymbolMap map[string]*Symbol 9 | 10 | // Add tries to put the given import into the scope. If the import does not 11 | // have a valid base name, returns false. 12 | func (s SymbolMap) Add(known *Symbol) bool { 13 | if basename, ok := symbolBasename(known.Name); ok { 14 | s[basename] = known 15 | return true 16 | } 17 | return false 18 | } 19 | 20 | // Get returns a symbol in scope. 21 | func (s SymbolMap) Get(basename string) (*Symbol, bool) { 22 | known, ok := s[basename] 23 | return known, ok 24 | } 25 | 26 | func symbolBasename(imp string) (string, bool) { 27 | index := strings.LastIndex(imp, ".") 28 | if index <= 0 { 29 | return "", false 30 | } 31 | return imp[index+1:], true 32 | } 33 | -------------------------------------------------------------------------------- /pkg/resolver/symbol_provider.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/config" 7 | "github.com/bazelbuild/bazel-gazelle/label" 8 | "github.com/bazelbuild/bazel-gazelle/rule" 9 | "github.com/bazelbuild/buildtools/build" 10 | ) 11 | 12 | // SymbolProvider is a flag-configurable entity that supplies symbols 13 | // to a registry. 14 | type SymbolProvider interface { 15 | // Providers have canonical names 16 | Name() string 17 | // RegisterFlags configures the flags. RegisterFlags is called for all 18 | // providers whether they are enabled or not. 19 | RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) 20 | // CheckFlags asserts that the flags are correct and provides a scope to 21 | // provide symbols to. CheckFlags is only called if the provider is 22 | // enabled. 23 | CheckFlags(fs *flag.FlagSet, c *config.Config, scope Scope) error 24 | // OnResolve is a lifecycle hook that gets called when the resolve phase has 25 | // started. 26 | OnResolve() error 27 | // OnEnd is a lifecycle hook that gets called when the resolve phase has 28 | // ended. 29 | OnEnd() error 30 | // Providers typically manage a particular sub-space of labels. For 31 | // example, the maven resolver may return true for labels like 32 | // "@maven//:junit_junit". The rule Index can be used to consult what type 33 | // of label from is, based on the rule characteristics. 34 | CanProvide(dep *ImportLabel, expr build.Expr, knownRule func(from label.Label) (*rule.Rule, bool), from label.Label) bool 35 | } 36 | -------------------------------------------------------------------------------- /pkg/resolver/symbol_provider_registry.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | // SymbolProviderRegistry is an index of import providers. 4 | type SymbolProviderRegistry interface { 5 | // SymbolProviders returns a list of all known providers. 6 | SymbolProviders() []SymbolProvider 7 | 8 | // AddSymbolProvider adds the given known import provider to the 9 | // registry. It is an error to add the same namedprovider twice. 10 | AddSymbolProvider(provider SymbolProvider) error 11 | } 12 | -------------------------------------------------------------------------------- /pkg/resolver/symbol_resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/config" 7 | "github.com/bazelbuild/bazel-gazelle/label" 8 | "github.com/bazelbuild/bazel-gazelle/resolve" 9 | ) 10 | 11 | // ErrSymbolNotFound is an error value assigned to an Import when the name could 12 | // not be resolved. 13 | var ErrSymbolNotFound = fmt.Errorf("symbol not found") 14 | 15 | // SymbolResolver knows how to resolve imports. 16 | type SymbolResolver interface { 17 | // ResolveSymbol takes the given config, gazelle rule index, and an 18 | // import to resolve. 19 | ResolveSymbol(c *config.Config, ix *resolve.RuleIndex, from label.Label, lang string, sym string) (*Symbol, bool) 20 | } 21 | -------------------------------------------------------------------------------- /pkg/resolver/symbol_test.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bazelbuild/bazel-gazelle/label" 7 | "github.com/google/go-cmp/cmp" 8 | ) 9 | 10 | func TestSymbolConflicts(t *testing.T) { 11 | for name, tc := range map[string]struct { 12 | symbol Symbol 13 | conflicts []*Symbol 14 | want []*Symbol 15 | }{ 16 | "degenerate": {}, 17 | "typical case": { 18 | conflicts: []*Symbol{ 19 | { 20 | Label: label.Label{Name: "a"}, 21 | }, 22 | { 23 | Label: label.Label{Name: "b"}, 24 | }, 25 | }, 26 | want: []*Symbol{ 27 | { 28 | Label: label.Label{Name: "a"}, 29 | }, 30 | { 31 | Label: label.Label{Name: "b"}, 32 | }, 33 | }, 34 | }, 35 | "keeps symbols without labels": { 36 | conflicts: []*Symbol{ 37 | { 38 | Label: label.Label{Name: "a"}, 39 | }, 40 | { 41 | Label: label.NoLabel, 42 | }, 43 | }, 44 | want: []*Symbol{ 45 | { 46 | Label: label.Label{Name: "a"}, 47 | }, 48 | { 49 | Label: label.NoLabel, 50 | }, 51 | }, 52 | }, 53 | } { 54 | t.Run(name, func(t *testing.T) { 55 | for _, sym := range tc.conflicts { 56 | tc.symbol.Conflict(sym) 57 | } 58 | got := tc.symbol.Conflicts 59 | if diff := cmp.Diff(tc.want, got); diff != "" { 60 | t.Errorf("(-want +got):\n%s", diff) 61 | } 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pkg/resolver/trim_prefix_scope.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // TrimPrefixScope implements Scope that trims a prefix from the lookup name. 9 | type TrimPrefixScope struct { 10 | prefix string 11 | next Scope 12 | } 13 | 14 | func NewTrimPrefixScope(prefix string, next Scope) *TrimPrefixScope { 15 | return &TrimPrefixScope{ 16 | prefix: prefix, 17 | next: next, 18 | } 19 | } 20 | 21 | // PutSymbol is not supported and will panic. 22 | func (r *TrimPrefixScope) PutSymbol(known *Symbol) error { 23 | return fmt.Errorf("unsupported operation: PutSymbol") 24 | } 25 | 26 | // GetSymbol implements part of the Scope interface 27 | func (r *TrimPrefixScope) GetSymbol(name string) (*Symbol, bool) { 28 | return r.next.GetSymbol(strings.TrimPrefix(name, r.prefix)) 29 | } 30 | 31 | // GetScope implements part of the resolver.Scope interface. 32 | func (r *TrimPrefixScope) GetScope(name string) (Scope, bool) { 33 | return r.next.GetScope(strings.TrimPrefix(name, r.prefix)) 34 | } 35 | 36 | // GetSymbols implements part of the Scope interface 37 | func (r *TrimPrefixScope) GetSymbols(name string) []*Symbol { 38 | return r.next.GetSymbols(strings.TrimPrefix(name, r.prefix)) 39 | } 40 | 41 | // String implements the fmt.Stringer interface 42 | func (r *TrimPrefixScope) String() string { 43 | return r.next.String() 44 | } 45 | -------------------------------------------------------------------------------- /pkg/resolver/universe.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | // Universe is a mashup of interfaces that represents all known symbols, rules, 4 | // etc. 5 | type Universe interface { 6 | SymbolProviderRegistry 7 | KnownRuleRegistry 8 | ConflictResolverRegistry 9 | DepsCleanerRegistry 10 | Scope 11 | SymbolResolver 12 | } 13 | -------------------------------------------------------------------------------- /pkg/scalaconfig/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 3 | 4 | go_library( 5 | name = "scalaconfig", 6 | srcs = ["config.go"], 7 | importpath = "github.com/stackb/scala-gazelle/pkg/scalaconfig", 8 | visibility = ["//visibility:public"], 9 | deps = [ 10 | "//pkg/collections", 11 | "//pkg/resolver", 12 | "//pkg/scalarule", 13 | "@bazel_gazelle//config:go_default_library", 14 | "@bazel_gazelle//label:go_default_library", 15 | "@bazel_gazelle//resolve:go_default_library", 16 | "@bazel_gazelle//rule:go_default_library", 17 | "@com_github_bazelbuild_buildtools//build:go_default_library", 18 | "@com_github_bmatcuk_doublestar_v4//:doublestar", 19 | "@com_github_rs_zerolog//:zerolog", 20 | ], 21 | ) 22 | 23 | go_test( 24 | name = "scalaconfig_test", 25 | srcs = ["config_test.go"], 26 | embed = [":scalaconfig"], 27 | deps = [ 28 | "//pkg/resolver", 29 | "//pkg/resolver/mocks", 30 | "//pkg/scalarule", 31 | "//pkg/testutil", 32 | "@bazel_gazelle//config:go_default_library", 33 | "@bazel_gazelle//label:go_default_library", 34 | "@bazel_gazelle//resolve:go_default_library", 35 | "@bazel_gazelle//rule:go_default_library", 36 | "@com_github_google_go_cmp//cmp", 37 | "@com_github_google_go_cmp//cmp/cmpopts", 38 | "@com_github_rs_zerolog//:zerolog", 39 | "@com_github_stretchr_testify//mock", 40 | ], 41 | ) 42 | 43 | package_filegroup( 44 | name = "filegroup", 45 | srcs = [ 46 | "BUILD.bazel", 47 | "config.go", 48 | "config_test.go", 49 | ], 50 | visibility = ["//visibility:public"], 51 | ) 52 | -------------------------------------------------------------------------------- /pkg/scalafiles/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | 4 | go_library( 5 | name = "scalafiles", 6 | srcs = [ 7 | "scala_files.go", 8 | "scala_fileset.go", 9 | ], 10 | importpath = "github.com/stackb/scala-gazelle/pkg/scalafiles", 11 | visibility = ["//visibility:public"], 12 | deps = [ 13 | "//build/stack/gazelle/scala/parse", 14 | "//pkg/collections", 15 | "//pkg/scalarule", 16 | "@bazel_gazelle//config:go_default_library", 17 | "@bazel_gazelle//resolve:go_default_library", 18 | "@bazel_gazelle//rule:go_default_library", 19 | ], 20 | ) 21 | 22 | package_filegroup( 23 | name = "filegroup", 24 | srcs = [ 25 | "BUILD.bazel", 26 | "scala_files.go", 27 | "scala_fileset.go", 28 | ], 29 | visibility = ["//visibility:public"], 30 | ) 31 | -------------------------------------------------------------------------------- /pkg/scalarule/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | 4 | go_library( 5 | name = "scalarule", 6 | srcs = [ 7 | "config.go", 8 | "global_provider_registry.go", 9 | "package.go", 10 | "provider.go", 11 | "provider_registry.go", 12 | "rule.go", 13 | "rule_provider.go", 14 | "rule_resolver.go", 15 | ], 16 | importpath = "github.com/stackb/scala-gazelle/pkg/scalarule", 17 | visibility = ["//visibility:public"], 18 | deps = [ 19 | "//build/stack/gazelle/scala/parse", 20 | "//pkg/collections", 21 | "//pkg/resolver", 22 | "@bazel_gazelle//config:go_default_library", 23 | "@bazel_gazelle//label:go_default_library", 24 | "@bazel_gazelle//language:go_default_library", 25 | "@bazel_gazelle//resolve:go_default_library", 26 | "@bazel_gazelle//rule:go_default_library", 27 | "@com_github_rs_zerolog//:zerolog", 28 | ], 29 | ) 30 | 31 | package_filegroup( 32 | name = "filegroup", 33 | srcs = [ 34 | "BUILD.bazel", 35 | "config.go", 36 | "global_provider_registry.go", 37 | "package.go", 38 | "provider.go", 39 | "provider_registry.go", 40 | "rule.go", 41 | "rule_provider.go", 42 | "rule_resolver.go", 43 | ], 44 | visibility = ["//visibility:public"], 45 | ) 46 | -------------------------------------------------------------------------------- /pkg/scalarule/global_provider_registry.go: -------------------------------------------------------------------------------- 1 | package scalarule 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | // globalProviderRegistry is the default registry singleton. 9 | var globalProviderRegistry = NewProviderRegistryMap() 10 | 11 | // GlobalProviderRegistry returns a reference to the global ProviderRegistry 12 | // implementation. 13 | func GlobalProviderRegistry() ProviderRegistry { 14 | return globalProviderRegistry 15 | } 16 | 17 | // ProviderRegistryMap implements ProviderRegistry using a map. 18 | type ProviderRegistryMap struct { 19 | providers map[string]Provider 20 | } 21 | 22 | func NewProviderRegistryMap() *ProviderRegistryMap { 23 | return &ProviderRegistryMap{ 24 | providers: make(map[string]Provider), 25 | } 26 | } 27 | 28 | // ProviderNames implements part of the ProviderRegistry interface. 29 | func (p *ProviderRegistryMap) ProviderNames() []string { 30 | names := make([]string, 0, len(p.providers)) 31 | for name := range p.providers { 32 | names = append(names, name) 33 | } 34 | sort.Strings(names) 35 | return names 36 | } 37 | 38 | // RegisterProvider implements part of the ProviderRegistry interface. 39 | func (p *ProviderRegistryMap) RegisterProvider(name string, provider Provider) error { 40 | _, ok := p.providers[name] 41 | if ok { 42 | return fmt.Errorf("duplicate rule provider registration: %q", name) 43 | } 44 | p.providers[name] = provider 45 | return nil 46 | } 47 | 48 | // LookupProvider implements part of the RuleRegistry interface. 49 | func (p *ProviderRegistryMap) LookupProvider(name string) (Provider, bool) { 50 | provider, ok := p.providers[name] 51 | if !ok { 52 | return nil, false 53 | } 54 | return provider, true 55 | } 56 | -------------------------------------------------------------------------------- /pkg/scalarule/mocks/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | 4 | go_library( 5 | name = "mocks", 6 | srcs = ["ProviderRegistry.go"], 7 | importpath = "github.com/stackb/scala-gazelle/pkg/scalarule/mocks", 8 | visibility = ["//visibility:public"], 9 | deps = [ 10 | "//pkg/scalarule", 11 | "@com_github_stretchr_testify//mock", 12 | ], 13 | ) 14 | 15 | package_filegroup( 16 | name = "filegroup", 17 | srcs = [ 18 | "BUILD.bazel", 19 | "ProviderRegistry.go", 20 | ], 21 | visibility = ["//visibility:public"], 22 | ) 23 | -------------------------------------------------------------------------------- /pkg/scalarule/package.go: -------------------------------------------------------------------------------- 1 | package scalarule 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/language" 5 | grule "github.com/bazelbuild/bazel-gazelle/rule" 6 | ) 7 | 8 | // Package is responsible for instantiating a Rule interface for the given 9 | // gazelle.Rule, parsing the attribute name given (typically 'srcs'). 10 | type Package interface { 11 | // ParseRule parses the sources from the named attr (typically 'srcs') and 12 | // created a new Rule. 13 | ParseRule(r *grule.Rule, attrName string) (Rule, error) 14 | // GenerateArgs returns the GenerateArgs for the package 15 | GenerateArgs() language.GenerateArgs 16 | // GeneratedRules returns a list of generated rules in the package. 17 | GeneratedRules() []*grule.Rule 18 | } 19 | -------------------------------------------------------------------------------- /pkg/scalarule/provider.go: -------------------------------------------------------------------------------- 1 | package scalarule 2 | 3 | import grule "github.com/bazelbuild/bazel-gazelle/rule" 4 | 5 | // Provider is factory pattern capable of taking a config and package and 6 | // returning a Provider. 7 | type Provider interface { 8 | // Name returns the name of the rule 9 | Name() string 10 | // LoadInfo returns the gazelle LoadInfo. 11 | LoadInfo() grule.LoadInfo 12 | // KindInfo returns the gazelle KindInfo. 13 | KindInfo() grule.KindInfo 14 | // ProvideRule takes the given configuration and compilation and emits a 15 | // RuleProvider. If the state of the ScalaConfiguration is such that the 16 | // rule should not be emitted, implementation should return nil. 17 | ProvideRule(rc *Config, pkg Package) RuleProvider 18 | } 19 | -------------------------------------------------------------------------------- /pkg/scalarule/provider_registry.go: -------------------------------------------------------------------------------- 1 | package scalarule 2 | 3 | // ProviderRegistry represents a library of rule provider implementations. 4 | type ProviderRegistry interface { 5 | // ProviderNames returns a sorted list of rule names. 6 | ProviderNames() []string 7 | // LookupProvider returns the implementation under the given name. If the 8 | // rule is not found, false is returned. 9 | LookupProvider(name string) (Provider, bool) 10 | // RegisterProvider installs a Provider implementation under the given name 11 | // in the global rule registry. Error will occur if the same rule is 12 | // registered multiple times. 13 | RegisterProvider(name string, provider Provider) error 14 | } 15 | -------------------------------------------------------------------------------- /pkg/scalarule/rule.go: -------------------------------------------------------------------------------- 1 | package scalarule 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/label" 5 | "github.com/bazelbuild/bazel-gazelle/resolve" 6 | 7 | sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" 8 | "github.com/stackb/scala-gazelle/pkg/resolver" 9 | ) 10 | 11 | // Rule represents a collection of files with their imports and exports. 12 | type Rule interface { 13 | // Exports returns the list of provided symbols that are importable by other 14 | // rules. 15 | Provides() []resolve.ImportSpec 16 | // Import returns the list of required imports for the rule. 17 | Imports(from label.Label) resolver.ImportMap 18 | // Import returns the list of required exports for the rule. 19 | Exports(from label.Label) resolver.ImportMap 20 | // Files returns the list of files in the Rule 21 | Files() []*sppb.File 22 | } 23 | -------------------------------------------------------------------------------- /pkg/scalarule/rule_provider.go: -------------------------------------------------------------------------------- 1 | package scalarule 2 | 3 | import ( 4 | "github.com/bazelbuild/bazel-gazelle/config" 5 | "github.com/bazelbuild/bazel-gazelle/label" 6 | "github.com/bazelbuild/bazel-gazelle/resolve" 7 | "github.com/bazelbuild/bazel-gazelle/rule" 8 | ) 9 | 10 | // ResolveContext carries context about a rule during rule provider import 11 | // resolution. 12 | type ResolveContext struct { 13 | Config *config.Config 14 | RuleIndex *resolve.RuleIndex 15 | Rule *rule.Rule 16 | From label.Label 17 | File *rule.File 18 | } 19 | 20 | // RuleProvider implementations are capable of providing a rule and import list 21 | // to the gazelle GenerateArgs response. 22 | type RuleProvider interface { 23 | // Kind of rule e.g. 'scala_library' 24 | Kind() string 25 | // Name of the rule. 26 | Name() string 27 | // Rule provides the gazelle rule implementation. 28 | Rule() *rule.Rule 29 | // Resolve performs deps resolution, similar to the gazelle Resolver 30 | // interface. 31 | Resolve(ctx *ResolveContext, importsRaw interface{}) 32 | // Imports implements part of the resolve.Resolver interface. 33 | Imports(c *config.Config, r *rule.Rule, file *rule.File) []resolve.ImportSpec 34 | } 35 | -------------------------------------------------------------------------------- /pkg/scalarule/rule_resolver.go: -------------------------------------------------------------------------------- 1 | package scalarule 2 | 3 | import "github.com/bazelbuild/bazel-gazelle/rule" 4 | 5 | // RuleResolver is an optional interface for a RuleInfo implementation. This is 6 | // a mechanism for rule implementations to only modify an existing rule rather 7 | // than having to create one from scratch. 8 | type RuleResolver interface { 9 | // ResolveRule takes the given configuration emits a RuleProvider. If the 10 | // state of the package is such that the rule should not managed, return nil. 11 | ResolveRule(rc *Config, pkg Package, existing *rule.Rule) RuleProvider 12 | } 13 | -------------------------------------------------------------------------------- /pkg/semanticdb/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 3 | 4 | # gazelle:exclude testdata 5 | 6 | go_library( 7 | name = "semanticdb", 8 | srcs = [ 9 | "globalscope.go", 10 | "io.go", 11 | "semanticdb.go", 12 | "semanticdb_index.go", 13 | "visitor.go", 14 | ], 15 | importpath = "github.com/stackb/scala-gazelle/pkg/semanticdb", 16 | visibility = ["//visibility:public"], 17 | deps = [ 18 | "//pkg/resolver", 19 | "//pkg/scalarule", 20 | "//scala/meta/semanticdb", 21 | "@bazel_gazelle//config:go_default_library", 22 | "@bazel_gazelle//label:go_default_library", 23 | "@bazel_gazelle//resolve:go_default_library", 24 | "@bazel_gazelle//rule:go_default_library", 25 | "@org_golang_google_protobuf//proto", 26 | ], 27 | ) 28 | 29 | go_test( 30 | name = "semanticdb_test", 31 | srcs = [ 32 | "semanticdb_test.go", 33 | "visitor_test.go", 34 | ], 35 | data = glob(["testdata/**/*"]), 36 | embed = [":semanticdb"], 37 | deps = [ 38 | "//build/stack/gazelle/scala/parse", 39 | "//pkg/protobuf", 40 | "//scala/meta/semanticdb", 41 | "@com_github_google_go_cmp//cmp", 42 | "@com_github_google_go_cmp//cmp/cmpopts", 43 | ], 44 | ) 45 | 46 | package_filegroup( 47 | name = "filegroup", 48 | srcs = [ 49 | "BUILD.bazel", 50 | "README.md", 51 | "globalscope.go", 52 | "io.go", 53 | "semanticdb.go", 54 | "semanticdb_index.go", 55 | "semanticdb_test.go", 56 | "visitor.go", 57 | "visitor_test.go", 58 | ], 59 | visibility = ["//visibility:public"], 60 | ) 61 | -------------------------------------------------------------------------------- /pkg/semanticdb/README.md: -------------------------------------------------------------------------------- 1 | # pkg/semanticdb 2 | 3 | Support for reading semanticdb files 4 | 5 | To regenerate the golden files: 6 | 7 | ``` 8 | bazel run //pkg/semanicdb:semanicdb_test -- -update 9 | ``` 10 | -------------------------------------------------------------------------------- /pkg/semanticdb/globalscope.go: -------------------------------------------------------------------------------- 1 | package semanticdb 2 | 3 | import ( 4 | "github.com/stackb/scala-gazelle/pkg/resolver" 5 | ) 6 | 7 | var globalScope resolver.Scope 8 | 9 | func SetGlobalScope(scope resolver.Scope) { 10 | globalScope = scope 11 | } 12 | 13 | func GetGlobalScope() resolver.Scope { 14 | return globalScope 15 | } 16 | -------------------------------------------------------------------------------- /pkg/semanticdb/semanticdb.go: -------------------------------------------------------------------------------- 1 | package semanticdb 2 | 3 | import ( 4 | spb "github.com/stackb/scala-gazelle/scala/meta/semanticdb" 5 | ) 6 | 7 | func SemanticImports(in *spb.TextDocument) []string { 8 | visitor := NewTextDocumentVisitor() 9 | visitor.VisitTextDocument(in) 10 | return visitor.SemanticImports() 11 | } 12 | -------------------------------------------------------------------------------- /pkg/semanticdb/testdata/testdata/stringlib.jar.textdocuments.json.golden/common/utils/string/src/package.scala.file.json: -------------------------------------------------------------------------------- 1 | { 2 | "semanticImports": [ 3 | "common.utils.string.package", 4 | "java.lang.String", 5 | "scala.AnyRef", 6 | "scala.Predef" 7 | ] 8 | } -------------------------------------------------------------------------------- /pkg/semanticdb/testdata/testdata/stringlib.jar.textdocuments.json.golden/trumid/common/utils/string/src/package.scala.file.json: -------------------------------------------------------------------------------- 1 | { 2 | "semanticImports": [ 3 | "java.lang.String", 4 | "scala.AnyRef", 5 | "scala.Predef", 6 | "trumid.common.utils.string.package" 7 | ] 8 | } -------------------------------------------------------------------------------- /pkg/semanticdb/visitor_test.go: -------------------------------------------------------------------------------- 1 | package semanticdb 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/google/go-cmp/cmp" 7 | ) 8 | 9 | func TestToImport(t *testing.T) { 10 | for name, tc := range map[string]struct { 11 | symbol string 12 | want string 13 | }{ 14 | "degenerate": {}, 15 | "class": { 16 | symbol: "scala/Unit#", 17 | want: "scala.Unit", 18 | }, 19 | "package function": { 20 | symbol: "omnistac/euds/package.isFinraTraceEvent().(r)", 21 | want: "omnistac.euds.package", 22 | }, 23 | "package": { 24 | symbol: "trumid/ats/limitsng/", 25 | want: "", 26 | }, 27 | } { 28 | t.Run(name, func(t *testing.T) { 29 | got := toImport(tc.symbol) 30 | if diff := cmp.Diff(tc.want, got); diff != "" { 31 | t.Errorf("(-want +got):\n%s", diff) 32 | } 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pkg/starlarkeval/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | 4 | go_library( 5 | name = "starlarkeval", 6 | srcs = [ 7 | "convert_ast.go", 8 | "convert_value.go", 9 | "interpreter.go", 10 | ], 11 | importpath = "github.com/stackb/scala-gazelle/pkg/starlarkeval", 12 | visibility = ["//visibility:public"], 13 | deps = [ 14 | "@com_github_bazelbuild_buildtools//build:go_default_library", 15 | "@net_starlark_go//starlark", 16 | "@net_starlark_go//syntax", 17 | ], 18 | ) 19 | 20 | package_filegroup( 21 | name = "filegroup", 22 | srcs = [ 23 | "BUILD.bazel", 24 | "convert_ast.go", 25 | "convert_value.go", 26 | "interpreter.go", 27 | ], 28 | visibility = ["//visibility:public"], 29 | ) 30 | -------------------------------------------------------------------------------- /pkg/starlarkeval/convert_value.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This file contains functions to convert from one AST to the other. 18 | // Input: AST from go.starlark.net/syntax 19 | // Output: AST from github.com/bazelbuild/buildtools/build 20 | 21 | package starlarkeval 22 | 23 | import ( 24 | "strconv" 25 | "strings" 26 | 27 | "github.com/bazelbuild/buildtools/build" 28 | "go.starlark.net/starlark" 29 | // "go.starlark.net/syntax" 30 | ) 31 | 32 | func ConvValue(value starlark.Value) build.Expr { 33 | switch t := value.(type) { 34 | case *starlark.Int: 35 | if val, ok := t.Int64(); ok { 36 | return &build.LiteralExpr{ 37 | Token: strconv.FormatInt(val, 10), 38 | } 39 | } 40 | case *starlark.String: 41 | return &build.StringExpr{ 42 | Value: t.GoString(), 43 | TripleQuote: strings.HasPrefix(t.String(), "\"\"\""), 44 | } 45 | } 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /pkg/starlarkeval/interpreter.go: -------------------------------------------------------------------------------- 1 | package starlarkeval 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | 8 | "go.starlark.net/starlark" 9 | ) 10 | 11 | type Interpreter struct { 12 | // Global state 13 | globals *starlark.StringDict 14 | // Thread context 15 | thread *starlark.Thread 16 | // Last eval error 17 | evalErr *starlark.EvalError 18 | // reporter 19 | reporter Reporter 20 | } 21 | 22 | // Reporter is implemented by *testing.T. 23 | type Reporter func(format string, args ...interface{}) 24 | 25 | func NewInterpreter(reporter Reporter) *Interpreter { 26 | interpreter := &Interpreter{ 27 | reporter: reporter, 28 | thread: &starlark.Thread{ 29 | Print: func(_ *starlark.Thread, msg string) { 30 | reporter(msg) 31 | }, 32 | }, 33 | } 34 | 35 | interpreter.globals = &starlark.StringDict{} 36 | 37 | return interpreter 38 | } 39 | 40 | func (i *Interpreter) GetGlobal(name string) starlark.Value { 41 | globals := *i.globals 42 | return globals[name] 43 | } 44 | 45 | func (i *Interpreter) handleDepset(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 46 | // always expect a single list argument for depsets 47 | if len(args) != 1 { 48 | return nil, fmt.Errorf("depset() expected a single positional argument of type List") 49 | } 50 | return args[0], nil 51 | } 52 | 53 | func (i *Interpreter) Exec(filename string, src io.Reader) error { 54 | data, err := io.ReadAll(src) 55 | if err != nil { 56 | return err 57 | } 58 | state, err := starlark.ExecFile(i.thread, filename, bytes.NewReader(data), *i.globals) 59 | i.globals = &state 60 | if evalErr, ok := err.(*starlark.EvalError); ok { 61 | fmt.Printf("EvalErr: %v\n", evalErr) 62 | i.evalErr = evalErr 63 | } 64 | return err 65 | } 66 | -------------------------------------------------------------------------------- /pkg/testutil/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | 4 | go_library( 5 | name = "testutil", 6 | testonly = True, 7 | srcs = [ 8 | "test_logger.go", 9 | "testutil.go", 10 | ], 11 | importpath = "github.com/stackb/scala-gazelle/pkg/testutil", 12 | visibility = ["//visibility:public"], 13 | deps = [ 14 | "//pkg/procutil", 15 | "@bazel_gazelle//testtools:go_default_library", 16 | ], 17 | ) 18 | 19 | package_filegroup( 20 | name = "filegroup", 21 | srcs = [ 22 | "BUILD.bazel", 23 | "test_logger.go", 24 | "testutil.go", 25 | ], 26 | visibility = ["//visibility:public"], 27 | ) 28 | -------------------------------------------------------------------------------- /pkg/testutil/test_logger.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // TestLogger is a wrapper around testing.T that implements a subset 8 | // of the log.Logger interface by forwarding log messages to t.Log 9 | type TestLogger struct { 10 | t *testing.T 11 | } 12 | 13 | // New creates a new TestLogger that writes to the provided testing.T 14 | func NewTestLogger(t *testing.T) *TestLogger { 15 | return &TestLogger{t: t} 16 | } 17 | 18 | // Printf formats the log message and writes it to the test log 19 | func (l *TestLogger) Printf(format string, v ...any) { 20 | l.t.Logf(format, v...) 21 | } 22 | 23 | // Print writes the log message to the test log 24 | func (l *TestLogger) Print(v ...any) { 25 | l.t.Log(v...) 26 | } 27 | 28 | // Println writes the log message to the test log with a newline 29 | func (l *TestLogger) Println(v ...any) { 30 | l.t.Log(v...) 31 | } 32 | 33 | // Fatal logs the message and fails the test 34 | func (l *TestLogger) Fatal(v ...any) { 35 | l.t.Fatal(v...) 36 | } 37 | 38 | // Fatalf logs the formatted message and fails the test 39 | func (l *TestLogger) Fatalf(format string, v ...any) { 40 | l.t.Fatalf(format, v...) 41 | } 42 | 43 | // Fatalln logs the message and fails the test 44 | func (l *TestLogger) Fatalln(v ...any) { 45 | l.t.Fatal(v...) 46 | } 47 | 48 | // Panic logs the message and fails the test 49 | func (l *TestLogger) Panic(v ...any) { 50 | l.t.Fatal(v...) 51 | } 52 | 53 | // Panicf logs the formatted message and fails the test 54 | func (l *TestLogger) Panicf(format string, v ...any) { 55 | l.t.Fatalf(format, v...) 56 | } 57 | 58 | // Panicln logs the message and fails the test 59 | func (l *TestLogger) Panicln(v ...any) { 60 | l.t.Fatal(v...) 61 | } 62 | -------------------------------------------------------------------------------- /pkg/wildcardimport/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 3 | 4 | go_library( 5 | name = "wildcardimport", 6 | srcs = [ 7 | "fixer.go", 8 | "scanner.go", 9 | "text_file.go", 10 | ], 11 | importpath = "github.com/stackb/scala-gazelle/pkg/wildcardimport", 12 | visibility = ["//visibility:public"], 13 | deps = ["//pkg/bazel"], 14 | ) 15 | 16 | go_test( 17 | name = "wildcardimport_test", 18 | srcs = [ 19 | "fixer_test.go", 20 | "scanner_test.go", 21 | ], 22 | embed = [":wildcardimport"], 23 | deps = ["@com_github_google_go_cmp//cmp"], 24 | ) 25 | 26 | package_filegroup( 27 | name = "filegroup", 28 | srcs = [ 29 | "BUILD.bazel", 30 | "fixer.go", 31 | "fixer_test.go", 32 | "scanner.go", 33 | "scanner_test.go", 34 | "text_file.go", 35 | ], 36 | visibility = ["//visibility:public"], 37 | ) 38 | -------------------------------------------------------------------------------- /pkg/wildcardimport/fixer_test.go: -------------------------------------------------------------------------------- 1 | package wildcardimport 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/google/go-cmp/cmp" 7 | ) 8 | 9 | func TestMakeImportLine(t *testing.T) { 10 | for name, tc := range map[string]struct { 11 | importPrefix string 12 | symbols []string 13 | wantErr string 14 | want string 15 | }{ 16 | "missing import prefix": { 17 | symbols: []string{}, 18 | wantErr: "importPrefix must not be empty", 19 | want: "", 20 | }, 21 | "no symbols": { 22 | symbols: []string{}, 23 | importPrefix: "foo", 24 | wantErr: "must have at least one symbol in list", 25 | want: "", 26 | }, 27 | "one symbol": { 28 | symbols: []string{"Bar"}, 29 | importPrefix: "foo", 30 | want: "import foo.Bar", 31 | }, 32 | "two symbols": { 33 | symbols: []string{"Bar", "Baz"}, 34 | importPrefix: "foo", 35 | want: "import foo.{Bar, Baz}", 36 | }, 37 | "sorts symbol list": { 38 | symbols: []string{"B", "A"}, 39 | importPrefix: "foo", 40 | want: "import foo.{A, B}", 41 | }, 42 | } { 43 | t.Run(name, func(t *testing.T) { 44 | got, err := makeImportLine(tc.importPrefix, tc.symbols) 45 | var gotErr string 46 | if err != nil { 47 | gotErr = err.Error() 48 | } 49 | if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" { 50 | t.Errorf("error (-want +got):\n%s", diff) 51 | } 52 | if diff := cmp.Diff(tc.want, got); diff != "" { 53 | t.Errorf("result (-want +got):\n%s", diff) 54 | } 55 | }) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pkg/wildcardimport/scanner.go: -------------------------------------------------------------------------------- 1 | package wildcardimport 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "log" 7 | "regexp" 8 | "sort" 9 | "strings" 10 | ) 11 | 12 | // omnistac/gum/testutils/DbDataInitUtils.scala:98: error: [rewritten by -quickfix] not found: value FixSessionDao 13 | var notFoundLine = regexp.MustCompile(`^(.*):\d+: error: .*not found: (value|type) ([A-Z].*)$`) 14 | 15 | type outputScanner struct { 16 | debug bool 17 | } 18 | 19 | func (s *outputScanner) scan(output []byte) ([]string, error) { 20 | notFound := make(map[string]bool) 21 | 22 | scanner := bufio.NewScanner(bytes.NewReader(output)) 23 | for scanner.Scan() { 24 | line := strings.TrimSpace(scanner.Text()) 25 | if line == "" { 26 | continue 27 | } 28 | if s.debug { 29 | log.Println("line:", line) 30 | } 31 | if match := notFoundLine.FindStringSubmatch(line); match != nil { 32 | typeOrValue := match[3] 33 | notFound[typeOrValue] = true 34 | continue 35 | } 36 | } 37 | if err := scanner.Err(); err != nil { 38 | return nil, err 39 | } 40 | 41 | list := make([]string, 0, len(notFound)) 42 | for k := range notFound { 43 | list = append(list, k) 44 | } 45 | sort.Strings(list) 46 | 47 | return list, nil 48 | } 49 | -------------------------------------------------------------------------------- /rules/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | 3 | package_filegroup( 4 | name = "filegroup", 5 | srcs = [ 6 | "BUILD.bazel", 7 | "artifacts.bzl", 8 | "java_index.bzl", 9 | "java_indexer_aspect.bzl", 10 | "package_filegroup.bzl", 11 | "providers.bzl", 12 | "scala_files.bzl", 13 | "semanticdb_index.bzl", 14 | ], 15 | visibility = ["//visibility:public"], 16 | ) 17 | -------------------------------------------------------------------------------- /rules/package_filegroup.bzl: -------------------------------------------------------------------------------- 1 | def package_filegroup(**kwargs): 2 | srcs = kwargs.pop("srcs", []) 3 | deps = kwargs.pop("deps", []) 4 | native.filegroup( 5 | srcs = srcs + deps, 6 | **kwargs 7 | ) 8 | -------------------------------------------------------------------------------- /rules/providers.bzl: -------------------------------------------------------------------------------- 1 | """providers.bzl contains Providers instances that are public. 2 | """ 3 | 4 | JarIndexerAspectInfo = provider( 5 | "a provider for Jar Indexing", 6 | fields = { 7 | "info_file": "The index database", 8 | "jar_index_files": "A list of jar index files", 9 | }, 10 | ) 11 | -------------------------------------------------------------------------------- /scala/meta/semanticdb/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@rules_proto//proto:defs.bzl", "proto_library") 4 | load("@build_stack_rules_proto//rules:proto_compiled_sources.bzl", "proto_compiled_sources") 5 | 6 | proto_library( 7 | name = "semanticdb_proto", 8 | srcs = ["semanticdb.proto"], 9 | visibility = ["//scala/meta:__subpackages__"], 10 | deps = ["//scalapb:scalapb_proto"], 11 | ) 12 | 13 | proto_compiled_sources( 14 | name = "semanticdb_go_compiled_sources", 15 | srcs = ["semanticdb.pb.go"], 16 | output_mappings = [ 17 | "semanticdb.pb.go=github.com/stackb/scala-gazelle/scala/meta/semanticdb/semanticdb.pb.go", 18 | ], 19 | plugins = ["@build_stack_rules_proto//plugin/golang/protobuf:protoc-gen-go"], 20 | proto = "semanticdb_proto", 21 | ) 22 | 23 | go_library( 24 | name = "semanticdb", 25 | srcs = ["semanticdb.pb.go"], 26 | importpath = "github.com/stackb/scala-gazelle/scala/meta/semanticdb", 27 | visibility = ["//visibility:public"], 28 | deps = [ 29 | "//scalapb", 30 | "@org_golang_google_protobuf//reflect/protoreflect", 31 | "@org_golang_google_protobuf//runtime/protoimpl", 32 | ], 33 | ) 34 | 35 | package_filegroup( 36 | name = "filegroup", 37 | srcs = [ 38 | "BUILD.bazel", 39 | "semanticdb.pb.go", 40 | "semanticdb.proto", 41 | ], 42 | visibility = ["//visibility:public"], 43 | ) 44 | -------------------------------------------------------------------------------- /scalapb/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@rules_proto//proto:defs.bzl", "proto_library") 4 | load("@build_stack_rules_proto//rules:proto_compiled_sources.bzl", "proto_compiled_sources") 5 | 6 | proto_library( 7 | name = "scalapb_proto", 8 | srcs = ["scalapb.proto"], 9 | visibility = ["//visibility:public"], 10 | deps = ["@com_google_protobuf//:descriptor_proto"], 11 | ) 12 | 13 | proto_compiled_sources( 14 | name = "scalapb_go_compiled_sources", 15 | srcs = ["scalapb.pb.go"], 16 | output_mappings = ["scalapb.pb.go=github.com/stackb/scala-gazelle/scalapb/scalapb.pb.go"], 17 | plugins = ["@build_stack_rules_proto//plugin/golang/protobuf:protoc-gen-go"], 18 | proto = "scalapb_proto", 19 | ) 20 | 21 | go_library( 22 | name = "scalapb", 23 | srcs = ["scalapb.pb.go"], 24 | importpath = "github.com/stackb/scala-gazelle/scalapb", 25 | visibility = ["//visibility:public"], 26 | deps = [ 27 | "@org_golang_google_protobuf//reflect/protoreflect", 28 | "@org_golang_google_protobuf//runtime/protoimpl", 29 | "@org_golang_google_protobuf//types/descriptorpb", 30 | ], 31 | ) 32 | 33 | package_filegroup( 34 | name = "filegroup", 35 | srcs = [ 36 | "BUILD.bazel", 37 | "scalapb.pb.go", 38 | "scalapb.proto", 39 | ], 40 | visibility = ["//visibility:public"], 41 | ) 42 | -------------------------------------------------------------------------------- /third_party/bazelbuild/bazel-gazelle/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | 3 | exports_files(["pr-1394.patch"]) 4 | 5 | package_filegroup( 6 | name = "filegroup", 7 | srcs = [ 8 | # "BUILD.bazel", 9 | "BUILD.bazel", 10 | "pr-1394.patch", 11 | ], 12 | visibility = ["//visibility:public"], 13 | ) 14 | -------------------------------------------------------------------------------- /tools/plantuml/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup") 2 | 3 | java_binary( 4 | name = "plantuml", 5 | srcs = ["Main.java"], 6 | main_class = "Main", 7 | visibility = ["//visibility:public"], 8 | deps = ["@plantuml_jar//jar"], 9 | ) 10 | 11 | package_filegroup( 12 | name = "filegroup", 13 | srcs = [ 14 | "BUILD.bazel", 15 | "Main.java", 16 | "plantuml.bzl", 17 | ], 18 | visibility = ["//visibility:public"], 19 | ) 20 | -------------------------------------------------------------------------------- /tools/plantuml/Main.java: -------------------------------------------------------------------------------- 1 | import net.sourceforge.plantuml.SourceStringReader; 2 | import net.sourceforge.plantuml.FileFormat; 3 | import net.sourceforge.plantuml.FileFormatOption; 4 | 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.nio.file.Paths; 9 | 10 | public class Main { 11 | public static void main(String[] args) { 12 | if (args.length != 2) { 13 | System.err.println("Usage: java PlantUMLToPNG "); 14 | System.exit(1); 15 | } 16 | 17 | String inputFilePath = args[0]; 18 | String outputFilePath = args[1]; 19 | 20 | try { 21 | // Read PlantUML source from file 22 | String source = new String(Files.readAllBytes(Paths.get(inputFilePath))); 23 | 24 | // Create a SourceStringReader 25 | SourceStringReader reader = new SourceStringReader(source); 26 | 27 | // Generate the PNG image 28 | FileOutputStream outputStream = new FileOutputStream(outputFilePath); 29 | reader.generateImage(outputStream, new FileFormatOption(FileFormat.PNG)); 30 | outputStream.close(); 31 | 32 | System.out.println("PNG image generated successfully!"); 33 | 34 | } catch (IOException e) { 35 | System.err.println("Error processing PlantUML file: " + e.getMessage()); 36 | e.printStackTrace(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /tools/plantuml/plantuml.bzl: -------------------------------------------------------------------------------- 1 | """plantuml_diagram.bzl provides the plantuml_diagram rule. 2 | """ 3 | 4 | def _plantuml_diagram_impl(ctx): 5 | args = ctx.actions.args() 6 | args.add(ctx.file.src.path) 7 | args.add(ctx.outputs.dst.path) 8 | 9 | ctx.actions.run( 10 | mnemonic = "PlantUML", 11 | progress_message = "Generating PlantUML file: %s" % ctx.outputs.dst.basename, 12 | executable = ctx.executable._plantuml_tool, 13 | arguments = [args], 14 | inputs = [ctx.file.src], 15 | outputs = [ctx.outputs.dst], 16 | ) 17 | 18 | return [DefaultInfo( 19 | files = depset([ctx.outputs.dst]), 20 | )] 21 | 22 | plantuml_diagram = rule( 23 | implementation = _plantuml_diagram_impl, 24 | attrs = { 25 | "src": attr.label( 26 | doc = "the plantuml source file", 27 | allow_single_file = True, 28 | ), 29 | "_plantuml_tool": attr.label( 30 | default = Label("@build_stack_scala_gazelle//tools/plantuml"), 31 | cfg = "exec", 32 | executable = True, 33 | doc = "the mergeindex tool", 34 | ), 35 | "dst": attr.output( 36 | mandatory = True, 37 | doc = "the output file for the diagram", 38 | ), 39 | }, 40 | ) 41 | 42 | def plantuml_diagram_genrule(name, srcs, format = "png", visibility = None): 43 | native.genrule( 44 | name = name, 45 | srcs = srcs, 46 | cmd = "java -jar $(location @plantuml_jar//jar) -t%s -o $@ $(SRCS)" % format, 47 | outs = [name + ".sh"], 48 | executable = True, 49 | tools = ["@plantuml_jar//jar"], 50 | visibility = visibility, 51 | ) 52 | --------------------------------------------------------------------------------