CircleCI 2.0 Android Build in Android Docker Image fails both on Cloud and CLI

android

#1

I have the following in my CircleCI 2.0 config in the Android project code-base.

version: 2

jobs:
    build:

    environment:
        TERM: dumb
        MAX_HEAP_SIZE: 8g

        ANDROID_COMPILE_VERSION: 27
        ANDROID_TARGET_VERSION: 27
        ANDROID_MIN_VERSION: 21
        ANDROID_SUPPORT_VERSION: 27.1.1
        KOTLIN_VERSION: 1.2.70

        JVM_OPTS: -Xmx$MAX_HEAP_SIZE -Xms2g -XX:MaxPermSize=5g

    working_directory: ~/workSpace

    branches:
        only:
            - develop
            - release/<*>
            - feature/circle_ci_cd
        ignore:
            - bugfix/<*>
            - refactor/<*>

    docker:
        - image: circleci/android:api-$ANDROID_TARGET_VERSION-alpha

#    java:
#      version: oraclejdk8

#     ####### Other Docker Images, if need be ############
#    dependencies:
#      pre:
#        - $SDK_MANAGER --install "tools" && yes | $SDK_MANAGER --licenses
#        - $SDK_MANAGER --install "platforms;android-$ANDROID_TARGET_VERSION" && yes | $SDK_MANAGER --licenses
#        - $SDK_MANAGER --install "platform-tools" && yes | $SDK_MANAGER --licenses
#        - $SDK_MANAGER --install "extras;android;m2repository" && yes | $SDK_MANAGER --licenses
#        - $SDK_MANAGER --install "extras;google;m2repository" && yes | $SDK_MANAGER --licenses
#        - $SDK_MANAGER --install "extras;google;google_play_services" && yes | $SDK_MANAGER --licenses

    steps:
        - checkout

        - run:
            name: "Pull Submodules"
            command: |
                git submodule init
                git submodule sync
                git submodule update --remote

        - run:
            name: "Android SDK Properties"
            command: |
                sed -i "s/compileSdkVersion=*.*/compileSdkVersion=$ANDROID_COMPILE_VERSION/" gradle.properties
                sed -i "s/targetSdkVersion=*.*/targetSdkVersion=$ANDROID_TARGET_VERSION/"  gradle.properties
                sed -i "s/minSdkVersion=*.*/minSdkVersion=$ANDROID_MIN_VERSION/" gradle.properties
                sed -i "s/supportVersion=*.*/supportVersion=$ANDROID_SUPPORT_VERSION/" gradle.properties
                sed -i "s/kotlinVersion=*.*/kotlinVersion=$KOTLIN_VERSION/" gradle.properties
                sed -i "s/versionCode=*.*/versionCode=${CIRCLE_BUILD_NUM:-1}/" gradle.properties

#      ######## Other Docker Images, if need be ###########
#      - run:
#          name: "Update Android"
#          command: $SDK_MANAGER --update && yes | $SDK_MANAGER --licenses

        - run:
            name: "Clean local.properties"
            command: rm -rf local.properties || true

        - run:
            name: Chmod permissions #if permission for Gradlew Dependencies fail, use this.
            command: chmod +x ./gradlew

        - restore_cache:
            key: jars-{{ checksum "build.gradle" }}-{{ checksum  "app/build.gradle" }}

        - run:
            name: Assemble
            command: ./gradlew clean assemble

        - save_cache:
            paths:
              - ${CIRCLE_WORKING_DIRECTORY}/.gradle
              - ${ANDROID_SDK_ROOT}
            key: jars-{{ checksum "build.gradle" }}-{{ checksum  "app/build.gradle" }}
#      - store_artifacts:
#          path: app/build/reports
#          destination: reports
#      - store_test_results:
#          path: app/build/test-results
# See https://circleci.com/docs/2.0/deployment-integrations/ for deploy examples

Few other properties in the gradle.properties file

dexMaxHeapSize=8g
org.gradle.jvmargs=-Xms2g -Xmx4g -XX:MaxPermSize=2g -XX:+HeapDumpOnOutOfMemoryError -XX:ReservedCodeCacheSize=2g -Dfile.encoding=UTF-8
org.gradle.configureondemand=false
org.gradle.caching=true
org.gradle.daemon=true
org.gradle.parallel=true
kotlin.incremental=true
kotlin.compiler.execution.strategy=in-process
kotlin.incremental.usePreciseJavaTracking=true
android.enableBuildCache=true
android.enableD8=true
android.enableR8=true

As you may have already noticed, I am injecting a dex compiler Option javaMaxHeapSize as a gradle-property even.

Gradle wrapper version is as follows.

 https\://services.gradle.org/distributions/gradle-4.10.1-all.zip

