定义NDK LOCAL_SRC_FILES {} DSL定义、NDK、DSL、LOCAL_SRC_FILES

2023-09-12 00:17:55 作者:哭泣的心

我想知道是否有可能来定义gradle.build NDK {}块LOCAL_SRC_FILES。

I would like to know whether it is possible to define LOCAL_SRC_FILES in gradle.build ndk {} block.

我目前使用的:

dependencies {
    classpath 'com.android.tools.build:gradle:1.3.0'
}

在我的顶层gradle.build文件。

in my top level gradle.build file.

我的JNI模块gradle.build文件是这样的:

My jni module gradle.build file looks like this:

apply plugin: 'com.android.library'

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

android {
    compileSdkVersion 11
    buildToolsVersion "22.0.1"

    def jniSrc = System.getProperty("user.home") + "/srcs/jni"

    defaultConfig {
        ndk {
            moduleName "core"
            stl "gnustl_shared"
            cFlags "-std=c++11"
        }
    }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
            jni.srcDirs = ["${jniSrc}"]
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            jniDebuggable true
        }
    }

    productFlavors {
        x86 {
            ndk {
                abiFilter "x86"
            }
        }
        arm {
            ndk {
                abiFilter "armeabi-v7a"
            }
        }
        mips {
            ndk {
                abiFilter "mips"
            }
        }
    }
}

我问的原因是,在我的JNI来源有code针对不同的平台,不只是Android的,而且iOS和WinRT的。

The reason I am asking is that under my jni sources there is code targeting different platforms, not just Android, but also iOS and WinRT.

我有点不情愿移植到实验性的com.android.tools.build:gradle-experimental:0.2.0',但如果上述模块解决了这个问题,我可以试试看。

I am a bit reluctant to migrate to experimental 'com.android.tools.build:gradle-experimental:0.2.0' but if the aforementioned module solves the problem I could give it a try.

我也不会喜欢使用:

jni.srcDirs = []

和覆盖创建Android.mk,从而用我自己的自定义的,因为我不知道我是否能调试C ++原生Android电子工作室以后(我可能是错在这里,虽然,我绝对不是专家用户Android的工作室NDK插件)。

and override the creation of Android.mk and thus use my own custom one, given that I am not sure if I could debug C++ natively from Android Studio thereafter (I could be wrong here though, I am definitely not an expert user of Android Studios ndk plugin).

在预先感谢,

马诺斯

推荐答案

不幸的是,这是不支持当前的摇篮插件。即使是实验的插件只允许添加的目录。我建议保持传统的 Android.mk 这做这件工作可靠。

Unfortunately this is not supported by current gradle plugins. Even the "experimental" plugin only allows to add directories. I recommend to keep the traditional Android.mk which does this job reliably.

我也建议不要设置 jni.srcDirs = [] ,而是让 $ {jniSrc} 来让机器人工作室显示这些文件为方便和语法高亮显示。如果将 CPPFLAGS CFLAGS 正确,你会通过头有交叉引用的全部功能了。

I also recommend not to set jni.srcDirs = [], but rather keep ${jniSrc} to let Android Studio display these files for easy access and syntax highlight. If you set cppFlags and cFlags correctly, you will have full power of cross referencing through headers, too.

关键是要禁用定期NDK构建任务,而是注入 buildNative 任务:

The trick is to disable the regular NDK build tasks, and inject a buildNative task instead:

def ndkBuild = android.ndkDirectory
import org.apache.tools.ant.taskdefs.condition.Os
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
    ndkBuild += '.cmd'
}

task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
    commandLine '$ndkBuild', 'NDK_PROJECT_PATH="$jniSrc/..'
}

task cleanNative(type: Exec, description: 'Clean JNI object files') {
    commandLine '$ndkBuild', 'clean', 'NDK_PROJECT_PATH="$jniSrc/..'
}

clean.dependsOn 'cleanNative'

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

tasks.all {
    task -> if (task.name.contains('compileDebugNdk') || task.name.contains('compileReleaseNdk')) task.enabled = false
}

对于 com.android.tools.build:gradle-experimental:0.2.0 ,但任务匹配类似的方法工作是不同的

