CMake官方教程练习记录

官方教程地址
官方代码地址


1. 原本的编译

1.1 工程的编译方式

文件目录

[root@VM-4-6-centos staticLib]# tree .
.
|-- include
|   `-- swap.h
|-- main
|-- main.cpp
`-- src
    `-- swap.cpp

2 directories, 4 files

swap.cpp

void swap(int *a, int *b) {
    int t = 0;
    t = *a; *a = *b; *b = t;
}

swap.h

void swap(int *a, int *b);

main.cpp

#include 
#include 
#include "swap.h"

int main() {
    int a = 1, b = 2;
    std::cout << a << " " << b << std::endl;
    swap(&a,&b);
    std::cout << a << " " << b << std::endl;
}

编译指令

$ g++ main.cpp src/swap.cpp -o main -Iinclude

1.2 链接静态库生成可执行文件

.
|-- include
|   `-- swap.h
|-- main
|-- main.cpp
|-- src
|   |-- libSwap.a
|   |-- swap.cpp
|   `-- swap.o
`-- staticmain

2 directories, 7 files
$ cd src/
$ g++ swap.cpp -c -I../include
$ ar rs libSwap.a swap.o
ar: creating libSwap.a
$ cd ..
$ g++ main.cpp -Iinclude -Lsrc -lSwap -o staticmain
$ ./staticmain 
1 2
2 1
  • -I:说明头文件在哪里
  • -L:说明链接库的位置
  • -l:需要链接的文件名称
  • -o: 生成可执行文件的名称

步骤

  1. 生成*.o目标文件,$ g++ swap.cpp -c -I../include
  2. ar 指令意为, ar - create, modify, and extract from archives归档,可以通过man ar来查看它后面可以跟的指令,ar rs lib*.a *.o 意思是把目标文件归档到静态库中。
  3. g++ main.cpp -Iinclude -Lsrc -lSwap -o staticmain编译参数如上所示。ps:直接写上静态库的也可以编译,如g++ main.cpp -Iinclude src/libSwap.a

还可以看到静态库里面有什么目标文件

[root@VM-4-6-centos src]# ar t libSwap.a 
swap.o

1.3 链接动态库生成可执行文件

[root@VM-4-6-centos src]$ g++ swap.cpp -I../include -fPIC -shared -o libSwap.so
[root@VM-4-6-centos src]$ ls
libSwap.a  libSwap.so  swap.cpp  swap.o
# 等价于
# g++ swap.cpp -I../include -c -fPIC
# g++ -shared -o libSwap.so swap.o

链接写法同静态库

$ g++ main.cpp -Iinclude -Lsrc -lSwap -o dynamicmain
$ g++ main.cpp -Iinclude src/libSwap.so 

静态库和动态库都存在时,优先使用动态库(这里是用这种-L -l的方式),如果是直接将库写在后面则是看哪个库靠前。

不明白的问题

## 这里不可以直接执行
[root@VM-4-6-centos staticLib]# ./dynamicmain 
./dynamicmain: error while loading shared libraries: libSwap.so: cannot open shared object file: No such file or directory

## a.out 和 dynamicmain 大小一样
[root@VM-4-6-centos staticLib]# ll
total 48
-rwxr-xr-x 1 root root 9064 Dec  6 23:04 a.out
-rwxr-xr-x 1 root root 9064 Dec  6 23:08 dynamicmain
drwxr-xr-x 2 root root 4096 Dec  6 20:56 include
-rwxr-xr-x 1 root root 9096 Dec  6 21:06 main
-rw-r--r-- 1 root root  200 Dec  6 21:06 main.cpp
drwxr-xr-x 2 root root 4096 Dec  6 22:59 src
[root@VM-4-6-centos staticLib]#  g++ main.cpp -Iinclude src/libSwap.so 
[root@VM-4-6-centos staticLib]# ./a.out 
1 2
2 1
## 为什么这里可以直接执行


