GitHubのPrivateリポジトリのライブラリを参照する2

# 前置き

以前、こんなエントリーを書いたのですが、もう少し調査して簡単な方法や複雑なライブラリ構成のときにどうすればいいか等が分かったので、書き直しました。

[GitHubのPrivateリポジトリのライブラリを参照する](http://qiita.com/kasa_le/items/3438411640645928754c)

上記の方法だと、**publishNonDefault=true**に対応出来ていませんでしたが、こちらは対応出来る方法になっています。

# A. 1ライブラリ1プロジェクトの場合

プロジェクト内に1ライブラリモジュールしかない、シンプルな構成のプロジェクトの場合です。

## ライブラリ側

### 1. ライブラリプロジェクト下にgradle.propertiesを配置して以下を記述

```groovy:gradle.properties
# mavenリポジトリ名
mavenRepository=maven-repo
# maven出力フォルダのプロジェクト相対パス(最後が必ず”mavenリポジトリ名”と一致していること)
mavenPublishDir=../maven-repo
# maven groupId
mavenGroupId=jp.co.sample.group
```

もちろん、上記の値は全て適宜、変更して下さい。

### 2. ライブラリプロジェクト下にmavenupload.gradleを配置して以下を記述

```groovy:mavenupload.gradle
def repo = new File(rootDir, mavenPublishDir)
def groupId = mavenGroupId
def libName = project.name
def versionName = android.defaultConfig.versionName

// maven push

apply plugin: 'maven'
uploadArchives {
    repositories {
        mavenDeployer {
            repository url: "file://${repo.absolutePath}"
            pom.version =  "${versionName}"// version
            pom.groupId = "${groupId}"    // グループ名
            pom.artifactId = "${libName}" // ライブラリ名
        }
    }
}
```

### 3. ライブラリプロジェクトのbuild.gradleに追記

- 先頭に追加。（ライブラリプロジェクトの作成方法によってはすでに記述されている場合も有り）

```groovy:lib/build.gradle
buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'

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

- android{}内に追記。

```groovy:lib/build.gradle
android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"
...
    defaultPublishConfig 'release' // 任意
    publishNonDefault true // 任意
}
```

**`publishNonDefault=true`がtrueかどうかは関係が無いので、自由に設定して下さい。**

- 末尾に追加。

```groovy:lib/build.gradle
apply from: '../mavenupload.gradle'
```

参考：最終build.gradle

```groovy:libA/build.gradle
apply plugin: 'com.android.library'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
    publishNonDefault true
}

dependencies {
    compile 'com.android.support:support-v4:23.3.0'
}

apply from: '../mavenupload.gradle'
```

### 4. mavenにpublishするタスクを実行する

必要があればバージョンアップすること(依存している全てのプロジェクトが影響を受けるので要注意！)

GradleTasksの中から、**uploadArchives**というタスクを見つけてダブルクリックするか、ライブラリプロジェクト下で以下を実行する。

```
$> ./gradlew uploadArchives 
```

### 5. GitHubにcommit & Pushする

コマンドなら以下。

```
$ git add -A
$ git commit -m 'libA build'  // コメントは随時変更してください
$ git push
```

## アプリ側(ライブラリ使用側)

[git-repo](https://github.com/layerhq/gradle-git-repo-plugin)を使います。

### 1. GitHubにSSH Key(公開鍵)を登録する

個人アカウントのSettingsでやります。
SSH Keyの作り方や、設定方法はあちこちにあるのでググって下さい。

### 2. アプリのルートプロジェクトのbuild.gradleに追記

app/下のbuild.gradleではなく、その上の階層にあるファイルです。

```groovy:root/build.gradle
repositories {
        mavenCentral()
}
dependencies {
        classpath group: 'com.layer', name: 'gradle-git-repo-plugin', version: '2.0.2'
}
```

### 3. アプリのbuild.gradleに追記

app/下のbuild.gradleです。

- dependenciesの上に追加。

```groovy:app/build.gradle

apply plugin: 'git-repo'

repositories {
    github("GitHubユーザー名", "リポジトリ名", "ブランチ", "mavenディレクトリ名")
}
```

- dependenciesを追加。  
ライブラリ側が`publishNonDefault=true`にしているかどうかで変わります。  

**publishNonDefault=false(または未設定)の場合**

```groovy:app/build.gradle
dependencies {
...
    compile 'グループID:ライブラリ名:1.0.0'
}
```

参考：最終build.gradle

```groovy:app/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "sample.jp.co.cy.samplelib"
        minSdkVersion 19
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

