使用Android NDK构建可执行文件

时间:2017-06-18 10:09:01

标签: android cmake

我正在尝试使用Android NDK构建可执行文件。为此,我创建了一个“Android库”项目,并在其目录“src / main / cpp”下包含了我的所有本机代码(cpp)文件。

在CMakeLists.txt文件中,我添加了以下内容。

cmake_minimum_required(VERSION 3.4.1)

set (TEST_SRC
    src/test/cpp/test.cpp
)

add_library (test-lib SHARED ${TEST_SRC})
add_executable(test-exec ${TEST_SRC})

当我执行构建时,我看到在我的构建输出目录(libtest-lib.so)中正确创建了库,但是,不会生成可执行文件。

我决定使用命令行(./gradlew clean build --info --debug)构建我的项目,并找到以下日志消息。

externalNativeBuildRelease: not building target test-exec because no 
targets are specified and library build output file extension isn't 
'so'.

似乎android NDK故意禁止我的可执行文件被构建:(有没有办法解决这个问题?或者我是否指定了错误的内容?

1 个答案:

答案 0 :(得分:2)

更新:由于我发布了这个答案,我找到了一个更清洁的解决方案。无需修改Android.mk个文件中的任何内容。以下是app/build.gradle的相关部分(原始gradle脚本属于syncopoli project。您可以找到此build.gradle脚本here的原始最新版本:

android {

    defaultConfig {
        // ...

        externalNativeBuild {
            ndkBuild {
                // I only want the following targets to be built
                targets 'rsync', 'ssh'
            }
        }

        ndk {
            // add whatever ABIs you want here
            abiFilters 'armeabi'
        }
    }

    externalNativeBuild {
        ndkBuild {
            // all my external sources are under src/main/jni
            path 'src/main/jni/Android.mk'
        }
    }
}

// this is the meat. It copies the binaries to appropriate location
// after externalNativeBuildRelease task is finished.
// you can change the paths to match where your binaries live and where
// you want them to go
gradle.taskGraph.afterTask { task ->
    if (task.name == "externalNativeBuildRelease") {
        def src = rootProject.file('app/build/intermediates/ndkBuild/release/obj/local/')
        def dst = rootProject.file('app/src/main/assets/')

        copy {
            from(src) {
                // the objs directory has all the .o files I don't care about
                exclude "**/objs"
            }

            into dst

            // this is purely for debugging purposes. it might come in handy
            eachFile {
                println "file = " + it.getPath()
            }
        }

    }
}

我的src/main/jni/Android.mk具有以下内容:

include $(call all-subdir-makefiles)

以前的答案

我遇到了这个问题并通过在gradle中创建额外的任务来手动解决它,手动调用ndk-build并将项目设置为依赖于这些任务。

这是app/build.gradle

的相关部分
import org.apache.tools.ant.taskdefs.condition.Os

Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())

task ndkBuild(type: Exec) {
    def ndkDirProperty = properties.getProperty('ndk.dir')
    def ndkDirPrefix = ndkDirProperty != null ? ndkDirProperty + '/' : ''

    def ndkBuildExt = Os.isFamily(Os.FAMILY_WINDOWS) ? ".cmd" : ""

    commandLine "${ndkDirPrefix}ndk-build${ndkBuildExt}", '-C', file('src/main').absolutePath,
            '-j', Runtime.runtime.availableProcessors()
}

tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn ndkBuild
}

// Cleanup task to remove previously generated binaries
task ndkClean(type: Exec) {
    def ndkDirProperty = properties.getProperty('ndk.dir')
    def ndkDirPrefix = ndkDirProperty != null ? ndkDirProperty + '/' : ''

    def ndkBuildExt = Os.isFamily(Os.FAMILY_WINDOWS) ? ".cmd" : ""

    commandLine "${ndkDirPrefix}ndk-build${ndkBuildExt}", '-C', file('src/main').absolutePath, 'clean'
}

tasks.withType(Delete) {
    cleanTask -> cleanTask.dependsOn ndkClean
}

我还为可执行文件进一步修改了Android.mk个文件,以便最终的可执行文件最终在assets/<target_arch_abi>/<executable

SAVED_NDK_APP_DST_DIR := $(NDK_APP_DST_DIR)
NDK_APP_DST_DIR := assets/$(TARGET_ARCH_ABI)
...LOCAL_MODULE/LOCAL_CFLAGS/LOCAL_etc...
include $(BUILD_EXECUTABLE)
NDK_APP_DST_DIR := $(SAVED_NDK_APP_DST_DIR)

现在,运行gradlew build会将可执行文件与其他可执行文件一起构建。

希望这有帮助。