倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Maven から GitHub Packages にライブラリを公開する、ライブラリを使う

いつの間にか GitHub に「GitHub Packages」という機能がついて、自作のライブラリ等を公開できるようになっていたので、Java のプロジェクト管理ツール Apache Maven からの使い方を試してみました。

Maven から GitHub Packages を使う方法は公式のドキュメントに書かれていて日本語訳もあるので単なる"やってみました"記事ですが、Scala のビルドツール sbt で同様のことをしてみる準備でもあります。

GitHub Packages にライブラリを公開する

Maven から GitHub Packages にライブラリを公開するには、Maven プロジェクト的には POM ファイルの distributionManagement 要素で GitHubリポジトリを指定して「mvn deploy」コマンドを実行するだけですが、Maven から GitHub にアクセスするために GitHub のアクセストークンを取得して setting.xml に少々の設定が必要です。 この設定は Maven のグローバル設定なので、一度行えば別の Maven プロジェクトで同様のことを行う際に再設定の必要はありません。

GitHub のアクセストークンを取得する

GitHub のアクセストークンは GitHub の「New personal access token」ページから新規作成できます*1。 このページで「Note」に適当な説明を入力、「write:packages」にチェックを入れて、下部の「Generate Token」をクリックします:



生成されたトークンをコピーして下記の setting.xml に使用します。 ページ遷移をするとアクセストークンを再表示できなくなるので注意。

setting.xml の設定

アクセストークンを取得したら、それを使って MavenGitHub にアクセスできるように setting.xml を設定します。 setting.xml はユーザホーム・ディレクト*2下の .m2 ディレクトリにあります(なければ作成してください)。 setting.xml の設定は servers 要素下の server 要素で、サーバの id、GitHub のユーザ名、上記のアクセストークン(パスワード)を指定するだけです。

また、必須ではありませんが、ついでに成果物の SNAPSHOT バージョンを有効にする設定もしておきましょう。 これには activeProfiles 要素と profiles 要素で設定を行いますが詳細は下記のサンプルコードを参照(「Configuring Apache Maven for use with GitHub Packages」サイトのサンプルのコピペ)。

これらの設定を行った setting.xml ファイルは以下のようになります。 ただし

GitHub のユーザ名 $GITHUB_USER
アクセストーク $GITHUB_TOKEN

としています:

~/.m2/settings.xml

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <servers>
    <server>
      <id>github</id>
      <username>$GITHUB_USER</username>
      <password>$GITHUB_TOKEN</password>
    </server>
  </servers>

  <activeProfiles>
    <activeProfile>github</activeProfile>
  </activeProfiles>

  <profiles>
    <profile>
      <id>github</id>
      <repositories>
        <repository>
          <id>central</id>
          <url>https://repo1.maven.org/maven2</url>
        </repository>
        <repository>
          <id>github</id>
          <url>https://maven.pkg.github.com/$GITHUB_USER/*</url>
          <snapshots>
            <enabled>true</enabled>
          </snapshots>
        </repository>
      </repositories>
    </profile>
  </profiles>
</settings>

$GITHUB_USER, $GITHUB_TOKEN の値以外はコピペでいいと思います。

pom.xml の設定

setting.xmlMaven のグローバル設定が完了すれば、後はプロジェクトごとの設定を POM ファイル (pom.xml) で行うだけです。 POM ファイルに必要な追加設定は、成果物をアップロードする GitHubリポジトリで、これは distributionManagement 要素下の repository 要素で id, name, url を指定して行います。 id, name 要素の値は適当な文字列でいいですが、url の値は、GitHub アカウントのユーザ名、リポジトリ名を使って

  • https://maven.pkg.github.com/《ユーザ名》/《リポジトリ名》

とします。

これらの設定を施した POM ファイルは以下のようになります。 ただし

プロジェクトのグループ名 org.example
成果物の名前 github-packages-maven-example
成果物のバージョン 0.1-SNAPSHOT
GitHub のユーザ名 $GITHUB_USER
成果物をアップロードする
GitHubリポジトリ
github-packages-maven-example

としています。 成果物の名前は GitHubリポジトリ名と同じでいいでしょう。 自分のコピペの簡易化のため、distributionManagement 要素以外にもあれこれ設定していますが、不必要なら無視してください。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>github-packages-maven-example</artifactId>
    <version>0.1-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <distributionManagement>
        <!-- 成果物をディプロイする GitHub Packages リポジトリを登録 -->
        <repository>
            <id>github</id>
            <name>GitHub $GITHUB_USER Apache Maven Packages</name>
            <url>https://maven.pkg.github.com/$GITHUB_USER/github-packages-maven-example</url>
        </repository>
    </distributionManagement>
</project>

成果物のディプロイ

以上で設定は完了です。 プロダクションコード、テストコードが書けて成果物を GitHub Packages にディプロイするためには、コマンドラインでプロジェクト・ルートに移動し、コマンド

mvn deploy

を実行します。 コンパイル、テスト、パッケージング等が実行された後、指定したリポジトリに成果物がアップロードされれば成功です。 アップロードされた成果物は GitHub リポジトリ・ページの右方で確認できます:


(画像の成果物のバージョンが違ってますが)ちなみに、一度ディプロイした成果物はプライベートリポジトリでないかぎり削除できないので注意。 リポジトリ自体を削除することはできますが。

GitHub Packages に公開されているライブラリを使う

GitHub Packages に公開されているライブラリを使うには POM ファイルにそのライブラリに関する依存性を dependencies 要素に書けばいいのですが、Maven Central に公開されているライブラリの場合と違って(加えて) GitHub Packages を Maven リポジトリとして登録する必要があります。

また、GitHub にアクセスする必要があるので、上記のライブラリを公開する方法の説明で行った「GitHub のアクセストークンを取得する」と「setting.xml の設定」を行う必要があります。 少々異なるのは、ライブラリを使うのに必要なアクセストークンは「read:packages」だけであること*3と、setting.xml の SNAPSHOT バージョンの設定は必要ないので servers 要素の設定だけでいいことです。 これらの設定は「GitHub Packages にライブラリを公開する」で設定していれば再度行う必要はありません。

~/.m2/settings.xml

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <servers>
    <server>
      <id>github</id>
      <username>$GITHUB_USER</username>
      <password>$GITHUB_TOKEN</password>
    </server>
  </servers>
</settings>

$GITHUB_USER, $GITHUB_TOKEN は適切な値に変更してください。

これらの設定の後、Maven プロジェクトからライブラリを使うために POM ファイルに設定を行います。 必要なのは dependencies 要素に書く通常の依存性と、そのライブラリが公開されている GitHub Packages リポジトリの登録です。 リポジトリの登録は repositories 要素下の repository 要素で id, name, url を指定して行います。 これらは「ライブラリを公開する」場合の distributionManagement 要素の repository 要素と同様のものですが、複数の GitHub Packages リポジトリのライブラリを使う際に id が一致しないようにする必要があります。

