Cmake = Cmake = Cmake

1. What is CMake

  • In Android Studio 2.2 and above, the default tool for building native libraries isCMake.
  • CMake is a cross-platform build tool that describes the installation (compilation process) for all platforms in a single statement. Can output a variety of makefiles or project files. CMake does not build the final software directly, but rather generates scripts for other tools (such as makefiles) that are then used in accordance with how the tool was built.
  • CMake is a more advanced compilation and configuration tool than make. It can generate makefiles or vcproj projects for different platforms and different compilers, thus achieving cross-platform purposes. Android Studio uses CMake to generate ninja. Ninja is a small, speed-focused build system. We don’t need to worry about ninja scripts, we just need to know how to configure CMake.
  • CMake is actually a cross-platform tool that supports the production of a variety of build scripts.

2. CMake source file

  • The source file for CMake can contain commands, comments, Spaces, and newlines.
  • Source files written in CMake are named cMakelists.txt or with the extension.cmake.
  • Can be achieved byadd_subdirectory()Command to add the CMake source file to the subdirectory.
  • All valid statements in the CMake source file are commands, which can be built-in commands or custom function/macro commands.

3, CMake comment

Single line comments

#[[multi-line commentsMultiline comment []Copy the code
  • One-line comments: # Comment content (comments start at # and end at line end)
  • Fishing rod comments: You can use parentheses to implement multi-line comments: #[[multi-line comments]]

4, CMake variable

# Declare variable: set(variable name, variable value)
set(var 123)
The # reference variable: message command is used to print
message("var = ${var}")
Copy the code
  • All variables in CMake are of type string. You can useset()andunset()Command to declare or remove a variable
  • References to variables:${variable name}

5, CMake lists

Set (list name, value 1, value 2... Value of N)
Set (list name "value 1; Value 2; . ; Value N ")
set(list_var 1 2 3 4 5)
# or
set(list_var2 "1. 2; 3; 4. 5. "")

message("list_var = ${list_var}")
Copy the code
  • A list is also a string, and you can think of a list as a special variable that has multiple values.
  • Syntax format:Set (list name value 1 value 2... Value of N),Set (list name "value 1; Value 2; . ; Value N ")
  • References to lists:${list name}

6, CMake flow control – operator

type The name of the
One yuan EXIST,COMMAND,DEFINED
binary EQUAL,LESS,LESS_EQUAL,GREATER,GREATER_EQUAL,STREQUAL,STRLESS,

STRLESS_EQUAL,STRGREATER,STRGREATER_EQUAL,VERSION_EQUAL,VERSION_LESS,

VERSION_LESS_EQUAL,VERSION_GREATER,VERSION_GREATER_EQUAL,MATCHES
logic NOT,AND,OR

7, CMake process control – Boolean constant value

type value
true 1,ON,YES,TRUE,Y, non-zero values
false 0, OFF, NO FALSE, N, IGNORE, NOTFOUND, an empty string,

A string ending in -notfound

8, CMake process control – conditional command

set(if_tap OFF)
set(elseif_tap ON)

if(${if_tap})
	message("if")
elseif(${elseif_tap})
	message("elseif")
else(${if_tap})
	message("else")
endif(${if_tap})
Copy the code
  • Syntax format:
if(expression) COMMAND(ARGS...)elseif(expression) COMMAND(ARGS...)else(expression) COMMAND(ARGS...)endif(Expression)Copy the code
  • The elseif and else sections are optional, and there can be more than one elseif section. Indentation and whitespace have no effect on statement parsing.

9, CMake flow control – loop command

set(a "")
while(NOT a STREQUAL "xxx")
	set(a "${a}x")
	message("a = ${a}")
endwhile(a)Copy the code
  • Syntax format:
while(expression) COMMAND(ARGS...)endwhile(Expression)Copy the code
  • The break() command breaks the entire loop.
  • Continue () breaks out of the current loop.

10, CMake flow control – loop traversal

1) Format 1

foreach(item 1 2 3)
	message("item = ${item}")
endforeach(item)
Copy the code
  • Syntax format:
foreach(Loop variable parameter1parameter2. Parameter N) COMMAND (ARGS...).endforeach(Loop variable)Copy the code
  • Set loop variables as parameters for each iteration.
  • Foreach also supports break() and continue() commands to break out of loops.

