From e74689395d1a377393da49d36e4acfc9ba5ac08d Mon Sep 17 00:00:00 2001
From: Jamie <jamieli550@gmail.com>
Date: Tue, 28 Jan 2020 14:08:05 -0800
Subject: [PATCH 1/7] calling

---
 scala/private/common.bzl                      | 119 +++++++-----------
 scala/private/dependency.bzl                  |  78 ++++++++++++
 .../phases/phase_collect_exports_jars.bzl     |   6 +-
 scala/private/phases/phase_collect_jars.bzl   |  35 ++----
 scala/private/phases/phase_compile.bzl        |  46 ++++---
 scala/private/phases/phase_dependency.bzl     |  49 ++++++++
 .../phases/phase_unused_deps_checker.bzl      |  11 --
 scala/private/phases/phases.bzl               |  15 +--
 scala/private/rule_impls.bzl                  |  92 +++++---------
 scala/private/rules/scala_binary.bzl          |   4 +-
 scala/private/rules/scala_junit_test.bzl      |   4 +-
 scala/private/rules/scala_library.bzl         |  15 +--
 scala/private/rules/scala_repl.bzl            |   4 +-
 scala/private/rules/scala_test.bzl            |   4 +-
 scala_proto/private/scalapb_aspect.bzl        |  10 +-
 .../rulesscala/scalac/CompileOptions.java     |   6 +-
 .../rulesscala/scalac/ScalacProcessor.java    |  58 +++++----
 .../DependencyAnalyzerSettings.scala          |   2 +-
 twitter_scrooge/twitter_scrooge.bzl           |  10 +-
 19 files changed, 322 insertions(+), 246 deletions(-)
 create mode 100644 scala/private/dependency.bzl
 create mode 100644 scala/private/phases/phase_dependency.bzl
 delete mode 100644 scala/private/phases/phase_unused_deps_checker.bzl

diff --git a/scala/private/common.bzl b/scala/private/common.bzl
index 86722bc80..895c5fd75 100644
--- a/scala/private/common.bzl
+++ b/scala/private/common.bzl
@@ -11,44 +11,15 @@ def write_manifest_file(actions, output_file, main_class):
 
 def collect_jars(
         dep_targets,
-        dependency_analyzer_is_off = True,
-        unused_dependency_checker_is_off = True,
-        plus_one_deps_is_off = True):
+        dependency_mode,
+        need_direct_info,
+        need_indirect_info):
     """Compute the runtime and compile-time dependencies from the given targets"""  # noqa
 
-    if dependency_analyzer_is_off:
-        return _collect_jars_when_dependency_analyzer_is_off(
-            dep_targets,
-            unused_dependency_checker_is_off,
-            plus_one_deps_is_off,
-        )
-    else:
-        return _collect_jars_when_dependency_analyzer_is_on(dep_targets)
-
-def collect_plugin_paths(plugins):
-    """Get the actual jar paths of plugins as a depset."""
-    paths = []
-    for p in plugins:
-        if hasattr(p, "path"):
-            paths.append(p)
-        elif JavaInfo in p:
-            paths.extend([j.class_jar for j in p[JavaInfo].outputs.jars])
-            # support http_file pointed at a jar. http_jar uses ijar,
-            # which breaks scala macros
-
-        elif hasattr(p, "files"):
-            paths.extend([f for f in p.files.to_list() if not_sources_jar(f.basename)])
-    return depset(paths)
-
-def _collect_jars_when_dependency_analyzer_is_off(
-        dep_targets,
-        unused_dependency_checker_is_off,
-        plus_one_deps_is_off):
+    transitive_compile_jars = []
+    jars2labels = {}
     compile_jars = []
-    plus_one_deps_compile_jars = []
     runtime_jars = []
-    jars2labels = {}
-
     deps_providers = []
 
     for dep_target in dep_targets:
@@ -59,60 +30,62 @@ def _collect_jars_when_dependency_analyzer_is_off(
         compile_jars.append(java_provider.compile_jars)
         runtime_jars.append(java_provider.transitive_runtime_jars)
 
-        if not unused_dependency_checker_is_off:
+        additional_transitive_compile_jars = _additional_transitive_compile_jars(
+            java_provider=java_provider,
+            dep_target=dep_target,
+            dependency_mode=dependency_mode
+        )
+        transitive_compile_jars.append(additional_transitive_compile_jars)
+
+        if need_direct_info or need_indirect_info:
+            if need_indirect_info:
+                all_jars = additional_transitive_compile_jars.to_list()
+            else:
+                all_jars = []
             add_labels_of_jars_to(
                 jars2labels,
                 dep_target,
-                [],
+                all_jars,
                 java_provider.compile_jars.to_list(),
             )
 
-        if (not plus_one_deps_is_off) and (PlusOneDeps in dep_target):
-            plus_one_deps_compile_jars.append(
-                depset(transitive = [dep[JavaInfo].compile_jars for dep in dep_target[PlusOneDeps].direct_deps if JavaInfo in dep]),
-            )
-
     return struct(
         compile_jars = depset(transitive = compile_jars),
         transitive_runtime_jars = depset(transitive = runtime_jars),
         jars2labels = JarsToLabelsInfo(jars_to_labels = jars2labels),
-        transitive_compile_jars = depset(transitive = compile_jars + plus_one_deps_compile_jars),
+        transitive_compile_jars = depset(transitive = transitive_compile_jars),
         deps_providers = deps_providers,
     )
 
-def _collect_jars_when_dependency_analyzer_is_on(dep_targets):
-    transitive_compile_jars = []
-    jars2labels = {}
-    compile_jars = []
-    runtime_jars = []
-    deps_providers = []
-
-    for dep_target in dep_targets:
-        # we require a JavaInfo for dependencies
-        # must use java_import or scala_import if you have raw files
-        java_provider = dep_target[JavaInfo]
-        deps_providers.append(java_provider)
-        current_dep_compile_jars = java_provider.compile_jars
-        current_dep_transitive_compile_jars = java_provider.transitive_compile_time_jars
-        runtime_jars.append(java_provider.transitive_runtime_jars)
-
-        compile_jars.append(current_dep_compile_jars)
-        transitive_compile_jars.append(current_dep_transitive_compile_jars)
+def collect_plugin_paths(plugins):
+    """Get the actual jar paths of plugins as a depset."""
+    paths = []
+    for p in plugins:
+        if hasattr(p, "path"):
+            paths.append(p)
+        elif JavaInfo in p:
+            paths.extend([j.class_jar for j in p[JavaInfo].outputs.jars])
+            # support http_file pointed at a jar. http_jar uses ijar,
+            # which breaks scala macros
 
-        add_labels_of_jars_to(
-            jars2labels,
-            dep_target,
-            current_dep_transitive_compile_jars.to_list(),
-            current_dep_compile_jars.to_list(),
-        )
+        elif hasattr(p, "files"):
+            paths.extend([f for f in p.files.to_list() if not_sources_jar(f.basename)])
+    return depset(paths)
 
-    return struct(
-        compile_jars = depset(transitive = compile_jars),
-        transitive_runtime_jars = depset(transitive = runtime_jars),
-        jars2labels = JarsToLabelsInfo(jars_to_labels = jars2labels),
-        transitive_compile_jars = depset(transitive = transitive_compile_jars),
-        deps_providers = deps_providers,
-    )
+def _additional_transitive_compile_jars(
+        java_provider,
+        dep_target,
+        dependency_mode):
+    if dependency_mode == 'transitive':
+        return java_provider.transitive_compile_time_jars
+    elif dependency_mode == 'plus-one':
+        if PlusOneDeps in dep_target:
+            plus_one_jars = [dep[JavaInfo].compile_jars for dep in dep_target[PlusOneDeps].direct_deps if JavaInfo in dep]
+            return depset(transitive = plus_one_jars + [java_provider.compile_jars])
+        else:
+            return java_provider.compile_jars
+    else: # direct
+        return java_provider.compile_jars
 
 # When import mavan_jar's for scala macros we have to use the jar:file requirement
 # since bazel 0.6.0 this brings in the source jar too
diff --git a/scala/private/dependency.bzl b/scala/private/dependency.bzl
new file mode 100644
index 000000000..994f17cd8
--- /dev/null
+++ b/scala/private/dependency.bzl
@@ -0,0 +1,78 @@
+
+# This file contains all computations for what the dependency mode is
+# (i.e. transitive, plus-one, direct, etc)
+# and what/how dependency analysis is performed (unused deps, strict deps, etc).
+
+def new_dependency_info(
+        dependency_mode,
+        unused_deps_mode,
+        strict_deps_mode,
+        dependency_tracking_method):
+
+    is_strict_deps_on = strict_deps_mode != "off"
+    # Currently we do not support running both strict and unused deps at the same time
+    if is_strict_deps_on:
+        unused_deps_mode = "off"
+
+    is_unused_deps_on = unused_deps_mode != "off"
+
+    need_direct_jars = is_strict_deps_on or is_unused_deps_on
+    need_direct_targets = is_unused_deps_on
+
+    return struct(
+        dependency_mode = dependency_mode,
+        need_indirect_info = is_strict_deps_on,
+        need_direct_jars = need_direct_jars,
+        need_direct_targets = need_direct_targets,
+        need_direct_info = need_direct_jars or need_direct_targets,
+        dependency_tracking_method = "high-level",
+        unused_deps_mode = unused_deps_mode,
+        strict_deps_mode = strict_deps_mode,
+        use_analyzer = is_strict_deps_on or is_unused_deps_on
+    )
+
+def dependency_info_for_addons(ctx):
+    return new_dependency_info(
+        dependency_mode=_dependency_mode_for_addons(ctx),
+        unused_deps_mode="off",
+        strict_deps_mode=get_strict_deps_mode(ctx),
+        dependency_tracking_method="high-level"
+    )
+
+def get_dependency_mode(ctx):
+    if get_is_strict_deps_on(ctx):
+        # all transitive dependencies are included
+        return "transitive"
+    elif not _is_plus_one_deps_off(ctx):
+        # dependencies and dependencies of dependencies are included
+        return "plus-one"
+    else:
+        # only explicitly-specified dependencies are included
+        return "direct"
+
+def _dependency_mode_for_addons(ctx):
+    if get_is_strict_deps_on(ctx):
+        return "transitive"
+    else:
+        return "direct"
+
+def get_strict_deps_mode(ctx):
+    if not hasattr(ctx.attr, "_dependency_analyzer_plugin"):
+        return "off"
+    # when the strict deps FT is removed the "default" check
+    # will be removed since "default" will mean it's turned on
+    if  ctx.fragments.java.strict_java_deps == "default":
+        return "off"
+    return ctx.fragments.java.strict_java_deps
+
+def get_is_strict_deps_on(ctx):
+    return get_strict_deps_mode(ctx) != "off"
+
+def get_unused_deps_mode(ctx):
+    if ctx.attr.unused_dependency_checker_mode:
+        return ctx.attr.unused_dependency_checker_mode
+    else:
+        return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].unused_dependency_checker_mode
+
+def _is_plus_one_deps_off(ctx):
+    return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].plus_one_deps_mode == "off"
diff --git a/scala/private/phases/phase_collect_exports_jars.bzl b/scala/private/phases/phase_collect_exports_jars.bzl
index 35a83dbe3..abce4cef8 100644
--- a/scala/private/phases/phase_collect_exports_jars.bzl
+++ b/scala/private/phases/phase_collect_exports_jars.bzl
@@ -12,4 +12,8 @@ def phase_collect_exports_jars(ctx, p):
     # Add information from exports (is key that AFTER all build actions/runfiles analysis)
     # Since after, will not show up in deploy_jar or old jars runfiles
     # Notice that compile_jars is intentionally transitive for exports
