*RubyでScalaスクリプトをコンパイル補助するツールを作成する。 [#vd683dab] #contents *仕様 [#df26398e] **ツール1 タイムスタンプスナップ機能 [#ac9ea477] フォルダ内の.scalaファイルのタイプスタンプのスナップを取得する。 **ツール2 更新ファイル抽出機能 [#g2830135] 既存のタイムスタンプと現在のファイルのタイムスタンプを比較し更新されたファイルを取得しコンパイル可能なバッチファイルを生成する *scalacc.batの例 [#n0b705be] -説明 このバッチファイルはSCALAファイルをコンパイルする際、ライブラリを自動的に取り込んでからコンパイルする BASEが2回出てきているが、どちらも自動的にJarファイルを読み込みたいフォルダである。下記の例のように追加したいフォルダの数だけ繰り返し記述するとよいでしょう。 @echo off setlocal ENABLEDELAYEDEXPANSION set CP=. set BASE=C:\scala\lib for %%f in (%BASE%\*) do set CP=!CP!;%%f set BASE=C:\scala\lib\user for %%f in (%BASE%\*) do set CP=!CP!;%%f SET SCALA_DOC_HOME="C:\scala\doc" ECHO %CP% scalac -classpath %CP% %1 *ツール1 タイムスタンプスナップ機能 [#la5cc9ff] **rubyのディレクトリ関連の処理 [#q1973441] ***ディレクトリ中のファイル一覧を取得する [#ic08cdb2] -コード例 p Dir::entries("/home/take") --結果 [".", "..", "WorkbookScala.scala"] ***ワイルドカードにマッチしたファイル全てに処理を行う [#nda0b89a] -コード例 p Dir::entries("/home/take") --結果 [".", "..", "WorkbookScala.scala"] ---注意点 フォルダの区切り文字は\ではなく、/を使うこと ***ファイルのタイムスタンプを取得する [#ud2674ec] -例 s = File.stat("c:/scala/user/src/WorkbookScala.scala") puts File.mtime(fname).strftime("%Y/%m/%d %H:%M:%S") # 最終更新時刻 -結果 c:/scala/user/src/WorkbookScala.scala 2009/12/05 22:16:11 ***snap_timestamp.rb [#hffed54a] path=ARGV[0].gsub("\\","/") ext="scala" outfile="timestamp.txt" buffer = "" # **は再帰的検索。\0は複数パターンの区切り。.*は隠しファイル取得 Dir.glob("#{path}/**/*\0#{path}/**/.*" + ext).each{|name| puts name buffer = buffer + name + "\n" s = File.stat(name) timestamp = File.mtime(fname).strftime("%Y/%m/%d %H:%M:%S") # 最終更新時刻 puts timestamp buffer = buffer + timestamp + "\n" } # タイムスタンプスナップを上書き更新します。 file = File.open(path + "/" + outfile, "w"); # 書き込む file.write(buffer); file.close -使い方 ruby c:\scala\bin\snap_timestamp.rb "c:\scala\user\src" --指定したフォルダにtimestamp.txtが出力されファイルのタイムスタンプのスナップショットがとれます。 **snap_timestamp_diff.rb [#a7a5adf5] 更新したファイルを出力する #第一引数はフォルダ名 path=ARGV[0].gsub("\\","/") ext=/.*\.scala$/ outfile="timestamp.txt" current={} # **は再帰的検索。\0は複数パターンの区切り。.*は隠しファイル取得 Dir.glob("#{path}/**/*\0#{path}/**/.*" ).each{|name| if ext =~ name s = File.stat(name) current.store(name,File.mtime(name).strftime("%Y/%m/%d %H:%M:%S")) end } # タイムスタンプ保存用ファイル読み込み before={} file = open(path+"/"+outfile) buffer = file.read().split("\n"); file.close #2行を1つの配列にまとめた配列を作成 newItem="" newItem = nil buffer.each{|line| if newItem == nil newItem=line else if ext =~ newItem before.store(newItem,line) end newItem = nil end } #タイムスタンプの比較 更新・新規追加したファイルを出力 current.each{|filepath, timestamp| if timestamp != before.fetch(filepath) puts filepath end } -使い方 snap_timestamp_diff.rb "c:\scala\user\src" ***備考 [#w8c99c3c] RubyをWindowsのDOSで動作させるとRubyからバッチファイルを動作させる際に使用する システムのShellが使用している -ruby\lib\ruby\1.8\shell\command-processor.rb のPathの分割処理がバグっているというかcygwinしか想定していないので、 使えません。 動かしてもwindows版rubyにはfork関数が最初から実装されていないっぽい。 つまり複数プロセスの同時実行には対応していない。 *Scala でコマンドの実行結果を文字列として取得 [#m4deff0b] **「java -version」を実行する例 [#ddc5fad5] Runtime r = Runtime.getRuntime(); Process p = r.exec("java -version"); **複数の引数の指定方法 [#dfda1e31] Runtime r = Runtime.getRuntime(); Process p = r.exec(new String[] { "C:\\Program Files\\Java\\j2re1.4.2_13\\bin\\java", "-version" }); **「echo zzz」を実行する例 [#sc8628a2] Runtime r = Runtime.getRuntime(); Process p = r.exec("cmd /c echo zzz"); // Process p = r.exec(new String[]{ "cmd", "/c", "echo", "zzz" }); **環境変数の指定 [#jab4f667] String[] env = new String[2]; env[0] = "TEST=サンプル"; env[1] = "PATH=" + System.getProperty("java.library.path"); Runtime r = Runtime.getRuntime(); Process p = r.exec("cmd /c echo %TEST%", env); ***参考URL 外部プロセス起動 [#qeb4d144] http://www.ne.jp/asahi/hishidama/home/tech/java/process.html#Runtime ***Javaで,子プロセスを使うときの注意点 [#f56f123d] http://isolinear.info/wiki/index.php/Java/Tips/Java%A4%C7%A1%A4%BB%D2%A5%D7%A5%ED%A5%BB%A5%B9%A4%F2%BB%C8%A4%A6%A4%C8%A4%AD%A4%CE%C3%ED%B0%D5%C5%C0.html *外部プロセスを実行し標準出力と標準エラーを取得する。 [#c01df620] **Actorをつかってスレッドプログラムを置き換えてみる [#md939dcd] 上記で紹介した外部プロセス起動のスレッドを使ったコードはこちら import java.io.*; import java.util.*; /** * InputStreamを読み込むスレッド */ public class InputStreamThread extends Thread { private BufferedReader br; private List<String> list = new ArrayList<String>(); /** コンストラクター */ public InputStreamThread(InputStream is) { br = new BufferedReader(new InputStreamReader(is)); } /** コンストラクター */ public InputStreamThread(InputStream is, String charset) { try { br = new BufferedReader(new InputStreamReader(is, charset)); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } @Override public void run() { for (;;) { try { String line = br.readLine(); if (line == null) break; list.add(line); } catch (IOException e) { throw new RuntimeException(e); } } } /** 文字列取得 */ public List<String> getStringList() { return list; } } -ScalaのActorをつかって書き換えてみました。 class InputStreamActor(is :InputStream , charset :String) extends Actor{ var br : BufferedReader = null; var list:ArrayList[String] = new ArrayList() if (charset == "") { br = new BufferedReader(new InputStreamReader(is)); } else { try { br = new BufferedReader(new InputStreamReader(is, charset)); } catch { case e: UnsupportedEncodingException => { throw new RuntimeException(e); } } } def this(is :InputStream) = this(is,""); def act() { var cnt = 0 val maximum = 100 while (cnt < maximum) { cnt = cnt + 1 try { var line = br.readLine(); if (line == null){ exit() } list.add(line); } catch { case e:IOException => throw new RuntimeException(e); } } } }