Android NDK programming-Android. mk
Android. mk
Android. mk is a GUN Makefile clip that describes the NDK project to the Android NDK build system. It is a required component of every NDK project. The build system wants it to appear in the jni subdirectory. The following is the content of the Android. mk file in the hello-jni project.
- # Copyright (C) 2009 The Android Open Source Project
- #
- # Licensed under the Apache License, Version 2.0 (the License );
- # You may not use this file before t in compliance with the License.
- # You may obtain a copy of the License
- #
- # Http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # Distributed under the License is distributed on an as is basis,
- # Without warranties or conditions of any kind, either express or implied.
- # See the License for the specific language governing permissions and
- # Limitations under the License.
- #
- LOCAL_PATH: = $ (call my-dir)
-
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = hello-jni
- LOCAL_SRC_FILES: = hello-jni.c
-
- Include $ (BUILD_SHARED_LIBRARY)
To better understand its syntax, we analyze it row by row. Because this is a GUN Makefile segment, its syntax is the same as that of other makefiles. Each line contains a separate command. The comment line starts with "#", and the GUN Make tool does not process them. According to naming conventions, variable names must be capitalized.
The first command after the annotation block is used to define the LOCAL_PATH variable. According to the requirements of the Android build system, the Android. mk document must start with the definition of LOCAL_PATH variable.
- LOCAL_PATH: = $ (call my-dir)
The Android build system uses LOCAL_PATH to locate the source file. Because it is not suitable to set this variable to a hard-coded value, the Android build system provides a macro function named my-dir. You can put this variable in the current directory by setting it to the return value of the my-dir macro function.
The Android build system sets the CLEAR_VARS variable to the location of the clear-vars.mk fragment. The Makefile fragment can be used to clear the LOCAL _ variables except LOCAL_PATH, such as LOCAL_MODULE and LOCAL_SRC_FILES.
- Include $ (CLEAR_VARS)
This is because the Android build system parses multiple build files and module definitions in a single execution, while the LOCAL _ is a global variable. Clearing them can avoid conflicts. Every native component is called a module.
The LOCAL_MODULE variable is used to set a unique name for these modules. The following code sets the module name to hello-jni:
- LOCAL_MODULE: = hello-jni
Because the module name is also used to name the file generated during the build process, the build system adds an appropriate prefix and suffix to the file. In this example, the hello-jni module generates a shared library file and the build system names it as a libhello-jni.so.
Use the LOCAL_SRC_FILES variable to define the source file list for creating and assembling this module.
- LOCAL_SRC_FILES: = hello-jni.c
Here, the hello-jni module is only generated by one source file, while the LOCAL_SRC_FILES variable can contain multiple source file names separated by spaces.
So far, the build system variables defined in the Android. mk file briefly describe the native project. Compiling and generating the build system of the actual module also needs to contain the appropriate build system fragment. The specific part to be included depends on the type of the module to be generated.
1. Build a shared library
To create a module that can be used by the main application, you must convert the module into a shared library. The Android NDK build system sets the BUILD_SHARED_LIBRARY variable to the storage location of the build-shared-library.mk file. This Makefile fragment contains the necessary process for building and assembling the source file into a shared library:
- Include $ (BUILD_SHARED_LIBRARY)
Hello-jni is a simple module. However, unless your module requires special processing, the Android. mk document will contain identical processes and commands.
2. Build multiple shared libraries
Based on the architecture of different applications, a single Android. mk document may generate multiple shared library modules. Such as program:
- LOCAL_PATH: = $ (call my-dir)
-
- #
- # Module 1
- #
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = module1
- LOCAL_SRC_FILES: = module1.c
- Include $ (BUILD_SHARED_LIBRARY)
-
- #
- # Module 2
- #
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = module2
- LOCAL_SRC_FILES: = module2.c
-
- Include $ (BUILD_SHARED_LIBRARY)
After processing this Android. mk build document, the Android NDK build system generates two shared libraries: libmodule1.so and libmodule2.so.
3. Construct a static database
The Android NDK build system also supports static libraries. The actual Android application does not directly use static libraries, and the application package does not contain static libraries. Static libraries can be used to build shared libraries. For example, when adding third-party code to an existing native project, you do not need to directly include the third-party source code in the native project, but compile the third-party code into a static library and then integrate it into the shared library, such as the program:
- LOCAL_PATH: = $ (call my-dir)
-
- #
- # Third-party AVI Library
- #
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = avilib
- LOCAL_SRC_FILES: = avilib. c platform_posix.c
-
- Include $ (BUILD_STATIC_LIBRARY)
-
- #
- # Native Module
- #
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = module
- LOCAL_SRC_FILES: = module. c
-
- LOCAL_STATIC_LIBRARIES: = avilib
-
- Include $ (BUILD_SHARED_LIBRARY)
After a third-party code module generates a static library, the shared library can be used by adding its module name to the LOCAL_STATIC_LIBRARIES variable.
4. share a common module with a shared library
The static library can ensure the modularization of source code. However, when the static library is connected to the shared library, it becomes a part of the shared library. When multiple shared libraries are connected to the same static library, multiple copies of the common module must be repeatedly connected to different shared libraries, in this way, the application size is increased. In this case, you do not need to build a static library, but instead use a common module as a shared library to dynamically connect to the dependent module to eliminate duplicate copies, such as programs:
- LOCAL_PATH: = $ (call my-dir)
-
- #
- # Third-party AVI Library
- #
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = avilib
- LOCAL_SRC_FILES: = avilib. c platform_posix.c
-
- Include $ (BUILD_SHARED_LIBRARY)
-
- #
- # Native Module 1
- #
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = module1
- LOCAL_SRC_FILES: = module1.c
-
- LOCAL_SHARED_LIBRARIES: = avilib
-
- Include $ (BUILD_SHARED_LIBRARY)
-
- #
- # Native module 2
- #
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = module2
- LOCAL_SRC_FILES: = module2.c
-
- LOCAL_SHARED_LIBRARIES: = avilib
-
- Include $ (BUILD_SHARED_LIBRARY)
5. Share modules among multiple NDK Projects
When both static and shared libraries are used, common modules can be shared between modules. Note that all these modules must belong to the same NDK project. Android NDK allows sharing and reusing modules between NDK projects starting with R5. To share the avilib module among multiple NDK projects, follow these steps:
First, move the avilib source code outside the NDK project, for example, C: androidshared-modules avilib. To avoid name conflicts, the directory structure can also contain the name of the module provider, for example, C: androidshared-modules ranscodeavilib.
Note:
In the Android NDK build system, the shared module path cannot contain spaces.
As a shared module, avilib needs its own Android. mk file, such as a program:
- LOCAL_PATH: = $ (call my-dir)
-
- #
- # Third-party AVI Library
- #
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = avilib
- LOCAL_SRC_FILES: = avilib. c platform_posix.c
-
- Include $ (BUILD_SHARED_LIBRARY)
Now, you can remove the avilib module from the Android. mk file of the NDK project. To use this shared module, add the macro import-module part of the call function with transcode/avilib as the parameter at the end of the build document. To avoid System Building conflicts, you should put the import-module function macro call at the end of the Android. mk document. Such as program:
- #
- # Native Module
- #
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = module
- LOCAL_SRC_FILES: = module. c
- LOCAL_SHARED_LIBRARIES: = avilib
-
- Include $ (BUILD_SHARED_LIBRARY)
-
- $ (Call import-module, transcode/avilib)
The import-module function macro must first locate the shared module and then import it to the NDK project. By default, the import-module function macro only searches for the/sources directory. To search for the c: androidshared-modules directory, define a new environment variable named NDK_MODULE_PATH and set it to the root directory of the shared module, for example, c: androidshared-modules.
6. Use the Prebuilt Library
The source code of the shared module is required. The Android NDK build system simply includes these source files in the NDK project and builds them each time. Android NDK also supports Prebuilt Libraries After R5. In the following cases, the Prebuilt library is very useful:
I want to publish your module to others without publishing the source code.
You want to use the pre-built version of the shared module to accelerate the building process.
Despite being compiled, the pre-built module still requires an Android. mk build document, such as the program:
- LOCAL_PATH: = $ (call my-dir)
-
- #
- # Third-party pre-built AVI Library
- #
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = avilib
- LOCAL_SRC_FILES: = libavilib. so
-
- Include $ (PREBUILT_SHARED_LIBRARY)
The LOCAL_SRC_FILES variable points not to the source file, but to the location of the actual Prebuilt library relative to LOCAL_PATH.
Note:
The Prebuilt library definition does not contain any information about the actual machine architecture built by the database. Developers need to ensure that the Prebuilt library is built for the same machine architecture as the NDK project.
The PREBUILT_SHARED_LIBRARY variable points to the prebuilt-shared-library.mk Makefile fragment. It does not build anything, But it copies the Prebuilt Library to the libs directory of the NDK project. By using the PREBUILT_STATIC_LIBRARY variable, the static library can be used as the Prebuilt library like a shared library, and the NDK project can use the Prebuilt library like a common shared library.
- ...
- LOCAL_SHARED_LIBRARIES: = avilib
- ...
7. Build independent executable files
The recommended and supported way to use native components on the Android platform is to package them into a shared library. However, to facilitate testing and rapid prototyping, Android NDK also supports building independent executable files. These independent executable files can be copied to common Linux applications on Android devices without being packaged into APK files, and they can be directly executed without being loaded through Java applications. To generate an independent executable file, you must import the BUILD_EXECUTABLE variable in the Android. mk build document, instead of the BUILD_SHARED _ LIBRARY variable, such as the program:
- #
- # Independent and executable native modules
- #
- Include $ (CLEAR_VARS)
-
- LOCAL_MODULE: = module
- LOCAL_SRC_FILES: = module. c
-
- LOCAL_STATIC_LIBRARIES: = avilib
-
- Include $ (BUILD_EXECUTABLE)
The BUILD_EXECUTABLE variable points to the build-executable.mk Makefile fragment that contains the steps necessary to generate an independent executable file on the Android platform. The independent executable file is put in libs/with the same name as the module/ Directory. Although stored in this directory, it is not included in the APK file during the packaging phase.
8. Other build system variables
In addition to the variables mentioned in the previous sections, the Android NDK build system also supports other variables. This section briefly describes these variables.
The following variables are defined for the build system:
TARGET_ARCH: name of the target CPU architecture, for example, arm
TARGET_PLATFORM: name of the target Android platform, for example, android-3
TARGET_ARCH_ABI: name of the target CPU architecture and ABI, for example, armeabi-v7a
TARGET_ABI: connection between the target platform and ABI, for example, android-3-armeabi-v7a
Variables that can be defined as module descriptions include:
LOCAL_MODULE_FILENAME: an optional variable used to redefine the name of the generated output file. By default, the build system uses the value of LOCAL_MODULE as the name of the generated output file, but the variable LOCAL_MODULE _ FILENAME can overwrite the value of LOCAL_MODULE.
LOCAL_CPP_EXTENSION: the default extension of the C ++ source file is. cpp. This variable can be used to specify one or more file extensions for the C ++ source code.
- ...
- LOCAL_CPP _ EXTENSION: =. cpp. cxx
- ...
LOCAL_CPP_FEATURES: an optional variable used to specify the specific C ++ features on which the module depends, such as RTTI and exceptions.
- ...
- LOCAL_CPP_FEATURES: = rtti
- ...
Local_c_cmddes: list of available directories. The relative path of the NDK installation directory is used to search for header files.
- ...
- LOCAL_C_INCLUDES: = sources/shared-module
- LOCAL_C_INCLUDES: = $ (LOCAL_PATH)/include
- ...
LOCAL_CFLAGS: an optional set of compiler flags that are transmitted to the compiler when compiling C and C ++ source files.
- ...
- LOCAL_CFLAGS: =-DNDEBUG-DPORT = 1234
- ...
LOCAL_CPP_FLAGS: a set of optional compilation flags that are transmitted to the compiler when only the C ++ source file is compiled.
LOCAL_WHOLE_STATIC_LIBRARIES: A variant of LOCAL_STATIC_LIBRARIES that specifies all static library content that should be included in the generated shared library.
(LOCAL_WHOLE_STATIC_LIBRARIES is useful when several static libraries have circular dependencies .)
LOCAL_LDLIBS: an optional list of link flag. When the target file is linked to generate an output file, the flag is sent to the linker. It is mainly used to transmit the list of system libraries to be dynamically linked. For example, to connect to the Android NDK logstore, use the following code:
- LOCAL_LDFLAGS: =? Llog
LOCAL_ALLOW_UNDEFINED_SYMBOLS: an optional parameter that prevents missing symbols from being checked in the generated file. If not defined, the linker will generate an error message when the symbol is missing.
LOCAL_ARM_MODE: an optional parameter, which is a specific variable of the ARM machine architecture. It is used to specify the ARM binary type to be generated. By default, the build system is generated with 16-bit commands in thumb mode, but this variable can be set to arm to specify 32-bit commands.
- LOCAL_ARM_MODE: = arm
This variable changes the building system behavior of the entire module. You can use the. arm extension to specify to build a specific file only in the arm mode.
- LOCAL_SRC_FILES: = file1.c file2.c. arm
LOCAL_ARM_NEON: an optional parameter, which is a special variable of the ARM machine architecture. It is used to specify the ARM advanced Single command stream multi-Data stream (Single Instruction Multiple Data, SIMD) (a.k. a. NEON) inline function.
- LOCAL_ARM_NEON: = true
This variable changes the building system behavior of the entire module. You can use the. neon extension to specify that only specific files with NEON inline functions can be built.
- LOCAL_SRC_FILES: = file1.c file2.c. neon
LOCAL_DISABLE_NO_EXECUTE: an optional variable to disable the NX Bit security feature. NX Bit stands for Never Execute (Never executed). It is a technology used in the CPU to isolate code and storage areas. This prevents malware from controlling the application by inserting its code into the application's storage zone.
- LOCAL_DISABLE_NO_EXECUTE: = true
LOCAL_EXPORT_CFLAGS: this variable records a set of compiler flags that are added to the LOCAL_STATIC_LIBRARIES or LOCAL_SHARED_LIBRARIES definitions that use other modules of this module through the LOCAL_STATIC_LIBRARIES variable.
- LOCAL_MODULE: = avilib
- ...
- LOCAL_EXPORT_CFLAGS: = −denable_audio
- ...
- LOCAL_MODULE: = module1
- LOCAL_CFLAGS: = −ddebug
- ...
- LOCAL_SHARED_LIBRARIES: = avilib
The compiler will execute module1 with the-DENABLE_AUDIO-DDEBUG flag.
LOCAL_EXPORT_CPPFLAGS: similar to LOCAL_EXPORT_CLAGS, but it is a C ++-specific code compiler flag.
LOCAL_EXPORT_LDFLAGS: Same as LOCAL_EXPORT_CFLAGS, but used as a linker flag.
LOCAL_EXPORT_C_INCLUDES: this variable allows you to record the path set. These paths are added to the LOCAL_STATIC_LIBRARIES or LOCAL_SHARED_LIBRARIES definitions that use the module.
LOCAL_SHORT_COMMANDS: this variable should be set to true for modules with a large number of resources or independent static/Shared libraries. An operating system such as Windows allows a maximum of 191 characters to be entered on a command line. The variable is less than 191 characters long by breaking down and constructing commands. This method is not recommended in small modules because it slows down the building process.
LOCAL_FILTER_ASM: this variable defines an application used to filter assembly files from LOCAL_SRC_FILES.
9. Other build system function macros
This section summarizes other function macros supported by the Android NDK build system.
All-subdir-makefiles: returns the list of Android. mk build files in all subdirectories of the current directory. For example, you can call the following command to include all Android. mk files in the subdirectory In the build process:
- Include $ (call all-subdir-makefiles)
This-makefile: returns the path of the current Android. mk build file.
Parent-makefile: returns the path of the parent Android. mk build file that contains the current build file.
Grand-parent-makefile: Same as parent-makefile, but used for the grandfather directory.
10. define new variables
Developers can define other variables to simplify their build files. Names prefixed with LOCAL _ and NDK _ are reserved for Android NDK build systems. It is recommended that the variables defined by developers start with MY _, such as programs:
- ...
- MY_SRC_FILES: = avilib. c platform_posix.c
- LOCAL_SRC_FILES: = $ (addprefix avilib/, $ (MY_SRC_FILES ))
- ...
11. Conditional operations
The Android. mk build file can also contain conditional operations on these variables. For example, each architecture contains a different source file set, such as a program:
- ...
- Ifeq ($ (TARGET_ARCH), arm)
- LOCAL_SRC_FILES + = armonly. c
- Else
- LOCAL_SRC_FILES + = generic. c
- Endif
- ...
Application. mk
Application. mk is an optional build file used by the Android NDK build system. Like the Android. mk file, it is also stored in the jni directory. Application. mk is also a GUN Makefile clip. It aims to describe the modules required by the application, and it also defines the common variables of all modules. The following are the variables supported by the Application. mk build file:
APP_MODULES: by default, the Android NDK build system builds all modules declared by the Android. mk file. This variable can overwrite the above behavior and provide a list of modules to be constructed separated by spaces.
APP_OPTIM: this variable can be set to release or debug to change the optimization level of the generated binary file. The release mode is used by default, and the generated binary file is highly optimized. This variable can be set to debug mode to generate unoptimized binary files that are easier to debug.
APP_CLAGS: this variable lists some compiler flags that are passed to the compiler when compiling the C and C ++ source files of any module.
APP_CPPFLAGS: this variable lists some compiler flags that are passed to the compiler when compiling C ++ source files of any module.
APP_BUILD_SCRIPT: by default, the Android NDK build system searches for the Android. mk build file in the jni subdirectory of the project. You can use this variable to change the above behavior and use different file generation methods.
APP_ABI: by default, the Android NDK build system generates binary files for armeabi ABI. You can use this variable to change the above behavior and generate binary files for other ABI files. For example:
- APP_ABI: = mips
In addition, you can set multiple ABI
- APP_ABI: = armeabi mips
Generate binary files for all supported ABI files
- APP_ABI: = all
APP_STL: by default, the Android NDK build system uses the minimum STL Runtime Library, also known as the system library. You can use this variable to select different STL implementations.
- APP_STL: = stlport_shared
APP_GNUSTL_FORCE_CPP_FEATURES: similar to the LOCAL_CPP_EXTENSIONS variable. This variable indicates that all modules depend on specific C ++ features, such as RTTI and exceptions.
APP_SHORT_COMMANDS: similar to the LOCAL_SHORT_COMMANDS variable, this variable allows the build system to use shorter commands in the project when there are a large number of source files.