-    return collect_jars(ctx.attr.exports)
+    return collect_jars(
+        dep_targets=ctx.attr.exports,
+        dependency_mode='direct',
+        need_direct_info=False,
+        need_indirect_info=False)
diff --git a/scala/private/phases/phase_collect_jars.bzl b/scala/private/phases/phase_collect_jars.bzl
index d3d4fce9b..76cfb9dbd 100644
--- a/scala/private/phases/phase_collect_jars.bzl
+++ b/scala/private/phases/phase_collect_jars.bzl
@@ -3,11 +3,6 @@
 #
 # DOCUMENT THIS
 #
-load(
-    "@io_bazel_rules_scala//scala/private:rule_impls.bzl",
-    "is_dependency_analyzer_off",
-    "is_plus_one_deps_off",
-)
 load(
     "@io_bazel_rules_scala//scala/private:common.bzl",
     "collect_jars",
@@ -46,22 +41,16 @@ def phase_collect_jars_junit_test(ctx, p):
     )
     return _phase_collect_jars_default(ctx, p, args)
 
-def phase_collect_jars_library_for_plugin_bootstrapping(ctx, p):
-    args = struct(
-        unused_dependency_checker_mode = "off",
-    )
-    return _phase_collect_jars_default(ctx, p, args)
-
 def phase_collect_jars_common(ctx, p):
     return _phase_collect_jars_default(ctx, p)
 
 def _phase_collect_jars_default(ctx, p, _args = struct()):
     return _phase_collect_jars(
-        ctx,
-        _args.base_classpath if hasattr(_args, "base_classpath") else p.scalac_provider.default_classpath,
-        _args.extra_deps if hasattr(_args, "extra_deps") else [],
-        _args.extra_runtime_deps if hasattr(_args, "extra_runtime_deps") else [],
-        _args.unused_dependency_checker_mode if hasattr(_args, "unused_dependency_checker_mode") else p.unused_deps_checker,
+        ctx=ctx,
+        p=p,
+        base_classpath=_args.base_classpath if hasattr(_args, "base_classpath") else p.scalac_provider.default_classpath,
+        extra_deps=_args.extra_deps if hasattr(_args, "extra_deps") else [],
+        extra_runtime_deps=_args.extra_runtime_deps if hasattr(_args, "extra_runtime_deps") else [],
     )
 
 # Extract very common code out from dependency analysis into single place
@@ -69,18 +58,16 @@ def _phase_collect_jars_default(ctx, p, _args = struct()):
 # collects jars from deps, runtime jars from runtime_deps, and
 def _phase_collect_jars(
         ctx,
+        p,
         base_classpath,
         extra_deps,
-        extra_runtime_deps,
-        unused_dependency_checker_mode):
-    unused_dependency_checker_is_off = unused_dependency_checker_mode == "off"
-    dependency_analyzer_is_off = is_dependency_analyzer_off(ctx)
+        extra_runtime_deps):
 
     deps_jars = collect_jars(
-        ctx.attr.deps + extra_deps + base_classpath,
-        dependency_analyzer_is_off,
-        unused_dependency_checker_is_off,
-        is_plus_one_deps_off(ctx),
+        dep_targets=ctx.attr.deps + extra_deps + base_classpath,
+        dependency_mode=p.dependency.dependency_mode,
+        need_direct_info=p.dependency.need_direct_info,
+        need_indirect_info=p.dependency.need_indirect_info,
     )
 
     (
diff --git a/scala/private/phases/phase_compile.bzl b/scala/private/phases/phase_compile.bzl
index c9ea6f4e5..18dd72666 100644
--- a/scala/private/phases/phase_compile.bzl
+++ b/scala/private/phases/phase_compile.bzl
@@ -9,6 +9,10 @@ load(
     "@io_bazel_rules_scala//scala/private:coverage_replacements_provider.bzl",
     _coverage_replacements_provider = "coverage_replacements_provider",
 )
+load(
+    "@io_bazel_rules_scala//scala/private:dependency.bzl",
+    "new_dependency_info",
+)
 load(
     "@io_bazel_rules_scala//scala/private:rule_impls.bzl",
     _compile_scala = "compile_scala",
@@ -51,16 +55,6 @@ def phase_compile_library(ctx, p):
     )
     return _phase_compile_default(ctx, p, args)
 
-def phase_compile_library_for_plugin_bootstrapping(ctx, p):
-    args = struct(
-        unused_dependency_checker_ignored_targets = [
-            target.label
-            for target in p.scalac_provider.default_classpath + ctx.attr.exports
-        ],
-        unused_dependency_checker_mode = "off",
-    )
-    return _phase_compile_default(ctx, p, args)
-
 def phase_compile_macro_library(ctx, p):
     args = struct(
         buildijar = False,
@@ -124,8 +118,7 @@ def _phase_compile_default(ctx, p, _args = struct()):
         _args.srcjars if hasattr(_args, "srcjars") else depset(),
         _args.buildijar if hasattr(_args, "buildijar") else True,
         _args.implicit_junit_deps_needed_for_java_compilation if hasattr(_args, "implicit_junit_deps_needed_for_java_compilation") else [],
-        _args.unused_dependency_checker_ignored_targets if hasattr(_args, "unused_dependency_checker_ignored_targets") else [],
-        _args.unused_dependency_checker_mode if hasattr(_args, "unused_dependency_checker_mode") else p.unused_deps_checker,
+        unused_dependency_checker_ignored_targets = _args.unused_dependency_checker_ignored_targets if hasattr(_args, "unused_dependency_checker_ignored_targets") else [],
     )
 
 def _phase_compile(
@@ -135,8 +128,7 @@ def _phase_compile(
         buildijar,
         # TODO: generalize this hack
         implicit_junit_deps_needed_for_java_compilation,
-        unused_dependency_checker_ignored_targets,
-        unused_dependency_checker_mode):
+        unused_dependency_checker_ignored_targets):
     manifest = ctx.outputs.manifest
     jars = p.collect_jars.compile_jars
     rjars = p.collect_jars.transitive_runtime_jars
@@ -154,10 +146,10 @@ def _phase_compile(
         transitive_compile_jars,
         jars2labels,
         implicit_junit_deps_needed_for_java_compilation,
-        unused_dependency_checker_mode,
-        unused_dependency_checker_ignored_targets,
-        deps_providers,
-        default_classpath,
+        deps_providers=deps_providers,
+        default_classpath=default_classpath,
+        dependency_info=p.dependency,
+        unused_dependency_checker_ignored_targets=unused_dependency_checker_ignored_targets
     )
 
     # TODO: simplify the return values and use provider
@@ -186,10 +178,10 @@ def _compile_or_empty(
         transitive_compile_jars,
         jars2labels,
         implicit_junit_deps_needed_for_java_compilation,
-        unused_dependency_checker_mode,
-        unused_dependency_checker_ignored_targets,
+        dependency_info,
         deps_providers,
-        default_classpath):
+        default_classpath,
+        unused_dependency_checker_ignored_targets):
     # We assume that if a srcjar is present, it is not empty
     if len(ctx.files.srcs) + len(srcjars.to_list()) == 0:
         _build_nosrc_jar(ctx)
