Skip to content

Commit b135622

Browse files
johnynekdamienmg
authored andcommitted
Adds export and runtime_deps support to scala rule
This brings the scala rules a step closer to feature parity with java. -- Change-Id: I21e6929a36f5084a29b230f1b91307dd5b012d4c Reviewed-on: bazelbuild/bazel#916 MOS_MIGRATED_REVID=114947019
1 parent 9b589f3 commit b135622

File tree

7 files changed

+99
-31
lines changed

7 files changed

+99
-31
lines changed

README.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ scala_repositories()
3434
## scala\_library / scala\_macro_library
3535

3636
```python
37-
scala_library(name, srcs, deps, data, main_class, resources, scalacopts, jvm_flags)
38-
scala_macro_library(name, srcs, deps, data, main_class, resources, scalacopts, jvm_flags)
37+
scala_library(name, srcs, deps, runtime_deps, exports, data, main_class, resources, scalacopts, jvm_flags)
38+
scala_macro_library(name, srcs, deps, runtime_deps, exports, data, main_class, resources, scalacopts, jvm_flags)
3939
```
4040

4141
`scala_library` generates a `.jar` file from `.scala` source files. This rule
@@ -81,6 +81,22 @@ In order to make a java rule use this jar file, use the `java_import` rule.
8181
<p>List of other libraries to linked to this library target</p>
8282
</td>
8383
</tr>
84+
<tr>
85+
<td><code>runtime_deps</code></td>
86+
<td>
87+
<p><code>List of labels, optional</code></p>
88+
<p>List of other libraries to put on the classpath only at runtime. This is rarely needed in Scala.</p>
89+
</td>
90+
</tr>
91+
<tr>
92+
<td><code>exports</code></td>
93+
<td>
94+
<p><code>List of labels, optional</code></p>
95+
<p>List of targets to add to the dependencies of those that depend on this target. Similar
96+
to the `java_library` parameter of the same name. Use this sparingly as it weakens the
97+
precision of the build graph.</p>
98+
</td>
99+
</tr>
84100
<tr>
85101
<td><code>data</code></td>
86102
<td>
@@ -141,7 +157,7 @@ In order to make a java rule use this jar file, use the `java_import` rule.
141157
## scala_binary
142158

143159
```python
144-
scala_binary(name, srcs, deps, data, main_class, resources, scalacopts, jvm_flags)
160+
scala_binary(name, srcs, deps, runtime_deps, data, main_class, resources, scalacopts, jvm_flags)
145161
```
146162

147163
`scala_binary` generates a Scala executable. It may depend on `scala_library`, `scala_macro_library`
@@ -181,6 +197,13 @@ A `scala_binary` requires a `main_class` attribute.
181197
<p>List of other libraries to linked to this binary target</p>
182198
</td>
183199
</tr>
200+
<tr>
201+
<td><code>runtime_deps</code></td>
202+
<td>
203+
<p><code>List of labels, optional</code></p>
204+
<p>List of other libraries to put on the classpath only at runtime. This is rarely needed in Scala.</p>
205+
</td>
206+
</tr>
184207
<tr>
185208
<td><code>data</code></td>
186209
<td>

scala/scala.bzl

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,17 @@ cd $0.runfiles
117117
output=ctx.outputs.executable,
118118
content=content)
119119

120-
def _collect_comp_run_jars(ctx):
121-
compile_jars = set() # not transitive
122-
runtime_jars = set() # this is transitive
123-
for target in ctx.attr.deps:
120+
def _collect_jars(targets):
121+
"""Compute the runtime and compile-time dependencies from the given targets"""
122+
compile_jars = set() # not transitive
123+
runtime_jars = set() # this is transitive
124+
for target in targets:
124125
found = False
125126
if hasattr(target, "scala"):
126127
compile_jars += [target.scala.outputs.ijar]
128+
compile_jars += target.scala.transitive_compile_exports
127129
runtime_jars += target.scala.transitive_runtime_deps
130+
runtime_jars += target.scala.transitive_runtime_exports
128131
found = True
129132
if hasattr(target, "java"):
130133
# see JavaSkylarkApiProvider.java, this is just the compile-time deps
@@ -137,43 +140,47 @@ def _collect_comp_run_jars(ctx):
137140
# support http_file pointed at a jar. http_jar uses ijar, which breaks scala macros
138141
runtime_jars += target.files
139142
compile_jars += target.files
140-
return (compile_jars, runtime_jars)
143+
return struct(compiletime = compile_jars, runtime = runtime_jars)
141144

