Android Studio supports CMake since version 2.2. You can use CMake and NDK to compile C/C++ code into an underlying library, which can then be packaged into APK with Gradle compilation.

This means there is no need to write.mk files to compile the so dynamic library.

CMake is a cross-platform build system that was widely used before Android Studio introduced CMake.

There is a demonstration of CMake on Google’s official website. You can refer to the official guide.

Summary of the official website for the use of CMake, in fact, the following steps:

  1. Add_library specifies the library to compile and adds all.c.cppThe file contains the specified.
  2. Include_directories adds the header file to the search path
  3. Set_target_properties sets some properties of the library
  4. Target_link_libraries associates libraries with other libraries

If you’re still not familiar with the above steps, take a closer look at CMake

The basic operation of CMake

Use Clion as a tool to explain the basic use of CMake.

CMake compiles the executable

A CPP file that prints Hello World and compiles it to an executable using CMake.

Create a cmakelists.txt file in the same directory as CPP with the following contents:

# specify the version used by CMake
cmake_minimum_required(VERSION 3.9)
# project name
project(HelloCMake)
# Compile the executable
add_executable(HelloCMake main.cpp )
Copy the code

The cmake_minimum_required method specifies the version used by CMake, and the project name is specified by project.

Add_executable specifies the name of the executable file to compile and the CPP file to compile, and if the project is large and has multiple CPP files, add them.

With the CMake file defined, you can start compiling and building.

CMake creates a lot of temporary files when it builds the project. To avoid contaminating the code with these temporary files, it typically places them in a separate directory.

The operation steps are as follows:

Create build directory under CPP
mkdir build
# call cmake to generate the makefile
cmake ..
# compiler
make
Copy the code

The resulting executable can be found in the Build directory.

This is a simple operation of CMake to compile CPP into an executable, but in Android most scenarios compile CPP into a library file.

CMake compiles both static and dynamic libraries

There is also a CPP file and a CMake file. The CPP file contains functions that print strings:

#include <iostream>
void print(a) {
    std: :cout << "hello lib" << std: :endl;
}
Copy the code

At the same time, the CMake file should be changed accordingly:

cmake_minimum_required(VERSION 3.12)
# specify the compiled library and file, SHARED build dynamic library
add_library(share_lib SHARED lib.cpp)
# STATIC Compile STATIC libraries
# add_library(share_lib STATIC lib.cpp)
Copy the code

Add_library specifies the name of the library to compile, the dynamic or static library, and the file to compile.

Finally, do the same with the build, and you can see the generated library files in the Build directory.

At this point, you are basically ready to use CMake to build C/C++ projects.

CMake basic syntax

Once you are familiar with the basic operations above, you will inevitably encounter the following problems:

  • If there are many C/C++ files to be compiled, do you want to add each one manually?
  • Can compiled executable files or libraries be automatically placed in the specified location?
  • Can I specify a version number for the compiled library?

With these questions in mind, it is still necessary to further study the syntax of CMake. The best learning material is the official website documentation.

To avoid confusion when looking directly at official documents, here are some common syntax commands.

Comments and case

CMake comments have already been used, with the # at the beginning of each line representing comments.

In addition, all syntax instructions for CMake are case insensitive.

Variable definition and message printing

Use set to define a variable:

# The variable name is var and the value is Hello
set(var hello) 
Copy the code

When you need to reference a variable, add ${} to the name of the variable to reference it.

Var variable
${var}
Copy the code

You can also print print on the command line via message.

set(var hello) 
message(${var})
Copy the code

Math and string manipulation

Mathematical operations

Math is used in CMake to perform mathematical operations.

# math uses EXPR for size
math(EXPR <output-variable> <math-expression>)
Copy the code
math(EXPR var "1 + 1")
The output is 2
message(${var})
Copy the code

Math support +, -, *, /, %, |, &, ^. ~, < <, > >, etc, and roughly the same in the C language.

String manipulation

CMake implements string operations through strings. There are many operations in this wave, including all uppercase, all lowercase, length of string, find and replace, and so on.

