1.cmake
cmake 是kitware 公司以及⼀些开源开发者在开发⼏个⼯具套件(VTK)的过程中衍⽣品,最终形成体系,成为⼀个独⽴的开放源代码项⽬。官⽅⽹站是www.cmake.org,可以通过访问官⽅⽹站获得更多关于cmake 的信息。
说就是:只有少数⼏个“编译专家”能够掌握 KDE 现在的构建体系(admin/Makefile.common),在经历了unsermake, scons 以及cmake 的选型和尝试之后,KDE4 决定使⽤cmake 作为⾃⼰的构建系统。在迁移过程中,进展异常的顺利,并获得了cmake 开发者的⽀持。所以,⽬前的 KDE4 开发版本已经完全使⽤cmake 来进⾏构建。像kdesvn,rosegarden 等项⽬也开始使⽤cmake,这也注定了cmake 必然会成为⼀个主流的构建体系。
特点
cmake 的特点主要有:
1.开放源代码,使⽤类BSD 许可发布。http://cmake.org/HTML/Copyright.html
2.跨平台,并可⽣成native 编译配置⽂件,在Linux/Unix 平台,⽣成 makefile,在苹果平台,可以⽣成xcode,在 Windows 平台,可以⽣成 MSVC 的⼯程⽂件。
3.能够管理⼤型项⽬,KDE4 就是最好的证明。
4.简化编译构建过程和编译过程。Cmake 的⼯具链⾮常简单:cmake+make。
5.⾼效虑,按照KDE 官⽅说法,CMake 构建KDE4 的 kdelibs 要⽐使⽤autotools 来构建KDE3.5.6 的kdelibs 快40%,主要是因为 Cmake 在⼯具链中没有libtool。
6.可扩展,可以为cmake 编写特定功能的模块,扩充cmake 功能。
问题
难道就没有问题?
1.cmake 很简单,但绝对没有听起来或者想象中那么简单。
2.cmake 编写的过程实际上是编程的过程,跟以前使⽤autotools ⼀样,不过你需要编写的是CMakeLists.txt(每个⽬录⼀个),使⽤的是”cmake 语⾔和语法”。
3.cmake 跟已有体系的配合并不是特别理想,⽐如pkgconfig,您在实际使⽤中会有所体会,虽然有⼀些扩展可以使⽤,但并不理想。
建议
1.如果你没有实际的项⽬需求,那么看到这⾥就可以停下来了,因为cmake 的学习过程就是实践过程,没有实践,读的再多⼏天后也会忘记。
2.如果你的⼯程只有⼏个⽂件,直接编写Makefile 是最好的选择。
3.如果使⽤的是C/C++/Java 之外的语⾔,请不要使⽤cmake(⾄少⽬前是这样)
4.如果你使⽤的语⾔有⾮常完备的构建体系,⽐如java 的 ant,也不需要学习cmake,虽然有成功的例⼦,⽐如QT4.3 的csharp 绑定qyoto。
5.如果项⽬已经采⽤了⾮常完备的⼯程管理⼯具,并且不存在维护问题,没有必要迁移到 cmake
6.如果仅仅使⽤qt 编程,没有必要使⽤ cmake,因为qmake 管理 Qt ⼯程的专业性和⾃动化程度⽐cmake 要⾼很多。
安装 cmake
(1)卸载已经安装的旧版的CMake(⾮必需)
apt-get autoremove cmake
(2)⽂件下载解压
wget https://cmake.org/files/v3.9/cmake-3.9.1-Linux-x86_64.tar.gz
tar zxvf cmake-3.9.1-Linux-x86_64.tar.gz
查看解压后⽬录:
bin下⾯有各种cmake家族的产品程序.
创建软链接
注: ⽂件路径是可以指定的, ⼀般选择在 /opt 或 /usr 路径下, 这⾥选择 /opt
mv cmake-3.9.1-Linux-x86_64 /opt/cmake-3.9.1
ln -sf /opt/cmake-3.9.1/bin/* /usr/bin/
cmake 的 helloworld
本节并不会深⼊的探讨cmake,仅仅展示⼀个简单的例⼦,并加以粗略的解释。
准备⼯作
⾸先,建⽴⼀个cmake ⽬录,⽤来放置我们学习过程中的所有练习。 mkdir -p cmake以后我们所有的cmake 练习都会放在cmake 的⼦⽬录下(你也可以⾃⾏安排⽬录,这个并不是限制,仅仅是为了叙述的⽅便),然后在cmake 建⽴第⼀个练习⽬录t1。
cd cmake
mkdir t1
cd t1
在 t1(hello-world) ⽬录建⽴main.c 和 CMakeLists.txt(注意⽂件名⼤⼩写):
main.c ⽂件内容:
CmakeLists.txt ⽂件内容:
注:上图有问题,最后两行的SRC_LIST应该为${SRC_LIST},这里SRC_LIST是个变量,后续如果还有这种情况,请自动更正。
开始构建
所有的⽂件创建完成后,t1 ⽬录中应该存在main.c 和 CMakeLists.txt 两个⽂件接下来我们来构建这个⼯程,在这个⽬录运⾏:
cmake . (注意命令后⾯的点号,代表本⽬录)。
输出⼤概是这个样⼦:
再让我们看⼀下⽬录中的内容, 你会发现,系统⾃动⽣成了:
CMakeFiles, CMakeCache.txt, cmake_install.cmake 等⽂件,并且⽣成了 Makefile。不需要理会这些⽂件的作⽤,以后你也可以不去理会。最关键的是,它⾃动⽣成了 Makefile。然后进⾏⼯程的实际构建,在这个⽬录输⼊ make 命令,⼤概会得到如下的彩⾊输出:
如果你需要看到make 构建的详细过程,可以使⽤make VERBOSE=1 或者VERBOSE=1 make 命令来进⾏构建。这时候,我们需要的⽬标⽂件hello 已经构建完成,位于当前⽬录,尝试运⾏⼀下:
./hello
得到输出:
Hello World from Main
简单的解释
我们来重新看⼀下 CMakeLists.txt,这个⽂件是 cmake 的构建定义⽂件,⽂件名是⼤⼩写相关的,如果⼯程存在多个⽬录,需要确保每个要管理的⽬录都存在⼀个CMakeLists.txt。(关于多⽬录构建,后⾯我们会提到,这⾥不作过多解释)。
上⾯例⼦中的CMakeLists.txt ⽂件内容如下:
PROJECT
PROJECT 指令的语法是:
PROJECT(projectname [CXX] [C] [Java])
你可以⽤这个指令定义⼯程名称,并可指定⼯程⽀持的语⾔,⽀持的语⾔列表是可以忽略的,默认情况表示⽀持所有语⾔。这个指令隐式的定义了两个 cmake 变量: <projectname>_BINARY_DIR 以及<projectname>_SOURCE_DIR,这⾥就是 HELLO_BINARY_DIR 和HELLO_SOURCE_DIR(所以CMakeLists.txt 中两个MESSAGE指令可以直接使⽤了这两个变量),因为采⽤的是内部编译,两个变量⽬前指的都是⼯程所在路cmake/t1,后⾯我们会讲到外部编译,两者所指代的内容会有所不同。同时cmake 系统也帮助我们预定义PROJECT_BINARY_DIR 和PROJECT_SOURCE_DIR变量,他们的值分别跟HELLO_BINARY_DIR 与HELLO_SOURCE_DIR ⼀致。
为了统⼀起⻅,建议以后直接使⽤PROJECT_BINARY_DIR,PROJECT_SOURCE_DIR,即使修改了⼯程名称,也不会影响这两个变量。如果使⽤了<projectname>_SOURCE_DIR,修改⼯程名称后,需要同时修改这些变量。
SET
SET 指令的语法是:
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
现阶段,你只需要了解SET 指令可以⽤来显式的定义变量即可。⽐如我们⽤到的是SET(SRC_LIST main.c),如果有多个源⽂件,也可以定义成:
SET(SRC_LIST main.c t1.c t2.c)。
MESSAGE
MESSAGE 指令的语法是:
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)
这个指令⽤于向终端输出⽤户定义的信息,包含了三种类型:
(1)SEND_ERROR,产⽣错误,⽣成过程被跳过。
(2)SATUS,输出前缀为—的信息。
(3)FATAL_ERROR,⽴即终⽌所有cmake 过程
在这⾥使⽤的是STATUS 信息输出,演示了由PROJECT 指令定义的两个隐式变量HELLO_BINARY_DIR 和HELLO_SOURCE_DIR。
ADD_EXECUTABLE
ADD_EXECUTABLE(hello ${SRC_LIST})
定义了这个⼯程会⽣成⼀个⽂件名为hello 的可执⾏⽂件,相关的源⽂件是 SRC_LIST 中定义的源⽂件列表, 本例中你也可以直接写成ADD_EXECUTABLE(hello main.c)。
在本例我们使⽤了${}来引⽤变量,这是cmake 的变量应⽤⽅式。
注意:有⼀些例外,⽐如在IF 控制语句,变量是直接使⽤变量名引⽤,⽽不需要${}。如果使⽤了${}去应⽤变量,其实IF 会去判断名为${}所代表的值的变量,那当然是不存在的了。
将本例改写成⼀个最简化的CMakeLists.txt:
PROJECT(HELLO)
ADD_EXECUTABLE(hello main.c)
基本语法规则
前⾯提到过,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 是没有任何关系的。
⼯程名和执⾏⽂件
hello 定义了可执⾏⽂件的⽂件名,你完全可以写成:ADD_EXECUTABLE(t1 main.c)编译后会⽣成⼀个t1 可执⾏⽂件。
关于语法的疑惑
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 时注意形成统⼀的⻛格即可。
清理⼯程
跟经典的autotools 系列⼯具⼀样,运⾏:
make clean
问题
我尝试运⾏了 make distclean,这个指令⼀般⽤来清理构建过程中产⽣的中间⽂件的,如果要发布代码,必然要清理掉所有的中间⽂件,但是为什么在 cmake ⼯程中这个命令是⽆效的?cmake 并不⽀持 make distclean,关于这⼀点,官⽅是有明确解释的:
因为CMakeLists.txt 可以执⾏脚本并通过脚本⽣成⼀些临时⽂件,但是却没有办法来跟踪这些临时⽂件到底是哪些。因此,没有办法提供⼀个可靠的 make distclean ⽅案。
同时,还有另外⼀个⾮常重要的提示,就是:我们刚才进⾏的是内部构建(in-source build),⽽ cmake强烈推荐的是外部构建(out-of-source build)。
内部构建与外部构建
上⾯的例⼦展示的是“内部构建”,相信看到⽣成的临时⽂件⽐您的代码⽂件还要多的时候,估计这辈⼦你都不希望再使⽤内部构建。
对于cmake,内部编译上⾯已经演示过了,它⽣成了⼀些⽆法⾃动删除的中间⽂件,所以,引出了我们对外部编译的探讨,外部编译的过程如下:
(1)⾸先,请清除t1 ⽬录中除main.c CmakeLists.txt 之外的所有中间⽂件,最关键的是CMakeCache.txt。
(2)在 t1 ⽬录中建⽴build ⽬录,当然你也可以在任何地⽅建⽴build ⽬录,不⼀定必须在⼯程⽬录中。
(3)进⼊ build ⽬录,运⾏cmake ..(注意,..代表⽗⽬录,因为⽗⽬录存在我们需要的CMakeLists.txt,如果你在其他地⽅建⽴了build ⽬录,需要运⾏cmake <⼯程的全路径>),查看⼀下build ⽬录,就会发现了⽣成了编译需要的Makefile 以及其他的中间⽂件.
(4)运⾏ make 构建⼯程,就会在当前⽬录(build ⽬录)中获得⽬标⽂件 hello。
上述过程就是所谓的out-of-source 外部编译,⼀个最⼤的好处是,对于原有的⼯程没有任何影响,所有动作全部发⽣在编译⽬录。通过这⼀点,也⾜以说服我们全部采⽤外部编译⽅式构建⼯程。⽽ HELLO_BINARY_DIR 则指代编译路径,即cmake/t1/build
本⼩节描述了使⽤cmake 构建Hello World 程序的全部过程,并介绍了三个简单的指令:PROJECT/MESSAGE/ADD_EXECUTABLE 以及变量调⽤的⽅法,同时提及了两个隐式变量<projectname>_SOURCE_DIR 及<projectname>_BINARY_DIR,演示了变量调⽤的⽅法,从这个过程来看,有些开发者可能会想,这实在⽐我直接写 Makefile 要复杂多了,甚⾄我都可以不编写Makefile,直接使⽤gcc main.c 即可⽣成需要的⽬标⽂件。是的,正如第⼀节提到的,如果⼯程只有⼏个⽂件,还是直接编写 Makefile 最简单。但是, kdelibs 压缩包达到了50 多M,您认为使⽤什么⽅案会更容易⼀点呢?
4 更好⼀点的 Hello World
从本⼩节开始,后⾯所有的构建我们都将采⽤ out-of-source 外部构建,约定的构建⽬录是⼯程⽬录下的build ⾃录。
建设一个真正的工程,需要如下步骤:
(1)为⼯程添加⼀个⼦⽬录src,⽤来放置⼯程源代码
(2)添加⼀个⼦⽬录doc,⽤来放置这个⼯程的⽂档hello.txt
(3)在⼯程⽬录添加⽂本⽂件COPYRIGHT, README
(4)在⼯程⽬录添加⼀个runhello.sh 脚本,⽤来调⽤hello ⼆进制
(5)将构建后的⽬标⽂件放⼊构建⽬录的bin ⼦⽬录
(6)最终安装这些⽂件:将hello ⼆进制与runhello.sh 安装⾄/usr/bin,将doc ⽬录的内容以及
COPYRIGHT/README 安装到/usr/share/doc/cmake/t2。
具体工作如下:
(1)在cmake/⽬录下建⽴t2 (hello-world-clear)⽬录。并将 t1 ⼯程的 main.c 和 CMakeLists.txt 拷⻉到t2 ⽬录中。
(2)添加⼦⽬录src。
mkdir src
mv main.c src
现在的⼯程看起来是这个样⼦:⼀个⼦⽬录src,⼀个 CMakeLists.txt。
上⼀节我们提到,需要为任何⼦⽬录建⽴⼀个 CMakeLists.txt,进⼊⼦⽬录src,编写 CMakeLists.txt
如下:
ADD_EXECUTABLE(hello main.c)
将 t2 ⼯程的 CMakeLists.txt 修改为:
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
然后建⽴build ⽬录,进⼊build ⽬录进⾏外部编译。
cmake ..
make
构建完成后,你会发现⽣成的⽬标⽂件 hello 位于 build/bin ⽬录中。
语法解释
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令⽤于向当前⼯程添加存放源⽂件的⼦⽬录,并可以指定中间⼆进制和⽬标⼆进制存放的位置。EXCLUDE_FROM_ALL 参数的含义是将这个⽬录从编译过程中排除,⽐如,⼯程的 example,可能就需要⼯程构建完成后,再进⼊ example ⽬录单独进⾏构建(当然,你也可以通过定义依赖来解决此类问题)。
上⾯的例⼦定义了将src ⼦⽬录加⼊⼯程,并指定编译输出(包含编译中间结果)路径为 bin ⽬录。如果不进⾏ bin ⽬录的指定,那么编译结果(包括中间结果)都将存放在 build/src ⽬录(这个⽬录跟原有的src ⽬录对应),指定 bin ⽬录后,相当于在编译时将src 重命名为bin,所有的中间结果和⽬标⼆进制都将存放在bin ⽬录。
如果我们在上⾯的例⼦中将ADD_SUBDIRECTORY (src bin)修改为SUBDIRS(src)。那么在build ⽬录中将出现⼀个src ⽬录,⽣成的⽬标代码 hello 将存放在src ⽬录中。
这里提下SUBDIRS 指令,SUBDIRS(dir1 dir2...),这个指令已经不推荐使⽤。可以⼀次添加多个⼦⽬录,并且,即使外部编译,⼦⽬录体系仍然会被保存。
换个地⽅保存⽬标⼆进制
不论是SUBDIRS 还是 ADD_SUBDIRECTORY 指令(不论是否指定编译输出⽬录),我们都可以通过SET指令重新定义EXECUTABLE_OUTPUT_PATH 和LIBRARY_OUTPUT_PATH 变量来指定最终的⽬标⼆进制的位置(指最终⽣成的hello 或者最终的共享库,不包含编译⽣成的中间⽂件).
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
在第⼀节我们提到了<projectname>_BINARY_DIR 和PROJECT_BINARY_DIR 变量,他们指的编译发⽣的当前⽬录,如果是内部编译,就相当于 PROJECT_SOURCE_DIR 也就是⼯程代码所在⽬录,如果是外部编译,指的是外部编译所在⽬录,也就是本例中的 build ⽬录。
上⾯两个指令分别定义了:可执⾏⼆进制的输出路径为build/bin 和库的输出路径为build/lib。
没有提到共享库和静态库的构建,所以,你可以不考虑第⼆条指令。我应该把这两条指令写在⼯程的 CMakeLists.txt 还是 src ⽬录下的CMakeLists.txt,把握⼀个简单的原则,在哪⾥ADD_EXECUTABLE 或ADD_LIBRARY,如果需要改变⽬标存放路径,就在哪⾥加⼊上述的定义。在这个例⼦⾥,当然就是指src 下的CMakeLists.txt 了
如何安装
安装的需要有两种,⼀种是从代码编译后直接 make install 安装,⼀种是打包时的指定⽬录安装。所以,即使最简单的⼿⼯编写的Makefile,将 hello 直接安装到/usr/bin ⽬录,也可以通过make install DESTDIR=/tmp/test 将他安装在/tmp/test/usr/bin ⽬录,打包时这个⽅式经常被使⽤,看起来也是这个样⼦的:
DESTDIR=
install:
mkdir -p $(DESTDIR)/usr/bin install -m 755 hello $(DESTDIR)/usr/bin
make install
稍微复杂⼀点的是还需要定义PREFIX,⼀般autotools ⼯程,会运⾏这样的指令:
./configure –prefix=/usr 或者./configure --prefix=/usr/local 来指定 PREFIX
⽐如上⾯的Makefile 就可以改写成:
DESTDIR=
PREFIX=/usr
install:
mkdir -p $(DESTDIR)/$(PREFIX)/bin install -m 755 hello $(DESTDIR)/$(PREFIX)/bin
那么我们的HelloWorld 应该怎么进⾏安装呢?这⾥需要引⼊⼀个新的cmake 指令 INSTALL 和⼀个⾮常有⽤的变量
CMAKE_INSTALL_PREFIX。
CMAKE_INSTALL_PREFIX 变量类似于configure 脚本的 –prefix,常⻅的使⽤⽅法看起来是这个样⼦:
cmake -DCMAKE_INSTALL_PREFIX=/usr .
INSTALL 指令⽤于定义安装规则,安装的内容可以包括⽬标⼆进制、动态库、静态库以及⽂件、⽬录、脚本等。
INSTALL 指令包含了各种安装类型,我们需要⼀个个分开解释:
⽬标⽂件的安装:
参数中的TARGETS 后⾯跟的就是我们通过ADD_EXECUTABLE 或者ADD_LIBRARY 定义的⽬标⽂件,可能是可执⾏⼆进制、动态库、静态库。
⽬标类型也就相对应的有三种,ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME特指可执⾏⽬标⼆进制。
DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX 其实就⽆效了。如果你希望使⽤CMAKE_INSTALL_PREFIX 来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>。
注意:特别注意的是你不需要关⼼TARGETS 具体⽣成的路径,只需要写上TARGETS 名称就可以了。
简单的例⼦:
上⾯的例⼦会将:
(1)可执⾏⼆进制myrun 安装到${CMAKE_INSTALL_PREFIX}/bin
(2)⽬录动态库libmylib 安装到${CMAKE_INSTALL_PREFIX}/lib ⽬录
(3)静态库libmystaticlib 安装到${CMAKE_INSTALL_PREFIX}/libstatic ⽬录
普通⽂件的安装:
可⽤于安装⼀般⽂件,并可以指定访问权限,⽂件名是此指令所在路径下的相对路径。如果默认不定义权限PERMISSIONS,安装后的权限为:
OWNER_WRITE, OWNER_READ, GROUP_READ,和WORLD_READ,即 644 权限。
⾮⽬标⽂件的可执⾏程序安装(⽐如脚本之类):
跟上⾯的FILES 指令使⽤⽅法⼀样,唯⼀的不同是安装后权限为:
OWNER_EXECUTE, GROUP_EXECUTE, 和WORLD_EXECUTE,即755 权限⽬录的安装:
下面主要介绍其中的DIRECTORY、PATTERN 以及 PERMISSIONS 参数。
注意:abc 和 abc/有很⼤的区别。DIRECTORY 后⾯连接的是所在Source ⽬录的相对路径。如果⽬录名不以/结尾,那么这个⽬录将被安装为⽬标路径下的abc,如果⽬录名以/结尾,代表将这个⽬录中的内容安装到⽬标路径,但不包括这个⽬录本身。
PATTERN ⽤于使⽤正则表达式进⾏过滤,PERMISSIONS ⽤于指定PATTERN 过滤后的⽂件权限。
例⼦:
这条指令的执⾏结果是:
将 icons ⽬录安装到 <prefix>/share/myproj,将 scripts/中的内容安装到 <prefix>/share/myproj不包含⽬录名为 CVS 的⽬录,对于scripts/*⽂件指定权限为 OWNER_EXECUTE OWNER_WRITEOWNER_READ GROUP_EXECUTE GROUP_READ。
安装时CMAKE 脚本的执⾏:
INSTALL([[SCRIPT <file>] [CODE <code>]] [...])
SCRIPT 参数⽤于在安装时调⽤cmake 脚本⽂件(也就是<abc>.cmake ⽂件)
CODE 参数⽤于执⾏CMAKE 指令,必须以双引号括起来。⽐如:
INSTALL(CODE "MESSAGE(\"Sample install message.\")")
安装还有⼏个被标记为过时的指令,⽐如 INSTALL_FILES 等,这些指令已经不再推荐使⽤,所以,这⾥就不再赘述了。
下⾯,我们就来改写我们的⼯程⽂件,让他来⽀持各种⽂件的安装,并且,我们要使⽤CMAKE_INSTALL_PREFIX 指令。
修改 Helloworld ⽀持安装
在本节开头我们定义了本节的任务如下:
(1)为⼯程添加⼀个⼦⽬录src,⽤来存储源代码。
(2)添加⼀个⼦⽬录doc,⽤来存储这个⼯程的⽂档hello.txt。
(3)在⼯程⽬录添加⽂本⽂件COPYRIGHT, README。
(4)在⼯程⽬录添加⼀个runhello.sh 脚本,⽤来调⽤hello ⼆进制。
(5)将构建后的⽬标⽂件放⼊构建⽬录的bin ⼦⽬录。
(6)最终安装这些⽂件:将hello ⼆进制与runhello.sh 安装⾄/<prefix>/bin,将 doc ⽬录中的 hello.txt以及COPYRIGHT/README 安装到/<prefix>/share/doc/cmake/t2,将⾸先我们先补上为添加的⽂件。
添加doc ⽬录及⽂件:
cd cmake/t2
mkdir doc
vi doc/hello.txt
在⼯程⽬录添加runhello.sh 脚本,内容为:
hello
添加⼯程⽬录中的COPYRIGHT 和 README
touch COPYRIGHT
touch README
下⾯改写各⽬录的CMakeLists.txt ⽂件
(1)安装 COPYRIGHT/README,直接修改主⼯程⽂件CMakelists.txt,加⼊以下指令:
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)
(2)安装 runhello.sh,直接修改主⼯程⽂件CMakeLists.txt,加⼊如下指令:
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
(3)安装 doc 中的hello.txt,这⾥有两种⽅式:⼀是通过在doc ⽬录建⽴ CMakeLists.txt 并将 doc ⽬录通过ADD_SUBDIRECTORY 加⼊⼯程来完成。另⼀种⽅法是直接在⼯程⽬录通过INSTALL(DIRECTORY 来完成),前者⽐较简单,各位可以根据兴趣⾃⼰完成,我们来尝试后者,顺便演示以下DIRECTORY 的安装。
(4)因为hello.txt 要安装到/<prefix>/share/doc/cmake/t2,所以我们不能直接安装整个doc ⽬录,这⾥采⽤的⽅式是安装 doc ⽬录中的内容,也就是使⽤”doc/”
在⼯程⽂件中添加
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2)
尝试我们修改的结果:
现在进⼊build ⽬录进⾏外部编译,注意使⽤ CMAKE_INSTALL_PREFIX 参数,这⾥我们将它安装到了/tmp/t2 ⽬录:
cmake -DCMAKE_INSTALL_PREFIX=/tmp/t2/usr ..
然后运⾏
make
make install
让我们进⼊/tmp/t2 ⽬录看⼀下安装结果:
如果你要直接安装到系统,可以使⽤如下指令:
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
如果我没有定义CMAKE_INSTALL_PREFIX 会安装到什么地⽅?你可以尝试以下,cmake ..;make;make install,你会发现 CMAKE_INSTALL_PREFIX 的默认定义是/usr/local
描述了如何在⼯程中使⽤多⽬录、各种安装指令以及CMAKE_INSTALL_PREFIX 变量
在下一篇文章将讨论如何在cmake中构建动态库和静态库,以及如何使用外部头文件和外部共享库。