TFSでJavaのビルド環境構築 in 2012 その5:ビルド処理(javacタスク)

まず、ビルドタスクの順番はこうしました。

019

ソースのビルド(build)→テストソースのビルド(testbuild)→CheckStyle(checkstyle)→カバレッジ取得準備(instrument-cobertura)→JUnitテスト実行&レポート作成(junitreport)→カバレッジレポート作成(coverage)→FindBugs実行(findbugs)→エラー有無確認(checkmetrics)→Jarファイル作成(make-jar)

さて、最初のソースのビルド(build)/テストソースのビルド(testbuild)についてです。

020

元々のソースに対する出力先は「bin」配下に、テストソースに対する出力先は「testbin」に分離し、最後にJarファイルを作成する際の対象を「bin」配下のファイルだけにすればいいようにしています。

次はCheckStyle部分についてです。

TFSでJavaのビルド環境構築 in 2012 その4:パス定義

build.xmlの先頭には各種パスの定義を記載していますが、まずは全体のフォルダ構成はこうしました。

016

今回、TFSのプロジェクト名を「JavaProject1」、ビルド定義名を「CI-Build」にしています。

TFSのビルドを実行すると、「Sources」配下にソースファイルなどが展開され、ワークフォルダを作成しつつビルドが進み、最終生成物を「Binaries」配下に格納するという感じになります。

build.xmlのフォルダパス定義部分です。

017

「~_HOME」はOS環境変数を参照しています。
「${BinariesRoot}」は、TFS上での最終生成物の格納先フォルダを示す定義で、TFSのビルドサービスから実行されると自動的に設定されます。(今回は「\Build\1\JavaProject1\CI-Build\Binaries」がセットされます)

基本的には、HOMEの設定と、生成物の出力先の設定を行ってます。
1つ、「cobertura.instrument.dir」についてですが、カバレッジ取得のため、coberturaがオリジナルソースに独自の処理を追加するのですが、追加されたクラスファイルを格納するためのフォルダを指定しています。

次は各処理で指定するクラスパス定義とFindBugs/CheckStyle/Coberturaタスクを使うためのtaskdefです。

018

基本的には、各処理のマニュアルに記載されている通りです。

次はソースのビルド処理(javacタスク)部分についてです。

TFSでJavaのビルド環境構築 in 2012 その3:build.xml

とりあえず、今回使ったbuild.xmlをそのまま置いておきます。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project basedir="." default="buildall" name="JavaProject1">
    <property environment="env"/>
    <property name="debuglevel" value="source,lines,vars"/>
    <property name="target" value="1.6"/>
    <property name="source" value="1.6"/>
    <property name="jar.output.dir" value="${BinariesRoot}/Jar"/>
    <property name="junit.home" value="${env.JUNIT_HOME}"/>
    <property name="junit.result.dir" value="${BinariesRoot}/JUnitResult"/>
    <property name="findbugs.home" value="${env.FINDBUGS_HOME}"/>
    <property name="findbugs.result.dir" value="${BinariesRoot}/FindBugsResult"/>
    <property name="checkstyle.home" value="${env.CHECKSTYLE_HOME}"/>
    <property name="checkstyle.result.dir" value="${BinariesRoot}/CheckStyleResult"/>
    <property name="cobertura.home" value="${env.COBERTURA_HOME}"/>
    <property name="cobertura.instrument.dir" value="instrument-cobertura"/>
    <property name="cobertura.result.dir" value="${BinariesRoot}/CoberturaResult"/>
    <property name="log4j.home" value="${env.LOG4J_HOME}"/>

    <path id="JavaProject1.classpath">
        <pathelement location="bin"/>
        <pathelement location="testbin"/>
    </path>
<path id="cobertura.classpath">
     <fileset dir="${cobertura.home}">
         <include name="cobertura.jar" />
         <include name="lib/**/*.jar" />
     </fileset>
</path>
    <path id="junit.classpath">
        <pathelement location="${cobertura.instrument.dir}"/>
     <path refid="JavaProject1.classpath"/>
        <pathelement location="testbin"/>
        <pathelement location="${junit.home}/junit.jar"/>
     <path refid="cobertura.classpath"/>
    </path>

<taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
   classpath="${findbugs.home}/lib/findbugs-ant.jar"/>
<taskdef resource="checkstyletask.properties"
   classpath="${checkstyle.home}/checkstyle-5.6-all.jar"/>
<taskdef classpathref="cobertura.classpath" resource="tasks.properties"/>

