ضبط CMake

النص البرمجي للإصدار CMake هو ملف نصي عادي يجب تسميته CMakeLists.txt ويتضمن الأوامر التي يستخدمها CMake لإنشاء مكتبات C/C++ الخاصة بك. إذا لم تكن مصادرك الأصلية تحتوي على نص برمجي لإنشاء CMake، ستحتاج إلى إنشاء واحد بنفسك وتضمين أوامر CMake المناسبة. للتعرّف على كيفية تثبيت CMake المخصّص، اطّلِع على القسم تثبيت NDK وCMaker وضبطهما.

يتناول هذا القسم بعض الأوامر الأساسية التي يجب تضمينها في النص البرمجي للإصدار لإعلام CMake بالمصادر التي يجب استخدامها عند إنشاء مكتبة مدمجة مع المحتوى. لمزيد من المعلومات، اطّلِع على المستندات الرسمية حول أوامر CMake.

بعد ضبط نص برمجي لإنشاء CMake الجديد، يجب ضبط Gradle لتضمين مشروع CMake الخاص بك كتبعية للإصدار، حتى تتمكّن خدمة Gradle من إنشاء مكتبتك الأصلية وتجميعها مع حزمة APK لتطبيقك.

ملاحظة: إذا كان مشروعك يستخدم ndk-build، لن تحتاج إلى إنشاء نص برمجي لإنشاء CMake. يمكنك بسهولة ضبط Gradle لتضمين مشروعك الحالي الخاص بالمكتبة الأصلية من خلال توفير مسار إلى ملف Android.mk.

إنشاء نص برمجي لإنشاء CMake

لإنشاء ملف نص عادي يمكنك استخدامه كنص برمجي لإنشاء CMake، اتّبِع الخطوات التالية:

  1. افتح لوحة المشروع من الجانب الأيسر من بيئة التطوير المتكاملة واختَر عرض المشروع من القائمة المنسدلة.
  2. انقر بزر الماوس الأيمن على الدليل الجذري في your-module واختَر جديد > ملف.

    ملاحظة: يمكنك إنشاء النص البرمجي للإصدار في أي موقع جغرافي تريده. مع ذلك، عند ضبط النص البرمجي للإصدار، تكون المسارات إلى ملفات ومكتبات المصدر الأصلي مرتبطة بموقع النص البرمجي للإصدار.

  3. أدخِل "CMakeLists.txt" كاسم الملف، ثم انقر على OK (حسنًا).

يمكنك الآن ضبط النص البرمجي للإصدار عن طريق إضافة أوامر 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 يلبي معظم متطلبات المشروع.

عند إضافة مكتبة أو ملف مصدر إلى النص البرمجي لإنشاء CMake باستخدام add_library()، يعرض "استوديو 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:

Kotlin

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

Java

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

ملاحظة: في حال إعادة تسمية مكتبة أو إزالتها في النص البرمجي للإصدار CMake، يجب تنظيف مشروعك قبل أن يطبّق Gradle التغييرات أو يزيل الإصدار القديم من المكتبة من حزمة APK. لتنظيف مشروعك، اختَر إنشاء > تنظيف المشروع من شريط القوائم.

يضيف "استوديو 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() كما هو موضّح أدناه.

توفر بعض المكتبات حزمًا منفصلة لبنى وحدة معالجة مركزية معينة أو الواجهات الثنائية للتطبيقات (ABI)، ونظّمها في أدلة منفصلة. يساعد هذا الأسلوب المكتبات في الاستفادة من بُنى وحدة معالجة مركزية معيّنة وتسمح لك باستخدام إصدارات المكتبة التي تريدها فقط. لإضافة عدة إصدارات من واجهة التطبيق الثنائية (ABI) من مكتبة إلى النص البرمجي الخاص بإصدار CMake، بدون الحاجة إلى كتابة أوامر متعدّدة لكل نسخة من المكتبة، يمكنك استخدام متغيّر المسار ANDROID_ABI. يستخدم هذا المتغيّر قائمة بقيم ABI المتوافقة مع نظام التشغيل NDK أو قائمة تمت فلترتها بواجهات ABI التي تضبط منصة 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، يمكنك التحقق من مكتبات Gradle التي سيتم تضمينها في ملف APK باستخدام أداة تحليل 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. بالنسبة إلى الإصدار 3.21 من CMake أو الإصدارات الأحدث، يمكن استخدام دعم NDK المضمّن في CMake بدلاً من ذلك، ولكن يجب استخدام مجموعة مختلفة من المتغيرات كما هو موضّح في مستندات Cross Compiling for Android (التجميع المتبادل لنظام التشغيل Android).