Scala.jsは、ScalaのコードをJavaScript(JS)に変換するコンパイラです。
公式の紹介ページでは、以下の4点がScala.jsの優れた点であると謳われています。
- 正確性: Scalaの優れた型システムやコンパイル時検査を活用
- 高性能: 実行時間は生JSの高々2倍
- 相互運用性: DOMやJSのライブラリが利用可能
- IDE: IntelliJがScala.jsの記述を完璧にサポート
Scala.jsは、単なるJSの代替記法(altJS)に加え、JSとJava仮想マシン(JVM)とで動作するコードのクロスビルドツールとして利用することができます。
クロスビルドに関する資料として、以下のようなものが挙げられます。
- Scala.js - Cross-building (公式)
- Isomorphic web development with scala and scala.js
- scalaとか・・・ - scalazとscalapropsとmsgpack4zを全部Scala.js対応した
- PABlog - Scala.jsとJVMの両対応コードとScala.jsのテストの書き方
しかし、これらの資料は、十分にクロスビルドを説明しているとは言えません。
クロスビルド初心者は、チュートリアルが存在しないので、試す前に諦めてしまうかもしれません。
私も全然わからず、ソースコードから動作を推測するなどして、やっと動かすことができました。
そこで、コードを書く部分を含めた、Scala.jsクロスコンパイルの簡単なチュートリアルを作成します。
本ページでは、Scala.jsでクロスコンパイルできる環境を構築し、JSとJVM向けにそれぞれ実行可能なものが生成されることを確認します。
環境のセットアップ
事前にSBTをインストールしておいてください。
以下に示すようにsbt new
コマンドを実行すると、自動的にScalajsクロスビルド対応のSBTプロジェクトが生成されます。
$ sbt new umireon/scalajs-cross-seed.g8
Minimum Cross-building Scala.js build.
name [My Something Project]: Scalajs Cross Example⏎
Template applied in ./scalajs-cross-example
自動生成されたプロジェクトの内容は以下の通りです。
scalajs-cross-example/
├── build.sbt
├── js/src/main/scala/example/Hello.scala
├── jvm/src/main/scala/example/Hello.scala
├── project/
│ ├── build.properties
│ └── scalajs.sbt
└── shared/src
├── main/scala/example/Hello.scala
└── test/scala/example/HelloSpec.scala
これで、Scala.jsクロスビルドの準備は完了です。
ここでは、build.sbtの内容に触れませんが、気になる方は公式ドキュメントを確認してください。
ビルドの確認
先ほど生成したプロジェクトにはテストが含まれています。
Scala.jsが正しく動作しているか確認するために、JSとJVM上でビルド及びテストを実行してみます。
プロジェクトディレクトリへ移動し、以下のコマンドを実行してみてください。
$ sbt test
[info] HelloSpec:
[info] The Hello object
[info] - should say hello
[info] Run completed in 660 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[info] Fast optimizing /path/to/scalajs-cross-example/hello-test-fastopt.js
[info] HelloSpec:
[info] The Hello object
[info] - should say hello
[info] Run completed in 606 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 24 s, completed 2017/04/18 14:23:36
出力結果を見ると、All tests passed.
が2回現れていることがわかります。
これは、1番目がJVMにおけるテストの結果、2番目がJSにおけるテストの結果を示すものです。
ビルド後、ディレクトリ構成は以下のように変化します。
scalajs-cross-example/
├── build.sbt
├── js
│ ├── src/main/scala/example/Hello.scala
│ └── target/
├── jvm
│ ├── src/main/scala/example/Hello.scala
│ └── target/
├── project/
│ ├── build.properties
│ └── scalajs.sbt
├── shared/src
│ ├── main/scala/example/Hello.scala
│ └── test/scala/example/HelloSpec.scala
└── target/
ディレクトリ構成
本節では、上記のディレクトリ構成のうち、Scala.jsクロスビルド特有のものについて説明します。
shared
は、JSとJVMで共有するコードを格納するディレクトリです。
ここに格納されたScalaコードは、JVMビルド時とJSビルド時に別々にコンパイルされます。
したがって、クロスビルド実現のためには、JVMとJSの両環境で実行可能なScala.jsコードを書く必要があります。
js
及びjvm
は、各環境にて実行可能なオブジェクトが生成されるディレクトリです。
JS用の生成物はjs/target/
、JVM用の生成物はjvm/target
にそれぞれ出力されます。
また、Hello.scala
のように、片方の環境のみで実行したいコードもここに格納します。
参考のため、各Hello.scala
の内容を以下に示します。
package example
import scala.scalajs.js
object Hello extends Greeting with js.JSApp {
def main(): Unit = {
println(greeting)
}
}
package example
object Hello extends Greeting with App {
println(greeting)
}
生成物の確認
JVMでは、JARファイルを生成し、それが実行可能であることを確認します。
JARファイルはsbt package
コマンドを実行することにより生成され、jvm/target/scala-2.12
以下に得られます。
$ ls scalajs-cross-example/jvm/target/scala-2.12
classes test-classes
hello_2.12-0.1.0-SNAPSHOT.jar
実行結果は、以下のコマンドにより確認できます。
$ sbt helloJVM/run
...
[info] Running example.Hello
hello
[success] Total time: 1 s, completed 2017/04/18 15:11:04
JSでは、JSファイルを生成し、それが実行可能であることを確認します。
JSファイルはsbt fullOptJS
コマンドを実行することにより生成され、js/target/scala-2.12
以下に得られます。
$ ls scalajs-cross-example/jvm/target/scala-2.12
classes hello-test-fastopt.js
hello-jsdeps.js hello-test-fastopt.js.map
hello-jsdeps.min.js hello-test-jsdeps.js
hello-opt.js hello_sjs0.6_2.12-0.1.0-SNAPSHOT.jar
hello-opt.js.map test-classes
実行結果は、以下のコマンドにより確認できます。
$ node scalajs-cross-example/js/target/scala-2.12/hello-opt.js
hello
続き
Scala.jsでクロスコンパイル - 2. 型とコレクション
そのうち書く