CMake

Android NDK از استفاده از CMake برای همگردانی کد C و C++‎ برای برنامه‌تان پشتیبانی می‌کند. این صفحه نحوه استفاده از CMake با NDK ازطریق ExternalNativeBuild «افزایه Android Gradle» یا هنگام فراخوانی مستقیم CMake را توضیح می‌دهد.

فایل زنجیره ابزار CMake

NDK از CMake ازطریق فایل زنجیره ابزار پشتیبانی می‌کند. فایل‌های Toolchain فایل‌های CMake هستند که رفتار زنجیره ابزار را برای کامپایل متقاطع سفارشی می‌کنند. فایل زنجیره ابزار استفاده‌شده برای NDK در NDK در <NDK>/build/cmake/android.toolchain.cmake قرار دارد.

پارامترهای ساخت مانند ABI،‏ minSdkVersion، و غیره در خط فرمان هنگام فراخوانی cmake ارائه می‌شوند. برای مشاهده فهرست آرگومان‌های پشتیبانی‌شده، به بخش آرگومان‌های Toolchain مراجعه کنید.

فایل زنجیره ابزار «جدید»

NDKهای قبلی پیاده‌سازی جدیدی از فایل زنجیره ابزار را آزمایش کردند که تفاوت‌های رفتاری بین استفاده از فایل زنجیره ابزار NDK و استفاده از پشتیبانی CMake داخلی را کاهش می‌دهد. این کار درنهایت به حجم قابل‌توجهی کار نیاز داشت (که تکمیل نشده است)، اما درواقع رفتار را بهبود نداد، بنابراین دیگر این کار را دنبال نمی‌کنیم.

فایل زنجیره ابزار «جدید» درمقایسه با فایل زنجیره ابزار «قدیمی» دچار پس‌رفت رفتاری شده است. رفتار پیش‌فرض گردش کار توصیه‌شده است. اگر از -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF استفاده می‌کنید، توصیه می‌کنیم آن پرچم را از ساختتان بردارید. فایل زنجیره ابزار جدید هرگز به برابری با فایل زنجیره ابزار قدیمی نرسید، بنابراین احتمالاً پس‌رفت‌های رفتاری وجود دارد.

اگرچه توصیه می‌کنیم از فایل زنجیره ابزار جدید استفاده نکنید، درحال‌حاضر هیچ برنامه‌ای برای برداشتن آن از NDK وجود ندارد. انجام این کار باعث می‌شود ساخت‌هایی که به تفاوت‌های رفتاری بین فایل‌های زنجیره ابزار جدید و قدیمی متکی هستند خراب شوند و متأسفانه تغییر نام گزینه برای روشن کردن اینکه «قدیمی» درواقع توصیه می‌شود نیز باعث می‌شود کاربران آن گزینه خراب شوند. اگر از فایل زنجیره ابزار جدید راضی هستید، نیازی به انتقال ندارید، اما بدانید که هرگونه اشکال ثبت‌شده در رابطه با رفتار فایل زنجیره ابزار جدید احتمالاً برطرف نخواهد شد و درعوض باید انتقال دهید.

کاربرد

Gradle

استفاده از فایل زنجیره ابزار CMake هنگام استفاده از externalNativeBuild خودکار است. برای اطلاعات بیشتر، به راهنمای افزودن کد C و C++‎ به پروژه در Android Studio مراجعه کنید.

خط فرمان

هنگام ساختن با CMake خارج از Gradle، خود فایل زنجیره ابزار و آرگومان‌های آن باید به CMake ارسال شود. برای مثال:

$ cmake \
    -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI=$ABI \
    -DANDROID_PLATFORM=android-$MINSDKVERSION \
    $OTHER_ARGS

متغیرهای مستقل زنجیره ابزار

آرگومان‌های زیر را می‌توان به فایل زنجیره ابزار CMake ارسال کرد. اگر با Gradle می‌سازید، همان‌طور که در اسناد ExternalNativeBuild توضیح داده شده است، آرگومان‌ها را به android.defaultConfig.externalNativeBuild.cmake.arguments اضافه کنید. اگر از خط فرمان می‌سازید، متغیرهای مستقل را با -D به CMake ارسال کنید. برای مثال، برای اینکه armeabi-v7a را مجبور کنید با پشتیبانی Neon ساخته نشود، -DANDROID_ARM_NEON=FALSE را ارسال کنید.

