热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

CMake基础使用和实战详解

CMake基础使用和实战详解一、CMake简介1.1、cmake的特点1.2、注意1.3、使用建议二、安装cmake三、CMake的简单使用3.1、准备工作3.2、开始构建3.3、

CMake基础使用和实战详解


  • 一、CMake简介
    • 1.1、cmake 的特点
    • 1.2、注意
    • 1.3、使用建议

  • 二、安装 cmake
  • 三、CMake的简单使用
    • 3.1、准备工作
    • 3.2、开始构建
    • 3.3、解释CMakeLists.txt的内容
    • 3.4、基本语法规则

  • 四、更像样的CMake工程
    • 4.1、准备工作
    • 4.2、构建
    • 4.3、语法解释
    • 4.4、修改保存目标二进制的地方
    • 4.5、如何安装编译的软件
    • 4.6、修改CMakeLists.txt支持安装

  • 总结


一、CMake简介

CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。只是 CMake 的组态档取名为CMakeLists.txt。

Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再以一般的建构方式使用。可以通过CMake官方网站获得更多关于cmake 的信息。

可以理解为,编写Makefile难度太大,CMake基于Makefile做了二次开发。

1.1、cmake 的特点


  1. 开放源代码,使用类BSD 许可发布。
  2. 跨平台,并可生成native 编译配置文件,在Linux/Unix 平台,生成 makefile,在苹果平台,可以生成xcode,在 Windows 平台,可以生成 MSVC 的工程文件。
  3. 能够管理大型项目,KDE4 就是最好的证明。
  4. 简化编译构建过程和编译过程。Cmake 的工具链非常简单:cmake+make。
  5. 高效率,CMake 构建KDE4 的 kdelibs 要比使用autotools 来构建KDE3.5.6 的 kdelibs 快40%,主要是因为 Cmake 在工具链中没有libtool。
  6. 可扩展,可以为cmake 编写特定功能的模块,扩充cmake 功能。

1.2、注意


  1. cmake 很简单,但绝对没有想象中那么简单,简单是相对Makefile而言的。
  2. cmake 编写的过程实际上是编程的过程,跟使用autotools 一样,不过需要编写的是CMakeLists.txt(每个目录一个),使用的是cmake 语言和语法。
  3. cmake 跟已有体系的配合并不是特别理想,比如pkgconfig。

1.3、使用建议


  1. 如果工程只有几个文件,直接编写Makefile 是最好的选择。
  2. 如果使用的是C/C++/Java 之外的语言,建议不要使用cmake。
  3. 如果使用的语言有非常完备的构建体系,比如java 的 ant,也不需要学习cmake。
  4. 如果项目已经采用了非常完备的工程管理工具,并且不存在维护问题,没有必要迁移到 cmake。
  5. 如果仅仅使用qt 编程,没有必要使用 cmake,因为qmake 管理 Qt 工程的专业性和自动化程度比cmake 要高很多。

二、安装 cmake

我当前的CMake版本:

$ cmake --version
cmake version 3.5.1
CMake suite maintained and supported by Kitware (kitware.com/cmake).

有些项目有最低版本要求,如果版本过低,可能项目会编译不了,可以升级CMake。

(1)卸载已经安装的旧版的CMake[非必需]。

sudo apt-get autoremove cmake

(2)下载CMake压缩包。

wget https://cmake.org/files/v3.21/cmake-3.21.3-linux-x86_64.tar.gz

(3)解压压缩包。

tar zxvf cmake-3.21.3-linux-x86_64.tar.gz

查看解压后目录:

cmake-3.21.3-linux-x86_64
├── bin
│ ├── ccmake
│ ├── cmake
│ ├── cmake-gui
│ ├── cpack
│ └── ctest
├── doc
│ └── cmake
├── man
│ ├── man1
│ └── man7
└── share
├── aclocal
├── applications
├── bash-completion
├── cmake-3.21
├── emacs
├── icons
├── mime
└── vim
15 directories, 5 files

bin下面有各种cmake家族的产品程序。

(4)创建软链接。

