Cmake — practice of building software projects gracefully (1)

Time:2021-8-29

This article is original, reprint and indicate the source. Welcome to pay attention to wechat appletXiaobai AI blogWeChat official accountXiaobai AIOr website https://xiaobaiai.net Or my CSDN http://blog.csdn.net/freeape

Cmake --- practice of building software projects gracefully (1)

[TOC]

First of all, this article does not start from the whole syntax of cmake, but from the construction of an actual project to understand how to build a software project gracefullyC/C++The basic dependent components of a software project form a buildC/C++The template of the software project to facilitate the reuse of new projects later. I believe it will be a good harvest for our daily software project construction. Don’t talk nonsense. Let’s go.

1 basis we need to know

First of all, if we are familiar with some basic operations of cmake, we can easily and gracefully build a project to avoid stepping on unnecessary pits. Involved are:

  • Variable scope of cmake?
  • Data structure in cmake?
  • Macro function and function?
  • How to build dynamic and static libraries and find them?
  • How to support multi platform project construction?
  • How to build an application?
  • How to implement the final install of the project?
  • How to display various levels of information about the construction process in a friendly way?
  • How to adapt cmake GUI and build it with friendly ccmake or cmake GUI?

Here is a general description of common cmake knowledge. Generally speaking, cmake is used to find dependent header files and library files, compile source files, link target files (static libraries are also a collection of target files), and finally generate executable files or dynamic / static libraries:

  • INCLUDE_DIRECTORIESAdds the given directory to the directory that the compiler uses to search for files (such as header files), and the relative path is interpreted as relative to the current source directory. Note that the directory is only added to the current cmakelists file and acts on the libraries, executables or files related to the current cmakelists fileSub moduleCompilation is invalid for the juxtaposition of two different cmakelists.cmake. Different fromTARGET_INCLUDE_DIRECTORIES, this command only works on the specified target and adds a search path for the specified target. Similarly, there areTARGET_LINK_LIBRARIESCommand (add the library file directory that needs to be linked).
  • PROJECT_SOURCE_DIR: no doubt, as long as there is cmakelists.txt containing the latest project() command declaration, it is relative to the cmakelists.txt path.
  • CMAKE_SOURCE_DIR: when building the whole project, you may rely on a third-party project. The value of this variable is the path of cmakelists.txt at the top level.
  • stayfind_pathandfind_libraryas well asfind_packageSome default paths are searched. When we install some LIBS in the non default search path, cmake cannot be searched. You can set:

    • SET(CMAKE_ INCLUDE_ PATH “include_ path”) // find_ Path to find the header file
    • SET(CMAKE_ LIBRARY_ PATH “lib_ path”) // find_ Library to find the library file
    • SET(CMAKE_MODULE_PATH “module_path”) // find_package
  • You don’t need to write findxx.cmake to find 3rdparty. You can also use include (XXX. Cmake) directlyfind_fileCommand implementation, find dependent Library_ The results found in file are stored in the cache variable, for example:
# Once done, this will define                                                      
#                                                                                  
#  NANOMSG_INCLUDE_DIR - the NANOMSG include directory                             
#  NANOMSG_LIBRARY_DIR - the SPDLOG library directory                                                                                                                                          
#  NANOMSG_LIBS - link these to use NANOMSG                                        
#                                                                                  
#  SPDLOG_INCLUDE_DIR - the SPDLOG include directory                               
#  SPDLOG_LIBRARY_DIR - the SPDLOG library directory                               
#  SPDLG_LIBS - link these to use SPDLOG                                           
                                                                                   
MACRO(LOAD_LIBNANOMSG os arch)                                                     
    SET(3RDPARTY_DIR ${PROJECT_SOURCE_DIR}/3rdparty/target/${${os}}_${${arch}}) 
    MESSAGE(STATUS "3RDPARTY_DIR: ${3RDPARTY_DIR}")                                
    FIND_FILE(NANOMSG_INCLUDE_DIR include ${3RDPARTY_DIR} NO_DEFAULT_PATH)         
    FIND_FILE(NANOMSG_LIBRARY_DIR lib ${3RDPARTY_DIR} NO_DEFAULT_PATH)             
                                                                                   
    SET(NANOMSG_LIBS                                                               
        nanomsg                                                                    
        pthread                                                                    
        anl          
        PARENT_SCOPE
    )                                                                              
    IF(NANOMSG_INCLUDE_DIR)                                                        
        MESSAGE(STATUS "NANOMSG_LIBS : ${NANOMSG_LIBS}")                           
    ELSE()                                                                         
        MESSAGE(FATAL_ERROR "NANOMSG_LIBS not found!")                             
    ENDIF()                                                                        
