android - 使用 cmake for Android 时将共享对象复制到 jniLibs

标签 android c++ gradle cmake

我最近开始将我的 Android 构建从 Ant 转移到 Gradle,然后我想对我的 C++ 代码使用 cmake。该构建目前运行良好,但在创建 aar 文件之前,没有共享对象被复制到它们需要的 jniLibs 文件夹(这是一个库项目,目前在 Windows 10 上构建)。

我查看了使用 ./gradlew assembleDebug 构建时正在运行的任务.它们是:

:app:preBuild UP-TO-DATE
:app:preDebugBuild UP-TO-DATE
:app:checkDebugManifest
:app:preDebugAndroidTestBuild UP-TO-DATE
:app:preDebugUnitTestBuild UP-TO-DATE
:app:preReleaseBuild UP-TO-DATE
:app:preReleaseUnitTestBuild UP-TO-DATE
:app:prepareComAndroidSupportAppcompatV72221Library UP-TO-DATE
:app:prepareComAndroidSupportSupportV42221Library UP-TO-DATE
:app:prepareDebugDependencies
:app:compileDebugAidl UP-TO-DATE
:app:compileLint UP-TO-DATE
:app:copyDebugLint UP-TO-DATE
:app:copyLibs
:app:compileDebugRenderscript UP-TO-DATE
:app:generateDebugBuildConfig UP-TO-DATE
:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources UP-TO-DATE
:app:mergeDebugResources UP-TO-DATE
:app:processDebugManifest UP-TO-DATE
:app:processDebugResources UP-TO-DATE
:app:generateDebugSources UP-TO-DATE
:app:incrementalDebugJavaCompilationSafeguard UP-TO-DATE
:app:compileDebugJavaWithJavac UP-TO-DATE
:app:extractDebugAnnotations UP-TO-DATE
:app:mergeDebugShaders UP-TO-DATE
:app:compileDebugShaders UP-TO-DATE
:app:generateDebugAssets UP-TO-DATE
:app:mergeDebugAssets UP-TO-DATE
:app:mergeDebugProguardFiles UP-TO-DATE
:app:packageDebugRenderscript UP-TO-DATE
:app:packageDebugResources UP-TO-DATE
:app:processDebugJavaRes UP-TO-DATE
:app:transformResourcesWithMergeJavaResForDebug UP-TO-DATE
:app:transformClassesAndResourcesWithSyncLibJarsForDebug UP-TO-DATE
:app:generateJsonModelDebug UP-TO-DATE
:app:externalNativeBuildDebug
  building E:\path\to\app\Android\app\.externalNativeBuild\cmake\debug\libs\x86\libApp.so
  building 
:app:mergeDebugJniLibFolders
:app:transformNative_libsWithMergeJniLibsForDebug
:app:transformNative_libsWithSyncJniLibsForDebug
:app:bundleDebug
:app:compileDebugSources
:app:assembleDebug

一切都很好,但是文件夹 app/src/main/jniLibs为空,因此没有共享对象被复制到 aar 文件。那么接下来就是如何添加 gradle 构建步骤来实际复制这些文件的问题。事实证明这很难做到。

方法一:

尝试创建一个复制任务在 :app:bundleDebug 之前运行任务。为此,我做了几次尝试:

第一种可能:

task copyLibs(type: Copy, dependsOn: 'bundleDebug') {
    from ('.externalNativeBuild/cmake/debug/libs') {
        include '**/libApp.so'
    }
    into 'src/main/jniLibs'
}

第二种可能:

task copyLibs(type: Copy) {
    from ('.externalNativeBuild/cmake/debug/libs') {
        include '**/libApp.so'
    }
    into 'src/main/jniLibs'
}

tasks.whenTaskAdded { task ->
    if (task.name ==  'bundleDebug') {
        task.dependsOn copyLibs
    }
}

请注意路径是正确的,因为我可以通过 task copyLibs << 强制完成复制里面有一个复制步骤。这将在构建的第二次 运行时被复制,因为共享对象在第一次运行时不存在。 (因为任务是在开始时运行的。)

方法 2:

在 cmake 中复制。这是可行的,但不可取。因此我没有走这条路。

这是我的 build.gradle:

评论和更多信息在下面的评论中。

apply plugin: 'com.android.library'

// This task is run before everything else, so it does the copy on the *second* build,
// thereby copying the shared objects from the *previous* build.
//task copyLibs << {
//    copy {
//        from ('.externalNativeBuild/cmake/debug/libs') {
//            include '**/libApp.so'
//        }
//        from ('.externalNativeBuild/cmake/release/libs') {
//            include '**/libApp.so'
//        }
//        into 'src/main/jniLibs'
//        includeEmptyDirs = false
//    }
//}

