Skip to content

Commit 7ea9079

Browse files
authored
add plus one deps transitive mode for compilation (#698)
* add plus one deps transitive mode for compilation tries to balance between sterness of strict-deps off and strict-deps on cache issues and false positives * Update .travis.yml * move plusone provider and aspect to separate file * try 17 again after moving out provider * move configuration from `define` to attribute on toolchain * added documentation for the plusone provider * remove trailing 9
1 parent 1fa4408 commit 7ea9079

24 files changed

+240
-37
lines changed

WORKSPACE

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,30 @@ git_repository(
171171
name = "bazel_skylib",
172172
remote = "https://github.com/bazelbuild/bazel-skylib.git",
173173
tag = "0.6.0",
174-
)
174+
)
175+
176+
## deps for tests of limited deps support
177+
scala_maven_import_external(
178+
name = "org_springframework_spring_core",
179+
artifact = "org.springframework:spring-core:5.1.5.RELEASE",
180+
jar_sha256 = "f771b605019eb9d2cf8f60c25c050233e39487ff54d74c93d687ea8de8b7285a",
181+
licenses = ["notice"], # Apache 2.0
182+
server_urls = [
183+
"https://repo1.maven.org/maven2/",
184+
"https://mirror.bazel.build/repo1.maven.org/maven2",
185+
],
186+
)
187+
188+
scala_maven_import_external(
189+
name = "org_springframework_spring_tx",
190+
artifact = "org.springframework:spring-tx:5.1.5.RELEASE",
191+
jar_sha256 = "666f72b73c7e6b34e5bb92a0d77a14cdeef491c00fcb07a1e89eb62b08500135",
192+
licenses = ["notice"], # Apache 2.0
193+
server_urls = [
194+
"https://repo1.maven.org/maven2/",
195+
"https://mirror.bazel.build/repo1.maven.org/maven2",
196+
],
197+
deps = [
198+
"@org_springframework_spring_core"
199+
]
200+
)