ENDMACRO()
  • Example of conditional control switching:
# set target
if (NOT YOUR_TARGET_OS)
    set(YOUR_TARGET_OS linux)
endif()

if (NOT YOUR_TARGET_ARCH)
    set(YOUR_TARGET_ARCH x86_64)
endif()

if (NOT YOUR_BUILD_TYPE)
    set (YOUR_BUILD_TYPE Release)
endif()

......

if(${YOUR_TARGET_ARCH} MATCHES "(arm*)|(aarch64)")
    ......
elseif(${YOUR_TARGET_ARCH} MATCHES x86*)
    ......
  • Cross compilation:CMAKE_TOOLCHAIN_FILEVariables,
MESSAGE(STATUS "Configure Cross Compiler")

IF(NOT TOOLCHAIN_ROOTDIR)                                                       
    MESSAGE(STATUS "Cross-Compiler defaut root path: $ENV{HOME}/Softwares/arm-himix200-linux")
    SET(TOOLCHAIN_ROOTDIR "$ENV{HOME}/Softwares/arm-himix200-linux")            
ENDIF()                                                                         

SET(CMAKE_SYSTEM_NAME Linux)                                                    
SET(CMAKE_SYSTEM_PROCESSOR arm)                                                 

SET(CMAKE_C_COMPILER       ${TOOLCHAIN_ROOTDIR}/bin/arm-himix200-linux-gcc)        
SET(CMAKE_CXX_COMPILER     ${TOOLCHAIN_ROOTDIR}/bin/arm-himix200-linux-g++)        

# set searching rules for cross-compiler                                        
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)                                    
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)                                     
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)                                     

SET(YOUR_TARGET_OS linux)                                                       
SET(YOUR_TARGET_ARCH armv7-a) 

SET(CMAKE_CXX_FLAGS "-std=c++11 -march=armv7-a -mfloat-abi=softfp -mfpu=neon-vfpv4 ${CMAKE_CXX_FLAGS}")
  • AUX_SOURCE_DIRECTORYDoes not recursively include subdirectories, only the specified dir directory
  • ADD_SUBDIRECTORYFor the compilation of sub modules, cmakelists.txt can be compiled in the sub folder or under the specified external folder.
  • ADD_LIBRARYWhen compiling a dynamic / static library or module, the set name must be unique in the whole project, and it has nothing to do with the parent-child folder path in the same projectTARGET_LINK_LIBRARIESDepend on this module.
  • ADD_DEFINITIONS(-DTEST -DFOO="foo")add toFOOandTESTMacro definition.

2. We should build gracefully

For a large software project, we will rely on many third-party projects, including source code dependency or library dependency, and then build our own software project completely. We need to build the dependent project or find the library we need; In addition, software projects will consider portability, that is, they can build projects on different platforms and friends, and can quickly start construction when they are transferred to another development environment.

In addition to the above, we also need to consider the architecture and source code structure of our actual software project, which can enable developers to understand the whole project more clearly and quickly.

In addition, C / C + + programmers have been manually managing dependencies for a long time, that is, manually finding and installing dependencies, and then configuring build tools (such as cmake) to use dependencies. Cmake also provides a series of find_ The package method helps simplify configuration dependencies. Cmake also supports multi project / module management. If the dependent source code is managed and built by cmake at the same time, the situation will be much simpler. This method is called source level dependency management. With the emergence and widespread use of code management tool git, GIT submodule provides a good source level dependency management method.

To sum up, by building a software project gracefully, we realize:

  • The source code of software projects depends on third-party projects
  • The software project library relies on third-party projects
  • Clear software project structure
  • Rapid realization of software project construction in the new environment of transformation
  • Information friendly display in the process of software project construction
  • After the software project is built, it is packaged and released
  • Software projects support cross platform construction
  • Software projects support cross build
  • Git submodule & cmake manage / build source level dependencies

