Gradle:gradle.propertiesの値をどこでも利用できるようにする



gradle.propertiesは、他の設定ファイルとは異なり、マシンに一つだけなので、ここに様々な共通設定を書いておく、あるいは、より複雑な設定ファイルの場所を示しておくことができる。

さらに、公開ソフトウェアを開発中でも個人的なアカウントやパスワードは当然隠しておきたいのだが、これらをgradle.propertiesに記述しておくことによって、成果物から分離しておくことができる。

しかし、問題点がある。

プロパティとしてしか使えない

gradle.propertiesファイルはその名の通り、プロパティファイルとしてしか使用できない。

つまり、単純に名称/文字列値のペアしか格納ができない。文字列格納の際のクォーテーションも必要無い。

CENTRAL_REPOSITORY=http://10.8.92.1:8080/artifactory/libs-release
INHOUSE_REPOSITORY=http://10.8.92.1:8080/artifactory/libs-release-local
INHOUSE_USER=user
INHOUSE_PASSWORD=password
COMMON_GRADLE=C:/devel/common.gradle

この値が見える場所と見えない場所がある

この値は「プロジェクト」の中に格納されるようで、プロジェクトに無関係の部分から参照することができない。

例えば、当然のことながらclassのメソッド中では参照できない。

class Sample {
  def showit() {
    println COMMON_GRADLE // エラー
  }
}

Globalsのstatic変数に変更する

これでは使いづらいので、別の変数にそのまま移動させることにする。gradle.propertisの値はproject.propertiesに格納されるので、ここから値を取り出し、クラスのstaticフィールドにセットする。

これらのstaticフィールドとプロパティの名称が同一でなければいけない。

/* グローバル変数の定義
 * これらは、gradle.propertiesに定義されたものをそのまま引き継ぐ、ただし、値が事前に格納されている
 * ものについてはそのままいじらない
 */
class Globals {
  static String CENTRAL_REPOSITORY
  static String COMMON_GRADLE
  static String INHOUSE_REPOSITORY
  static String INHOUSE_USER
  static String INHOUSE_PASSWORD

  static void setup(Project project) {    
    Globals.getDeclaredFields().each {
      // フィールド名称を取得し、'_'、'$'で開始するもの、metaClassは無視する。
      String name = it.getName();
      if (name =~ /^[_\$].*|metaClass$/)
        return

      // 既に値が格納されているものは無視する
      it.setAccessible(true)
      if (it.get(null) != null) return;

      // project.propertiesから値を取得する。存在しなければエラー      
      String value = project.properties[name]
      if (value == null) throw new RuntimeException("No " + name + " in gradle.properties")

      // 値を格納する
      it.set(null, value)
    }
  }
}
Globals.setup(project)

クラス変数に格納後のアクセス

このようにすれば、このクラスを指定してアクセスが可能になる

class Sample {
  def showit() {
    println Globals.COMMON_GRADLE
  }
}

スクリプトファイルが分割されている場合

しかし、これでもアクセスができない場合がある。以下のようにスクリプトファイルが分離しているケースだ。

sample.gradle

// Globalsの定義とセットアップ
class Globals {
}
ext.Globals = Globals // これが必要

build.gradle

apply from: 'sample.gradle'

def ShowClosure = {
  println Globals.COMMON_GRADLE // OK
}

task Show {
  println Globals.COMMON_GRADLE // OK
}

class Sample {
  def showit() {
    println Globals.COMMON_GRADLE // NG
  }
}

この場合は、なぜかクラスメソッドからはアクセスできなくなる。

さんざん調べてみたのだが、このケースではどうやっても何らかの形で引数として与えてやる以外に、この値にアクセスする方法は今のところ見つからない。