CMakeLists 编写记录, 一些基本的CMakeLists编写方式

427 阅读5分钟

如果多人开发要编写出一个共享库或是可执行文件就可以使用CMake来完成

CMake编写Hello World

编写一个cpp文件

这里随手写个Hello world就好了

#include <iostream>

int main(){
  std::cout <<  "hello word" << std::endl;
}

编写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})

编译文件

cmake .
# 这个命令表示编译当前文件夹下的CMakeLists.txt文件

make

再使用make命令就可以发现在当前目录下生成了可执行文件

CMake Hello world语法介绍

PROJECT关键字

可以用来指定工程的名字和支持的语言,默认支持所有语言

PROJECT (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是一致的。所以改了工程名也没有关系

SET关键字

用来显示的指定变量的

SET(SRC_LIST main.cpp) SRC_LIST变量就包含了main.cpp

也可以 SET(SRC_LIST main.cpp t1.cpp t2.cpp)

MESSAGE关键字

向终端输出用户自定义的信息

主要包含三种信息:

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

这些信息前面会多上一个—-

ADD_EXECUTABLE关键字

生成可执行文件

ADD_EXECUTABLE(hello ${SRC_LIST}) 生成的可执行文件名是hello,源文件读取变量SRC_LIST中的内容

也可以直接写 ADD_EXECUTABLE(hello main.cpp)

  • 也就相当于是生成的文件名是读取了我们写在set里面的文件名

上述例子可以简化的写成

PROJECT(HELLO) ADD_EXECUTABLE(hello main.cpp)

注意:工程名的 HELLO 和生成的可执行文件 hello 是没有任何关系的

语法基本规则

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

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

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

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

注意

就是在set里面我们的cpp文件如果带有空格就需要使用""扣起来

内部构建和外部构建

上面的hello例子就是内部构建, 会产生大量的临时文件

  • 不方便清理, 大多还是使用外部构建

外部构建法

在cmake文件夹里面创建build文件夹并进入

然后运行下面代码

cmake ..

其实就是camke上一级目录里面的CMakeLists.txt文件, 最后的结果就是将生成的文件丢到了build里面

使用make指令之后的可执行文件也是生成在这里的

编写一个hello工程(工程框架)

先建立一个src文件夹, 这里是用来存放我们的main文件的

编写主文件夹里面的CMakeLists.txt文件

这个文件其实就是一个指定我们要编译的文件的作用

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
# 这个命令是用于向当前工程添加存放源文件的子目录, 后面的bin是指定的中间二进制和目标二进制存放的位置

编写src里面的CMakeLists.txt文件

和正常的main编译文件差不多

ADD_EXECUTABLE(hello main.cpp)

看看现在的目录结构

编译

还是在build里面使用这个命令来编译

cmake ..
make

不过这次不同的地方是生成的可执行文件是在bin目录下了

ADD_SUBDIRECTORY 指令

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • 这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置

  • EXCLUDE_FROM_ALL函数是将写的目录从编译中排除,如程序中的example

  • ADD_SUBDIRECTORY(src bin)

    将 src 子目录加入工程并指定编译输出(包含编译中间结果)路径为bin 目录

    如果不进行 bin 目录的指定,那么编译结果(包括中间结果)都将存放在build/src 目录

更改二进制的保存路径

SET 指令重新定义 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH 变量 来指定最终的目标二进制的位置

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

思考:加载哪个CMakeLists.txt当中

哪里要改变目标存放路径,就在哪里加入上述的定义,所以应该在src下的CMakeLists.txt下写

安装

这里涉及CMake的INSTALL指令

  • 安装包括 : 二进制、动态库、静态库以及文件、目录、脚本等

使用CMAKE一个新的变量:CMAKE_INSTALL_PREFIX

  • 这里是少了个脚本文件的
[root@Lin cmake01]# tree
.
├── build
├── CMakeLists.txt
├── COPYRIGHT
├── doc
│   └── hello.txt
├── README.md
├── runhello.sh
└── src
    ├── CMakeLists.txt
    └── main.cpp

主目录下的CMake文件

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
# 这个命令是用于向当前工程添加存放源文件的子目录, 后面的bin是指定的中间二进制和目标二进>制存放的位置
INSTALL(FILES COPYRIGHT README.md DESTINATION share/doc/cmake/)
# 这里使用的是相对路径, 使用这个相对路径最后文件会被安装到/usr/local/share/doc/cmake/下
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
# 这里实际的安装路径是/usr/bin下, 这一步是安装脚本的意思
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)

这里可以发现就是使用了很多的下载

安装文件COPYRIGHT和README.md

INSTALL(FILES COPYRIGHT README.md DESTINATION share/doc/cmake/)

FILES:文件

DESTINATION:

1、写绝对路径

