Sublime Text 2にensimeを入れてみたものの、完璧ではない。
Sublime Text 2でensimeを利用してみたが…
It seems that the validation is not yet perfect, so I would still use this in combination with an SBT buildこんな記述もあるわけだし、完璧ではないのは承知の上。 エラーがあるのか無いのかよくわからないこともあり、sbtも導入しておくのがよさそう。
Sublime Text 2にensimeを入れてみたものの、完璧ではない。
It seems that the validation is not yet perfect, so I would still use this in combination with an SBT buildこんな記述もあるわけだし、完璧ではないのは承知の上。 エラーがあるのか無いのかよくわからないこともあり、sbtも導入しておくのがよさそう。
また海藻猫が騒いでいたのを見つけてしまったのが運の尽き…。 [blackbirdpie id="126122863177633793"]
Android開発はやったことがないからわからないけど、Scala + Eclipseは使っているから、簡単に手順を書いておく。
■ teaplanet/sbt-appengine-eclipse - GitHub
https://github.com/teaplanet/sbt-appengine-eclipse以下の手順で作るふたつのbuild.sbtはGitHubに置いといた。こんな役割分担で使ってます。 依存関係解決:sbt エディタ:Eclipse コンパイル:Eclipse(, sbt) テスト:Eclipse(, sbt) デバッグ:Eclipse こんな構成で行います。
ライブラリの依存関係の解決はsbtに任せる。ついでにEclipseのプロジェクトファイル(.project)の生成もsbtに任せる。 Eclipseはエディタとして使いつつ、同時にコンパイルも行う。 テストしつつデバッグもEclipseで行ってる。 基本的にどちらを使ってもいいんだけど、個人的にはこうやって使ってる。
依存関係の解決をsbtがやってくれるもんだから、バージョン管理にバイナリが含まれなくて精神衛生上よい。 プロジェクトをコンパイルするにもコンソールだけで済むし(Eclipse不要)。
java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"
chmod u+x ~/bin/sbt
mkdir -p ws/sample cd ws/sample sbt
name := "sample"
version := "1.0.0"
scalaVersion := "2.9.1"
organization := "teaplanet"
// for Specs2
resolvers ++= Seq("snapshots" at "http://scala-tools.org/repo-snapshots",
"releases" at "http://scala-tools.org/repo-releases",
"releases" at "http://repo1.maven.org/maven2/")
libraryDependencies ++= {
val appEngineVersion = "1.5.5"
Seq(
"com.google.appengine" % "appengine-api-1.0-sdk" % appEngineVersion,
"com.google.appengine" % "appengine-api-stubs" % appEngineVersion % "test",
"com.google.appengine" % "appengine-api-labs" % appEngineVersion % "test",
"com.google.appengine" % "appengine-testing" % appEngineVersion % "test",
"com.google.appengine" % "appengine-tools-sdk" % appEngineVersion % "test",
"org.specs2" %% "specs2" % "1.6.1" % "test",
"org.specs2" %% "specs2-scalaz-core" % "6.0.1" % "test",
"junit" % "junit" % "4.9" % "test"
)
}
build.sbtの空行は大切。
このファイルを作成したら、sbtプロンプトへ戻る。
reload
update
reloadコマンドで更新したファイルを読み込み、updateコマンドで設定を反映する。 初回は依存ライブラリをダウンロードするから時間がかかると思うので、しばし待つ。 ここまででAppEngineの開発が出来る状態になってる。
resolvers += Classpaths.typesafeResolver
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse" % "1.4.0-RC4")
pluginsディレクトリは無いと思うので作成する。 ここでもbuild.sbtの空行は意味があるので注意。
またsbtプロンプトへ戻ってお約束のコマンド。
reload
update
これでsbteclipseの導入が完了。
ecl[TAB]
と打って "eclipse" と補完されるようであれば正常に導入されてる。
.projectを作るために次のコマンドを実行する。
eclipse create-src
Eclipseの設定でbuildmanagerとしてsbtを使う設定ができる。
■ Using the Google Plugin for Eclipse - Google App Engine - Google Code
http://code.google.com/intl/en/appengine/docs/java/tools/eclipse.html#Installing_the_Google_Plugin_for_Eclipseライブラリの依存関係はsbtで解決してるので、こっちは必要ないかも。入れてあるけど。この環境はsbtとEclipseの共存なので、好きな方を使えばいい。 テストの実行をリアルタイムに行いたいなら、sbtで "~test" しておけばいいし、トレース実行したいならいつものようにEclipseを使えばいい。
NullPointerException: No API environment is registered for this thread. (DatastoreApiHelper.java:118)これはAppEngine絡みの問題なので、AppEngineを使わない人は出会わないだろうし、設定次第で解決できそうな気もする。
今回作ったプロジェクトはEclipseのプラグインを導入してない。導入した所で.projectファイル作成時に設定が上書きされ、消えてしまう。 ■ eed3si9n/sbt-appengine - GitHub
https://github.com/eed3si9n/sbt-appenginesbt-appengineプラグインもあるんだけど、うまく動かなかったので放置してる。いつの間にかsbtのバージョンが0.9.1とかなっていたので、慌てて持ってきました! 昨日、sbtバージョンアップしないなぁ…とか思ってたら、ここにあった。 ■ harrah/xsbt - GitHub
https://github.com/harrah/xsbtsbtはbrewでインストールしてたからbrew管理したかったけど、今の状況を見るとbrew管理しない方がよさそう。
unzip harrah-xsbt-v0.9.1-0-g3ce0e5d.zip
cd harrah-xsbt-3ce0e5d vi project/build/XSbt.scala
lazy val precompiled29 = precompiledSub("2.9.0-SNAPSHOT")この一行をコメントアウトする。でも、今はこんなことしなくても大丈夫っぽい。
それと、compileをやったらPermGenがOutOfMemoryになった。 そんなわけで、sbtの実行スクリプトは下記にように修正。
java -Xmx1024M -XX:MaxPermSize=512m -jar /usr/local/Cellar/sbt/0.7.4/libexec/sbt-launch-0.7.4.jar "$@"
-Xmx1024M -XX:MaxPermSize=512mを追加して、潤沢にしました。
実際はこれだけあれば足りるらしい。 以下のビルドもこの助言のとおりにやっただけだったりする🙄
sbt set build.scala.versions 2.8.1 reload update proguard publish-local
こちらも実現できたので、方法を紹介。 やったことはこれだけ。
環境を簡単に書いておくと、IDEA+sbt(appengine plugin)という組み合わせ。 MyProjectがあって、その下にSubProject1, SubProject2がある構成。このMyProjectとかSubProjectはsbtの管理下にある状態。
まずはJRebelの導入から。 JRebel自体は有償だけど、Scala開発者にはライセンスを提供してくれてるのでこれを活用。 ■ ZeroTurnaround
http://sales.zeroturnaround.com/jrebel.jarを適当な場所に置く。ライセンスファイルもjarと同じところに置いておく。<?xml version="1.0"?>
<application>
<classpath>
<dir name="/Users/ken/workspace/MyProject/SubProject1/target/scala_2.8.1/classes" />
<dir name="/Users/ken/workspace/MyProject/SubProject2/target/scala_2.8.1/classes" />
</classpath>
</application>
続いて、sbtでJRebelを有効にする。 sbt-appengine-pluginではJREBEL_JAR_PATHを指定するだけでJRebelが有効になるはずなんだけど、IDEAのsbtコンソールでは環境変数を見てくれないらしいので、直接指定します。 MyProject/project/build/Project.scala
override def scanDirectories = Nil
override val devAppserverJvmOptions = List("-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=2011,suspend=y") ++ List("-noverify", "-javaagent:/Users/ken/dev/jrebel/jrebel.jar") ++ super.devAppserverJvmOptions
JRebelに関係するのは
List("-noverify", "-javaagent:/Users/ken/dev/jrebel/jrebel.jar")
の部分。 ちなみに、XdebugとかはIDEAで接続するための記述。
そこで、SubProjectの出力先をwebapp配下にします。具体的には、MyProject/SubProject1/target/scala_2.8.1/classesを削除して、MyProject/target/scala_2.8.1/webapp/WEB-INF/classesにシンボリックリンクを張る。 手動でシンボリックリンクを張ればいいんだけど、sbt cleanを実行すると再度やり直さなければならないので、symlinkアクションを作った。 MyProject/project/build/Project.scala
import java.lang.Runtime
lazy val symlinkInstance = new Symlink
lazy val symlink = symlinkAction
def symlinkAction = task { args => task { symlinkInstance(args); None } }
class Symlink() extends Runnable {
def run() = {
def exec(command:String) = Runtime.getRuntime.exec(command)
val subs = Set("SubProject1", "SubProject2")
val webapp = outputPath / "webapp" / "WEB-INF" / "classes"
log.info("Creating directory %s".format(webapp))
exec("mkdir -p %s".format(webapp))
subs.foreach { sub =>
log.info("Processing for %s.".format(sub))
val path = "%s/%s".format(sub, mainCompilePath)
val command = "ln -s ../../../%s %s".format(webapp, path)
log.info("Removing directory %s".format(path))
exec("rm -fr %s".format(path))
log.info("Creating symlink %s -> %s".format(path, webapp))
exec(command)
log.info("")
}
None
}
def apply(args:Seq[String]):Option[String] = {
new Thread(this).start()
None
}
}
コード中の val subs = Set("SubProject1", "SubProject2")"
を各自の環境に合わせてください。
sbt reload後に "symlink" アクションが追加されているので、実行してパスを確認して下さい。サブプロジェクトのclassesがシンボリックリンクになっているはず。
簡単に仕組みを説明。 symlinkを実行することによって、appserverの管理下にクラスファイルを出力するようになります。単にシンボリックリンクを張るだけなので初回だけ。sbt cleanしたときは再度実行します。 続いて、IDEAでデバッガを起動し、sbtの "~ compile" を実行してソースファイルを監視します。ソースコードに修正があった場合はsbtの監視によってコンパイルされ、クラスファイルが生成されます(生成先はsbt symlinkによってappsever管理下になっている)。 ブラウザからアクセスすると、JRebelによってクラスの再読込が行われ、sbtには "JRebel: Reloading class 'package.path.to.ClassName'." と出力されます。
これで、IDEAからsbtを起動し、ブラウザで確認しつつコードを書いていくことが出来る♪
IDEAとsbtを連携してデバッグしたいと思っていたところ@pomu0325さんが解決してくれた。 だけど、appengineの起動はsbtコンソールにdev-appserver-startと入力しなくてはならず、面倒。 どうせなら、IDEAのデバッグ時に一緒にappserverも起動して欲しい。 これを解決する方法がわかったので、紹介。
MyProject/project/build/Project.scalaに以下を追記。
override def devAppserverStartTask(args:Seq[String]) = task {
val status = devAppserverInstance(args)
Thread.sleep(5000)
status
}
これによってdev-appserver-startを実行後、5秒(5000ms)スリープする。 この5秒というのは各自のマシンスペックと相談して、適当に変更してください。
あとはsbtでreload後、IDEAでデバッグするだけ。 appserver起動後にIDEAがコネクションを張りに行くはずです。
余談だけど、こんな記述をしておくとちょっと楽できるかも?
lazy val start = devAppserverStartAction
lazy val stop = devAppserverStop
sbtでdev-appserver-startのエイリアスとしてstartを定義してます。同様にstopも。 appserverの起動はIDEAから可能だけど、sbtのappserverを停止するときにstopが役立つかも?
IDEAとsbtで開発するための手順。 今回はsbtでベースを作り、IDEAで開発環境を構築する方法。
■ An integrated SBT + IDEA Scala Development Setup ← Decodified
http://www.decodified.com/scala/2010/10/12/an-integrated-sbt-and-idea-scala-dev-setup.html参考にしたサイトはこちら。使ったことはないが、sbtの定義ファイルからIDEAのプロジェクトファイルを作ってくれるsbt pluginもあるらしい。 ■ sbt-idea 0.1.0 - implicit.ly
http://implicit.ly/sbt-idea-010以下が本題の手順。
最終的にはIDEAのプロジェクトとして "Hello" を、モジュールとして "World" が出来ることになる。
MacBookにはhomebrewの環境を作ってあるので、うちはこれでインストール完了。
brew install sbt
実際に利用するjarファイルはここにインストールされる。 /usr/local/Cellar/sbt/0.7.4/libexec/sbt-launch-0.7.4.jar
$ cd $WORKSPACE $ mkdir hello $ cd hello $ sbt[object Object]
続いて、Worldモジュールを作成。
$ mkdir world $ cd world/ $ sbt Project does not exist, create new project? (y/N/s) y Name: World Organization: Hello Version [1.0]: Scala version [2.7.7]: 2.8.1 sbt version [0.7.4]:
exitでsbtから抜ける。
これで、sbtを利用したHelloプロジェクトとWorldモジュールの作成を完了。
プロジェクトの作成は完了したので、続いてモジュールの作成に移る。
実際に活用するのはこれからなので、使いやすさなどは不明。 IntelliJ IDEAとsbtは使いやすいという噂を聞いたことはあるが、実際に設定手順をまとめてるサイトがなかったので、簡単に書いてみた。