Following is build-variants configuration.

flavorDimensions "product", "store"

productFlavors {
    free { dimension "product" }
    paid { dimension "product" }
    google { dimension "store" }
    amazon { dimension "store" }
}

Development machine is MacOS High Sierra: 10.13.6, 3.5 GHz Intel Core i5 CPU, 32 GB 2400 MHz DDR4 RAM.

Here’s what’s happenning.

The order of build-variants is chronological, as per the given flavor-dimensions, therefore following eight are created by gradle in the exact order.

  • freeAmazonDebug
  • freeAmazonRelease
  • freeGoogleDebug
  • freeGoogleRelease
  • paidAmazonDebug
  • paidAmazonRelease
  • paidGoogleDebug
  • paidGoogleRelease

Apparently, all Debug builds are generated first, followed by Release Builds. Each of those build-variants will have a task-dependency list. Based on the task-dependency, this is what’s happenning.

  • kaptFreeAmazonDebugKotlin executes without error.
  • kaptFreeGoogleDebugKotlin fails with the following stack-trace but does not abruptly kill the build.

Compilation with Kotlin compile daemon was not successful java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is: java.io.EOFException at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:236) at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:161) at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:227) at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:179) at com.sun.proxy.$Proxy106.compile(Unknown Source) at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.nonIncrementalCompilationWithDaemon(GradleKotlinCompilerRunner.kt:256) at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.compileWithDaemon(GradleKotlinCompilerRunner.kt:219) at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.compileWithDaemonOrFallback(GradleKotlinCompilerRunner.kt:166) at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.compileWithDaemonOrFallback(GradleKotlinCompilerRunner.kt:63) at org.jetbrains.kotlin.compilerRunner.KotlinCompilerRunner.runCompiler(KotlinCompilerRunner.kt:133) at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.runJvmCompiler(GradleKotlinCompilerRunner.kt:119) at org.jetbrains.kotlin.gradle.internal.KaptWithKotlincTask.compile(KaptWithKotlincTask.kt:71) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73) at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:46) at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:39) at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:26) at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:801) at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:768) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:131) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:300) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:292) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:174) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:120) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:99) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:77) at org.gradle.api.internal.tasks.execution.OutputDirectoryCreatingTaskExecuter.execute(OutputDirectoryCreatingTaskExecuter.java:51) at org.gradle.api.internal.tasks.execution.SkipCachedTaskExecuter.execute(SkipCachedTaskExecuter.java:105) at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:59) at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54) at org.gradle.api.internal.tasks.execution.ResolveBuildCacheKeyExecuter.execute(ResolveBuildCacheKeyExecuter.java:79) at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:59) at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101) at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44) at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:91) at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62) at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:59) at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54) at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43) at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.run(EventFiringTaskExecuter.java:51) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:300) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:292) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:174) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:46) at org.gradle.execution.taskgraph.LocalTaskInfoExecutor.execute(LocalTaskInfoExecutor.java:42) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:277) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:262) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:135) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:130) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.execute(DefaultTaskPlanExecutor.java:200) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.executeWithWork(DefaultTaskPlanExecutor.java:191) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.run(DefaultTaskPlanExecutor.java:130) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55) at java.lang.Thread.run(Thread.java:748) Caused by: java.io.EOFException at java.io.DataInputStream.readByte(DataInputStream.java:267) at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:222) … 65 more Unable to clear jar cache after compilation, maybe daemon is already down: java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is: java.net.ConnectException: Connection refused (Connection refused) Could not connect to kotlin daemon. Using fallback strategy.

  • kaptPaidAmazonDebugKotlin fails with the above exact same stacktrace, and also abruptly kills the gradle build.

I have tried every possible combination of the gradle options and java options and docker options that I could think of, and I could try and find on the Internet, but nothing to the rescue.

These failures happen only in CircleCI 2.0 on the cloud or with CircleCI CLI, with the given Docker-image. The gradle builds locally are stable with the command-line

./gradlew clean assemble

or

./gradlew :app:clean :app:assemble

Any pointers to a solution will be appreciated.


#2

Resolved. Atleast locally on CircleCI CLI. Haven’t tested yet on CircleCI Cloud, shouldn’t be any different.

Upgraded to Gradle 4.10.2, Android-Gradle plugin 3.2.1, and Kotlin-Android 1.3.0.

Removed all GRADLE_OPTS from gradle.properties file.

Changed the JVM_OPTS to _JAVA_OPTS in the CircleCi Config YAML file to use -Xmx2048m only, and no -Xms either.

My Main Project Module build.gradle file, I put the following -

android {
    kapt {
        useBuildCache true
    }
    dexOptions {
        preDexLibraries true
    }
}

That was all it took to resolve.

Thank you for looking into this though.