2) Format 2

foreach(item RANGE 3)
	message("item = ${item}")
endforeach(item)
Copy the code
  • Syntax format:
foreachRANGE total COMMAND(ARGS...)endforeach(Loop variable)Copy the code
  • The loop ranges from 0 to total.

3) Format 3

foreach(item RANGE 1 5 2)
	message("item = ${item}")
endforeach(item)
Copy the code
  • Syntax format:
foreachLoop variable RANGE start stop step COMMAND(ARGS...)endforeach(Loop variable)Copy the code
  • The loop ranges from start to stop and increments to step.

4) Format 4

set(list_var 1 2 3)

foreach(item IN LISTS list_var)
	message("item = ${item}")
endforeach(item)
Copy the code
  • Foreach also supports looping over lists.
  • Syntax format:
foreach(loop variable IN LISTS) COMMAND(ARGS...)endforeach(Loop variable)Copy the code

11, CMake custom function command

function(func x y z)
	message("call function func")
	message("x = ${x}")
	message("y = ${y}")
	message("z = ${z}")
	message("ARGC = ${ARGC}")
	message("arg1 = ${ARGV0} arg2 = ${ARGV1} arg3 = ${ARGV2}")
	message("all args = ${ARGV}")
endfunction(func)

func(1 2 3)
Copy the code

ARGC: indicates the number of arguments passed. ARGV: indicates all parameters. ARGV0: indicates the first parameter, ARGV1, ARGV2, and so on.

  • Custom function command format:
function(<name> [arg1[arg2[arg3...]]])
	COMMAND()
endfunction(<name>)
Copy the code
  • Function command call format:Name (argument list)

12, CMake custom macro command

macro(ma x y z)
	message("call macro ma")
	message("x = ${x}")
	message("y = ${y}")
	message("z = ${z}")
endmacro(ma)

ma(1 2 3)
Copy the code

Function commands have their own scope. Macro commands have the same scope as the caller.

  • Custom macro command format:
macro(<name> [arg1[arg2[arg3...]]])
	COMMAND()
endmacro(<name>)
Copy the code
  • Macro command invocation format:Name (argument list)

The scope of the CMake variable

  • Global layer: Cache variables, which can be seen throughout the project scope, are defined by specifying the cache parameter when a variable is defined by set.
  • Directory layer: variables defined in the current directory, cMakelists.txt, and in other cmake source files included in this file.
  • The function layer: A variable defined in a command function that belongs to the scope of the function.

Variable lookup priority: function layer over directory layer, directory layer over global layer, namely: function layer -> directory layer -> global layer

Cmakelist.txt file details

1, cmakelists.txt brief analysis

Create a C/C++Support project using AndroidStudio3.4. By default, the CPP directory containing cMakelists. TXT and native-lib. CPP is generated under app/ SRC /main. The following code is the content of cmakelists. TXT with the English comments removed and formatted:

cmake_minimum_required(VERSION 3.4.1)

# Add a library that compiles a native-lib dynamic library from the native-lib. CPP source file
add_library(
	native-lib
	SHARED
	native-lib.cpp)

Find the system library, in this case, the system log library, and assign to the variable log-lib
find_library(
	log-lib
	log)

# Set the dependent library (the first parameter must be the target module)
target_link_libraries(
	native-lib
	${log-lib})
Copy the code
  • cmake_minimum_required: Specifies the lowest supported version of cmake. This command is optional. If you use the higher version-specific command in the cMakelists. TXT file, you will need to add this command to specify the minimum supported version of CMake. If the current version of CMake is lower than the version specified, you will get an error.
  • aux_source_directory(. DIR_SRCS): Searches for all source files in the current directory and saves the source file name list toDIR_SRCSVariables; But you cannot find source files in subdirectories.
  • find_library: to find the system library, the default is inAndroidNDK directory xx \ \ platforms \ android - the arch - arm \ usr \ libThe lookup.

2, common command -add_library

1) Add a library

  • Add a library file named<name>.
  • Specify the STATIC, SHARED, and MODULE arguments to specify the library type. STATIC: STATIC library, SHARED: dynamic library, MODULE: used in dyLD system, if dyLD is not supported, the same as SHARED.
  • EXCLUDE_FROM_ALL: indicates that the library will not be built by default.
  • source1 source2 … SourceN: Specifies the source file of the library.
