Scala Support

Examples

The sample code examples/src/scala/org/pantsbuild/example/hello/welcome/ shows how to define a library of Scala code.

Its BUILD file looks like that for a Java library, but contains a scala_library target with .scala sources:

scala_library(
  dependencies=[
    'examples/src/java/org/pantsbuild/example/hello/greet:greet',
    'examples/src/resources/org/pantsbuild/example/hello',
  ],
  provides = scala_artifact(org='org.pantsbuild.example.hello',
                            name='welcome',
                            repo=public,),
)

There's a sample test in examples/tests/scala/org/pantsbuild/example/hello/welcome. It's a junit_tests with .scala sources.

junit_tests(
  dependencies=[
    'examples/src/scala/org/pantsbuild/example/hello/welcome:welcome',
    '3rdparty:junit',
    '3rdparty:scalatest',
  ],
)

Scala/Java Circular Dependencies

Scala code and Java code can depend on each other. As long as the dependencies aren't circular, scala_library targets can depend on java_library targets and vice versa. If the dependencies are circular, you can set up targets to compile all of this code together. Assuming your *.java and *scala files are in separate directories, you can have:

  • a java_library whose sources param is the *.java files; one of its dependencies should be...
  • a scala_library whose sources param is the *.scala files and whose java_sources is the above java_library.

Do not put the java_library in the scala_library's dependencies or Pants will error out in its circular dependencies check. Instead, put the java_library in java_sources to work around this check.

The scala_with_java_sources example shows how this works:

scala_library(
  sources = ['Greet.scala'],
  java_sources=[
    'examples/src/java/org/pantsbuild/example/java_sources',
   ],
)

The referred-to java_sources java_library has this java_library in its dependencies:

java_library(
  dependencies = [
   'examples/src/scala/org/pantsbuild/example/scala_with_java_sources',
  ],
)

(If your circularly-referencing *.scala and *.java files are in the same directory, you don't need separate java_library and scala_library targets. Instead, use scala_library(sources=globs('*.scala', '*.java'),...).)

Scala Version

You can override the default version of the entire Scala toolchain with the single --scala-platform-version option. You can set that option to one of the supported Scala versions (currently "2.10" or "2.11"), or to the special value "custom".

If you choose a custom version, you must use the --scala-platform-runtime-spec, --scala-platform-repl-spec and --scala-platform-suffix-version options to provide information about your custom Scala version. The first two of these default to the target addresses //:scala-library and //:scala-repl respectively, so you can simply define those targets (in the root BUILD.tools file by convention) to point to the relevant JARs.

Scala REPL

To bring up Scala's interactive console, use Pants' repl goal. In the resulting console, you can import any Scala or Java code from the Pants invocation's targets and their dependencies.

$ ./pants repl examples/src/scala/org/pantsbuild/example/hello/welcome
   ...much build output...
15:08:13 00:11   [resources]
15:08:13 00:11     [prepare]
                   Invalidated 1 target containing 1 payload file.
15:08:13 00:11   [repl]
15:08:13 00:11     [python-repl]
15:08:13 00:11     [scala-repl]
15:08:13 00:11       [bootstrap-scala-repl]
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_60).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import org.pantsbuild.example.hello.welcome
import org.pantsbuild.example.hello.welcome

scala> val folks = List("Abel", "Baker", "Charlie", "Delta")
folks: List[java.lang.String] = List(Abel, Baker, Charlie, Delta)

scala> org.pantsbuild.example.hello.welcome.WelcomeEverybody(folks)
res0: Seq[String] = List(Hello, Abel!, Hello, Baker!, Hello, Charlie!, Hello, Delta!)

scala> exit
warning: there were 1 deprecation warnings; re-run with -deprecation for details

               Waiting for background workers to finish.
               SUCCESS

$

Testing

Scala tests are run using the junit_tests BUILD target. Both Junit and ScalaTest tests are supported by default. Most other scala test frameworks support running with JUnit via a base class/trait or via a @RunWith annotation; so you can use junit_tests for your scala tests as well.

Formatting and Linting

scalafmt and scalafix are installed by default in both the fmt and lint goals, but "semantic" scalafix rewrites are disabled by default since most deployments will prefer that the lint goal run quickly. Semantic rewrites introduce a dependency on compilation, and require an additional compiler plugin.

Usage

To run a particular scalafix rule on targets, pass:

./pants fmt.scalafix --rules=ProcedureSyntax ${TARGETS}

Enabling semantic rewrites

Enabling scalafix semantic rewrites involves using a compiler plugin, and passing the --semantic flag to the scalafix task.

In a BUILD file at the root of the repo, define the semanticdb compiler plugin:

SCALA_REV=2.11.11
jar_library(
  name = 'scalac-plugin-dep',
  jars = [jar(org='org.scalameta', name='semanticdb-scalac_{}'.format(SCALA_REV), rev='2.0.1')],
)

Then, reference it from pants.ini to load the plugin, and enable semantic rewrites to require compilation for fmt and lint:

[compile.zinc]
args: [
    # The `-S` prefix here indicates that zinc should pass this option to scalac rather than
    # to javac (`-C` prefix).
    '-S-Yrangepos',
  ]

scalac_plugins: [
    'semanticdb',
  ]

[lint.scalafix]
semantic: True

[fmt.scalafix]
semantic: True
Generated by publish_docs from dist/markdown/html/examples/src/scala/org/pantsbuild/example/README.html 2018-05-03T17:02:31.081270