cmake-CMake记录


title: cmake-CMake记录
categories: CMake
tags: [cmake, makefile, 跨平台, 记录]
date: 2020-02-08 15:41:39
comments: false以前

测试工程: https://github.com/yangxuan0261/CMakeLab


前篇

  • 官方
    • 下载: https://github.com/Kitware/CMake/releases
    • cmake tutorial - https://cmake.org/cmake-tutorial
  • CMake官方教程 - https://www.jianshu.com/p/6df3857462cd
  • cmake使用教程 (翻译自官方) - https://juejin.im/post/5a6f32e86fb9a01ca6031230
  • CMake-Cookbook (不错的系列) - https://chenxiaowei.gitbook.io/cmake-cookbook/

CMake编译原理

CMake 是一种跨平台编译工具,比 make 更为高级,使用起来要方便得多。CMake 主要是编写 CMakeLists.txt 文件,然后用 cmake 命令将 CMakeLists.txt 文件转化为 make 所需要的 makefile 文件,最后用 make 命令编译源码生成可执行程序或共享库(so(shared object))。因此 CMake 的编译基本就两个步骤:

  1. cmake
  2. make

CMake说明

一般把 CMakeLists.txt 文件放在工程目录下,使用时,先创建一个叫 build 的文件夹(这个并非必须,因为 cmake 命令指向CMakeLists.txt 所在的目录,例如 cmake … 表示 CMakeLists.txt 在当前目录的上一级目录。cmake 后会生成很多编译的中间文件以及 makefile 文件,所以一般建议新建一个新的目录,专门用来编译),然后执行下列操作:

cd build 
cmake .. 
make 

其中 cmake … 在build里生成 Makefile,make根据生成 makefile 文件,编译程序,make 应当在有 Makefile 的目录下,根据 Makefile 生成可执行文件。


最简单 CMakeLists.txt 文件

最简单的一个工程需要有一个这样的 CMakeLists.txt 文件

cmake_minimum_required(VERSION 3.15)
project(Tutorial) 
add_executable(Tutorial tutorial.cxx) // 或者 库: add_library(Tool01 SHARED library.cpp library.h)

ubuntu 安装 cmake

可以使用 apt install cmake, 不过 18.04 安装的 cmake 版本是 3.10.2.

