/ android

The pains of building Android React-Native from source

I have a project employing React-Native. I'm building the RN module from source, so my project has the ReactAndroid module as a dependency.

I was trying to upgrade the project to Android gradle build tools >=2.3.2, so I could use intant-run:

buildscript {
    repositories {
        jcenter()
        mavenLocal()
        maven { url 'https://maven.fabric.io/public' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.2'
        ...
    }
}

This change forced me to upgrade gradle itself from 3.1 to 3.5 (latest stable).
Doing so opened pandora's hell box.

No such property: sdkHandler for class: com.android.build.gradle.AppPlugin

Upgrading gradle to 3.5 and trying to build the project from scratch produced the following gradle error from the RN module:

No such property: sdkHandler for class: com.android.build.gradle.AppPlugin

The fix

This was easy. It seemed that starting with gradle 3.3, the build script looks for the environment variable ANDROID_NDK for the NDK location. Not being able to locate the NDK directory causes gradle to miss some mandatory properties, such as sdkHandler.

So all I had to do was set ANDROID_NDK correctly:

ANDROID_NDK=C:\dev\google\android-sdk\ndk-bundle\

Could not get unknown property 'repositoryUrl' for project

Building the project now produces the another gradle error:

Could not get unknown property 'repositoryUrl' for project ':ReactAndroid' of type org.gradle.api.Project.

Reserching the issue

I did manage to find a GitHub pull request thread where Facebook's own team members are lamenting about the issue here. However they themselves didn't seem to like their fix, so who am I to argue.

The error pointed to the this line at the React-Native module's release.gradle:

def getRepositoryUrl() {
    return hasProperty('repositoryUrl')  ? property('repositoryUrl') : 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
}

Strangely enough, the problem is that hasProperty('repositoryUrl') returns true, while property('repositoryUrl') causes the error.

On gradle 3.1, hasProperty('repositoryUrl') returns false.

Apparently, in gradle 3.5 hasProperty() does return true in cases where the property is indeed missing: when it still has a getter. In our case the getter is this overriding method that the error points to:

def getRepositoryUrl() {...}

This is vaguely explained here.

There is however another method of checking for properties, which ignores getters, named findProperty.

The fix

So the fix was to change the following block from release.gradle:

def getRepositoryUrl() {
    return hasProperty('repositoryUrl') ? property('repositoryUrl') : 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
}

def getRepositoryUsername() {
    return hasProperty('repositoryUsername') ? property('repositoryUsername') : ''
}

def getRepositoryPassword() {
    return hasProperty('repositoryPassword') ? property('repositoryPassword') : ''
}

To this:

def getRepositoryUrl() {
    return findProperty('repositoryUrl') != null ? property('repositoryUrl') : 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
}

def getRepositoryUsername() {
    return findProperty('repositoryUsername') !=null ? property('repositoryUsername') : ''
}

def getRepositoryPassword() {
    return findProperty('repositoryPassword') != null ? property('repositoryPassword') : ''
}

Now my RN project finally compiles again.