首页 > 其他分享 >[CMake] CMake学习笔记

[CMake] CMake学习笔记

时间:2023-05-28 16:57:04浏览次数:84  
标签:CMakeLists CMake 笔记 学习 ADD usr cpp txt hello

自己的学习和使用总结,还不完善,不定时更新。

一. 简介

  • cmake是一款高级编译配置工具;

  • 所有操作都是通过编译CMakeLists.txt来完成的;

  • CMake官方全部推荐使用大写指令;

  • 学习目的:为将来处理大型的C/C++、Java项目做准备;

环境

  • Ubuntu:20.04
  • cmake:3.16.3

简单尝试

  1. C++写一个 “Hello World” 程序:

    #include <iostream>
    using namespace std;
    int main() {
        cout << "Hello World!" << endl;
        return 0;
    }
    
  2. 编写 CMakeLists.txt 文件(严格区分大小写):

    #CMakeLists.txt
    PROJECT (HELLO)
    
    SET(SRC_LIST main.cpp)
    
    MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
    
    MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})
    
    ADD_EXECUTABLE(hello ${SRC_LIST})
    
    
  3. 使用cmake,生成 Makefile 文件;

  4. 当前目录下执行 make 命令,就会生成可执行文件。

二. 语法

1. 关键字

1.1 PROJECT

用于指定工程的名字和支持的语言,默认支持所有语言,下面是一些示例:

  • PROJECT (HELLO):指定了工程的名字为 HELLO ,并且支持所有语言,建议使用

  • PROJECT (HELLO CXX):指定了工程的名字,并且支持语言是C++;

  • PROJECT (HELLO C CXX):指定了工程的名字,并且支持语言是C和C++;

该关键字隐式定义了两个CMAKE的变量

_BINARY_DIR,本例中是 HELLO_BINARY_DIR

_SOURCE_DIR,本例中是 HELLO_SOURCE_DIR

MESSAGE关键字就可以直接使用者两个变量,当前都指向当前的工作目录,后面会讲外部编译

问题:如果改了工程名,这两个变量名也会改变

解决:又定义两个预定义变量:PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,这两个变量和HELLO_BINARY_DIR,HELLO_SOURCE_DIR是一致的。所以改了工程名也没有关系。


1.2 SET

  • 作用:定义变量;

例如,SET(SRC_LIST main.cpp) ,定义了一个变量 SRC_LIST变量,其包含了main.cpp。

如果有多个值,也可以 SET(SRC_LIST main.cpp t1.cpp t2.cpp)


1.3 MESSAGE

  • 作用:向终端输出用户自定义的信息;

主要包含三种信息:

  • SEND_ERROR:产生错误,生成过程被跳过。
  • SATUS:输出前缀为 "--" 的状态信息。
  • FATAL_ERROR:立即终止所有 cmake 过程.

1.4 ADD_EXECUTABLE

  • 作用:生成可执行文件,例如 ADD_EXECUTABLE(hello ${SRC_LIST}) 中,将读取变量 SRC_LIST 中包含的内容,编译生成名为 “hello” 的可执行文件(名字可任意指定);

1.5 ADD_SUBDIRECTORY

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

  • 作用:用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制的存放位置;
  • 示例ADD_SUBDIRECTORY (src bin),源码目录是 ./src,输出的目标二进制文件存放位置是 ./build/bin(会自动新建);

1.6 ADD_LIBRARY

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
  • 作用:生成库文件;
  • hello:生成的库文件名;
  • SHARED / STATIC:固定关键字,SHARED——动态库 STATIC——静态库;
  • ${LIBHELLO_SRC}:包含源文件的变量。

