پیکربندی CMake

اسکریپت ساخت CMake یک فایل نوشتار ساده است که باید نام آن را CMakeLists.txt بگذارید و شامل فرمان‌هایی است که CMake برای ساختن کتابخانه‌های C/C++‎ شما استفاده می‌کند. اگر منابع بومی شما ازقبل اسکریپت ساخت CMake ندارند، باید خودتان یکی ایجاد کنید و فرمان‌های CMake مناسب را در آن بگنجانید. برای آشنایی با نحوه نصب CMake، به نصب و پیکربندی NDK و CMake مراجعه کنید.

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

پس‌از پیکربندی کردن اسکریپت ساخت CMake جدید، باید Gradle را پیکربندی کنید تا پروژه CMake شما را به‌عنوان وابستگی ساخت اضافه کند تا Gradle کتابخانه بومی شما را با APK برنامه‌تان بسازد و بسته‌بندی کند.

توجه: اگر پروژه شما از ndk-build استفاده می‌کند، نیازی به ایجاد اسکریپت ساخت CMake ندارید. می‌توانید به‌سادگی Gradle را پیکربندی کنید تا پروژه کتابخانه بومی موجودتان را با ارائه مسیر فایل Android.mk دربرگیرد.

ایجاد اسکریپت ساخت CMake

برای ایجاد فایل نوشتار ساده‌ای که بتوانید از آن به‌عنوان اسکریپت ساخت CMake استفاده کنید، به‌صورت زیر عمل کنید:

  1. قاب پروژه را از سمت راست IDE باز کنید و نمای پروژه را از منوِ کرکره‌ای انتخاب کنید.
  2. روی دایرکتوری ریشه your-module کلیک راست کنید و جدید > فایل را انتخاب کنید.

    توجه: می‌توانید اسکریپت ساخت را در هر مکانی که می‌خواهید ایجاد کنید. بااین‌حال، هنگام پیکربندی اسکریپت ساخت، مسیرهای فایل‌های منبع و کتابخانه‌های بومی شما نسبت به مکان اسکریپت ساخت هستند.

  3. «CMakeLists.txt» را به‌عنوان نام فایل وارد کنید و روی تأیید کلیک کنید.

اکنون می‌توانید با افزودن فرمان‌های CMake، اسکریپت ساخت را پیکربندی کنید. برای دستور دادن به CMake برای ایجاد کتابخانه بومی از کد منبع بومی، فرمان‌های cmake_minimum_required() و add_library() را به اسکریپت ساخت اضافه کنید:

# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.

cmake_minimum_required(VERSION 3.4.1)

# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add_library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.

add_library( # Specifies the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )

نکته: همان‌طور که می‌توانید به CMake بگویید کتابخانه بومی از فایل‌های منبع ایجاد کند، می‌توانید از فرمان add_executable() استفاده کنید تا به CMake بگویید به‌جای آن، فایل اجرایی از آن فایل‌های منبع ایجاد کند. بااین‌حال، ساختن فایل‌های اجرایی از منابع بومی اختیاری است و ساختن کتابخانه‌های بومی برای بسته‌بندی در APK اکثر الزامات پروژه را برآورده می‌کند.

وقتی فایل منبع یا کتابخانه‌ای را بااستفاده از add_library() به اسکریپت ساخت CMake اضافه می‌کنید، «استودیو Android» فایل‌های سرایند مرتبط را نیز پس‌از همگام‌سازی پروژه در نمای پروژه نشان می‌دهد. بااین‌حال، برای اینکه CMake بتواند فایل‌های سرایند شما را درطول زمان ترجمه پیدا کند، باید فرمان include_directories() را به دستورگان ساخت CMake اضافه کنید و مسیر سرایندهایتان را مشخص کنید:

add_library(...)

# Specifies a path to native header files.
include_directories(src/main/cpp/include/)

قراردادی که CMake برای نام‌گذاری فایل کتابخانه شما استفاده می‌کند به‌صورت زیر است:

liblibrary-name.so

برای مثال، اگر «native-lib» را به‌عنوان نام کتابخانه مشترک در اسکریپت ساخت مشخص کنید، CMake فایلی با نام libnative-lib.so ایجاد می‌کند. بااین‌حال، هنگام بار کردن این کتابخانه در کد Java یا Kotlin، از نامی که در اسکریپت ساخت CMake مشخص کرده‌اید استفاده کنید:

کاتلین

companion object {
    init {
        System.loadLibrary("native-lib");
    }
}

جاوا

static {
    System.loadLibrary("native-lib");
}

توجه: اگر نام کتابخانه‌ای را در اسکریپت ساخت CMake خود تغییر دهید یا آن را حذف کنید، باید پروژه خود را پاک کنید تا Gradle تغییرات را اعمال کند یا نسخه قدیمی‌تر کتابخانه را از APK شما حذف کند. برای پاک کردن پروژه، Build > Clean Project (ساختن > پاک کردن پروژه) را از نوار منو انتخاب کنید.

«استودیو Android» به‌طور خودکار فایل‌های منبع و سرصفحه‌ها را به گروه cpp در قاب پروژه اضافه می‌کند. بااستفاده از چند فرمان add_library()، می‌توانید کتابخانه‌های دیگری را برای CMake تعریف کنید تا از فایل‌های منبع دیگر ساخته شوند.

افزودن میاناهای برنامه‌سازی کاربردی NDK

Android NDK مجموعه‌ای از میاناهای برنامه‌سازی کاربردی و کتابخانه‌های بومی را ارائه می‌دهد که ممکن است برایتان مفید باشد. با گنجاندن کتابخانه‌های NDK در فایل اسکریپت CMakeLists.txt پروژه‌تان، می‌توانید از هرکدام از این میاناهای برنامه‌سازی کاربردی استفاده کنید.

کتابخانه‌های NDK ازپیش‌ساخته‌شده ازقبل در پلاتفرم Android وجود دارند، بنابراین نیازی نیست آن‌ها را بسازید یا در APK خود بسته‌بندی کنید. چون کتابخانه‌های NDK ازقبل بخشی از مسیر جستجوی CMake هستند، حتی لازم نیست مکان کتابخانه را در نصب NDK محلی‌تان مشخص کنید—فقط باید نام کتابخانه‌ای را که می‌خواهید استفاده کنید به CMake ارائه دهید و آن را به کتابخانه بومی خودتان پیوند دهید.

فرمان find_library() را به متن ساخت CMake اضافه کنید تا کتابخانه NDK را پیدا کند و مسیر آن را به‌عنوان متغیر ذخیره کند. از این متغیر برای ارجاع به کتابخانه NDK در بخش‌های دیگر اسکریپت ساخت استفاده می‌کنید. نمونه زیر کتابخانه پشتیبانی گزارش مختص Android را پیدا می‌کند و مسیر آن را در log-lib ذخیره می‌کند:

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

              # Specifies the name of the NDK library that
              # CMake needs to locate.
              log )

