Как исключить дубликаты общих библиотек C (.so) в многопроектной сборке Android?

Я получаю конфликт «дубликаты файлов» при создании родительского проекта с двумя библиотечными модулями, которые используют одну и ту же общую библиотеку libc++_shared.so.

(ПРИМЕЧАНИЕ: пожалуйста, не считайте это «повторяющимся вопросом». Я прочитал несколько связанных сообщений, которые помогли мне зайти так далеко. Однако ни одно из сообщений не дало ответа, который работает в моем случае, связанном с Артефакты NDK.)

Сборка работала правильно, когда у меня был только 1 такой библиотечный модуль. Добавление второго библиотечного модуля теперь создает конфликт.

Рассмотрим следующую структуру проекта: 1 родительский проект, 2 «дочерних» проекта, но каждый проект расположен на одном уровне каталога (т.е. не вложен иерархически).

ProjectA/   (Parent)
    LibraryModuleA1/
        build/exploded-aar/com.package.name/
            LibraryModuleB1/<version>/jni/armeabi-v7a/libc++_shared.so
            LibraryModuleC1/<version>/jni/armeabi-v7a/libc++_shared.so
        build.gradle    (bgA1)
    Test_APK_Module A1T/
        build.gradle    (bgA1T)
    build.gradle    (bgPA)

ProjectB/
    LibraryModuleB1/  (Uses NDK)
        build/lib/armeabi-v7a/libc++_shared.so
        build.gradle    (bgB1)
    build.gradle    (bgPB)

ProjectC/
    LibraryModuleC1/  (Uses NDK)
        build/lib/armeabi-v7a/libc++_shared.so
        build.gradle    (bgC1)
    build.gradle    (bgPC)

Библиотечный модуль A1 зависит от обоих библиотечных модулей B1 и C1.
A1 -> B1
A1 -> C1

Проекты B и C имеют код на основе NDK и корректно строятся/тестируются. Оба зависят от общей библиотеки libc++_shared.so.

Однако при сборке проекта А я получаю следующую ошибку во время задачи :LibraryModuleA1:packageDebugTest:

Error: duplicate files during packaging of APK   /ProjectA/LibraryModuleA1/build/apk/LibraryModuleA1-debug-test-unaligned.apk
    Path in archive: lib/armeabi-v7a/libc++_shared.so
    Origin 1:  /ProjectA/LibraryModuleA1/build/exploded-aar/com.package.name/LibraryModuleB1/<version>/jni/armeabi-v7a/libc++_shared.so
    Origin 2:  /ProjectA/LibraryModuleA1/build/exploded-aar/com.package.name/LibraryModuleC1/<version>/jni/armeabi-v7a/libc++_shared.so
You can ignore those files in your build.gradle:
    android {
      packagingOptions {
        exclude 'lib/armeabi-v7a/libc++_shared.so'
      }
    }

* What went wrong:
Execution failed for task ':LibraryModuleA1:packageDebugTest'.
> Duplicate files copied in APK lib/armeabi-v7a/libc++_shared.so
    File 1: /ProjectA/LibraryModuleA1/build/exploded-aar/com.package.name/LibraryModuleC1/<version>/jni/armeabi-v7a/libc++_shared.so
    File 2: /ProjectA/LibraryModuleA1/build/exploded-aar/com.package.name/LibraryModuleC1/<version>/jni/armeabi-v7a/libc++_shared.so

:LibraryModuleA1:packageDebugTest FAILED

Что я уже пробовал

  1. Я попытался добавить предлагаемое закрытие в свой файл build.gradle, но в какой файл build.gradle мне его добавить? Я добавил замыкание к bgA1, bgB1 и bgC1 (по одному), но безуспешно.
  2. Предлагаемое закрытие говорит использовать exclude 'lib/armeabi-v7a/libc++_shared.so'. Каждый «дочерний» библиотечный модуль создает файл libc++_shared.so по пути build/lib. Однако я заметил, что модуль родительской библиотеки копирует файл libc++_shared.so под jni/armeabi-v7a/libc++_shared.so внутри структуры каталогов build/exploded-aar. (См. выше) Должно ли замыкание читаться как exclude 'jni/armeabi-v7a/libc++_shared.so (т. е. jni вместо lib)?
  3. Поскольку я использую плагин Gradle 0.9.1, я попытался использовать pickFirst вместо exclude, но это тоже не увенчалось успехом.

Может ли кто-нибудь помочь определить, как мне настроить закрытие `packagingOptions' для моего данного случая?

Спасибо за помощь!


person Jack Straw    schedule 19.06.2014    source источник


Ответы (1)


Я столкнулся с той же проблемой, и мне не повезло с exclude или pickFirst. Поэтому я использовал несколько уродливый обходной путь. Идея состоит в том, чтобы создать папку «native-libs» в каталоге сборки основного проекта, скопировать туда все необходимые файлы *.so из проектов библиотеки ndk, а затем указать системе сборки упаковать эти библиотеки в apk.

В моем основном проекте (проекте приложения) я явно определяю список модулей, содержащих коды ndk, от которых я завишу.

// Ndk stuff. We have to explicitely manage our NDK dependencies
ext.jniProjects = [project(':ndklib1'), project(':ndklib2'), project(':ndklib3')]
apply from: '../depend_ndk.gradle'

И затем «depend_ndk.gradle» — это внешний скрипт gradle, который содержит

// Build helper for projects that depends on a native library with a NDK part
// Define the list of ndk library you depend on in project main file :
//   ext.jniProjects = [project(':ndklib1')]
//   apply from : 'depend_ndk.gradle'
buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.12.+'
    }
}
import com.android.build.gradle.tasks.PackageApplication

// As a workaround, we create a new 'native-libs' folder in the current project and
// copy all the .so we depend on into it
def ndkLibsDir = new File(buildDir, 'native-libs')
ndkLibsDir.mkdir()

task copyDependingNativeLibs(type: Copy) {
    // Doc for copy http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.Copy.html
    println 'jniProjects ' + jniProjects
    jniProjects.each {
        from(new File(it.buildDir, 'native-libs')) {
            include '**/*.so'
        }
    }
    into ndkLibsDir
}

tasks.withType(PackageApplication) { pkgTask ->
    pkgTask.jniFolders = new HashSet<File>()
    pkgTask.jniFolders.add(ndkLibsDir)
    pkgTask.dependsOn copyDependingNativeLibs
}
person Julien    schedule 27.09.2014
comment
Как запускается PackageApplication? в приложении - я не вижу его в отладочных сборках... поэтому мои отладочные APK включают... Спасибо. - person RoundSparrow hilltx; 30.09.2014
comment
Вы правы, у меня проблемы с автоматическим запуском copyDependingNativeLibs. Я думаю, что добавлять его в качестве зависимости для pkgTask неправильно. У меня действительно не было времени, чтобы найти решение, поэтому пока я просто запускаю './gradlew app:copyDependingNativeLibs', когда меняю какую-то нативную часть. - person Julien; 09.10.2014