Как настроить NDK с плагином Android Gradle 0.7

Новый плагин Android gradle (0.7), похоже, содержит новую поддержку для NDK, но в документации почти нет упоминания об этом (единственная ссылка, которую я нашел, - это тест под названием ndkSanAngeles).

Похоже, что gradle ищет NDK, который я включил в свою PATH. Однако сбой проекта с помощью

  • Что пошло не так: Выполнение не выполнено для задачи ': OGLTests: compileDefaultFlavorDebugNdk'. NDK не настроен

Как настроить NDK в gradle?

Мой текущий build.gradle выглядит так:

task nativeLibsToJar(type: Zip, description: 'create a jar with native libs') {
    destinationDir file("$buildDir/native-libs")
    baseName 'native-libs'
    extension 'jar'
    from fileTree(dir: 'src/main/libs', include: '**/*.so')
    from fileTree(dir: 'src/main/libs', include: '**/gdb*')
    into 'lib/'
}

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

dependencies {
    compile fileTree(dir: "$buildDir/native-libs", include: '*.jar')
}

android {
    compileSdkVersion 19
    buildToolsVersion '19.0.0'

    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 19
        versionCode 1
        versionName "0.1"

    }
    buildTypes {
        release {
            runProguard false
        }
        debug {
           // jniDebugBuild true
            runProguard false
            debuggable true
        }
    }
    productFlavors {
        defaultFlavor {
            proguardFile 'proguard-rules.txt'
        }
    }
}

Спасибо.

Ответ 1

Нашел ответ. Включить ndk.dir=path/to/ndk в файл local.properties сделал трюк.

Update: В последних версиях Android Studio вы можете установить значение непосредственно в структуре Project > SDK.

Ответ 2

Просматривая код плагина gradle, я нашел следующее, что помогло мне использовать как NDK, так и готовые собственные библиотеки:

Чтобы просто связать в сборных национальных библиотеках, просто добавьте раздел ndk в свою задачу. Например, я добавил его ниже в productFlavors. AbiFilter - это имя папки, в которой хранятся библиотеки. AbiFilters означает, что обе библиотеки из списка, разделенные запятой, будут добавлены в ваш окончательный APK (так что вы могли бы теоретически иметь "armeabi", "armeabi-v7a", "x86" и "armaabi-v7a", mips "все в одном APK, а O/S выбирает поддерживаемую библиотеку lib при установке):

productFlavors {
    arm {
        ndk {
            abiFilters "armeabi", "armeabi-v7a"
        }
    }
    x86 {
        ndk {
            abiFilter "x86"
        }
    }
}

В этом примере сборка arm создаст APK с V5 и V7A arm libs в ней, а сборка x86 создаст APK с только x86 libs в ней. Это приведет к поиску собственных библиотек в каталоге jniLibs. Каталог jniLibs должен быть структурой как старый jni-каталог, т.е.:

[project]/[app]/src/main/jniLibs/armeabi/libmyNative.so
[project]/[app]/src/main/jniLibs/armeabi-v7a/libmyNative.so
[project]/[app]/src/main/jniLibs/x86/libmyNative.so

Затем вы можете загрузить его на Java следующим образом:

static
{
    loadLibrary("myNative");
}

Теперь скажем, что одна родная библиотека зависит от другой. Вы ДОЛЖНЫ (если установить минимальный API для API 17 или ниже) сначала загрузите зависимые библиотеки:

static
{
    loadLibrary("myDependency");
    loadLibrary("myNative");
}

Вы также можете поместить раздел ndk {} в свой defaultConfig или тип buildType (например, отладка или выпуск или что-то еще, что вы можете использовать). Например:

buildTypes {
    debug {
        ndk {
            abiFilters "armeabi", "armeabi-v7a"
        }
    }
}

Предварительно, я имею в виду загруженные вами сторонние библиотеки или библиотеку, которую вы создали, с помощью инструментальной цепи NDK или вашей собственной инструментальной привязки ARM (а не самой ndk-build script).

В API 18 они исправили давнюю архитектурную проблему, которая помешала загрузчику собственного загрузчика из "автоматически" загружать зависимости, потому что он не знал о каталоге приложения lib (соображения безопасности и т.д.). В API 18 и выше, если myNative зависит от myDependency выше, вы можете просто вызвать loadLibrary ( "myNative" ), и ОС будет обрабатывать загрузку myDependency. Не смейтесь об этом, хотя до тех пор, пока проникновение на рынок устройств, работающих под API 17 и ниже, не будет приемлемым для вас.


Чтобы явно указать Создать библиотеки NDK из источника в текущей версии Android Studio, вы можете сделать следующее:

Установите значение ndk.dir в local.properties, чтобы указать на дом NDK, как упоминалось ранее. Кто-нибудь знает, можете ли вы использовать env vars непосредственно в local.properties?:)

