1. 基础语法

(一) 基础语法

1. 指定cmake最低版本

cmake_minimum_required (VERSION 2.6)

2. 设置项目名称

project (LearnCMake)

3.创建可执行程序

add_executable函数用于创建一个可执行程序工程。

add_executable( [WIN32] [MACOSX_BUNDLE]

​ [EXCLUDE_FROM_ALL]

​ source1 [source2 …])

如下所示:

add_executable(FirstExecutable hello_world.cpp)

也可以添加多个源文件到工程中,如下:

add_executable(FirstExecutable main.cpp app_util.h app_util.cpp)

4. 创建库文件工程(a/so/lib/dll)

add_library函数用于创建一个库文件工程。

add_library( [STATIC | SHARED | MODULE]

​ [EXCLUDE_FROM_ALL]

​ source1 [source2 …])

如下所示:

add_library(SecondLibrary second_library.cpp)

和add_executable一样,也可以添加多个源文件。

add_library(SecondLibrary test.cpp app_util.h app_util.cpp)

默认的是静态库,也可以显式的设置库是否为静态库、动态库或者是模块。另外BUILD_SHARED_LIBS也可控制编译成哪种库。

add_library(SecondLibrary SHARED second_library.cpp)

5、set设置变量

前面的add_library和add_executable可以添加多个源文件,但是文件多了之后可能会一行占用很长,因此我们可以使用set函数来进行变量赋值,然后在调用add_library和add_executable生成项目。

如下所示,效果和前面的示例一样。

set(SOURCES 
${PROJECT_SOURCE_DIR}/src/datastruct_test.c
${PROJECT_SOURCE_DIR}/src/app.cpp
)
set(HEADER
${PROJECT_SOURCE_DIR}/inc
)
include_directories(${PROJECT_NAME} ${HEADER})

add_executable(${PROJECT_NAME} ${SOURCES})
add_library(${PROJECT_NAME} ${SOURCES})

使用set函数,还可以对变量值进行累加,如下AppUtilSrcs就代表3个文件了

set(AppUtilSrcs app_util.h app_util.cpp)
set(AppUtilSrcs ${AppUtilSrcs} b.cpp)

除了文件名定义,set还用于变量定义

SET(CMAKE_BUILD_TYPE "Debug")  
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb -lpthread")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall -lpthread")

6、代码控制

如果一个项目太大,文件成千上万,那么一个一个文件的添加太过于麻烦了,因此cmake使用aux_source_directory函数来添加目录到工程中。

如下所示,将目录下所有文件赋值给第一个变量,然后将这个变量加到工程中。

# find all source file in src
aux_source_directory(${PROJECT_SOURCE_DIR}/src SOURCES)
message(STATUS "SRC FILES: " ${SOURCES})
add_executable(${PROJECT_NAME} ${SOURCES})

除了添加文件目录外,我们经常还需要包含第三方库(头文件、库文件)等需求,添加头文件目录功能如下:

  • include_directories函数用于添加头文件包含目录。

  • link_directories函数用于添加需要链接文件的库目录。

  • link_libraries函数用于添加需要连接的库文件。

include_directories(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/inc)
link_directories("../thirdparty/googletest/googletest/lib")
link_libraries("protobuf.so")

链接目标文件和的库文件,使用target_link_libraries函数,这里的目标文件是指通过add_executable()和add_library()指令生成已经创建的目标文件。所以target_link_libraries是在add_executable之后,而link_libraries是在add_executable之前。

target_include_directories(${PROJECT_NAME} PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})

从编译文件列表中排除文件,可以使用cmake提供的list的REMOVE_ITEM功能来实现。

aux_source_directory(src lua_src)
list(REMOVE_ITEM lua_src "src/lua.c", "src/luac.c")

7、添加编译选项

​ cmake使用add_compile_options函数来添加编译选项,用add_definitions函数来为源文件的编译添加由-D定义的标志,示例如下:

add_compile_options(-std=gnu99)
add_definitions(-O3 -g -W -Wall
-Wno-deprecated -Woverloaded-virtual -Wwrite-strings
-D__WUR= -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DTIXML_USE_STL
)

也可以通过设置变量添加编译选项

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# gdb debug
SET(CMAKE_BUILD_TYPE "Debug")
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb -lpthread")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall -lpthread")

注意,这两个选项都是针对所有平台、编译器,因此需要慎重使用,最好使用if来进行流程处理。