1.7 SET_TARGET_PROPERTIES

  • 官方set_target_properties — CMake 3.26.4 Documentation

  • 作用:可以使用该命令设置目标的编译选项、链接选项、输出名称、输出路径等属性。例如,可以使用 set_target_properties 命令设置一个可执行文件的输出名称和输出路径,如下所示:

    set_target_properties(myapp PROPERTIES OUTPUT_NAME "myapp" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
    

    这将设置可执行文件 myapp 的输出名称为 myapp,输出路径为 ${CMAKE_BINARY_DIR}/bin。


1.8 INCLUDE_DIRECTORIES

  • 作用:添加头文件的搜索路径;
  • 示例INCLUDE_DIRECTORIES(/usr/include/hello),如果有多个头文件搜索路径,路径之间用空格分割。

  • 作用:添加非标准的共享库搜索路径;
  • 示例LINK_DIRECTORIES(/usr/lib)

  • 作用:添加需要链接的共享库,只需要传入名字,不需要传入路径,但是共享库的路径需要在系统环境路径下,否则只能使用LINK_DIRECTORIES
  • 示例TARGET_LINK_LIBRARIES(hello libhello.so)

2. 注意事项

  • 变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名;

  • 指令(参数 1 参数 2...) 参数使用括弧括起,参数之间使用空格分号分开。 以上面的 ADD_EXECUTABLE 指令为例,如果存在另外一个 func.cpp 源文件

    就要写成:ADD_EXECUTABLE(hello main.cpp func.cpp)或者ADD_EXECUTABLE(hello main.cpp;func.cpp)

  • 指令是大小写无关的,参数和变量是大小写相关的。但,推荐全部使用大写指令。

  • 常量参数可以加双引号也可以不加,但是如果有空格,则必须加,例如源代码文件名为“m ain.cpp”;

三. 外部构建

上面的简单尝试中就是使用内部构建。但是它生产的临时文件特别多,不方便清理,因此一般都是使用外部构建,该方式就会把生成的临时文件放在build目录下,不会对源文件有任何影响强烈使用外部构建方式。

1. 单CMakeLists文件

通常这种方式是只有一个 CMakeLists.txt 文件,代码源文件放在工程根目录下。通常大一点的项目都不会使用这种方式

工程根目录结构

.
├── build/
├── CMakeLists.txt
└── main.cpp 	# 源码文件可以有多个

步骤:(在 CMakeLists.txt 文件和 源码(main.cpp文件) 都存在的情况)

  1. 新建 build 目录(固定名称,不可指定);
  2. 转到 build 目录;
  3. 执行 cmake .. ,生成 Makefile 文件;
  4. 执行 make 命令,就会在当前目录(build目录)生成可执行文件;

2. 多CMakeLists文件

源码通常放在一个目录下。通常用改方式构建项目。

工程根目录结构

.
├── build
├── CMakeLists.txt		# 根目录下的 CMakeLists.txt 文件
└── src
    ├── CMakeLists.txt	# 源码目录下的 CMakeLists.txt 文件
    └── main.cpp

两个 CMakeLists.txt 文件,通常在根目录存放一个,在源码中存放多个:

根目录下的 CMakeLists.txt 文件

PROJECT (HELLO)
ADD_SUBDIRECTORY (src bin)	# 指定源码文件夹和输出二进制文件夹,注意bin是在build目录下生成的

源码目录下的 CMakeLists.txt 文件

# 生成名为“hello”的可执行文件:这里就不用变量了
ADD_EXECUTABLE (hello main.cpp)

编译步骤和上面的四点一样。

.
├── build
│   ├── bin
│   │   ├── hello	# 可执行文件
│   │   └── # 其他文件
│   ├── CMakeCache.txt
│   └── # 其他文件
├── CMakeLists.txt
└── src
    ├── CMakeLists.txt
    └── main.cpp

四. 安装

安装需要使用倒cmake的一个命令:INSTALL。它可以安装包括:二进制、动态库、静态库以及文件、目录、脚本等。

通常我们会把不同的文件安装到不同的路径下:

类型 路径 参数类型 备注
可执行文件 /usr/bin/usr/local/bin -
静态库*.a /usr/lib/usr/local/lib TARGETS -
动态库(*.so) /usr/lib/usr/local/lib TARGETS -
头文件(*.h) /usr/include/usr/local/include FILES -
脚本(*.sh) (下面是安装在/usr/local/bin,不确定是规范的) PROGRAMS -
工程文档 (下面是安装在/usr/local/share/doc/程序名/,不确定是规范的) DIRECTORY -

1. 工程要求

  • 为工程添加一个子目录 src,用来放置工程源代码;
  • 添加一个子目录 doc,用来放置这个工程的文档 tutorial.txt;
  • 在工程目录添加文本文件 COPYRIGHT、README;
  • 在工程目录添加一个 runhello.sh 脚本,用来调用 hello 二进制;
  • 将构建后的目标文件放入构建目录的 bin 子目录;
  • 将 doc 目录 的内容以及 COPYRIGHT/README 安装到 /usr/share/doc/cmake/ ;

2. 项目目录结构

.
├── build
├── CMakeLists.txt
├── COPYRIGHT
├── doc
│   └── tutorial.txt
├── runhello.sh
└── src
    ├── CMakeLists.txt
    └── main.cpp

3. 安装COPYRIGHT和README

INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)
  • FILES:表示你需要安装的是文件,后面跟上具体文件名;
  • DESTINATION:表示安装的目标路径,后面跟上路径名(相对路径或绝对路径)
    • 相对路径:它是相对于 ${CMAKE_INSTALL_PREFIX} 变量表示的路径,默认为 “/usr/local/”。

