Определить LOCAL_SRC_FILES в ndk {} DSL

Я хотел бы знать, можно ли определить LOCAL_SRC_FILES в блоке gradle.build ndk {}.

В настоящее время я использую:

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

в моем верхнем уровне gradle.build файл.

Мой jni-модуль gradle.build файл выглядит следующим образом:

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-источниками есть код, предназначенный для разных платформ, а не только для Android, но также для iOS и WinRT.

Я немного неохотно перехожу к экспериментальному "com.android.tools.build: gradle-experimental:0.2.0", но если вышеупомянутый модуль решает проблему, я могу попробовать.

Я также не хотел бы использовать:

jni.srcDirs = []

и переопределить создание Android.mk и, таким образом, использовать мой собственный пользовательский, учитывая, что я не уверен, что я мог бы отлаживать С++ изначально из Android Studio после этого (я мог ошибаться здесь, хотя я определенно не эксперт Android Studios ndk plugin).

Большое спасибо заранее,

Манос

Ответ 1

С экспериментальным плагином 0.4.0 можно исключить файлы из NDK по шаблону, например.

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

Благодаря Paul Spark!

PS (спасибо rajveer): не пропустите Build/Clean после изменения exclude!

Старый ответ

К сожалению, это не поддерживается текущими плагинами gradle. Даже "экспериментальный" плагин позволяет добавлять каталоги. Я рекомендую сохранить традиционный Android.mk, который надежно выполняет эту работу.

Я также рекомендую не устанавливать jni.srcDirs = [], а сохранять ${jniSrc}, чтобы Android Studio отображал эти файлы для легкого доступа и подсветки синтаксиса. Если вы установите правильные значения cppFlags и cFlags, вы также сможете использовать перекрестные ссылки через заголовки.

Трюк состоит в том, чтобы отключить регулярные задачи сборки NDK и вместо этого ввести задачу buildNative:

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', но сопоставление задач отличается:

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

UPDATE

buildNative не создает отладочную установку. В частности, при запуске отладочной конфигурации Android Native, Android Studio жалуется, что не удалось найти папку, содержащую объектные файлы, с символами в приложении модуля.

Я предлагаю следующее обходное решение, которое я тестировал только в сценарии, когда родные источники разделены (по крайней мере) двумя каталогами: файлы, специфичные для Android (я буду называть их мостом JNI), находятся в отдельном каталоге, а отдых в другом месте. Обходной путь предполагает создание статической библиотеки с ndk-build и связывание ее с минимальным набором объектов, которые вытаскивают из этой библиотеки все необходимые символы.

Для простоты предположим, что файлы в Android ( Application.mk, Android.mk и "android-jni.cpp" находятся в каталоге ~/srcs/jni, в то время как независимые от платформы файлы находятся в ~/srcs и других подкаталогах.

Вот соответствующий фрагмент 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"]
        }
}
}

Файл ~/srcs/Android.mk может выглядеть следующим образом:

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 в сборке .gradle.

ОБНОВЛЕНИЕ 2

Возможно, это возможно, благодаря jforce, см. "Связать отдельный исходный исходный файл с проектом Android Studio"!