昨今、機械学習や画像処理でGPUが使える、使えないによって、Javaの依存関係を切り替えなきゃしゃーないってプロジェクトもあるかと思います。こんな時、ビルドする度にpom.xmlの依存関係を手で書き換えるのも凄く面倒。そこで、Mavenのプロファイル機能を使ってうまく対応できないか、検証してみました。

Mavenのプロファイル機能

Mavenではpom.xmlでプロパティ(変数)を設定できるのですが、それを「プロファイル」という単位で切り替えることができます。 ベーシックに「DEFAULT MESSAGE」という値を持つプロパティ"message"を定義するには次のように書きます。
<project>
  ...
  <properties>
    <message>DEFAULT MESSAGE</message>
  </properties>
  ...
</project>
このmessageの値をプロファイルごとに切り替えたい場合は、次のように書きます。
<project>
  ...
  <profiles>
    <profile>
      <id>profile1</id>
      <properties>
        <message>PROFILE MESSAGE 1</message>
      </properties>
    </profile>
    <profile>
      <id>profile2</id>
      <properties>
        <message>PROFILE MESSAGE 2</message>
      </properties>
    </profile>
  </profiles>
  ...
</project>
これでmessageの値は、プロファイルを「profile1」としたときは「PROFILE MESSAGE 1」、「profile2」としたときは「PROFILE MESSAGE 2」となります。

Maven実行時にプロファイルを指定するには、コマンドラインにて「-P プロファイル名」を付与します。packageフェーズでプロファイル「profile1」を指定するときは、
mvn package -P profile1
となります。

echoでメッセージが切り替わることを確認する

MavenのAnt Pluginで、メッセージをechoで表示して切り替わることを確認します。
pom.xmlは次のようになります。フェーズcleanを実行すると、メッセージが表示される仕組みです。メッセージの内容はmessageプロパティを参照しています。
<project>
  ...
  <properties>
    <message>DEFAULT MESSAGE</message>
  </properties>
  
  <profiles>
    <profile>
      <id>profile1</id>
      <properties>
        <message>PROFILE MESSAGE 1</message>
      </properties>
    </profile>
    <profile>
      <id>profile2</id>
      <properties>
        <message>PROFILE MESSAGE 2</message>
      </properties>
    </profile>
  </profiles>
  
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <executions>
          <execution>
            <id>echo-message</id>
            <phase>clean</phase>
            <configuration>
              <tasks>
                <echo message="MESSAGE:${message}" />
              </tasks>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>
これを実行してみます。まずはプロファイル指定なし。プロファイル外で設定した値が表示されています。
>mvn clean
[INFO] Scanning for projects...
  ...
[INFO] --- maven-antrun-plugin:1.3:run (echo-message) @ mavenprofile ---
[INFO] Executing tasks
     [echo] MESSAGE:DEFAULT MESSAGE
[INFO] Executed tasks
  ...
続いてprofile1を指定した場合。profile1で設定した値が表示されています。
>mvn clean -P profile1
[INFO] Scanning for projects...
  ...
[INFO] --- maven-antrun-plugin:1.3:run (echo-message) @ mavenprofile ---
[INFO] Executing tasks
     [echo] MESSAGE:PROFILE MESSAGE 1
[INFO] Executed tasks
  ...
同様にprofile2。profile2で設定した値が表示されています。
>mvn clean -P profile2
[INFO] Scanning for projects...
  ...
[INFO] --- maven-antrun-plugin:1.3:run (echo-message) @ mavenprofile ---
[INFO] Executing tasks
     [echo] MESSAGE:PROFILE MESSAGE 2
[INFO] Executed tasks
  ...
これで、プロファイルによってプロパティ値を切り替えることができることが分かりました。
なお、プロファイルは同時に複数設定することができます。もし同じプロパティが複数のプロファイル(デフォルト含む)で定義されている場合、pom.xmlの一番最後に定義されたプロファイルが使用されるそうです。

依存関係を切り替えるには

切り替えたい依存関係の情報(groupId、artifactId、versionなど)をプロパティとして定義します。さらにこれらの定義をプロファイルにも持たせます。また、依存関係の定義では、プロパティを参照するようにします。
例えば、OpenCVのGPU対応モジュールと非GPU対応モジュールをそれぞれ切り替えたいとき、次のようにします(artifactIdなどは下記の値でローカルリポジトリにインストール済みであることが前提)。
<project>
  ...
  <properties>
    <opencv.groupId>org.opencv</opencv.groupId>
    <opencv.artifactId>opencv</opencv.artifactId>
    <opencv.version>3.4.0</opencv.version>
  </properties>
  ...
  <profiles>
    <profile>
      <id>opencv-non-gpu</id>
      <properties>
        <opencv.groupId>org.opencv</opencv.groupId>
        <opencv.artifactId>opencv</opencv.artifactId>
        <opencv.version>3.4.0</opencv.version>
      </properties>
    </profile>
    <profile>
      <id>opencv-gpu</id>
      <properties>
        <opencv.groupId>org.opencv</opencv.groupId>
        <opencv.artifactId>opencv-gpu</opencv.artifactId>
        <opencv.version>3.4.0</opencv.version>
      </properties>
    </profile>
  </profiles>
  ...
  <dependencies>
    <dependency>
      <groupId>${opencv.groupId}</groupId>
      <artifactId>${opencv.artifactId}</artifactId>
      <version>${opencv.version}</version>
    </dependency>
  </dependencies>
  ...
</project>
このようにすれば、Maven実行時のプロファイル指定で依存関係を切り替えることができます。

謝辞

今回参考にさせて頂いたサイトは下記の通りです。日付的に古めですが、分かりやすく参考になりました。