侧边栏壁纸
  • 累计撰写 55 篇文章
  • 累计创建 13 个标签
  • 累计收到 1 条评论

cmake教程

秋山人家
2022-05-10 / 0 评论 / 0 点赞 / 12 阅读 / 23,340 字
温馨提示:
本文最后更新于 2022-05-10,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

--------------------------------: | :----------------------------------------------------------: |
| Unix/Linux Source (has \n line feeds) | cmake-3.23.1.tar.gz |
| Windows Source (has \r\n line feeds) | cmake-3.23.1.zip |

平台 文件
Windows x64 Installer cmake-3.23.1-windows-x86_64.msi
Windows x64 ZIP cmake-3.23.1-windows-x86_64.zip
Windows i386 Installer cmake-3.23.1-windows-i386.msi
Windows i386 ZIP cmake-3.23.1-windows-i386.zip
macOS 10.13 or later cmake-3.23.1-macos-universal.dmg
cmake-3.23.1-macos-universal.tar.gz
macOS 10.10 or later cmake-3.23.1-macos10.10-universal.dmg
cmake-3.23.1-macos10.10-universal.tar.gz
Linux x86_64 cmake-3.23.1-linux-x86_64.sh
cmake-3.23.1-linux-x86_64.tar.gz
Linux aarch64 cmake-3.23.1-linux-aarch64.sh
cmake-3.23.1-linux-aarch64.tar.gz

注:在国产化平台中,通常采用源码方式安装。

linux源码安装方式

./bootstrap ----prefix=<install_prefix>
make
make install

apt方式安装cmake

sudo apt install cmake

初识CMakeLists.txt

  • 参考cmake_example/example_01

CMakeLists.txe

cmake组织项目通过CMakeLists.txe脚本完成,一个典型的CMakeLists.txt如下:

cmake_minimum_required (VERSION 3.10)
project (CmakeDemo)

set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/lib)  

add_executable(CmakeDemo main.c)

测试代码

编写一个main.c。

#include <stdio.h>
void main()
{
    printf("welcome to cmake world.\n");
    return;
}

运行构建

通常不在项目根目录直接运行构建,直接运行构建有可能导致项目结构混乱。一般是在根目录创建build目录,在build目录中运行构建。

mkdir build_rpm
cd build_rpm
cmake ../../
make
cd ../
  • windows构建脚本
md build_win
cd build_win
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_BUILD_TYPE=Release ../../
cd ../

该项目生成一个CmakeDemo的可执行文件。其中

cmake_minimum_required

设置一个工程所需要的最低CMake版本

cmake_minimum_required(VERSION major[.minor[.patch[.tweak]]]
                         [FATAL_ERROR])

如果CMake的当前版本低于指定的版本,它会停止处理工程文件,并报告错误。

project

为整个工程设置一个工程名

project(<projectname>)

为本工程设置一个工程名。而且,该命令还将变量<projectName>_BINARY_DIR<projectName>_SOURCE_DIR设置为对应值。

add_executable

使用给定的源文件,为工程引入一个可执行文件。

add_executable(<name> source1 source2 ... sourceN)

引入一个名为<name>的可执行目标,该目标会由调用该命令时在源文件列表中指定的源文件来构建。<name>对应于逻辑目标名字,并且在工程范围内必须是全局唯一的。被构建的可执行目标的实际文件名将根据具体的本地平台创建出来(比如<name>.exe或者仅仅是<name>)。

set

将一个CMAKE变量设置为给定值

 set(<variable> <value> [[CACHE <type> <docstring> [FORCE]] | PARENT_SCOPE])

将变量<variable>的值设置为<value>

EXECUTABLE_OUTPUT_PATH

可执行文件路径。

LIBRARY_OUTPUT_PATH

库文件路径

扫描源文件

  • 参考cmake_example/example_02

在构建工程过程中,如果将源文件逐个添加到脚本中,效率太过缓慢,cmake提供了aux_source_directory用来扫描目录下所有源文件。

cmake_minimum_required (VERSION 3.10)
project (CmakeDemo)

aux_source_directory(${CMAKE_SOURCE_DIR}/src ADD_SOURCE_LIST)
add_executable(CmakeDemo main.c ${ADD_SOURCE_LIST})

CMAKE_SOURCE_DIR

表示源码树的顶层路径,在cmake中获取值通过${变量}的方式获取。

aux_source_directory

aux_source_directory(<dir> <variable>)

搜集所有在指定路径下的源文件的文件名,将输出结果列表储存在指定的变量中。

生成库文件

  • 参考cmake_example/example_03

在对外提供的产品中,我们有时会将代码编译成库文件形式,提供给用户调用,cmake构建库文件通过add_library函数实现。

cmake_minimum_required (VERSION 3.10)
project (Arithmetic)

aux_source_directory(${CMAKE_SOURCE_DIR}/src ADD_SOURCE_LIST)
add_library(Arithmetic SHARED ${ADD_SOURCE_LIST})
#add_library(Arithmetic STATIC ${ADD_SOURCE_LIST})

add_library

