Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android new build system (gradle) and aspectj

In Google IO the new build system gradle is announced to replace ant. My project is using aspectj and I would like to use it in my project. I couldn't figure out some variables to get it working. I don't find android.* output classpath there. Anyone can help?

Here is my current build.gradle:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.4'
    }
}
apply plugin: 'android'
sourceCompatibility = 1.6

configurations {
    ajc
} 

dependencies {
    compile fileTree(dir: 'libs', includes: ['*.jar'])
    ajc files('build-tools/aspectjtools.jar', 'libs/aspectjrt.jar')
}

android {
    compileSdkVersion 16
    buildToolsVersion "17"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 16
    }
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        instrumentTest.setRoot('test')
    }
}

gradle.projectsEvaluated {
    compileJava.doLast {
        tasks.compileAspectJ.execute()
    }
    println 'lalalalala'
}

task compileAspectJ {

    ant.taskdef(resource: "org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties",
        classpath: configurations.ajc.asPath)
    ant.iajc(source: sourceCompatibility, target: sourceCompatibility,
        destDir: "?????????????????????",
        classpath: "????????????????????????????") {

        sourceroots{
            android.sourceSets.main.java.srcDirs.each {
                pathelement(location: it.absolutePath)
            }  
        }  
    }  
}

This is the old ant code that works very well: http://code.google.com/p/anymemo/source/browse/custom_rules.xml

Edit:

Updated the build.gradle according to the first answer.However I the iajc does not seem to recognize all the libraries and complain the classes in the libraries not found

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.5.+'
    }
}

apply plugin: 'android'
sourceCompatibility = 1.6
targetCompatibility = 1.6

repositories {
    mavenCentral()
}

android {
    compileSdkVersion 18
    buildToolsVersion "18.1.0"

    defaultConfig {
        minSdkVersion 9
        targetSdkVersion 16
    }
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        // Move the tests to tests/java, tests/res, etc...
        instrumentTest.setRoot('tests')

        // Move the build types to build-types/<type>
        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
        // This moves them out of them default location under src/<type>/... which would
        // conflict with src/ being used by the main source set.
        // Adding new build types or product flavors should be accompanied
        // by a similar customization.
        debug.setRoot('build-types/debug')
        release.setRoot('build-types/release')
    }
}

configurations {
    ajc
    aspects
    ajInpath
}

ext.aspectjVersion = '1.7.3'

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')

    ajc "org.aspectj:aspectjtools:${aspectjVersion}"
    compile "org.aspectj:aspectjrt:${aspectjVersion}"
    compile 'com.android.support:appcompat-v7:18.0.0'
}

