読者です 読者をやめる 読者になる 読者になる

sbtで環境依存ファイルを変更するためのカスタムタスクを書いてみたよ

Scala使うならsbt. これはもはや基本装備だと思うのですが、ちょっと困ってることがあります。
というのも、mavenのprofileに相当する機能が分かんなくて(というか無い?)環境依存の設定を開発環境と本番環境で手動で切り替える事にorz

 

というわけで激しく車輪の再発明を疑いつつも、やり方が分からない & 勉強のために、シンプルなものをカスタムタスクで書いてみました。というか、カスタムタスクの情報がかなり少ない気がするんだけど、rakeとかに比べてみんなあまり書かない感じかな?

 

基本情報は下記の辺りを読むと良さそう。でも自分もあまり理解してない...

まずは、今回作りたいような副作用を伴うコマンド的なものを作るにはTaskKeyを使えば良いみたい。

import sbt._
import sbt.Keys._
import sbt.IO._

object ProjectBuild extends Build {
  // タスク名と説明を定義.
  val prod = TaskKey[Unit]("env-production", "change prodction enviroment.")
  val dev = TaskKey[Unit]("env-development", "change development enviroment.")

  // 実際の振る舞いを定義.
  val envProdTask = prod := changeEnviroment("prod")
  val envDevTask  = dev  := changeEnviroment("dev")

  def changeEnviroment(env:String) = { 
    val dir = "src/main/webapp/WEB-INF/"
    val file = "web.xml"
    println("change enviroment:" + env)
    copyFile(new File(dir + file + "." + env), new File(dir + file))
  }

  lazy val root = Project( id = "", base = file("."),
  // タスクを登録.
  settings = Defaults.defaultSettings ++ Seq(envProdTask, envDevTask)).dependsOn()
}

こんな感じで書けます。機能としてはweb.xml.prodとweb.xml.devと二つの環境のファイルを用意しておいて、必要に応じてweb.xmlにコピーするだけの簡単仕様。

振る舞いはscalaのコードなので関数だろうが何でも好きなの書けていいですね。XMLでロジック書く必要が無いのが素晴らしいw ちなみに良く使いそうなメソッドはsbt.IOに定義されてるようです。

 

ただ、この書き方だと環境毎にタスクが増えるのでコマンドライン引数を受け取ってそれで制御すりゃ良くね? ということで引数を受けとれるようにInputKeyを利用したバージョンに書き直しました。

import sbt._
import sbt.Keys._
import sbt.IO._

object ProjectBuild extends Build {
  // タスク名と説明を定義.
  val enviroment = InputKey[Unit]("enviroment", "chagen enviroment seting.")

  // 実際の振る舞いを定義.
  val enviromentTask = enviroment << inputTask { (argTask: TaskKey[Seq[String]]) =>
    argTask map { (args: Seq[String]) =>
      changeEnviroment(args.first)
    }
  }

  def changeEnviroment(env:String) = {
    val dir = "src/main/webapp/WEB-INF/"
    val file = "web.xml"
    println("change enviroment:" + env)
    copyFile(new File(dir + file + "." + env), new File(dir + file))
  }

  lazy val root = Project( id = "", base = file("."),
  // タスクを登録.
  settings = Defaults.defaultSettings ++ Seq(enviromentTask)).dependsOn()
}

基本的にはほぼ一緒だけど、振る舞いの定義のところが少し変わってます。ちなみに今回は標準のパーサーを使いましたがパーサーも独自に定義することが出きるようなので、柔軟な設定ができるはずです。

この辺のKeysとSettingsがsbtの根幹らしいけど、ちょっとまだ理解しきれてない感じ。simple build toolと言う割にはこの辺が複雑な気がするw

 

あとプラグインに落としたりとか、build.sbtだかどこかに、環境依存ファイルの一覧を定義できるようにとかした方がいいと思うけど、今回はここまで。毎回設定ファイルの改修が無くなっただけでも大分楽ちん! ちなみに作ったファイルはgistに置いておきました。もし、欲しい人が居ればどうぞ。

 

では、Happy Hacking !