add_library(<name> [STATIC | SHARED | MODULE]
              [EXCLUDE_FROM_ALL]
              source1 source2 ... sourceN)

​ 添加一个名为<name>的库文件,该库文件将会根据调用的命令里列出的源文件来创建。<name>对应于逻辑目标名称,而且在一个工程的全局域内必须是唯一的。待构建的库文件的实际文件名根据对应平台的命名约定来构造(比如lib<name>.a或者<name>.lib)。指定STATICSHARED,或者MODULE参数用来指定要创建的库的类型。STATIC库是目标文件的归档文件,在链接其它目标的时候使用。SHARED库会被动态链接,在运行时被加载。MODULE库是不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数动态链接。如果没有类型被显式指定,这个选项将会根据变量BUILD_SHARED_LIBS的当前值是否为真决定是STATIC还是SHARED

默认状态下,库文件将会在于源文件目录树的构建目录树的位置被创建,该命令也会在这里被调用。通过ARCHIVE_OUTPUT_DIRECTORYLIBRARY_OUTPUT_DIRECTORY,和RUNTIME_OUTPUT_DIRECTORY这三个目标属性的文档来改变这一位置。

生成可执行文件

  • 参考cmake_example/example_04

cmake构建可执行项目通过add_executable实现

cmake_minimum_required (VERSION 3.10)
project (CmakeDemo)
add_executable(CmakeDemo ${CMAKE_SOURCE_DIR}/main.c)
#include <stdio.h>
void main()
{
    printf("welcome to cmake world.\n");
    return;
}

该项目生成一个CmakeDemo的可执行文件。其中

add_executable

使用给定的源文件,为工程引入一个可执行文件。

add_executable(<name> source1 source2 ... sourceN)

引入一个名为<name>的可执行目标,该目标会由调用该命令时在源文件列表中指定的源文件来构建。<name>对应于逻辑目标名字,并且在工程范围内必须是全局唯一的。被构建的可执行目标的实际文件名将根据具体的本地平台创建出来(比如<name>.exe或者仅仅是<name>)。

添加子项目

  • 参考cmake_example/example_05

在项目构建中,往往不是一个单独的可执行文件或者库文件组成,而是由多个库文件和多个可执行文件组成,cmake中添加子项目通过add_subdirectory实现。

  • 主编译脚本
cmake_minimum_required (VERSION 3.10)
project (Arithmetic)

add_subdirectory(${CMAKE_SOURCE_DIR}/src/add_subdirectory)
add_subdirectory(${CMAKE_SOURCE_DIR}/src/mult_subdirectory)

子项目Arithmetic_Add

在根目录下创建目录src/add_subdirectory,并编写源码add.c和add.h

//add.h
#ifndef __ADD_H
#define __ADD_H
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif

int add(int x,int y);

#ifdef __cplusplus
}
#endif
#endif
//add.c
#include "add.h"
int add(int x,int y)
{
	return x + y;
}
  • 子项目编译脚本Arithmetic_Add

在目录add_subdirectory下创建CMakeLists.txt

project(Arithmetic_Add)
aux_source_directory(${CMAKE_SOURCE_DIR}/src/add_subdirectory ADD_SOURCE_LIST)
add_library(Arithmetic_Add SHARED ${ADD_SOURCE_LIST})

子项目Arithmetic_Mult

在根目录下创建目录src/add_subdirectory,并编写源码mult.c和mult.h

//mult.h
#ifndef __MULT_H
#define __MULT_H
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif

int mult(int x,int y);

#ifdef __cplusplus
}
#endif
#endif
//mult.c
#include "mult.h"

int mult(int x,int y)
{
	return x * y;
}
  • 子项目编译脚本Arithmetic_Mult

在目录mult_subdirectory下创建CMakeLists.txt

project(Arithmetic_Mult)
aux_source_directory(${CMAKE_SOURCE_DIR}/src/mult_subdirectory MULT_SOURCE_LIST)
add_library(Arithmetic_Mult SHARED ${MULT_SOURCE_LIST})

add_subdirectory

add_subdirectory(source_dir [binary_dir]
                   [EXCLUDE_FROM_ALL])

​ 这条命令的作用是为构建添加一个子路径。source_dir选项指定了CMakeLists.txt源文件和代码文件的位置。如果source_dir是一个相对路径,那么source_dir选项会被解释为相对于当前的目录,但是它也可以是一个绝对路径。binary_dir选项指定了输出文件的路径。如果binary_dir是相对路径,它将会被解释为相对于当前输出路径,但是它也可以是一个绝对路径。如果没有指定binary_dir,binary_dir的值将会是没有做任何相对路径展开的source_dir,这也是通常的用法。在source_dir指定路径下的CMakeLists.txt将会在当前输入文件的处理过程执行到该命令之前,立即被CMake处理。

​ 如果指定了EXCLUDE_FROM_ALL选项,在子路径下的目标默认不会被包含到父路径的ALL目标里,并且也会被排除在IDE工程文件之外。用户必须显式构建在子路径下的目标,比如一些示范性的例子工程就是这样。典型地,子路径应该包含它自己的project()命令调用,这样会在子路径下产生一份完整的构建系统(比如VS IDE的solution文件)。注意,目标间的依赖性要高于这种排除行为。如果一个被父工程构建的目标依赖于在这个子路径下的目标,被依赖的目标会被包含到父工程的构建系统中,以满足依赖性的要求。

