cmake 学习记录

本文讲述了作者学习CMake的经历,介绍了基本命令如cmake和makefile的配合,以及如何通过CMakeLists.txt配置项目,包括添加执行文件、链接头文件和理解不同编译选项的区别。作者还分享了在处理头文件路径和C++标准设置上的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景:一直他妈的不会cmake,然后很多时候建立新环境运行不起来,很烦

终于有一小段时间,算是闲里偷忙来学一下

就是根据官方的文档来学的,它不是有很多的练习嘛,挨个做了一遍

各个地址:

cmake的总链接:https://cmake.org/cmake/help/latest/

练习题的内容:https://cmake.org/cmake/help/latest/guide/tutorial/index.html

开始吧:

1. 首先熟悉最基本的几个命令:

cmake <dir> 与 CMakeLists.txt配套,通过 cmake 命令将 CMakeLinks.txt翻译为能被make命令使用的makefile.txt文件

因此,cmake在执行命令的时候,需要 <dir> 为 包含CMakeLists.txt 的路径

make 与 makefile.txt文件配套,是用于编译链接各个.c .o .h 文件的

因此,make在执行命令的时候,需要与makefile.txt 在同一路径中

补充:

(1)cmake 也有通过makefile.txt编译链接的能力,执行 cmake --build <dir>  命令就可以,<dir>就是makefile.txt的路径

(2)cmake -S . -B ./build  指令
    -S 就是在指定 CMakeLists.txt文件和要build的源代码文件在哪里 
        【我猜测】:就是add_executable(Tutorial tutorial.cxx)中的 tutorial.cxx 文件的位置
    -B 的含义是指定将 buildtree 的路径放在哪里, 
        build tree 含义是:包括 CMakeCache.txt文件的位值,若 不使用 -B 特殊指定那么就会生成在当前pwd的目录下
        若 -B 后的路径不存在,那么会先新建路经然后再向该路径中存放对应得内容
        (若 build目录存在就直接使用build中的CMakeCache.txt文件链,因此,为了避免出错,建议直接 rm -rf build)

若在指令中不显示地指定 -S -B ,直接写为  `cmake ..` 
                           那么,这个命令的含义就是: source tree 的路径是 `..` 
                           因为,当 -S -B 全省略时,指令退化为 ‵cmake src‵

接着,可以使用 `make`命令 或 `cmake --build <dir>`命令 to build the project      
    若使用 `cmake --build <dir>` 命令,那么,‵--build‵ 命令必须放在参数的最开始


(3)可以并行编译:`cmake --build <dir> -j [<jobs>]`        
                    例如: `cmake --build . -j 4 `

当我看不懂一个东西的时候,我应该继续,还是应该速速看别人的博客来回理解呢?

我打算采取的策略是,看别人的博客吧

还有一个疑问是:一些关键字的含义都他妈的是看明白的阿,以及,别说是看明白了,我他嘛有时候都不知道这些关键字的存在,不是我靠,这些关键字都从哪里列出来的阿????

 引入指定的头文件:

使用 target_include_directories(Tutorial PUBLIC "<dir>") 函数 引入要链接的 .h文件

使用方法:

先add_executable(Tutorial tutorial.cxx)

再 target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")

注意 :

target_include_directories(<目标可执行文件名字> <PUBLIC | INTERFACE | PRIVATE> "<dir>")

这个用法:第三个参数可以有多种写法:

目前的感受是:第三个参数的内容应该是 包含.h文件所在的路径

由于我们在build文件夹下执行的各个命令,因此,生成的二进制文件的位置在不使用set(PROJECT_BINARY_DIR "<dir>") 指明的时候,就会存放在当前文件夹位置处

而可行的2个变量 ${PROJECT_BINARY_DIR}" ${CMAKE_CURRENT_BINARY_DIR}的含义分别是:该文件二进制内容所在路径 或 当前执行CMAKE后生成的二进制文件的位置,因此都是可行的