## PAth字母打错了
[root@VM-4-6-centos staticLib]# LD_LIBRARY_PAth=src ./dynamicmain 
./dynamicmain: error while loading shared libraries: libSwap.so: cannot open shared object file: No such file or directory
[root@VM-4-6-centos staticLib]# tree .
.
|-- a.out
|-- dynamicmain
|-- include
|   `-- swap.h
|-- main
|-- main.cpp
`-- src
    |-- libSwap.a
    |-- libSwap.so
    |-- swap.cpp
    `-- swap.o

2 directories, 9 files
[root@VM-4-6-centos staticLib]# LD_LIBRARY_PATH=src ./dynamicmain 
1 2
2 1

2. 使用CMake

STEP 1

首先是最基础的三个参数

必须首先使用指定最低 CMake 版本cmake_minimum_required()命令,我们使用project()命令设置项目名称,add_executable()命令告诉 CMake 使用指定的源代码文件创建可执行文件。set()函数,给变量赋值。

# TODO 1: Set the minimum required version of CMake to be 3.10
cmake_minimum_required(VERSION 3.10)
# TODO 2: Create a project named Tutorial
project(Tutorial VERSION 1.7)
# TODO 7: Set the project version number as 1.0 in the above project command
# TODO 7 project的第二个参数会为项目设置版本号 在“1.7”中 1 代表 Tutorial_VERSION_MAJOR
#													7 代表 Tutorial_VERSION_MINOR
#													用于我们在h.in文件中替换配置使用
# TODO 6: Set the variable CMAKE_CXX_STANDARD to 11
#         and the variable CMAKE_CXX_STANDARD_REQUIRED to True
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# TODO 8: Use configure_file to configure and copy TutorialConfig.h.in to
#         TutorialConfig.h
configure_file(TutorialConfig.h.in TutorialConfig.h)
# TODO 3: Add an executable called Tutorial to the project
# Hint: Be sure to specify the source file as tutorial.cxx
add_executable(Tutorial tutorial.cxx)
# TODO 9: Use target_include_directories to include ${PROJECT_BINARY_DIR}
# 添加头文件的位置,因为产生的 TutorialConfig.h 文件在这里
target_include_directories(Tutorial PUBLIC
                            "${PROJECT_BINARY_DIR}"
                            )

# 关于这两个路径是什么,可以使用如下命令打印出来
message(STATUS "PROJECT_BINARY_DIR: ${PROJECT_BINARY_DIR}")
message(STATUS "PROJECT_SOURCE_DIR: ${PROJECT_SOURCE_DIR}")
# 结果是
# -- PROJECT_BINARY_DIR: /home/maheWork/learnCmake/cmake-3.25.1-tutorial-source/Step1_build
# -- PROJECT_SOURCE_DIR: /home/maheWork/learnCmake/cmake-3.25.1-tutorial-source/Step1
# 实践发现 这两个参数指的是 顶层CMakelists的绝对地址和产生二进制文件的目录的地址

configure_file([input][output]),根据输入文件的替换规则,会读取编译时的一些信息替换给自己设置的宏定义,结合上面注释中的TODO 7和以下文件信息能够理解。

TutorialConfig.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@

TutorialConfig.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 7

STEP 2

学习如何创建库、添加库

  1. 首先是目录结构
.
|-- CMakeLists.txt
|-- MathFunctions
|   |-- CMakeLists.txt
|   |-- MathFunctions.h
|   `-- mysqrt.cxx
|-- TutorialConfig.h.in
`-- tutorial.cxx
  1. 生成库文件: add_library( )

    MathFunctions/CMakeLists.txt将cpp实现文件添加入库

  2. 添加子目录: add_subdirectory( )、target_link_libraries( )、target_include_directories( )

既然要用其他目录下的文件,就要把该目录添加入工程,将可执行文件需要链接的库写出,需要的头文件写出。

根据编译条件自动添加库

cmake_minimum_required(VERSION 3.10)
project(Tutorial VERSION 1.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# configure_file(TutorialConfig.h.in TutorialConfig.h)
# 之前的话 USE_MYMATH 获取不到默认值 ON
# OPTION 中只有 ON 和 OFF
option(USE_MYMATH "Use tutorial provided math implementation" ON)

configure_file(TutorialConfig.h.in TutorialConfig.h)

# 条件判断是否要加入库,list中除了APPEND以外还有很多操作
if(USE_MYMATH)
    add_subdirectory(MathFunctions)
    list(APPEND EXTRA_LIBS MathFunctions)
    list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()

add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           ${EXTRA_INCLUDES}
                           )

#cmakedefine 用于h.in文件中,只有当CMakeLists.txt中的同名变量为真时才会在生成的头文件中定义,区别于#define无论何时都会定义。这个定义是为了编译时 h.in生成的h文件中带有定义,使得cpp中可以判断导入哪个头文件、使用哪些函数等。

额外问题

如果Configure文件配置语句在OPTION之前会有什么问题?

那么配置文件的部分值就只能通过自己的输入,获取不了option的默认值。

STEP 3

为库添加使用要求

  • target_compile_definitions()
  • target_compile_options()
  • target_include_directories()
  • target_link_directories()
  • target_link_options()
  • target_precompile_headers()
  • target_sources()

这一节让顶层list文件可以不用再包含子目录的头文件。

MathFunctions/CMakelists.txt

add_library(MathFunctions mysqrt.cxx)

# TODO 1: State that anybody linking to MathFunctions needs to include the
# current source directory, while MathFunctions itself doesn't.
# Hint: Use target_include_directories with the INTERFACE keyword
target_include_directories(MathFunctions
                            INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

message(STATUS "current source dir ${CMAKE_CURRENT_SOURCE_DIR}")
message(STATUS "current bin dir ${CMAKE_CURRENT_BINARY_DIR}")
message(STATUS "source dir ${CMAKE_SOURCE_DIR}")
message(STATUS "bin dir ${CMAKE_BINARY_DIR}")

可以看一下这些参数都代表什么位置

-- current source dir /home/maheWork/learnCmake/cmake-3.25.1-tutorial-source/Step3/MathFunctions
-- current bin dir /home/maheWork/learnCmake/cmake-3.25.1-tutorial-source/Step3_build/MathFunctions
-- source dir /home/maheWork/learnCmake/cmake-3.25.1-tutorial-source/Step3
-- bin dir /home/maheWork/learnCmake/cmake-3.25.1-tutorial-source/Step3_build

CURRENT 代表当前目录。

INTERFACE 的官网解释是 Remember INTERFACE means things that consumers require but the producer doesn’t,意味自己不需要这个头文件但是如果其他其他调用需要这个头文件。

STEP 4

使用接口库设置 C++ 标准,使用生成器表达式添加编译器警告标志

target_compile_features

  1. 先创建一个接口库用来设定信息
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
# 这样的话就可以把后两句注释掉了
# set(CMAKE_CXX_STANDARD 11)
# set(CMAKE_CXX_STANDARD_REQUIRED True)

target_compile_features()用来设置编译器版本

  1. 设置不同编译器的报错信息
set(gcc_like_cxx "$")
set(msvc_cxx "$")

target_compile_options(tutorial_compiler_flags 
                        INTERFACE
                        "$<${gcc_like_cxx}:$>"
                        "$<${msvc_cxx}:$>"
                        )
  1. 记得给方法链接上接口库

STEP 5

安装和测试

安装

make完之后执行make install指令可以按照makefile的内容将可执行文件、头文件、库文件安装到某个地方,默认是usr/local为前缀,然后就可以直接在命令行通过文件名直接调用了

教程中出现三种情况:

install(TARGETS 库名 DESTINATION lib)
install(FILES (头文件名)MathFunctions.h DESTINATION include)
install(TARGETS (可执行文件名)Tutorial DESTINATION bin)

测试

make完之后执行make test指令

# Enable
enable_testing()
# 测试名称和参数,以及成功的条件
add_test(NAME Runs COMMAND Tutorial 25)
set_tests_properties(Usage
                      PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
                      )
# 用函数的形式
function(do_test target arg result)
  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
  set_tests_properties(Comp${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
    )
endfunction()

# 使用示例
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 5 "5 is 2.236")
do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is (-nan|nan|0)")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
# 以上都是写在CMakelists中的

你可能感兴趣的:(C++,后端,c++)