添加版本号和配置头文件

  • 参考cmake_example/example_06

​ 在项目中为了方便管理,常常需要在项目中设置版本号,通过手动向代码中添加版本,不仅麻烦而且容易在版本发布时忘记添加。cmake提供了版本添加设置的方法。

在配置文件中添加版本号

//config.h.in
#ifndef __CONFIG_H
#define __CONFIG_H

#define PROJECT_NAME            "@PROJECT_NAME@"
#define PROJECT_VERSION         "@PROJECT_VERSION@"
#define PROJECT_VERSION_MAJOR   @PROJECT_VERSION_MAJOR@
#define PROJECT_VERSION_MINOR   @PROJECT_VERSION_MINOR@
#define PROJECT_VERSION_PATCH   @PROJECT_VERSION_PATCH@
#define PROJECT_VERSION_TWEAK   @PROJECT_VERSION_TWEAK@

#endif
  • cmake构建脚本
cmake_minimum_required (VERSION 3.10)
project(Arithmetic VERSION 3.4.0.0)

configure_file(${CMAKE_SOURCE_DIR}/src/config.h.in ${CMAKE_SOURCE_DIR}/src/config.h)
include_directories(${CMAKE_SOURCE_DIR}/src)

add_executable(Arithmetic ${CMAKE_SOURCE_DIR}/src/main.c)
  • main文件内容
#include <stdio.h>
#include "config.h"

void main()
{
    printf("cmake config version.\n");
    printf("PROJECT_NAME:%s.\n",PROJECT_NAME);
    printf("PROJECT_VERSION:%s.\n",PROJECT_VERSION);
    printf("PROJECT_VERSION_MAJOR:%d.\n",PROJECT_VERSION_MAJOR);
    printf("PROJECT_VERSION_MINOR:%d.\n",PROJECT_VERSION_MINOR);
    printf("PROJECT_VERSION_PATCH:%d.\n",PROJECT_VERSION_PATCH);
    printf("PROJECT_VERSION_TWEAK:%d.\n",PROJECT_VERSION_TWEAK);
    return;
}

configure_file

configure_file(<input> <output>
                 [COPYONLY] [ESCAPE_QUOTES] [@ONLY])

​ configure_file其本质是为了在input文件中可以使用cmake变量

​ 将文件<input>拷贝到<output>然后替换文件内容中引用到的变量值。如果<input>是相对路径,它被评估的基础路径是当前源码路径。<input>必须是一个文件,而不是个路径。如果<output>是一个相对路径,它被评估的基础路径是当前二进制文件路径。如果<output>是一个已有的路径,那么输入文件将会以它原来的名字放到那个路径下。

该命令替换掉在输入文件中,以${VAR}格式或@VAR@格式引用的任意变量,如同它们的值是由CMake确定的一样。 如果一个变量还未定义,它会被替换为空。如果指定了COPYONLY选项,那么变量就不会展开。如果指定了ESCAPE_QUOTES选项,那么所有被替换的变量将会按照C语言的规则被转义。该文件将会以CMake变量的当前值被配置。如果指定了@ONLY选项,只有@VAR@格式的变量会被替换而${VAR}格式的变量则会被忽略。这对于配置使用${VAR}格式的脚本文件比较有用。

include_directories

为构建树添加包含路径

include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)

​ 将给定的路径添加到编译器搜索包含文件(.h文件)的路径列表中。缺省情况下,该路径会被附加在当前路径列表的后面。这种缺省行为可以通过设置CMAKE_include_directories_BEFORE变量为ON被改变。通过将该变量改变为BEFOREAFTER,你可以在追加和附加在前端这两种方式中选择,而不用理会缺省设置。如果指定了SYSTEM选项,编译器将会认为该路径是某种平台上的系统包含路径。

target设置版本号

set_target_properties(target1 target2 ...
                        PROPERTIES prop1 value1
                        prop2 value2 ...)
动态库设置版本号

set_target_properties(demo PROPERTIES VERSION 1.6.0.0 SOVERSION 1)

其中,libdemo.so.1.6.0.0为动态库的文件名(realname),libdemo.so.1为动态库的别名(soname),libdemo.so为动态库的链接名(linkname)。

可执行文件设置版本号

set_target_properties(demo PROPERTIES VERSION 1.6.0.0 )

会产生两个文件demo和demo-1.6.0.0两个文件,其中demo-1.6.0.0为可执行文件名。

dll添加版本信息

  • 参考cmake_example/example_05

windows平台dll添加版本信息,通过rc资源文件实现。其实现原理与配置文件中添加版本号类似。

在源码根目录创建rc资源文件模板version.rc.in