2、可以写相对路径,相对路径实际路径是:${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>

CMAKE_INSTALL_PREFIX 默认是在 /usr/local/

cmake -DCMAKE_INSTALL_PREFIX=/usr 在cmake的时候指定CMAKE_INSTALL_PREFIX变量的路径

安装脚本runhello.sh

INSTALL(PROGRAMS runhello.sh DESTINATION bin)

PROGRAMS:非目标文件的可执行程序安装(比如脚本之类)

INSTALL(PROGRAMS runhello.sh DESTINATION bin)

说明:实际安装到的是 /usr/bin

安装 doc 中的 hello.txt

INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
  • 一、是通过在 doc 目录建立CMakeLists.txt ,通过install下的file

  • 二、是直接在工程目录通过

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

DIRECTORY 后面连接的是所在 Source 目录的相对路径

注意:abc 和 abc/有很大的区别

目录名不以/结尾:这个目录将被安装为目标路径下的

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

安装过程

还是在build目录下的cmake + make

不过多了个make install命令来安装

[root@Lin build]# make install
[100%] Built target hello
Install the project...
-- Install configuration: ""
-- Up-to-date: /usr/local/share/doc/cmake/COPYRIGHT
-- Installing: /usr/local/share/doc/cmake/README.md
-- Installing: /usr/local/bin/runhello.sh
-- Up-to-date: /usr/local/share/doc/cmake
-- Installing: /usr/local/share/doc/cmake/hello.txt

静态库和动态库的构建/CMake构建共享库

静态库和动态库的区别

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

页面树

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

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;
}

项目中的cmake内容

PROJECT(HELLO)
ADD_SUBDIRECTORY(lib bin)

lib中CMakeLists.txt中的内容

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

ADD_LIBRARY

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
  • hello:就是正常的库名,生成的名字前面会加上lib,最终产生的文件是libhello.so
  • SHARED就是动态库 STATIC就是静态库
  • ${LIBHELLO_SRC} :源文件

运行以下

  • 最后可以发现生成了对应的库文件

[root@Lin build]# cmake ..
-- The C compiler identification is GNU 8.5.0
-- The CXX compiler identification is GNU 8.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
CMake Warning (dev) in CMakeLists.txt:
  No cmake_minimum_required command is present.  A line of code such as

    cmake_minimum_required(VERSION 3.3)

  should be added at the top of the file.  The version specified may be lower
  if you wish to support older CMake versions for this project.  For more
  information run "cmake --help-policy CMP0000".
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Configuring done
-- Generating done
-- Build files have been written to: /home/File/cppFile/cmake02/build
[root@Lin build]# make
Scanning dependencies of target hello
[ 50%] Building CXX object bin/CMakeFiles/hello.dir/hello.o
[100%] Linking CXX shared library libhello.so
[100%] Built target hello

同时生成静态和动态库

  • 我们无法用两个ADD_LIBRARY来生成动态库和静态库, 只会生成上面那个

SET_TARGET_PROPERTIES

这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本

同时构建静态和动态库

这里的指令是写在lib下的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)

指定动态库的版本号

一般动态库都有一个版本号的关联

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

CMakeLists.txt 插入如下, 注意是在lib下的CMakeLists.txt

SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

VERSION 指代动态库版本,SOVERSION 指代 API 版本

安装的效果

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
[root@Lin build]# 

安装共享库和头文件

在lib的txt文件下添加这个

# 文件放到该目录下
INSTALL(FILES hello.h DESTINATION include/hello)

# 二进制,静态库,动态库安装都用TARGETS
# ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执行目标二进制。
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
  • 注意这里cmake的时候用的是别的命令
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
[root@Lin build]# cmake -DCMAKE_INSTALL_PREFIX=/usr ..
-- The C compiler identification is GNU 8.5.0
-- The CXX compiler identification is GNU 8.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
CMake Warning (dev) in CMakeLists.txt:
  No cmake_minimum_required command is present.  A line of code such as

    cmake_minimum_required(VERSION 3.3)

  should be added at the top of the file.  The version specified may be lower
  if you wish to support older CMake versions for this project.  For more
  information run "cmake --help-policy CMP0000".
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Configuring done
-- Generating done
-- Build files have been written to: /home/File/cppFile/cmake02/build
[root@Lin 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
[root@Lin build]# 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

这样我们就生成了两个共享库了

使用共享库

就直接导包使用即可

make后头文件找不到的问题

PS:include <hello/hello.h> 这样include是可以,这么做的话,就没啥好讲的了

关键字:INCLUDE_DIRECTORIES 这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割

在CMakeLists.txt中加入头文件搜索路径

INCLUDE_DIRECTORIES(/usr/include/hello)

解决:找到引用的函数问题

报错信息:undefined reference to `HelloFunc()'

关键字:LINK_DIRECTORIES 添加非标准的共享库搜索路径

指定第三方库所在路径,LINK_DIRECTORIES(/home/myproject/libs)

关键字:TARGET_LINK_LIBRARIES 添加需要链接的共享库

TARGET_LINK_LIBRARIES的时候,只需要给出动态链接库的名字就行了。

在CMakeLists.txt中插入链接共享库,主要要插在executable的后面

查看main的链接情况

[root@MiWiFi-R4CM-srv bin]# ldd main 
  linux-vdso.so.1 =>  (0x00007ffedfda4000)
  libhello.so => /lib64/libhello.so (0x00007f41c0d8f000)
  libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f41c0874000)
  libm.so.6 => /lib64/libm.so.6 (0x00007f41c0572000)
  libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f41c035c000)
  libc.so.6 => /lib64/libc.so.6 (0x00007f41bff8e000)
  /lib64/ld-linux-x86-64.so.2 (0x00007f41c0b7c000)

链接静态库

TARGET_LINK_LIBRARIES(main libhello.a)

特殊的环境变量 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH

注意:这两个是环境变量而不是 cmake 变量,可以在linux的bash中进行设置

我们上面例子中使用了绝对路径INCLUDE_DIRECTORIES(/usr/include/hello)来指明include路径的位置

我们还可以使用另外一种方式,使用环境变量export CMAKE_INCLUDE_PATH=/usr/include/hello

补充:生产debug版本的方法: cmake .. -DCMAKE_BUILD_TYPE=debug