apply plugin: 'git-repo'

repositories {
    github("myGitHubName", "libRepositoryName", "master", "maven-repo")
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.1.0'
    testCompile 'junit:junit:4.12'
    compile 'jp.co.sample.group:libA:1.0.0'
}
```

**publishNonDefault=trueの場合**  

アプリ側の設定だけ、false(または未設定)の場合と異なり、dependencies部分を以下のようにします。 

```groovy:app/build.gradle
dependencies {
...
    debugCompile('グループID:ライブラリ名:1.0.0:debug@aar')
    releaseCompile('グループID:ライブラリ名:1.0.0:release@aar')
}
```

参考：最終build.gradle  

```groovy:app/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "sample.jp.co.cy.samplelib"
        minSdkVersion 19
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

apply plugin: 'git-repo'

repositories {
    github("myGitHubName", "libRepositoryName", "master", "maven-repo")
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.1.0'
    testCompile 'junit:junit:4.12'
    debugCompile('jp.co.sample.group:libA:1.0.0:debug@aar')
    releaseCompile('jp.co.sample.group:libA:1.0.0:release@aar')
}
```

# B. 1プロジェクトに複数のライブラリがある場合

プロジェクト内にライブラリモジュールが複数ある場合です。それぞれ単独で参照されるだけのライブラリを寄せ集めただけのプロジェクトなら、**A**の方法で出来ます。

今回は、以下のような構成で、libAがsubA,subBに依存しているような場合を検討します。

```
+ Root(checkout フォルダ)
 + MyLibraries
  + subA
  + subB
  + libA
  + sampleApp
```

- sampleAppは、ライブラリ開発のための動作確認用サンプルアプリモジュール。
- sampleAppは、libAに依存します。
- libAは、subAをbuildTypeに依存して参照します。
- sampleAppは、libAをbuildTypeに依存して参照します。

通常、同一プロジェクト内のモジュール参照だと、dependenciesは以下のような記述になっているかと思います。  

```groovy:libA/build.gradle
dependencies{
    developDebugCompile project(path: ':subA', configuration: 'debug')
    developReleaseCompile project(path: ':subA', configuration: 'release')
    developCompile project(':subB')
}
```

```groovy:sampleApp/build.gradle
dependencies{
    developDebugCompile project(path: ':libA', configuration: 'debug')
    developReleaseCompile project(path: ':libA', configuration: 'release')
}
```

## ライブラリ側

一筋縄ではいきません。libAやsampleAppのdependenciesをいじることが出来ないからです。
**git-repo**を使用した書き方に変えてしまうと、ライブラリ内をデバッグ実行が出来ないのと、subAやsubBを編集して動作確認したいときに、maven-repoディレクトリを、いちいちコミット・pushする必要が生じてしまうからです。  
productFlavorsでdependenciesを切り替える方法も試しましたが、成果物の名称がかなりアレな感じになったのでやめました。  

ということで、今回は、以下の手段を執ることにします。

**開発用のプロジェクトと、publish用のプロジェクトを分ける**

少し手間ですが、この方が最終的に効率が良いと感じました。

### 1. publish用のフォルダを作成する

まず、subA,subB,libAをフォルダごとコピーして、以下のようなフォルダ構成にします。
ただし、MyLibraries自体がGradleプロジェクトですが、MyLibrariesForPublishは単純にフォルダを作成しただけで、Graldeプロジェクトとしては扱いません。AndroidStudioで**MyLibrariesForPublishを直接開かない**ように要注意です。  
また、この方法だと、開発プロジェクトの方でいくらversionCode/versionNameを上げても、こちらのpublish用プロジェクトの方を変更しないと反映されないので、そこが要注意ですね。

```
+ Root(checkout フォルダ)
 + MyLibraries
  + subA
  + subB
  + libA
  + sampleApp
 + MyLibrariesForPublish
  + subA
  + subB
  + libA