1 VERSIONINFO
 FILEVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, @PROJECT_VERSION_TWEAK@
 PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, @PROJECT_VERSION_TWEAK@
 FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x40004L
 FILETYPE 0x2L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "080404b0"
        BEGIN
            VALUE "CompanyName", "@PROJECT_NAME@"
            VALUE "FileDescription", "@PROJECT_NAME@"
            VALUE "FileVersion", "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.@PROJECT_VERSION_TWEAK@"
            VALUE "InternalName", "@PROJECT_NAME@.dll"
            VALUE "LegalCopyright", "Copyright (C) 2022"
            VALUE "OriginalFilename", "@PROJECT_NAME@.dll"
            VALUE "ProductName", "@PROJECT_NAME@"
            VALUE "ProductVersion", "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.@PROJECT_VERSION_TWEAK@"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x804, 1200
    END
END

在CMakeLists.txt中使用configure_file生成资源文件,将资源文件添加进源文件中。

project(Arithmetic_Add VERSION 3.4.0.0)

aux_source_directory(${CMAKE_SOURCE_DIR}/src/add_subdirectory ADD_SOURCE_LIST)

source_group(TREE ${CMAKE_SOURCE_DIR} FILES ${ADD_SOURCE_LIST})

configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in"
				"${CMAKE_CURRENT_SOURCE_DIR}/version.rc")

add_library(Arithmetic_Add SHARED ${ADD_SOURCE_LIST} ${CMAKE_CURRENT_SOURCE_DIR}/version.rc)

VS工程生成目录分组

  • 参考cmake_example/example_07

  • source_group

​ 通过add_library或者add_executable添加源文件,通过生成器产生的工程文件,默认是没有对文件进行分组,不利于管理。

在根目录下创建CMakeLists.txt

cmake_minimum_required (VERSION 3.10)
project (Arithmetic_Demo)

add_subdirectory(${CMAKE_SOURCE_DIR}/src/add_subdirectory)
add_subdirectory(${CMAKE_SOURCE_DIR}/src/mult_subdirectory)

add_executable(Arithmetic_Demo main.c)

在根目录下创建目录src/add_subdirectory/并创建CMakeLists.txt

project(Arithmetic_Add)
aux_source_directory(${CMAKE_SOURCE_DIR}/src/add_subdirectory ADD_SOURCE_LIST)
source_group(TREE ${CMAKE_SOURCE_DIR} FILES ${ADD_SOURCE_LIST})
add_library(Arithmetic_Add SHARED ${ADD_SOURCE_LIST})
  • source_group

为工程中的源文件中定义一个分组

source_group(name [REGULAR_EXPRESSION regex] [FILES src1 src2 ...])

​ 为工程中的源文件中定义一个分组。这主要用来在Visual Studio中建立文件组按钮(file tabs)。所有列出来的文件或者匹配正则表达式的文件都会被放到这个文件组中。

交叉编译工具链使用

  • 参考cmake_example/example_08

​ 软件密码模块采用C语言开发,除了通用平台编译,有时也会对用户的特殊平台进行交叉编译,主要通过用户提供的交叉编译工具链进行。cmake提供了交叉编译的设置。在cmake中设置交叉编译有两种方式,一种是直接在CMakeLists.txt中设置,一种是通过cmake工具链文件

直接在CMakeLists.txt中设置

cmake_minimum_required (VERSION 3.10)

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(TOOLCHAIN_PATH /home/guo/Desktop/example_07/armtools/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu)
set(CMAKE_SYSROOT /home/guo/Desktop/example_07/armtools/sysroot-glibc-linaro-2.25-2019.12-aarch64-linux-gnu)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH}/bin/aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH}/bin/aarch64-linux-gnu-g++)
set(CMAKE_AR ${TOOLCHAIN_PATH}/bin/aarch64-linux-gnu-ar)

project (Arithmetic_Demo)

add_executable(Arithmetic_Demo main.c)

通过cmake工具链文件

新建cross.cmake文件,文件内容如下:

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(TOOLCHAIN_PATH /home/guo/Desktop/example_07/armtools/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu)
set(CMAKE_SYSROOT /home/guo/Desktop/example_07/armtools/sysroot-glibc-linaro-2.25-2019.12-aarch64-linux-gnu)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH}/bin/aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH}/bin/aarch64-linux-gnu-g++)
set(CMAKE_AR ${TOOLCHAIN_PATH}/bin/aarch64-linux-gnu-ar)

在使用过程中,需要加入CMAKE_TOOLCHAIN_FILE

cmake -DCMAKE_TOOLCHAIN_FILE=/home/example_07/cross.cmake ../
  • CMAKE_SYSTEM_NAME

目标机target所在的操作系统名称。

  • CMAKE_SYSTEM_PROCESSOR

目标系统的硬件或者CPU型号

  • CMAKE_SYSROOT

工具链的sysroot目录,从这个目录中找到相关依赖

  • CMAKE_C_COMPILER

C语言编译器

  • CMAKE_CXX_COMPILER

C++编译器

  • CMAKE_AR

ar工具

注意:在设置路径时,最好采用绝对路径的方式。

打包rpm和deb

  • 参考cmake_example/example_09

编译好的库文件或者可执行文件,在交付时,需要进行打包操作。cmake中提供工具CPack进行打包操作。

cmake_minimum_required (VERSION 3.16)
project (Arithmetic_Demo)