ANDROID_ABI

رابط دودویی نرم‌افزار هدف. برای اطلاعات درباره میاناهای دودویی کاربردی پشتیبانی‌شده، به میاناهای دودویی کاربردی Android مراجعه کنید.

Gradle

Gradle این آرگومان را به‌طور خودکار ارائه می‌دهد. این آرگومان را به‌طور صریح در فایل build.gradle خود تنظیم نکنید. برای کنترل اینکه Gradle کدام رابط‌های دودویی نرم‌افزار را هدف‌یابی کند، از abiFilters همان‌طور که در رابط‌های دودویی نرم‌افزار Android شرح داده شده است استفاده کنید.

خط فرمان

CMake برای هر ساخت، یک هدف واحد می‌سازد. برای هدف‌یابی بیش‌از یک «رابط دودویی کاربردی» Android، باید برای هر «رابط دودویی کاربردی» یک‌بار ساخت را انجام دهید. توصیه می‌شود برای هر ABI از دایرکتوری‌های ساخت متفاوت استفاده کنید تا از تداخل بین ساخت‌ها جلوگیری شود.

مقدار یادداشت‌ها
armeabi-v7a
armeabi-v7a with NEON مشابه armeabi-v7a.
arm64-v8a
x86
x86_64

ANDROID_ARM_MODE

مشخص می‌کند که آیا دستورالعمل‌های arm یا thumb برای armeabi-v7a تولید شود یا نه. تأثیری بر دیگر واسط‌های دودویی برنامه ندارد. برای اطلاعات بیشتر، به مستندات رابط‌های دودویی برنامه Android مراجعه کنید.

مقدار یادداشت‌ها
فعال کردن
شست رفتار پیش‌فرض.

ANDROID_NATIVE_API_LEVEL

نام مستعار برای ANDROID_PLATFORM.

ANDROID_PLATFORM

حداقل سطح میانای برنامه‌سازی کاربردی پشتیبانی‌شده توسط برنامه یا کتابخانه را مشخص می‌کند. این مقدار با minSdkVersion برنامه مطابقت دارد.

Gradle

هنگام استفاده از افزونه Android Gradle، این مقدار به‌طور خودکار تنظیم می‌شود تا با minSdkVersion برنامه مطابقت داشته باشد و نباید به‌صورت دستی تنظیم شود.

خط فرمان

وقتی CMake مستقیماً فراخوانی می‌شود، این مقدار به‌طور پیش‌فرض روی پایین‌ترین سطح میانای برنامه‌سازی کاربردی پشتیبانی‌شده توسط NDK درحال استفاده تنظیم می‌شود. برای مثال، با NDK r20 این مقدار به‌طور پیش‌فرض روی سطح میانای برنامه‌سازی کاربردی ۱۶ تنظیم می‌شود.

قالب‌های متعددی برای این پارامتر پذیرفته می‌شود:

  • android-$API_LEVEL
  • $API_LEVEL
  • android-$API_LETTER

قالب $API_LETTER به شما امکان می‌دهد android-N را بدون نیاز به تعیین شماره مرتبط با آن نسخه مشخص کنید. توجه داشته باشید که برخی‌از نسخه‌های پخش بدون افزایش حرف، افزایش API دریافت کرده‌اند. این میاناهای برنامه‌سازی کاربردی را می‌توان با افزودن پسوند -MR1 مشخص کرد. برای مثال، سطح میانای برنامه‌سازی کاربردی ۲۵ android-N-MR1 است.

ANDROID_STL

مشخص می‌کند از کدام STL برای این برنامه استفاده شود. برای اطلاعات بیشتر، پشتیبانی کتابخانه C++‎ را ببینید. به‌طور پیش‌فرض، از c++_static استفاده خواهد شد.