```

### 2. publish用のプロジェクトを作成する

subA, subB, libAの各srcフォルダをごっそり削除します。  
その後、各build.gradleのsourceSetのrootを、開発用プロジェクトのフォルダに指定してやります。

```groovy:subA/build.gradle
android {
    // 開発用プロジェクトのソースコードを参照させる
    sourceSets {
        main.setRoot("${project.rootDir.getAbsolutePath()}/../MyLibraries/subA/src/main")
        debug.setRoot("${project.rootDir.getAbsolutePath()}/../MyLibraries/subA/src/debug")
        release.setRoot("${project.rootDir.getAbsolutePath()}/../MyLibraries/subA/src/release")
    }
...
}
```

```groovy:subB/build.gradle
android {
    // 開発用プロジェクトのソースコードを参照させる
    sourceSets {
        main.setRoot("${project.rootDir.getAbsolutePath()}/../MyLibraries/subB/src/main")
    }
...
}
```

```groovy:libA/build.gradle
android {
    // 開発用プロジェクトのソースコードを参照させる
    sourceSets {
        main.setRoot("${project.rootDir.getAbsolutePath()}/../MyLibraries/libA/src/main")
        debug.setRoot("${project.rootDir.getAbsolutePath()}/../MyLibraries/libA/src/debug")
        release.setRoot("${project.rootDir.getAbsolutePath()}/../MyLibraries/libA/src/release")
    }
...
}
```

### 3. publish用ディレクトリ下にmavenupload.gradleを配置して以下を記述

内容は**A**の時と同じです。

```groovy:mavenupload.gradle
def repo = new File(rootDir, mavenPublishDir)
def groupId = mavenGroupId
def libName = project.name
def versionName = android.defaultConfig.versionName

// maven push

apply plugin: 'maven'
uploadArchives {
    repositories {
        mavenDeployer {
            repository url: "file://${repo.absolutePath}"
            pom.version =  "${versionName}"// version
            pom.groupId = "${groupId}"    // グループ名
            pom.artifactId = "${libName}" // ライブラリ名
        }
    }
}
```

### 4. 各ライブラリプロジェクト下に、gradle.propertiesを配置する

内容は**A**の時と同じなので、割愛します。  
subA,subB,libA全て同じ内容になるはずなので、ファイルのコピペで済むはずです。  

### 5. 各ライブラリのbuild.gradleを書き換える

subA, subB, libAの各build.gradleの**末尾**に以下を追記。

```groovy:build.gradle
apply from: '../mavenupload.gradle'
```


いったんこの時点で、AndroidStudioで各ライブラリプロジェクトを開いて、GradleSyncが通るか確認して下さい。

続いて、libAのdependenciesを書き換えます。ただし、Syncが最初は通らなくなるので注意して下さい。(maven-repoディレクトリをpushしないと、当然ですね・・・)  

```groovy:libA/build.gradle
dependencies{
    debugCompile('jp.co.sample.group:subA:1.0.0:debug@aar')
    releaseCompile('jp.co.sample.group:subA:1.0.0:release@aar')
    compile 'jp.co.sample.group:subB:1.0.2'
}
```

### 6. 各ライブラリのuploadArchivesを実行する

まず、subA, subBのuploadArchivesを実行します。  
コマンドラインでやる方が楽です。

```
$ cd subA
$ ./gradlew uploadArchives
$ cd subB
$ ./gradlew uploadArchives
```

続いてそれをcommit & pushします。

```
$ git add -A
$ git commit -m 'subA/subB build'  // コメントは随時変更してください
$ git push
```

その後で、libAのuploadArchivesを実行します。

```
$ cd libA
$ ./gradlew uploadArchives
```

最後にlibAの成果物をcommit & pushします。

```
$ git add -A
$ git commit -m 'libA build'  // コメントは随時変更してください
$ git push
```

これで、他のアプリプロジェクトからlibAを参照出来るようになりました。

この項目の流れは、うまくbashスクリプトとかにしておけば、一気に出来るなと思ったので、この手法をとることにしました。  

ここにさらにsubCがあって、libBはlibAとsubCを参照してて・・・なんて複雑な構成になっても、スクリプトに追記していけばpublishする手間はさほど変わらずに済むかな、という点で有効かなと考えています。(試してはいません)


## アプリ側

単純な1ライブラリ参照と異なって、dependenciesを以下のように書く必要があります。  
(以下はpublishNonDefault=trueの場合)  

```groovy:app/build.gradle
dependencies{
    debugCompile('jp.co.sample.group:libA:1.0.0:debug@aar'){
        transitive=true
    }
    releaseCompile('jp.co.sample.group:libA:1.0.0:release@aar'){
        transitive=true
    }
}
```

**transitive=true**がミソですね。  
これは、**「libAの依存も順次解決してね」**という指定みたいです。

# 参考

https://medium.com/@maxirosson/how-to-improve-your-android-library-2e8b9fd6c090#.4i1jeeorx

http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Sourcesets-and-Dependencies

https://github.com/dcendents/android-maven-gradle-plugin/issues/11