担心的话,可以使用 message("  ${XXXXXXXXXX}  ") 输出一下

存在几个问题:

1.在add_executable()函数上不能直接用嘛:

目前看来是可以的:下述代码被允许,看来可以用相对路径来制定资源

【目前的感受是,所有的 文件都可以改写为】

add_executable(Tutorial tutorial.cxx "../testPath/lalal.h" )

2. 和 add_dirctionary区别?

PUBLIC PRIVATE INTERFACE 之间的区别:cmake:target_** 中的 PUBLIC,PRIVATE,INTERFACE - 知乎 

Step 1 的 3 个实验:

我汇总在一起说了:

要完成的工作是:

build一个最简单的项目需要 源码,头文件,链接库 , 指导CMake进行编译链接的CMakeLists.txt等等

先说下我们的项目路径构成:(左图是build之前,右图是build之后)

其中,我们把头文件放在lib文件夹中,把源文件放到src文件夹中,新建build目录用于存放cmake make 指令生成的内容

注:在tutorial.cxx中用到了 lalal.h 这个头文件中涉及的变量

    

lalal.h内容  

#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 56

tutorial.cxx

// A simple program that computes the square root of a number
#include <cmath>
#include <iostream>
#include <string>
#include <lalal.h>


int main(int argc, char* argv[])
{
  if (argc < 2) {
    std::cout << "Usage: " << argv[0] << " number" << std::endl;
    // Create a print statement using Tutorial_VERSION_MAJOR
    // and Tutorial_VERSION_MINOR
    std::cout << argv[0] << "Version " << Tutorial_VERSION_MAJOR 
              << Tutorial_VERSION_MINOR << std::endl;
    return 1;
  }

  // convert input to double
  const double inputValue = atof(argv[1]);  // 这个语句使用了C++11语法,因此编译的
                                            // 时候需要指明

  // calculate square root
  const double outputValue = sqrt(inputValue);
  std::cout << "The square root of " << inputValue << " is " << outputValue
            << std::endl;
  return 0;
}

CMakeLists.txt 

# 对于cmake版本的指定必须放在第一行
cmake_minimum_required(VERSION 3.10)

# 通过project()指定该项目的名称和版本号 
#    而项目的名字具体是啥其实目前来看没啥重要的,就只是一个名字
#    "VERSION" 是个关键字,后接该project的版本号
project(Tutorial VERSION 1.56)

# 设置一下编译链接该项目的C++语法标准:
#    注意,设置编译要求需要在 add_executable()函数之前调用
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)


# 生成可执行文件 :add_executable(<target> <source 1> [source 2] [source 3] .. )
#    这里注意,既然是source文件(即 源文件),其实就是 以 .c .cpp .cxx 为结尾的文件
#    这些源文件中所链接的头文件是不用写明的,不过你非要写,也不报错
#    以.h 为结尾的文件叫头文件(header file)
add_executable(Tutorial ./src/tutorial.cxx)


# 由于我们使用了头文件,因此,需要手动指定 要链接的头文件的路径,
#    基于的路径就是CMakeLists.txt的位置
#    [注意] target_include_directories() 函数应该写在add_executable()函数之后
target_include_directories(Tutorial PUBLIC "./lib") 

 有几个容易迷惑的地方,我重点说一下:

(1)路径到底用不用加 " " 括起来 ?

[回答]:用 " " 括起来 或者 不括起来 在语法上都对,但是为了我的身心健康和强迫症,我建议自己括起来。

(2)在写相对路径时,表示当前位置的 ` . ` 到底需要不需要?

 [回答]:我的建议时应写尽写。因为我在调用 target_include_directories() 函数时,由于第三个参数 " ./lib " 写成了 " /lib " 就导致cmake找不到头文件lalal.h报错了,,,所以写上吧

