android – Access sdk.dir value in build.gradle after project evaluation-ThrowExceptions

Exception or error:

I have a custom task in my build.gradle file that does bytecode transformations on class files before getting dex’d that looks like this:

task droidcook(type: JavaExec) {
    main 'org.tsg.android.asm.Main'
}

afterEvaluate { project ->
    android.applicationVariants.each { variant ->
        variant.javaCompile.doLast {
            project.tasks.droidcook.configure {
                classpath variant.javaCompile.classpath
                classpath "build/classes/" + variant.dirName
                classpath sdk.dir + "/platforms/android-19/android.jar"
                classpath "compile-libs/droidcook.jar"
                args "build/classes/" + variant.dirName
                args "com.example"
                // args "-debug"
                // args "-asmifier"
            }
            project.tasks.droidcook.execute()
        }
    }
}

The issue with the above is the classpath sdk.dir + ... line where sdk.dir isn’t evaluated appropriately. To get this working, I currently have to hard code the path to the android.jar.

Bonus points if you can answer with an appropriate value to replace android-19 with for accessing a platform specific android.jar based on project configuration. 😀

How to solve:

I don’t see a published API to expose sdk.dir to your build file, so I just cribbed the code from https://android.googlesource.com/platform/tools/build/+/d69964104aed4cfae5052028b5c5e57580441ae8/gradle/src/main/groovy/com/android/build/gradle/internal/Sdk.groovy to read it manually. As for replacing android-19, if I read android.compileSdkVersion I get that exact string. I’m not sure that this is a published API either, so it may be subject to change and breakage in future versions of the Android Gradle plugin.

With that said, this works for me:

afterEvaluate {
    def rootDir = project.rootDir
    def localProperties = new File(rootDir, "local.properties")
    if (localProperties.exists()) {
        Properties properties = new Properties()
        localProperties.withInputStream { instr ->
            properties.load(instr)
        }
        def sdkDir = properties.getProperty('sdk.dir')
        def androidJarPath = sdkDir + "/platforms/" + android.compileSdkVersion + "/android.jar"
        print androidJarPath + "\n"
    }
}

If there’s no local.properties, this code is going to fail. If you care about that case, you can copy more code from Sdk.groovy to mimic its behavior in trying to find a reasonable default.

###

There’s a much simpler way, although it isn’t documented, so it probably isn’t officially supported.

println "${android.getSdkDirectory().getAbsolutePath()}"

Oddly this method only seems to be available for certain versions of the gradle plugin. It isn’t available in the commit linked above by Scott. If the above code doesn’t work, then try this:

println "${android.plugin.getSdkFolder().getAbsolutePath()}"

For completeness, here’s how to get the ndk.dir variable, too:

println "${android.plugin.getNdkFolder().getAbsolutePath()}"

###

As of android gradle plugin v2.1.2, this is what I use in my gradle script:

android.ndkDirectory.path
android.sdkDirectory.path

For example to explicitly launch ndk-build:

task ndkBuild(type: Exec) {
    commandLine android.ndkDirectory.path+'/ndk-build', '-C', file('src/main').absolutePath
}

###

Use:

String androidJar = android.getBootClasspath()[0]

Assuming you’re using SDK 21, this would set ‘<path-to-SDK>/platforms/android-21/android.jar’ into variable androidJar (the SDK version selection performed automatically).

The next step would obviously be replacing ‘classpath sdk.dir …’ etc. with:

classpath androidJar

Leave a Reply

Your email address will not be published. Required fields are marked *