これを踏まえて POM ファイルは以下のようになります(またいくらか余計な設定を追加していますが):

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>my.example</groupId>
  <artifactId>myproject</artifactId>
  <version>0.1-SNAPSHOT</version>
 
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>
 
  <dependencies>
    <!-- GitHub Packages で公開されているライブラリの依存性 -->
    <dependency>
      <groupId>org.example</groupId>
      <artifactId>github-packages-maven-example</artifactId>
      <version>0.1-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <repositories>
    <!-- ライブラリを公開している GitHub Packages リポジトリを登録 -->
    <repository>
      <id>github-packages-maven-example</id>
      <name>GitHub $GITHUB_USER Apache Maven Packages</name>
      <url>https://maven.pkg.github.com/$GITHUB_USER/github-packages-maven-example</url>
    </repository>
  </repositories>
</project>

1つの GitHub Packages リポジトリごとに repository 要素を追加する必要があるのが少々面倒くさいですが仕方ないでしょう*4。 POM ファイルの設定が済めば、コマンドラインMaven プロジェクトのルートディレクトリに移動して以下のコマンド

mvn install

を実行すると、ライブラリがダウンロードされて使用可能になります。

*1:GitHub サイト内からこのページに行くには、右上アイコンのプルダウンメニュー「Settings」 → 左のメニュー「Developer Settings」 → 左のメニュー「Personal access tokens」 → 「Generate new token」と進みます。

*2:Windows では「C:\Users\《ユーザ名》」。 念のため。

*3:ライブラリを公開する場合に必要な「write:packages」は「read:packages」を含んでいるので、ライブラリを公開する設定を行っているならそれで充分です。

*4:もしかしたら何かしらのプラグインがあったりするかも?

どんとこいタイプ・アノテーション! Checker Framework 導入あの手この手 ~without IDE 編~

前回の記事『ラムダ式やストリーム API や新しい日時 API だけじゃない! Java8 のタイプ・アノテーションあの手この手』で、Java8 で導入されたタイプ・アノテーションがどういったものか紹介しました。 その記事では同一要素に重複して同じアノテーションを付けられる @Repeatable なども紹介しましたが、タイプ・アノテーションは主に「タイプ(型)が使われているところはどこにでもアノテーションが付けられる」という機能を指しているのだと思います。

で、その機能を紹介したのはいいんですが、実際の使い方には触れていませんでした。 これでは片手落ち感が否めないので、タイプ・アノテーションを使用できるライブラリである Checker Framework というのを見ていきたいと思います(が、次に述べる理由により、ライブラリ自体の使い方はこの記事で扱ってません)。 このライブラリは @NonNull のようなアノテーションを型に付加して null 値代入をコンパイル時にエラーを出すといったように、コンパイル時のエラーチェックを行うものです(@Override アノテーションみたいな感じの使い方)。 Java の公式のチュートリアルでもこのライブラリについて言及されていて、これから標準的に使われるようになるかもしれませ。 ただし、1つの難点は Java7 時代の JavaFX のような設定の面倒さがあるところ*1*2

ってことで、この記事では Checker Framework の機能ではなく、インストール方法というかコンパイラにチェック機能を追加する設定方法を見ていきます。 Checker Framework の機能自体もそのうち見ていきたいとは思ってるんですが、いつになることやら。 また、これらの設定は通常 IDE でもやらないといけないものですが、ちょっと手が回らないので今回はビルドツールをいくつか扱うだけにします。

この記事で使用する Checker Framework のバージョンは 1.8.1 とします。 Java は 8 です。 試してませんが Java7 でも同じような設定(ただし jdk8.jar の代わりにjdk7.jar が必要)でできるんじゃないかと思います。

参考

コマンドラインから使用する

まずは、あまり使わないかと思いますが、コマンドラインから Checker Framework を使う方法を見ていきます(Windows)。 別に設定が難しいわけではないのですが、使用するコンパイラを変えるのが如何に面倒かを味わってもらおうかと(笑) ってのは半分冗談ですが、あとで Gradle についての設定方法を見ていくのですが、Gradle には Checker Framework のプラグインが(現時点で)ないので、ビルドスクリプト (build.gradle) を手書きしてやります。 このときにコンパイラにどのような設定をするかは、コマンドラインから Checker Framework を使用する方法をほとんどそのまま移植してやることでできます。 なので、Gradle 上で Chekcer Framework を使いたい人は、コマンドラインからの使用方法(4つ!紹介しますが、特に4つ目)を理解しておいて下さい。 誰かこれを参考にして作ってくれないかなぁ。

コマンドラインから Checker Framework を使う方法は、公式のマニュアルに3つ、別途に通常の javac コマンドにオプションを付けて使用する方法を1つ紹介します。 どれか1つでOK。 最後の方法は公式のドキュメントには載ってないので使用は自己責任で。

最初3つの方法の、Unix 系(というか bash)での設定方法は The Checker Framework Manual 「1.3 Installation」に載ってます。 Windows に関しては The Checker Framework Manual 「24.1 Javac Compiler」にいくつかの方法が載ってます。

コマンドラインから使用する場合は、当然のことながらライブラリが自動ダウンロードされないので、まずは以下の設定をしておいて下さい:

  1. http://types.cs.washington.edu/checker-framework/current/checker-framework.zip から Zip ファイルをダウンロードして、適当なディレクトリに展開する(例えば "C:\java\")
  2. 環境変数*3CHECKERFRAMEWORK」に上記の Zip ファイルを展開したルート・ディレクトリを設定する(上記の例では "C:\java\checker-framework-1.8.1")

また、実際にコンパイル時にアノテーションによるチェックが行われているかを試すための Java ソースコードとして、以下の「GetStarted.java」ファイルを使います( The Checker Framework Manual 「1.3 Installation」から拝借):

// GetStarted.java
import org.checkerframework.checker.nullness.qual.*;

public class GetStarted{
    void sample(){
        @NonNull Object ref = new Object();
        //@NonNull Object ref = null;
    }
}

コメントアウトしている部分を外すと(代わりにその上の行をコメントアウトする)、コンパイル時にエラーが出ます。 では設定方法を見ていきましょう。

Checker Framework の javac コマンドを javac コマンドとして使う
まずは Checker Framework を展開したときに bin ディレクトリに含まれている javac コマンドを使ってコンパイルする方法(Windows)。

set PATH=%CHECKERFRAMEWORK%\checker\bin;%PATH%
javac -processor org.checkerframework.checker.nullness.NullnessChecker GetStarted.java
  • 1行目は1度だけでOK。 set は BASH の export と同じで環境変数を設定するコマンドですね。 PATH の値をセットする際に、%CHECKERFRAMEWORK%\checker\bin を最初に書いてあるところに注意。 これは Checker Framework の javac コマンドを優先して使うようにするために必要です。
  • javac コマンド実行時に -processor オプションによってアノテーション・プロセッサを設定しています。 このアノテーション・プロセッサの設定は他の方法でもどこかで指定する必要があります。 面倒ですが諦めて下さい。 複数のプロセッサを指定する場合はコンマ (,) で区切ります。

この方法では PATH が汚染されるのと、もとの javac コマンドが使えないのが難点。

Checker Framework の javac コマンドを javacheck コマンドとして使う
次の方法は Checker Framework の javac コマンドを javacheck コマンド(別に他の名前でもいいですが)として使う方法:

doskey javacheck=%CHECKERFRAMEWORK%\checker\bin\javac $*
javacheck -processor org.checkerframework.checker.nullness.NullnessChecker GetStarted.java
  • doskey は BASH の alias ですね。 初めて使ったw doskey では引数をパイプ(?)するため、最後に「$*」を付けておかないといけないようです。 alias はいらないらしいそうで。 この doskey コマンドの実行も1度で OK。
  • 2行目は1つ目の方法で javac の代わりに javacheck を使ってるだけです。

この方法だと、もともとの javac コマンドはそのまま使うことができます。 まぁ、普通にコマンドラインから使うにはこれで充分です。 次はコマンドラインから使う方法ですが、Java コード中から使うのに応用できる方法。

Checker Framework の Jar ファイルを使ってコンパイルする
この方法は、Checker Framework のアーカイブに含まれている checker.jar を実行可能 Jar ファイルとして実行する方法です:

java -jar %CHECKERFRAMEWORK%\checker\dist\checker.jar -processor ^
org.checkerframework.checker.nullness.NullnessChecker GetStarted.java

もしくは

doskey javacheck=java -jar %CHECKERFRAMEWORK%\checker\dist\checker.jar $*
javacheck -processor org.checkerframework.checker.nullness.NullnessChecker GetStarted.java

Jar ファイルの実行なので javac コマンドではなく java コマンドを使っていることに注意。 checker.jar に含まれているメインクラスは

です(リンクはソースコード)。 ソースコードは framework サブプロジェクトにあります。 まぁ、ソースコード読もうという人はあんまりいないかもしれませんが、メモのために書いておくと、このクラスは引数をオプションとして解析したり Jar ファイルへのクラスパスを設定したりする処理を行い、実際のコンパイルは com.sun.tools.javac.Main に投げてます。

通常の javac コマンドでコンパイルする
最後は公式のドキュメントには載ってませんが、javac コマンドに(ちょっと長めの)オプションをあれこれ指定して、通常の javac コマンドでコンパイルする方法。

javac -cp .;checker.jar;javac.jar ^
-Xbootclasspath/p:jdk8.jar ^
-processor org.checkerframework.checker.nullness.NullNessChecker GetStarted.java
  • クラスパスに checker.jar, javac.jar を含めます。 上記のコマンドではカレント・ディレクトリにこれらの Jar ファイルがあるとしてますが、そうでない場合はそれらへの(相対 or 絶対)パスをきちんと書く必要があります。
  • 非標準のオプション -Xbootclasspath/p: で jdk8.jar ファイルを指定します。 これもカレント・ディレクトリにない場合はきちんとパスを書く必要があります。 また、Java7 で使いたい場合は jdk7.jar にします(たぶん。 試してないけど)。

まぁ、長々とオプション書いて何が楽しいねんと言われそうですが、ちょっと Gradle で手書きするのに必要なので載せました。

Maven2/3 の設定

次は Maven2/3 で Checker Framework を使う方法。 Maven2/3 に対しては Checker Framework 側でプラグインを作ってくれているので、pom.xml の XML 地獄以外は特に問題なく使えます。 ドキュメントは The Checker Framework Manual 「24.3 Maven plugin」にあります:

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
         
  <modelVersion>4.0.0</modelVersion>
  <groupId>my.test</groupId>
  <artifactId>annotation-test</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>Type annotation Test</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.checkerframework</groupId>
      <artifactId>checker-qual</artifactId>
      <version>1.8.1</version>
    </dependency>    
  </dependencies>
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.checkerframework</groupId>
        <artifactId>checkerframework-maven-plugin</artifactId>
        <version>1.8.1</version>
        <executions>
          <execution>
            <phase>process-classes</phase>
            <goals>
              <goal>check</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <processors>
            <processor>org.checkerframework.checker.nullness.NullnessChecker</processor>
          </processors>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
  • マニュアルにあるリポジトリの設定は必要ありません(バージョン 1.8.0 以降)。
  • 複数のアノテーション・プロセッサが必要な場合、<checkerframework-maven-plugin> 要素下にある <processors> 要素に <processor> 要素を付け加えていけばいいんでしょう。
  • 結構あれこれとプラグインに設定ができるそうです。 詳しくはマニュアル参照。

ちょっと疑問なのが、process-classes フェーズに check ゴールを付加してるところ。 コンパイル2回してたりしない? まさかね。 もともとのコンパイルはスキップしたりしてるのかな?

Gradle の設定

最後は Gradle。 Gradle に関してもマニュアルに設定方法が書いてるのですが、その方法だと、「コマンドラインから使用する」の箇所で書いたようなアーカイブのダウンロード & 展開と環境変数の設定が必要になります。 うーむ、Maven2/3 では pom.xml を書く以外は全自動だったのに、Gradle では手動のインストールが必要みたいに扱われてるのは Gradle にとって不当な扱いだ! ってことで、それらのインストールがいらない build.gradle を書いてみました。 手書きなのでちょっと汚いです。 誰か Gradle プラグイン作って。

apply plugin : 'java'

group = "my.test"
version = "1.0-SNAPSHOT"
sourceCompatibility = 1.8
targetCompatibility = 1.8

project.ext{
    enc = 'UTF-8'
    checkerVersion = '1.8.1'
    processors = [
        'nullness.NullnessChecker',
        'interning.InterningChecker'
    ].collect{ 'org.checkerframework.checker.'+it }
}

repositories.mavenCentral()

dependencies{
    ['checker', 'checker-qual', 'compiler', 'jdk8'].each{
        compile "org.checkerframework:$it:$checkerVersion"
    }
}

tasks.withType(Compile){
    options.encoding = enc

    if(it in JavaCompile){
        options.with{
            fork = true

            def jdk8JarPath = configurations.compile.files.find{
                it.name == "jdk8-${checkerVersion}.jar"
            }.absolutePath

            compilerArgs = [
                "-Xbootclasspath/p:$jdk8JarPath",
                '-processor', processors.join(',')
            ]
        }
    }
}

task wrapper(type: Wrapper) {
    gradleVersion = "1.12"
}
  • 基本的には、「コマンドラインから使用する」の箇所に書いた4つ目の方法を Groovy & Gradle 風に書き直しただけです。 JavaCompiler タスクに最低限のオプションだけを設定しています。
  • このままだとテストコードについてもチェックが行われるので(別にいいんですけど)、テストコードのコンパイルではチェックを行わないような設定もしたいところ。
  • アノテーション・プロセッサを追加したい場合は getAnnotationProcessors() メソッドの適当な箇所 project.ext ノード下の processors プロパティの要素に追加して下さい。
  • checker-qual への依存関係はなくても Gredle でビルドする分にはいりませんが、IDE とか使う場合はいるんじゃないかと。
  • マルチ・プロジェクト(サブプロジェクトがあるプロジェクト)では、マニュアルにあるように allprojects{ ... } の ... の部分にコンパイラ等のスクリプトを書けば OK。
  • 【追記】Gradle のバージョンによってはこの build.gradle ではエンコーディングの設定がうまくいきません。 「tasks.withType(AbstractCompile){ ... }」とすると大丈夫かと。