In addition, we also implement a reusable C / C + + minimum development framework (this will be described in subsequent articles):

  • Support logging
  • Support task pool / thread pool
  • Support common related basic operation components

    • Time date operation
    • File read / write operation
  • Support Valgrind memory leak checking tool
  • Support static code checking
  • Support project document automation
  • …..

3 elegant software project structure template

3.1 formwork I

For an independent application, the application modules are interconnected and difficult to separate from each other. In this way, all source files and header files can be put together. For less complex applications, it is very fast to start the construction and source file modification operations:

.
├── 3rdparty
├── cmake
├── include
├── src
├── doc
├── tests
├── benchmarks
├── docker
├── CMakeLists.txt

3.2 formwork II

The source files and header files are stored in functional modules. This method is relatively simple, but if they become a 3rdparty of other projects, they need to be separated from the header files during installation and can not be directly referenced by other projects. Personally, I think it is applicable to app projects rather than SDK projects (for example, nanomsg, an open source messaging middleware library, puts the header files and source files together, However, it is not very direct and convenient to use the SDK as an external link. You need to do the install operation, or set the header file search scope to the SRC level of the dependent project, and the module classification under the SRC directory is very clear):

├── 3rdparty
    Source code dependency -- submodule # stores source code dependency
    Target - target # repository dependency
    ├── CMakeLists.txt
    - cmake # store find_ Package cmake file
├── cmake
├── platforms
│   └── linux
│       └── arm.toolchain.cmake
├── src
    ├── moudle1
        ├── source & include file 
    ├── moudle2
        ├── source & include file
    ├── ......
├── doc
├── tests
├── samples
├── benchmarks
├── docker
├── CMakeLists.txt

3.3 formwork III

The software project can be divided into many modules. Each module can be independent of each other or combined together. Typically, such as opencv project. Of course, this is also applicable to application projects, but the directory structure of application projects is too deep, which is a little inconvenient for development and editing:

├── 3rdparty
├── cmake
├── platforms
│   └── linux
│       └── arm.toolchain.cmake
This directory is only a summary of the header files of each function module 
├── modules
    ├── moudle1
        ├── src
        ├── include
    ├── moudle2
    ├── ......
├── doc
├── tests
├── samples
├── benchmarks
├── docker
├── CMakeLists.txt

4. Cmake implementation of elegant software project structure template

Here we only implement template 2. Other templates are similar. As described in the template section above, we

4.1 determination of directory structure

.
Source code dependency and library dependency storage location of 3rdparty # third party Library
│     Source code -- cmakelists.txt # third party library source code relies on compiling cmakelists file
│     Figure 1 - spdlog # source code dependency example project spdlog (GitHub searchable)
│     └ - target # library depends on storage directory
│         ├── linux_ Armv7-a # is distinguished by platform and architecture names
│         │     File - include # header file storage directory
│         │     └ - lib # library file storage directory
│       └── linux_x86-64
│           ├── include
│           └── lib
- cmake # stores cmakem module files related to the project
│   ├── load_3rdparty.cmake
│   ├── messagecolor.cmake
│   ├── toolchain_options.cmake
│   └── utils.cmake
Cmakelists.txt # project root directory, cmakelists file, cmake entry file
♪ conf # project configuration file storage directory
Doc - doc # project document storage directory
Platform - storage directory of platform related contents of platforms # project, including cross compilation
│   └── linux
│       └── arm.himix200.cmake
Description - readme.md # project description
Keywords - scripts # related scripts storage directory, including continuous integration and deployment related scripts
Source code - SRC # project source code directory
│   ├── CMakeLists.txt
│   ├── common
│   ├── logger
│   └── main
└ - directory for storing the source code of tests # test examples
    ├── CMakeLists.txt
    └── test_logger.cpp

4.2 project version management

Whether it is an SDK or an app project, there will be a version to record each node of software release. The software version can make it convenient for users or themselves to clearly know what content is updated in each version, and can make choices for the version or solve the bugs encountered in the version. To realize version management, you need to be able to clearly reflect the current version number in the compilation process and obtain the version number in the software. Here, the version number management uses the commonmajor.minor(.patch)Format, major is the largest version number, minor is the second, and patch corresponds to the patch level in the small version. When there is a great update, the version number of the major will be increased. When there is a big update but not the major, the version number of the minor will be updated. If the update is small, such as just bug fixing, the version number of the patch will be updated. Example of version number format:v1.0v1.2.2Wait.