مقدار یادداشت‌ها
c++_shared گونه کتابخانه مشترک libc++.
c++_static نوع کتابخانه ایستا از libc++.
هیچ‌کدام از کتابخانه استاندارد C++‎ پشتیبانی نمی‌شود.
سیستم STL سیستم

مدیریت پرچم‌های کامپایلر

اگر لازم است پرچم‌های خاصی را برای ساخت به کامپایلر یا پیونددهنده ارسال کنید، به اسناد CMake برای set_target_compile_options و مجموعه گزینه‌های مرتبط مراجعه کنید. بخش «همچنین ببینید» در پایین آن صفحه سرنخ‌های مفیدی دارد.

به‌طور کلی، بهترین روش این است که پرچم‌های کامپایلر را به‌عنوان باریک‌ترین محدوده موجود اعمال کنید. پرچم‌هایی که می‌خواهید برای همه هدف‌هایتان اعمال کنید (مثل -Werror) برای تکرار در هر ماژول مناسب نیستند، اما به‌ندرت باید به‌صورت سراسری اعمال شوند (CMAKE_CXX_FLAGS)، زیرا ممکن است تأثیرات نامطلوبی بر وابستگی‌های شخص ثالث در پروژه شما داشته باشند. برای چنین مواردی، پرچم‌ها را می‌توان در گستره دایرکتوری (add_compile_options) اعمال کرد.

برای زیرمجموعه کوچکی از پرچم‌های کامپایلر، می‌توان آن‌ها را در فایل build.gradle بااستفاده از cppFlags یا ویژگی‌های مشابه تنظیم کرد. نباید این کار را انجام دهید. پرچم‌هایی که از Gradle به CMake ارسال می‌شوند، رفتارهای اولویت‌بندی غافلگیرکننده‌ای خواهند داشت و در برخی موارد، پرچم‌هایی را که به‌طور ضمنی توسط پیاده‌سازی ارسال شده‌اند و برای ساخت کد Android لازم هستند، لغو می‌کنند. همیشه ترجیح دهید رفتار CMake را مستقیماً در CMake مدیریت کنید. اگر لازم است پرچم‌های کامپایلر را برای هر AGP buildType کنترل کنید، کار با انواع ساخت AGP در CMake را ببینید.

کار با انواع ساخت AGP در CMake

اگر لازم است رفتار CMake را برای Gradle buildType سفارشی تنظیم کنید، از آن نوع ساخت برای ارسال پرچم CMake اضافی (نه پرچم کامپایلر) استفاده کنید که اسکریپت‌های ساخت CMake شما بتوانند آن را بخوانند. برای مثال، اگر انواع ساخت «رایگان» و «ممتاز» دارید که توسط build.gradle.kts شما کنترل می‌شوند و باید آن داده‌ها را به CMake منتقل کنید:

android {
    buildTypes {
        free {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
                }
            }
        }
        premium {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
                }
            }
        }
    }
}

سپس، در CMakeLists.txt:

if (DPRODUCT_VARIANT_PREMIUM)
  # Do stuff for the premium build.
else()
  # Do stuff for the free build.
endif()

نام متغیر به شما بستگی دارد، اما مطمئن شوید که از هر چیزی با پیشوند ANDROID_، APP_، یا CMAKE_ اجتناب کنید تا از تداخل یا سردرگمی با پرچم‌های موجود جلوگیری شود.

برای نمونه، نمونه NDK پاک‌کننده‌ها را ببینید.

آشنایی با فرمان ساخت CMake

هنگام اشکال‌زدایی مشکلات ساخت CMake، دانستن آرگومان‌های ساخت خاصی که Gradle هنگام کامپایل متقابل برای Android استفاده می‌کند، مفید است.

«افزونه Android Gradle» آرگومان‌های ساخت را که برای اجرای ساخت CMake برای هر جفت ABI و نوع ساخت استفاده می‌کند در build_command.txt ذخیره می‌کند. این فایل‌ها در فهرست راهنمای زیر پیدا می‌شوند:

<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/