add_library(<name> [STATIC | SHARED | MODULE]
 [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
Copy the code

2) Import precompiled libraries

  • Add an existing precompiled library named<name>.
  • Usually used with set_target_Properties.
add_library(<name> <SHARED|STATIC|MODULE|UNKNOW> IMPORTED)

# such as
add_library(test SHARED IMPORTED)
set_target_properties(
	test # specifies the target database name
	PROPERTIES IMPORTED_LOCATION # specifies the parameter to be setThe library path /${ANDROID_ABI}/libtest.so # Import library path
)
Copy the code

3. Common command -set

Set the CMake variable:

(EXECUTABLE_OUTPUT_PATH is a global variable)
set(EXECUTABLE_OUTPUT_PATH [output_path])

# set LIBRARY_OUTPUT_PATH to LIBRARY_OUTPUT_PATH
set(LIBRARY_OUTPUT_PATH [output_path])

# set C++ compile parameters (CMAKE_CXX_FLAGS is global)
set(CMAKE_CXX_FLAGS "-Wall std=c++11")

# Set the set of source files (SOURCE_FILES is a local variable)
set(SOURCE_FILES main.cpp test.cpp ...)
Copy the code

4, the common command -include_directories

Set the header directory.

# can use relative or absolute paths, and can also use custom variable values
include_directories(. /include ${MY_INCLUDE})
Copy the code

Equivalent to the -l argument in the g++ option.

5, the common command -add_executable

Add executable file:

add_executable(<name> ${SRC_LIST})
Copy the code

6. Common command -target_link_libraries

target_link_libraries(<name> lib1 lib2 lib3)

CMake will allow cyclic dependencies to be included in the dependency graph if there are static libraries that depend on each other, such as:
add_library(A STATIC a.c)
add_library(B STATIC b.c)
target_link_libraries(A B)
target_link_libraries(B A)
add_executable(main main.c)
target_link_libraries(main A)
Copy the code
  • Link several libraries to the target library file.
  • If lib1 depends on lib2, and lib2 depends on lib3, then lib1, lib2, and lib3 must be in the same order as lib1, lib2, and lib3.

Use the -add_definitions command

Add the define flag introduced by -d to the source files in the current path and subdirectories

add_definitions(-DF00 -DDEBUG ...)
Copy the code

Usually used to add compile parameters

8. Run the -add_subdirectory command

CMake source files for adding subdirectories:

# sub_dir specifies the location of the subdirectory containing the cMakelists. TXT and source files
# binary_dir is the output path and may not be specified
add_subdirectory(sub_dir [binary_dir])
Copy the code

You can use add_subdirectory if there are subdirectories in the current directory, which also need to include cMakelists.txt.

9. Common command -file

File operation command:

# Writing message to filename overwrites the original contents of the file
file(WRITE filename "message")
# Write message to filename, appended to the end of the file
file(APPEND filename "message")
Read from filename and store in var; if numBytes and offset are specified,
If the HEX parameter is specified, the contents are stored in the var variable in hexadecimal format
file(READ filename var [LIMIT numbytes] [OFFSET offset] [HEX])
# Rename file
file(RENAME <oldname> <newname>)
# delete file, equal to rm command
file(REMOVE[file1 ...] )= rm -r = rm -rfile(REMOVE_RECURSE [file1...] )Download the file according to the specified URL
# timeout; The downloaded status is saved in status; Downloaded logs are saved to log. Sum specifies the expected MD5 value of the downloaded file. If specified, the MD5 value will be automatically compared.
If not, return an error; SHOW_PROGRESS: The progress information is printed as status information
file(DOWNLOAD url file [TIMEOUT timeout] [STATUS status] [LOG log] [EXPECTED_MD5 sum] [SHOW_PROGRESS])
# Create directory
file(MAKE_DIRECTORY[dir1 dir2 ...] )# converts path to cmake style path starting with Unix /, saved in result
file(TO_CMAKE_PATH path result)
# It converts cmake style paths to local style: "\" on Windows, "/" on Unix
file(TO_NATIVE_PATH path result)
A list of files will be generated for all files matching the query expression and stored in the variable variable. If an expression specifies RELATIVE, the result will be returned
*.cxx, *.vt?
# NOTE: According to the official documentation, it is not recommended to use the GLOB directive of file to collect the source files of a project
file(GLOB variable [RELATIVE path] [globbing expressions]...)
Copy the code

If you specify the source file in this way, if the project needs to add a new source file, for example, if there are two source files a.c and b.c in the project, then add a new c.c in the project. If you add c.c directly and then compile the project, you will get an error because the CMakeLists file has not been changed. Therefore, cmake does not regenerate the makefile, so we need to simply change the CMakeLists file. We can add a space to the CMakeLists file, and then compile it, and it will generate the makefile.

9. Run the -set_directory_properties command

Set a property of a path:

set_directory_properties(PROPERTIES prop1 value1 prop2 value2)
Copy the code

Prop1 and prop2 represent properties, which can be:

  • INCLUDE_DIRECTORIES
  • LINK_DIRECTORIES
  • INCLUDE_REGULAR_EXPRESSION
  • ADDITIONAL_MAKE_CLEAN_FILES

10. Common command -set_property

Set a named property in the given scope:

set_property(<GLOBAL | DIRECTORY [dir] | TARGET [target ...]  | SOURCE [src1 ...]  | TEST [test1 ...]  | CACHE [entry1 ...] > [APPEND] PROPERTY <name> [value ...] )Copy the code

The PROPERTY parameter is required. The first parameter determines the scope the property can affect:

  • GLOBAL: Indicates the GLOBAL scope
  • DIRECTORY: the default current path. You can also use [dir] to specify the path.
  • TARGET: TARGET scope, which can be zero or more existing targets.
  • SOURCE: scope of SOURCE files. The value can be 0 or more SOURCE files.
  • TEST: TEST scope, which can be zero or more existing tests.
  • CACHE: Specifies 0 or more existing entries in the CACHE.

11. Multiple source file processing

cmake_minimum_required(VERSION 3.4.1)
Find all source files in the current directory and save the names to the DIR_SRCS variable
# cannot find subdirectories
aux_source_directory(. DIR_SRCS)
# can also be used
# file(GLOB DIR_SRCS *.c *.cpp)

add_library(
	native-lib
	SHARED
	${DIR_SRCS})
Copy the code

If there are many source files and it is cumbersome to add them all one by one, you can use the aux_source_directory command or the file command, which will find all the source files in the specified directory and save the results in the specified variable name.

12, multi-directory multi-source file processing

cmake_minimum_required(VERSION 3.4.1)
aux_source_directory(. DIR_SRCS)
# add cmakelist to child subdirectory
add_subdirectory(child)

add_library(
	native_lib
	SHARED
	${DIR_SRCS})
target_link_libraries(native-lib child)
----------------------------------------

# child directory cmakelists. TXT:
cmake_minimum_required(VERSION 3.4.1)
aux_source_directory(. DIR_LIB_SRCS)
add_library(
	child
	SHARED
	${DIR_LIB_SRCS})
Copy the code
  • Add add_subdirectory(child) to cmakelists.txt in the home directory to indicate that this project contains a child project. It specifies in the target_link_libraries that the project needs to link to a library named Child.
  • Create cMakelists.txt in the subdirectory Child, where the child is compiled as a shared library.

13. Add precompiled libraries

1) before Android6.0