برای اینکه کتابخانه بومی شما بتواند توابع را در کتابخانه log فراخوانی کند، باید کتابخانه‌ها را بااستفاده از فرمان target_link_libraries() در دستورگان ساخت CMake پیوند دهید:

find_library(...)

# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the log library to the target library.
                       ${log-lib} )

NDK همچنین شامل برخی کتابخانه‌ها به‌عنوان کد منبع است که باید بسازید و به کتابخانه بومی‌تان پیوند دهید. می‌توانید کد منبع را بااستفاده از فرمان add_library() در اسکریپت ساخت CMake به کتابخانه بومی کامپایل کنید. برای ارائه مسیر به کتابخانه NDK محلی، می‌توانید از متغیر مسیر ANDROID_NDK استفاده کنید که «استودیو Android» به‌طور خودکار برای شما تعریف می‌کند.

فرمان زیر به CMake می‌گوید که android_native_app_glue.c را که رویدادهای چرخه حیات NativeActivity و ورودی لمسی را مدیریت می‌کند در یک کتابخانه ایستا بسازد و آن را به native-lib پیوند دهد:

add_library( app-glue
             STATIC
             ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )

# You need to link static libraries against your shared native library.
target_link_libraries( native-lib app-glue ${log-lib} )

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

افزودن کتابخانه ازپیش‌ساخته مشابه مشخص کردن کتابخانه بومی دیگری برای ساخت CMake است. اما چون کتابخانه ازقبل ساخته شده است، باید از پرچم IMPORTED استفاده کنید تا به CMake بگویید که فقط می‌خواهید کتابخانه را به پروژه‌تان وارد کنید:

add_library( imported-lib
             SHARED
             IMPORTED )

سپس باید مسیر کتابخانه را بااستفاده از فرمان set_target_properties() مشخص کنید، همان‌طور که در زیر نشان داده شده است.

برخی‌از کتابخانه‌ها بسته‌های جداگانه‌ای برای معماری‌های CPU خاص یا میاناهای دودویی کاربردی (ABI) ارائه می‌دهند و آن‌ها را در دایرکتوری‌های جداگانه سازمان‌دهی می‌کنند. این رویکرد به کتابخانه‌ها کمک می‌کند از معماری‌های خاص CPU بهره ببرند و درعین‌حال به شما امکان می‌دهد فقط از نسخه‌هایی از کتابخانه که می‌خواهید استفاده کنید. برای افزودن چندین نسخه ABI از یک کتابخانه به دستورگان ساخت CMake خود، بدون نیاز به نوشتن چندین فرمان برای هر نسخه از کتابخانه، می‌توانید از متغیر مسیر ANDROID_ABI استفاده کنید. این متغیر از فهرستی از رابط‌های دودویی کاربردی پیش‌فرضی که NDK پشتیبانی می‌کند یا فهرست فیلترشده‌ای از رابط‌های دودویی کاربردی که به‌طور دستی Gradle را برای استفاده از آن‌ها پیکربندی می‌کنید استفاده می‌کند. برای مثال:

add_library(...)
set_target_properties( # Specifies the target library.
                       imported-lib

                       # Specifies the parameter you want to define.
                       PROPERTIES IMPORTED_LOCATION

                       # Provides the path to the library you want to import.
                       imported-lib/src/${ANDROID_ABI}/libimported-lib.so )

برای اینکه CMake بتواند فایل‌های سرایند شما را درطول زمان ترجمه پیدا کند، باید از فرمان include_directories() استفاده کنید و مسیر فایل‌های سرایندتان را اضافه کنید:

include_directories( imported-lib/include/ )

توجه: اگر می‌خواهید کتابخانه ازپیش‌ساخته‌ای را بسته‌بندی کنید که وابستگی زمان ساخت نیست—برای مثال، هنگام افزودن کتابخانه ازپیش‌ساخته‌ای که وابستگی imported-lib است، لازم نیست دستورالعمل‌های زیر را برای پیوند دادن کتابخانه انجام دهید.

برای پیوند دادن کتابخانه ازپیش‌ساخته به کتابخانه بومی خودتان، آن را به فرمان target_link_libraries() در اسکریپت ساخت CMake اضافه کنید:

target_link_libraries( native-lib imported-lib app-glue ${log-lib} )

برای بسته‌بندی کتابخانه پیش‌ساخته در APK، باید Gradle را به‌طور دستی با بلوک sourceSets پیکربندی کنید تا مسیر فایل .so شما را دربرگیرد. پس‌از ساختن APK، می‌توانید بااستفاده از تحلیل‌کننده APK بررسی کنید که Gradle کدام کتابخانه‌ها را در APK شما بسته‌بندی می‌کند.

افزودن پروژه‌های CMake دیگر

اگر می‌خواهید چندین پروژه CMake بسازید و خروجی‌های آن‌ها را در پروژه Android خود بگنجانید، می‌توانید از یک فایل CMakeLists.txt به‌عنوان اسکریپت ساخت CMake سطح بالا (که همان فایلی است که به Gradle پیوند می‌دهید) استفاده کنید و پروژه‌های CMake اضافی را به‌عنوان وابستگی‌های آن اسکریپت ساخت اضافه کنید. اسکریپت ساخت CMake سطح بالا زیر از فرمان add_subdirectory() برای مشخص کردن فایل CMakeLists.txt دیگری به‌عنوان وابستگی ساخت استفاده می‌کند و سپس به خروجی آن پیوند می‌دهد، درست مانند هر کتابخانه ازپیش‌ساخته دیگری.

# Sets lib_src_DIR to the path of the target CMake project.
set( lib_src_DIR ../gmath )

# Sets lib_build_DIR to the path of the desired output directory.
set( lib_build_DIR ../gmath/outputs )
file(MAKE_DIRECTORY ${lib_build_DIR})

# Adds the CMakeLists.txt file located in the specified directory
# as a build dependency.
add_subdirectory( # Specifies the directory of the CMakeLists.txt file.
                  ${lib_src_DIR}

                  # Specifies the directory for the build outputs.
                  ${lib_build_DIR} )

# Adds the output of the additional CMake build as a prebuilt static
# library and names it lib_gmath.
add_library( lib_gmath STATIC IMPORTED )
set_target_properties( lib_gmath PROPERTIES IMPORTED_LOCATION
                       ${lib_build_DIR}/${ANDROID_ABI}/lib_gmath.a )
include_directories( ${lib_src_DIR}/include )

# Links the top-level CMake build output against lib_gmath.
target_link_libraries( native-lib ... lib_gmath )

فراخوانی CMake از خط فرمان

از فرمان زیر برای فراخوانی CMake برای تولید پروژه Ninja در خارج از «استودیو Android» استفاده کنید:

cmake
-Hpath/to/cmakelists/folder
-Bpath/to/generated/ninja/project/debug/ABI
-DANDROID_ABI=ABI                               // For example, arm64-v8a
-DANDROID_PLATFORM=platform-version-string      // For example, android-16
-DANDROID_NDK=android-sdk/ndk/ndk-version
-DCMAKE_TOOLCHAIN_FILE=android-sdk/ndk/ndk-version/build/cmake/android.toolchain.cmake
-G Ninja

این فرمان پروژه Ninja را تولید می‌کند که می‌تواند برای ایجاد کتابخانه‌های اجرایی Android (فایل‌های .so) اجرا شود. CMAKE_TOOLCHAIN_FILE برای استفاده از پشتیبانی CMake در NDK لازم است. برای CMake نسخه ۳.۲۱ یا جدیدتر، به‌جای آن می‌توان از پشتیبانی NDK داخلی CMake استفاده کرد، اما باید از گروه متفاوتی از متغیرها استفاده شود، همان‌طور که در سند همگردانی متقاطع برای Android در CMake شرح داده شده است.