4. 安装运行脚本

INSTALL(PROGRAMS runhello.sh DESTINATION bin)
  • PROGRAMS:代表非目标文件的可执行程序安装(比如脚本之类),后面跟上具体文件名;
  • DESTINATION:表示安装的目标路径,后面跟上路径名(相对路径或绝对路径)
    • 相对路径:它是相对于 ${CMAKE_INSTALL_PREFIX} 变量表示的路径,默认为 “/usr/local/”。

5. 安装工程文档

INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)

DIRECTORY是用于普通文档吗?

【注】abc 和 abc/有很大的区别

  • 目录名 不以/ 结尾:这个整个目录将被安装为目标路径下的(就是说将这个目录复制到目标路径);

  • 目录名 以/ 结尾:将这个目录中的内容安装到目标路径;

6. CMakeLists文件

只需要修改工程根目录下的 CMakeLists 文件就行了。

PROJECT (HELLO)
ADD_SUBDIRECTORY (src bin)	# 指定源码文件夹和输出二进制文件夹,注意bin是在build目录下生成的
# 以下是添加到内容
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)	# 安装COPYRIGHT和README
INSTALL(PROGRAMS runhello.sh DESTINATION bin)					# 安装运行脚本
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)				# 安装工程文档

7. 安装过程

  1. 转到 build 目录;
  2. 执行 cmake .. ,生成 Makefile 文件;
  3. 执行 make 命令;
  4. 执行 make install(普通用户需要加上sudo);
snail@chasemeng:~/work/CMake/build$ sudo make install
[100%] Built target hello
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/share/doc/cmake/COPYRIGHT
-- Installing: /usr/local/share/doc/cmake/README
-- Installing: /usr/local/bin/runhello.sh
-- Installing: /usr/local/share/doc/cmake
-- Installing: /usr/local/share/doc/cmake/tutorial.txt

五. 库文件

1. 静态库和动态库

  • 静态库的扩展名一般为“.a”或“.lib”,动态库的扩展名一般为“.so”或“.dll”。
  • 静态库在编译时会直接整合到目标程序中,编译成功的可执行文件可独立运行
  • 动态库在编译时不会放到连接的目标程序中,即可执行文件无法单独运行。

2. 构建静态库/动态库

2.1 项目目录结构

.
├── build
├── CMakeLists.txt
└── lib
    ├── CMakeLists.txt
    ├── hello.cpp
    └── hello.h

2.2 源码文件

存放在lib目录下,包含hello.h和hello.cpp两个文件。

hello.h头文件:动态库和静态库是其实现

#ifndef HELLO_H
#define Hello_H

void HelloFunc();

#endif

hello.cpp文件:制作成动态库和静态库

#include "hello.h"
#include <iostream>
void HelloFunc(){
    std::cout << "Hello World" << std::endl;
}

2.3 CMakeLists文件

根目录下的 CMakeLists.txt 文件

PROJECT(HELLO)
ADD_SUBDIRECTORY(lib bin)

源码目录下的 CMakeLists.txt 文件

SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})

生成步骤和上面编译步骤一样,这里不再赘述...

执行完之后会在 ./build/bin 文件夹中生成动态库文件 "libhello.so"。

3. 同时构建同名静态库和动态库

虽然静态库和动态库的后缀名不一样,但是只要它们的名字一样,就无法同时构建:

# 如果用这种方式,只会构建一个动态库,不会构建出静态库,虽然静态库的后缀是.a
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})

# 修改静态库的名字,这样是可以的,但是我们往往希望他们的名字是相同的,只是后缀不同而已
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

使用 SET_TARGET_PROPERTIES 可以解决该问题

在第2节的基础上,修改文件 源码目录下的 CMakeLists.txt 文件我不是很理解,以后再补充吧

SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

# 对hello_static的重名为hello
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
# cmake 在构建一个新的target 时,会尝试清理掉其他使用这个名字的库,因为,在构建 libhello.so 时, 就会清理掉 libhello.a
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello PROPERTIES  OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)

输出