add_subdirectory(${CMAKE_SOURCE_DIR}/src/add_subdirectory)
add_subdirectory(${CMAKE_SOURCE_DIR}/src/mult_subdirectory)

include_directories(${CMAKE_SOURCE_DIR}/src/add_subdirectory
	${CMAKE_SOURCE_DIR}/src/mult_subdirectory)

add_executable(Arithmetic_Demo main.c)
target_link_libraries(Arithmetic_Demo 
		Arithmetic_Add
		Arithmetic_Mult)

install(TARGETS Arithmetic_Demo Arithmetic_Add Arithmetic_Mult
	RUNTIME DESTINATION bin
	LIBRARY DESTINATION lib
	ARCHIVE DESTINATION lib
	PUBLIC_HEADER DESTINATION include)
	
install(FILES ${CMAKE_SOURCE_DIR}/src/add_subdirectory/add.h DESTINATION include)
install(FILES ${CMAKE_SOURCE_DIR}/src/mult_subdirectory/mult.h DESTINATION include)

set(CPACK_PACKAGE_NEME "Arithmetic_Demo")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.1-6), libc6 (<< 3)")
set(CPACK_PACKAGE_CONTACT "guo.jiaxin04828@westone.com.cn")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "guo.jiaxin")
#set(CPACK_GENERATOR "RPM")
set(CPACK_GENERATOR "DEB")

include(CPack)

在根目录下创建build目录,在build目录中生成执行脚本build.sh

mkdir build_pack
cd build_pack
cmake ../../
cmake --build ./
make
make package
cd ../

  • install

指定在安装时要运行的规则。

TARGETS版本的install命令

  install(TARGETS targets... [EXPORT <export-name>]
          [[ARCHIVE|LIBRARY|RUNTIME|FRAMEWORK|BUNDLE|
            PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
           [DESTINATION <dir>]
           [PERMISSIONS permissions...]
           [CONFIGURATIONS [Debug|Release|...]]
           [COMPONENT <component>]
           [OPTIONAL] [NAMELINK_ONLY|NAMELINK_SKIP]
          ] [...])

TARGETS格式的install命令规定了安装工程中的目标(targets)的规则。有5中可以被安装的目标文件:ARCHIVELIBRARYRUNTIMEFRAMEWORK,和BUNDLE。除了被标记为MACOSX_BUNDLE属性的可执行文件被当做OS X上的BUNDLE目标外,其他的可执行文件都被当做RUNTIME目标。静态链接的库文件总是被当做ARCHIVE目标。模块库总是被当做LIBRARY目标。对于动态库不是DLL格式的平台来说,动态库会被当做LIBRARY目标来对待,被标记为FRAMEWORK的动态库是例外,它们被当做OS X上的FRAMEWORK目标。对于DLL平台而言,动态库的DLL部分被当做一个RUNTIME目标而对应的导出库被当做是一个ARCHIVE目标。所有基于Windows的系统,包括Cygwin,都是DLL平台。ARCHIVE,LIBRARY,RUNTIME和FRAMEWORK参数改变了后续属性会加诸之上的目标的类型。如果只给出了一种类型,那么只有那种类型的目标会被安装(这样通常只会安装一个DLL或者一个导出库。)

FILES版本的install命令

  install(FILES files... DESTINATION <dir>
          [PERMISSIONS permissions...]
          [CONFIGURATIONS [Debug|Release|...]]
          [COMPONENT <component>]
          [RENAME <name>] [OPTIONAL])

FILES版本的install命令指定了为一个工程安装文件的规则。在命令中,以相对路径方式给出的文件名是相对于当前源代码路径而言的。以这个版本安装的文件,如果没有指定PERMISSIONS选项,默认会具有OWNER_WRITEOWNER_READGROUP_READ,和WORLD_READ的权限。

  • CPACK_PACKAGE_NEME

设置安装包的包名

  • CPACK_PACKAGE_VERSION

设置安装包的版本

  • CPACK_DEBIAN_PACKAGE_DEPENDS

设置安装包依赖

  • CPACK_PACKAGE_CONTACT

设置安装包联系方式

  • CPACK_DEBIAN_PACKAGE_MAINTAINER

设置维护人

  • CPACK_GENERATOR

设置安装包格式

识别操作系统

IF (CMAKE_SYSTEM_NAME MATCHES "Linux") 
    message(STATUS "current platform: Linux ") 
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Windows") 
    message(STATUS "current platform: Windows") 
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin") 
    message(STATUS "current platform: Mac OS X") 
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Android")
	message(STATUS "current platform: Android") 
ELSE () 
    message(STATUS "other platform: ${CMAKE_SYSTEM_NAME}") 
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux") 
  • CMAKE_SYSTEM_NAME

操作系统名称

  • message

控制台打印信息

message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR]
          "message to display" ...)

可选的关键字指定消息的类型:

      (无)           = 重要消息;
      STATUS         = 非重要消息;
      WARNING        = CMake 警告, 会继续执行;
      AUTHOR_WARNING = CMake 警告 (dev), 会继续执行;
      SEND_ERROR     = CMake 错误, 继续执行,但是会跳过生成的步骤;
      FATAL_ERROR    = CMake 错误, 终止所有处理过程;

