Подтвердить что ты не робот

Новая система сборки Android (gradle) и aspectj

В Google IO новая система сборки gradle объявляется заменой ant. Мой проект использует aspectj, и я хотел бы использовать его в своем проекте. Я не мог понять некоторые переменные, чтобы заставить его работать. Я не нашел android. * Output classpath. Кто может помочь?

Вот мой текущий 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)
            }  
        }  
    }  
}

Это старый код ant, который работает очень хорошо: http://code.google.com/p/anymemo/source/browse/custom_rules.xml

Edit:

Обновлен build.gradle в соответствии с первым ответом. Однако я не вижу, что iajc распознает все библиотеки и жалуется на классы в найденных библиотеках

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}")
            }
        }
    }
}

Ошибки:

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: Это окончательный файл build.gradle, который работает для моего проекта: https://code.google.com/p/anymemo/source/browse/build.gradle?spec=svnf85aaa4b2d78c62876d0e1f6c3e28252bf03f820&r=f85aaa4b2d78c62876d0e1f6c3e28252bf03f820

4b9b3361

Ответ 1

Я понял, что AAR не может использоваться в качестве библиотеки jar в моем коде. Если вы используете зависимости типа

compile 'com.android.support:appcompat-v7:18.0.0'

Вам нужно найти файл jar и добавить в путь к классам. Следующий код сделает это.

tree = fileTree(dir: "${project.buildDir}/exploded-bundles", include: '**/classes.jar')
tree.each { jarFile ->
    iajcClasspath += ":" + jarFile
}

Таким образом, весь раздел будет:

variant.javaCompile.doLast {
    // Find the android.jar and add to iajc classpath
    def androidSdk = android.adbExe.parent + "/../platforms/" + android.compileSdkVersion + "/android.jar"
    println 'Android SDK android.jar path: ' + androidSdk

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

    // handle aar dependencies pulled in by gradle (Android support library and etc)
    tree = fileTree(dir: "${project.buildDir}/exploded-bundles", include: '**/classes.jar')
    tree.each { jarFile ->
        iajcClasspath += ":" + jarFile
    }
        println 'Classpath for iajc: ' + iajcClasspath

        ant.taskdef( resource:"org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.ajc.asPath)

Для полного примера см. проект build.gradle для AnyMemo: https://code.google.com/p/anymemo/source/browse/build.gradle?spec=svnf85aaa4b2d78c62876d0e1f6c3e28252bf03f820&r=f85aaa4b2d78c62876d0e1f6c3e28252bf03f820

Ответ 2

Я также хотел использовать aspectj с gradle и Android Studio, и я, наконец, получил его работу, но у меня все еще есть некоторые рукописные пути, которые я бы хотел заменить более универсальными параметрами gradle.

Изменить: Я заменил все жестко закодированные абсолютные пути на gradle альтернативы, поэтому это решение больше не зависит от данной платформы или имени пользователя. Тем не менее, он по-прежнему использует относительные пути, которые могут измениться с IDE на другой или в дальнейших версиях Android Studio. Кроме того, я не очень доволен тем, как я нахожу android.jar.

Я сначала загружаю 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'
}

И затем я добавляю задачу, которая будет запускаться после задачи JavaCompile текущего варианта:

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}")
            }
        }
    }
}

Где бы я ни использовал ${variant.dirName}, он будет заменен либо "debug", либо "release" в соответствии с текущей конфигурацией сборки.

Добавление android.jar в путь к классам необходимо для компиляции специфических для Android классов, а для строки pathelement(location:"${project.buildDir}/source/r/${variant.dirName}") требуется использовать классы из автогенерированного файла R.java.

Изменить: Итерации над зависимостями проекта для сборки iajcClasspath позволяют использовать классы из проектов ваших библиотек. configurations.compile.asPath уже содержит ссылку на ваш apklib (файл aar), который на самом деле является почтовым индексом, содержащим как банку, так и ресурсы библиотеки. Iajc не распознает эти файлы как это, но есть каталог пакетов, содержащий класс .jar для вашей библиотеки в каталоге сборки. Я использую относительный путь с "высвобождением", жестко закодированным в нем, потому что библиотека имеет другой вариант, чем основной проект в моей ситуации, поэтому я не могу использовать ${variant.dirName} здесь.

