1. cmake概述及例子
CMake快速入门
cmake、qmake、cl之间关系
1.1 各种cmake
cmake根据CMakeLists.txt生成makefile,make根据makefile行编译。
1.1.1 最简cmake:生成可执行程序(一个文件)
#CMakeLists.txt
cmake_minimum_required(VERSION 2.8.9) #指定cmake最低版本
project (hello) #指定项目名称
add_executable(hello helloworld.cpp) #指定编译一个可执行文件
mkdir build
cd build
cmake .. 或 ccmake .. #生成Makefile文件
make -j8 #根据Makefile编译
make install #安装
1.1.2 生成可执行程序(带目录)
cmake_minimum_required(VERSION 2.8.9)
project(directory_test)
include_directories(include) #添加头文件搜索路径
#set(SOURCES src/mainapp.cpp src/Student.cpp) #手动添加源文件
file(GLOB SOURCES "src/*.cpp") #添加源文件
add_executable(testStudent ${SOURCES}) #生成可执行文件
1.1.3 生成动态库(带目录)
project(directory_test)
set(CMAKE_BUILD_TYPE Release)
include_directories(include) #添加头文件搜索目录
file(GLOB SOURCES "src/*.cpp") #添加源文件
add_library(testStudent SHARED ${SOURCES}) #生成库文件(动态库)
install(TARGETS testStudent DESTINATION /usr/lib) #安装库文件
1.1.4 生成静态库(带目录)
cmake_minimum_required(VERSION 2.8.9)
project(directory_test)
set(CMAKE_BUILD_TYPE Release)
include_directories(include)
file(GLOB SOURCES "src/*.cpp")
add_library(testStudent STATIC ${SOURCES}) #生成静态库
install(TARGETS testStudent DESTINATION /usr/lib) #安装静态库
1.1.5 生成可执行程序(使用共享库)
#include"Student.h"
int main(int argc, char *argv[])
{Student s("Joe");s.display();return 0;
}
cmake_minimum_required(VERSION 2.8.9)
project (TestLibrary)
set ( PROJECT_LINK_LIBS libtestStudent.so )
link_directories( ~/exploringBB/extras/cmake/studentlib_shared/build ) #添加库搜索路径
include_directories(~/exploringBB/extras/cmake/studentlib_shared/include) #添加头文件搜索路径
add_executable(libtest libtest.cpp)
target_link_libraries(libtest ${PROJECT_LINK_LIBS} ) #插入共享库,必须在add_executable之后
1.1.6 综合示例
1.2 cmake与qmake
cmake和qmake对比
cmke转qmake例子
qmake转cmake(lib)
cmake与qmake例子如下:
cmake:
cmake_minimum_required(VERSION 3.10)
project(osganimate)
include_directories("xx/osg/build/include")
link_directories("xx/osg/build/lib")
file(GLOB OSG_LIBS "xx/osg/build/lib/libosg*")
add_executable(${PROJECT_NAME} osganimate.cpp)
target_link_libraries(${PROJECT_NAME} -lOpenThreads ${OSG_LIBS})
qmake:
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = osg_qt_test
SOURCES += \osganimate.cppOSG_LIBS_PATH = xx/osg/build/lib/
DEPENDPATH += xx/osg/build/lib/
INCLUDEPATH += xx/osg/build/include
LIBS += \-L $$OSG_LIBS_PATH -losgAnimation \-L $$OSG_LIBS_PATH -losgDB \-L $$OSG_LIBS_PATH -losgFX \-L $$OSG_LIBS_PATH -losgGA \-L $$OSG_LIBS_PATH -losgManipulator \-L $$OSG_LIBS_PATH -losgParticle \-L $$OSG_LIBS_PATH -losgPresentation\-L $$OSG_LIBS_PATH -losgShadow \-L $$OSG_LIBS_PATH -losgSim \-L $$OSG_LIBS_PATH -losg \-L $$OSG_LIBS_PATH -losgTerrain \-L $$OSG_LIBS_PATH -losgText \-L $$OSG_LIBS_PATH -losgUI \-L $$OSG_LIBS_PATH -losgUtil \-L $$OSG_LIBS_PATH -losgViewer \-L $$OSG_LIBS_PATH -losgVolume \-L $$OSG_LIBS_PATH -losgWidget \-L $$OSG_LIBS_PATH -lOpenThreads
QMake 和 CMake 的思路是类似的,无非是添加头文件路径和库路径,然后链接指定的库,QT 的 Pro 文件(qmake)貌似不太支持通配符的写法。
2. cmake语法
官方命令参考
cmake命令参考
2.1 常用命令
指令 | 意义 |
---|---|
CMAKE_MINIMUM_REQUIRED | cmake最低版本要求 |
PROJECT | |
SET | 设置变量: set(SRC_LIST main.cpp test.cpp) |
MESSAGE | |
ADD_EXECUTABLE | 生成可执行文件 |
ADD_LIBRARY | 生成库文件 |
ADD_SUBDIRECTORY | 添加源文件子目录 |
INCLUDE_DIRECTORIES | 添加头文件搜索路径 |
LINK_DIRECTORIES | 添加库搜索目录 |
TARGET_LINK_LIBRARIES | 插入共享库 |
INSTALL | 安装二进制、动态库、静态库、文件、目录、脚本等 |
SUBDIRS | |
add_compile_options | 增加源文件的编译选项: -fPIC, -fpic, -fpie, -fPIE |
ADD_DEFINITIONS | 为源文件的编译添加由-D定义的标志: add_definitions(-DFOO -DBAR …) |
ADD_DEPENDENCIES | 添加依赖 |
ADD_TEST | |
ENABLE_TESTING | |
AUX_SOURCE_DIRECTORY | 将dir目录下所有源文件名字保存在变量varialble中:aux_source_directory(. DIR_SRCS) |
EXEC_PROGRAM | |
FILE | |
INCLUDE | |
FIND_FILE | |
FIND_LIBRARY | |
FIND_PATH | |
FIND_PROGRAM | |
FIND_PACKAGE | 查找依赖包:find_package(Protobuf REQUIRED) |
list | 追加或者删除变量的值: list(APPEND SRC_LIST test.cpp) list(REMOVE_ITEM SRC_LIST main.cpp) |
install(TARGETS xx libxx.so libxx.aRUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})INSTALL_DIR DESTINATION ${CMAKE_INSTALL_LIBDIR})安装路径可以通过CMAKE_INSTALL_PREFIX设置#目录拷贝
install(DIRECTORY xx/cofnig DESTINATION ${CMAKE_INSTALL_PREFIX}/)#文件拷贝
install(FILES xx DESTINATION ${CMAKE_INSTALL_PREFIX}/)
files(GLOB dep_libs "xx/xx/*.so*)
install(FILES dep_libs DESTINATION ${CMAKE_INSTALL_PREFIX}/)#编译过程中执行命令(非cmake时执行)
install(CODE "messge(\”xxxx\"))
install(CODE "execute_process(COMMAND bash -c \"cp xx/*.so ${dst} -rf\") ")
,
2.2 常用变量
SET(SRC_LIST main.cpp) #定义变量
${SRC_LIST} #引用变量
变量 | 意义 |
---|---|
CMAKE_BINARY_DIR PROJECT_BINARY_DIR <projectname>_BINARY_DIR | 这三个变量指代的内容是一致的,如果是 in source 编译,指得就是工程顶层目录,如果是 out-of-source 编译,指的是工程编译发生的目录。 |
CMAKE_SOURCE_DIR PROJECT_SOURCE_DIR <projectname>_SOURCE_DIR | 这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。 也就是在 in source 编译时,他跟 CMAKE_BINARY_DIR 等变量一致。 |
CMAKE_CURRENT_SOURCE_DIR | 指的是当前处理的 CMakeLists.txt 所在的路径,比如上面我们提到的 src 子目录。 |
CMAKE_CURRRENT_BINARY_DIR | 如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致, 如果是 out-of-source 编译,他指的是 target 编译目录。 使用我们上面提到的 ADD_SUBDIRECTORY(src bin)可以更改这个变量的值。 使用 SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。 |
CMAKE_CURRENT_LIST_FILE | 输出调用这个变量的 CMakeLists.txt 的完整路径 |
CMAKE_CURRENT_LIST_LINE | 输出这个变量所在的行 |
CMAKE_MODULE_PATH | 这个变量用来定义自己的 cmake 模块所在的路径。如果你的工程比较复杂,有可能会自己编写一些 cmake 模块,这些 cmake 模块是随你的工程发布的,为了让 cmake 在处理CMakeLists.txt 时找到这些模块,你需要通过 SET 指令,将自己的 cmake 模块路径设置一下。 比如 SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) 这时候你就可以通过 INCLUDE 指令来调用自己的模块了。 |
EXECUTABLE_OUTPUT_PATH LIBRARY_OUTPUT_PATH | 分别用来重新定义最终结果的存放目录,前面我们已经提到了这两个变量。 |
PROJECT_NAME | 返回通过 PROJECT 指令定义的项目名称。 |
CMAKE_CXX_STANDARD | SET(CMAKE_CXX_STANDARD 14):设置c++版本 |
CMAKE_BUILD_TYPE | SET( CMAKE_BUILD_TYPE Release):设置编译方式 |
#指定编译选项CMAKE_C_FLAGS : 指定gcc编译选项,如-02 ,-g,当然也可用通过add_definitions设置。CMAKE_CXX_FLAGS: 指定g++编译选项。CMAKE_C_FLAGS_DEBUG: 指定debug版本编译选项#指定链接选项CMAKE_EXE_LINKER_FLAGSCMAKE_MODILE_LINKER_FLAGSCMAKE_SHARED_LINKER_FLAGSCMAKE_STATIC_LINKER_FLAGS#指定编译器CMAKE_C_COMPILER: 指定C编译器,如gccCMAKE_CXX_COMPILER: 指定C++编译器,如g++BUILD_SHARED_LIBS: 指定默认生成库文件类型,on:动态库,off 静态CMAKE_BUILD_TYPE: 设置编译类型,如Debug、Release#指定RPATH相关选项,如果为true,则关闭rpath功能CMAKE_SKIP_RPATH; 构建和安装期间CMAKE_SKIP_BUILD_RPATH: 构建期间CMKAE_INSTALL_RPATH: 安装期间
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../map_common/lib) #module库
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../map_common/lib) #静态库
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../bin) #可执行
2.3 系统变量
$ENV{NAME} #引用系统环境变量
MESSAGE(STATUS "HOME dir: $ENV{HOME}")
变量 | 意义 |
---|---|
CMAKE_INCLUDE_CURRENT_DIR | 自动添加 CMAKE_CURRENT_BINARY_DIR 和 CMAKE_CURRENT_SOURCE_DIR 到当前处理的 CMakeLists.txt。相当于在每个 CMakeLists.txt 加入: INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) |
CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE | 将工程提供的头文件目录始终至于系统头文件目录的前面,当你定义的头文件确实跟系统发生冲突时可以提供一些帮助。 |
CMAKE_INCLUDE_PATH CMAKE_LIBRARY_PATH | 我们在上一节已经提及。 |
2.4 控制指令
2.4.1 IF
2.4.2 while
2.4.3 foreach
2.5 find_package
【轻松搞定CMake】find_package用法详解
该命令在搜索包时有两种模式:“模块”模式和“配置”模式。
2.5.1 模块模式
Module模式: 搜索CMAKE_MODULE_PATH指定路径下的FindXXX.cmake文件,执行该文件从而找到XXX库。其中,具体查找库并给XXX_INCLUDE_DIRS和XXX_LIBRARIES两个变量赋值的操作由FindXXX.cmake模块完成(先搜索当前项目里面的Module文件夹里面提供的FindXXX.cmake,然后再搜索系统路径/usr/local/share/cmake-x.y/Modules/FindXXX.cmake)
find_package(<package> [version] [EXACT] [QUIET][[REQUIRED|COMPONENTS] [components...]][NO_POLICY_SCOPE])
- 在该模式下,CMake搜索所有名为Find.cmake的文件,搜索这个文件的路径有两个,一个是cmake安装目录下的share/cmake-/Modules目录,另一个是我们指定的CMAKE_MODULE_PATH的所在目录。
- 该模式对查找包,检查版本以及生成任何别的必须信息负责。许多查找模块(find-module)仅仅提供了有限的,甚至根本就没有对版本化的支持;具体信息查看该模块的文档。
- 如果没有找到任何模块,该命令会进入配置模式继续执行。
2.5.2 配置模式
Config模式: 搜索XXX_DIR指定路径下的XXXConfig.cmake文件,执行该文件从而找到XXX库。其中具体查找库并给XXX_INCLUDE_DIRS和XXX_LIBRARIES两个变量赋值的操作由XXXConfig.cmake模块完成。
find_package(<package> [version] [EXACT] [QUIET][[REQUIRED|COMPONENTS] [components...]] [NO_MODULE][NO_POLICY_SCOPE][NAMES name1 [name2 ...]][CONFIGS config1 [config2 ...]][HINTS path1 [path2 ... ]][PATHS path1 [path2 ... ]][PATH_SUFFIXES suffix1 [suffix2 ...]][NO_DEFAULT_PATH][NO_CMAKE_ENVIRONMENT_PATH][NO_CMAKE_PATH][NO_SYSTEM_ENVIRONMENT_PATH][NO_CMAKE_PACKAGE_REGISTRY][NO_CMAKE_BUILDS_PATH][NO_CMAKE_SYSTEM_PATH][CMAKE_FIND_ROOT_PATH_BOTH |ONLY_CMAKE_FIND_ROOT_PATH |NO_CMAKE_FIND_ROOT_PATH])
-
配置模式,主要通过Config.cmake or -config.cmake这两个文件来引入我们需要的库。
-
以我们刚刚安装的glog库为例,在我们安装之后,它在/usr/local/lib/cmake/glog/目录下生成了glog-config.cmake文件,而/usr/local/lib/cmake//正是find_package函数的搜索路径之一。
NO_MODULE可以用来明确地跳过模块模式。它也隐含指定了不使用在精简格式中使用的那些选项。
配置模式试图查找一个由待查找的包提供的配置文件的位置。包含该文件的路径会被存储在一个名为_DIR的cache条目里。默认情况下,该命令搜索名为的包。如果指定了NAMES选项,那么其后的names参数会取代的角色。该命令会为每个在names中的name搜索名为Config.cmake或者<name全小写>-config.cmake的文件。通过使用CONFIGS选项可以改变可能的配置文件的名字。以下描述搜索的过程。如果找到了配置文件,它将会被CMake读取并处理。由于该文件是由包自身提供的,它已经知道包中内容的位置。配置文件的完整地址存储在cmake的变量_CONFIG中。
所有CMake要处理的配置文件将会搜索该包的安装信息,并且将该安装匹配的适当版本号(appropriate version)存储在cmake变量_CONSIDERED_CONFIGS中,与之相关的版本号(associated version)将被存储在_CONSIDERED_VERSIONS中。
如果没有找到包配置文件,CMake将会生成一个错误描述文件,用来描述该问题——除非指定了QUIET选项。如果指定了REQUIRED选项,并且没有找到该包,将会报致命错误,然后配置步骤终止执行。如果设置了_DIR变量被设置了,但是它没有包含配置文件信息,那么CMake将会直接无视它,然后重新开始查找。
如果给定了[version]参数,那么配置模式仅仅会查找那些在命令中请求的版本(格式是major[.minor[.patch[.tweak]]])与包请求的版本互相兼容的那些版本的包。如果指定了EXACT选项,一个包只有在它请求的版本与[version]提供的版本精确匹配时才能被找到。CMake不会对版本数的含义做任何的转换。包版本号由包自带的版本文件来检查。对于一个备选的包配置文件.cmake,对应的版本文件的位置紧挨着它,并且名字或者是-version.cmake或者是Version.cmake。如果没有这个版本文件,那么配置文件就会认为不兼容任何请求的版本。当找到一个版本文件之后,它会被加载然后用来检查(find_package)请求的版本号。
2.5.3 应用问题解决
1、如果没有搜索配置文件
对于可能没有***.cmake和***Config.cmake的库文件,可以直接找到其头文件和库文件所在文件夹,直接进行路径赋值:
SET(LAPACK_DIR /usr/local/lib/)
SET(LAPACK_INCLUDE_DIRS /usr/local/include)
SET(LAPACK_LIBRARIES /usr/local/lib)
cmake默认采取Module模式,如果Module模式未找到库,才会采取Config模式。如果XXX_DIR路径下找不到XXXConfig.cmake文件,则会找/usr/local/lib/cmake/XXX/中的XXXConfig.cmake文件。总之,Config模式是一个备选策略。通常,库安装时会拷贝一份XXXConfig.cmake到系统目录中,因此在没有显式指定搜索路径时也可以顺利找到。
2、A required library with LAPACK API not found. 错误解决
这里就比较坑了,库文件是已经安装了的,而且是安装到系统目录里面的,但没办法找到库文件。
原因是:官方给的FindLAPACK.cmake文件有误,
解决方法:$sudo su $rm FindLAPACK.cmake 删除Module模式里面的***.cmake文件,因为如果不删除其他地方不管你怎么改,CmakLists.txt如何链接都是枉子的,亲身踩坑!然后启用Config模式看看里面的XXXConfig.cmake能否正常工作。完整步骤如下:
sudo su
#找到报错路径/usr/local/share/cmake-x.y/Modules/删除FindXXX.cmake
rm FindXXX.cmake
#在项目文件Cmakelists.txt文件中添加如下一句,指定路径,指定之前先查看路径下是否有XXXConfig.cmake
SET(LAPACK_DIR /usr/local/lib/cmake/lapack-3.8.0)
3、添加findpackage查询路径
LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/modules)
2.5.4 编写自己的Find.cmake模块
- 对于原生支持Cmake编译和安装的库通常会安装Config模式的配置文件到对应目录,这个配置文件直接配置了头文件库文件的路径以及各种cmake变量供find_package使用。
- 而对于非由cmake编译的项目,我们通常会编写一个Find.cmake,通过脚本来获取头文件、库文件等信息。通常,原生支持cmake的项目库安装时会拷贝一份XXXConfig.cmake到系统目录中,因此在没有显式指定搜索路径时也可以顺利找到。
举例如下:
1、ModuleMode文件夹,在里面编写一个计算两个整数之和的一个简单的函数库。其Makefile如下:
# 1、准备工作,编译方式、目标文件名、依赖库路径的定义。
CC = g++
CFLAGS := -Wall -O3 -std=c++11 OBJS = libadd.o #.o文件与.cpp文件同名
LIB = libadd.so # 目标文件名
INCLUDE = ./ # 头文件目录
HEADER = libadd.h # 头文件all : $(LIB)# 2. 生成.o文件
$(OBJS) : libadd.cc$(CC) $(CFLAGS) -I ./ -fpic -c $< -o $@# 3. 生成动态库文件
$(LIB) : $(OBJS)rm -f $@g++ $(OBJS) -shared -o $@ rm -f $(OBJS)# 4. 删除中间过程生成的文件
clean:rm -f $(OBJS) $(TARGET) $(LIB)# 5.安装文件
install:cp $(LIB) /usr/libcp $(HEADER) /usr/include
编译安装:
make
sudo make install
2、在CMake项目中,在cmake文件夹下新建一个FindAdd.cmake的文件,目标是找到库的头文件所在目录和共享库文件的所在位置。
# 在指定目录下寻找头文件和动态库文件的位置,可以指定多个目标路径
find_path(ADD_INCLUDE_DIR libadd.h /usr/include/ /usr/local/include ${CMAKE_SOURCE_DIR}/ModuleMode)
find_library(ADD_LIBRARY NAMES add PATHS /usr/lib/add /usr/local/lib/add ${CMAKE_SOURCE_DIR}/ModuleMode)if (ADD_INCLUDE_DIR AND ADD_LIBRARY)set(ADD_FOUND TRUE)
endif (ADD_INCLUDE_DIR AND ADD_LIBRARY)
3、在CMakeLists.txt中添加:
# 将项目目录下的cmake文件夹加入到CMAKE_MODULE_PATH中,让find_pakcage能够找到我们自定义的函数库
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
add_executable(addtest addtest.cc)
find_package(ADD)
if(ADD_FOUND)target_include_directories(addtest PRIVATE ${ADD_INCLUDE_DIR})target_link_libraries(addtest ${ADD_LIBRARY})
else(ADD_FOUND)message(FATAL_ERROR "ADD library not found")
endif(ADD_FOUND)