<target name="init">
        <mkdir dir="bin"/>
        <copy includeemptydirs="false" todir="bin">
            <fileset dir="src">
                <exclude name="**/*.launch"/>
                <exclude name="**/*.java"/>
            </fileset>
        </copy>
    </target>

<target name="buildall"
  depends="build,testbuild,checkstyle,instrument-cobertura,junitreport,
     coverage,findbugs,checkmetrics,make-jar"/>

<target name="build" depends="init">
        <echo message="${ant.project.name}: ${ant.file}"/>
        <javac debug="true" debuglevel="${debuglevel}" destdir="bin"
          includeantruntime="false" source="${source}"
          target="${target}" encoding="utf-8">
            <src path="src"/>
            <classpath refid="JavaProject1.classpath"/>
        </javac>
    </target>

<target name="testbuild" depends="build">
        <mkdir dir="testbin"/>
        <javac debug="true" debuglevel="${debuglevel}" destdir="testbin"
          includeantruntime="false" source="${source}"
          target="${target}" encoding="utf-8">
            <src path="testsrc"/>
            <classpath refid="JavaProject1.classpath"/>
         <classpath path="${junit.home}/junit.jar"/>
        </javac>
    </target>

<target name="make-jar" depends="build">
  <mkdir dir="${jar.output.dir}"/>
  <jar jarfile="${jar.output.dir}/JavaProject1.jar" basedir="bin">
   <manifest>
    <attribute name="Build-Id" value="${BuildNumber}" />
   </manifest>
  </jar>
</target>

    <target name="junit" depends="build">
        <mkdir dir="${junit.result.dir}"/>
        <junit fork="yes" printsummary="withOutAndErr" haltonfailure="off"
    showoutput="true" failureproperty="junit.failed">
         <sysproperty key="net.sourceforge.cobertura.datafile" file="cobertura.ser"/>
            <classpath refid="junit.classpath"/>
            <formatter type="xml"/>
         <batchtest todir="${junit.result.dir}">
          <fileset dir="testsrc">
           <include name="**/*Test.java" />
          </fileset>
         </batchtest>
        </junit>
    </target>

    <target name="junitreport" depends="junit">
        <junitreport todir="${junit.result.dir}">
            <fileset dir="${junit.result.dir}">
                <include name="TEST-*.xml"/>
            </fileset>
            <report format="frames" todir="${junit.result.dir}"/>
        </junitreport>
    </target>

    <target name="findbugs" depends="build">
        <mkdir dir="${findbugs.result.dir}"/>
  <findbugs home="${findbugs.home}"
            output="html"
            outputFile="${findbugs.result.dir}/FindBugsResult.html"
      errorProperty="findbugs.error.failed"
      warningsProperty="findbugs.warnings.failed">
   <sourcePath path="src" />
   <class location="bin" />
  </findbugs>
    </target>

<target name="checkstyle">
        <mkdir dir="${checkstyle.result.dir}"/>
  <checkstyle config="${checkstyle.home}/sun_checks.xml" failOnViolation="false"
     failureProperty="checkstyle.failed">
   <fileset dir="src" includes="**/*.java" />
   <formatter type="xml" tofile="CheckStyleResult.xml" />
  </checkstyle>
  <style in="CheckStyleResult.xml"
    out="${checkstyle.result.dir}/CheckStyleResult.html"
    style="${checkstyle.home}/contrib/checkstyle-simple.xsl" />
</target>

<target name="instrument-cobertura">
        <mkdir dir="${cobertura.instrument.dir}"/>
  <cobertura-instrument todir="${cobertura.instrument.dir}">
      <ignore regex="org.apache.log4j.*" />
   <fileset dir="bin">
    <include name="**/*.class"/>
   </fileset>
  </cobertura-instrument>
</target>

<target name="coverage">
  <cobertura-report srcdir="src" destdir="${cobertura.result.dir}"/>
  <cobertura-report srcdir="src" destdir="${cobertura.result.dir}" format="xml"/>
</target>

<target name="checkmetrics">
  <echoproperties>
   <propertyset>
    <propertyref regex=".*.failed"/>
   </propertyset>
  </echoproperties>

  <condition property="metrics.failed"><or>
   <isset property="junit.failed"/>
   <isset property="findbugs.error.failed"/>
   <isset property="findbugs.warnings.failed"/>
   <isset property="checkstyle.failed"/>
  </or></condition>

  <fail if="metrics.failed" message="各種チェックでエラーがありました。さぁ、とっとと片づけましょう!"/>