(3)在CMakeLists.txt 中写的这些指令会涉及到一些文件的位置,要指定这些文件的路径,需要根据一个相对路径来指定。例如,要告知cmake 链接所需的 lalal.h 头文件 在哪里,那么,我需要给出 lalal.h 相对于当前位置的路径。那么,当前位置是谁的位置呢?

[回答]:当前位置是 CMakeLists.txt 文件所处的文件夹的位置

Step 1 的这三个练习有一些补充:

(1)指定编译时采用的C++标准时,可以使用上文 set  CMAKE_CXX_STANDARD 和CMAKE_CXX_STANDARD_REQUIRED 两个变量指定值的方法实现。

也可以采用下述方法,且这条语句放在 add_executable()函数后也是可以的

target_compile_features(Tutorial PUBLIC cxx_std_11)    

但是这种指定编译器编译语法规则在文档中不是很建议,那就算了,用 set 变量的方法指定吧

有个关于 target_include_dircetionaries() 的解释很好你看看:

cmake:target_** 中的 PUBLIC,PRIVATE,INTERFACE - 知乎

====================---------------------------------===================================

同时,通过 CMakeLists.txt 中使用 configure_file() 生成新的头文件,并在 CMakeLists.txt 中将生成的头文件链接到代码中,以此达成 根据 CmakeLists.txt 来修改源码的功能。比如,Step 1 的第三个实验就是要生成一个头文件 lalal.h 并且链接到源码中以修改项目的版本号:

具体实现如下:

项目结构层次:

将生成真正的lalal.h的配置文件 lalal.h.in文件放在config文件夹下面(由于此是真正用到的lalal.h文件还没被生成,因此,lib文件夹内容为空)。

同样,还是新建文件夹build,在build中执行cmake make 命令(执行前如左图,执行后如右图)

           

lalal.h.in 内容如下:

// the configured options and settings for Tutorial
// TODO 10: Define Tutorial_VERSION_MAJOR and Tutorial_VERSION_MINOR

#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

生成的lalal.h内容如下:

// the configured options and settings for Tutorial
// TODO 10: Define Tutorial_VERSION_MAJOR and Tutorial_VERSION_MINOR

#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 99

tutorial.cxx文件内容于上文的代码一致 :

// A simple program that computes the square root of a number
#include <cmath>
#include <iostream>
#include <string>
#include <lalal.h>


int main(int argc, char* argv[])
{
  if (argc < 2) {
    std::cout << "Usage: " << argv[0] << " number" << std::endl;
    // Create a print statement using Tutorial_VERSION_MAJOR
    // and Tutorial_VERSION_MINOR
    std::cout << argv[0] << "Version " << Tutorial_VERSION_MAJOR 
              << Tutorial_VERSION_MINOR << std::endl;
    return 1;
  }

  // convert input to double
  const double inputValue = atof(argv[1]);  // 这个语句使用了C++11语法,因此编译的
                                            // 时候需要指明

  // calculate square root
  const double outputValue = sqrt(inputValue);
  std::cout << "The square root of " << inputValue << " is " << outputValue
            << std::endl;
  return 0;
}

CMakeLists.txt 文件内容如下:

cmake_minimum_required(VERSION 3.10)

project(Tutorial VERSION 1.99)

message("@@@@@@@ VERSION :${Tutorial_VERSION_MAJOR}  ${Tutorial_VERSION_MINOR}")

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 输出内容是:the CMAKE_CURRENT_BINARY_DIR is /home/zch/Desktop/CMAKE_TRY/Note/build
message("the CMAKE_CURRENT_BINARY_DIR is ${CMAKE_CURRENT_BINARY_DIR}")

# 之所以输出 CMAKE_CURRENT_BINARY_DIR 是因为 
#   output 的相对路径是 相对于 CMAKE_CURRENT_BINARY_DIR 来说的
configure_file("./config/lalal.h.in" "../lib/lalal.h")  


add_executable(Tutorial ./src/tutorial.cxx )
target_include_directories(Tutorial PUBLIC "./lib" ) 

Step 2 的实验:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值