scala/plusone.bzl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""
2+
Keeps direct compile dependencies of targets.
3+
This enables targets to pass the to compiler the plus one dependencies in addition to the direct ones.
4+
For motivation of plus one see the e2e tests
5+
"""
6+
PlusOneDeps = provider(
7+
fields = {
8+
'direct_deps' : 'list of direct compile dependencies of a target',
9+
}
10+
)
11+
12+
def _collect_plus_one_deps_aspect_impl(target, ctx):
13+
return [PlusOneDeps(direct_deps = getattr(ctx.rule.attr,'deps',[]))]
14+
15+
collect_plus_one_deps_aspect = aspect(implementation = _collect_plus_one_deps_aspect_impl,
16+
attr_aspects = ['deps'],
17+
)

scala/private/common.bzl

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
load("@io_bazel_rules_scala//scala:jars_to_labels.bzl", "JarsToLabelsInfo")
2+
load("@io_bazel_rules_scala//scala:plusone.bzl", "PlusOneDeps")
23

34
def write_manifest(ctx):
45
main_class = getattr(ctx.attr, "main_class", None)
@@ -22,21 +23,25 @@ def collect_srcjars(targets):
2223
def collect_jars(
2324
dep_targets,
2425
dependency_analyzer_is_off = True,
25-
unused_dependency_checker_is_off = True):
26+
unused_dependency_checker_is_off = True,
27+
plus_one_deps_is_off = True):
2628
"""Compute the runtime and compile-time dependencies from the given targets""" # noqa
2729

2830
if dependency_analyzer_is_off:
2931
return _collect_jars_when_dependency_analyzer_is_off(
3032
dep_targets,
3133
unused_dependency_checker_is_off,
34+
plus_one_deps_is_off,
3235
)
3336
else:
3437
return _collect_jars_when_dependency_analyzer_is_on(dep_targets)
3538

3639
def _collect_jars_when_dependency_analyzer_is_off(
3740
dep_targets,
38-
unused_dependency_checker_is_off):
41+
unused_dependency_checker_is_off,
42+
plus_one_deps_is_off):
3943
compile_jars = []
44+
plus_one_deps_compile_jars = []
4045
runtime_jars = []
4146
jars2labels = {}
4247

@@ -58,11 +63,16 @@ def _collect_jars_when_dependency_analyzer_is_off(
5863
else:
5964
print("ignored dependency, has no JavaInfo: " + str(dep_target))
6065

66+
if (not plus_one_deps_is_off) and (PlusOneDeps in dep_target):
67+
plus_one_deps_compile_jars.append(
68+
depset(transitive = [dep[JavaInfo].compile_jars for dep in dep_target[PlusOneDeps].direct_deps if JavaInfo in dep ])
69+
)
70+
6171
return struct(
6272
compile_jars = depset(transitive = compile_jars),
6373
transitive_runtime_jars = depset(transitive = runtime_jars),
6474
jars2labels = JarsToLabelsInfo(jars_to_labels = jars2labels),
65-
transitive_compile_jars = depset(),
75+
transitive_compile_jars = depset(transitive = compile_jars + plus_one_deps_compile_jars),
6676
)
6777

6878
def _collect_jars_when_dependency_analyzer_is_on(dep_targets):

scala/private/rule_impls.bzl

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ CurrentTarget: {current_target}
220220
ignored_targets = ignored_targets,
221221
current_target = current_target,
222222
)
223+
if is_dependency_analyzer_off(ctx) and not _is_plus_one_deps_off(ctx):
224+
compiler_classpath_jars = transitive_compile_jars
223225

224226
plugins_list = plugins.to_list()
225227
plugin_arg = _join_path(plugins_list)
@@ -616,6 +618,9 @@ def is_dependency_analyzer_on(ctx):
616618
def is_dependency_analyzer_off(ctx):
617619
return not is_dependency_analyzer_on(ctx)
618620

621+
def _is_plus_one_deps_off(ctx):
622+
return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].plus_one_deps_mode == "off"
623+
619624
# Extract very common code out from dependency analysis into single place
620625
# automatically adds dependency on scala-library and scala-reflect
621626
# collects jars from deps, runtime jars from runtime_deps, and
@@ -631,6 +636,7 @@ def _collect_jars_from_common_ctx(
631636
ctx.attr.deps + extra_deps + base_classpath,
632637
dependency_analyzer_is_off,
633638
unused_dependency_checker_is_off,
639+
_is_plus_one_deps_off(ctx),
634640
)
635641

636642
(
@@ -985,9 +991,10 @@ def scala_test_impl(ctx):
985991
]
986992
unused_dependency_checker_is_off = unused_dependency_checker_mode == "off"
987993

994+
scalatest_base_classpath = scalac_provider.default_classpath + [ctx.attr._scalatest]
988995
jars = _collect_jars_from_common_ctx(
989996
ctx,
990-
scalac_provider.default_classpath,
997+
scalatest_base_classpath,
991998
extra_runtime_deps = [
992999
ctx.attr._scalatest_reporter,
9931000
ctx.attr._scalatest_runner,
@@ -1006,36 +1013,6 @@ def scala_test_impl(ctx):
10061013
jars.jars2labels,
10071014
)
10081015

1009-
# _scalatest is an http_jar, so its compile jar is run through ijar
1010-
# however, contains macros, so need to handle separately
1011-
scalatest_jars = collect_jars([ctx.attr._scalatest]).transitive_runtime_jars
1012-
cjars = depset(transitive = [cjars, scalatest_jars])
1013-
transitive_rjars = depset(transitive = [transitive_rjars, scalatest_jars])
1014-
1015-
if is_dependency_analyzer_on(ctx):
1016-
transitive_compile_jars = depset(
1017-
transitive = [scalatest_jars, transitive_compile_jars],
1018-
)
1019-
scalatest_jars_list = scalatest_jars.to_list()
1020-
j2l = jars_to_labels.jars_to_labels
1021-
add_labels_of_jars_to(
1022-
j2l,
1023-
ctx.attr._scalatest,
1024-
scalatest_jars_list,
1025-
scalatest_jars_list,
1026-
)
1027-
jars_to_labels = JarsToLabelsInfo(jars_to_labels = j2l)
1028-
1029-
elif not unused_dependency_checker_is_off:
1030-
j2l = jars_to_labels.jars_to_labels
1031-
add_labels_of_jars_to(
1032-
j2l,
1033-
ctx.attr._scalatest,
1034-
[],
1035-
scalatest_jars.to_list(),
1036-
)
1037-
jars_to_labels = JarsToLabelsInfo(jars_to_labels = j2l)
1038-
10391016
args = " ".join([
10401017
"-R \"{path}\"".format(path = ctx.outputs.jar.short_path),
10411018
_scala_test_flags(ctx),

scala/scala.bzl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ load(
2424
"@io_bazel_rules_scala//specs2:specs2_junit.bzl",
2525
_specs2_junit_dependencies = "specs2_junit_dependencies",
2626
)
27+
load(
28+
"@io_bazel_rules_scala//scala:plusone.bzl",
29+
_collect_plus_one_deps_aspect = "collect_plus_one_deps_aspect",
30+
)
2731

2832
_launcher_template = {
2933
"_java_stub_template": attr.label(
@@ -105,7 +109,7 @@ _junit_resolve_deps = {
105109
# Common attributes reused across multiple rules.
106110
_common_attrs_for_plugin_bootstrapping = {
107111
"srcs": attr.label_list(allow_files = [".scala", ".srcjar", ".java"]),
108-
"deps": attr.label_list(),
112+
"deps": attr.label_list(aspects = [_collect_plus_one_deps_aspect]),
109113
"plugins": attr.label_list(allow_files = [".jar"]),
110114
"runtime_deps": attr.label_list(providers = [[JavaInfo]]),
111115
"data": attr.label_list(allow_files = True),

scala/scala_toolchain.bzl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def _scala_toolchain_impl(ctx):
88
scalacopts = ctx.attr.scalacopts,
99
scalac_provider_attr = ctx.attr.scalac_provider_attr,
1010
unused_dependency_checker_mode = ctx.attr.unused_dependency_checker_mode,
11+
plus_one_deps_mode = ctx.attr.plus_one_deps_mode,
1112
)
1213
return [toolchain]
1314

@@ -23,5 +24,9 @@ scala_toolchain = rule(
2324
default = "off",
2425
values = ["off", "warn", "error"],
2526
),
27+
"plus_one_deps_mode": attr.string(
28+
default = "off",
29+
values = ["off", "on"],
30+
),
2631
},
2732
)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
load("//scala:scala_toolchain.bzl", "scala_toolchain")
2+
scala_toolchain(
3+
name = "plus_one_deps_impl",
4+
plus_one_deps_mode = "on",
5+
visibility = ["//visibility:public"],
6+
)
7+
toolchain(
8+
name = "plus_one_deps",
9+
toolchain = "plus_one_deps_impl",
10+
toolchain_type = "@io_bazel_rules_scala//scala:toolchain_type",
11+
visibility = ["//visibility:public"],
12+
)
13+
scala_toolchain(
14+
name = "plus_one_deps_with_unused_error_impl",
15+
unused_dependency_checker_mode = "error",
16+
plus_one_deps_mode = "on",
17+
visibility = ["//visibility:public"],
18+
)
19+
toolchain(
20+
name = "plus_one_deps_with_unused_error",
21+
toolchain = "plus_one_deps_with_unused_error_impl",
22+
toolchain_type = "@io_bazel_rules_scala//scala:toolchain_type",
23+
visibility = ["//visibility:public"],
24+
)
25+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package scalarules.test_expect_failure.plus_one_deps.internal_deps
2+
3+
class A {
4+
println(new B().hi)
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package scalarules.test_expect_failure.plus_one_deps.internal_deps
2+
3+
class B extends C {
4+
def hi: String = "hi"
5+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
load("//scala:scala.bzl", "scala_library")
2+
scala_library(
3+
name = "a",
4+
srcs = ["A.scala"],
5+
deps = [":b"],
6+
)
7+
scala_library(
8+
name = "b",
9+
srcs = ["B.scala"],
10+
deps = [":c"],
11+
)
12+
scala_library(
13+
name = "c",
14+
srcs = ["C.scala"],
15+
deps = [":d"],
16+
exports = ["d"],
17+
)
18+
scala_library(
19+
name = "d",
20+
srcs = ["D.scala"],
21+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package scalarules.test_expect_failure.plus_one_deps.internal_deps
2+
3+
class C extends D
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package scalarules.test_expect_failure.plus_one_deps.internal_deps
2+
3+
class D {
4+
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package scalarules.test_expect_failure.plus_one_deps.external_deps
2+
import org.springframework.dao.DataAccessException
3+
4+
class A {
5+
println(classOf[DataAccessException])
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
load("//scala:scala.bzl", "scala_library")
2+
scala_library(
3+
name = "a",
4+
srcs = ["A.scala"],
5+
deps = ["@org_springframework_spring_tx"]
6+
7+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package scalarules.test_expect_failure.plus_one_deps.internal_deps
2+
3+
class A {
4+
println(new B().hi)
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package scalarules.test_expect_failure.plus_one_deps.internal_deps
2+
3+
class B extends C {
4+
def hi: String = "hi"
5+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
load("//scala:scala.bzl", "scala_library")
2+
scala_library(
3+
name = "a",
4+
srcs = ["A.scala"],
5+
deps = [":b"],
6+
)
7+
scala_library(
8+
name = "b",
9+
srcs = ["B.scala"],
10+
deps = [":c"],
11+
)
12+
scala_library(
13+
name = "c",
14+
srcs = ["C.scala"],
15+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package scalarules.test_expect_failure.plus_one_deps.internal_deps
2+
3+
class C
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package scalarules.test_expect_failure.plus_one_deps.with_unused_deps
2+
3+
class A {
4+
println(new B().hi)
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package scalarules.test_expect_failure.plus_one_deps.with_unused_deps
2+
3+
class B {
4+
def hi: String = {
5+
println(classOf[C])
6+
"hi"
7+
}
8+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
load("//scala:scala.bzl", "scala_library")
2+
scala_library(
3+
name = "a",
4+
srcs = ["A.scala"],
5+
deps = [":b",":c"],
6+
)
7+
scala_library(
8+
name = "b",
9+
srcs = ["B.scala"],
10+
deps = [":c"],
11+
)
12+
scala_library(
13+
name = "c",
14+
srcs = ["C.scala"],
15+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package scalarules.test_expect_failure.plus_one_deps.with_unused_deps
2+
3+
class C

test_expect_failure/scala_import/ScalaImportPropagatesCompileDepsTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import org.specs2.mutable.SpecificationWithJUnit
77
class ScalaImportPropagatesCompileDepsTest extends SpecificationWithJUnit {
88

99
"scala_import" should {
10-
"propagate runtime deps" in {
10+
"propagate compile time deps" in {
1111
println(classOf[Cache[String, String]])
1212
println(classOf[ArrayUtils])
1313
println(classOf[cats.Applicative[Any]])

0 commit comments

Comments
 (0)