cmake practice笔记

本文为Cmake Practice--Cjacker这本电子书的实践笔记,作者之前将这个cmake过过一次手,现在再来写一个笔记

安装

Download | CMake

t1:cmake 的 helloworld

t1文件夹中创建文件:

分别写入:

PROJECT (HELLO)  #文件名
SET(SRC_LIST main.c)   #设置变量
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})   #在cmake的时候输出一个消息
MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})   #作者这里代码错了,引用变量需要使用${}
//main.c
#include 
int main()
{
printf("Hello World from t1 Main!\n");
return 0;
}

在t1中构建:

cmake .
make

即可产生可执行文件hello,然后执行:

./hello

即可输出

语句解释:

PROJECT(projectname [CXX] [C] [Java])
这个指令隐式的定义了两个 cmake 变量:

_BINARY_DIR 以及_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,即使修改了工程名称,也不会影响这两个变量。如果使用了_SOURCE_DIR ,修改工程名称后,需要同时修改这些变量。

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

cmake:是为了生成编译所需的Makefile以及其他中间文件

make:构建工程

make clean:即可对构建结果进行清理,这里清理的就是可执行文件hello

t2:更好一点的 Hello World

外部构建和安装(make install),安装部分还不是特别理解

cmake practice笔记_第1张图片

从上到下CMakeLists、runhello.sh、CMakeLists、main依次为:

PROJECT (HELLO)
ADD_SUBDIRECTORY(src bin)
ADD_EXECUTABLE(hello main.c)
//main.c
#include 
int main()
{
printf("Hello World from t1 Main!\n");
return 0;
}

解释

ADD_SUBDIRECTORY(source_dir    [binary_dir]    [EXCLUDE_FROM_ALL])

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

如果不进行 bin 目录也就是[binary_dir]的指定,那么编译结果(包括中间结果)都将存放在
build/src 目录,指定 bin 目录后,相当于在编译时将 src 重命名 为 bin,所有的中间结果和目标二进制都将存放在 bin 目录。

我们可以通过 SET 指令重新定义 EXECUTABLE_OUTPUT_PATH 和LIBRARY_OUTPUT_PATH 变量来指定最终的目标二进制的位置(指最终生成的 hello 或者最终的共享库,不包含编译生成的中间文件)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

t3:静态库与动态库构建

任务:建立一个静态库和动态库,提供 HelloFunc 函数供其他程序编程使用,HelloFunc
向终端输出 Hello World 字符串。

 

cmake practice笔记_第2张图片

从上到下:

PROJECT(HELLOLIB)
ADD_SUBDIRECTORY(lib)
cmake_minimum_required(VERSION 3.10)
SET(LIBHELLO_SRC hello.c)
#这个最终版的CMakeLists在后面的静态库和共享库共存部分给出
#include “hello.h”
void HelloFunc()
{
printf(“Hello World\n”);
}
#ifndef HELLO_H
#define HELLO_H
#include 
void HelloFunc();
#endif

编译:

cmake ..
make

cmake practice笔记_第3张图片

可以看到生成libhello.so共享库

如果你要指定 libhello.so 生成的位置,可以通过在主工程文件 CMakeLists.txt 中修改 ADD_SUBDIRECTORY(lib)指令来指定一个编译输出位置或者在 lib/CMakeLists.txt 中添加SET(LIBRARY_OUTPUT_PATH <路径>)来指定一个新的位置。

ADD_LIBRARY(libname   [SHARED|STATIC|MODULE]    [EXCLUDE_FROM_ALL]
source1 source2 ... sourceN):

你不需要写全 libhello.so,只需要填写 hello 即可,cmake 系统会自动为你生成
libhello.X

类型有三种:
SHARED,动态库
STATIC,静态库

MODULE,在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待。


EXCLUDE_FROM_ALL 参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手
工构建。

同名的共享和静态库共存:

上面的步骤(lib中的CMakeLists.txt)添加的是共享库,如果我们要添加静态库就需要把关键字改为STATIC:ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})