In the elegant build software template, we put the version information insrc/common/version.hppIn the file:

Note: all file paths are relative to the project root directory.

#pragma once                                                                       

// for cmake
//Used for parsing in cmakelists file
// 0.1.0                                                                 
#define HELLO_APP_VER_MAJOR 0                                                      
#define HELLO_APP_VER_MINOR 1                                                      
#define HELLO_APP_VER_PATCH 0                                                      

#define HELLO_APP_VERSION (HELLO_APP_VER_MAJOR * 10000 + HELLO_APP_VER_MINOR * 100 + HELLO_APP_VER_PATCH)

// for source code
//Used to obtain the version number string in the project source code
// v0.1.0                                                           
#define _HELLO_APP_STR(s) #s                                                       
#define HELLO_PROJECT_VERSION(major, minor, patch) "v" _HELLO_APP_STR(major.minor.patch)

In the cmakelists module file, we parse the file to obtain the version number into the cmake variablecmake/utils.cmakeAdd macro function:

FUNCTION(hello_app_extract_version)                                 
    FILE(READ "${CMAKE_CURRENT_LIST_DIR}/src/common/version.hpp" file_contents) 
    STRING(REGEX MATCH "HELLO_APP_VER_MAJOR ([0-9]+)" _  "${file_contents}")       
    IF(NOT CMAKE_MATCH_COUNT EQUAL 1)                                           
        MESSAGE(FATAL_ERROR "Could not extract major version number from version.hpp")
    ENDIF()                                                                     
    SET(ver_major ${CMAKE_MATCH_1})                                             

    STRING(REGEX MATCH "HELLO_APP_VER_MINOR ([0-9]+)" _  "${file_contents}")       
    IF(NOT CMAKE_MATCH_COUNT EQUAL 1)                                           
        MESSAGE(FATAL_ERROR "Could not extract minor version number from version.hpp")
    ENDIF()                                                                     
    SET(ver_minor ${CMAKE_MATCH_1})                                             
    STRING(REGEX MATCH "HELLO_APP_VER_PATCH ([0-9]+)" _  "${file_contents}")       
    IF(NOT CMAKE_MATCH_COUNT EQUAL 1)                                           
        MESSAGE(FATAL_ERROR "Could not extract patch version number from version.hpp")
    ENDIF()                                                                     
    SET(ver_patch ${CMAKE_MATCH_1})                                             

    SET(HELLO_APP_VERSION_MAJOR ${ver_major} PARENT_SCOPE)                      
    SET (HELLO_APP_VERSION "${ver_major}.${ver_minor}.${ver_patch}" PARENT_SCOPE)
ENDFUNCTION()

Call version macro in root directory CMakeLists:

CMAKE_MINIMUM_REQUIRED(VERSION 3.4)                                             

#--------------------------------------------                                   
# Project setting                                                               
#--------------------------------------------                                   
INCLUDE(cmake/utils.cmake)                                                      
HELLO_APP_EXTRACT_VERSION()                                                     

PROJECT(HelloApp VERSION ${HELLO_APP_VERSION} LANGUAGES CXX)                    

MESSAGE(INFO "--------------------------------")                                
MESSAGE(STATUS "Build HelloApp: ${HELLO_APP_VERSION}")

In the following dynamic and static library generation, you can set the version, such as:

SET_TARGET_PROPERTIES(MyLib PROPERTIES VERSION ${HELLO_APP_VERSION}
                                          SOVERSION ${HELLO_APP_VERSION_MAJOR})

This will generate aliMyLibr.so => liMyLib.so.0 => libMyLib.so.0.1.1Libraries and related soft links. However, this operation should be used with caution, because it is impossible to find JNI relying on libraries with versions on the Android platform.

4.3 third party library dependency

The third-party library depends on the need to write the library and header file search function ourselves. The storage location of the third-party library is distinguished by the platform and architecture. The directory structure will not change with the creation of the project. Examples of macro functions found in the library are as follows:

# Once done, this will define                                                                                                                                                                  
#                                                                               
#  SPDLOG_INCLUDE_DIR - the SPDLOG include directory                            
#  SPDLOG_LIBRARY_DIR - the SPDLOG library directory                            
#  SPDLG_LIBS - link these to use SPDLOG                                        
#                                                                               
#  ......                                                                       
                                                                                
MACRO(LOAD_LIBSPDLOG os arch)                                                   
    SET(3RDPARTY_DIR ${PROJECT_SOURCE_DIR}/3rdparty/target/${${os}}_${${arch}}) 
    MESSAGE(STATUS "3RDPARTY_DIR: ${3RDPARTY_DIR}")                             
    FIND_FILE(SPDLOG_INCLUDE_DIR include ${3RDPARTY_DIR} NO_DEFAULT_PATH)          
    FIND_FILE(SPDLOG_LIBRARY_DIR lib ${3RDPARTY_DIR} NO_DEFAULT_PATH)           
                                                                                
    SET(SPDLOG_LIBS                                                             
        spdlog          
        pthread
        #PARENT_SCOPE no parent                                                 
    )                                                                           
    IF(SPDLOG_INCLUDE_DIR)                                                      
        SET(SPDLOG_LIBRARY_DIR "${SPDLOG_LIBRARY_DIR}/spdlog")                  
        MESSAGE(STATUS "SPDLOG_INCLUDE_DIR : ${SPDLOG_INCLUDE_DIR}")            
        MESSAGE(STATUS "SPDLOG_LIBRARY_DIR : ${SPDLOG_LIBRARY_DIR}")            
        MESSAGE(STATUS "SPDLOG_LIBS : ${SPDLOG_LIBS}")                          
    ELSE()                                                                      
        MESSAGE(FATAL_ERROR "SPDLOG_LIBS not found!")                           
    ENDIF()                                                                     
ENDMACRO()

Note: ifSPDLOG_LIBSVariable if macro function is invoked in root directory CMakeLists, variable scope can act on all subdirectories. If it is not called in root directory, it needs setting up.PARENT_SCOPEProperties.

In the main CMakeLists, macro function is invoked to realize information import from three party libraries.

INCLUDE(cmake/load_3rdparty.cmake)                                              
                                                                                
IF(NOT YOUR_TARGET_OS)                                                          
    SET(YOUR_TARGET_OS linux)                                                   
ENDIF()                                                                         
IF(NOT YOUR_TARGET_ARCH)                                                        
    SET(YOUR_TARGET_ARCH x86-64)                                                
ENDIF()                                                                         
MESSAGE(STATUS "Your target os : ${YOUR_TARGET_OS}")                            
MESSAGE(STATUS "Your target arch : ${YOUR_TARGET_ARCH}")                        
                                                                                
LOAD_LIBSPDLOG(YOUR_TARGET_OS YOUR_TARGET_ARCH)

4.4 third party library source code dependency

If you want to rely on the source code of a third-party project and compile it together, we cangit submoduleTo manage the third-party source code and realize source code dependency and its version management. Of course, you can manually insert the source code without git submodule3rdpartyIn the directory.

Add a git submodule:

#URL is git project address
#Path is the project storage directory, which can be multi-level directory. The directory name is generally the project name
# git add <url.git> <path>
#After the example is executed, the project source code will be directly pulled to the 3rdparty / spdlog directory, and the. Gitmodule will be created in the root directory of the warehouse
$ git submodule add  https://github.com/gabime/spdlog.git 3rdparty/spdlog

You can also add with a specified branch:

#Note: the command needs to be executed under the project root directory. The first time, the source code will be pulled directly without update
$ git submodule add -b v1.x   https://github.com/gabime/spdlog.git 3rdparty/spdlog
$ git submodule update --remote

final.gitmodulesThe file is:

[submodule "3rdparty/spdlog"]
    path = 3rdparty/spdlog                                                   
    url = https://github.com/gabime/spdlog.git 
    branch = v1.x

Implement the compilation of the source code of the third-party project (first, the source code of the third-party project you rely on supports the cmake construction method)3rdparty/CMakeLists.txtWritten in:

CMAKE_MINIMUM_REQUIRED(VERSION 3.4)                                             
PROJECT(HiApp3rdparty)

ADD_SUBDIRECTORY(spdlog) 

Include cmakelists.txt in the 3rdparty in the root directory cmakelists.txt to compile the third-party library:

ADD_SUBDIRECTORY(3rdparty)

adoptTARGET_LINK_LIBRARIESYou can specify a third-party project name to implement the link.

4.5 function module addition

4.5.1 function module compilation

For example, we need to add a log module to implementspdlogA secondary package of the project, which can be better used in our own project, then we establishsrc/loggerNew directorylogger.hpplogger.cppandCMakeLists.txtThree files, in which cmakelists.txt is used to compile the log module:

CMAKE_MINIMUM_REQUIRED(VERSION 3.4)

AUX_SOURCE_DIRECTORY(. CURRENT_DIR_SRCS)                                        
ADD_LIBRARY(module_logger ${CURRENT_DIR_SRCS})   
# SPDLOG_ LIBS is the name of the spdlog project library
TARGET_LINK_LIBRARIES(module_logger ${SPDLOG_LIBS})

Then insrc/CMakeLists.txtContains the compilation of the log module:

ADD_SUBDIRECTORY(logger)

In the root directoryCMakeLists.txtContains subdirectoriessrcTo realize the construction of functional modules:

ADD_SUBDIRECTORY(src)

Note: for demonstration, both library dependency and source dependency use spdlog. If you implement the log module here, you need to choose one of them.

4.5.2 executable compilation

If we need to implement the call of the executable file to the log module, we can addsrc/main/main.cppFile, insrc/CMakeLists.txtAdd compilation of executable files in:

# main app                                                                      
SET(SRC_LIST ./main/main.cpp)                                                   

ADD_EXECUTABLE(HiApp ${SRC_LIST})
#Configure executable output directory
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)                           
TARGET_LINK_LIBRARIES(HelloApp module_logger)

Of course, if you usec++11We can create a cmake file specificallycmake/toolchain_options.cmakeTo configure compilation options, wherec++11Compile options and include the cmake file in the main cmakelists.txt:

# compiler configuration            
#Cmake is not supported until cmake version 3.1_ CXX_ Standard configuration item
IF(CMAKE_VERSION VERSION_LESS "3.1")                                            
    IF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")                                    
        SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")                  
    ENDIF()                                                                     
ELSE()                                                                          
    SET(CMAKE_CXX_STANDARD 11)                                                  
ENDIF()

4.6 adding test samples

Test samples are placed intestsDirectory, and create a cmakelists.txt file in this directory to build all test demos and include them under the main cmakelists.txttestscatalog:

CMAKE_MINIMUM_REQUIRED(VERSION 3.4)

PROJECT(Tests)                                                                  

INCLUDE_DIRECTORIES(                                                            
    ${SPDLOG_INCLUDE_DIR}                                                       
    ${CMAKE_SOURCE_DIR}/src                                                     
)                                                                               

LINK_DIRECTORIES(                                                               
    ${SPDLOG_LIBRARY_DIR}                                                       
)                                                                               

FILE(GLOB APP_SOURCES *.cpp)                                                    
FOREACH(testsourcefile ${APP_SOURCES})                                          
    STRING(REGEX MATCH "[^/]+$" testsourcefilewithoutpath ${testsourcefile})    
    STRING(REPLACE ".cpp" "" testname ${testsourcefilewithoutpath})             
    ADD_EXECUTABLE( ${testname} ${testsourcefile})                              
    SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin/tests)                   
    TARGET_LINK_LIBRARIES(${testname}                                           
        ${SPDLOG_LIBS}                                                          
        module_logger                                                           
        )                                                                       
ENDFOREACH(testsourcefile ${APP_SOURCES})

Then you cantestsAdd a test program under the directory, such astest_logger.cppOr more test demos,tests/CMakeLists.txtWill automaticallytestsAll source files in the directory are generated and built one by one. The construction of the whole test sample is completed.

4.7 cross compilation configuration

Cmake provides us with variable settings for cross compilation, i.eCMAKE_TOOLCHAIN_FILEThis variable, as long as we specify the cross compiled cmake configuration file, cmake will import the compiler configuration, compilation option configuration, etc. in the configuration file. The cross compilation tool chain configuration file we designed is stored in the directoryplatforms/Next, we use a compilation tool of Huawei Hisilicon. We name it by category and create a toolbar cmake configuration fileplatforms/linux/arm.himix200.cmake:

MESSAGE(STATUS "Configure Cross Compiler") 
SET(CMAKE_SYSTEM_NAME Linux)                                                    
SET(CMAKE_SYSTEM_PROCESSOR arm)                                                 

SET(CMAKE_C_COMPILER       arm-himix200-linux-gcc)                              
SET(CMAKE_CXX_COMPILER     arm-himix200-linux-g++)                              

# set searching rules for cross-compiler                                        
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)                                    
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)                                     
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)                                     

SET(YOUR_TARGET_OS linux)                                                       
SET(YOUR_TARGET_ARCH armv7-a)                                                   

SET(CMAKE_SKIP_BUILD_RPATH TRUE)                                                
SET(CMAKE_SKIP_RPATH TRUE)                                                      

# set ${CMAKE_C_FLAGS} and ${CMAKE_CXX_FLAGS}flag for cross-compiled process       
#SET(CROSS_COMPILATION_ARM himix200)                                            
#SET(CROSS_COMPILATION_ARCHITECTURE armv7-a)                                    

# set g++ param                                                                 
# -fopenmp link libgomp                                                         
SET(CMAKE_CXX_FLAGS "-std=c++11 -march=armv7-a -mfloat-abi=softfp -mfpu=neon-vfpv4 \
    -ffunction-sections \                                                       
    -fdata-sections -O2 -fstack-protector-strong -lm -ldl -lstdc++\             
    ${CMAKE_CXX_FLAGS}")

Note: the cross compilation tool chain needs to be installed on the compilation host. In addition, the third-party library also needs to compile the tool chain Version (except for source code dependency).

Perform cross compilation from the command line:

$ mkdir build
$ cd build
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=../platforms/linux/arm.himix200.cmake
$ make -j

This enables cross compilation. You can also configure other cross compilation tool chains.

4.8 others

4.8.1 cmake message command color highlighting

We can also customize how to initialize cmake buildsmessageCommand print color, can easily and quickly highlight the error message, we can create a filecmake/messagecolor.cmake

IF(NOT WIN32)                                
    STRING(ASCII 27 Esc)                                                        
    SET(ColourReset "${Esc}[m")                                                 
    SET(ColourBold  "${Esc}[1m")                                                
    SET(Red         "${Esc}[31m")                                               
    SET(Green       "${Esc}[32m")                                               
    SET(Yellow      "${Esc}[33m")                                               
    SET(Blue        "${Esc}[34m")                                               
    SET(MAGENTA     "${Esc}[35m")                                               
    SET(Cyan        "${Esc}[36m")                                               
    SET(White       "${Esc}[37m")                                               
    SET(BoldRed     "${Esc}[1;31m")                                             
    SET(BoldGreen   "${Esc}[1;32m")                                             
    SET(BoldYellow  "${Esc}[1;33m")                                             
    SET(BOLDBLUE    "${Esc}[1;34m")                                             
    SET(BOLDMAGENTA "${Esc}[1;35m")                                             
    SET(BoldCyan    "${Esc}[1;36m")                                             
    SET(BOLDWHITE   "${Esc}[1;37m")                                             
ENDIF()                                                                         

FUNCTION(message)                                                               
    LIST(GET ARGV 0 MessageType)                                                
    IF(MessageType STREQUAL FATAL_ERROR OR MessageType STREQUAL SEND_ERROR)        
        LIST(REMOVE_AT ARGV 0)                                                  
        _message(${MessageType} "${BoldRed}${ARGV}${ColourReset}")              
    ELSEIF(MessageType STREQUAL WARNING)                                        
        LIST(REMOVE_AT ARGV 0)                                                  
        _message(${MessageType}                                                 
        "${BoldYellow}${ARGV}${ColourReset}")                                   
    ELSEIF(MessageType STREQUAL AUTHOR_WARNING)                                 
        LIST(REMOVE_AT ARGV 0)                                                  
        _message(${MessageType} "${BoldCyan}${ARGV}${ColourReset}")             
    ELSEIF(MessageType STREQUAL STATUS)                                         
        LIST(REMOVE_AT ARGV 0)                                                  
        _message(${MessageType} "${Green}${ARGV}${ColourReset}")                
    ELSEIF(MessageType STREQUAL INFO)                                           
        LIST(REMOVE_AT ARGV 0)                                                  
        _message("${Blue}${ARGV}${ColourReset}")                                
    ELSE()                                                                      
        _message("${ARGV}")                                                     
ENDIF()   

If you import the cmake file in the main cmakelists.txt, you can change the color display of the message command at all levels of printing.

4.8.2 debug and release build

In order to facilitate debugging, we usually compile in the development processDebugVersion of the library or application, you can use GDB debugging, it is easy to find the specific location of the error. In the main cmake file, we only need to add the following settings:

IF(NOT CMAKE_BUILD_TYPE)                                                        
    SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose Release or Debug" FORCE)  
ENDIF()                                                                         

MESSAGE(STATUS "Build type: " ${CMAKE_BUILD_TYPE})

It can be set when executing the cmake commandCMAKE_BUILD_TYPEVariable value switchingDebugperhapsReleaseVersion compilation:

$ cmake .. -DCMAKE_BUILD_TYPE=Release

4.8.3 post construction installation

For SDK projects, we need to provide external header files and compiled library files, so we need to use the files provided by cmakeinstallOrders.

Our installation requirements are:

  • srcEach module header file in the directory can be installed and stored according to the original directory
  • Library files installed inlibDirectory
  • Executable files, including test files, are placed inbincatalogue

First, the module header files are installed and implemented insrc/{module}/CMakeLists.txtIn, here is the installation directory, and filter it out.cppperhaps.cDocuments andCMakeLists.txtFile tologgerModule as an example:

INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    DESTINATION ${CMAKE_INSTALL_PREFIX}/include
    FILES_MATCHING 
    PATTERN "*.h"
    PATTERN "*.hpp"
    PATTERN "CMakeLists.txt" EXCLUDE
    )