注意,文件路径是可以指定的, 一般选择在/opt 或 /usr 路径下, 这里选择/opt 。

sudo mv cmake-3.21.3-Linux-x86_64 /opt/cmake-3.21.3
sudo ln -sf /opt/cmake-3.21.3/bin/* /usr/bin/

检查版本号:

$ cmake --version
cmake version 3.21.3
CMake suite maintained and supported by Kitware (kitware.com/cmake).

三、CMake的简单使用

3.1、准备工作

(1)首先,建立一个文件夹,用来存放工程文件。

cd cmake
mkdir t1
cd t1

(2)在 t1 目录建立main.c 和 CMakeLists.txt(注意文件名大小写):

main.c 文件内容:

//main.c
#include
int main()
{
printf(“Hello World from t1 Main!\n”);
return 0;
}

CMakeLists.txt 文件内容:

PROJECT(HELLO)
SET(SRC_LIST main.c)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})
ADD_EXECUTABLE(hello2 ${SRC_LIST})

关于CMakeLists.txt 文件内容的语法,后面会做详细说明。

3.2、开始构建

cmake .

注意命令后面的点号,代表本目录。

执行结果:

CMake Warning (dev) at CMakeLists.txt:4:
Syntax Warning in cmake code at column 37
Argument not separated from preceding token by whitespace.
This warning is for project developers. Use -Wno-dev to suppress it.
-- The C compiler identification is GNU 8.4.0
-- The CXX compiler identification is GNU 8.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- This is BINARY dir /home/fly/workspace/cmakeProj/t1
-- This is SOURCE dir /home/fly/workspace/cmakeProj/t1
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fly/workspace/cmakeProj/t1

系统自动生成了: CMakeFiles, CMakeCache.txt, cmake_install.cmake 等文件,并且生成了 Makefile。

不需要理会这些文件的作用,最关键的是,它自动生成了 Makefile。然后进行工程的实际构建,在这个目录输入 make 命令,大概会得到如下的输出:

[ 25%] Building C object CMakeFiles/hello2.dir/main.c.o
[ 50%] Linking C executable hello2
[ 50%] Built target hello2
[ 75%] Building C object CMakeFiles/hello.dir/main.c.o
[100%] Linking C executable hello
[100%] Built target hello

如果需要看到make 构建的详细过程,可以使用make VERBOSE=1 或者VERBOSE=1 make 命令来进行构建。

这时候,需要的目标文件hello 已经构建完成,位于当前目录,尝试运行一下:

$ ./hello
Hello World from t1 main

3.3、解释CMakeLists.txt的内容

CMakeLists.txt这个文件是 cmake 的构建定义文件,文件名是大小写相关的,如果工程存在多个目录,需要确保每个要管理的目录都存在一个CMakeLists.txt。这涉及到关于多目录构建,后面解释。

上面例子中的CMakeLists.txt 文件内容如下:

PROJECT(HELLO)
SET(SRC_LIST main.c)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})
ADD_EXECUTABLE(hello2 ${SRC_LIST})

(1)PROJECT指令。

语法是:

PROJECT(projectname [CXX] [C] [Java])

可以用这个指令定义工程名称,并可指定工程支持的语言,支持的语言列表是可以忽略的,默认情况表示支持所有语言。这个指令隐式的定义了两个 cmake 变量: _BINARY_DIR 以及_SOURCE_DIR,这里就是HELLO_BINARY_DIR 和HELLO_SOURCE_DIR(所以CMakeLists.txt 中两个MESSAGE指令可以直接使用了这两个变量),因为采用的是内部编译,两个变量目前指的都是工程所在路/t1;注意,在外部编译中,两者所指代的内容会有所不同。

同时cmake 系统也帮助我们预定义了 PROJECT_BINARY_DIR 和PROJECT_SOURCE_DIR变量,他们的值分别跟HELLO_BINARY_DIR 与HELLO_SOURCE_DIR 一致。

为了统一起见,建议以后直接使用PROJECT_BINARY_DIR,PROJECT_SOURCE_DIR,即使修改了工程名称,也不会影响这两个变量。如果使用了_SOURCE_DIR,修改工程名称后,需要同时修改这些变量。

(2)SET指令。

语法是:

SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

SET 指令可以用来显式的定义变量即可。比如用到的是SET(SRC_LIST main.c),如果有多个源文件,也可以定义成:SET(SRC_LIST main.c t1.c t2.c)

(3)MESSAGE指令。

语法是:

MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)

这个指令用于向终端输出用户定义的信息,包含了三种类型:

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

这里使用的是STATUS 信息输出,演示了由PROJECT 指令定义的两个隐式变量HELLO_BINARY_DIR 和HELLO_SOURCE_DIR。

(4)ADD_EXECUTABLE 指令。

ADD_EXECUTABLE(hello ${SRC_LIST})

定义了这个工程会生成一个文件名为hello 的可执行文件,相关的源文件是 SRC_LIST 中定义的源文件列表, 本例中也可以直接写成ADD_EXECUTABLE(hello main.c)。

在本例使用了$ {} 来引用变量,这是cmake 的变量应用方式,但是,有一些例外,比如在IF 控制语句,变量是直接使用变量名引用,而不需要$ {}。如果使用了$ {}去应用变量,其实IF 会去判断名为${}所代表的值的变量,那当然是不存在的了。

3.4、基本语法规则

cmake 其实要使用”cmake 语言和语法”去构建,上面的内容就是所谓的 ”cmake 语言和语法”,最简单的语法规则是:

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

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

ADD_EXECUTABLE(hello main.c func.c)
# 或者
ADD_EXECUTABLE(hello main.c;func.c)

(3)指令是大小写无关的,参数和变量是大小写相关的。但,推荐全部使用大写指令。上面的MESSAGE 指令我们已经用到了这条规则:

MESSAGE(STATUS "This is BINARY dir" ${HELLO_BINARY_DIR})
# 也可以写成:
MESSAGE(STATUS "This is BINARY dir ${HELLO_BINARY_DIR}")

这里需要特别解释的是作为工程名的HELLO 和生成的可执行文件 hello 是没有任何关系的。

(4)工程名和执行文件。hello 定义了可执行文件的文件名,也完全可以写成:ADD_EXECUTABLE(t1 main.c)编译后会生成一个t1 可执行文件。

(5)关于语法的疑惑:

cmake 的语法还是比较灵活而且考虑到各种情况,比如:

SET(SRC_LIST main.c)
# 也可以写成
SET(SRC_LIST "main.c")

是没有区别的,但是假设一个源文件的文件名是 fu nc.c(文件名中间包含了空格)。这时候就必须使用双引号,如果写成了 SET(SRC_LIST fu nc.c),就会出现错误,提示找不到fu 文件和nc.c 文件。这种情况,就必须写成:

SET(SRC_LIST "fu nc.c")

此外,可以忽略掉source 列表中的源文件后缀,比如可以写成:

ADD_EXECUTABLE(t1 main)

cmake 会自动的在本目录查找main.c 或者main.cpp等,当然,最好不要偷这个懒,以免这个目录确实存在一个 main.c和一个main。

同时参数也可以使用分号来进行分割。下面的例子也是合法的:

ADD_EXECUTABLE(t1 main.c t1.c)
# 可以写成
ADD_EXECUTABLE(t1 main.c;t1.c).

只需要在编写CMakeLists.txt 时注意形成统一的风格即可。

(6)清理工程。跟经典的autotools 系列工具一样,运行make clean即可对构建结果进行清理。

(7)cmake 并不支持 make distclean,关于这一点,官方是有明确解释的。因为CMakeLists.txt 可以执行脚本并通过脚本生成一些临时文件,但是却没有办法来跟踪这些临时文件到底是哪些。因此,没有办法提供一个可靠的 make distclean 方案。

(8)内部构建。刚才进行的是内部构建(in-source build),而 cmake 强烈推荐的是外部构建(out-of-source build)。内部构建生成的临时文件可能比您的代码文件还要多,而且它生成了一些无法自动删除的中间文件。

(9)外部构建。外部编译的过程如下:

  1. 首先,请清除t1 目录中除main.c CmakeLists.txt 之外的所有中间文件,最关键的是CMakeCache.txt。
  2. 其次,在 t1 目录中建立build 目录,当然你也可以在任何地方建立build 目录,不一定必须在工程目录中。
  3. 再次&#xff0c;进入 build 目录&#xff0c;运行cmake …(注意,…代表父目录&#xff0c;因为父目录存在我们需要的CMakeLists.txt&#xff0c;如果你在其他地方建立了build 目录&#xff0c;需要运行cmake <工程的全路径>)&#xff0c;查看一下build 目录&#xff0c;就会发现了生成了编译需要的Makefile 以及其他的中间文件。
  4. 最后&#xff0c;运行 make 构建工程&#xff0c;就会在当前目录(build 目录)中获得目标文件 hello。
    上述过程就是所谓的out-of-source 外部编译&#xff0c;一个最大的好处是&#xff0c;对于原有的工程没有任何影响&#xff0c;所有动作全部发生在编译目录。通过这一点&#xff0c;也足以说服我们全部采用外部编译方式构建工程。

这里需要特别注意的是&#xff1a;通过外部编译进行工程构建&#xff0c;HELLO_SOURCE_DIR 仍然指代工程路径&#xff0c;即/t1&#xff1b;而 HELLO_BINARY_DIR 则指代编译路径&#xff0c;即cmake/t1/build。

四、更像样的CMake工程

以后所有的构建我们都将采用 out-of-source 外部构建&#xff0c;约定的构建目录是工程目录下的build 自录。

让前面的Hello World 更像一个工程&#xff0c;需要作的是&#xff1a;

  1. 为工程添加一个子目录src&#xff0c;用来放置工程源代码。
  2. 添加一个子目录doc&#xff0c;用来放置这个工程的文档hello.txt。
  3. 在工程目录添加文本文件COPYRIGHT&#xff0c;README。
  4. 在工程目录添加一个runhello.sh 脚本&#xff0c;用来调用hello 二进制。
  5. 将构建后的目标文件放入构建目录的bin子目录。
  6. 最终安装这些文件&#xff1a;将hello 二进制与runhello.sh 安装至/usr/bin&#xff0c;将doc 目录的内容以及COPYRIGHT/README 安装到/usr/share/doc/cmake/t2。

4.1、准备工作

&#xff08;1&#xff09;建立t2目录。

mkdir t2

&#xff08;2&#xff09;将 t1 工程的 main.c 和 CMakeLists.txt 拷贝到t2 目录中。

cp ./main.c ../t2
cp ./CMakeLists.txt ../t2
# 或者整个目录复制
# cp -r ../t1 ../t2

&#xff08;3&#xff09;添加子目录src&#xff1a;

cd t2
mkdir src
mv main.c src/

现在的工程看起来是这个样子&#xff1a;一个子目录src&#xff0c;一个 CMakeLists.txt。

&#xff08;4&#xff09;上面提到&#xff0c;需要为任何子目录建立一个 CMakeLists.txt&#xff0c;进入子目录src&#xff0c;编写 CMakeLists.txt 如下&#xff1a;

ADD_EXECUTABLE(hello main.c)

&#xff08;5&#xff09;将 t2 工程的 CMakeLists.txt 修改为&#xff1a;

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)

4.2、构建

建立build 目录&#xff0c;进入build 目录进行外部编译。

mkdir build
cd build
cmake ..
make

构建完成后&#xff0c;生成的目标文件 hello 位于 build/bin 目录中。

4.3、语法解释

&#xff08;1&#xff09;ADD_SUBDIRECTORY 指令&#xff1a;

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

这个指令用于向当前工程添加存放源文件的子目录&#xff0c;并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除&#xff0c;比如&#xff0c;工程的 example&#xff0c;可能就需要工程构建完成后&#xff0c;再进入 example 目录单独进行构建(当然&#xff0c;也可以通过定义依赖来解决此类问题)。

上面的例子定义了将src 子目录加入工程&#xff0c;并指定编译输出(包含编译中间结果)路径为 bin 目录。如果不进行 bin 目录的指定&#xff0c;那么编译结果(包括中间结果)都将存放在 build/src 目录(这个目录跟原有的src 目录对应)&#xff0c;指定 bin 目录后&#xff0c;相当于在编译时将src 重命名为bin&#xff0c;所有的中间结果和目标二进制都将存放在bin 目录。

这里需要提一下的是SUBDIRS 指令&#xff0c;使用方法是&#xff1a;

SUBDIRS(dir1 dir2...)

但是这个指令已经不推荐使用。它可以一次添加多个子目录&#xff0c;并且&#xff0c;即使外部编译&#xff0c;子目录体系仍然会被保存。

如果在上面的例子中将ADD_SUBDIRECTORY (src bin)修改为SUBDIRS(src)&#xff0c;那么在build 目录中将出现一个src 目录&#xff0c;生成的目标代码 hello 将存放在src 目录中。

4.4、修改保存目标二进制的地方

不论是SUBDIRS 还是 ADD_SUBDIRECTORY 指令(不论是否指定编译输出目录)&#xff0c;我们都可以通过SET 指令重新定义EXECUTABLE_OUTPUT_PATH 和LIBRARY_OUTPUT_PATH 变量来指定最终的目标二进制的位置(指最终生成的hello 或者最终的共享库&#xff0c;不包含编译生成的中间文件)。

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

前面提到了_BINARY_DIR 和PROJECT_BINARY_DIR 变量&#xff0c;他们指的编译发生的当前目录&#xff0c;如果是内部编译&#xff0c;就相当于 PROJECT_SOURCE_DIR 也就是工程代码所在目录&#xff0c;如果是外部编译&#xff0c;指的是外部编译所在目录&#xff0c;也就是本例中的 build 目录。

所以&#xff0c;上面两个指令分别定义了&#xff1a;可执行二进制的输出路径为build/bin 和库的输出路径为build/lib。

这里没有提到共享库和静态库的构建&#xff0c;所以&#xff0c;暂时可以不考虑第二条指令。

问题是&#xff0c;应该把这两条指令写在工程的 CMakeLists.txt 还是 src 目录下的CMakeLists.txt&#xff0c;把握一个简单的原则&#xff1a;在哪里ADD_EXECUTABLE 或ADD_LIBRARY&#xff0c;如果需要改变目标存放路径&#xff0c;就在哪里加入上述的定义。

在这个例子里&#xff0c;当然就是指src 下的CMakeLists.txt 了。

4.5、如何安装编译的软件

安装的需要有两种&#xff0c;一种是从代码编译后直接 make install 安装&#xff0c;一种是打包时的指定目录安装。所以&#xff0c;即使最简单的手工编写的Makefile&#xff0c;看起来也是这个样子的&#xff1a;

DESTDIR&#61;
install:
mkdir -p $(DESTDIR)/usr/bin install -m 755 hello $(DESTDIR)/usr/bin

这时就可以通过 make install 将 hello 直接安装到/usr/bin 目录&#xff0c;也可以通过make install ESTDIR&#61;/tmp/test 将它安装在/tmp/test/usr/bin 目录&#xff0c;打包时这个方式经常被使用。

稍微复杂一点的是还需要定义PREFIX&#xff0c;一般autotools 工程&#xff0c;会运行这样的指令:

./configure –prefix&#61;/usr
# 或者
./configure --prefix&#61;/usr/local
#来指定 PREFIX

比如上面的Makefile 就可以改写成:

DESTDIR&#61;
PREFIX&#61;/usr
install:
mkdir -p $(DESTDIR)/$(PREFIX)/bin install -m 755 hello $(DESTDIR)/$(PREFIX)/bin

那么我们CMake的HelloWorld 应该怎么进行安装呢&#xff1f;

这里需要引入一个新的cmake 指令 INSTALL 和一个非常有用的变量&#xff1a;CMAKE_INSTALL_PREFIX。

CMAKE_INSTALL_PREFIX 变量类似于configure 脚本的 –prefix&#xff0c;常见的使用方法是这个样子&#xff1a;

cmake -DCMAKE_INSTALL_PREFIX&#61;/usr .

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

INSTALL 指令包含了各种安装类型&#xff0c;需要一个个分开解释&#xff1a;

&#xff08;1&#xff09;目标文件的安装&#xff1a;

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

参数中的TARGETS 后面跟的就是我们通过ADD_EXECUTABLE 或者ADD_LIBRARY 定义的目标文件&#xff0c;可能是可执行二进制、动态库、静态库。

目标类型也就相对应的有三种&#xff1a;ARCHIVE 特指静态库&#xff0c;LIBRARY 特指动态库&#xff0c;RUNTIME特指可执行目标二进制。

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

举个简单的例子&#xff1a;

INSTALL(TARGETS myrun mylib mystaticlib RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic
)

上面的例子会将&#xff1a;

  • 可执行二进制myrun 安装到${CMAKE_INSTALL_PREFIX}/bin 。
  • 目录动态库libmylib 安装到${CMAKE_INSTALL_PREFIX}/lib 目录。
  • 静态库libmystaticlib 安装到${CMAKE_INSTALL_PREFIX}/libstatic 目录。
  • 特别注意的是不需要关心TARGETS 具体生成的路径&#xff0c;只需要写上TARGETS 名称就可以了。

&#xff08;2&#xff09;普通文件的安装&#xff1a;

INSTALL(FILES files... DESTINATION <dir> [PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>]
[RENAME <name>] [OPTIONAL])

可用于安装一般文件&#xff0c;并可以指定访问权限&#xff0c;文件名是此指令所在路径下的相对路径。如果默认不定义权限PERMISSIONS&#xff0c;安装后的权限为&#xff1a;

OWNER_WRITE, OWNER_READ, GROUP_READ,和WORLD_READ&#xff0c;即 644 权限。

&#xff08;3&#xff09;非目标文件的可执行程序安装(比如脚本之类)&#xff1a;

INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>] [RENAME <name>] [OPTIONAL])

跟上面的FILES 指令使用方法一样&#xff0c;唯一的不同是安装后权限为:

OWNER_EXECUTE, GROUP_EXECUTE, 和WORLD_EXECUTE&#xff0c;即755 权限目录的安装。

&#xff08;4&#xff09;目录安装&#xff1a;

INSTALL(DIRECTORY dirs... DESTINATION <dir> [FILE_PERMISSIONS permissions...] [DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])

这里主要介绍其中的DIRECTORY、PATTERN 以及 PERMISSIONS 参数。

DIRECTORY 后面连接的是所在Source 目录的相对路径&#xff0c;但务必注意&#xff1a;abc 和 abc/有很大的区别。如果目录名不以/结尾&#xff0c;那么这个目录将被安装为目标路径下的abc&#xff0c;如果目录名以/结尾&#xff0c;代表将这个目录中的内容安装到目标路径&#xff0c;但不包括这个目录本身。

PATTERN 用于使用正则表达式进行过滤&#xff0c;PERMISSIONS 用于指定PATTERN 过滤后的文件权限。

看一个例子:

INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*"
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_READ)

这条指令的执行结果是&#xff1a;将 icons 目录安装到 /share/myproj&#xff0c;将 scripts/中的内容安装到 /share/myproj不包含目录名为 CVS 的目录&#xff0c;对于scripts/*文件指定权限为 OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ。

&#xff08;5&#xff09;安装时CMAKE 脚本的执行&#xff1a;

INSTALL([[SCRIPT <file>] [CODE <code>]] [...])
# SCRIPT #参数用于在安装时调用cmake 脚本文件&#xff08;也就是.cmake 文件&#xff09;
# CODE 参数用于执行CMAKE 指令&#xff0c;必须以双引号括起来。比如&#xff1a;
INSTALL(CODE "MESSAGE(\"Sample install message.\")")

安装还有几个被标记为过时的指令&#xff0c;比如 INSTALL_FILES 等&#xff0c;这些指令已经不再推荐使用。

下面&#xff0c;就来改写我们的工程文件&#xff0c;让他来支持各种文件的安装&#xff0c;并且&#xff0c;我们要使用 CMAKE_INSTALL_PREFIX 指令。

4.6、修改CMakeLists.txt支持安装

&#xff08;1&#xff09;首先先补上为添加的文件。添加doc 目录及文件:

cd t2
mkdir doc
vi doc/hello.txt

随便填写一些内容并保存。

&#xff08;2&#xff09;在工程目录添加runhello.sh 脚本&#xff0c;内容为&#xff1a;

hello

&#xff08;3&#xff09;添加工程目录中的COPYRIGHT 和 README&#xff1a;

touch COPYRIGHT
touch README

&#xff08;4&#xff09;下面改写各目录的CMakeLists.txt 文件。

1&#xff09;安装 COPYRIGHT和README&#xff0c;直接修改主工程文件CMakelists.txt&#xff0c;加入以下指令&#xff1a;

INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)

2&#xff09;安装 runhello.sh&#xff0c;直接修改主工程文件CMakeLists.txt&#xff0c;加入如下指令&#xff1a;

INSTALL(PROGRAMS runhello.sh DESTINATION bin)

3&#xff09;安装 doc 中的hello.txt&#xff0c;这里有两种方式&#xff1a;一是通过在doc 目录建立 CMakeLists.txt 并将 doc 目录通过ADD_SUBDIRECTORY 加入工程来完成。另一种方法是直接在工程目录通过INSTALL(DIRECTORY 来完成)&#xff0c;前者比较简单&#xff0c;可以根据兴趣自己完成&#xff0c;我们来尝试后者&#xff0c;顺便演示以下DIRECTORY 的安装。
因为hello.txt 要安装到//share/doc/cmake/t2&#xff0c;所以不能直接安装整个doc 目录&#xff0c;这里采用的方式是安装 doc 目录中的内容&#xff0c;也就是使用”doc/”。在工程文件中添加&#xff1a;

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

&#xff08;5&#xff09;现在进入build 目录进行外部编译&#xff0c;注意使用 CMAKE_INSTALL_PREFIX 参数&#xff0c;这里我们将它安装到了/tmp/t2 目录&#xff1a;

cmake -DCMAKE_INSTALL_PREFIX&#61;/tmp/t2/usr ..

&#xff08;6&#xff09;然后运行&#xff1a;

make
sudo make install

&#xff08;7&#xff09;进入/tmp/t2 目录看一下安装结果&#xff1a;

$ tree -L 5 /tmp/t2/usr/
/tmp/t2/usr/
├── bin
│ └── runhello.sh
└── share
└── doc
└── cmakeProj
└── t2
├── COPYRIGHT
├── hello.txt
└── README
5 directories, 4 files

&#xff08;8&#xff09;如果要直接安装到系统&#xff0c;可以使用如下指令&#xff1a;

cmake -DCMAKE_INSTALL_PREFIX&#61;/usr ..

注意&#xff0c;CMAKE_INSTALL_PREFIX 的默认定义是/usr/local&#xff0c;如果没有定义CMAKE_INSTALL_PREFIX 会安装到这个地方。

总结
  1. 使用cmake 构建Hello World 程序的全部过程&#xff0c;并介绍三个简单的指令&#xff1a;PROJECT/MESSAGE/ADD_EXECUTABLE 以及变量调用的方法&#xff0c;同时提及了两个隐式变量 _SOURCE_DIR 及_BINARY_DIR&#xff0c;演示了变量调用的方法&#xff0c;从这个过程来看&#xff0c;可能觉得比直接写 Makefile 要复杂多了&#xff0c;甚至都可以不编写Makefile&#xff0c;直接使用gcc -o hello -c main.c 即可生成需要的目标文件。这是因为工程只有几个文件&#xff0c;还是直接编写 Makefile 最简单。但是&#xff0c;如果工程文件非常多&#xff0c;那么CMake就比Makefile简单了。
  2. 掌握如何在工程中使用多目录、各种安装指令以及CMAKE_INSTALL_PREFIX 变量。
    在这里插入图片描述






推荐阅读
  • 通过将常用的外部命令集成到VSCode中,可以提高开发效率。本文介绍如何在VSCode中配置和使用自定义的外部命令,从而简化命令执行过程。 ... [详细]
  • 本文详细介绍了在 CentOS 7 系统中配置 fstab 文件以实现开机自动挂载 NFS 共享目录的方法,并解决了常见的配置失败问题。 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 如何在Windows内置的Ubuntu系统中更改SSH服务的端口号设置
    如何在Windows内置的Ubuntu系统中更改SSH服务的端口号设置 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • MATLAB字典学习工具箱SPAMS:稀疏与字典学习的详细介绍、配置及应用实例
    SPAMS(Sparse Modeling Software)是一个强大的开源优化工具箱,专为解决多种稀疏估计问题而设计。该工具箱基于MATLAB,提供了丰富的算法和函数,适用于字典学习、信号处理和机器学习等领域。本文将详细介绍SPAMS的配置方法、核心功能及其在实际应用中的典型案例,帮助用户更好地理解和使用这一工具箱。 ... [详细]
  • 在 CentOS 6.4 上安装 QT5 并启动 Qt Creator 时,可能会遇到缺少 GLIBCXX_3.4.15 的问题。这是由于系统中的 libstdc++.so.6 版本过低。本文将详细介绍如何通过更新 GCC 版本来解决这一问题。 ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • 装饰者模式(Decorator):一种灵活的对象结构设计模式
    装饰者模式(Decorator)是一种灵活的对象结构设计模式,旨在为单个对象动态地添加功能,而无需修改原有类的结构。通过封装对象并提供额外的行为,装饰者模式比传统的继承方式更加灵活和可扩展。例如,可以在运行时为特定对象添加边框或滚动条等特性,而不会影响其他对象。这种模式特别适用于需要在不同情况下动态组合功能的场景。 ... [详细]
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
  • 深入解析Java虚拟机的内存分区与管理机制
    Java虚拟机的内存分区与管理机制复杂且精细。其中,某些内存区域在虚拟机启动时即创建并持续存在,而另一些则随用户线程的生命周期动态创建和销毁。例如,每个线程都拥有一个独立的程序计数器,确保线程切换后能够准确恢复到之前的执行位置。这种设计不仅提高了多线程环境下的执行效率,还增强了系统的稳定性和可靠性。 ... [详细]
  • 为了在Hadoop 2.7.2中实现对Snappy压缩和解压功能的原生支持,本文详细介绍了如何重新编译Hadoop源代码,并优化其Native编译过程。通过这一优化,可以显著提升数据处理的效率和性能。此外,还探讨了编译过程中可能遇到的问题及其解决方案,为用户提供了一套完整的操作指南。 ... [详细]
  • 在 Vue 应用开发中,页面状态管理和跨页面数据传递是常见需求。本文将详细介绍 Vue Router 提供的两种有效方式,帮助开发者高效地实现页面间的数据交互与状态同步,同时分享一些最佳实践和注意事项。 ... [详细]
  • 本指南详细介绍了在Linux环境中高效连接MySQL数据库的方法。用户可以通过安装并使用`mysql`客户端工具来实现本地连接,具体命令为:`mysql -u 用户名 -p 密码 -h 主机`。例如,使用管理员账户连接本地MySQL服务器的命令为:`mysql -u root -p pass`。此外,还提供了多种配置优化建议,以确保连接过程更加稳定和高效。 ... [详细]
  • 在VS2013中编译FFMPEG时遇到的问题及解决方案
    在使用VS2013编译旧版本FFMPEG库时遇到了一些问题,因为官方并未提供预编译的LIB和DLL文件。由于对Linux环境不熟悉,只能在Windows环境下进行配置和编译。具体步骤如下:首先,下载FFMPEG的源代码;然后,安装必要的编译工具和依赖项;接着,配置Visual Studio 2013的项目设置;最后,解决编译过程中出现的各种错误和警告。通过这些步骤,最终成功编译出所需的FFMPEG库文件。 ... [详细]
author-avatar
一剑吹雪_811
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有