snail@chasemeng:~/work/CMake/build$ make 
Scanning dependencies of target hello
[ 25%] Building CXX object bin/CMakeFiles/hello.dir/hello.o
[ 50%] Linking CXX shared library libhello.so
[ 50%] Built target hello
Scanning dependencies of target hello_static
[ 75%] Building CXX object bin/CMakeFiles/hello_static.dir/hello.o
[100%] Linking CXX static library libhello.a
[100%] Built target hello_static

指定版本(9)CMake入门笔记--同时生成动态库与静态库-蒲公英云 (dandelioncloud.cn)

4. 安装

安装之后,其他项目就可以该头文件,使用这些库文件了。但是需要在使用的项目中的 CMakeLists 文件中,使用 INCLUDE_DIRECTORY 添加这些库文件的头文件的路径;

# 安装头文件(下面不作解释)
INSTALL(FILES hello.h DESTINATION include/hello)
# 安装库文件
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
  • TARGETS:安装类型关键字?二进制、动态库,静态库都用 TARGETS
  • LIBRARY:代表动态库;
  • ARCHIVE:代表静态库;
  • DISTINATION:安装的目标路径,上面使用的是相对路径(相对于 ${CMAKE_INSTALL_PREFIX} 变量表示的路径,默认为 “/usr/local/”);

(安装步骤,略...)

【注】安装的时候,可以指定一下相对路径: cmake -D CMAKE_INSTALL_PREFIX=/usr ..,或者直接在项目根目录CMakeLists 文件中用SET指定该变量的值;

snail@chasemeng:~/work/CMake/build$ sudo make install
[ 50%] Built target hello
[100%] Built target hello_static
Install the project...
-- Install configuration: ""
-- Installing: /usr/include/hello/hello.h
-- Installing: /usr/lib/libhello.so
-- Installing: /usr/lib/libhello.a

5. CMakeList文件

根目录下的 CMakeLists.txt 文件

PROJECT(HELLO)
SET(CMAKE_INSTALL_PREFIX /usr)
MESSAGE(STATUS "根目录:安装的相对路径根目录是 " ${CMAKE_INSTALL_PREFIX})
ADD_SUBDIRECTORY(lib bin)

源码目录下的 CMakeLists.txt 文件

SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

# 更改安装的相对路径根目录
SET(CMAKE_INSTALL_PREFIX /usr)
MESSAGE(STATUS "源码:安装的相对路径根目录是 " ${CMAKE_INSTALL_PREFIX})

# 对hello_static的重名为hello
SET_TARGET_PROPERTIES(hello_static PROPERTIES  OUTPUT_NAME "hello")
# cmake 在构建一个新的target 时,会尝试清理掉其他使用这个名字的库,因为,在构建 libhello.so 时, 就会清理掉 libhello.a
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello PROPERTIES  OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)

# 安装头文件(下面不作解释)
INSTALL(FILES hello.h DESTINATION include/hello)
# 安装库文件
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)

六. 使用外部共享库

1. 项目目录结构

.
├── build
├── CMakeLists.txt
└── src
    ├── CMakeLists.txt
    └── main.cpp

2. 源码文件

此代码想要调用第五章中的头文件和库文件。

main.cpp

// 这个头文件是第五章安装好的
#include <hello.h>
int main() {
    HelloFunc();
}

3. CMakeList文件

根目录下的 CMakeLists.txt 文件

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)

源码目录下的 CMakeLists.txt 文件

# 添加头文件搜索路径
INCLUDE_DIRECTORIES(/usr/include/hello)
# 生成可执行文件(需要放在后面)
ADD_EXECUTABLE(hello main.cpp)

# 添加需要链接的共享库:需要放在ADD_EXECUTABLE后面
# 可执行文件hello链接动态库libhello.so
# 由于第五章中我们将库文件安装在/usr/lib,这个路径默认会被搜索到(32位机器),但是如果是64位机器,默认被搜索到路径是/usr/lib64,而不是/usr/lib,因此,如果机器是64位,就需要将库文件从/usr/lib移动到/usr/lib64,才能被正确链接,否则编译不会报错,但是执行生成的可执行文件(程序)会报错(但是我这里是没问题的,也不知道为啥)
TARGET_LINK_LIBRARIES(hello libhello.so)

# 或者也可以用此方法链接共享库(但是我这里执行不成功,不知道为啥)
# LINK_DIRECTORIES(/usr/lib)