تکه‌کد زیر نمونه‌ای از آرگومان‌های CMake را برای ساختن نسخه قابل‌اشکال‌زدایی از نمونه hello-jni با هدف معماری armeabi-v7a نشان می‌دهد.

                    Executable : ${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/cmake
arguments :
-H${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/src/main/cpp
-DCMAKE_FIND_ROOT_PATH=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/prefab/armeabi-v7a/prefab
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_TOOLCHAIN_FILE=${HOME}/Android/Sdk/ndk/22.1.7171670/build/cmake/android.toolchain.cmake
-DANDROID_ABI=armeabi-v7a
-DANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DANDROID_PLATFORM=android-23
-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a
-DCMAKE_ANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_MAKE_PROGRAM=${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/ninja
-DCMAKE_SYSTEM_NAME=Android
-DCMAKE_SYSTEM_VERSION=23
-B${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/armeabi-v7a
-GNinja
jvmArgs :


                    Build command args: []
                    Version: 1

استفاده از کتابخانه‌های ازپیش‌ساخته

اگر کتابخانه ازپیش‌ساخته‌ای که باید وارد کنید به‌عنوان AAR توزیع شده است، اسناد وابستگی Studio را برای وارد کردن و استفاده از آن‌ها دنبال کنید. اگر از AGP استفاده نمی‌کنید، می‌توانید https://google.github.io/prefab/example-workflow.html را دنبال کنید، اما احتمالاً انتقال به AGP بسیار آسان‌تر است.

برای کتابخانه‌هایی که به‌عنوان AAR توزیع نمی‌شوند، دستورالعمل‌های استفاده از کتابخانه‌های ازپیش‌ساخته‌شده با CMake، به add_library اسناد مربوط به IMPORTED هدف‌ها در راهنمای CMake مراجعه کنید.

درحال ساختن کد طرف سوم

چند روش برای ساختن کد طرف سوم به‌عنوان بخشی از پروژه CMake شما وجود دارد، و اینکه کدام گزینه بهتر کار می‌کند به وضعیت شما بستگی دارد. بهترین گزینه اغلب این است که اصلاً این کار را انجام ندهید. درعوض، یک AAR برای کتابخانه بسازید و آن را در برنامه خود مصرف کنید. لزوماً نیازی نیست که آن «توصیه اِعمال خودکار» را منتشر کنید. این فایل می‌تواند در پروژه Gradle شما باشد.

اگر این گزینه دردسترس نیست:

پشتیبانی از YASM در CMake

NDK از CMake برای ساختن کد اسمبلی نوشته‌شده در YASM برای اجرا در معماری‌های x86 و x86-64 پشتیبانی می‌کند. YASM اسمبلر متن‌بازی برای معماری‌های x86 و x86-64 است که براساس اسمبلر NASM ساخته شده است.

برای ساختن کد اسمبلی با CMake، تغییرات زیر را در CMakeLists.txt پروژه‌تان ایجاد کنید:

  1. enable_language را با مقدار تنظیم‌شده روی ASM_NASM فراخوانی کنید.
  2. بسته به اینکه درحال ساختن کتابخانه مشترک هستید یا دودویی اجرایی، add_library یا add_executable را فراخوانی کنید. در آرگومان‌ها، فهرستی از فایل‌های منبع شامل فایل‌های .asm برای برنامه اسمبلی در YASM و فایل‌های .c برای کتابخانه‌ها یا توابع C مرتبط را ارسال کنید.

قطعه کد زیر نشان می‌دهد که چگونه می‌توانید CMakeLists.txt خود را برای ساختن برنامه YASM به‌عنوان کتابخانه مشترک پیکربندی کنید.

cmake_minimum_required(VERSION 3.6.0)

enable_language(ASM_NASM)

add_library(test-yasm SHARED jni/test-yasm.c jni/print_hello.asm)

برای نمونه‌ای از نحوه ساختن برنامه YASM به‌عنوان فایل اجرایی، به yasm test در مخزن git‏ NDK مراجعه کنید.

گزارش مشکلات

اگر با NDK یا فایل زنجیره ابزار CMake آن مشکلی داشتید، آن را ازطریق ردیاب مشکل android-ndk/ndk در GitHub گزارش کنید. برای مشکلات Gradle یا «افزایه Android Gradle»، به‌جای آن اشکال Studio را گزارش کنید.