android.applicationVariants.all { variant ->

    variant.javaCompile.doLast {
        def androidSdk = android.adbExe.parent + "/../platforms/" + android.compileSdkVersion + "/android.jar"
    println 'AAAAAAAAAAAAAAAAA: ' + androidSdk

        def iajcClasspath = configurations.compile.asPath + ":" + androidSdk
        configurations.compile.dependencies.each { dep ->
            if(dep.hasProperty("dependencyProject")) {
                iajcClasspath += ":" + dep.dependencyProject.buildDir + "/bundles/release/classes.jar"
            }
        }
    println 'BBBBBBBBBBBBBB : ' + iajcClasspath 

        ant.taskdef( resource:"org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.ajc.asPath)
        ant.iajc (
                source:sourceCompatibility,
                target:targetCompatibility,
                destDir:"${project.buildDir}/classes/${variant.dirName}",
                maxmem:"512m",
                fork:"true",
                aspectPath:configurations.aspects.asPath,
                inpath:configurations.ajInpath.asPath,
                sourceRootCopyFilter:"**/.svn/*,**/*.java",
                classpath:iajcClasspath
        ){
            sourceroots{
                android.sourceSets.main.java.srcDirs.each{
                    pathelement(location:it.absolutePath)
                }
                pathelement(location:"${project.buildDir}/source/r/${variant.dirName}")
            }
        }
    }
}

Errors:

1 [error] The method onPrepareOptionsMenu(Menu) of type FingerPaint must override or impl[3780/18642]
rtype method
[ant:iajc] public boolean onPrepareOptionsMenu(Menu menu) {
[ant:iajc]                ^^^^^^^^^^^^^^^^^^^^^^^^^^
[ant:iajc] /home/liberty/mp/android/AnyMemo/src/com/example/android/apis/graphics/FingerPaint.java:21
2 [error] The method onPrepareOptionsMenu(Menu) is undefined for the type GraphicsActivity
[ant:iajc] super.onPrepareOptionsMenu(menu);
[ant:iajc]       ^^^^^^^^^^^
[ant:iajc] /home/liberty/mp/android/AnyMemo/src/com/example/android/apis/graphics/FingerPaint.java:21
7 [error] The method onOptionsItemSelected(MenuItem) of type FingerPaint must override or implement a
 supertype method
[ant:iajc] public boolean onOptionsItemSelected(MenuItem item) {
[ant:iajc]                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[ant:iajc] /home/liberty/mp/android/AnyMemo/src/com/example/android/apis/graphics/FingerPaint.java:22
8 [error] The constructor ColorPickerDialog(FingerPaint, FingerPaint, int) is undefined
[ant:iajc] new ColorPickerDialog(this, this, mPaint.getColor()).show();
[ant:iajc] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[ant:iajc] /home/liberty/mp/android/AnyMemo/src/com/example/android/apis/graphics/FingerPaint.java:25
4 [error] The method onOptionsItemSelected(MenuItem) is undefined for the type GraphicsActivity
[ant:iajc] return super.onOptionsItemSelected(item);
[ant:iajc]              ^^^^^^^^^^^^
[ant:iajc] /home/liberty/mp/android/AnyMemo/src/com/example/android/apis/graphics/FingerPaint.java:25
8 [error] The method getDefaultSharedPreferences(Context) in the type PreferenceManager is not applic
able for the arguments (FingerPaint)
[ant:iajc] SharedPreferences shre = PreferenceManager.getDefaultSharedPreferences(this);
[ant:iajc]              

EDIT: This the final build.gradle file that works for my project: https://code.google.com/p/anymemo/source/browse/build.gradle?spec=svnf85aaa4b2d78c62876d0e1f6c3e28252bf03f820&r=f85aaa4b2d78c62876d0e1f6c3e28252bf03f820

like image 996
Liberty Avatar asked Jun 21 '13 22:06

Liberty


1 Answers

I also wanted to use aspectj with gradle and Android Studio, and I finally got it working, but I still have some hand-written paths that I'd like to replace with more generic gradle options.

Edit: I replaced every hard-coded absolute paths with gradle based alternatives, so this solution does not depend any more on a given platform or user name. However, it still uses relative paths that could change from an IDE to an other or in further releases of Android Studio. Also, I'm not really satisfied with the way I find the android.jar.

I first load aspectj:

configurations {
ajc
aspects
ajInpath
}

ext.aspectjVersion = '1.7.3'

dependencies {
    compile project(":LibTest")

    ajc "org.aspectj:aspectjtools:${aspectjVersion}"
    compile "org.aspectj:aspectjrt:${aspectjVersion}"
    compile 'com.android.support:appcompat-v7:18.0.0'
}

And I then add a task that will run after the JavaCompile task of the current variant:

android.applicationVariants.all { variant ->

    variant.javaCompile.doLast {
        def androidSdk = android.adbExe.parent + "/../platforms/" + android.compileSdkVersion + "/android.jar"

        def iajcClasspath = configurations.compile.asPath + ";" + androidSdk
        configurations.compile.dependencies.each { dep ->
            if(dep.hasProperty("dependencyProject")) {
                iajcClasspath += ":" + dep.dependencyProject.buildDir + "/bundles/release/classes.jar"
            }
        }

        ant.taskdef( resource:"org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.ajc.asPath)
        ant.iajc (
                source:sourceCompatibility,
                target:targetCompatibility,
                destDir:"${project.buildDir}/classes/${variant.dirName}",
                maxmem:"512m",
                fork:"true",
                aspectPath:configurations.aspects.asPath,
                inpath:configurations.ajInpath.asPath,
                sourceRootCopyFilter:"**/.svn/*,**/*.java",
                classpath:iajcClasspath
        ){
            sourceroots{
                android.sourceSets.main.java.srcDirs.each{
                    pathelement(location:it.absolutePath)
                }
                pathelement(location:"${project.buildDir}/source/r/${variant.dirName}")
            }
        }
    }
}

Wherever I use ${variant.dirName}, it will be replaced by either "debug" or "release" according to the current build configuration.

Adding android.jar to the classpath is required to compile Android specific classes, and the line pathelement(location:"${project.buildDir}/source/r/${variant.dirName}") is require to use classes from the auto-generated R.java file.

Edit: The iterations over the project dependencies to build iajcClasspath let you use classes from your libraries projects. configurations.compile.asPath already contains a reference to your apklib (aar file), which is actually a zip containing both the jar and the resources of the library. Iajc doesn't recognize these files as it, but there is a bundle directory containing the classes.jar for your library under the build directory. I use a relative path with "release" hard-coded in it, because the library has a different variant than the main project in my situation, so I can't use ${variant.dirName} here.

Here is the complete build.gradle file:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.5.+'
    }
}
apply plugin: 'android'

repositories {
    mavenCentral()
}

android {
    compileSdkVersion 18
    buildToolsVersion "18.1.0"

    defaultConfig {
        minSdkVersion 7
        targetSdkVersion 18
    }
}

configurations {
    ajc
    aspects
    ajInpath
}

ext.aspectjVersion = '1.7.3'

dependencies {
    compile project(":LibTest")

    ajc "org.aspectj:aspectjtools:${aspectjVersion}"
    compile "org.aspectj:aspectjrt:${aspectjVersion}"
    compile 'com.android.support:appcompat-v7:18.0.0'
}

android.applicationVariants.all { variant ->

    variant.javaCompile.doLast {
        def androidSdk = android.adbExe.parent + "/../platforms/" + android.compileSdkVersion + "/android.jar"

        def iajcClasspath = configurations.compile.asPath + ";" + androidSdk
        configurations.compile.dependencies.each { dep ->
            if(dep.hasProperty("dependencyProject")) {
                iajcClasspath += ":" + dep.dependencyProject.buildDir + "/bundles/release/classes.jar"
            }
        }

        ant.taskdef( resource:"org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.ajc.asPath)
        ant.iajc (
                source:sourceCompatibility,
                target:targetCompatibility,
                destDir:"${project.buildDir}/classes/${variant.dirName}",
                maxmem:"512m",
                fork:"true",
                aspectPath:configurations.aspects.asPath,
                inpath:configurations.ajInpath.asPath,
                sourceRootCopyFilter:"**/.svn/*,**/*.java",
                classpath:iajcClasspath
        ){
            sourceroots{
                android.sourceSets.main.java.srcDirs.each{
                    pathelement(location:it.absolutePath)
                }
                pathelement(location:"${project.buildDir}/source/r/${variant.dirName}")
            }
        }
    }
}
like image 62
aegar Avatar answered Sep 21 '22 16:09

aegar