</target>
</project>

通常(?)、各種ワークフォルダを削除する処理が入りますが、TFSからビルド実行した場合、TFS側で基底フォルダを削除後新規作成しますので、build.xml内では不要です。

「オンラインストレージ使え」とか聞こえそうですが、会社からは使えないんです^ ^;

個別の内容説明について次から書きます。

TFSでJavaのビルド環境構築 in 2012 その2:サーバ環境とプロジェクト構成

サーバ環境ですが、
 Windows Server 2012
 Team Foundation Server 2012
 Microsoft Visual Studio Team Foundation Server 2012 Build Extensions(11.0.50912.0)
 JDK 1.7
 Ant 1.8.4
 JUnit 4.1
 Cobertura 1.9.4.1
 FindBugs 2.0.1
 CheckStyle 5.6
 Log4j 1.2.17
を使いました。
基本的にはインストール後、環境変数にそれぞれのHOME(JAVA_HOME、JUNIT_HOMEなど)を定義するのですが、1つだけ追加で設定するものがあります。
追加設定するのは、「LANG=jaJP.UTF-8」です。
これを設定しないと、FindBugsのレポート結果に出力される日本語がちゃんと表示されません。(というか、作成されるレポートがShift-JISで作成され、ヘッダ部にエンコード情報が記載されていないので文字化けとなります)

プロジェクト構成はこんな感じです。
Eclipseから

014

そもそものソースは「src」配下に、JUnitテスト用ソースは「testsrc」配下に入れてみました。

TFSのソース管理エクスプローラーから

015

「.tfignore」は何?というかたは、こちらをどうぞ。
MS長沢さんのブログ
Team Foundation Service Preview で Java / JUnit の継続的インテグレーションを行う
(この機能、密かに欲しかったんですw)

次は、build.xmlの内容についてです。

TFSでJavaのビルド環境構築 in 2012 その1:概要

TFS2010での環境構築は以前に書きましたが、改めてTFS2012で構築してみました。
ただ、同じものを再構築しても面白くないので、
 ・FindBugsだけじゃなく、JUnitもCobertura(JCoverage)もCheckStyleも動かす
 ・チェック実行は全部行うが、どれかでエラーが検知されたらビルドをエラーとする
  「JUnitでエラー検知したら、FindBugsのチェックが実行されない」なんてことはありません。
 ・ビルド結果からの直接リンクは使わない(TFServiceで使えないので)
という感じで変更しました。

まずはどう動くかですが、
○正常時
 ビルド結果はこんな感じです

001

JUnitの実行結果がちゃんと表示できています。
「格納フォルダーを開く」でビルドで生成したファイルの内容を確認します。

002

JUnit

003

FindBugs

004

Cobertura

005


006

CheckStyleは次(エラー検知時)に載せます。
(ソースが汚くエラー消し込みできなかったので、正常時のスナップショット取るときはCheckStyle通しませんでした<(_ _)>)

○エラー検知時
「ビルドに失敗しました」となってます。

007

JUnitのテストエラーがちゃんと反映されています。
「ログファイルの表示」でビルドログを確認してみます。

008

「checkmetrics」のところに、どれでエラーを検知したかを追加しています。
(今回はCheckStyle、FindBugs、JUnitの全部でエラーを出しています)
エラーを検知したので、最終生成物のJarファイルは作成していません。

009

それぞれのチェック結果も出力できています。
JUnit

010

FindBugs

011

Cobertura

012

CheckStyle

013

これぐらいのことができれば、仕事で環境構築する際にも最低限使える内容かなと思ってます。
次はサーバ環境とプロジェクト構成についてです。

TFSサーバはスペックの良いものを使用しましょう

確認用としてサーバ環境を構築し、チームプロジェクトを作ってみようとしたところ、こんなエラー...。

201

「タイムアウト」...orz
しかも「ロールバックしようとしましたが、成功しませんでした」と出てます><
SharePoint入れたのに、メモリをケチりすぎたみたいです...。

「困った」ということで、まずはログを確認してみます。
サーバの管理コンソールで「TFS構成」のログをのぞいてみます。
(「TFS構成」のレコードをダブルクリックすると開けます)

202

下の方から追っかけていくと、「ロールバック中です。」というログがありました。

203

推定ロールバック完了率100%と言いながら、そのあとの数値が「161 / 169」となっているので、一部ロールバック処理がタイムアウトしたと判断されたようです。

どうしようと思いながら、ログの続きを確認すると、