Suppose our local project references libimported-lib.so:

cmake_minimum_required(VERSION 3.4.1)
Use the IMPORTED flag to tell CMake that you only want the library to be IMPORTED into your project
If the library is static, change shared to static
add_library(imported-lib
			SHARED
			IMPORTED)

# Parameters are: library, property, import address, library address
set_target_propertiesIMPORTED_LOCATION < path >/libimported-lib.soaux_source_directory(. DIR_SRCS)
add_library(
	native-lib
	SHARED
	${DIR_SRCS})

target_link_libraries(native-lib imported-lib)
Copy the code
  • addadd_libraryCommand, the first argument is the module name, the second argumentSHAREDRepresents a dynamic library,STATICRepresents the static library, the third parameterIMPORTEDIndicates to be added in the form of an import.
  • addset_target_propertiesCommand to set import path attributes.
  • willimport-libAdded to thetarget_link_librariesIn the command parameters, the native-lib module needs to be linked to the imported lib module

2) after Android6.0

On Android6.0 and above, there is a problem with adding a precompiled dynamic library using the method in the previous section. We can configure it in another way:

The set command defines a variable
# CMAKE_C_FLAGS: c is passed to the compiler
For c++ files, use CMAKE_CXX_FLAGS
# -l: the path to the library
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -l [so directory])
Copy the code