安装指定版本

  1. 去 GitHub 上下载指定的版本, 如: cmake-3.15.0.tar.gz

  2. 解压 并 编译

    $ tar -xvzf cmake-3.15.0.tar.gz
    $ cd cd cmake-3.15.0
    $ ./bootstrap    #执行引导文件, 该命令执行需要一定时间,请耐心等待。成功执行结束之后,末尾提示:CMake has bootstrapped.  Now run make.
    $ make
    $ sudo make install
    

    生成的可执行文件都在 bin 目录下

  3. 拷贝 可执行文件 到系统目录

    $ cp bin/* /usr/bin/
    $ cmake --version # 查看版本
    cmake version 3.15.0
    
  4. 清理安装源代码

    $ cd ..
    $ rm -fr cmake-3.15.0
    

常用的预定义变量

  • CMake中常用的预定义变量 - https://blog.csdn.net/u012086173/article/details/86480886

在这里插入图片描述

PROJECT_NAME: 通过PROJECT指定的项目名称

project(Demo)

PROJECT_SOURCE_DIR: 工程的根目录,上图中的Demo目录
PROJECT_BINARY_DIR: 执行cmake命令的目录,一般是在build目录,在此目录执行cmake ..
CMAKE_CURRENT_SOURCE_DIR: 当前CMakeLists.txt文件所在目录
CMAKE_CURRENT_BINARY_DIR: 编译目录,可使用ADD_SUBDIRECTORY来修改此变量

# 添加cmake执行子目录
ADD_SUBDIRECTORY(example)

EXECUTABLE_OUTPUT_PATH: 二进制可执行文件输出位置

# 设置可执行文件的输出路径为 build/bin
set(EXECUTABLE_OUTPUT_PATH ${
    
    PROJECT_BINARY_DIR}/bin)

LIBRARY_OUTPUT_PATH: 库文件输出位置

set(LIBRARY_OUTPUT_PATH ${
    
    PROJECT_BINARY_DIR}/lib)

常用系统信息变量

$ cmake --version
cmake version 3.11.2

CMAKE_MAJOR_VERSION: cmake 的 主版本号cmake version 3.11.2中的3
CMAKE_MINOR_VERSION: cmake 的 次版本号cmake version 3.11.2中的11
CMAKE_PATCH_VERSION: cmake 的 补丁等级cmake version 3.11.2中的2

CMAKE_SYSTEM: 系统名称,带版本号
CMAKE_SYSTEM_NAME: 系统名称,不带版本号
CMAKE_SYSTEM_VERSION: 系统版本号
CMAKE_SYSTEM_PROCESSOR: 处理器名称


编译选项

BUILD_SHARED_LIBS: 默认的库编译方式(shared or static),默认为static,一般在 ADD_LIBRARY时直接指定编译库的类型
CMAKE_C_FLAGS: 设置C编译选项
CMAKE_CXX_FLAGS: 设置C++编译选项

  • CMAKE_CXX_FLAGS_DEBUG: 设置编译类型为 Debug 时的编译选项
  • CMAKE_CXX_FLAGS_RELEASE: 设置编译类型为 Release 时的编译选项

CMAKE_CXX_COMPILER: 设置C++编译器

# 设置C++编译器为g++
set(CMAKE_CXX_COMPILER "g++")
# 设置标准库版本为c++17 并开启警告
set(CMAKE_CXX_FLAGS "-std=c++17 -Wall")
# 设置Debug模式下,不开启优化,开启调试,生成更详细的gdb调试信息
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -ggdb")
# 设置Release模式下,开启最高级优化
set(CMAKE_CXX_FLAGS_RELEASE "-O3")

语法

  • 蛋疼的语法 - https://juejin.im/post/5a73eba75188257a64266c15

拷贝文件

语法:

configure_file(<input> <output>
               [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
               [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ]
)

示例

# 拷贝文件
configure_file(
        "${LINK_DIR}/libTool01.dll"
        "${PROJECT_BINARY_DIR}/libTool01.dll"
        COPYONLY
)

设置导出名称

set_target_properties(tool01 PROPERTIES OUTPUT_NAME "hello") # 修改导出的库名. 将原来导出为 libtool01.so 的库名修改为 libhello.so

添加子目录

可以参考: 有源码

作用是: 可以加入含有 CMakeLists.txt 的子目录, 一起构建, 可以理解为 有源码的库

语法:

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

例如

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)

构建完后, src 生成的中间文件会在 执行命令时所在的目录的 bin 目录下


添加编译选项

CMAKE_CXX_FLAGS 内置变量拼接一下即可, 如: 拼接上 -lmingw32

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lmingw32")

glob 匹配文件 - 批量导入

  • 官方文档 - https://cmake.org/cmake/help/v3.12/command/file.html#glob-recurse

如果每加一个 .h/.cpp 文件都要在 add_executable 编译中加入, 未免有点麻烦

可以使用 glob 去匹配文件实现 批量导入


非递归 GLOB

# set(SOURCE_FILES main.cpp test.cpp test.h) # 指定文件

file(GLOB SOURCES_H_CPP # glob 匹配导入根目录文件, 不能递归子目录
    *.h
    *.cpp
)
set(SOURCE_FILES ${
    
    SOURCES_H_CPP})
add_executable(TestCpp ${
    
    SOURCE_FILES})

递归 GLOB_RECURSE

file(GLOB_RECURSE SRC_aaa # 递归 aaa 目录及子目录 中的所有 h/cpp 文件
        aaa/**.h
        aaa/**.cpp
)
set(SOURCE_FILES main.cpp ${SRC_aaa} )
add_executable(TestCpp ${SOURCE_FILES})

添加 预编译宏

# 添加 预编译宏
add_definitions(
        -D USE_LOG=3 # 定义值
        -D USE_FILTER
)

cpp 中使用

#if (USE_LOG == 3)
    std::cout << USE_LOG << std::endl;
    std::cout << "USE_LOG yes! " << std::endl;
#endif

#ifdef USE_FILTER
    std::cout << "USE_FILTER yes!" << std::endl;
#endif

添加 第三方库

无源码

这种方式适用于 第三方库 没有源码, 只有 头文件库文件

  • CLion中使用CMake导入第三方库的方法 - https://blog.csdn.net/Haoran823/article/details/71657602
  1. 编写根目录 CMakeLists.txt 文件

    1. 引入 头文件 路径 和 动态库 路径, 位置要在 add_executable 之前

      set(INC_DIR E:/ws_cpp/Tool)
      set(LINK_DIR E:/ws_cpp/Tool/cmake-build-debug)
      
      include_directories(${
              
              INC_DIR})
      link_directories(${
              
              LINK_DIR})
      

      这里引入动态库文件是 libTool.dll, 所以这里写 Tool (lib的写法就是: lib[name].dll )

    2. 连接库, 位置要在 add_executable 之后

      target_link_libraries(TestCpp Tool)
      
  2. build 一下.

    "D:\CLion 2019.3.3\bin\cmake\win\bin\cmake.exe" --build E:\ws_cpp\TestCpp\cmake-build-debug --target TestCpp -- -j 6
    [ 33%] Linking CXX executable TestCpp.exe
    [100%] Built target TestCpp
    

    构建完执行 TestCpp.exe 会报错: Process finished with exit code -1073741515 (0xC0000135), 原因是找不到 libTool.dll, 需要将 libTool.dll 掉与 TestCpp.exe 同一目录.

    • Linux 环境下的编译

      $ mkdir build # 新建一个 build 临时目录用来存放临时生产的 中间文件
      $ cd build
      $ cmake .. # .. 是指 CMakeLists.txt 文件所在目录, 这里也就是上一级目录
      $ make # 可执行程序就出来了
      

附:

  • 完整 CMakeLists.txt

    cmake_minimum_required(VERSION 3.15)
    project(TestCpp)
    
    set(CMAKE_CXX_STANDARD 14)
    
    set(SOURCE_FILES main.cpp test.cpp test.h)
    
    set(INC_DIR E:/ws_cpp/Tool)
    set(LINK_DIR E:/ws_cpp/TestCpp/cmake-build-debug)
    
    include_directories(${
          
          INC_DIR}) # 相当于gcc/clang 中的-I(i的大写字母)参数, 引入 头文件 目录
    

link_directories(${LINK_DIR}) # 相当于gcc 中的-L参数, 引入 库文件 目录
```

add_executable(TestCpp ${SOURCE_FILES})

target_link_libraries(TestCpp Tool) # 相当于gcc中的-l(小写的l)参数, 链接 库文件
```

有源码

这种方式适用于 第三方库 有源码, 只有 头文件cpp文件

一般可视为项目的其他模块, 连同其他模块一起编译, 比如: 项目根目录下有个模块 Tool02

  1. 编写模块 Tool02 的 CMakeLists.txt 文件.

    add_library(Tool02 library.cpp)
    
  2. 编写根目录 CMakeLists.txt 文件

    1. 引入 头文件 路径 和 模块目录 Tool02

      include_directories ("${PROJECT_SOURCE_DIR}/Tool02")
      add_subdirectory (Tool02) 
      
    2. 连接库, 位置要在 add_executable 之后

      target_link_libraries(TestCpp Tool02)
      
  3. build 一下.

    "D:\CLion 2019.3.3\bin\cmake\win\bin\cmake.exe" --build E:\ws_cpp\TestCpp\cmake-build-debug --target TestCpp -- -j 6
    [ 33%] Linking CXX executable TestCpp.exe
    [100%] Built target TestCpp
    

附:

  • 完整 CMakeLists.txt

    cmake_minimum_required(VERSION 3.15)
    project(TestCpp)
    
    set(CMAKE_CXX_STANDARD 14)
    
    set(SOURCE_FILES main.cpp test.cpp test.h)
    
    include_directories ("${PROJECT_SOURCE_DIR}/Tool02")
    add_subdirectory (Tool02)
    
    add_executable(TestCpp ${
          
          SOURCE_FILES})
    
    target_link_libraries(TestCpp Tool02)
    

动态库 静态库

  • 静态库与动态库构建 - https://www.cnblogs.com/52php/p/5681755.html

语法:

ADD_LIBRARY(hello [SHARED|STATIC|MODULE][EXCLUDE_FROM_ALL]source1 source2 ... sourceN)

你不需要写全libhello.so,只需要填写hello即可,cmake系统会自动为你生成libhello.X。类型有三种:

  • SHARED,动态库(扩展名为.so)
  • STATIC,静态库(扩展名为.a)
  • MODULE,在使用dyld的系统有效,如果不支持dyld,则被当作SHARED对待。
  • EXCLUDE_FROM_ALL 参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手工构建。

库 版本号

语法:

SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

VERSION指代动态库版本,SOVERSION指代API版本。将上述指令加入lib/CMakeLists.txt中,重新构建看看结果。

在build/lib目录会生成:

libhello.so.1.2
libhello.so.1->libhello.so.1.2
libhello.so ->libhello.so.1

安装 共享库/头文件

将libhello.a, libhello.so.x以及hello.h安装到系统目录,才能真正让其他人开发使用,在本例中我们将hello的共享库安装到/lib目录,将hello.h安装到/include/hello目录。

CMakeLists.txt中添加如下指令:

INSTALL(TARGETS hello hello1 hello2
LIBRARY DESTINATION lib # 动态库目的目录
ARCHIVE DESTINATION libstatic) # 静态库目的目录

INSTALL(FILES hello.h DESTINATION include/hello) # 文件的目录

注意,静态库要使用 ARCHIVE 关键字通过:

cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
make install

就可以将头文件和共享库安装到系统目录 /usr/lib 和 /usr/include/hello 中了。


安装

这里需要引入一个新的 cmake 指令 INSTALL和一个非常有用的变量

CMAKE_INSTALL_PREFIX

CMAKE_INSTALL_PREFIX 变量类似于 configure 脚本的 –prefix,常见的使用方法看起来是这个样子:

cmake -D CMAKE_INSTALL_PREFIX=/usr .

CMAKE_INSTALL_PREFIX 的默认定义是 /usr/local

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

INSTALL 指令包含了各种安装类型,我们需要一个个分开解释:

目标文件的安装:

INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
] [...])

参数中的 TARGETS 后面跟的就是我们通过 ADD_EXECUTABLE 或者 ADD_LIBRARY 定义的目标文件,可能是可执行二进制、动态库、静态库。目标类型也就相对应的有三种,ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执行目标二进制。

DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候 CMAKE_INSTALL_PREFIX 其实就无效了。如果你希望使用 CMAKE_INSTALL_PREFIX 来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是${CMAKE_INSTALL_PREFIX}/<DESTINATION定义的路径>

举个简单的例子:

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 名称就可以了。


打包

  • 11.1 生成源代码和二进制包 - https://chenxiaowei.gitbook.io/cmake-cookbook/11.0-chinese/11.1-chinese
  • 使用cpack打包源码 (主要参考) - https://juejin.im/post/5b51b3e76fb9a04fc34c0c7b

CPack是作为一个模块出现在cmake构建系统中的,它是一个非常强大的打包工具,可以用来打包二进制文件或者源码。打包好的二进制文件中包含了所有的cmake install命令需要的安装文件。在打包源码时,也可以生成对应的压缩包。 cpack可以依赖cmake构建生成的config文件,也可以自己编写配置文件

流程

  1. 编写好 CMakeLists.txt 打包指令. 然后 build, 会生成打包配置文件:

    # 打包 源码
    set(CPACK_SOURCE_GENERATOR "TGZ")
    set(CPACK_SOURCE_PACKAGE_FILE_NAME ${
          
          PROJECT_NAME}-${
          
          PROJECT_VERSION_FULL}-src) # 打包文件名称
    # 生成打包 源码 的配置: cmake-build-debug/CPackSourceConfig.cmake
    
    # 打包 可执行文件
    set(CPACK_GENERATOR "TGZ")
    set(CPACK_PACKAGE_FILE_NAME ${
          
          PROJECT_NAME}-${
          
          PROJECT_VERSION_FULL}-bin) # 打包文件名称
    # 生成打包 可执行文件 的配置: cmake-build-debug/CPackConfig.cmake
    
  2. 使用 cpack 指定配置文件进行打包

    $ cpack --config CPackSourceConfig.cmake
    

    示例:

    E:\ws_cpp\CMakeLab\cmake-build-debug (master -> origin)
    $ cpack --config CPackSourceConfig.cmake
    CPack: Create package using TGZ
    CPack: Install projects
    CPack: - Install directory: E:/ws_cpp/CMakeLab
    CPack: Create package
    CPack: - package: E:/ws_cpp/CMakeLab/pack/CMakeLab-1.3.1-src.tar.gz generated.
    

打包格式

set(CPACK_SOURCE_GENERATOR “TGZ”)

  1. 7Z-7Zzip-(.7z)
  2. TBZ2(tar.bz2)
  3. TGZ(.tar.gz)
  4. TXZ(.tar.xz)
  5. TZ(.tar.Z)
  6. ZIP(.zip)

猜你喜欢

转载自blog.csdn.net/yangxuan0261/article/details/104244271