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

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

SharePoint Foundation 2010はWindows Server 2012ではサポートされない

Windows 8/Windows Server 2012/Visual Studio 2012/Team Foundation Server 2012とRTM版が一通りMSDNで公開されたので、そろそろ環境作ってみようかとしたところ、さっそく引っかかりました^ ^;

「まずはサーバ構築」と思い、Windows Server 2012にTFS2012をインストールし、構成ウィザードで「標準の単一サーバ」を選択。
サービス用アカウントを指定し、次画面でSharePointのインストール可否チェックの終了を待っていると、なんとエラーが。



015_2

「SharePoint Foundation 2010はWindows8にインストールできません。」

ですか...orz
(インストーラー内部では、Windows Server 2012は「Windows8Server」として認識していました)

困ったなと思ってるところ、@kkamegawaさんより素早いフォローを頂きました。(ありがとうございます。<(_ _)>)
原因はタイトル通りですが、「Windows Server 2012 では SharePoint Server 2010 がサポートされない」ということです。(KB2724471)
KB上では、インストールに失敗するとか、インストールできてもエラーになるとかという記載になっていますが、TFSの構成ウィザードからインストールする場合は事前チェックでインストーラーすら実行できないようになっています。
今のところ回避策はないようで、手っ取り早いのはWindows Server 2008 R2にTFS2012環境を構築するぐらいみたいです。
SharePoint Foundation 2012とかが出たときにまた確認してみたいと思います。

AD環境でTFS2012にユーザを登録するとき

ちょっとした小ネタです。

AD環境でTFS2012を構築したあと、ユーザ(グループ)を登録する際に、ちょっと変な動作になるところがあります。
内容は、ユーザ(グループ)を選択する画面にユーザ(グループ)が表示されないので選択できないというものです。

とりあえず、ADに開発者ユーザと開発者用グループを作成します。
・デフォルトで存在するOUの「Users」に開発者ユーザ「Dev01」「Dev02」とグループ「TestDevelopers01」を作成し、TestDevelopers01にDev01とDev02を追加します。

011_2

・次に新規にOU「TestDevelopers」を作成し、その中に開発者ユーザ「Dev11」「Dev12」とグループ「TestDevelopers10」を作成後、TestDevelopers10にDev11とDev12を追加します。

012

ユーザを作成したら、クライアントからチームプロジェクトを作成し、そのチームプロジェクトに対してグループメンバーシップ(ユーザ設定)を設定します。

013

VS2010と違い、グループメンバーシップの設定はWebで設定します。

014

今回は、作成したチームプロジェクト用チームグループ(「TestTFSProject01」)に対してユーザ(グループ)を追加します。
画面真ん中あたりの「追加...」を押下し、「Windowsユーザまたはグループの追加」を選択します。

015

ユーザ(グループ)の追加画面が表示されますので、「参照」を押下します。

016

ユーザ(グループ)の一覧が表示されるのですが...

017

なぜか、最初に登録したユーザ(グループ)が表示されません。
表示されたのは、セットアップ時に作成したデフォルトのAdministratorユーザ(名前を変更したので、イメージ編集してます)と、TFSサービス実行用に作成した「TFSAdmin」だけです。

「選択できないと登録できない!」と思ったのですが、ダメ元で「TestDevelopers01」を直接入力してみます。

018

入力欄の下に「検索結果がありません」と表示されるので、やっぱりダメかと思いましたが、Enterキーを押してみると...

019

ちゃんと登録候補として認識されました^ ^

残りの「TestDevelopers10」も直接入力します。

020

この状態で「変更の保存」を押下すると、ユーザ(グループ)が登録されます。

021

ちなみに、チームプロジェクト作成後、サーバの管理コンソールからはちゃんと選択できます。

022

できれば、サービスパックとかのタイミングで治ってほしいものです。