日々是好日

プログラミングについてのあれこれ、ムダ知識など

PowerShellスクリプトでbuild.gradleをオレオレ記述化

Android Studioでプロジェクトを初期化したときに生成されるbuild.gradleをオレオレ記述化したのでメモ。

書き換える動機はこちらの記事が基です。

android.benigumo.com

今まで手動で直していましたが、あまりにもめんどうだったのでテンプレート化してみました。 なお筋肉で解決しているので、ps1ファイルに処理べた書きです。必要に応じ function に切り出し等行ってください。

どのように書き換えるか

プロジェクトレベルbuild.gradleのbuildScriptブロックに次の定数を定義して、ライブラリなどのバージョンを管理します。

#hoge#となっている部分は、Android Studioで初期化されたbuild.gradleから取得し、PowerShellスクリプトで置換する部分です。 マーキングみたいなものです。

// Project root build.gradle
buildScript {
  ext.buildConfig = [
    'minSdk'       : #minSdk#, //eg. 23
    'targetSdk'    : #targetSdk#, // 29
    'versionCode'  : 1,
    'versionName'  : "0.0.1",
    'compileSdk'   : #compileSdk# // 29
  ]

  ext.versions = [
    'kotlin'             : #kotlin#, // '1.3.72'
    'gradle'             : #gradle#, // '3.6.3'
    'appcompat'          : #appcompat#, // '1.1.0'
    'core_ktx'           : #core_ktx#, // '1.2.0'
    'constraintlayout'   : #constraintlayout#, // '1.1.3'
    'junit'              : #junit#, // '4.12'
    'junitX'             : #junitX#, // '1.1.1'
    'espresso_core'      : #espresso_core# // '3.2.0'
  ]

  // 上記のversionブロックを参照するように、バージョン部分を書き換え
  dependencies {
    classpath "com.android.tools.build:gradle:${versions.gradle}"
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
  }
}

次にappモジュールのbuild.gradle。

プロジェクトレベルbuild.gradleで定義したバージョンを参照するようにしつつ、kotlin-kaptbuildTypesdataBindingなど確実に追記するであろうステートメントを書き込む。

// app module build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' // kotlin-kaptを追加

android {  
  // バージョン部分をガシガシ修正
  compileSdkVersion buildConfig.compileSdk

  defaultConfig {
    applicationId 'work.kcs_labo.MyApp'
    minSdkVersion buildConfig.minSdk
    targetSdkVersion buildConfig.targetSdk
    versionCode buildConfig.versionCode
    versionName buildConfig.versionName
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    vectorDrawables.useSupportLibrary = true
  }

  // BuildConfig.DEBUG が常に false を返す仕様のため、BuildConfig.IS_DEBUG を定義しておく
  buildTypes {
    debug {
      buildConfigField("boolean", "IS_DEBUG", "true")
    }
    release {
      buildConfigField("boolean", "IS_DEBUG", "false")
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }

  // dataBindingも記述しておく
  dataBinding {
    enabled = true
  }

  testOptions {
    unitTests {
      includeAndroidResources = true
    }
  }
}

dependencies {
  // Auto generated from
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}"
  implementation "androidx.appcompat:appcompat:${versions.appcompat}"
  implementation "androidx.core:core-ktx:${versions.core_ktx}"
  implementation "androidx.constraintlayout:constraintlayout:${versions.constraintlayout}"
  testImplementation "junit:junit:${versions.junit}"
  androidTestImplementation "androidx.test.ext:junit:${versions.junitX}"
  androidTestImplementation "androidx.test.espresso:espresso-core:${versions.espresso_core}"
  // Auto generated end

  // Any libraries from
  // Any libraries end
}

使った技術

テンプレートとなるテキストファイルをコピーし、初期化された内容にあわせて修正します。

PowerShell スクリプト

init_gradle.ps1

init_gradle.ps1というファイルを作り、中に次のように記述しました。

# project rootのパスを引数に指定
Param ($root)

# 絶対パスの取得。たぶん絶対パスで渡されるとは思うけど。
$rootDir = Resolve-Path -Path $root

# プロジェクトレベルbuild.gradleのパス指定
$rootGradle = Join-Path $rootDir build.gradle

# appモジュールbuild.gradleのパス指定
$appGradle = Join-Path $rootDir app | Join-Path -ChildPath build.gradle