以上、コマンドラインからの実行、Maven, Gradle の設定方法を見てきました。 マニュアルには Apache Ant についての設定も書いてあります。 必要な方はそちらをどうぞ。

Checker Framework を実際の開発に使うには、さらに IDE の方で

  • Java8 タイプ・アノテーションに対するサポートの設定
  • Checker Framework によるコンパイルの設定

を行う必要があるので、今回の記事の設定だけでは Checker Framework を導入するには不十分ですが、この記事はとりあえずこのへんで。 @Override アノテーションも今や使っている人の方が多いんじゃないかと思うので、Checker Framework も使われ出したら広まるの速いんじゃないかなぁ。 Lombok のような別ライブラリもあるけど。 なんにしろまず使ってみないことには話が始まらないね。

修正

  • 前半の日本語が変だったところをいくつか修正しました。
  • 「Gradle の設定」の build.gradle をちょっとリファクタリングしました(修正前と動作は同じだと思います)。

修正2
Gradle の箇所に【追記】しました。

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

Jenkins実践入門 ?ビルド・テスト・デプロイを自動化する技術 (WEB+DB PRESS plus)

Jenkins実践入門 ?ビルド・テスト・デプロイを自動化する技術 (WEB+DB PRESS plus)

  • 作者: 佐藤聖規,和田貴久,河村雅人,米沢弘樹,山岸啓,川口耕介
  • 出版社/メーカー: 技術評論社
  • 発売日: 2011/11/11
  • メディア: 単行本(ソフトカバー)
  • 購入: 26人 クリック: 496回
  • この商品を含むブログ (65件) を見る

*1:ライセンスがどうこうという話ではありませんが。

*2:あまり正確なところは分からないのですが、Java7 でも使えるようになってるせいか、Java8 で使う場合にもコンパイラを実行するときに設定がいるのが原因じゃないかと。 まぁ、逆に言えば Java7 でも使えるようなので、ご興味のある方は試してみて下さい。 Java6 以前は非サポートなようです。 対応してた痕跡はコードに残ってますが。

*3:Windows のバージョンによるかと思いますが、「コントロールパネル ▶ システムとセキュリティ ▶ システム ▶ システムの詳細設定 ▶ 環境変数」あたりで設定できます。

Spock ことはじめ ~without IDE 編~

Java/Groovy のテスティング・フレームワーク Spock の使い方をあれこれ調べてみました。 まぁ、Gradle 使えるならそれに越したことはないですが、なんとなく Maven3 やコマンドラインからの使用方法も試してみました。

どうも Maven3 から GMaven が Groovy コードのコンパイルをサポートしなくなったようで(Groovy スクリプトの実行などに機能を絞ったもよう)、Spock のドキュメントに書いてある方法では Maven3 上で Spock を使えなくなってます。 Maven3 での Groovy コードのコンパイルGroovy Eclipse compiler プラグインというのを使って行うようです。 このプラグインは Maven3 の Java コンパイラを Groovy もコンパイルできるものに置き換えるというアプローチの Groovy サポートなようです。 まぁ、Maven3 上で Spock を使うってのにどの程度のニーズがあるか分かりませんが。

それ以上に使うかどうか分かりませんが、Gradle や Maven3 のようなビルドツール(プロジェクト管理ツール)を使わずに groovy コマンドで Spock テストを行うことも出来ます。

他に Apache Ant を使ったビルド方法もあるようですが、build.xml を読むのが面倒だったのでパス。 ご興味のある方はこちらを参照。

目次

参考

Gradle

まずは王道の Gradle による Spock テスト。 プロジェクトのディレクトリ構造が以下のようになっているとします:

  • ${project.rootDir}
    • build.gradle
    • src
      • main
      • test
        • groovy
          • my
            • test
              • HelloSpock.groovy

ファイルは build.gradle と HelloSpock.groovy のみ。 build.gradle はプロジェクトのルートディレクトリに、HelloSpock.groovy は my.test パッケージに配置しています。 まぁ、普通の Gradle プロジェクトですね。 プロジェクトのテストに Spock を使うには、build.gradle に以下のような設定を書きます(ちょっと余計な設定を入れてますが):

// build.gradle
apply plugin: "groovy"

group = "my.test"
version = "1.0-SNAPSHOT"
sourceCompatibility = 1.8
targetCompatibility = 1.8
tasks.withType(Compile){ options.encoding = "UTF-8" }

repositories.mavenCentral()

dependencies{
    compile "org.codehaus.groovy:groovy-all:2.3.0-rc-1"
    testCompile "org.spockframework:spock-core:0.7-groovy-2.0"
}

task wrapper(type: Wrapper) {
    gradleVersion = "1.12"
}

大事なのは「apply plugin : "groovy"」と dependency ノード下の依存性の設定です。 Gradle のちょっと前のバージョンから、Groovy の依存性は groovy ではなく compile によって設定するようになりましたね*1

次は Spock によるテストコード HelloSpock.groovy。 Spock wikiHelloSpock」から拝借しました(パッケージ宣言だけ追加しています):

// HelloSpock.groovy
package my.test

import spock.lang.*

class HelloSpock extends Specification{
    def "length of Spock's and his friends' names"(){
        expect:
        name.size() == length

        where:
        name     | length
        "Spock"  | 5
        "Kirk"   | 4
        "Scotty" | 6
    }
}

これで準備 OK。 次はテストの実行。

テストの実行
上記のようにプロジェクトが用意できたら、次はテストの実行。 プロジェクトのルートディレクトリで以下のようにコマンドを実行します*2

gradle test

実行結果は

  • ${project.rootDir}/build/reports/tests/index.html

から確認できます。 コマンドライン上で確認できないのは少々面倒。 まぁ、失敗が多いとブラウザで構造的に閲覧できないと手に負えなくなりますがね。

テストのフィルタリング
Gradle の最後は、1つもしくは一部のテストを指定して実行する方法。 Gradle User Guide 「The Java Plugin : Test」 の Test filtering の箇所を参照。 「--tests」オプションを使用して、クラスやメソッド (feature) を完全修飾名で指定したり、ワイルドカード「*」で適合するものを指定したりできます:

  • gradle test --tests org.gradle.SomeTest.someSpecificFeature
  • gradle test --tests *SomeTest.someSpecificFeature
  • gradle test --tests *SomeSpecificTest
  • gradle test --tests all.in.specific.package*
  • gradle test --tests *IntegTest
  • gradle test --tests *IntegTest*ui*
  • gradle someTestTask --tests *UiTest someOtherTestTask --tests *WebTest*ui

たとえば my.test.HelloSpec クラスに定義されたテストを全て実行するためには、以下のコマンドを実行します:

gradle test --tests my.test.HelloSpec

まぁ、ただ個別にテストを実行するには IDE を使う方が楽ちん。 IDE 上で Spock を使う方法は後日に。 って、普通に JUnit のように使えるようですけど。

Maven 3