Note: on UNIX systems,CMAKE_INSTALL_PREFIXVariable default point/usr/local, on Windows systems, the default point isc:/Program Files/${PROJECT_NAME}

Then there is the installation of library files, which is also relatedADD_LIBRARYAfter the command is called, it is implemented in:

INSTALL(TARGETS module_logger
    ARCHIVE DESTINATION lib                                                     
    LIBRARY DESTINATION lib                                                     
    RUNTIME DESTINATION bin)

Finally, the installation of executable files, which is the same as the installation library, is added toADD_EXECUTABLEAfter the command call, only because it is an executable file, it belongs toRUNTIMEType, cmake will be automatically installed in the bin directory we set, hereHelloAppFor example:

INSTALL(TARGETS HelloApp
    ARCHIVE DESTINATION lib                                                     
    LIBRARY DESTINATION lib                                                     
    RUNTIME DESTINATION bin)

Execute the installation command:

$ make install DESTDIR=$PWD/install

Relative to the current directoryinstall/usr/localGenerated under Directory:

.
├── bin
│   ├── HelloApp
│   └── test_logger
├── include
│   ├── common
│   │   ├── common.hpp
│   │   └── version.hpp
│   └── logger
│       └── logger.hpp
└── lib
    └── libmodule_logger.a

At this point, the installation is complete.

5 Summary

“If you want to do a good job, you must first sharpen your tools”. Building a good foundation is also very important in the process of software development, just as the requirements in the project are clear. In this article, IC/C++The overall framework of project development forms a template for continuous summary and improvement to facilitate the rapid development of subsequent similar projects. This article also mainly implements the content of project construction. The next article is ready to implement a basicC/C++The basic modules required by the framework include log module, thread pool, common basic function module, configuration import module, unit test, memory leak check, etc. If there are problems or improvements, let’s learn together. Finally, I would like to welcome official account.Xiaobai AI, don’t advertise, don’t write for writing, just to share your learning process ^ ^ ^.

The entire build template source code can be found on my GitHub. Welcome to star: https://github.com/yicm/CMakeCppProjectTemplate

6 references

  • http://www.oolap.com/cxx-depe…
  • http://www.yeolar.com/note/20…

Recommended Today

Swift advanced (XV) extension

The extension in swift is somewhat similar to the category in OC Extension can beenumeration、structural morphology、class、agreementAdd new features□ you can add methods, calculation attributes, subscripts, (convenient) initializers, nested types, protocols, etc What extensions can’t do:□ original functions cannot be overwritten□ you cannot add storage attributes or add attribute observers to existing attributes□ cannot add parent […]