Check the official documentation for details.

set(var "this is string")
set(sub "this")
set(sub1 "that")
# string lookup, the result is stored in the result variable
string(FIND ${var} ${sub1} result )
Output 0 was found, otherwise -1
message(${result})

# uppercase all strings
string(TOUPPER ${var} result)
message(${result})

Find the length of the string
string(LENGTH ${var} num)
message(${num})
Copy the code

In addition, sequences of strings can be represented by whitespace or delimiters.

set(foo this is a list) // The actual contents are stringsmessage(${foo})
Copy the code

When whitespace or delimiters are needed in a string, double parentheses “” are used to indicate the same string content.

set(foo "this is a list") // The actual content is a stringmessage(${foo})
Copy the code

File operations

CMake uses file to operate files, including reading and writing files, downloading files, and renaming files.

Check the official documentation for details

Rename the file
file(RENAME "test.txt" "new.txt")

# File download
Set the file URL as a variable
set(var "http://img.zcool.cn/community/0117e2571b8b246ac72538120dd8a4.jpg")

# DOWNLOAD it
file(DOWNLOAD ${var} "/Users/glumes/CLionProjects/HelloCMake/image.jpg")
Copy the code

There are also two important instructions in the file operation, GLOB and GLOB_RECURSE.

# GLOB usage
file(GLOB ROOT_SOURCE *.cpp)
# GLOB_RECURSE
file(GLOB_RECURSE CORE_SOURCE ./detail/*.cpp)
Copy the code

The GLOB directive forms a list of all files that match the *.cpp expression and stores it in the ROOT_SOURCE variable.

The GLOB_RECURSE directive is similar to GLOB, but it iterates through all files in the matching directory and files under subdirectories.

GLOB and GLOB_RECURSE have the benefit of adding files that need to be compiled one by one. The contents of the same directory are included in the corresponding variables, but CMake does not change, causing the build file to be regenerated at compile time. To fix this, you just need to tweak CMake so that the compiler detects that it has changed.

Predefined constants

There are a number of predefined constants in CMake that can be used to make a difference.

  • CMAKE_CURRENT_SOURCE_DIR
    • Indicates the path of the current CMake file folder
  • CMAKE_SOURCE_DIR
    • Indicates the CMake file path of the current project
  • CMAKE_CURRENT_LIST_FILE
    • Refers to the full path of the current CMake file
  • PROJECT_SOURCE_DIR
    • Indicates the path of the current project

For example, in add_library you need to specify the path to the CPP file. Using CMAKE_CURRENT_SOURCE_DIR as a benchmark, specify the path to the CPP relative to it.

# use predefined constants to specify file paths
add_library( # Sets the name of the library.
             openglutil
             # Sets the library as a shared library.
             SHARED
             # Provides a relative path to your source file(s).
             ${CMAKE_CURRENT_SOURCE_DIR}/opengl_util.cpp
             )
Copy the code

Platform dependent constants

CMake can be used to compile on Windows, Linux, and Mac platforms, and it also defines variables that are relevant to those platforms.

Check the official documentation for details.

To name some common ones:

  • WIN32
    • If the target system is Windows, WIN32 is True.
  • UNIX
    • True for Unix if the compiled target system is Unix or uniX-like that is Linux.
  • MSVC
    • If the compiler is something like Visual C++ on a Window, then MSVC is True.
  • ANDROID
    • If the target system is Android, then Android is 1.
  • APPLE
    • If the target system is APPLE, then APPLE is 1.

With these constants as a differentiator, you can write compilation options for different platforms in a single CMake file.

if(WIN32){
    # do something
}elseif(UNIX){
    # do something
}
Copy the code

Functions, macros, flow control, options and other commands

See cmake-Commands for details, which include many important and common commands.

A simple example of a function operation in CMake:

function(add a b)
    message("this is function call")
    math(EXPR num "${a} + ${b}" )
    message("result is ${aa}")
endfunction()

add(1 2)
Copy the code

Function is the function defined, the first parameter is the function name, followed by the function parameters.

When calling a function, separate arguments with Spaces, not commas.

Macros are used in a similar way to functions:

macro(del a b)
    message("this is macro call")
    math(EXPR num "${a} - ${b}")
    message("num is ${num}")
endmacro()

del(1 2)
Copy the code

In terms of flow control, CMake also provides if and else operations:

set(num 0)
if (1 AND ${num})
    message("and operation")
elseif (1 OR ${num})
    message("or operation")
else(a)message("not reach")
endif(a)Copy the code

CMake provides AND, OR, NOT, LESS, EQUAL, etc. For example, AND requires both sides to be True.

CMake also provides operations for iterating through loops:

set(stringList this is string list)
foreach (str ${stringList})
    message("str is ${str}")
endforeach(a)Copy the code

CMake also provides an option directive.

You can use it to define some global options for CMake:

option(ENABLE_SHARED "Build shared libraries" TRUE)

if(ENABLE_SHARED)
    # do something
else(a)# do something   
endif(a)Copy the code

You might think that option is nothing more than a True or False flag bit, which can be replaced by a variable, but with a variable, you have to add ${} to the variable, and use option to refer to the name.

CMake Reading practice

Once you understand the above CMake syntax and look up strange instructions from the official website, you can basically understand most CMake files.

Here are two examples of open source libraries:

  • github.com/g-truc/glm
    • glmIs a used to achieve matrix calculation, in the development of OpenGL will be used.
    • CMakeLists.txtThe address inhere
  • Github.com/libjpeg-tur…
    • libjpeg-turboIs used for image compression, which is used at the bottom of Android.
    • CMakeLists.txtThe address inhere

These two examples use a lot of the previous content, try to read to increase your proficiency.

Set properties for the compiled library

Back to compiling dynamic libraries with CMake, after all, Android NDK development is mainly used to compile libraries, so after compiling so, we can do something with it.

Use set_target_properties to set the properties of the compiled library. The function prototype is as follows:

set_target_properties(target1 target2 ...
                      PROPERTIES prop1 value1
                      prop2 value2 ...)
Copy the code

For example, to change the name of the compiled library:

set_target_properties(native-lib PROPERTIES OUTPUT_NAME "testlib" )
Copy the code

For more information about attributes, see the official documentation.

However, there are some properties that don’t work on Android Studio, but they do on CLion, or maybe I’m using them the wrong way.

For example, implementing the version number of the dynamic library:

set_target_properties(native-lib PROPERTIES VERSION 1.2 SOVERSION 1 )
Copy the code

If you want to import a compiled dynamic library, you also need to use a property.

Like the compiled FFmpeg dynamic library,

IMPORTED_LOCATION: IMPORTED_LOCATION: IMPORTED_LOCATION: IMPORTED_LOCATION: IMPORTED_LOCATION: IMPORTED_LOCATION: IMPORTED_LOCATION: IMPORTED_LOCATION set_target_properties(avcodec-57_lib PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/armeabi/libavcodec-57.so )Copy the code

Links to other libraries

If you compile multiple libraries and want to link between them, use target_link_libraries.

target_link_libraries( native-lib
                       glm
                       turbojpeg
                       log )
Copy the code

Android in the bottom also provides some so library for the upper link to use, but also through the above way to link, such as the most common is the log library to print logs.

If you want to link multiple libraries you compiled, first make sure that the code for each library corresponds to a cmakelists.txt file that specifies information about the current library to be compiled.

Then add the directories of other libraries in the cmakelists.txt file of the current library by ADD_SUBDIRECTORY so that you can link to.

ADD_SUBDIRECTORY(src/main/cpp/turbojpeg)
ADD_SUBDIRECTORY(src/main/cpp/glm)
Copy the code

Add header file

An easy step to skip is to add a header file, including it through the include_directories directive.

This allows you to include the header file directly using #include “header.h” instead of adding a path like #include “path/path/header.h”.

summary

That’s the summary of CMake.

Welcome to pay attention to wechat public number: [paper talk], get the latest article push ~~~