8、添加其他的CMakeLists.txt

一个CMakeLists.txt里面的target如果要链接其他CMakeLists.txt中的target,可以使用add_subdirectory函数,如下所示:

add_subdirectory(debug)
add_subdirectory(common)

9、find_package

find_package 为外部工程加载设置。

find_package( [version] [EXACT] [QUIET]

​ [[REQUIRED|COMPONENTS] [components…]]
​ [NO_POLICY_SCOPE])

QUIET选项将会禁掉包没有被发现时的警告信息。REQUIRED选项表示如果报没有找到的话,cmake的过程会终止,并输出警告信息。

find_package可以根据cmake内置的.cmake的脚本去找相应的库的模块,调用了find_package成功之后,会有相应的变量“生成”有效。

比如调用了find_package(Qt5Widgets),返回之后就会有变量Qt5Widgets_FOUND,Qt5Widgets_INCLUDE_DIRS相应的变量生效。 然后就可以在CMakeLists.txt里面使用上述的变量了。

find_package( OpenCV REQUIRED )
if(OpenCV_FOUND)
message(STATUS "OpenCV library has found")
message(STATUS "OpenCV_INCLUDE_DIR: " ${OpenCV_INCLUDE_DIRS})
message(STATUS "OpenCV_LIBRARY: " ${OpenCV_LIBS})
target_include_directories(${PROJECT_NAME} PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})
else(OpenCV_FOUND)
message(FATAL_ERROR "OpenCV library not found")
endif(OpenCV_FOUND)

10、条件、循环控制

  • If else结构

    if(condition)
    elseif(condition)
    else()
    endif()
  • for循环

    foreach(loop_var arg1 arg2 ...)
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
    endforeach(loop_var)
  • while循环

    while(condition)
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
    endwhile(condition)

11、Install指令

Install指令用于定义安装规则,安装的内容可以包括目标二进制、动态库、静态库以及文件、目录、脚本等。

参数中的TARGETS后面跟的就是我们通过ADD_EXECUTABLE或者ADD_LIBRARY定义的目标文件,可能是可执行二进制、动态库、静态库。

目标类型也就相对应的有三种,ARCHIVE特指静态库,LIBRARY特指动态库,RUNTIME特指可执行目标二进制。

INSTALL(TARGETS targets…

​ [[ARCHIVE|LIBRARY|RUNTIME]

​ [DESTINATION

]

​ [PERMISSIONS permissions…]

​ [CONFIGURATIONS

​ [Debug|Release|…]]

​ [COMPONENT ]

​ [OPTIONAL]

​ ] […])

示例如下:

 INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic
)

可执行二进制myrun安装到${CMAKE_INSTALL_PREFIX}/bin目录

动态库libmylib安装到${CMAKE_INSTALL_PREFIX}/lib目录

静态库libmystaticlib安装到${CMAKE_INSTALL_PREFIX}/libstatic目录

特别注意的是你不需要关心TARGETS具体生成的路径,只需要写上TARGETS名称就可以了。

(二)使用cmake流程

1. linux下使用

apt-get update
apt-get install cmake
# cd code source direction
cd ~/code
mkdir build
cd build
rm -r *
cmake -DCMAKE_BUILD_TYPE=Debug ..
cmake --build . #可以使用make -j16,支持并行编译

2. windows下使用

  1. 安装mingw
  2. 安装mingw安装package中的mingw32-gcc-g++-bin(用来编译),mingw32-gdb-bin(用来调试)
  3. 添加环境变量
  4. 最后运行
    1. 每次cmake之前删除build里的缓存
    2. cd build
    3. cmake -G "Unix Makefiles" ../ (非常重要)

(三)自动生成版本信息

1. 获取git信息:commit号和分支信息

通过git log -1 –pretty=format:%H获取commit号

通过git symbolic-ref –short -q HEAD获取分支信息

示例如下:

# get git version
set(COMMIT_HASH "")
set(BRANCH_NAME "")
find_package(Git QUIET)
if(GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%H
OUTPUT_VARIABLE COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
execute_process(
COMMAND ${GIT_EXECUTABLE} symbolic-ref --short -q HEAD
OUTPUT_VARIABLE BRANCH_NAME
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif()
string(SUBSTRING ${COMMIT_HASH} 0 8 COMMIT_HASH)
message(STATUS "Git version is ${BRANCH_NAME}:${COMMIT_HASH}")
add_definitions(-DCOMMIT_HASH=\"${COMMIT_HASH}\")
add_definitions(-DBRANCH_NAME=\"${BRANCH_NAME}\")

(四)opencv的集成

1. linux下集成opencv:

sudo apt-get update
sudo apt-get install libopencv-dev

直接就可以编译了

2. windows下集成opencv

去官网下载opencv windows安装包,运行就生成一个文件夹。里面有两个目录,build和source,build放的是预编译好的库,source里面放的是源码。

把build\x64\vc15\lib加入到环境变量中,看下代码运行是否正确。

问题1:

正常来说会报mutux找不到定义的错误。

那是因为mingw多线程的库有问题,必须要特定64位版本的才能够安装

下载安装:MinGW-x64-4.8.1-release-posix-seh-rev5

并把mingw的bin文件加到环境变量中去,就可以运行了。

问题2:

确认自己的cmake把opencv的库加进去的情况下,还是找不到imread等常用函数路径。那很可能是预编译没有编译出适合你电脑系统的库,需要自己编译opencv库

在source文件夹下新建一个build目录。编译的时候用mingw32-make -j16多线程编译会比cmake –build .更快一点

cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .. -G "MinGW Makefiles"
mingw32-make -j16

问题3:

windows下编译的时候会出现这个问题:

gcc: error: long: No such file or directory

解决方法:

cmake -DOPENCV_ENABLE_ALLOCATOR_STATS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .. -G "MinGW Makefiles"

问题4:

编译完成后,用cmake编译opencv的样例的时候发现,cmake打印出来的include路径不对。

需要编译之后安装!

cmake --install .

接着把生成的库绝对路径:sources\build\install\x64\mingw\lib加入环境变量,注意这个要看编译器,不同的编译器和系统产生的库路径不一样

问题5:

在本地电脑上运行样例程序,提示缺少dll库

需要把source/build/bin加入环境变量

cmake 模板

# TODO: Set the project name
project(opencv_sample)
message("====================================================")
message(STATUS "build app: " ${PROJECT_NAME})

# TODO: set source file (c/cpp)
set(SOURCES
${PROJECT_SOURCE_DIR}/src/opencv_sample.cpp
)
message(STATUS "SRC FILES: " ${SOURCES})

# set output file path
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
message(STATUS "BUILD EXE: " ${EXECUTABLE_OUTPUT_PATH} "/" ${PROJECT_NAME})

# Add an executable
add_executable(${PROJECT_NAME} ${SOURCES})

# find opencv package
find_package( OpenCV REQUIRED )
if(OpenCV_FOUND)
message(STATUS "OpenCV library has found")
message(STATUS "OpenCV_INCLUDE_DIR: " ${OpenCV_INCLUDE_DIRS})
message(STATUS "OpenCV_LIBRARY: " ${OpenCV_LIBS})
target_include_directories(${PROJECT_NAME} PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})
else(OpenCV_FOUND)
message(FATAL_ERROR "OpenCV library not found")
endif(OpenCV_FOUND)

(五) QT的集成

类似opencv,但是不需要自己编译了

在官网下载安装包:https://download.qt.io/official_releases/

选择QT版本,不需要全部勾选,比如我是mingw64编译器,那就选择mingw64版本的

然后把安装路径的Qt\5.15.0\mingw81_64\lib\cmake和C:\Qt\5.15.0\mingw81_64\bin加入环境变量,QT的安装就算完成了

cmake模版:

cmake_minimum_required(VERSION 3.5)

project(ImageTools LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# find all source file in src
aux_source_directory(${PROJECT_SOURCE_DIR} SOURCES)
message(STATUS "SRC FILES: " ${SOURCES})

include_directories(${PROJECT_NAME} ${PROJECT_SOURCE_DIR})

#查找当前文件夹中所有的ui文件
set(UI_FILES
mainwindow.ui
imageeditor.ui
staticsview.ui
)

#添加资源文件
set( QRCS resource.qrc )

find_package(Qt5 COMPONENTS Widgets Core Gui REQUIRED)

if(ANDROID)
add_library( ${PROJECT_NAME} ${SOURCES} ${UI_FILES} ${QRCS})
else()
add_executable( ${PROJECT_NAME} ${SOURCES} ${UI_FILES} ${QRCS})
endif()

target_link_libraries(ImageTools PRIVATE Qt5::Widgets Qt5::Core Qt5::Gui)