204

「removing all items.(169 / 169)」とか「Node returned:Success」とか出力されています。
これだと、タイムアウト後も処理をロールバックを継続していたように見えます。

「物は試し」で、もう一度チームプロジェクトの作成を実行したところ、正常に作成できました。

ちょっとびっくりしましたが、結論は「サーバはケチるな!」です。
SQL ServerがDiskを、SharePointがメモリを相当消費しますし、ビルドも同一マシンで行うのであれば、さらにリソースを消費しますので、結構スペックを必要とします。
チームプロジェクトがギリギリ作れたにしても、リソースが足りないとビルド/自動テストが遅くなり、「時間を短縮するために自動化したのに短縮できない」という残念な状況になりますので、サーバはスペックの良いものを使用しましょう。

ちなみに、今回は仮想マシンで構築していたので、
 ・仮想ディスクをHDDからSSDに移動
 ・割り当てメモリを4GBから10GBに変更
したところ、数分で作成が完了しました。最初からこうしておけばよかったorz

開発環境を全てクラウドで! その2

開発環境を全てクラウドで! その1」の続きです。

9.デスクトップクラウド上にTFS(Serverの方です)をインストールします
  これ、結構鬼門でした。
  SkyDrive経由でISOイメージを展開すればいいかなと思ってましたが、ブラウザからは300MB/Windows ストアアプリからは100MBの制約があり、1.2GBぐらいあるTFSのISOイメージはアップロードできませんでした。
  とりあえず、今回はデスクトップアプリのSkyDriveクライアントを使って上げてみました。
  ※「お名前.com デスクトップクラウド for Windowsアプリ」環境にソフトをインストールするのは問題ないとのことでした。

10.ビルドサービスを構築します
   構築方法については、「Team Foundation Serviceのビルド環境をローカルに構築する」を参考にしてみてください。

11.ビルド定義を作成します
  Visual Studioからビルド定義を作成します。
  変更点は、ビルドコントローラーをデスクトップクラウドマシンに変更することと、ビルド出力をソース管理フォルダにすることです。

129

12.ビルドを実行します
   しばらくすると、ビルドが正常に完了するはずです。

136

こんな感じで、開発に関するすべての環境がクラウド上で構築できてしまいました。
今回はWindowsストアアプリで試してみましたが、Javaなどの.NET以外の開発環境も構築できると思います。
凄いことになってます!!

※現時点でTFServiceのBuildサービスがまだPreviewであることと、価格が未定なため、今後この環境が構築できない/有償となる可能性があります。実際に構築される際にはマイクロソフト/GMOインターネット株式会社様に確認してください。

開発環境を全てクラウドで! その1

先日、クラウド上でTeam Foundation Serviceが正式稼働したばっかりですが、開発環境でもう一つクラウド上で稼働しているサービスがあります。
「お名前.com」で有名なGMOインターネット株式会社様から「お名前.com デスクトップクラウド for Windowsアプリ」が提供されています。
これはクラウド上でVisual Studio 2012環境が提供されるサービスで、ローカルにWindows 8がなくてもWindows Storeアプリなどを開発できるとというものです。

・・・となると、「開発環境を全てクラウド上で構築できるのでは?」と思い、ちょっと構築してみました。
※ブログ掲載については、GMOインターネット株式会社様に快諾頂きました。ありがとうございます。

1.TFService上にアカウントを登録しておきます。
2.「お名前.com デスクトップクラウド for Windowsアプリ」に申し込みをします。
  通常、10分から2時間ぐらいでアカウントが作成されるようです。
  今回は14日間無償トライアルで申し込みしました。
3.手続きが完了すると設定完了メールが送付されますので接続してみます。
  RDPクライアント/ブラウザの両方で接続できます。
4.ちゃんと接続できたら、Visual Studio 2012を起動し、Windowsストアアプリプロジェクトを作成します。ビルド/デバッグ実行ができることを確認します。
5.ブラウザでTFServiceに接続し、プロジェクトを作成します。
  ここでは「CloudDevEnv1」を作成します。



138_3




139

6.Visual Studio 2012のチームエクスプローラーから、TFServiceに接続します。
  ローカルサーバとの違いは、続先サーバの名前に「https://~.visualstudio.com」と指定することぐらいです。

111

7.TFService上に作成したチームプロジェクトに、4.で作成したプロジェクトを追加します。

113

8.追加したプロジェクトをチェックインします。



115_2

長くなったので、分割します。

