I'm trying to build a Docker image for developing an Android app. I have an image that installs the JDK, Android SDK + NDK.
When I run the image and run the gradle wrapper (gradlew
), the wrapper downloads the latest Gradle, and then all the dependencies of my app, so I see messages like:
Download https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.1.4/gradle-3.1.4.pom
Download https://dl.google.com/dl/android/maven2/com/android/tools/build/builder/3.1.4/builder-3.1.4.pom
...
Download https://jcenter.bintray.com/org/jetbrains/kotlin/kotlin-stdlib-jre8/1.2.0/kotlin-stdlib-jre8-1.2.0.pom
Download https://jcenter.bintray.com/com/squareup/javawriter/2.5.0/javawriter-2.5.0.pom
...
Download https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.1.4/gradle-3.1.4.jar
Download https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle-core/3.1.4/gradle-core-3.1.4.jar
Etc.
Due to the nature of Docker containers, every time I run a new container, it will have to download all these dependencies again, as if it's the first time. (I could re-use a container, but I'd prefer everyone who uses this image not have to download these files; and using a fresh container every time is desirable for other reasons.) So I'd like to get them "baked into" the Docker image. What's the best way to do so?
One idea is to add instructions to the Dockerfile to copy in the source code of my project and do a build (and then remove the project files from the image). However, this seems tricky (we should ignore files that are not tracked by .git) and wasteful (why actually do an entire build; I just want these files downloaded and put into the appropriate location).
Another option is to do a build, get all those URLs out of the build output, figure out where they are supposed to go on disk (not sure how to do that), and then add instructions in the Dockerfile to manually copy them into place. But this seems like a lot of work, and will require manual updating when my app dependencies change.
It seems like there should be a way to copy in the gradle wrapper and a minimal set of gradle build files and ask it to "just restore dependencies", but I haven't been able to find such a target. Is there a way to do that?
I got the downloads cached by creating a cut-down, hacked-up version the build.gradle
script, and used that to trigger the downloads from my Dockerfile. So the directory with my Dockerfile also contained the gradle wrapper (gradlew script and associated gradle directory with the .jar in it), my minimal build.gradle file, and also a minimal dummy AndroidManifest.xml file which was needed to satisfy the android plugin.
(This is not ideal IMO, so I'd still love to hear if someone has a better way.)
From my Dockerfile, I trigger download like so:
# The following steps use some dummy build files to trigger gradle to download depencies,
# so that they will be available in the image.
COPY . /tmp/triggerGradleDownloads/
RUN cd /tmp/triggerGradleDownloads
&& ./gradlew --no-daemon --refresh-dependencies androidDependencies lint
&& rm -rf /tmp/triggerGradleDownloads
Content of ./src/main/AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<!-- we just need a package name in this file to satisfy the android plugin -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.dummy">
</manifest>
Content of my build.gradle file:
// This is a cut-down and hacked-together build script used only to trigger download of dependencies.
// From your Dockerfile, run "./gradlew --no-daemon --refresh-dependencies androidDependencies lint".
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 15
targetSdkVersion 28
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0-rc02'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:design:28.0.0-rc02'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}