CMake

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

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

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

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

فایل «جدید» toolchain

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

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

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

کاربرد

گرادل

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

خط فرمان

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

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

آرگومان‌های زنجیره ابزار

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

ANDROID_ABI

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

گرادل

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

خط فرمان

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

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

ANDROID_ARM_MODE

مشخص می‌کند که آیا دستورالعمل‌های مربوط به بازو یا شست برای armeabi-v7a تولید شود یا خیر. هیچ تاثیری بر سایر ABIها ندارد. برای اطلاعات بیشتر، به مستندات ABIهای اندروید مراجعه کنید.

ارزش یادداشت‌ها
بازو
شست رفتار پیش‌فرض.

ANDROID_NATIVE_API_LEVEL

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

ANDROID_PLATFORM

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

گرادل

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

خط فرمان

هنگام فراخوانی مستقیم CMake، این مقدار به صورت پیش‌فرض روی پایین‌ترین سطح API پشتیبانی‌شده توسط NDK مورد استفاده قرار می‌گیرد. برای مثال، با NDK r20 این مقدار به صورت پیش‌فرض روی سطح API 16 تنظیم می‌شود.

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

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

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

ANDROID_STL

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

ارزش یادداشت‌ها
سی پلاس پلاس_مشترک نوع کتابخانه مشترک libc++ .
سی پلاس پلاس استاتیک نوع کتابخانه‌ای ایستا از libc++ .
هیچ کدام عدم پشتیبانی از کتابخانه استاندارد ++C
سیستم سیستم STL

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

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

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

برای زیرمجموعه‌ی محدودی از پرچم‌های کامپایلر، می‌توان آن‌ها را با استفاده cppFlags یا ویژگی‌های مشابه در فایل build.gradle تنظیم کرد. شما نباید این کار را انجام دهید. پرچم‌هایی که از Gradle به CMake ارسال می‌شوند، رفتارهای تقدمی شگفت‌انگیزی خواهند داشت، در برخی موارد پرچم‌هایی که به طور ضمنی توسط پیاده‌سازی ارسال می‌شوند و برای ساخت کد اندروید مورد نیاز هستند، نادیده گرفته می‌شوند. همیشه ترجیح می‌دهید رفتار 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_ اجتناب می‌کنید تا از برخورد یا سردرگمی با پرچم‌های موجود جلوگیری شود.

برای مثال به نمونه Sanitizers NDK مراجعه کنید.

دستور ساخت CMake را درک کنید

هنگام اشکال‌زدایی مشکلات ساخت CMake، دانستن آرگومان‌های ساخت خاصی که 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 برای کتابخانه بسازید و آن را در برنامه خود استفاده کنید. لزوماً نیازی به انتشار آن AAR ندارید. می‌تواند در داخل پروژه Gradle شما باشد.

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

  • منبع شخص ثالث را از فروشنده (یعنی کپی) در مخزن خود قرار دهید و از add_subdirectory برای ساخت آن استفاده کنید. این روش فقط در صورتی کار می‌کند که کتابخانه دیگر نیز با CMake ساخته شده باشد.
  • یک پروژه خارجی تعریف کنید.
  • کتابخانه را جداگانه از پروژه خود بسازید و برای وارد کردن آن به عنوان یک کتابخانه از پیش ساخته شده، از دستور Use prebuilt libraries استفاده کنید .

پشتیبانی 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 یا افزونه Gradle اندروید، به جای آن، یک اشکال (bug) در Studio گزارش دهید .