Team Foundation Service 正式稼働!

11/1、ついにTeam Foundation Serviceが正式稼働しました。
(これのおかげで少し寝不足ですw)

URLも「~.tfspreview.com」から「~.visualstudio.com」に変更されていますが、今までtfspreview.comで作成されたプロジェクトはそのままvisualstudio.comに移行されています。

11/1現在では以下の内容で稼働しているみたいです。(英語ちゃんと読めません><)
○今は無償プランのみ。
 ・5ユーザまで
 ・作成プロジェクト数制限なし
 ・バージョン管理
 ・作業項目管理
 ・アジャイルプランニングツール
  プロダクトバックログリストとかタスクボードとか
 ・フィードバック管理
 ・ビルド(Preview)
 追加プランと価格は2013年まで待ってw
○6人以上で使いたいときは?
 →今だけは使える。2013年には有償プランへの移行ガイダンス出すよ
○今は誰でも無償だけど、MSDNサブスクリプション持ってれば、2013年以降も無償で使える。
○接続できるのは?
 Visual Studio 2010/2012
  Visual Studio Express 2012 for Windows 8/Windows Desktop/Windows Phone 8/Web
 Visual Studio Team Explorer Everywhere 2012
 Xcode+Git-TF
 →何でも使えるw
○ビルドは課金される?
 →今はプレビューだから無償。将来的には月単位でビルドした量に応じて課金される。
  (追加料金対応可能)

ほんのちょっとだけですが、さわってみました。
・ブラウザで、タスクボードを使って作業項目を操作。

009

「サンプルプロジェクト作成」という作業項目を"PROPOSED"(提案)から"ACTIVE"にドラッグしている最中です。こんな操作がブラウザでできるのがいいですね。

・Eclipseからアクセス

010

Eclipse(+Team Explorer Everywhere 2012)からもアクセスできてます。

ちょっとだけ引っかかったこと。
・プロジェクトコレクションの"Project Collection Valid Users"(一般ユーザグループ?)にユーザが追加できない
 
 管理者グループである"Project Collection Administrators"にはユーザ追加できるのですが、"Project Collection Valid Users"に対してユーザ追加しようとすると、

006

 と怒られてしまいます。
 (プロジェクト別のユーザ追加は問題ないです)

・1ユーザで1つだけサイトのオーナーになれる。複数のオーナーを兼任することはできない
 
 ユーザAでサイトを作成する→オーナーをユーザAからユーザBに変更する→ユーザBでサインインする→サイト作ってくれと言われるので作ってみる→エラーになる

008

 サイト作成時のエラー。
 オーナー変更がすぐに反映されないっぽいので、それが問題かもしれませんが。

TFServiceはちょくちょく改善されてますので、これからも目が離せません。

コードカバレッジ分析の色分けがちゃんと表示されないとき

VS2012 PremiumかUltimateでのうれしい機能に、「コードカバレッジ分析」があります。
単体テスト機能とも連動させることも可能です。
実行された/実行されなかったブロックが色分けで表示できるため、とっても見やすいです。

実際に試してみようと思い、とある環境からProfessional版をアンインストール後、Ultimate評価版をインストールしました。
確認のためのテストも作成し、実行してみたところ...

001

青枠内が実行されたブロックなのですが、色が変わってません。
(ちゃんと、「コードカバレッジ分析の色分けを表示」ボタンは押してます)
よく見ると、文字が青に変わっているのですが、MSDNとかで見かけるような本来の色ではありません。
MSDNを検索すると、「色変えれるよ」と載っているので、オプション設定で変えてみようとしてみます。

002

...「カバレッジ~」がありません><
半日ほど悩みましたが、結論は「Professionalがインストールされた環境にUltimateをインストールする」と発生するようです。
(最初からUltimateをインストールした環境では発生しませんでした)

あまりこういうインストールを行うことはないと思いますが、なってしまった場合には
1.英語版のLanguage Packをインストール
2.オプション設定で英語環境に切り替え

003

3.英語環境でVSを再起動し、一度コードカバレッジ分析を実行/表示



004_2

4.オプション設定で、日本語環境に戻してVS再起動

これで、ちゃんと(日本語環境で)色分け表示できるようになります。
(OSから入れ直すという手もありますが...)

ちなみに、カバレッジ関連の表示色の変更はこんな項目でした。

005

TFSのビルドでコードカバレッジ分析を実行する設定/環境についてはまた次回に。
(元々はこれが確認したかったのでUltimate入れたんですが...^ ^;)