但是如果直接加上这句话

就会报错:

 cmake practice笔记_第4张图片

因为hello作为一个target不能重名,虽然我们可以通过改名字的方式如:

将两者区分开 ,但是这种结果显示不是我们想要的,我们需要的是名字相同的静态库和动态库,因为 target 名称是唯一的,所以,我们肯定不能通过 ADD_LIBRARY 指令来实现了。这时候我们需要用到
另外一个指令:

SET_TARGET_PROPERTIES(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...):

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

在本例中,我们需要作的是向 lib/CMakeLists.txt 中添加一条:
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
这样,我们就可以同时得到 libhello.so/libhello.a 两个库了。

但是由于cmake 在构建一个新的 target 时,会尝试清理掉其他使用这个名字的库,因为,在构建libhello.a 时,就会清理掉 libhello.so.为了回避这个问题,比如再次使用SET_TARGET_PROPERTIES 定义CLEAN_DIRECT_OUTPUT 属性。

即加入SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")

所以最终的CMakeLists.txt这样:

cmake_minimum_required(VERSION 3.10)
SET(LIBHELLO_SRC hello.c)

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})  #共享库
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})  #静态库

SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")
#下面两行为作者说要进一步进行修改的,不然就不会出现同名的.so和.a文件,但是我并不需要加上下面两步就可以出现。
# SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
# SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)

即可同时构建,如下图:

cmake practice笔记_第5张图片

 有libhello.so和libhello.a

为了实现动态库版本号,我们仍然需要使用 SET_TARGET_PROPERTIES 指令。
具体使用方法如下:
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
VERSION 指代动态库版本,SOVERSION 指代 API 版本。
将上述指令加入 lib/CMakeLists.txt 中,重新构建看看结果。
在 build/lib 目录会生成:
libhello.so.1.2
libhello.so.1->libhello.so.1.2
libhello.so ->libhello.so.1
 

最后安装库:

库的安装是为了让别的工程项目也调用自己写好的函数,这里我们的函数就是一个简单的输出hello的效果。

我们向 lib/CMakeLists.txt 中添加如下指令:

INSTALL(TARGETS hello hello_staticLIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include/hello)

注意,静态库要使用 ARCHIVE 关键字

cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
make install

这里makeinstall报错:

加一个sudo即可

我们就可以将头文件和共享库分别安装到系统目录/usr/include/hello和/usr/lib 中了。

cmake practice笔记_第6张图片 cmake practice笔记_第7张图片

t4:如何使用外部共享库和头文件

cmake practice笔记_第8张图片

 依次为:

cmake_minimum_required(VERSION 3.10)
PROJECT(NEWHELLO)
ADD_SUBDIRECTORY(src)
cmake_minimum_required(VERSION 3.10)
ADD_EXECUTABLE(main main.c)
#include 
int main()
{
HelloFunc();
return 0;
}

然后编译

cmake ..
make

然后在编译的时候报错

原因是并没有位于系统标准的头文件路径,我们可以include 来解决,但是这并不是我们想要的。

所以使用:

INCLUDE_DIRECTORIES(  [AFTER|BEFORE]   [SYSTEM]    dir1 dir2 ...):

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

中包含了空格,可以使用双引号将它括起来。

现在我们在src/CMakeLists.txt中添加:

INCLUDE_DIRECTORIES(/usr/include/hello)

此时编译又出现新问题

原因是我们并没有 link 到共享库 libhello 上

所以还需要加上

TARGET_LINK_LIBRARIES(target library1
library2
...):

可以用来为 target 添加需要链接的共享库

向src/CMakeLists.txt 中添加如下指令:

TARGET_LINK_LIBRARIES(main hello)
也可以写成
TARGET_LINK_LIBRARIES(main libhello.so)

这里的 hello 指的是我们上一节构建的共享库 libhello

再进行编译即可通过

build/src下产生可执行文件main

你可能感兴趣的:(#,cmake,c++,cmake)