142-
def _scala_library_impl(ctx):
143-
(cjars, rjars) = _collect_comp_run_jars(ctx)
145+
def _lib(ctx, use_ijar):
146+
jars = _collect_jars(ctx.attr.deps)
147+
(cjars, rjars) = (jars.compiletime, jars.runtime)
144148
_write_manifest(ctx)
145-
_compile(ctx, cjars, True)
149+
_compile(ctx, cjars, use_ijar)
146150

147151
rjars += [ctx.outputs.jar]
148-
scalaattr = struct(outputs = struct(ijar=ctx.outputs.ijar, class_jar=ctx.outputs.jar),
149-
transitive_runtime_deps = rjars)
150-
runfiles = ctx.runfiles(
151-
files = list(rjars),
152-
collect_data = True)
153-
return struct(
154-
scala = scalaattr,
155-
runfiles=runfiles)
152+
rjars += _collect_jars(ctx.attr.runtime_deps).runtime
156153

157-
def _scala_macro_library_impl(ctx):
158-
(cjars, rjars) = _collect_comp_run_jars(ctx)
159-
_write_manifest(ctx)
160-
_compile(ctx, cjars, False)
154+
ijar = None
155+
if use_ijar:
156+
ijar = ctx.outputs.ijar
157+
else:
158+
# macro code needs to be available at compile-time, so set ijar == jar
159+
ijar = ctx.outputs.jar
161160

162-
rjars += [ctx.outputs.jar]
163-
# macro code needs to be available at compiletime, so set ijar == jar
164-
scalaattr = struct(outputs = struct(ijar=ctx.outputs.jar, class_jar=ctx.outputs.jar),
165-
transitive_runtime_deps = rjars)
161+
texp = _collect_jars(ctx.attr.exports)
162+
scalaattr = struct(outputs = struct(ijar=ijar, class_jar=ctx.outputs.jar),
163+
transitive_runtime_deps = rjars,
164+
transitive_compile_exports = texp.compiletime,
165+
transitive_runtime_exports = texp.runtime
166+
)
166167
runfiles = ctx.runfiles(
167168
files = list(rjars),
168169
collect_data = True)
169170
return struct(
170171
scala = scalaattr,
171172
runfiles=runfiles)
172173

174+
def _scala_library_impl(ctx):
175+
return _lib(ctx, True)
176+
177+
def _scala_macro_library_impl(ctx):
178+
return _lib(ctx, False) # don't build the ijar for macros
179+
173180
# Common code shared by all scala binary implementations.
174181
def _scala_binary_common(ctx, cjars, rjars):
175182
_write_manifest(ctx)
176-
_compile(ctx, cjars, False)
183+
_compile(ctx, cjars, False) # no need to build an ijar for an executable
177184

178185
runfiles = ctx.runfiles(
179186
files = list(rjars) + [ctx.outputs.executable] + [ctx.file._java] + ctx.files._jdk,
@@ -183,16 +190,20 @@ def _scala_binary_common(ctx, cjars, rjars):
183190
runfiles=runfiles)
184191