Similar approach work for 'com.android.tools.build:gradle-experimental:0.2.0', but task matching is different:

tasks.all {
    task ->
        if (task.name.startsWith('compile') && task.name.contains('MainC')) {
            task.enabled = false
        }
        if (task.name.startsWith('link')) {
            task.enabled = false
        }
        if (task.name.endsWith("SharedLibrary") ) {
            task.dependsOn buildNative
        }
}

更新:

buildNative 不产生一个可调试安装。具体而言,在运行时的 Android原生调试配置,Android的工作室抱怨说,它的无法包含目标文件中的模块的应用程序的符号的定位文件夹。

Update:

buildNative does not produce a debuggable setup. Specifically, when running Android Native debug configuration, Android Studio complains that it Could not locate folder containing object files with symbols within module app.

我建议以下解决方法,这是我的情况下只测试时,本地资源被分割在(至少)两个目录:Android的特定的文件(我会打电话给他们的 JNI桥的)是在一个单独的目录,剩下的都是其他地方。解决方法包括建立一个静态库是 NDK建造和连接其与设定,这将拉动所有必要的符号,从库中最小的对象。

I suggest the following workaround, which I only tested in scenario when the native sources are split in (at least) two directories: the Android-specific files (I will call them JNI bridge) are in a separate directory, and the rest are elsewhere. The workaround involves building a static library with ndk-build and linking it with the minimal set of objects that will pull all necessary symbols from that library.

为了简单起见,我们假设了Android特定的文件( Application.mk Android.mk ,然后Android的jni.cpp在目录中〜/索马里红新月会/ JNI ,而独立于平台的文件都在〜/索马里红新月和它的其他子目录。

For simplicity, let us assume that the Android-specific files (Application.mk, Android.mk, and "android-jni.cpp" are in the directory ~/srcs/jni, while the platform-independent files are in ~/srcs and its other subdirectories.

下面是相关片段 build.gradle

def LOCAL_MODULE = "staticLib"
def appAbi = "armeabi-v7a"
def ndkOut = "build/intermediates/$LOCAL_MODULE"
def staticLibPath = "$ndkOut/local/$appAbi/lib${LOCAL_MODULE}.a"
task buildStaticLib(type: Exec, description: 'Compile Static lib via NDK') {
    commandLine "$ndkBuild", "$staticLibPath", "NDK_PROJECT_PATH=~/srcs", "NDK_OUT=$ndkOut", "APP_ABI=$appAbi", "APP_STL=gnustl_static"
}

tasks.all {
    task ->
        if (task.name.startsWith('link')) {
            task.dependsOn buildStaticLib
        }
}

model {
    android.ndk {
        moduleName = "hello-jni"
        abiFilters += "$appAbi".toString()
        ldFlags += "$staticLib".toString()
        ldLibs += "log"
        cppFlags += "-std=c++11"
    }

    android.sources {
        main.jni.source {
            srcDirs = ["~/srcs/jni"]
        }
}
}

在〜/索马里红新月会/ Android.mk 文件可能是这样的:

The ~/srcs/Android.mk file may look like this:

LOCAL_PATH := $(call my-dir)/..

include $(CLEAR_VARS)

LOCAL_MODULE    := staticLib
LOCAL_SRC_FILES := HelloJni.cpp

LOCAL_CPPFLAGS += -std=c++11

include $(BUILD_STATIC_LIBRARY)

这是 LOCAL_MODULE 在 Android.mk ,以适合您使用的名称 LOCAL_MODULE 在 build.gradle

It is important for LOCAL_MODULE in Android.mk to fit the name you use for LOCAL_MODULE in build.gradle.

更新2

这仍是可能的,这要感谢的 jforce 的,请参阅Link个别本地源文件到Android Studio项目!

It may still be possible, thanks to jforce, see "Link individual native source file to Android Studio project"!

更新3

使用实验插件,能够排除来自NDK文件建立由图案,例如

With experimental plugin, it is possible to exclude files from NDK build by pattern, e.g.

android.sources {
        main.jni.source {
            srcDirs = ["~/srcs/jni"]
            exclude "**/win.cpp"
        }
}

由于保罗星火!