补充

  • 特殊的环境变量 CMAKE_INCLUDE_PATHCMAKE_LIBRARY_PATH:这两个是系统环境变量而不是 cmake 变量,可以在linux的bash中进行设置。我们上面例子中使用了绝对路径INCLUDE_DIRECTORIES(/usr/include/hello) 来指明include路径的位置。我们还可以使用另外一种方式,使用环境变量 export CMAKE_INCLUDE_PATH=/usr/include/hello

  • 生成可调试的 debug版本 的方法:cmake .. -D CMAKE_BUILD_TYPE=debug

标签:CMakeLists,CMake,笔记,学习,ADD,usr,cpp,txt,hello
From: https://www.cnblogs.com/chasemeng/p/17438474.html

相关文章

  • Rust学习笔记——基础篇3:数据类型
    数据类型整数类型位长度有符号无符号8-biti8u816-biti16u1632-biti32u3264-biti64u64128-biti128u128archisizeusize整数型的表述方式进制例十进制98_222十六进制0xff八进制0o77二进制0b1111_0000字节(只能......
  • CAN笔记
    一、为什么需要总线1、人类需要交换信息的时候可以通过语言、文字,机器、电器设备之间需要交流该如何呢?是的需要一门他们能够读懂的语言,那就是通信协议,这也是在最早的汽车上都是使用了大量的线束,后来慢慢的通过各类的总线进行信息的交换。2、人类的交流手段:文字、语言、动作->......
  • 用CRU给笔记本显示器超频刷新率
    https://www.monitortests.com/forum/Thread-Custom-Resolution-Utility-CRU1、下载下来解压后得到4个文件,双击CRU 2、点击Add3、在RefreshRate后面改超频刷新率,然后点OK 4、回到4个文件那里,双击restart64(你是32位系统就双击restart)5、等电脑闪屏后,重启电脑。(不重启......
  • HCIP学习笔记-云安全服务规划-6
    1.云上安全设计以及华为云安全体系1.1为什么要关注云上安全CSA:CloudSecurityAlliance,云安全联盟1.2云上企业安全诉求1.3五大安全维度应对云上安全诉求1.4华为云安全服务全景2.工作负载安全2.1企业主机安全HSS管理控制台是可视化的管理平台,便于用户集中下发配置信息,查看在同......
  • babylon.js 学习笔记(6)
    接上回继续,今天继续捣腾动画,上一节咱们让汽车的轮子动了起来,回顾一下核心代码://轮子转动constwheelAnimation=(scene,wheels)=>{//定义一个动画,每秒30帧,绕y轴转动constanimWheel=newBABYLON.Animation("wheelAnimation","rotation.y",30,BABYLO......
  • CMake Commands
    cmake_minimum_requiredcmake_minimum_required(VERSION<min>[...<policy_max>][FATAL_ERROR])#限制CMake的版本支持范围cmake_minimum_required(VERSION3.16.3)#限制CMake最低版本3.16.3cmake_minimum_required(VERSION3.16.3...3.20.0)#限制CMake版本最低3.16.3,只要C......
  • 算法学习day28回溯part04-93、78、90
    packageLeetCode.backtrackpart04;importjava.util.ArrayList;importjava.util.List;/***93.复原IP地址*有效IP地址正好由四个整数(每个整数位于0到255之间组成,且不能含有前导0),整数之间用'.'分隔。*例如:"0.1.2.201"和"192.168.1.1"是有效IP地......
  • CMake Variables
    变量说明CMAKE_SOURCE_DIR源代码所在目录就是根cmakelists.txt所在目录CMAKE_BINARY_DIR跑cmake命令的目录CMAKE_BUILD_TYPE构建类型DebugReleaseRelWithDebInfoMinSizeRelCMAKE_MODULE_PATHcmake的模块路径以;分隔RUNTIME_OUTPUT_DIRECTORYCMAKE_C_......
  • httprunner4.x学习6 - 两种方式处理接口关联
    第一种方式:使用export导出变量,变成全局变量当登录用例写完后,后面想继续写其他用例,可以导入前面的login用例,当成下个用例的步骤使用导入前一个用例之前,需先export导出变量,变成全局变量。登陆用例:创建文件夹login,在文件夹下分别创建两个文件login.yml和useinfo.ymllogin.yml......
  • DRF学习第三课
    Restful接口标准:1,请求方式:获取GET,保存POST,更新PUT,删除DELETE2,请求路径:1,资源的名词复数作为路径 2,单一资源操作如:books/1/3,请求参数:1,根据模型确定传递的字段-保存或更新--JSON 2,过滤信息--查询字符串形式传递4,返回结果:1,错误{error:错误信息}2,正确 根据请求方式返回......