次は Maven3 上で Spock を使う方法。 試した Maven3 のバージョンは 3.2.1 です。 かなり久し振りに Maven 使った(というかインストールした)。 プロジェクト構造は Gradle と同じ(Gradle が Maven2/3 と同じという方が正しいかな?):

  • ${project.rootDir}
    • build.gradle
    • src
      • main
      • test
        • groovy
          • my
            • test
              • HelloSpockTest.groovy

Spock テストのファイルを HelloSpockTest.groovy に変更してるのに注意。 もちろんクラス名も変更してます。 どうも、Maven3 に(というか、テストを実行している Surefire プラグインに)テストであることを認識してもらうためにはクラス名が Test で終わらないといけないようです。 こちらにテストとして含めるファイル名(クラス名)の指定方法が書かれてますが、試してもうまくいかなかったのでスルーします。 Gradle の build.gradle に対応する pom.xml は以下のようにします(XML 助長過ぎる・・・):

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
         
  <modelVersion>4.0.0</modelVersion>
  <groupId>my.test</groupId>
  <artifactId>spock-test</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>Spock Test</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <compilerId>groovy-eclipse-compiler</compilerId>
          <source>1.7</source>
          <target>1.7</target>
          <!--verbose>true</verbose-->
        </configuration>

        <dependencies>
          <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-eclipse-compiler</artifactId>
            <version>2.8.0-01</version>
          </dependency>

          <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-eclipse-batch</artifactId>
            <version>2.1.8-01</version>
          </dependency>
        </dependencies>
      </plugin>

      <plugin>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy-eclipse-compiler</artifactId>
        <version>2.8.0-01</version>
        <extensions>true</extensions>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.codehaus.groovy</groupId>
      <artifactId>groovy-all</artifactId>
      <version>2.1.8</version>
    </dependency>

    <dependency>
      <groupId>org.spockframework</groupId>
      <artifactId>spock-core</artifactId>
      <version>0.7-groovy-2.0</version>
      <scope>test</scope>
    </dependency>    
  </dependencies>
</project>
  • いまのところ、動かせる Groovy のバージョンは 2.1.8 のようです。 groovy-eclipse-batch プラグインが対応していない Groovy のバージョンはダメなよう(試してないけど)。
  • 詳細な情報を出力したい場合は、maven-compiler-plugin の設定の箇所の、コメントアウトしてある verbose 要素の部分のコメントを外します。
  • javaソースコードのバージョンを指定する source/target に 1.8 を指定すると、いまのところ例外が投げられるようです。 ラムダ式とか使ってなくても。

pom.xmlXML 地獄以外は Gradle の場合と対して変わりませんね。 テストの実行は簡単。 プロジェクトのルートディレクトリで以下のコマンドを実行します:

mvn test

テストのフィルタリングもできるようですが、まぁ、いいでしょう。

コマンドライン

最後はコマンドラインから Spock を使う方法。 Spock wiki にはコマンドライン上で Jar ファイルをクラスパスに指定してテストを実行する方法が書かれてますが、ここでは Grape によって Jar ファイルを自動ダウンロードする方法を見てみましょう。 実質的には同じですが。 Grape によって Spock を使うには、Spock テストのコードに、以下のような @Grab, @GrabExclude アノテーションを追加します:

// HelloSpock.groovy
@Grab("org.spockframework:spock-core:0.7-groovy-2.0")
@GrabExclude("org.codehaus.groovy:groovy-all")
import spock.lang.*

class HelloSpock extends Specification{

    def "length of Spock's and his friends' names"(){
        expect:
        name.size == length

        where:
        name     | length
        "spock"  | 5
        "Kirk"   | 4
        "Scotty" | 6
    }
}

2つのアノテーション以外は同じです。 @GrabExclude アノテーションは、groovy-all.jar のバージョン衝突を防ぐために付けておく必要があります。 テストの実行は普通の Groovy スクリプトの実行と同じです:

groovy HelloSpock
  • コマンドを実行するディレクトリ上にある Groovy コードは自動でクラスパスに含められます。 それ以外に使用するソースコードがある場合は、そのファイルがあるディレクトリを「-cp」オプションによってクラスパスに含める必要があります。
  • Grape を使わないなら「-cp」オプションで Spock Framework と JUnit の Jar ファイルをクラスパスに追加します。 JUnit はバージョンによっては他に Jar が必要だったかもしれません。 そのあたりの依存性解決が面倒なので Grape でやっておく方が無難かと。

とりあえず Spock を使うための環境整備はこんなものですかね。 通常の開発では IDE からテストを行うことの方が多いと思いますが、Spock テストは JUnit が動く IDE 上では同じように使えるので便利です。 後は Gradle のマルチプロジェクトで使う場合をもうちょっと試したいところですが、それらは次回以降に見ていきたいと思います。

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

Jenkins実践入門 ?ビルド・テスト・デプロイを自動化する技術 (WEB+DB PRESS plus)

Jenkins実践入門 ?ビルド・テスト・デプロイを自動化する技術 (WEB+DB PRESS plus)

プログラミングGROOVY

プログラミングGROOVY

*1:これを testCompile にするとテストにのみ Groovy を使うようにできるのかな? とりあえずテストは機能しますがパッケージングしたりするのが面倒なので成果物が Groovy に依存しないかどうかは試してません。

*2:もしくは、一度「gradle wrapper」と実行して、その後に「gradlew test」と 'w' を付けて実行。

GMaven 1.3 での pom.xml