# build.gradleが存在するか
if ((Test-Path -Path $rootGradle) -and (Test-Path -Path $appGradle)) {

  # 処理内でオリジナルのbuild.gradleをorig.build.gradleとしてバックアップするため、
  # orig.build.gradleが存在したら処理を中止する
  if (!(Test-Path -Path (Join-Path $rootDir orig.build.gradle))
  -and !(Test-Path -Path (Join-Path $rootDir app | Join-Path -ChildPath orig.build.gradle))) {

    # プロジェクトレベルbuild.gradleの中身の取得
    $rootContent = Get-Content $rootGradle
    
    # 正規表現でkotlinとgradleのバージョン取得
    # シングルクォーテーションはバッククォートでエスケープ
    $kotlin = $rootContent | Select-String -Pattern "ext.kotlin_version = `'(.*?)`'" | foreach {$_.Matches.Groups[1].Value}
    $gradle = $rootContent | Select-String -Pattern "build:gradle:(.*?)`'" | foreach {$_.Matches.Groups[1].Value}

    # appモジュールbuild.gradleの中身の取得
    $appContent = Get-Content $appGradle

    # Sdkバージョンや依存ライブラリのバージョンを正規表現で取得
    $minSdk = $appContent | Select-String -Pattern "minSdkVersion ([0-9]{2})" | foreach {$_.Matches.Groups[1].Value}
    # ...
    $espresso_core = $appContent | Select-String -Pattern "espresso:espresso-core:(.*)'" | foreach {$_.Matches.Groups[1].Value}

    # オリジナルのbuild.gradleをorig.build.gradleにリネームしてバックアップ
    Rename-Item -Path $rootGradle -NewName orig.build.gradle

    # init_gradle.ps1と同じフォルダ($PSScriptRoot)に保存した root-build-template.gradleを取得(後述)
    $rootGradleTemplate = Join-Path $PSScriptRoot root-build-template.gradle

    Get-Content $rootGradleTemplate |
      foreach {$_.Replace("#kotlin#", "'$kotlin'")} |
      # 中略。マーキング下箇所を先ほど取得したバージョンに置換
      foreach {$_.Replace("#espresso_core#", "'$espresso_core'")} |
      # Encodingをデフォルト指定して保存
      Set-Content -Encoding Default $rootGradle

    # appモジュールbuild.gradleも同様に処理
    Rename-Item -Path $appGradle -NewName orig.build.gradle
    $appGradleTemplate = Join-Path $PSScriptRoot app-build-template.gradle

    # appモジュールbuild.gradle内で書換が必要なのは applicationId のみ
    Get-Content $appGradleTemplate |
      foreach {$_.Replace("#applicationId#", "'$applicationId'")} |
      Set-Content -Encoding Default $appGradle
  }
}

build-template.gradle

次の記述をしたhoge-build-template.gradleinit_gradle.ps1と同じフォルダに保存します。

// プロジェクトレベルbuild.gradle用
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
  ext.buildConfig = [
    'minSdk'       : #minSdk#,
    'targetSdk'    : #targetSdk#,
    'versionCode'  : 1,
    'versionName'  : "0.0.1",
    'compileSdk'   : #compileSdk#
  ]

  ext.versions = [
    'kotlin'             : #kotlin#,
    'gradle'             : #gradle#,
    'appcompat'          : #appcompat#,
    'core_ktx'           : #core_ktx#,
    'constraintlayout'   : #constraintlayout#,
    'junit'              : #junit#,
    'junitX'             : #junitX#,
    'espresso_core'      : #espresso_core# 
  ]

  repositories {
    google()
    jcenter()
  }

  dependencies {
    classpath "com.android.tools.build:gradle:${versions.gradle}"
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"

    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
  }
}

allprojects {
  repositories {
    google()
    jcenter()

  }
}

task clean(type: Delete) {
  delete rootProject.buildDir
}
// appモジュールbuild.gradle用
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
  compileSdkVersion buildConfig.compileSdk

  defaultConfig {
    applicationId #applicationId#
    minSdkVersion buildConfig.minSdk
    targetSdkVersion buildConfig.targetSdk
    versionCode buildConfig.versionCode
    versionName buildConfig.versionName
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    vectorDrawables.useSupportLibrary = true
  }

  buildTypes {
    debug {
      buildConfigField("boolean", "IS_DEBUG", "true")
    }
    release {
      buildConfigField("boolean", "IS_DEBUG", "false")
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }

  dataBinding {
    enabled = true
  }

  testOptions {
    unitTests {
      includeAndroidResources = true
    }
  }
}

dependencies {
  // Auto generated from
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}"
  implementation "androidx.appcompat:appcompat:${versions.appcompat}"
  implementation "androidx.core:core-ktx:${versions.core_ktx}"
  implementation "androidx.constraintlayout:constraintlayout:${versions.constraintlayout}"
  testImplementation "junit:junit:${versions.junit}"
  androidTestImplementation "androidx.test.ext:junit:${versions.junitX}"
  androidTestImplementation "androidx.test.espresso:espresso-core:${versions.espresso_core}"
  // Auto generated end

  // Any libraries from
  // Any libraries end
}

スクリプトの実行

上記ファイルが用意できたら、引数にAndroidプロジェクトのパスを指定してスクリプトを実行します。

$ PS> ./init_gradle.ps1 <Project-root-path>

build.gradleの中身を確認して終了です。