Вот полный файл build.gradle:

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}")
            }
        }
    }
}

Ответ 3

Несмотря на то, что сценарии предыдущих ответов работают в большинстве ситуаций, они не охватывают некоторые проблемы использования Android с AspectJ и Gradle.

Мой тест состоял в том, чтобы создать проект библиотеки, который должен использоваться кем-либо через mavenCentral или мной как проект справочной библиотеки, и проект тестового приложения. Проект библиотеки - это тот, который имеет все аспекты, и прикладной тест пытается использовать эти аспекты. Предоставляя это как контекст, результирующая структура проекта была:

HEAD-Gradle
---LibraryProject
-------SomeAspects
---TestApplication
-------Uses-SomeAspects

Решения, которые я нашел, чтобы заставить его работать:

1- Для проектов библиотеки вы должны использовать

libraryVariants.all { variant -> 

вместо

android.applicationVariants.all { variant ->

2. Созданный dir был изменен для инструментов 19. + build для Android, так как это предлагается в одном комментарии (благодаря "Без Class" ), вы должны использовать вместо него взорванный-aar файл вместо вложенных пакетов определение древовидной переменной. From:

def tree = fileTree(dir: "${project.buildDir}/exploded-bundles", include: '**/classes.jar')

To:

def tree = fileTree(dir: "${project.buildDir}/exploded-aar", include: '**/classes.jar')

3. Последняя проблема, с которой я столкнулся при интеграции, заключалась в том, что если у вас есть проект библиотеки, на нем не были определены аспекты, определенные на дочернем проекте. Чтобы решить эту проблему, вы должны добавить class.jar вашей пользовательской библиотеки в конфигурацию компилятора aspectJ. Вы можете добиться этого, добавив в зависимости:

aspects project(":YourLibraryProject")

и также необходимо внести некоторые изменения в script, предоставленные в финале этого сообщения.

В настоящее время лучший script, я могу себе представить, что дает полную поддержку aspectj, используя даже библиотечные проекты:

Для зависимостей:

configurations {
    ajc
    aspects
    ajInpath
}

//Version of aspectj
def aspectjVersion = '1.8.+'
// The dependencies for this project
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    //Your dependencies to a custom library project
    compile project(":YourLibraryProject")
    aspects project(":YourLibraryProject")
    //Aspectj dependencies
    ajc "org.aspectj:aspectjtools:${aspectjVersion}"
    compile "org.aspectj:aspectjrt:${aspectjVersion}"
}

Работа компилятора script

android.applicationVariants.all { variant ->

variant.javaCompile.doLast {
    // Find the android.jar and add to iajc classpath
    def androidSdk = android.adbExe.parent + "/../platforms/" + android.compileSdkVersion + "/android.jar"

    def iajcClasspath = androidSdk + ":" + configurations.compile.asPath
    //This line and the fordward assignations allow the aspects support in the child project from the library project
    def customAspectsPath = configurations.aspects.asPath
    configurations.compile.dependencies.each { dep ->
        if(dep.hasProperty("dependencyProject")) {
            iajcClasspath += ":" + dep.dependencyProject.buildDir + "/bundles/${variant.buildType.name}/classes.jar"
            customAspectsPath += ":" + dep.dependencyProject.buildDir + "/bundles/${variant.buildType.name}/classes.jar"
        }
    }

    // handle aar dependencies pulled in by gradle (Android support library and etc)

    def tree = fileTree(dir: "${project.buildDir}/exploded-aar", include: '**/classes.jar')
    tree.each { jarFile ->
        iajcClasspath += ":" + jarFile
    }

    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:customAspectsPath,
            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}")
        }
    }
}
}

Помните, что если вы хотите запустить AspectJ в проекте library-child, вы должны также иметь этот script в build.gradle библиотеки.

Ответ 4

В случае использования Android Studio 0.8 или выше, кажется, что использование gradle 0.12. + необходимо.

В gradle 0,12. +, взорванные аары извлекаются в папке сборки, а не в папке с расширенным кодом.

Поэтому, чтобы обрабатывать зависимости aar, вы должны использовать этот код:

tree = fileTree(dir: "${project.buildDir}", include: '**/classes.jar')
tree.each { jarFile ->
    iajcClasspath += ":" + jarFile
}