Фон
В настоящее время я работаю над проектом Android, который содержит достаточное количество нативного кода.Нативный код построен для нескольких ABI.По разным причинам собственный код до сих пор создавался с использованием пользовательской задачи Gradle, которая вызывает ndk-build.cmd
.
. Сейчас я нахожусь в процессе изменения этого значения, чтобы использовать externalNativeBuild
для интеграции с NDK,и списки CMake вместо старых Android.mk
файлов.Кажется, это лучший поддерживаемый / документированный способ ведения дел.
Проблема
Одна приятная вещь в старом методе ndk-build
заключалась в том, что он собиралдля нескольких ABI параллельно - например, версия ARM и версия библиотеки x86 могут быть построены параллельно.
Это особенно полезно в моем случае, потому что я должен использовать сторонний инструмент на этапе соединения, который 1) занимает очень много времени (минут), и 2) в основном однопоточный.Следовательно, параллельное построение библиотеки для нескольких ABI помогло значительно сократить время сборки.
При сборке с помощью CMake / Ninja я не могу воспроизвести это поведение.Ninja должен распараллеливать сборки по умолчанию, и он вполне может это делать для данного ABI (т.е. компилировать несколько исходных файлов параллельно для одного ABI ).Но из того, что я могу сказать, он никогда не начинает сборку библиотеки для другого ABI, пока не завершит сборку для текущего ABI.
Вопрос
При использованииCMake / Ninja через externalNativeBuild
, есть ли способ сказать системе сборки, что я хочу, чтобы она создавала свой собственный код для нескольких ABI параллельно?
Пример
Минимальный пример, демонстрирующий это, так же прост, как шаблон «Новый проект» в Android Studio, то есть что-то вроде:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
add_library(native-lib SHARED
src/main/cpp/native-lib.cpp )
app /build.gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.cmakemultiabis"
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
ndk {
abiFilters 'armeabi-v7a', 'x86'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
А затем сборка с помощью, например, gradlew assembleRelease
даст вам:
> Task :app:externalNativeBuildRelease
Build native-lib x86
[1/2] Building CXX object CMakeFiles/native-lib.dir/src/main/cpp/native-lib.cpp.o
[2/2] Linking CXX shared library ..\..\..\..\build\intermediates\cmake\release\obj\x86\libnative-lib.so
Build native-lib armeabi-v7a
[1/2] Building CXX object CMakeFiles/native-lib.dir/src/main/cpp/native-lib.cpp.o
[2/2] Linking CXX shared library ..\..\..\..\build\intermediates\cmake\release\obj\armeabi-v7a\libnative-lib.so
Из этого вывода может быть не очевидно, что две библиотеки построены последовательно,но это становится очевидным, если у вас есть что-то, что занимает заметное количество времени для связывания.
Я пробовал это как с версиями CMake / Ninja, которые вы можете загрузить с помощью SDK Manager, так и с некоторыми более новымиверсии (CMake 3.12.3,Ниндзя 1.8.2) и видел одинаковое поведение в обоих случаях.