185192
def _scala_binary_impl(ctx):
186-
(cjars, rjars) = _collect_comp_run_jars(ctx)
193+
jars = _collect_jars(ctx.attr.deps)
194+
(cjars, rjars) = (jars.compiletime, jars.runtime)
187195
cjars += [ctx.file._scalareflect]
188196
rjars += [ctx.outputs.jar, ctx.file._scalalib, ctx.file._scalareflect]
197+
rjars += _collect_jars(ctx.attr.runtime_deps).runtime
189198
_write_launcher(ctx, rjars)
190199
return _scala_binary_common(ctx, cjars, rjars)
191200

192201
def _scala_test_impl(ctx):
193-
(cjars, rjars) = _collect_comp_run_jars(ctx)
202+
jars = _collect_jars(ctx.attr.deps)
203+
(cjars, rjars) = (jars.compiletime, jars.runtime)
194204
cjars += [ctx.file._scalareflect, ctx.file._scalatest, ctx.file._scalaxml]
195205
rjars += [ctx.outputs.jar, ctx.file._scalalib, ctx.file._scalareflect, ctx.file._scalatest, ctx.file._scalaxml]
206+
rjars += _collect_jars(ctx.attr.runtime_deps).runtime
196207
_write_test_launcher(ctx, rjars)
197208
return _scala_binary_common(ctx, cjars, rjars)
198209

@@ -213,6 +224,7 @@ _common_attrs = {
213224
allow_files=_scala_filetype,
214225
non_empty=True),
215226
"deps": attr.label_list(),
227+
"runtime_deps": attr.label_list(),
216228
"data": attr.label_list(allow_files=True, cfg=DATA_CFG),
217229
"resources": attr.label_list(allow_files=True),
218230
"scalacopts":attr.string_list(),
@@ -223,6 +235,7 @@ scala_library = rule(
223235
implementation=_scala_library_impl,
224236
attrs={
225237
"main_class": attr.string(),
238+
"exports": attr.label_list(allow_files=False),
226239
} + _implicit_deps + _common_attrs,
227240
outputs={
228241
"jar": "%{name}_deploy.jar",
@@ -235,6 +248,7 @@ scala_macro_library = rule(
235248
implementation=_scala_macro_library_impl,
236249
attrs={
237250
"main_class": attr.string(),
251+
"exports": attr.label_list(allow_files=False),
238252
"_scala-reflect": attr.label(default=Label("@scala//:lib/scala-reflect.jar"), single_file=True, allow_files=True),
239253
} + _implicit_deps + _common_attrs,
240254
outputs={

test/BUILD

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ scala_test(
4949
scala_library(
5050
name = "OtherLib",
5151
srcs = ["OtherLib.scala"],
52+
exports = ["Exported"], # test of exported target
53+
)
54+
55+
scala_library(
56+
name = "Exported",
57+
srcs = ["Exported.scala"],
58+
runtime_deps = ["Runtime"],
59+
)
60+
61+
scala_library(
62+
name = "Runtime",
63+
srcs = ["Runtime.scala"],
5264
)
5365

5466
java_library(

test/Exported.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package scala.test
2+
3+
object Exported {
4+
def message: String = {
5+
// terrible, don't do this in real code:
6+
val msg = Class.forName("scala.test.Runtime")
7+
.newInstance
8+
.toString
9+
"you all, everybody. " + msg
10+
}
11+
}

test/HelloLib.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ object HelloLib {
1818
def printMessage(arg: String) {
1919
println(getOtherLibMessage(arg))
2020
println(getOtherJavaLibMessage(arg))
21+
println(Exported.message)
2122
}
2223

2324
def getOtherLibMessage(arg: String) : String = {

test/OtherLib.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ package scala.test
1717
// It is just to show how a Scala library can depend on another Scala library.
1818
object OtherLib {
1919
def getMessage(): String = {
20-
return "scala!"
20+
// This won't compile because Exported is exported, not a dep:
21+
// Exported.message
22+
"scala!"
2123
}
2224
}

test/Runtime.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package scala.test
2+
3+
class Runtime {
4+
override def toString = "I am Runtime"
5+
}

0 commit comments

Comments
 (0)