@@ -224,7 +216,12 @@ def _compile_or_empty(
         # We are not able to verify whether dependencies are used when compiling java sources
         # Thus we disable unused dependency checking when java sources are found
         if len(java_srcs) != 0:
-            unused_dependency_checker_mode = "off"
+            dependency_info = new_dependency_info(
+                dependency_mode=dependency_info.dependency_mode,
+                unused_deps_mode='off',
+                strict_deps_mode=dependency_info.strict_deps_mode,
+                dependency_tracking_method=dependency_info.dependency_tracking_method,
+            )
 
         sources = [
             f
@@ -251,9 +248,8 @@ def _compile_or_empty(
             ctx.attr.expect_java_output,
             ctx.attr.scalac_jvm_flags,
             ctx.attr._scalac,
-            unused_dependency_checker_ignored_targets =
-                unused_dependency_checker_ignored_targets,
-            unused_dependency_checker_mode = unused_dependency_checker_mode,
+            dependency_info=dependency_info,
+            unused_dependency_checker_ignored_targets=unused_dependency_checker_ignored_targets
         )
 
         # build ijar if needed
diff --git a/scala/private/phases/phase_dependency.bzl b/scala/private/phases/phase_dependency.bzl
new file mode 100644
index 000000000..2771a8591
--- /dev/null
+++ b/scala/private/phases/phase_dependency.bzl
@@ -0,0 +1,49 @@
+# Gathers information about dependency mode and analysis
+
+load(
+    "@io_bazel_rules_scala//scala/private:dependency.bzl",
+    "get_dependency_mode",
+    "get_strict_deps_mode",
+    "get_unused_deps_mode",
+    "new_dependency_info",
+)
+
+def _phase_dependency_default(ctx, p, args=struct()):
+    return _phase_dependency(
+        ctx=ctx,
+        p=p,
+        unused_deps_always_off=args.unused_deps_always_off if hasattr(args, 'unused_deps_always_off') else False,
+        strict_deps_always_off=args.strict_deps_always_off if hasattr(args, 'strict_deps_always_off') else False,
+    )
+
+def _phase_dependency(
+        ctx,
+        p,
+        unused_deps_always_off,
+        strict_deps_always_off):
+    if strict_deps_always_off:
+        strict_deps_mode = 'off'
+    else:
+        strict_deps_mode = get_strict_deps_mode(ctx)
+
+    if unused_deps_always_off:
+        unused_deps_mode = 'off'
+    else:
+        unused_deps_mode = get_unused_deps_mode(ctx)
+
+    return new_dependency_info(
+        dependency_mode=get_dependency_mode(ctx),
+        unused_deps_mode=unused_deps_mode,
+        strict_deps_mode=strict_deps_mode,
+        dependency_tracking_method='high-level',
+    )
+
+def phase_dependency_common(ctx, p):
+    return _phase_dependency_default(ctx, p)
+
+def phase_dependency_library_for_plugin_bootstrapping(ctx, p):
+    args = struct(
+        unused_deps_always_off=True,
+        strict_deps_always_off=True,
+    )
+    return _phase_dependency_default(ctx, p, args)
diff --git a/scala/private/phases/phase_unused_deps_checker.bzl b/scala/private/phases/phase_unused_deps_checker.bzl
deleted file mode 100644
index 21f0daebb..000000000
--- a/scala/private/phases/phase_unused_deps_checker.bzl
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# PHASE: unused deps checker
-#
-# DOCUMENT THIS
-#
-
-def phase_unused_deps_checker(ctx, p):
-    if ctx.attr.unused_dependency_checker_mode:
-        return ctx.attr.unused_dependency_checker_mode
-    else:
-        return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].unused_dependency_checker_mode
diff --git a/scala/private/phases/phases.bzl b/scala/private/phases/phases.bzl
index cc2396684..af4b1aed6 100644
--- a/scala/private/phases/phases.bzl
+++ b/scala/private/phases/phases.bzl
@@ -23,7 +23,6 @@ load(
     "@io_bazel_rules_scala//scala/private:phases/phase_collect_jars.bzl",
     _phase_collect_jars_common = "phase_collect_jars_common",
     _phase_collect_jars_junit_test = "phase_collect_jars_junit_test",
-    _phase_collect_jars_library_for_plugin_bootstrapping = "phase_collect_jars_library_for_plugin_bootstrapping",
     _phase_collect_jars_macro_library = "phase_collect_jars_macro_library",
     _phase_collect_jars_repl = "phase_collect_jars_repl",
     _phase_collect_jars_scalatest = "phase_collect_jars_scalatest",
@@ -34,7 +33,6 @@ load(
     _phase_compile_common = "phase_compile_common",
     _phase_compile_junit_test = "phase_compile_junit_test",
     _phase_compile_library = "phase_compile_library",
-    _phase_compile_library_for_plugin_bootstrapping = "phase_compile_library_for_plugin_bootstrapping",
     _phase_compile_macro_library = "phase_compile_macro_library",
     _phase_compile_repl = "phase_compile_repl",
     _phase_compile_scalatest = "phase_compile_scalatest",
@@ -50,7 +48,11 @@ load("@io_bazel_rules_scala//scala/private:phases/phase_scalac_provider.bzl", _p
 load("@io_bazel_rules_scala//scala/private:phases/phase_write_manifest.bzl", _phase_write_manifest = "phase_write_manifest")
 load("@io_bazel_rules_scala//scala/private:phases/phase_collect_srcjars.bzl", _phase_collect_srcjars = "phase_collect_srcjars")
 load("@io_bazel_rules_scala//scala/private:phases/phase_collect_exports_jars.bzl", _phase_collect_exports_jars = "phase_collect_exports_jars")
-load("@io_bazel_rules_scala//scala/private:phases/phase_unused_deps_checker.bzl", _phase_unused_deps_checker = "phase_unused_deps_checker")
+load(
+    "@io_bazel_rules_scala//scala/private:phases/phase_dependency.bzl",
+    _phase_dependency_common = "phase_dependency_common",
+    _phase_dependency_library_for_plugin_bootstrapping = "phase_dependency_library_for_plugin_bootstrapping",
+)
 load("@io_bazel_rules_scala//scala/private:phases/phase_declare_executable.bzl", _phase_declare_executable = "phase_declare_executable")
 load("@io_bazel_rules_scala//scala/private:phases/phase_merge_jars.bzl", _phase_merge_jars = "phase_merge_jars")
 load("@io_bazel_rules_scala//scala/private:phases/phase_jvm_flags.bzl", _phase_jvm_flags = "phase_jvm_flags")
@@ -73,8 +75,9 @@ phase_collect_exports_jars = _phase_collect_exports_jars
 # write_manifest
 phase_write_manifest = _phase_write_manifest
 
-# unused_deps_checker
-phase_unused_deps_checker = _phase_unused_deps_checker
+# dependency
+phase_dependency_common = _phase_dependency_common
+phase_dependency_library_for_plugin_bootstrapping = _phase_dependency_library_for_plugin_bootstrapping
 
 # declare_executable
 phase_declare_executable = _phase_declare_executable
@@ -103,13 +106,11 @@ phase_collect_jars_scalatest = _phase_collect_jars_scalatest
 phase_collect_jars_repl = _phase_collect_jars_repl
 phase_collect_jars_macro_library = _phase_collect_jars_macro_library
 phase_collect_jars_junit_test = _phase_collect_jars_junit_test
-phase_collect_jars_library_for_plugin_bootstrapping = _phase_collect_jars_library_for_plugin_bootstrapping
 phase_collect_jars_common = _phase_collect_jars_common
 
 # compile
 phase_compile_binary = _phase_compile_binary
 phase_compile_library = _phase_compile_library
-phase_compile_library_for_plugin_bootstrapping = _phase_compile_library_for_plugin_bootstrapping
 phase_compile_macro_library = _phase_compile_macro_library
 phase_compile_junit_test = _phase_compile_junit_test
 phase_compile_repl = _phase_compile_repl
diff --git a/scala/private/rule_impls.bzl b/scala/private/rule_impls.bzl
index c5747d1ca..9105aae8f 100644
--- a/scala/private/rule_impls.bzl
+++ b/scala/private/rule_impls.bzl
@@ -60,73 +60,55 @@ def compile_scala(
         expect_java_output,
         scalac_jvm_flags,
         scalac,
-        unused_dependency_checker_mode = "off",
-        unused_dependency_checker_ignored_targets = []):
+        dependency_info,
+        unused_dependency_checker_ignored_targets):
     # look for any plugins:
     input_plugins = plugins
     plugins = _collect_plugin_paths(plugins)
     internal_plugin_jars = []
-    strict_deps_mode = "off"
     compiler_classpath_jars = cjars
+    if dependency_info.dependency_mode != 'direct':
+        compiler_classpath_jars = transitive_compile_jars
     optional_scalac_args = ""
     classpath_resources = []
     if (hasattr(ctx.files, "classpath_resources")):
         classpath_resources = ctx.files.classpath_resources
 
-    if is_dependency_analyzer_on(ctx):
-        # "off" mode is used as a feature toggle, that preserves original behaviour
-        strict_deps_mode = ctx.fragments.java.strict_java_deps
+    optional_scalac_args_map = {}
+
+    if dependency_info.use_analyzer:
         dep_plugin = ctx.attr._dependency_analyzer_plugin
         plugins = depset(transitive = [plugins, dep_plugin.files])
         internal_plugin_jars = ctx.files._dependency_analyzer_plugin
-        compiler_classpath_jars = transitive_compile_jars
 
-        direct_jars = _join_path(cjars.to_list())
+        current_target = str(target_label)
+        optional_scalac_args_map['CurrentTarget'] = current_target
 
+    if dependency_info.need_indirect_info:
         transitive_cjars_list = transitive_compile_jars.to_list()
         indirect_jars = _join_path(transitive_cjars_list)
         indirect_targets = ",".join([str(labels[j.path]) for j in transitive_cjars_list])
 
-        current_target = str(target_label)
-
-        optional_scalac_args = """
-DirectJars: {direct_jars}
-IndirectJars: {indirect_jars}
-IndirectTargets: {indirect_targets}
-CurrentTarget: {current_target}
-        """.format(
-            direct_jars = direct_jars,
-            indirect_jars = indirect_jars,
-            indirect_targets = indirect_targets,
-            current_target = current_target,
-        )
-
-    elif unused_dependency_checker_mode != "off":
-        dependency_analyzer_plugin = ctx.attr._dependency_analyzer_plugin
-        plugins = depset(transitive = [plugins, dependency_analyzer_plugin.files])
-        internal_plugin_jars = ctx.files._dependency_analyzer_plugin
-
-        cjars_list = cjars.to_list()
-        direct_jars = _join_path(cjars_list)
-        direct_targets = ",".join([str(labels[j.path]) for j in cjars_list])
+        optional_scalac_args_map['IndirectJars'] = indirect_jars
+        optional_scalac_args_map['IndirectTargets'] = indirect_targets
 
+    if dependency_info.unused_deps_mode != 'off':
         ignored_targets = ",".join([str(d) for d in unused_dependency_checker_ignored_targets])
+        optional_scalac_args_map['UnusedDepsIgnoredTargets'] = ignored_targets
 
-        current_target = str(target_label)
-
-        optional_scalac_args = """
-DirectJars: {direct_jars}
-DirectTargets: {direct_targets}
-IgnoredTargets: {ignored_targets}
-CurrentTarget: {current_target}
-        """.format(
-            direct_jars = direct_jars,
-            direct_targets = direct_targets,
-            ignored_targets = ignored_targets,
-            current_target = current_target,
-        )
-    if is_dependency_analyzer_off(ctx) and not is_plus_one_deps_off(ctx):
-        compiler_classpath_jars = transitive_compile_jars
+    if dependency_info.need_direct_info:
+        cjars_list = cjars.to_list()
+        if dependency_info.need_direct_jars:
+            direct_jars = _join_path(cjars_list)
+            optional_scalac_args_map['DirectJars'] = direct_jars
+        if dependency_info.need_direct_targets:
+            direct_targets = ",".join([str(labels[j.path]) for j in cjars_list])
+            optional_scalac_args_map['DirectTargets'] = direct_targets
+
+    optional_scalac_args = "\n".join([
+        "{k}: {v}".format(k=k, v=v) for (k, v)
+        in sorted(optional_scalac_args_map.items())
+    ])
 
     plugins_list = plugins.to_list()
     plugin_arg = _join_path(plugins_list)
@@ -154,6 +136,7 @@ ScalacOpts: {scala_opts}
 SourceJars: {srcjars}
 StrictDepsMode: {strict_deps_mode}
 UnusedDependencyCheckerMode: {unused_dependency_checker_mode}
+DependencyTrackingMethod: {dependency_tracking_method}
 StatsfileOutput: {statsfile_output}
 """.format(
         out = output.path,
@@ -170,8 +153,9 @@ StatsfileOutput: {statsfile_output}
         resource_targets = ",".join([p[0] for p in resource_paths]),
         resource_sources = ",".join([p[1] for p in resource_paths]),
         resource_jars = _join_path(resource_jars),
-        strict_deps_mode = strict_deps_mode,
-        unused_dependency_checker_mode = unused_dependency_checker_mode,
+        strict_deps_mode = dependency_info.strict_deps_mode,
+        unused_dependency_checker_mode = dependency_info.unused_deps_mode,
+        dependency_tracking_method = dependency_info.dependency_tracking_method,
         statsfile_output = statsfile.path,
     )
 
@@ -250,19 +234,5 @@ def java_bin(ctx):
         javabin = "%s/%s" % (runfiles_root_var, java_path)
     return javabin
 
-def is_dependency_analyzer_on(ctx):
-    if (hasattr(ctx.attr, "_dependency_analyzer_plugin") and
-        # when the strict deps FT is removed the "default" check
-        # will be removed since "default" will mean it's turned on
-        ctx.fragments.java.strict_java_deps != "default" and
-        ctx.fragments.java.strict_java_deps != "off"):
-        return True
-
-def is_dependency_analyzer_off(ctx):
-    return not is_dependency_analyzer_on(ctx)
-
-def is_plus_one_deps_off(ctx):
-    return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].plus_one_deps_mode == "off"
-
 def is_windows(ctx):
     return ctx.configuration.host_path_separator == ";"
diff --git a/scala/private/rules/scala_binary.bzl b/scala/private/rules/scala_binary.bzl
index 07afd2574..97a8af42a 100644
--- a/scala/private/rules/scala_binary.bzl
+++ b/scala/private/rules/scala_binary.bzl
@@ -16,11 +16,11 @@ load(
     "phase_compile_binary",
     "phase_declare_executable",
     "phase_default_info",
+    "phase_dependency_common",
     "phase_java_wrapper_common",
     "phase_merge_jars",
     "phase_runfiles_common",
     "phase_scalac_provider",
-    "phase_unused_deps_checker",
     "phase_write_executable_common",
     "phase_write_manifest",
     "run_phases",
@@ -33,7 +33,7 @@ def _scala_binary_impl(ctx):
         [
             ("scalac_provider", phase_scalac_provider),
             ("write_manifest", phase_write_manifest),
-            ("unused_deps_checker", phase_unused_deps_checker),
+            ("dependency", phase_dependency_common),
             ("collect_jars", phase_collect_jars_common),
             ("java_wrapper", phase_java_wrapper_common),
             ("declare_executable", phase_declare_executable),
diff --git a/scala/private/rules/scala_junit_test.bzl b/scala/private/rules/scala_junit_test.bzl
index 8b58180b7..4e0444fc3 100644
--- a/scala/private/rules/scala_junit_test.bzl
+++ b/scala/private/rules/scala_junit_test.bzl
@@ -15,12 +15,12 @@ load(
     "phase_compile_junit_test",
     "phase_declare_executable",
     "phase_default_info",
+    "phase_dependency_common",
     "phase_java_wrapper_common",
     "phase_jvm_flags",
     "phase_merge_jars",
     "phase_runfiles_common",
     "phase_scalac_provider",
-    "phase_unused_deps_checker",
     "phase_write_executable_junit_test",
     "phase_write_manifest",
     "run_phases",
@@ -37,7 +37,7 @@ def _scala_junit_test_impl(ctx):
         [
             ("scalac_provider", phase_scalac_provider),
             ("write_manifest", phase_write_manifest),
-            ("unused_deps_checker", phase_unused_deps_checker),
+            ("dependency", phase_dependency_common),
             ("collect_jars", phase_collect_jars_junit_test),
             ("java_wrapper", phase_java_wrapper_common),
             ("declare_executable", phase_declare_executable),
diff --git a/scala/private/rules/scala_library.bzl b/scala/private/rules/scala_library.bzl
index 17356b843..06a51a6ac 100644
--- a/scala/private/rules/scala_library.bzl
+++ b/scala/private/rules/scala_library.bzl
@@ -20,17 +20,17 @@ load(
     "extras_phases",
     "phase_collect_exports_jars",
     "phase_collect_jars_common",
-    "phase_collect_jars_library_for_plugin_bootstrapping",
     "phase_collect_jars_macro_library",
     "phase_collect_srcjars",
+    "phase_compile_common",
     "phase_compile_library",
-    "phase_compile_library_for_plugin_bootstrapping",
     "phase_compile_macro_library",
     "phase_default_info",
+    "phase_dependency_common",
+    "phase_dependency_library_for_plugin_bootstrapping",
     "phase_merge_jars",
     "phase_runfiles_library",
     "phase_scalac_provider",
-    "phase_unused_deps_checker",
     "phase_write_manifest",
     "run_phases",
 )
@@ -60,7 +60,7 @@ def _scala_library_impl(ctx):
             ("scalac_provider", phase_scalac_provider),
             ("collect_srcjars", phase_collect_srcjars),
             ("write_manifest", phase_write_manifest),
-            ("unused_deps_checker", phase_unused_deps_checker),
+            ("dependency", phase_dependency_common),
             ("collect_jars", phase_collect_jars_common),
             ("compile", phase_compile_library),
             ("merge_jars", phase_merge_jars),
@@ -137,8 +137,9 @@ def _scala_library_for_plugin_bootstrapping_impl(ctx):
             ("scalac_provider", phase_scalac_provider),
             ("collect_srcjars", phase_collect_srcjars),
             ("write_manifest", phase_write_manifest),
-            ("collect_jars", phase_collect_jars_library_for_plugin_bootstrapping),
-            ("compile", phase_compile_library_for_plugin_bootstrapping),
+            ("dependency", phase_dependency_library_for_plugin_bootstrapping),
+            ("collect_jars", phase_collect_jars_common),
+            ("compile", phase_compile_common),
             ("merge_jars", phase_merge_jars),
             ("runfiles", phase_runfiles_library),
             ("collect_exports_jars", phase_collect_exports_jars),
@@ -191,7 +192,7 @@ def _scala_macro_library_impl(ctx):
             ("scalac_provider", phase_scalac_provider),
             ("collect_srcjars", phase_collect_srcjars),
             ("write_manifest", phase_write_manifest),
-            ("unused_deps_checker", phase_unused_deps_checker),
+            ("dependency", phase_dependency_common),
             ("collect_jars", phase_collect_jars_macro_library),
             ("compile", phase_compile_macro_library),
             ("merge_jars", phase_merge_jars),
diff --git a/scala/private/rules/scala_repl.bzl b/scala/private/rules/scala_repl.bzl
index 3d508af45..805965671 100644
--- a/scala/private/rules/scala_repl.bzl
+++ b/scala/private/rules/scala_repl.bzl
@@ -16,11 +16,11 @@ load(
     "phase_compile_repl",
     "phase_declare_executable",
     "phase_default_info",
+    "phase_dependency_common",
     "phase_java_wrapper_repl",
     "phase_merge_jars",
     "phase_runfiles_common",
     "phase_scalac_provider",
-    "phase_unused_deps_checker",
     "phase_write_executable_repl",
     "phase_write_manifest",
     "run_phases",
@@ -33,7 +33,7 @@ def _scala_repl_impl(ctx):
         [
             ("scalac_provider", phase_scalac_provider),
             ("write_manifest", phase_write_manifest),
-            ("unused_deps_checker", phase_unused_deps_checker),
+            ("dependency", phase_dependency_common),
             # need scala-compiler for MainGenericRunner below
             ("collect_jars", phase_collect_jars_repl),
             ("java_wrapper", phase_java_wrapper_repl),
diff --git a/scala/private/rules/scala_test.bzl b/scala/private/rules/scala_test.bzl
index 1a6bd3e7d..dbc259708 100644
--- a/scala/private/rules/scala_test.bzl
+++ b/scala/private/rules/scala_test.bzl
@@ -17,11 +17,11 @@ load(
     "phase_coverage_runfiles",
     "phase_declare_executable",
     "phase_default_info",
+    "phase_dependency_common",
     "phase_java_wrapper_common",
     "phase_merge_jars",
     "phase_runfiles_scalatest",
     "phase_scalac_provider",
-    "phase_unused_deps_checker",
     "phase_write_executable_scalatest",
     "phase_write_manifest",
     "run_phases",
@@ -34,7 +34,7 @@ def _scala_test_impl(ctx):
         [
             ("scalac_provider", phase_scalac_provider),
             ("write_manifest", phase_write_manifest),
-            ("unused_deps_checker", phase_unused_deps_checker),
+            ("dependency", phase_dependency_common),
             ("collect_jars", phase_collect_jars_scalatest),
             ("java_wrapper", phase_java_wrapper_common),
             ("declare_executable", phase_declare_executable),
diff --git a/scala_proto/private/scalapb_aspect.bzl b/scala_proto/private/scalapb_aspect.bzl
index 0d51cfb65..0fc8fad7c 100644
--- a/scala_proto/private/scalapb_aspect.bzl
+++ b/scala_proto/private/scalapb_aspect.bzl
@@ -2,7 +2,13 @@ load(
     "//scala/private:common.bzl",
     "write_manifest_file",
 )
-load("//scala/private:rule_impls.bzl", "compile_scala")
+load(
+    "//scala/private:dependency.bzl",
+    "dependency_info_for_addons",
+)
+load(
+    "//scala/private:rule_impls.bzl",
+    "compile_scala")
 load("//scala_proto/private:proto_to_scala_src.bzl", "proto_to_scala_src")
 
 ScalaPBAspectInfo = provider(fields = [
@@ -89,6 +95,8 @@ def _compile_scala(
         expect_java_output = False,
         scalac_jvm_flags = [],
         scalac = scalac,
+        dependency_info = dependency_info_for_addons(ctx),
+        unused_dependency_checker_ignored_targets = [],
     )
 
     return JavaInfo(
diff --git a/src/java/io/bazel/rulesscala/scalac/CompileOptions.java b/src/java/io/bazel/rulesscala/scalac/CompileOptions.java
index 5f3a84885..e781c280e 100644
--- a/src/java/io/bazel/rulesscala/scalac/CompileOptions.java
+++ b/src/java/io/bazel/rulesscala/scalac/CompileOptions.java
@@ -21,13 +21,14 @@ public class CompileOptions {
   public final String[] classpathResourceFiles;
   public final String[] directJars;
   public final String[] directTargets;
-  public final String[] ignoredTargets;
+  public final String[] unusedDepsIgnoredTargets;
   public final String[] indirectJars;
   public final String[] indirectTargets;
   public final String strictDepsMode;
   public final String unusedDependencyCheckerMode;
   public final String currentTarget;
   public final String statsfile;
+  public final String dependencyTrackingMethod;
 
   public CompileOptions(List<String> args) {
     Map<String, String> argMap = buildArgMap(args);
@@ -55,13 +56,14 @@ public CompileOptions(List<String> args) {
 
     directJars = getCommaList(argMap, "DirectJars");
     directTargets = getCommaList(argMap, "DirectTargets");
-    ignoredTargets = getCommaList(argMap, "IgnoredTargets");
+    unusedDepsIgnoredTargets = getCommaList(argMap, "UnusedDepsIgnoredTargets");
     indirectJars = getCommaList(argMap, "IndirectJars");
     indirectTargets = getCommaList(argMap, "IndirectTargets");
 
     strictDepsMode = getOrElse(argMap, "StrictDepsMode", "off");
     unusedDependencyCheckerMode = getOrElse(argMap, "UnusedDependencyCheckerMode", "off");
     currentTarget = getOrElse(argMap, "CurrentTarget", "NA");
+    dependencyTrackingMethod = getOrElse(argMap, "DependencyTrackingMethod", "high-level");
 
     statsfile = getOrError(argMap, "StatsfileOutput", "Missing required arg StatsfileOutput");
   }
diff --git a/src/java/io/bazel/rulesscala/scalac/ScalacProcessor.java b/src/java/io/bazel/rulesscala/scalac/ScalacProcessor.java
index 31a33a554..fe5b7721f 100644
--- a/src/java/io/bazel/rulesscala/scalac/ScalacProcessor.java
+++ b/src/java/io/bazel/rulesscala/scalac/ScalacProcessor.java
@@ -3,7 +3,12 @@
 import io.bazel.rulesscala.jar.JarCreator;
 import io.bazel.rulesscala.worker.GenericWorker;
 import io.bazel.rulesscala.worker.Processor;
-import java.io.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.lang.reflect.Field;
 import java.nio.file.FileSystems;
 import java.nio.file.FileVisitResult;
@@ -12,8 +17,10 @@
 import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
-import java.util.*;
-import java.util.Map.Entry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import org.apache.commons.io.IOUtils;
@@ -173,33 +180,36 @@ private static boolean isModeEnabled(String mode) {
   private static String[] getPluginParamsFrom(CompileOptions ops) {
     ArrayList<String> pluginParams = new ArrayList<>(0);
 
-    if (isModeEnabled(ops.strictDepsMode)) {
-      String[] indirectTargets = encodeBazelTargets(ops.indirectTargets);
+    if (isModeEnabled(ops.strictDepsMode) || isModeEnabled(ops.unusedDependencyCheckerMode)) {
       String currentTarget = encodeBazelTarget(ops.currentTarget);
 
       String[] dependencyAnalyzerParams = {
-        "-P:dependency-analyzer:direct-jars:" + String.join(":", ops.directJars),
-        "-P:dependency-analyzer:indirect-jars:" + String.join(":", ops.indirectJars),
-        "-P:dependency-analyzer:indirect-targets:" + String.join(":", indirectTargets),
-        "-P:dependency-analyzer:strict-deps-mode:" + ops.strictDepsMode,
-        "-P:dependency-analyzer:current-target:" + currentTarget,
-        "-P:dependency-analyzer:dependency-tracking-method:" + "high-level",
+            "-P:dependency-analyzer:strict-deps-mode:" + ops.strictDepsMode,
+            "-P:dependency-analyzer:unused-deps-mode:" + ops.unusedDependencyCheckerMode,
+            "-P:dependency-analyzer:current-target:" + currentTarget,
+            "-P:dependency-analyzer:dependency-tracking-method:" + ops.dependencyTrackingMethod,
       };
+
       pluginParams.addAll(Arrays.asList(dependencyAnalyzerParams));
-    } else if (isModeEnabled(ops.unusedDependencyCheckerMode)) {
-      String[] directTargets = encodeBazelTargets(ops.directTargets);
-      String[] ignoredTargets = encodeBazelTargets(ops.ignoredTargets);
-      String currentTarget = encodeBazelTarget(ops.currentTarget);
 
-      String[] unusedDependencyCheckerParams = {
-        "-P:dependency-analyzer:direct-jars:" + String.join(":", ops.directJars),
-        "-P:dependency-analyzer:direct-targets:" + String.join(":", directTargets),
-        "-P:dependency-analyzer:unused-deps-ignored-targets:" + String.join(":", ignoredTargets),
-        "-P:dependency-analyzer:unused-deps-mode:" + ops.unusedDependencyCheckerMode,
-        "-P:dependency-analyzer:current-target:" + currentTarget,
-        "-P:dependency-analyzer:dependency-tracking-method:" + "high-level",
-      };
-      pluginParams.addAll(Arrays.asList(unusedDependencyCheckerParams));
+      if (ops.directJars.length > 0) {
+        pluginParams.add("-P:dependency-analyzer:direct-jars:" + String.join(":", ops.directJars));
+      }
+      if (ops.directTargets.length > 0) {
+        String[] directTargets = encodeBazelTargets(ops.directTargets);
+        pluginParams.add("-P:dependency-analyzer:direct-targets:" + String.join(":", directTargets));
+      }
+      if (ops.indirectJars.length > 0) {
+        pluginParams.add("-P:dependency-analyzer:indirect-jars:" + String.join(":", ops.indirectJars));
+      }
+      if (ops.indirectTargets.length > 0) {
+        String[] indirectTargets = encodeBazelTargets(ops.indirectTargets);
+        pluginParams.add("-P:dependency-analyzer:indirect-targets:" + String.join(":", indirectTargets));
+      }
+      if (ops.unusedDepsIgnoredTargets.length > 0) {
+        String[] ignoredTargets = encodeBazelTargets(ops.unusedDepsIgnoredTargets);
+        pluginParams.add("-P:dependency-analyzer:unused-deps-ignored-targets:" + String.join(":", ignoredTargets));
+      }
     }
 
     return pluginParams.toArray(new String[pluginParams.size()]);
diff --git a/third_party/dependency_analyzer/src/main/io/bazel/rulesscala/dependencyanalyzer/DependencyAnalyzerSettings.scala b/third_party/dependency_analyzer/src/main/io/bazel/rulesscala/dependencyanalyzer/DependencyAnalyzerSettings.scala
index f6f87ca33..31e707097 100644
--- a/third_party/dependency_analyzer/src/main/io/bazel/rulesscala/dependencyanalyzer/DependencyAnalyzerSettings.scala
+++ b/third_party/dependency_analyzer/src/main/io/bazel/rulesscala/dependencyanalyzer/DependencyAnalyzerSettings.scala
@@ -75,7 +75,7 @@ object DependencyAnalyzerSettings {
         .takeStringOpt(key)
         .map { str =>
           AnalyzerMode.parse(str).getOrElse {
-            error(s"Failed to parse option $key")
+            error(s"Failed to parse option $key with value $str")
             AnalyzerMode.Error
           }
         }
diff --git a/twitter_scrooge/twitter_scrooge.bzl b/twitter_scrooge/twitter_scrooge.bzl
index a0217d668..a3223c1ce 100644
--- a/twitter_scrooge/twitter_scrooge.bzl
+++ b/twitter_scrooge/twitter_scrooge.bzl
@@ -13,7 +13,13 @@ load(
     "//scala/private:common.bzl",
     "write_manifest_file",
 )
-load("//scala/private:rule_impls.bzl", "compile_scala")
+load(
+    "//scala/private:dependency.bzl",
+    "dependency_info_for_addons",
+)
+load(
+    "//scala/private:rule_impls.bzl",
+    "compile_scala")
 load("@io_bazel_rules_scala//thrift:thrift_info.bzl", "ThriftInfo")
 load(
     "@io_bazel_rules_scala//thrift:thrift.bzl",
@@ -239,6 +245,8 @@ def _compile_scala(
         expect_java_output = False,
         scalac_jvm_flags = [],
         scalac = ctx.attr._scalac,
+        dependency_info = dependency_info_for_addons(ctx),
+        unused_dependency_checker_ignored_targets = []
     )
 
     return JavaInfo(

From 8b0da8a2c487df748c0ad0c8a712ef0c9da8c456 Mon Sep 17 00:00:00 2001
From: Jamie <jamieli550@gmail.com>
Date: Wed, 29 Jan 2020 16:29:18 -0800
Subject: [PATCH 2/7] lint

---
 scala/private/common.bzl                      | 12 ++++-----
 scala/private/dependency.bzl                  | 16 ++++++------
 .../phases/phase_collect_exports_jars.bzl     |  9 ++++---
 scala/private/phases/phase_collect_jars.bzl   | 19 +++++++-------
 scala/private/phases/phase_compile.bzl        | 20 +++++++-------
 scala/private/phases/phase_dependency.bzl     | 26 +++++++++----------
 scala/private/rule_impls.bzl                  | 20 +++++++-------
 scala_proto/private/scalapb_aspect.bzl        |  3 ++-
 twitter_scrooge/twitter_scrooge.bzl           |  5 ++--
 9 files changed, 66 insertions(+), 64 deletions(-)

diff --git a/scala/private/common.bzl b/scala/private/common.bzl
index 895c5fd75..742283e52 100644
--- a/scala/private/common.bzl
+++ b/scala/private/common.bzl
@@ -31,9 +31,9 @@ def collect_jars(
         runtime_jars.append(java_provider.transitive_runtime_jars)
 
         additional_transitive_compile_jars = _additional_transitive_compile_jars(
-            java_provider=java_provider,
-            dep_target=dep_target,
-            dependency_mode=dependency_mode
+            java_provider = java_provider,
+            dep_target = dep_target,
+            dependency_mode = dependency_mode,
         )
         transitive_compile_jars.append(additional_transitive_compile_jars)
 
@@ -76,15 +76,15 @@ def _additional_transitive_compile_jars(
         java_provider,
         dep_target,
         dependency_mode):
-    if dependency_mode == 'transitive':
+    if dependency_mode == "transitive":
         return java_provider.transitive_compile_time_jars
-    elif dependency_mode == 'plus-one':
+    elif dependency_mode == "plus-one":
         if PlusOneDeps in dep_target:
             plus_one_jars = [dep[JavaInfo].compile_jars for dep in dep_target[PlusOneDeps].direct_deps if JavaInfo in dep]
             return depset(transitive = plus_one_jars + [java_provider.compile_jars])
         else:
             return java_provider.compile_jars
-    else: # direct
+    else:  # direct
         return java_provider.compile_jars
 
 # When import mavan_jar's for scala macros we have to use the jar:file requirement
diff --git a/scala/private/dependency.bzl b/scala/private/dependency.bzl
index 994f17cd8..7de5dbec8 100644
--- a/scala/private/dependency.bzl
+++ b/scala/private/dependency.bzl
@@ -1,4 +1,3 @@
-
 # This file contains all computations for what the dependency mode is
 # (i.e. transitive, plus-one, direct, etc)
 # and what/how dependency analysis is performed (unused deps, strict deps, etc).
@@ -8,8 +7,8 @@ def new_dependency_info(
         unused_deps_mode,
         strict_deps_mode,
         dependency_tracking_method):
-
     is_strict_deps_on = strict_deps_mode != "off"
+
     # Currently we do not support running both strict and unused deps at the same time
     if is_strict_deps_on:
         unused_deps_mode = "off"
@@ -28,15 +27,15 @@ def new_dependency_info(
         dependency_tracking_method = "high-level",
         unused_deps_mode = unused_deps_mode,
         strict_deps_mode = strict_deps_mode,
-        use_analyzer = is_strict_deps_on or is_unused_deps_on
+        use_analyzer = is_strict_deps_on or is_unused_deps_on,
     )
 
 def dependency_info_for_addons(ctx):
     return new_dependency_info(
-        dependency_mode=_dependency_mode_for_addons(ctx),
-        unused_deps_mode="off",
-        strict_deps_mode=get_strict_deps_mode(ctx),
-        dependency_tracking_method="high-level"
+        dependency_mode = _dependency_mode_for_addons(ctx),
+        unused_deps_mode = "off",
+        strict_deps_mode = get_strict_deps_mode(ctx),
+        dependency_tracking_method = "high-level",
     )
 
 def get_dependency_mode(ctx):
@@ -59,9 +58,10 @@ def _dependency_mode_for_addons(ctx):
 def get_strict_deps_mode(ctx):
     if not hasattr(ctx.attr, "_dependency_analyzer_plugin"):
         return "off"
+
     # when the strict deps FT is removed the "default" check
     # will be removed since "default" will mean it's turned on
-    if  ctx.fragments.java.strict_java_deps == "default":
+    if ctx.fragments.java.strict_java_deps == "default":
         return "off"
     return ctx.fragments.java.strict_java_deps
 
diff --git a/scala/private/phases/phase_collect_exports_jars.bzl b/scala/private/phases/phase_collect_exports_jars.bzl
index abce4cef8..9a023d410 100644
--- a/scala/private/phases/phase_collect_exports_jars.bzl
+++ b/scala/private/phases/phase_collect_exports_jars.bzl
@@ -13,7 +13,8 @@ def phase_collect_exports_jars(ctx, p):
     # Since after, will not show up in deploy_jar or old jars runfiles
     # Notice that compile_jars is intentionally transitive for exports
     return collect_jars(
-        dep_targets=ctx.attr.exports,
-        dependency_mode='direct',
-        need_direct_info=False,
-        need_indirect_info=False)
+        dep_targets = ctx.attr.exports,
+        dependency_mode = "direct",
+        need_direct_info = False,
+        need_indirect_info = False,
+    )
diff --git a/scala/private/phases/phase_collect_jars.bzl b/scala/private/phases/phase_collect_jars.bzl
index 76cfb9dbd..0b51543e6 100644
--- a/scala/private/phases/phase_collect_jars.bzl
+++ b/scala/private/phases/phase_collect_jars.bzl
@@ -46,11 +46,11 @@ def phase_collect_jars_common(ctx, p):
 
 def _phase_collect_jars_default(ctx, p, _args = struct()):
     return _phase_collect_jars(
-        ctx=ctx,
-        p=p,
-        base_classpath=_args.base_classpath if hasattr(_args, "base_classpath") else p.scalac_provider.default_classpath,
-        extra_deps=_args.extra_deps if hasattr(_args, "extra_deps") else [],
-        extra_runtime_deps=_args.extra_runtime_deps if hasattr(_args, "extra_runtime_deps") else [],
+        ctx = ctx,
+        p = p,
+        base_classpath = _args.base_classpath if hasattr(_args, "base_classpath") else p.scalac_provider.default_classpath,
+        extra_deps = _args.extra_deps if hasattr(_args, "extra_deps") else [],
+        extra_runtime_deps = _args.extra_runtime_deps if hasattr(_args, "extra_runtime_deps") else [],
     )
 
 # Extract very common code out from dependency analysis into single place
@@ -62,12 +62,11 @@ def _phase_collect_jars(
         base_classpath,
         extra_deps,
         extra_runtime_deps):
-
     deps_jars = collect_jars(
-        dep_targets=ctx.attr.deps + extra_deps + base_classpath,
-        dependency_mode=p.dependency.dependency_mode,
-        need_direct_info=p.dependency.need_direct_info,
-        need_indirect_info=p.dependency.need_indirect_info,
+        dep_targets = ctx.attr.deps + extra_deps + base_classpath,
+        dependency_mode = p.dependency.dependency_mode,
+        need_direct_info = p.dependency.need_direct_info,
+        need_indirect_info = p.dependency.need_indirect_info,
     )
 
     (
diff --git a/scala/private/phases/phase_compile.bzl b/scala/private/phases/phase_compile.bzl
index 18dd72666..65c2a1c84 100644
--- a/scala/private/phases/phase_compile.bzl
+++ b/scala/private/phases/phase_compile.bzl
@@ -146,10 +146,10 @@ def _phase_compile(
         transitive_compile_jars,
         jars2labels,
         implicit_junit_deps_needed_for_java_compilation,
-        deps_providers=deps_providers,
-        default_classpath=default_classpath,
-        dependency_info=p.dependency,
-        unused_dependency_checker_ignored_targets=unused_dependency_checker_ignored_targets
+        deps_providers = deps_providers,
+        default_classpath = default_classpath,
+        dependency_info = p.dependency,
+        unused_dependency_checker_ignored_targets = unused_dependency_checker_ignored_targets,
     )
 
     # TODO: simplify the return values and use provider
@@ -217,10 +217,10 @@ def _compile_or_empty(
         # Thus we disable unused dependency checking when java sources are found
         if len(java_srcs) != 0:
             dependency_info = new_dependency_info(
-                dependency_mode=dependency_info.dependency_mode,
-                unused_deps_mode='off',
-                strict_deps_mode=dependency_info.strict_deps_mode,
-                dependency_tracking_method=dependency_info.dependency_tracking_method,
+                dependency_mode = dependency_info.dependency_mode,
+                unused_deps_mode = "off",
+                strict_deps_mode = dependency_info.strict_deps_mode,
+                dependency_tracking_method = dependency_info.dependency_tracking_method,
             )
 
         sources = [
@@ -248,8 +248,8 @@ def _compile_or_empty(
             ctx.attr.expect_java_output,
             ctx.attr.scalac_jvm_flags,
             ctx.attr._scalac,
-            dependency_info=dependency_info,
-            unused_dependency_checker_ignored_targets=unused_dependency_checker_ignored_targets
+            dependency_info = dependency_info,
+            unused_dependency_checker_ignored_targets = unused_dependency_checker_ignored_targets,
         )
 
         # build ijar if needed
diff --git a/scala/private/phases/phase_dependency.bzl b/scala/private/phases/phase_dependency.bzl
index 2771a8591..6e63242e2 100644
--- a/scala/private/phases/phase_dependency.bzl
+++ b/scala/private/phases/phase_dependency.bzl
@@ -8,12 +8,12 @@ load(
     "new_dependency_info",
 )
 
-def _phase_dependency_default(ctx, p, args=struct()):
+def _phase_dependency_default(ctx, p, args = struct()):
     return _phase_dependency(
-        ctx=ctx,
-        p=p,
-        unused_deps_always_off=args.unused_deps_always_off if hasattr(args, 'unused_deps_always_off') else False,
-        strict_deps_always_off=args.strict_deps_always_off if hasattr(args, 'strict_deps_always_off') else False,
+        ctx = ctx,
+        p = p,
+        unused_deps_always_off = args.unused_deps_always_off if hasattr(args, "unused_deps_always_off") else False,
+        strict_deps_always_off = args.strict_deps_always_off if hasattr(args, "strict_deps_always_off") else False,
     )
 
 def _phase_dependency(
@@ -22,20 +22,20 @@ def _phase_dependency(
         unused_deps_always_off,
         strict_deps_always_off):
     if strict_deps_always_off:
-        strict_deps_mode = 'off'
+        strict_deps_mode = "off"
     else:
         strict_deps_mode = get_strict_deps_mode(ctx)
 
     if unused_deps_always_off:
-        unused_deps_mode = 'off'
+        unused_deps_mode = "off"
     else:
         unused_deps_mode = get_unused_deps_mode(ctx)
 
     return new_dependency_info(
-        dependency_mode=get_dependency_mode(ctx),
-        unused_deps_mode=unused_deps_mode,
-        strict_deps_mode=strict_deps_mode,
-        dependency_tracking_method='high-level',
+        dependency_mode = get_dependency_mode(ctx),
+        unused_deps_mode = unused_deps_mode,
+        strict_deps_mode = strict_deps_mode,
+        dependency_tracking_method = "high-level",
     )
 
 def phase_dependency_common(ctx, p):
@@ -43,7 +43,7 @@ def phase_dependency_common(ctx, p):
 
 def phase_dependency_library_for_plugin_bootstrapping(ctx, p):
     args = struct(
-        unused_deps_always_off=True,
-        strict_deps_always_off=True,
+        unused_deps_always_off = True,
+        strict_deps_always_off = True,
     )
     return _phase_dependency_default(ctx, p, args)
diff --git a/scala/private/rule_impls.bzl b/scala/private/rule_impls.bzl
index 9105aae8f..e4cb619eb 100644
--- a/scala/private/rule_impls.bzl
+++ b/scala/private/rule_impls.bzl
@@ -67,7 +67,7 @@ def compile_scala(
     plugins = _collect_plugin_paths(plugins)
     internal_plugin_jars = []
     compiler_classpath_jars = cjars
-    if dependency_info.dependency_mode != 'direct':
+    if dependency_info.dependency_mode != "direct":
         compiler_classpath_jars = transitive_compile_jars
     optional_scalac_args = ""
     classpath_resources = []
@@ -82,32 +82,32 @@ def compile_scala(
         internal_plugin_jars = ctx.files._dependency_analyzer_plugin
 
         current_target = str(target_label)
-        optional_scalac_args_map['CurrentTarget'] = current_target
+        optional_scalac_args_map["CurrentTarget"] = current_target
 
     if dependency_info.need_indirect_info:
         transitive_cjars_list = transitive_compile_jars.to_list()
         indirect_jars = _join_path(transitive_cjars_list)
         indirect_targets = ",".join([str(labels[j.path]) for j in transitive_cjars_list])
 
-        optional_scalac_args_map['IndirectJars'] = indirect_jars
-        optional_scalac_args_map['IndirectTargets'] = indirect_targets
+        optional_scalac_args_map["IndirectJars"] = indirect_jars
+        optional_scalac_args_map["IndirectTargets"] = indirect_targets
 
-    if dependency_info.unused_deps_mode != 'off':
+    if dependency_info.unused_deps_mode != "off":
         ignored_targets = ",".join([str(d) for d in unused_dependency_checker_ignored_targets])
-        optional_scalac_args_map['UnusedDepsIgnoredTargets'] = ignored_targets
+        optional_scalac_args_map["UnusedDepsIgnoredTargets"] = ignored_targets
 
     if dependency_info.need_direct_info:
         cjars_list = cjars.to_list()
         if dependency_info.need_direct_jars:
             direct_jars = _join_path(cjars_list)
-            optional_scalac_args_map['DirectJars'] = direct_jars
+            optional_scalac_args_map["DirectJars"] = direct_jars
         if dependency_info.need_direct_targets:
             direct_targets = ",".join([str(labels[j.path]) for j in cjars_list])
-            optional_scalac_args_map['DirectTargets'] = direct_targets
+            optional_scalac_args_map["DirectTargets"] = direct_targets
 
     optional_scalac_args = "\n".join([
-        "{k}: {v}".format(k=k, v=v) for (k, v)
-        in sorted(optional_scalac_args_map.items())
+        "{k}: {v}".format(k = k, v = v)
+        for (k, v) in sorted(optional_scalac_args_map.items())
     ])
 
     plugins_list = plugins.to_list()
diff --git a/scala_proto/private/scalapb_aspect.bzl b/scala_proto/private/scalapb_aspect.bzl
index 0fc8fad7c..337e5a712 100644
--- a/scala_proto/private/scalapb_aspect.bzl
+++ b/scala_proto/private/scalapb_aspect.bzl
@@ -8,7 +8,8 @@ load(
 )
 load(
     "//scala/private:rule_impls.bzl",
-    "compile_scala")
+    "compile_scala",
+)
 load("//scala_proto/private:proto_to_scala_src.bzl", "proto_to_scala_src")
 
 ScalaPBAspectInfo = provider(fields = [
diff --git a/twitter_scrooge/twitter_scrooge.bzl b/twitter_scrooge/twitter_scrooge.bzl
index a3223c1ce..7b0b4907d 100644
--- a/twitter_scrooge/twitter_scrooge.bzl
+++ b/twitter_scrooge/twitter_scrooge.bzl
@@ -19,7 +19,8 @@ load(
 )
 load(
     "//scala/private:rule_impls.bzl",
-    "compile_scala")
+    "compile_scala",
+)
 load("@io_bazel_rules_scala//thrift:thrift_info.bzl", "ThriftInfo")
 load(
     "@io_bazel_rules_scala//thrift:thrift.bzl",
@@ -246,7 +247,7 @@ def _compile_scala(
         scalac_jvm_flags = [],
         scalac = ctx.attr._scalac,
         dependency_info = dependency_info_for_addons(ctx),
-        unused_dependency_checker_ignored_targets = []
+        unused_dependency_checker_ignored_targets = [],
     )
 
     return JavaInfo(

From 7ea430aa360f770d02d65f0fbd23c1fffc073bfb Mon Sep 17 00:00:00 2001
From: Jamie <jamieli550@gmail.com>
Date: Fri, 31 Jan 2020 09:34:56 -0800
Subject: [PATCH 3/7] path

---
 scala/private/phases/phase_compile.bzl    | 14 --------------
 scala/private/phases/phase_dependency.bzl | 11 +++++++++++
 2 files changed, 11 insertions(+), 14 deletions(-)

diff --git a/scala/private/phases/phase_compile.bzl b/scala/private/phases/phase_compile.bzl
index 056dbf4de..f1f4bd83e 100644
--- a/scala/private/phases/phase_compile.bzl
+++ b/scala/private/phases/phase_compile.bzl
@@ -9,10 +9,6 @@ load(
     "@io_bazel_rules_scala//scala/private:coverage_replacements_provider.bzl",
     _coverage_replacements_provider = "coverage_replacements_provider",
 )
-load(
-    "@io_bazel_rules_scala//scala/private:dependency.bzl",
-    "new_dependency_info",
-)
 load(
     "@io_bazel_rules_scala//scala/private:paths.bzl",
     _get_files_with_extension = "get_files_with_extension",
@@ -195,16 +191,6 @@ def _compile_or_empty(
         in_srcjars = _get_files_with_extension(ctx, _srcjar_extension)
         all_srcjars = depset(in_srcjars, transitive = [srcjars])
 
-        # We are not able to verify whether dependencies are used when compiling java sources
-        # Thus we disable unused dependency checking when java sources are found
-        if len(java_srcs) != 0:
-            dependency_info = new_dependency_info(
-                dependency_mode = dependency_info.dependency_mode,
-                unused_deps_mode = "off",
-                strict_deps_mode = dependency_info.strict_deps_mode,
-                dependency_tracking_method = dependency_info.dependency_tracking_method,
-            )
-
         sources = scala_srcs + java_srcs
         _compile_scala(
             ctx,
diff --git a/scala/private/phases/phase_dependency.bzl b/scala/private/phases/phase_dependency.bzl
index 6e63242e2..c4d0af99b 100644
--- a/scala/private/phases/phase_dependency.bzl
+++ b/scala/private/phases/phase_dependency.bzl
@@ -7,6 +7,11 @@ load(
     "get_unused_deps_mode",
     "new_dependency_info",
 )
+load(
+    "@io_bazel_rules_scala//scala/private:paths.bzl",
+    _get_files_with_extension = "get_files_with_extension",
+    _java_extension = "java_extension",
+)
 
 def _phase_dependency_default(ctx, p, args = struct()):
     return _phase_dependency(
@@ -31,6 +36,12 @@ def _phase_dependency(
     else:
         unused_deps_mode = get_unused_deps_mode(ctx)
 
+    # We are not able to verify whether dependencies are used when compiling java sources
+    # Thus we disable unused dependency checking when java sources are found
+    java_srcs = _get_files_with_extension(ctx, _java_extension)
+    if len(java_srcs) != 0:
+        unused_deps_mode = "off"
+
     return new_dependency_info(
         dependency_mode = get_dependency_mode(ctx),
         unused_deps_mode = unused_deps_mode,

From 9d60be250e5cf4d5ff04ac37ca4ff0c5fba90ace Mon Sep 17 00:00:00 2001
From: Jamie <jamieli550@gmail.com>
Date: Mon, 3 Feb 2020 10:33:07 -0800
Subject: [PATCH 4/7] comments

---
 scala/private/common.bzl                    |  2 +-
 scala/private/dependency.bzl                | 32 ++++-----------
 scala/private/phases/phase_collect_jars.bzl | 10 ++---
 scala/private/phases/phase_dependency.bzl   | 43 +++++++++++++++------
 scala/private/phases/phases.bzl             |  2 +
 scala/private/rule_impls.bzl                |  1 +
 scala/private/rules/scala_library.bzl       |  3 +-
 scala_proto/private/scalapb_aspect.bzl      |  4 +-
 twitter_scrooge/twitter_scrooge.bzl         |  4 +-
 9 files changed, 53 insertions(+), 48 deletions(-)

diff --git a/scala/private/common.bzl b/scala/private/common.bzl
index 742283e52..358743a03 100644
--- a/scala/private/common.bzl
+++ b/scala/private/common.bzl
@@ -78,7 +78,7 @@ def _additional_transitive_compile_jars(
         dependency_mode):
     if dependency_mode == "transitive":
         return java_provider.transitive_compile_time_jars
-    elif dependency_mode == "plus-one":
+    elif dependency_mode == "plus-one":  # XXX examine logic
         if PlusOneDeps in dep_target:
             plus_one_jars = [dep[JavaInfo].compile_jars for dep in dep_target[PlusOneDeps].direct_deps if JavaInfo in dep]
             return depset(transitive = plus_one_jars + [java_provider.compile_jars])
diff --git a/scala/private/dependency.bzl b/scala/private/dependency.bzl
index 7de5dbec8..b5a37aad1 100644
--- a/scala/private/dependency.bzl
+++ b/scala/private/dependency.bzl
@@ -30,27 +30,18 @@ def new_dependency_info(
         use_analyzer = is_strict_deps_on or is_unused_deps_on,
     )
 
-def dependency_info_for_addons(ctx):
+# TODO(https://github.com/bazelbuild/rules_scala/issues/987): Clariy the situation
+def legacy_unclear_dependency_info_for_protobuf_scrooge(ctx):
     return new_dependency_info(
-        dependency_mode = _dependency_mode_for_addons(ctx),
+        dependency_mode = _legacy_unclear_dependency_mode_for_protobuf_scrooge(ctx),
         unused_deps_mode = "off",
         strict_deps_mode = get_strict_deps_mode(ctx),
         dependency_tracking_method = "high-level",
     )
 
-def get_dependency_mode(ctx):
-    if get_is_strict_deps_on(ctx):
-        # all transitive dependencies are included
-        return "transitive"
-    elif not _is_plus_one_deps_off(ctx):
-        # dependencies and dependencies of dependencies are included
-        return "plus-one"
-    else:
-        # only explicitly-specified dependencies are included
-        return "direct"
-
-def _dependency_mode_for_addons(ctx):
-    if get_is_strict_deps_on(ctx):
+# TODO(https://github.com/bazelbuild/rules_scala/issues/987): Clariy the situation
+def _legacy_unclear_dependency_mode_for_protobuf_scrooge(ctx):
+    if is_strict_deps_on(ctx):
         return "transitive"
     else:
         return "direct"
@@ -65,14 +56,5 @@ def get_strict_deps_mode(ctx):
         return "off"
     return ctx.fragments.java.strict_java_deps
 
-def get_is_strict_deps_on(ctx):
+def is_strict_deps_on(ctx):
     return get_strict_deps_mode(ctx) != "off"
-
-def get_unused_deps_mode(ctx):
-    if ctx.attr.unused_dependency_checker_mode:
-        return ctx.attr.unused_dependency_checker_mode
-    else:
-        return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].unused_dependency_checker_mode
-
-def _is_plus_one_deps_off(ctx):
-    return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].plus_one_deps_mode == "off"
diff --git a/scala/private/phases/phase_collect_jars.bzl b/scala/private/phases/phase_collect_jars.bzl
index 0b51543e6..e16fcb7a1 100644
--- a/scala/private/phases/phase_collect_jars.bzl
+++ b/scala/private/phases/phase_collect_jars.bzl
@@ -47,7 +47,7 @@ def phase_collect_jars_common(ctx, p):
 def _phase_collect_jars_default(ctx, p, _args = struct()):
     return _phase_collect_jars(
         ctx = ctx,
-        p = p,
+        dependency_info = p.dependency,
         base_classpath = _args.base_classpath if hasattr(_args, "base_classpath") else p.scalac_provider.default_classpath,
         extra_deps = _args.extra_deps if hasattr(_args, "extra_deps") else [],
         extra_runtime_deps = _args.extra_runtime_deps if hasattr(_args, "extra_runtime_deps") else [],
@@ -58,15 +58,15 @@ def _phase_collect_jars_default(ctx, p, _args = struct()):
 # collects jars from deps, runtime jars from runtime_deps, and
 def _phase_collect_jars(
         ctx,
-        p,
+        dependency_info,
         base_classpath,
         extra_deps,
         extra_runtime_deps):
     deps_jars = collect_jars(
         dep_targets = ctx.attr.deps + extra_deps + base_classpath,
-        dependency_mode = p.dependency.dependency_mode,
-        need_direct_info = p.dependency.need_direct_info,
-        need_indirect_info = p.dependency.need_indirect_info,
+        dependency_mode = dependency_info.dependency_mode,
+        need_direct_info = dependency_info.need_direct_info,
+        need_indirect_info = dependency_info.need_indirect_info,
     )
 
     (
diff --git a/scala/private/phases/phase_dependency.bzl b/scala/private/phases/phase_dependency.bzl
index c4d0af99b..012c8d77d 100644
--- a/scala/private/phases/phase_dependency.bzl
+++ b/scala/private/phases/phase_dependency.bzl
@@ -2,9 +2,8 @@
 
 load(
     "@io_bazel_rules_scala//scala/private:dependency.bzl",
-    "get_dependency_mode",
     "get_strict_deps_mode",
-    "get_unused_deps_mode",
+    "is_strict_deps_on",
     "new_dependency_info",
 )
 load(
@@ -13,6 +12,16 @@ load(
     _java_extension = "java_extension",
 )
 
+def phase_dependency_common(ctx, p):
+    return _phase_dependency_default(ctx, p)
+
+def phase_dependency_library_for_plugin_bootstrapping(ctx, p):
+    args = struct(
+        unused_deps_always_off = True,
+        strict_deps_always_off = True,
+    )
+    return _phase_dependency_default(ctx, p, args)
+
 def _phase_dependency_default(ctx, p, args = struct()):
     return _phase_dependency(
         ctx = ctx,
@@ -34,7 +43,7 @@ def _phase_dependency(
     if unused_deps_always_off:
         unused_deps_mode = "off"
     else:
-        unused_deps_mode = get_unused_deps_mode(ctx)
+        unused_deps_mode = _get_unused_deps_mode(ctx)
 
     # We are not able to verify whether dependencies are used when compiling java sources
     # Thus we disable unused dependency checking when java sources are found
@@ -43,18 +52,28 @@ def _phase_dependency(
         unused_deps_mode = "off"
 
     return new_dependency_info(
-        dependency_mode = get_dependency_mode(ctx),
+        dependency_mode = _get_dependency_mode(ctx),
         unused_deps_mode = unused_deps_mode,
         strict_deps_mode = strict_deps_mode,
         dependency_tracking_method = "high-level",
     )
 
-def phase_dependency_common(ctx, p):
-    return _phase_dependency_default(ctx, p)
+def _is_plus_one_deps_on(ctx):
+    return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].plus_one_deps_mode != "off"
 
-def phase_dependency_library_for_plugin_bootstrapping(ctx, p):
-    args = struct(
-        unused_deps_always_off = True,
-        strict_deps_always_off = True,
-    )
-    return _phase_dependency_default(ctx, p, args)
+def _get_dependency_mode(ctx):
+    if is_strict_deps_on(ctx):
+        # all transitive dependencies are included
+        return "transitive"
+    elif _is_plus_one_deps_on(ctx):
+        # dependencies and dependencies of dependencies are included
+        return "plus-one"
+    else:
+        # only explicitly-specified dependencies are included
+        return "direct"
+
+def _get_unused_deps_mode(ctx):
+    if ctx.attr.unused_dependency_checker_mode:
+        return ctx.attr.unused_dependency_checker_mode
+    else:
+        return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].unused_dependency_checker_mode
diff --git a/scala/private/phases/phases.bzl b/scala/private/phases/phases.bzl
index 26acc1527..3f7ff7f06 100644
--- a/scala/private/phases/phases.bzl
+++ b/scala/private/phases/phases.bzl
@@ -33,6 +33,7 @@ load(
     _phase_compile_common = "phase_compile_common",
     _phase_compile_junit_test = "phase_compile_junit_test",
     _phase_compile_library = "phase_compile_library",
+    _phase_compile_library_for_plugin_bootstrapping = "phase_compile_library_for_plugin_bootstrapping",
     _phase_compile_macro_library = "phase_compile_macro_library",
     _phase_compile_repl = "phase_compile_repl",
     _phase_compile_scalatest = "phase_compile_scalatest",
@@ -120,6 +121,7 @@ phase_collect_jars_common = _phase_collect_jars_common
 # compile
 phase_compile_binary = _phase_compile_binary
 phase_compile_library = _phase_compile_library
+phase_compile_library_for_plugin_bootstrapping = _phase_compile_library_for_plugin_bootstrapping
 phase_compile_macro_library = _phase_compile_macro_library
 phase_compile_junit_test = _phase_compile_junit_test
 phase_compile_repl = _phase_compile_repl
diff --git a/scala/private/rule_impls.bzl b/scala/private/rule_impls.bzl
index ec3e46b8c..afe2717cf 100644
--- a/scala/private/rule_impls.bzl
+++ b/scala/private/rule_impls.bzl
@@ -104,6 +104,7 @@ def compile_scala(
 
     optional_scalac_args = "\n".join([
         "{k}: {v}".format(k = k, v = v)
+        # We sort the arguments for input stability and reproducibility
         for (k, v) in sorted(optional_scalac_args_map.items())
     ])
 
diff --git a/scala/private/rules/scala_library.bzl b/scala/private/rules/scala_library.bzl
index 5aee3e1c4..23805df49 100644
--- a/scala/private/rules/scala_library.bzl
+++ b/scala/private/rules/scala_library.bzl
@@ -24,6 +24,7 @@ load(
     "phase_collect_srcjars",
     "phase_compile_common",
     "phase_compile_library",
+    "phase_compile_library_for_plugin_bootstrapping",
     "phase_compile_macro_library",
     "phase_coverage_common",
     "phase_coverage_library",
@@ -142,7 +143,7 @@ def _scala_library_for_plugin_bootstrapping_impl(ctx):
             ("write_manifest", phase_write_manifest),
             ("dependency", phase_dependency_library_for_plugin_bootstrapping),
             ("collect_jars", phase_collect_jars_common),
-            ("compile", phase_compile_common),
+            ("compile", phase_compile_library_for_plugin_bootstrapping),
             ("merge_jars", phase_merge_jars),
             ("runfiles", phase_runfiles_library),
             ("collect_exports_jars", phase_collect_exports_jars),
diff --git a/scala_proto/private/scalapb_aspect.bzl b/scala_proto/private/scalapb_aspect.bzl
index 337e5a712..d76fd18ce 100644
--- a/scala_proto/private/scalapb_aspect.bzl
+++ b/scala_proto/private/scalapb_aspect.bzl
@@ -4,7 +4,7 @@ load(
 )
 load(
     "//scala/private:dependency.bzl",
-    "dependency_info_for_addons",
+    "legacy_unclear_dependency_info_for_protobuf_scrooge",
 )
 load(
     "//scala/private:rule_impls.bzl",
@@ -96,7 +96,7 @@ def _compile_scala(
         expect_java_output = False,
         scalac_jvm_flags = [],
         scalac = scalac,
-        dependency_info = dependency_info_for_addons(ctx),
+        dependency_info = legacy_unclear_dependency_info_for_protobuf_scrooge(ctx),
         unused_dependency_checker_ignored_targets = [],
     )
 
diff --git a/twitter_scrooge/twitter_scrooge.bzl b/twitter_scrooge/twitter_scrooge.bzl
index 7b0b4907d..fa2482939 100644
--- a/twitter_scrooge/twitter_scrooge.bzl
+++ b/twitter_scrooge/twitter_scrooge.bzl
@@ -15,7 +15,7 @@ load(
 )
 load(
     "//scala/private:dependency.bzl",
-    "dependency_info_for_addons",
+    "legacy_unclear_dependency_info_for_protobuf_scrooge",
 )
 load(
     "//scala/private:rule_impls.bzl",
@@ -246,7 +246,7 @@ def _compile_scala(
         expect_java_output = False,
         scalac_jvm_flags = [],
         scalac = ctx.attr._scalac,
-        dependency_info = dependency_info_for_addons(ctx),
+        dependency_info = legacy_unclear_dependency_info_for_protobuf_scrooge(ctx),
         unused_dependency_checker_ignored_targets = [],
     )
 

From f442b26af263db7504dc97d096ee4d7382b8e13a Mon Sep 17 00:00:00 2001
From: Jamie <jamieli550@gmail.com>
Date: Mon, 3 Feb 2020 10:46:01 -0800
Subject: [PATCH 5/7] comments

---
 scala/private/common.bzl                       |  6 +++++-
 .../phases/phase_collect_exports_jars.bzl      |  8 ++++----
 scala/private/phases/phase_collect_jars.bzl    | 18 +++++++++---------
 scala/private/phases/phase_compile.bzl         | 12 ++++++------
 scala/private/phases/phase_dependency.bzl      | 16 ++++++++--------
 5 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/scala/private/common.bzl b/scala/private/common.bzl
index 358743a03..2c600df39 100644
--- a/scala/private/common.bzl
+++ b/scala/private/common.bzl
@@ -78,9 +78,13 @@ def _additional_transitive_compile_jars(
         dependency_mode):
     if dependency_mode == "transitive":
         return java_provider.transitive_compile_time_jars
-    elif dependency_mode == "plus-one":  # XXX examine logic
+    elif dependency_mode == "plus-one":
+        # dep_target will not always have a PlusOneDeps provider, such as
+        # with scala_maven_import_external, hence the need for the fallback.
         if PlusOneDeps in dep_target:
             plus_one_jars = [dep[JavaInfo].compile_jars for dep in dep_target[PlusOneDeps].direct_deps if JavaInfo in dep]
+            # plus_one_jars only contains the deps of deps, not the deps themselves.
+            # Hence the need to include the dep's compile jars anyways
             return depset(transitive = plus_one_jars + [java_provider.compile_jars])
         else:
             return java_provider.compile_jars
diff --git a/scala/private/phases/phase_collect_exports_jars.bzl b/scala/private/phases/phase_collect_exports_jars.bzl
index 9a023d410..fe88e9344 100644
--- a/scala/private/phases/phase_collect_exports_jars.bzl
+++ b/scala/private/phases/phase_collect_exports_jars.bzl
@@ -13,8 +13,8 @@ def phase_collect_exports_jars(ctx, p):
     # Since after, will not show up in deploy_jar or old jars runfiles
     # Notice that compile_jars is intentionally transitive for exports
     return collect_jars(
-        dep_targets = ctx.attr.exports,
-        dependency_mode = "direct",
-        need_direct_info = False,
-        need_indirect_info = False,
+        ctx.attr.exports,
+        "direct",
+        False,
+        False,
     )
diff --git a/scala/private/phases/phase_collect_jars.bzl b/scala/private/phases/phase_collect_jars.bzl
index e16fcb7a1..e246f1c2a 100644
--- a/scala/private/phases/phase_collect_jars.bzl
+++ b/scala/private/phases/phase_collect_jars.bzl
@@ -46,11 +46,11 @@ def phase_collect_jars_common(ctx, p):
 
 def _phase_collect_jars_default(ctx, p, _args = struct()):
     return _phase_collect_jars(
-        ctx = ctx,
-        dependency_info = p.dependency,
-        base_classpath = _args.base_classpath if hasattr(_args, "base_classpath") else p.scalac_provider.default_classpath,
-        extra_deps = _args.extra_deps if hasattr(_args, "extra_deps") else [],
-        extra_runtime_deps = _args.extra_runtime_deps if hasattr(_args, "extra_runtime_deps") else [],
+        ctx,
+        p.dependency,
+        _args.base_classpath if hasattr(_args, "base_classpath") else p.scalac_provider.default_classpath,
+        _args.extra_deps if hasattr(_args, "extra_deps") else [],
+        _args.extra_runtime_deps if hasattr(_args, "extra_runtime_deps") else [],
     )
 
 # Extract very common code out from dependency analysis into single place
@@ -63,10 +63,10 @@ def _phase_collect_jars(
         extra_deps,
         extra_runtime_deps):
     deps_jars = collect_jars(
-        dep_targets = ctx.attr.deps + extra_deps + base_classpath,
-        dependency_mode = dependency_info.dependency_mode,
-        need_direct_info = dependency_info.need_direct_info,
-        need_indirect_info = dependency_info.need_indirect_info,
+        ctx.attr.deps + extra_deps + base_classpath,
+        dependency_info.dependency_mode,
+        dependency_info.need_direct_info,
+        dependency_info.need_indirect_info,
     )
 
     (
diff --git a/scala/private/phases/phase_compile.bzl b/scala/private/phases/phase_compile.bzl
index 0f230cbfd..dce03f170 100644
--- a/scala/private/phases/phase_compile.bzl
+++ b/scala/private/phases/phase_compile.bzl
@@ -137,10 +137,10 @@ def _phase_compile(
         transitive_compile_jars,
         jars2labels,
         implicit_junit_deps_needed_for_java_compilation,
-        deps_providers = deps_providers,
-        default_classpath = default_classpath,
-        dependency_info = p.dependency,
-        unused_dependency_checker_ignored_targets = unused_dependency_checker_ignored_targets,
+        p.dependency,
+        deps_providers,
+        default_classpath,
+        unused_dependency_checker_ignored_targets,
     )
 
     # TODO: simplify the return values and use provider
@@ -204,8 +204,8 @@ def _compile_or_empty(
             ctx.attr.expect_java_output,
             ctx.attr.scalac_jvm_flags,
             ctx.attr._scalac,
-            dependency_info = dependency_info,
-            unused_dependency_checker_ignored_targets = unused_dependency_checker_ignored_targets,
+            dependency_info,
+            unused_dependency_checker_ignored_targets,
         )
 
         # build ijar if needed
diff --git a/scala/private/phases/phase_dependency.bzl b/scala/private/phases/phase_dependency.bzl
index 012c8d77d..90eab5545 100644
--- a/scala/private/phases/phase_dependency.bzl
+++ b/scala/private/phases/phase_dependency.bzl
@@ -24,10 +24,10 @@ def phase_dependency_library_for_plugin_bootstrapping(ctx, p):
 
 def _phase_dependency_default(ctx, p, args = struct()):
     return _phase_dependency(
-        ctx = ctx,
-        p = p,
-        unused_deps_always_off = args.unused_deps_always_off if hasattr(args, "unused_deps_always_off") else False,
-        strict_deps_always_off = args.strict_deps_always_off if hasattr(args, "strict_deps_always_off") else False,
+        ctx,
+        p,
+        args.unused_deps_always_off if hasattr(args, "unused_deps_always_off") else False,
+        args.strict_deps_always_off if hasattr(args, "strict_deps_always_off") else False,
     )
 
 def _phase_dependency(
@@ -52,10 +52,10 @@ def _phase_dependency(
         unused_deps_mode = "off"
 
     return new_dependency_info(
-        dependency_mode = _get_dependency_mode(ctx),
-        unused_deps_mode = unused_deps_mode,
-        strict_deps_mode = strict_deps_mode,
-        dependency_tracking_method = "high-level",
+        _get_dependency_mode(ctx),
+        unused_deps_mode,
+        strict_deps_mode,
+        "high-level",
     )
 
 def _is_plus_one_deps_on(ctx):

From fe91373aa5dd0d7770528f94af1443d57a59dce4 Mon Sep 17 00:00:00 2001
From: Jamie <jamieli550@gmail.com>
Date: Mon, 3 Feb 2020 11:47:43 -0800
Subject: [PATCH 6/7] lint

---
 scala/private/common.bzl | 1 +
 1 file changed, 1 insertion(+)

diff --git a/scala/private/common.bzl b/scala/private/common.bzl
index 2c600df39..2a3de8d14 100644
--- a/scala/private/common.bzl
+++ b/scala/private/common.bzl
@@ -83,6 +83,7 @@ def _additional_transitive_compile_jars(
         # with scala_maven_import_external, hence the need for the fallback.
         if PlusOneDeps in dep_target:
             plus_one_jars = [dep[JavaInfo].compile_jars for dep in dep_target[PlusOneDeps].direct_deps if JavaInfo in dep]
+
             # plus_one_jars only contains the deps of deps, not the deps themselves.
             # Hence the need to include the dep's compile jars anyways
             return depset(transitive = plus_one_jars + [java_provider.compile_jars])

From 4a897f49af54c4cce2a132889931e51e5c6c3aed Mon Sep 17 00:00:00 2001
From: Jamie <jamieli550@gmail.com>
Date: Thu, 6 Feb 2020 12:01:48 -0800
Subject: [PATCH 7/7] comment

---
 scala/private/phases/phase_collect_jars.bzl | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/scala/private/phases/phase_collect_jars.bzl b/scala/private/phases/phase_collect_jars.bzl
index e246f1c2a..a353e0ac7 100644
--- a/scala/private/phases/phase_collect_jars.bzl
+++ b/scala/private/phases/phase_collect_jars.bzl
@@ -47,7 +47,7 @@ def phase_collect_jars_common(ctx, p):
 def _phase_collect_jars_default(ctx, p, _args = struct()):
     return _phase_collect_jars(
         ctx,
-        p.dependency,
+        p,
         _args.base_classpath if hasattr(_args, "base_classpath") else p.scalac_provider.default_classpath,
         _args.extra_deps if hasattr(_args, "extra_deps") else [],
         _args.extra_runtime_deps if hasattr(_args, "extra_runtime_deps") else [],
@@ -58,15 +58,15 @@ def _phase_collect_jars_default(ctx, p, _args = struct()):
 # collects jars from deps, runtime jars from runtime_deps, and
 def _phase_collect_jars(
         ctx,
-        dependency_info,
+        p,
         base_classpath,
         extra_deps,
         extra_runtime_deps):
     deps_jars = collect_jars(
         ctx.attr.deps + extra_deps + base_classpath,
-        dependency_info.dependency_mode,
-        dependency_info.need_direct_info,
-        dependency_info.need_indirect_info,
+        p.dependency.dependency_mode,
+        p.dependency.need_direct_info,
+        p.dependency.need_indirect_info,
     )
 
     (