添加编译参数

CMAKE_<LANG>_FLAGS

在构建过程中,需要传递编译参数,在cmake中通过CMAKE_<LANG>_FLAGS参数传递

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
  • CMAKE_C_FLAGS:初始化CFLAGS环境变量.
  • CMAKE_CXX_FLAGS: 初始化CXXFLAGS环境变量.
  • CMAKE_CUDA_FLAGS: 初始化CUDAFLAGS环境变量.
  • CMAKE_Fortran_FLAGS:初始化FFLAGS环境变量.

其编译标志会添加到add_compile_options()target_compile_options()之前。

add_compile_options

为所有编译器添加编译选项

add_compile_options(-std=c++11)

由于是对所有编译器添加的的编译选项,当编译C代码时,会产生警告。

添加宏定义

  • add_definitions

为所有源文件添加宏定义

add_definitions(-DENABLE_OPENSSL -DENABLE_OPENSSL_RSA)

对应C/C++如下,通过add_definitions

#define ENABLE_OPENSS
#define ENABLE_OPENSSL_RSA
  • add_compile_definitions

为所有源文件添加宏定义

add_compile_definitions(ENABLE_OPENSSL=1 ENABLE_OPENSSL_RSA=1)

对应C/C++如下,通过add_compile_definitions

#define ENABLE_OPENSS	1
#define ENABLE_OPENSSL_RSA	1
  • target_compile_definitions

为指定target添加宏定义

target_compile_definitions(scmsdk PUBLIC ENBALE_RSA=1)

寻找文件

寻找库文件

在构建target时,常常需要引入第三方库文件,通过find_library查找库文件。

make_minimum_required (VERSION 3.21)
project (CmakeDemo)
find_library (LIBC_VAR libc)
#find_library (LIBC_VAR libArithmetic PATHS ./ PATH_SUFFIXES lib) # 会从./以及./lib中搜索指定的Arithmetic库是否存在
if (${LIBC_VAR} STREQUAL "libc-NOTFOUND")
    message (FATAL_ERROR "required libc library but not found!")
else()
    message (STATUS "libc library found in ${LIBC_VAR}")
endif()

find_library简版命令如下,简版命令已经能够满足大多数场景使用:

find_library(<VAR> name1 [path1 path2 ...])

find_library完整版命令:

find_library(
    <VAR>
    name | NAMES name1 [name2 ...]
    [HINTS path1 [path2 ... ENV var]]
    [PATHS path1 [path2 ... ENV var]]
    [PATH_SUFFIXES suffix1 [suffix2 ...]]
    [DOC "cache documentation string"]
    [NO_DEFAULT_PATH]
    [NO_CMAKE_ENVIRONMENT_PATH]
    [NO_CMAKE_PATH]
    [NO_SYSTEM_ENVIRONMENT_PATH]
    [NO_CMAKE_SYSTEM_PATH]
    [CMAKE_FIND_ROOT_PATH_BOTH |
    ONLY_CMAKE_FIND_ROOT_PATH |
    NO_CMAKE_FIND_ROOT_PATH])

该命令用来查找一个库文件。一个名为<VAR>的cache条目会被创建来存储该命令的结果。如果找到了该库文件,那么结果会存储在该变量里,并且搜索过程将不再重复,除非该变量被清空。如果没有找到,结果变量将会是<VAR>-NOTFOUND,并且在下次使用相同变量调用find_library命令时,搜索过程会再次尝试。在NAMES参数后列出的文件名是要被搜索的库名。附加的搜索位置在PATHS参数后指定。如果再HINTS或者PATHS字段中设置了ENV变量var,环境变量var将会被读取并从系统环境变量转换为一个cmake风格的路径list。例如,指定ENV PATH是获取系统path变量并将其转换为cmake的list的一种方式。在DOC之后的参数用来作为cache中的注释字符串。PATH_SUFFIXES选项指定了每个搜索路径下待搜索的子路径。

如果指定了NO_DEFAULT_PATH选项,那么搜索的过程中不会有其他的附加路径。如果没有指定该选项,搜索过程如下:

  1. 搜索cmake特有的cache变量指定的路径。这些变量是在用cmake命令行时,通过-DVAR=value指定的变量。如果指定了NO_CMAKE_PATH选项,这些路径会被跳过。搜索的路径还包括:
   对于每个在CMAKE_PREFIX_PATH中的<prefix>,路径<prefix>/lib
   CMAKE_LIBRARY_PATH
   CMAKE_FRAMEWORK_PATH
  1. 搜索cmake特有的环境变量指定的路径。这些变量是用户的shell配置中设置的变量。如果指定了NO_CMAKE_ENVIRONMENT_PATH选项,这些路径会被跳过。搜索的路径还包括:
   对于每个在CMAKE_PREFIX_PATH中的<prefix>,路径<prefix>/lib
   CMAKE_LIBRARY_PATH
   CMAKE_FRAMEWORK_PATH
  1. 搜索由HINTS选项指定的路径。这些路径是系统内省(introspection)估算出的路径,比如由另一个已经发现的库文件的地址提供的参考信息。硬编码的推荐路径应该通过PATHS选项指定。

  2. 查找标准的系统环境变量。如果指定了NO_SYSTEM_ENVIRONMENT_PATH选项,这些路径会被跳过。搜索的路径还包括:

   PATH
   LIB
  1. 查找在为当前系统的平台文件中定义的cmake变量。如果指定了NO_CMAKE_SYSTEM_PATH选项,该路径会被跳过。搜索的路径还包括:
   对于每个在CMAKE_SYSTEM_PREFIX_PATH中的<prefix>,路径<prefix>/lib
   CMAKE_SYSTEM_LIBRARY_PATH
   CMAKE_SYSTEM_FRAMEWORK_PATH
  1. 搜索PATHS选项或者精简版命令指定的路径。这些通常是硬编码的推荐搜索路径。