// This is the task signature if tasks.whenTaskAdded below is *not* used. If both are
// used we get a circular dependency.
//task copyLibs(type: Copy, dependsOn: 'bundleDebug') {

task copyLibs(type: Copy) {
    from ('.externalNativeBuild/cmake/debug/libs') {
        include '**/libApp.so'
    }
    into 'src/main/jniLibs'
}

tasks.whenTaskAdded { task ->
    if (task.name ==  'bundleDebug') {}
        // This dependecy *is* set. This can be seen by using task copyLibs(type: Copy, dependsOn: 'bundleDebug')
        // together with this and get a circular dependency.
        task.dependsOn copyLibs
    }
}

// Prints:
// [task ':app:assemble', task ':app:assembleAndroidTest', task ':app:assembleDefault', task ':app:buildDependents',
// task ':app:buildNeeded', task ':app:check', task ':app:compileLint', task ':app:connectedCheck', task ':app:copyLibs',
// task ':app:deviceCheck', task ':app:extractProguardFiles', task ':app:lint', task ':app:preBuild', task ':app:sourceSets',
// task ':app:uninstallAll']
println(tasks)

android {
    compileSdkVersion 22
//    buildToolsVersion "22.0.1"
    buildToolsVersion "24.0.2" // Tried using latest for good measures

    defaultConfig {
        minSdkVersion 12
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"

        // Some defines to build certain architectures, e.g. with './gradlew -Ponly-x86 assembleDebug'. This is working.
        if (project.hasProperty('only-x86')) {
            ndkConfig.abiFilters = ["x86"] as Set<String>
        }
        else if (project.hasProperty('only-armeabi-v7a')) {
            ndkConfig.abiFilters = ["armeabi-v7a"] as Set<String>
        }
        else {
            ndkConfig.abiFilters = ["x86", "armeabi-v7a"] as Set<String>
        }

        externalNativeBuild {
            cmake {
                cppFlags    "-fexceptions", "-frtti", "-Wno-error"
                arguments   "-DANDROID_STL=gnustl_static"
            }
        }
    }

    buildTypes {
        release {
        }
        debug {
        }
    }

    externalNativeBuild {
        cmake {
            path '../../../CMakeLists.txt'
        }
        beforeEvaluate( println("I ma printed at the top.") )
        afterEvaluate { println("I am printed before compiling.") }
    }
}


dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.1'
}

task clean(type: Delete) {
    delete "${rootProject.buildDir}"
    delete "${project.buildDir}"
    delete "${project.projectDir}/.externalNativeBuild"
    delete fileTree(dir: "${project.projectDir}/src/main/jniLibs", include: '**/*.so')
}

其他信息:

  • cmake 版本:3.6.3155560
  • 如果共享对象位于 jniLibs 文件夹中,则会正确创建 aar 文件,因此除了复制步骤之外,构建实际上可以正常工作。

最佳答案

最后我想出了怎么做。要从某个地方包含构建的共享对象,我们需要在 build.gradle 的 android 部分使用 sourceSets.main.jniLibs.srcDirs。下面的示例直接从共享对象的 Android cmake 输出目录中获取(用于调试)。

例如:

android {
    ...
    defaultConfig {
        ...
    }

    buildTypes {
        release {
        }
        debug {
        }
    }

    sourceSets {
        main {
            // Bundle so files with the final apk.
            // NOTE: Currently bundles all shared objects in that directory.
            // It was not straightforward to exclude in Android sourceSets at the time of writing,
            // see https://code.google.com/p/android/issues/detail?id=64957
            jniLibs.srcDirs = ['.externalNativeBuild/cmake/debug/libs']
        }
    }

    externalNativeBuild {
        cmake {
            path '../../../CMakeLists.txt'
        }
    }
}

关于android - 使用 cmake for Android 时将共享对象复制到 jniLibs,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39613452/

相关文章:

android - 为了编写自定义gradle任务,我需要在项目中包含哪些文件?

Android VideoView重复

android - 如何在 (5'11) 等编辑文本中添加人物高度

android - 如何编辑 list Recycler 项目以及新选择的项目

c++ - 为什么此 c++ 代码在 Dev c++ 上运行而不在网站上运行?

Android Studio 导出库\SDK 不暴露代码

java - Android WebView滚动到底部

c++ - 意外标记附近出现语法错误 '('

c++ - 我如何使用 while 循环找到 GCD?模运算符如何工作?

eclipse - 如何在 Eclipse 中使用本地 gradle 项目作为另一个本地 gradle 项目的依赖项?