GMaven 1.3がリリースされていたので使ってみた(参照 URL : Ben Tilford's Blog「GMaven 1.3 is out」)。

このバージョンでは

  • configuration 要素の sourceEncoding, source, target が動作するようになった
  • Java スタブコードで java.util.*, java.io.* 内のクラスがきちんと解決できるようになった
  • ソースコードのエンコーディングがデフォルトで ${project.build.sourceEncoding} から読まれるようになった

などが修正されてます(他にもありますが)。 詳しくは「GMaven 1.3 is out」などを参照のこと。

ただ、ジェネリクスを含む Groovy クラスから生成された Java スタブコードに、以前には出てなかったコンパイルエラーが報告されるようになってましたが・・・*1

まぁそれはともかく、プロジェクトのパラメータを

パラメータ名 パラメータ値
groupId org.sample
artifactId gmaven-1.3-sample
version 1.0-SNAPSHOT

として、GMaven 1.3 プロジェクトでの「pom.xml」はこんな感じ:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.sample</groupId>
    <artifactId>gmaven-1.3-sample</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <groovy.version>1.7.4</groovy.version>
        <gmaven.version>1.3</gmaven.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>${groovy.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.gmaven</groupId>
                <artifactId>gmaven-plugin</artifactId>
                <version>${gmaven.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generateStubs</goal>
                            <goal>compile</goal>
                            <goal>generateTestStubs</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
     </build>
</project>

この GMaven プロジェクトでコンパイル等を実行する gmaven-plugin で使われる Groovy のバージョンは「1.7.4」です。 これは dependency 要素で設定されている groovy-all ライブラリとは無関係です(この groovy-all のバージョンを 1.7.3 などにしても、gmaven-plugin で使われる Groovy のバージョンは 1.7.4)。 Groovy 1.7.5 がリリースされてそれを使いたい場合は、「イカす達人 (2) : Maven2 流 Groovy プロジェクト with Groovy 1.7」で行ったような、gmaven-plugin の依存性の設定が必要そうです。 まぁ、今のところは気にすることでもありませんが。

P.S. 「pom.xml」のプラグインの設定に executions 要素を加えるのを忘れてましたm(_ _)m

Groovyイン・アクション

Groovyイン・アクション


Apache Maven 2.0入門 Java・オープンソース・ビルドツール

Apache Maven 2.0入門 Java・オープンソース・ビルドツール

*1:Groovy クラス自体にはジェネリクスを使う必要はあんまりなさそうだけど、ジェネリクスを使った Java クラス(インターフェース)を拡張(実装)して Groovy クラスを作ろうとすると、この手の例外が出てしまうことが・・・

Groovy プロジェクトを Maven2 でコンパイルしてみたら・・・

なんか、やたらとコンパイルエラーがでますねぇ・・・(^ ^;) ほとんどライブラリのダウンロード(+ちょっとしたスクリプト)にしか使ってなかったので、問題ないかと思ってたけど、あまり気持ちのいいものじゃないですねぇ。

IntelliJ IDEA 上でコンパイル&実行する分には問題ないようですが、どうも Groovy コードからコンパイラによって生成した Java のスタブコードがもんだいなようですね。 GMaven が悪いのかな?

報告されたコンパイルエラーは大体次のようなもの:

  • java.io, java.util パッケージのクラスが解決できない
  • 独自アノテーションが解決できない
  • main() メソッドが認識できない
  • JUnit(3.x) の test メソッドが認識できない

とりあえずの解決策を:

java.io, java.util パッケージのクラスが解決できない
それらのパッケージの import 文を書く。 import java.io.* ワイルドカードは使っても大丈夫。 import 文の代わりに、完全修飾名を使ってもよい*1
独自アノテーションが解決できない
アノテーションクラスごとに import 文を書く。 ワイルドカードによる import はまずい模様。 アノテーションクラスだけを含むパッケージを作った方が良さそう。
main() メソッドが認識できない
返り値を void に設定する*2。 static void main(args){...}
JUnit(3.x) の test メソッドが認識できない
main() メソッドと同じように返り値を void に設定する。 void testXxxx(){...}

Groovyイン・アクション

Groovyイン・アクション


Griffon in Action

Griffon in Action

*1:import 文だと、「Optimize Imports (Ctrl + Alt + O)」で消えてしまう怖れが・・・

*2:普通なら「static main(args)」だけで main() メソッドと認識してくれます。

イカす達人 (8) : テストにだけ Groovy を使うプロジェクト

以前Maven2 プロジェクトで Groovy を使用する方法を見ました(一覧)。 ただし、Groovy は便利で、Java と Groovy がシームレスに連携できるとはいえ、いきなり Java プロジェクトを Groovy プロジェクトに変更できるとは限りません。

そこで、メインコードは Java のみで書き、テストコードは Groovy (と Java)で書けるというプロジェクト(の pom.xml)を考えてみます。

サンプルのパラメータ


まず、以下のサンプルで使用するパラメータを示しておきます:

パラメータ
groupId org.sample
artifactId sample
version 1.0-SNAPSHOT

pom.xml


pom.xml の概要は以下のようになります。 「...」の箇所はそれぞれ後で見ていきます。

<?xml version="1.0" encoding="UTF-8"?>
<project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.sample</groupId>
    <artifactId>sample</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties> ... </properties>

    <dependencies> ... </dependencies>

    <build>
        <defaultGoal>install</defaultGoal>
        <plugins>
            <plugin> ... </plugin>

            <plugin> ... </plugin>
        </plugins>
    </build>
</project>
  • project/build/defaultGoal」を install に設定しているのは個人的嗜好。 特に必須ではありません。

properties : プロパティ


次は pom.xml 内で使用するプロパティの値です。 一応、この箇所を変更すればプロジェクトの設定を変更できますが、pom.xml の書き方上、変更できないモノもあります。

    <properties>
        <java.version>1.6</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <groovy.version>1.7.2</groovy.version>
        <gmaven.runtime>1.7</gmaven.runtime>
        <gmaven.version>1.2</gmaven.version>
    </properties>
  • java.version」は Java ファイルのコンパイル時に source, target に指定する値です。 まぁ、1.5, 1.6 のどちらかでしょうね。
  • project.build.sourceEncoding」はソースコードのエンコーディングです。 Javaコード、リソース、Groovy コードに対してエンコーディングを指定します。
  • groovy.version」は Groovy のバージョンです。 変更する際には 1.7.x 系だけにして下さい。
  • gmaven.runtime, gmaven.version」は基本的には変更しないでください。

dependencies : 依存性


次はプロジェクト内で使用するライブラリへの依存性です。 もちろん、必要なら他のライブラリへの依存性を追加してください。

    <dependencies>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>${groovy.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
  • Groovy はテスト内でだけ使うので、スコープを test にしておきましょう。
  • JUnit は Groovy 内に入ってそうな気もしますが・・・ まぁ、一応。

plugin (1) : maven-compiler-plugin


プラグインその1、Java コードのコンパイルをする maven-compiler-plugin の設定。

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
  • source, target を java.version プロパティの値に指定します。
  • encoding の指定も付加。

plugin (2) : gmaven-plugin


プラグインその2、テストのための Groovy コードに対してスタブコード生成 & コンパイルを行います。 指定されているゴールが generateTestStubs, testCompile の2つだけです。 dependencies 要素は Groovy 1.7 を使うために必要。

            <plugin>
                <groupId>org.codehaus.gmaven</groupId>
                <artifactId>gmaven-plugin</artifactId>
                <version>${gmaven.version}</version>

                <executions>
                    <execution>
                        <goals>
                            <goal>generateTestStubs</goal>
                            <goal>testCompile</goal>
                        </goals>

                        <configuration>
                            <providerSelection>${gmaven.runtime}</providerSelection>
                            <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
                        </configuration>
                    </execution>
                </executions>

                <dependencies>
                    <dependency>
                        <groupId>org.codehaus.gmaven.runtime</groupId>
                        <artifactId>gmaven-runtime-${gmaven.runtime}</artifactId>
                        <version>${gmaven.version}</version>

                        <exclusions>
                            <exclusion>
                                <groupId>org.codehaus.groovy</groupId>
                                <artifactId>groovy-all</artifactId>
                            </exclusion>
                        </exclusions>
                    </dependency>

                    <dependency>
                        <groupId>org.codehaus.groovy</groupId>
                        <artifactId>groovy-all</artifactId>
                        <version>${groovy.version}</version>
                    </dependency>
                </dependencies>
            </plugin>

これで設定完了です。 まぁ、Groovy 1.7を使うプロジェクトの pom.xml とそんなには違いませんね。

上記をまとめた pom.xmlこちらに置いてます。

Groovyイン・アクション

Groovyイン・アクション


Apache Maven 2 Effective Implementation: Build and Manage Applications With Maven, Continuum, and Archiva

Apache Maven 2 Effective Implementation: Build and Manage Applications With Maven, Continuum, and Archiva

GMaven プラグインを作ろう! (4) : GMaven プラグイン・プロジェクトを生成する Groovy スクリプト

GMaven プラグイン・プロジェクトの pom.xml は結構複雑なので(以前の記事)毎回書くのは大変です。 そこで Groovy の練習を兼ねて、テンプレートを生成するスクリプトを書いてみました(GMavenPluginArchetype.groovy*1一覧)。

スクリプトの実行


上記の Groovy スクリプトでプロジェクトを生成するには、プロジェクトを生成したいフォルダ上にその Groovy スクリプトを配置し、以下のコマンド

groovy GMavenPluginArchetype

を実行してください。

入力するパラメータ


上記の Groovy スクリプトを実行すると、「mvn archetype:generate」と同様に、いくつかのパラメータの入力を求められます。 入力するパラメータは以下の通り:

パラメータ デフォルト値 下記でのサンプルでの値
groupId - org.sample
artifactId - maven-hello-plugin
version 1.0-SNAPSHOT 1.0-SNAPSHOT
package ${groupId} org.sample
goal sayhi sayhi
encoding ${file.encoding} UTF-8

生成されるファイル


GMavenPluginArchetype.groovy を実行して生成されるファイルは以下の5つです:

pom.xml
src/main/groovy/HelloMojo.groovy
src/test/groovy/HelloTest.groovy
src/test/project/pom.xml
src/test/project/mvn.groovy

フォルダ構造は下図のようになります:

以下でそれぞれを見ていきましょう。 ただし、各パラメータの値は上記の表の「下記サンプルでの値」が設定されているものとします*2

★pom.xml

これはプラグイン開発するプロジェクトの pom.xml です。 前々回の記事参照。

<?xml version="1.0" encoding="UTF-8"?>
<project
        xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>org.sample</groupId>
    <artifactId>maven-hello-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>maven-plugin</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.codehaus.gmaven</groupId>
            <artifactId>gmaven-mojo</artifactId>
            <version>1.2</version>

            <exclusions>
                <exclusion>
                    <groupId>org.codehaus.gmaven.runtime</groupId>
                    <artifactId>gmaven-runtime-1.5</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.codehaus.gmaven.runtime</groupId>
            <artifactId>gmaven-runtime-1.6</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <defaultGoal>install</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.codehaus.gmaven</groupId>
                <artifactId>gmaven-plugin</artifactId>
                <version>1.2</version>

                <executions>
                    <execution>
                        <goals>
                            <goal>generateStubs</goal>
                            <goal>compile</goal>
                            <goal>generateTestStubs</goal>
                            <goal>testCompile</goal>
                        </goals>

                        <configuration>
                            <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
                            <providerSelection>1.6</providerSelection>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

★src/main/groovy/HelloMojo.groovy

これは実際にプラグインの処理を実装するクラスです。 前回の記事参照。

// Generated from archetype; please customize.
package org.sample

import org.codehaus.gmaven.mojo.GroovyMojo

/**
 * Example Maven2 Groovy Mojo.
 *
 * @goal sayhi
 */
class HelloMojo extends GroovyMojo {
    /**
     * The hello message to display.
     *
     * @parameter expression="${message}" default-value="Hello World"
     */
    String message

    void execute() {
        log.info("${message}")
        show()
    }

    void show(){
        log.info(GroovySystem.properties.'package'.toString())
    }
}

★src/test/groovy/HelloTest.groovy

テストコード。

// Generated from archetype; please customize.
package org.sample

import groovy.util.GroovyTestCase

class HelloTest extends GroovyTestCase {
    void testShow() {
        new HelloMojo().show()
    }
}

★src/test/project/pom.xml

Maven2 に関わらず、プラグインというのはテストが大変。 ということで、プラグインを実際にコンパイル&インストール(ローカルリポジトリへ)した後に別プロジェクトで動作させるテストを作っておくと便利かと。

ってことで、「src/test/project」フォルダ下にそれ用の pom.xml を生成します。 このプロジェクトは validate フェーズを実行すると @sayhi ゴール(プロジェクトを生成時に指定したゴール)が実行されるように設定されています。

少々注意が必要なのは、このプラグイン・テストはプロジェクトをローカルリポジトリへインストールした後に実行する必要があることです。 Maven2 でのテストはインストール前に行うので、このプラグインテストはライフサイクルへは組み込まれてません。

<?xml version="1.0" encoding="UTF-8"?>
<project
        xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>test.org.sample</groupId>
    <artifactId>test-maven-hello-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>
        <defaultGoal>validate</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.sample</groupId>
                <artifactId>maven-hello-plugin</artifactId>
                <version>1.0-SNAPSHOT</version>

                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>sayhi</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

★src/test/project/mvn.groovy

この Groovy プロジェクトは、上記のテストプロジェクトを実行するためのスクリプトです。 実行するためには、環境変数 M2_HOMEMaven2 をインストールしたフォルダへのパスを設定する必要があります。

ただし、テストプロジェクトを実行するためにこの Groovy スクリプトを通す必要はありません。 IDE などで直接テストプロジェクトのフェーズやゴールを実行しても構いません。

ant = new AntBuilder()
ant.property(environment:'env')

if(System.properties['os.name'].contains('Windows'))
  ant.property(name:'mvn', location:'${env.M2_HOME}/bin/mvn.bat')
else
  ant.property(name:'mvn', location:'${env.M2_HOME}/bin/mvn')

ant.exec(dir:'.', executable:'${mvn}', failonerror:true)

今回説明した Groovy スクリプトを実行するためには、このスクリプトをダウンロードしてあれこれしないといけないので、少々面倒かも知れませんが、「プラグインを実行するテストプロジェクト」は結構便利だと思うので、ここでの Groovy スクリプトとは関係なく使ってみてはいかがでしょう?

*1:独自の archetype を書いても使えるようにする設定は大変だし(リポジトリの設定か setting.xml の設定が必要そう)、そもそも Groovy ファイルを配置すべき「src/main/groovy」が何故かしら作成できません。

*2:つまり、サンプル中に現れている ${...} は実際にその様に出力されているということです。

GMaven プラグインを作ろう! (3) : GroovyMojo、プラグインのインストール&使用とか

今回はプラグインのインストール&使用とか(一覧)。

GroovyMojo クラス


GMaven プラグインで行う処理を実装するのは GroovyMojo のサブクラスです。 なので、GMaven プラグインには最低1つは以下のようなクラス(GroovyMojo クラス)を作成する必要があります。 GroovyMojo クラスの実装は概ね以下のような感じになります:

// Generated from archetype; please customize.
package org.sample

import org.codehaus.gmaven.mojo.GroovyMojo

/**
 * Example Maven2 Groovy Mojo.
 *
 * @goal sayhi
 */
class HelloMojo extends GroovyMojo {
    /**
     * The hello message to display.
     *
     * @parameter expression="${message}" default-value="Hello World"
     */
    String message

    void execute() {
        ...
    }
}

作成する GroovyMojo クラスで重要なのは

くらいでしょうか。 加えて、

というのもよく使います。

GroovyMojo の実装


GroovyMojo の実装には、通常の Groovy のコーディングに加えて

  • Mojo と共通する手法
  • GMaven プロジェクトと共通する手法

を知っておく必要があります。

★Mojo と共通する手法

GroovyMojo は通常の (Abstract)Mojo と同じように、JavaDoc アノテーションによってプロジェクトの情報を得ることができます。 詳しくは以下を参照:

★GMaven プロジェクトと共通する手法

GMaven プロジェクトで Groovy コードを実行する際に見た、「定義済み変数、メソッド」も GroovyMojo 内で使用することができます。 詳しくは

などを参照してください。 「定義済み変数、メソッド」を一応再掲:

定義済み変数 説明
project, pom ビルドを実行している Maven2 プロジェクト
session 実行しているビルド・セッション
settings 実行しているビルドの設定
log SLF4J ログ・インスタンス
ant Ant ビルダー
fail() MojoExecutionException を投げる

プラグインのインストールと使用


自己完結のため、一応プラグインのインストール方法と使用方法を載せておきます。 これらは、通常の Mojo プロジェクトと同じです。

★プラグインをインストールする

プラグインのインストールはいつもの如く、コマンド

mvn install

を実行するだけです。

★他のプロジェクトからプラグインを使用する(1) :ゴール単独で使用する

ゴール単独で実行したい場合は、チョット長いですが

mvn groupId:artifactId:version:goal

という風にコマンドを実行します。 前回のサンプルでは(作成したゴールが「sayhi」の場合)

mvn org.sample:maven-hello-plugin:1.0:sayhi

を実行します。

★他のプロジェクトからプラグインを使用する(2) :他のフェーズにゴールを割り当てる

ゴールをどこかのフェーズに割り当てて、ライフサイクルの実行時に自動的にそのゴールが実行されるようにするには、pom.xml を修正する必要があります。 前回のサンプルで、「sayhi」ゴールを「validate」フェーズへ割り当てる場合、次のようになります:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.sample</groupId>
        <artifactId>maven-hello-plugin</artifactId>
        <version>1.0</version>
        <executions>
          <execution>
            <phase>validate</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
          </executions>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Maven2 は設定が大変!

Groovyイン・アクション

Groovyイン・アクション


Apache Maven 2.0入門 Java・オープンソース・ビルドツール

Apache Maven 2.0入門 Java・オープンソース・ビルドツール

GMaven プラグインを作ろう! (2) : GMaven プラグイン・プロジェクトの pom.xml

今回は GMaven プラグイン・プロジェクト(Groovy による Maven2 プラグインを開発するプロジェクト)の pom.xml を見ていきます(一覧)。

まずその前に、お詫びと訂正を。 別に拙者が悪いわけではないんですけど。 前回見た GMaven プラグイン・プロジェクトの生成方法ですが、「対話的にプロジェクトを生成する」方法でも「パラメータを指定してプロジェクトを生成する」方法でも、生成された pom.xml そのままではうまく動かないようです。 正確には、install はできますが、実際に他のプロジェクトでそのプラグインを使おうとすると例外が投げられるといった具合です*1

また、「Groovy 1.7 を使用する方法」を後日書くと言っていましたが、ちょっとした修正程度では使用できるようにならなさそうなので Groovy 1.7の使用は諦めます*2

総じて「GMaven で Maven2 プラグインを開発している人間は、世界中に1人もいない」という結論に達しました(^ ^;) 書く気力がゴッソリなくなりましたが、あれこれ試行&錯誤したのを無駄にするのもそれはそれで哀しいので、とりあえず動いたサンプルだけでも書いておきます。

Groovy 1.6 + GMaven 1.2


Groovy 1.7 の使用は諦めたので、Groovy 1.6 を使用します。

また、各プロジェクトで設定する必要のある値 (groupId, artifactId, version) には、以下の値を使用します:

パラメータ名
groupId org.sample
artifactId meven-hello-plugin
version 1.0

これらを踏まえて、GMaven プラグイン・プロジェクトの pom.xml は以下のようになります:

<?xml version="1.0" encoding="UTF-8"?>
<project
        xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>org.sample</groupId>
    <artifactId>maven-hello-plugin</artifactId>
    <version>1.0</version>
    <packaging>maven-plugin</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.codehaus.gmaven</groupId>
            <artifactId>gmaven-mojo</artifactId>
            <version>1.2</version>

            <exclusions>
                <exclusion>
                    <groupId>org.codehaus.gmaven.runtime</groupId>
                    <artifactId>gmaven-runtime-1.5</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.codehaus.gmaven.runtime</groupId>
            <artifactId>gmaven-runtime-1.6</artifactId>
            <version>1.2</version>
        </dependency>
        
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <defaultGoal>install</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.codehaus.gmaven</groupId>
                <artifactId>gmaven-plugin</artifactId>
                <version>1.2</version>

                <executions>
                    <execution>
                        <goals>
                            <goal>generateStubs</goal>
                            <goal>compile</goal>
                            <goal>generateTestStubs</goal>
                            <goal>testCompile</goal>
                        </goals>

                        <configuration>
                            <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
                            <providerSelection>1.6</providerSelection>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
  • <packaging>maven-plugin<packaging>」の設定は大切。 忘れないように。
  • ソースコードのエンコーディングは UTF-8 に設定しています。 変更したい場合は「project/properties/source.encoding」要素のテキストを変更してください。
  • 使用される Groovy のバージョンは1.6.5です。

Groovyイン・アクション

Groovyイン・アクション


Programming Groovy: Dynamic Productivity for the Java Developer (The Pragmatic Programmers)

Programming Groovy: Dynamic Productivity for the Java Developer (The Pragmatic Programmers)

*1:org.codehaus.groovy.runtime.callsite.CallSite が見つからない」と言ったメッセージが出てくるので、適切な依存性を付加すれば動きそうなんですが・・・何故か上手く動かない。

*2:誰かが「設定方法の確立」もしくは「プラグインのバグの修正」をしてくれることを祈って、一応原因を書いておきます。 原因は GroovyMojo の実装クラスからその Java スタブコードを生成する際に(つまり generateStubs ゴールの実行時に) JavaDoc が除去されてしまうことです。 その結果、@goal (や @parameter)が設定されていないとみなされます。 別途 Java アノテーションで設定できるようになっているとかいうこともなさそうです

プロジェクトのテンプレートが・・・

対話的に Maven2 プロジェクトを生成する」際のコマンド

mvn archetype:generate

を実行すると、プロジェクトのテンプレートが249個も出てくるんですけど! 「番号で選べ」って言われても探しきれない! しかも番号が前のモノから変わってるし!

挙げ句の果てには、「パラメータを指定してプロジェクトを生成する」のも上手く作動しなくなってるっぽいし(途中で確かめるの面倒になった)。

Apache Maven 2.0入門 Java・オープンソース・ビルドツール

Apache Maven 2.0入門 Java・オープンソース・ビルドツール


Apache Maven 2 Effective Implementation: Build and Manage Applications With Maven, Continuum, and Archiva

Apache Maven 2 Effective Implementation: Build and Manage Applications With Maven, Continuum, and Archiva