在Darwin或者支持OS X 框架的系统上,cmake变量CMAKE_FIND_FRAMEWORK可以用来设置为空,或者下述值之一:

   "FIRST"  - 在标准库或头文件之前查找框架。在Darwin系统上这是默认选项。
   "LAST"   - 在标准库或头文件之后查找框架。
   "ONLY"   - 仅仅查找框架。
   "NEVER" - 从不查找框架。

在Darwin或者支持OS X Application Bundles的系统,cmake变量CMAKE_FIND_APPBUNDLE可以被设置为空或者下面这些值中的一个:

   "FIRST"  - 在标准库或头文件之前查找application bundles。在Darwin系统上这是默认选项。
   "LAST"   - 在标准库或头文件之后查找application bundles。
   "ONLY"   - 仅仅查找application bundles。
   "NEVER" - 从不查找application bundles。

CMake变量CMAKE_FIND_ROOT_PATH指定了一个或者多个优先于其他搜索路径的搜索路径。该变量能够有效地重新定位在给定位置下进行搜索的根路径。该变量默认为空。当使用交叉编译时,该变量十分有用:用该变量指向目标环境的根目录,然后CMake将会在那里查找。默认情况下,在CMAKE_FIND_ROOT_PATH中列出的路径会首先被搜索,然后是“非根”路径。该默认规则可以通过设置CMAKE_FIND_ROOT_PATH_MODE_LIBRARY做出调整。在每次调用该命令之前,都可以通过设置这个变量来手动覆盖默认行为。如果使用了NO_CMAKE_FIND_ROOT_PATH变量,那么只有重定位的路径会被搜索。

默认的搜索顺序的设计逻辑是按照使用时从最具体到最不具体。通过多次调用find_library命令以及NO_*选项,可以覆盖工程的这个默认顺序:

find_library(<VAR> NAMES name PATHS paths... NO_DEFAULT_PATH)
find_library(<VAR> NAMES name)

只要这些调用中的一个成功返回,结果变量就会被设置并且被存储到cache中;这样随后的调用都不会再行搜索。如果那找到的库是一个框架,VAR将会被设置为指向框架“<完整路径>/A.framework” 的完整路径。当一个指向框架的完整路径被用作一个库文件,CMake将使用-framework A,以及-F<完整路径>这两个选项将框架连接到目标上。

寻找头文件

通过find_file查找一个文件的完整路径。

make_minimum_required (VERSION 3.21)
project (CmakeDemo)
find_file (PCKS11_VAR pkcs11.h)
#find_library (PCKS11_VAR pkcs11.h PATHS ./ PATH_SUFFIXES include) # 会从./以及./include中搜索指定的pkcs11.h库是否存在
if (${PCKS11_VAR} STREQUAL "pkcs11.h-NOTFOUND")
    message (FATAL_ERROR "required pkcs11.h header but not found!")
else()
    message (STATUS "pkcs11.h header found in ${PCKS11_VAR}")
endif()

find_file简版命令如下,简版命令已经能够满足大多数场景使用:

find_file(<VAR> name1 [path1 path2 ...])

find_file完整版命令:

 find_file(
             <VAR>
             name | NAMES name1 [name2 ...]
             [HINTS path1 [path2 ... ENV var]]
             [PATHS path1 [path2 ... ENV var]]
             [PATH_SUFFIXES suffix1 [suffix2 ...]]
             [DOC "cache documentation string"]
             [NO_DEFAULT_PATH]
             [NO_CMAKE_ENVIRONMENT_PATH]
             [NO_CMAKE_PATH]
             [NO_SYSTEM_ENVIRONMENT_PATH]
             [NO_CMAKE_SYSTEM_PATH]
             [CMAKE_FIND_ROOT_PATH_BOTH |
              ONLY_CMAKE_FIND_ROOT_PATH |
              NO_CMAKE_FIND_ROOT_PATH])