В файле build.gradle добавьте что-то подобное к вашей задаче (опять же, может быть defaultConfig, debug, release, productFlavor и т.д.):

ndk {
    moduleName "myNDKModule"
    stl "stlport_shared"
    ldLibs "log", "z", "m"
    cFlags "-I/some/include/path"
}

Это базовая структура с поддерживаемыми в настоящее время типами (moduleName, stl, ldLibs и cFlags). Я посмотрел и не нашел больше этого. Я полагаю, что с ldLibs возникает проблема, потому что он автоматически добавляет "-l" в начало каждого поля выше. Вы можете обмануть его, хотя (я должен был), говоря:   ldLibs "log -lz -lm -Wl, -whole-archive -l/path/to/someOtherLib -Wl, -no-whole-archive"

В этой строке вы просто помечаете в конце первого параметра, чтобы добавить параметры, которые не начинаются с -l, поэтому вы можете обойтись пока. В приведенном выше случае я связываю всю статическую библиотеку с модулем NDK для использования с Java. Я попросил разработчика Google добавить дополнительные функции, чтобы позволить это или даже возможность объединить ваш собственный файл Android.mk в процесс сборки NDK, но поскольку это все новое, может быть какое-то время.

В настоящее время независимо от того, что вы вставляете в build.gradle, вы удаляете каталог temp build и каждый раз его воссоздаете, поэтому, если вы не хотите загружать и изменять исходный код плагина gradle android plugin (что было бы интересно), есть некоторые "сделать должным", как это необходимо, чтобы ваш материал был скопирован в сборку. Андроид gradle script, который обеспечивает эту поддержку ndk, по существу генерирует файл Android.mk и строит с использованием системы NDK во временном каталоге.

Заблокировано в течение секунды. Имя модуля должно соответствовать файлу c или cpp в вашем проекте в каталоге jni, например:

[project]/[app]/src/main/jni/myNDKModule.cpp

Значение stl должно быть установлено в значение "stlport_shared" или "stlport_static", если вы хотите использовать библиотеки stlport для С++. Вы можете оставить stl, если вам не нужна расширенная поддержка С++. Помните, что Android поддерживает базовую поддержку С++ по умолчанию. Для других поддерживаемых библиотек С++ просмотрите свои рекомендации по документации NDK в загруженном вами NDK. Обратите внимание, что, установив его в stlport_shared здесь, gradle копирует libstlport_shared.so lib из вашего каталога NDK sources/cxx-stl/stlport/libs в ваши каталоги файлов APK lib. Он также обрабатывает путь включения в компилятор (технически gradle не делает все это, кроме системы сборки Android NDK). Поэтому не ставьте свою собственную копию stlport в каталог jniLibs.

Наконец, я думаю, что cFlags довольно очевиден.

Вы не можете установить ANDROID_NDK_HOME на Mac OSX (см. ниже), но из некоторых исследований, которые я сделал, возможно, это все еще работает на других ОС. Он будет удален.

Я хотел прокомментировать, но пока не имею репутации. Деннис, переменные среды игнорируются полностью, а не только переопределены. Фактически, вы не получаете никаких переменных среды. Из того, что я могу сказать, Android Studio IDE создает свою собственную среду с несколькими определенными переменными среды (проверьте System.getenv() и распечатайте ее из gradle script).

Я написал это как ошибку здесь, потому что использование env vars строит отлично из cmd-линии:
https://code.google.com/p/android/issues/detail?id=65213

но, как вы можете видеть, Google решил, что они не хотят, чтобы переменные среды использовались IDE вообще; Я все еще нахожусь на этом решении. Мне очень тяжело обновлять local.properties, указывая на абсолютные пути, которые могут быть загружены в мои сценарии gradle, которые я до сих пор не понял, как это сделать (но не так уж и тяжело). Это означает, что я либо заставляю членов моей команды использовать тот же путь, что и я, либо играть со ссылками, чтобы они все набирали их каждый раз, когда они вытягивали репо, или добавляли автоматизацию script. Я считаю, что это плохое решение, которое будет стоить времени для любых разработчиков, которые полагаются на env vars, которые могут быть небольшими на микроуровне, но огромными на макроуровне.

groundloop, я считаю, что вскоре IDE скоро будет обновлена ​​с возможностью добавления пути к папке NDK в ваш проект, и он автоматически сгенерирует файл local.properties из этого (по крайней мере, это не имело бы смысла, если бы они не думал об этом).

Более подробные примеры из Google приведены в следующих примерах (поиск jni или ndk): https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA


кросс-платформенных АПК с использованием NDK:

Наконец, есть недостаток в использовании gradle и не в состоянии предоставить ваш собственный файл Android.mk, чтобы вы могли ссылаться только на сторонние библиотеки из одной архитектуры на ваш NDK. Примечание. Я сказал "ссылка в". Вы можете создать модули NDK (имя модуля выше) в нескольких архитектурах с помощью команды "abiFilters", и они будут помещены в ваше приложение таким образом, чтобы один и тот же APK мог использоваться на нескольких архитектурах. Если вам нужно связать свои собственные сторонние библиотеки или даже иметь разные значения для cFlags в зависимости от вашей архитектуры, нет простого способа.

Я пробовал следующее, и, похоже, он работал сначала, но потом я узнал, что он просто строит NDK, добавляя все вместе из двух разделов ndk (или что-то в этом роде, он каким-то образом создал несколько архитектур библиотеки):

android {
    compileSdkVersion 23
    buildToolsVersion '23.0.1'
    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 28
        versionName "3.0"
    }
    buildTypes {
        def commonLibs = " -lfoo -lbar -lwhatever"
        def armV7LibsDir = "/whatever/armv7a/libs"
        def armX86LibsDir = "/whatever/x86/libs"
        def armV7IncDir = "/whatever/armv7a/include"
        def x86IncDir = "/whatever/x86/include"
        debug {
            ndk {
                cFlags = "-I" + armV7IncDir
                moduleName "myNativeCPPModule"
                stl "stlport_shared"
                abiFilter "armeabi-v7a"
                ldLibs "log -L" + armV7LibsDir + commonLibs
            }
            ndk {
                cFlags = "-I" + armX86IncDir
                moduleName "myNativeCPPModule"
                stl "stlport_shared"
                abiFilter "x86"
                ldLibs "log -L" + armX86LibsDir + commonLibs
            }
        }
    }
}

После многого горения, пытаясь создать толстую двоичную систему в чистой усадьбе с gradle и родными сторонними библиотеками, я, наконец, пришел к выводу, что встроенная поддержка многопользовательской архитектуры APK для Google Play - это лучший путь к в любом случае, создайте отдельные APK для каждой архитектуры.

Итак, я создал несколько типов buildTypes, без продуктов и добавил следующий код для генерации кода версии для каждого типа.

// This is somewhat nasty, but we need to put a "2" in front of all ARMEABI-V7A builds, a "3" in front of 64-bit ARM, etc.
// Google Play chooses the best APK based on version code, so if a device supports both X86 and
// ARM, it will choose the X86 APK (preferred because Inky ARM running on an X86 with Houdini ARM Emulator crashes in our case)
android.applicationVariants.all { variant ->
    if (variant.buildType.name.equals('release')) {
        variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debug')) {
        variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debugArmV8a')) {
        variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('releaseArmV8a')) {
        variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debugMips')) {
        variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('releaseMips')) {
        variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debugMips64')) {
        variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('releaseMips64')) {
        variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debugX86')) {
        variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('releaseX86')) {
        variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('debugX86_64')) {
        variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode
    } else if (variant.buildType.name.equals('releaseX86_64')) {
        variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode
    }
}

Теперь вам нужно установить значение для версииCode в свой объект defaultConfig, как и обычно, и это добавляет его в конец строки версии для конкретной архитектуры, основанной на типе сборки. Строка версии остается неизменной для всех сборок, но изменяет код, чтобы обеспечить порядок приоритета от ARM вплоть до X86_64. Это немного хакерское или жестко закодированное, но оно выполняет свою работу. Обратите внимание, что это дает вам до 999 версий, поэтому, если вам нужно больше, умножьте числа выше на 10, не уверен, какое максимальное значение вы можете ввести для кода версии.

В моем случае у нас довольно сложная система сборки. Мы строим CPython для 9 архитектур, 3 из которых - Android, затем создаем кучу наших собственных библиотек и объединяем их все вместе в одну библиотеку для каждой архитектуры. Мы используем инструменты построения командной строки ndk, automake и python для сборки всего, а не файлов Android.mk. Окончательные библиотеки затем соединяются в один файл cpp интерфейса JNI (называемый myNativeCPPModule выше). Одним нажатием кнопки, и все построено сразу, очень приятно Android Studio.

Ответ 3

вы также можете установить переменную среды ANDROID_NDK_HOME

Ответ 4

Я потратил много времени на config ndk в build.gradle. Я получил хороший блог, который решил мою проблему.

Ответ 5

Как уже отмечалось, добавление ndk.dir = в local.properties помогает. Интересно, что я обнаружил, что local.properties переопределяет любое значение, установленное для переменной среды ANDROID_NDK_HOME, , даже если у вас нет ndk.dir, настроенного в local.properties. (по крайней мере, с плагином gradle android v 0.7.3).

Это путается, так как Android Studio может перезаписывать local.properties и, похоже, не дает способа настроить ndk.dir: (

Ответ 6

Студия Android предлагает включить путь к ndk в local.properties