14. Add the header directory

To ensure that CMake can locate header files at compile time, use include_directories, which corresponds to the -i parameter in the g++ option. #include

; otherwise, use #include “path/xx.h” :

cmake_minimum_required(VERSION 3.4.1)
Set header directory
include_directories(< file directory >)set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -l [so directory]")
aux_source_directory(. DIR_SRCS)
add_library(
	native-lib
	SHARED
	${DIR_SRCS})
target_link_libraries(native-lib imported-lib)
Copy the code

15, build. Gradle configuration

You can use Arguments to set some configurations in Gradle:

Android {defaultConfig{cmake{// use compiler clang/ GCC // cmake default is gnustl_static arguments"-DANDROID_TOOLCHAIN=clang"."-DANDROID_STL=gnustl_static"// Specify cflags and cppflags. The effect is the same as cmakelist""
			cppFlags ""// Specify the CPU architecture abiFilters to compile"armeabi-v7a"}} externalNativeBuild{cmake{// specify the path of the cMakelists. TXT file relative to the current build.gradle path"xxx/CMakeLists.txt"}}}Copy the code

Third, in field

Website: www.fmod.com/ SDK download page: www.fmod.com/download

You need to register and log in before you can download

1, library file integration

In fmod website to download fmodstudioapi20000android. Tar. Gz after decompression, find the API/core, including inc is fmod header files, lib is fmod precompiled good so and jar file.

1) Integrate SO and JAR

2) Integrate the. H header file

2, cmakelists. TXT file configuration:

The SO file provided by Fmod belongs to the pre-compiled library, so you need to integrate fMOD and use it in your own project, which can be roughly divided into two steps:

  1. Specifies the header directory. Convenient to use in native-lib. CPPinclude <fmod.hpp>
  2. Specifies the precompiled library directory. Make it possible for the native Lib to successfully link to the fmod at compile time.
# Specifies the minimum version of cmake supported
cmake_minimum_required(VERSION 3.4.1)

Set header directory
include_directories(${CMAKE_SOURCE_DIR}/inc)

# set third-party so library path (required after android6.0)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/.. /jniLibs/${CMAKE_ANDROID_ARCH_ABI}")

# Add a library that compiles a native-lib dynamic library from the native-lib. CPP source file
add_library(
        native-lib
        SHARED
        native-lib.cpp)

Find the system library, in this case, the system log library, and assign to the variable log-lib
find_library(
        log-lib
        log)

The first argument must be the target module, and the order cannot be changed.
target_link_libraries(
        native-lib
        fmod
        fmodL
        ${log-lib})
Copy the code

${CMAKE_SOURCE_DIR} to obtain the current directory path of the cMakelists. TXT file.

3. Build. Gradle configuration

Because the tool runs only on an emulator, only so library files from the x86 platform are needed, which can be filtered using abiFilters.

Mobile phones are generally ARM platforms, abiFilters can be modified according to the actual situation.

android {
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters "x86" // Specify the CPU architecture of the local library
            }
        }
        ndk {
            abiFilters "x86" // Specify the CPU architecture of the third-party library
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"}}}Copy the code

4. Write native lib.cpp

Using logs to output the FMOD version number:

#include <jni.h>
#include <string>
#include <android/log.h>
#include <fmod.hpp>

using namespace FMOD;

extern "C" JNIEXPORT jstring JNICALL
Java_com_lqr_cmakefmod_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    System *system;
    System_Create(&system);
    unsigned int version;
    system->getVersion(&version);
    __android_log_print(ANDROID_LOG_ERROR, "TEST"."FMOD Version: %08x", version);

    std: :string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
Copy the code

Finally, after the stringFromJNI() method is fired, you can see the output of the fmod version number in the console.