这条命令用来查找指定文件的完整路径。一个名字是<VAR>的缓存条目(参见CMakeCache.txt的介绍——译注)变量会被创建,用来存储该命令的结果。如果发现了文件的一个完整路径,该结果会被存储到该变量里并且搜索过程不会再重复,除非该变量被清除。如果什么都没发现,搜索的结果将会是<VAR>-NOTFOUND;并且在下一次以相同的变量调用find_file时,该搜索会重新尝试。被搜索的文件的文件名由NAMES选项后的名字列表指定。附加的其他搜索位置可以在PATHS选项之后指定。如果ENV varHINTSPATHS段中出现,环境变量var将会被读取然后被转换为一个系统级环境变量,并存储在一个cmake风格的路径list中。比如,使用ENV PATH将会将系统的path变量列出来。在DOC之后的变量将会用于cache中的文档字符串(documentation string)。PATH_SUFFIXES指定了在每个搜索路径下的需要搜索的子路径。

如果指定了NO_DEFAULT_PATH选项,那么在搜索时不会附加其它路径。如果没有指定NO_DEFAULT_PATH选项,搜索过程如下:

  1. 在cmake特有的cache变量中指定的搜索路径搜索。这些路径用于在命令行里用-DVAR=value被设置。如果使用了NO_CMAKE_PATH选项,该路径会被跳过。(此句翻译可能有误——译注。)搜索路径还包括:
  对于每个在CMAKE_PREFIX_PATH中的路径<prefix>,<prefix>/include
  变量:CMAKE_INCLUDE_PATH
  变量:CMAKE_FRAMEWORK_PATH
  1. 在cmake特定的环境变量中指定的搜索路径搜索。该路径会在用户的shell配置中被设置。如果指定了NO_CMAKE_ENVIRONMENT_PATH选项,该路径会被跳过。搜索路径还包括:
  对于每个在CMAKE_PREFIX_PATH中的路径<prefix>,<prefix>/include
  变量:CMAKE_INCLUDE_PATH
  变量:CMAKE_FRAMEWORK_PATH
  1. 由HINTS选项指定的搜索路径。这些路径是由系统内省(introspection)时计算出来的路径,比如已经发现的其他项的位置所提供的痕迹。硬编码的参考路径应该使用PATHS选项指定。(HINTS与PATHS有何不同?比后者的优先级高?有疑问。——译注)

  2. 搜索标准的系统环境变量。如果指定NO_SYSTEM_ENVIRONMENT_PATH选项,搜索路径将跳过其后的参数。搜索路径包括环境变量PATHINCLUDE

  3. 查找在当前系统的平台文件中定义的cmake变量。如果指定了NO_CMAKE_SYSTEM_PATH选项,该路径会被跳过。其他的搜索路径还包括:

  对于每个在CMAKE_PREFIX_PATH中的路径<prefix>,<prefix>/include
  变量:CMAKE_SYSTEM_INCLUDE_PATH
  变量:CMAKE_SYSTEM_FRAMEWORK_PATH
  1. 搜索由PATHS选项指定的路径或者在命令的简写版本中指定的路径。这一般是一些硬编码的参考路径。在Darwin后者支持OS X框架的系统上,cmake变量CMAKE_FIND_FRAMWORK可以设置为空或者下述值之一:
   "FIRST"  - 在标准库或者头文件之前先查找框架。对于Darwin系统,这是默认的。
   "LAST"   - 在标准库或头文件之后再查找框架。
   "ONLY"   - 只查找框架。
   "NEVER" - 从不查找框架。

在Darwin或者支持OS X Application Bundles的系统上,cmake变量CMAKE_FIND_APPBUNDLE可以被设置为空,或者下列值之一:

   "FIRST"  - 在标准程序之前查找application bundles,这也是Darwin系统的默认选项。
   "LAST"   - 在标准程序之后查找application bundlesTry。
   "ONLY"   - 只查找application bundles。
   "NEVER" - 从不查找application bundles。

CMake的变量CMAKE_FIND_ROOT_PATH指定了一个或多个在所有其它搜索路径之前的搜索路径。该选项很有效地将给定位置下的整个搜索路径的最优先路径进行了重新指定。默认情况下,它是空的。当交叉编译一个指向目标环境下的根目录中的目标时,CMake也会搜索那些路径;该变量这时显得非常有用。默认情况下,首先会搜索在CMAKE_FIND_ROOT_PATH变量中列出的路径,然后才是非根路径。设置CMAKE_FIND_ROOT_PATH_MODE_INCLUDE变量可以调整该默认行为。该行为可以在每次调用时被手动覆盖。通过使用CMAKE_FIND_ROOT_PATH_BOTH变量,搜索顺序将会是上述的那样。如果使用了NO_CMAKE_FIND_ROOT_PATH变量,那么CMAKE_FIND_ROOT_PATH将不会被用到。如果使用了ONLY_CMAKE_FIND_ROOT_PATH变量,那么只有CMAKE_FIND_ROOT_PATH中的路径(即re-rooted目录——译注)会被搜索。

一般情况下,默认的搜索顺序是从最具体的路径到最不具体的路径。只要用NO_*选项多次调用该命令,工程就可以覆盖该顺序。

     find_file(<VAR> NAMES name PATHS paths... NO_DEFAULT_PATH)
     find_file(<VAR> NAMES name)

只要这些调用中的一个成功了,返回变量就会被设置并存储在cache中;然后该命令就不会再继续查找了。

0

评论区