cmake practice学习总结

目录

  • 1 概要
  • 2 小试牛刀
    • 2.1 内部构建(in-source build)
      • 2.1.1 准备工作
      • 2.1.2 开始构建
      • 2.1.3 内部构建与外部构建
    • 2.2 外部构建(out-of-source build)
      • 2.2.1 准备工作
      • 2.2.2 开始构建
  • 3 静态库与动态库构建
    • 3.1 建立共享库
    • 3.2 编译共享库
    • 3.3 添加静态库
    • 3.4 动态库版本号
    • 3.5 安装共享库和头文件
  • 4 使用外部共享库和头文件
    • 4.1 准备工作
  • 5 cmake语法
    • 5.1 基本语法规则
    • 5.2 cmake常用指令
  • 6 Find模块的使用
    • 6.1 直接法
    • 6.2 find_package

1 概要

2 小试牛刀

这一节中,我们通过一个最简单的例子Helloworld来演示cmake完整的构建过程。

2.1 内部构建(in-source build)

2.1.1 准备工作

新建一个项目文件夹,进入该文件夹并新建两个文件,分别为main.cCMakeLists.txt:其内容分别如下:

// Project/main.c

#include 
int main(){
     
	printf("Hello World!\n");
	return 0;
}
// Project/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)

project(HELLO)

add_executable(hello main.c)

2.1.2 开始构建

构建只需要在命令行中依此输入以下两条命令:

cmake .

第一条命令运行后,你会发现目录下生成了CMakeFiles, CMakeCache.txt, cmake_install.cmake等文件,这些文件是中间生成文件,你可以不用去理会;除了上述文件,它自动生成了Makefile。

make

make命令运行后,我们需要的目标文件hello已经构建完成,通过输入下述命令你可以运行它:

./hello

2.1.3 内部构建与外部构建

上面的例子展示的是“内部构建”,相信看到生成的临时文件比您的代码文件还要多的时候,估计这辈子你都不希望再使用内部构建,在2.2小节中我们对上述项目进行外部构建。

2.2 外部构建(out-of-source build)

外部构建会使得项目更具可读性,通过外部构建,我们得到的项目目录结构如下:

Project/
......src/                           // 放置工程源代码
.........main.c
.........CMakeLists.txt
......build/                         // 进行外部编译
.........bin/
............hello
......doc/                           // 放置文档
.........hello.txt
......COPYRIGHT
......README
......runhello.sh

2.2.1 准备工作

  1. 新建工程文件夹;
  2. 在工程目录下新建子文件夹src;
  3. 将2.1.1中的main.c复制到src目录下;
  4. 在src中新建CMakeLists.txt,内容如下:
add_executable(hello main.c)
  1. 在工程目录下新建CMakeLists.txt,内容如下:
cmake_minimum_required(VERSION 2.8)

project(HELLO)

add_subdirectary(src bin)

2.2.2 开始构建

  1. 新建build文件夹并进入,依此运行:
cmake .
make

构建完成后,你会发现生成的目标文件hello位于build/bin目录中。

3 静态库与动态库构建

本节的任务:

  1. 建立一个静态库和动态库,提供HelloFunc函数供其他程序编程使用(HelloFunc向终端输出Hello World字符串);
  2. 安装头文件与共享库。

3.1 建立共享库

  1. 新建项目文件夹
  2. 在项目目录中建立CMakeLists.txt,内容如下:
cmake_minimum_required(VERSION 2.8)

project(HELLOLIB)

add_subdirectory(lib)
  1. 新建lib子文件夹并建立两个源文件hello.h和hello.c
#ifndef HELLO_H
#define HELLO_H
#include 
void HelloFunc();
#endif
#include "hello.h"
void HelloFunc(){
     
	printf("Hello World\n");
}
  1. 在lib目录下建立CMakeLists.txt,内容如下:
set(LIBHELLO hello.c)
add_library(hello SHARED ${
     LIBHELLO_SRC})

3.2 编译共享库

  1. 建立一个build目录,在build目录下运行:
cmake .
make

这时,你就可以在lib目录得到一个libhello.so,这就是我们期望的共享库。

3.3 添加静态库

尝试一:使用add_library指令在生成动态库的基础上再为工程添加一个静态库(参数STATIC),按照习惯,静态库名字和动态库名字一致,只不过后缀是.a。但是外部构建后发现,没有生成静态库,这是因为构建生成的目标文件不能同名。

add_library(hello STATIC ${
     LIBHELLO_SRC})

add_library(hello_static STATIC ${
     LIBHELLO_SRC})
// 这样就可以构建一个libhello_static.a的静态库,但我们需要的是和动态库同名的静态库

尝试二:因为我们想要生成名字相同的静态库和动态库,这样的话add_library指令中target名称势必一样,因此我们借助与set_target_properties指令,设置静态库的OUTPUT_NAME属性为hello。构建之后我们发现生成了静态库libhello.a,但动态库libhello.so消失了,这是因为cmake在构建一个新的target时,会尝试清理掉其他使用这个名字的库。

set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")

尝试三:为了回避这个问题,我们同时修改静态库和动态库的CLEAN_DIRECT_OUTPUT属性为1。

set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)

3.4 动态库版本号

set_target_properties(hello PROPERTIES VERSION 1.2 SOVERSION 1)
// VERSION指代动态库版本号,SOVERSION指代API版本

3.5 安装共享库和头文件

4 使用外部共享库和头文件

4.1 准备工作

  1. 新建项目文件夹;
  2. 建立src子文件夹,编写源文件main.c,内容如下:
#include 

int main(){
     
	HelloFunc();
	return 0;
}
  1. 编写工程主文件CMakeLists.txt;
cmake_minimum_required(VERSION 2.8)

project(NEWHELLO)

add_subdirectory(src)
  1. 编写src/CMakeLists.txt;
add_executable(main main.c)
include_directories(/user/include/hello)
target_link_libraries(main libhello.so)  // 链接到动态库
target_link_libraries(main libhello.a)   // 链接到静态库

5 cmake语法

5.1 基本语法规则

  1. 变量使用${}方式取值,但是在if控制语句中是直接使用变量名;
  2. 指令{参数1 参数2 …}参数使用括弧括起,参数之间使用空格分号分开;
  3. 指令是大小写无关的,参数和变量是大小写相关的。

5.2 cmake常用指令

project指令的语法是:

project(projectname [CXX] [C] [Jave])
/*
作用:
1. 定义工程名称;
2. 指定工程支持的语言,支持的语言列表可以忽略,缺省情况表示支持所有语言;
3. 隐式地定义了两个cmake变量:_BINARY_DIR和_SOURCE_DIR;
补充:关于作用3,cmake系统也帮我们预定义了PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,它们的值和_BINARY_DIR和   	_SOURCE_DIR相同,但我们推荐使用前者,这样即使修改了工程名称,也不影响这两个变量。
*/

set指令的语法是:

set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
/*
作用:
4. 定义变量
*/

message指令的语法是:

message([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)
/*
作用:
向终端输出用户定义的信息,包含三种类型:
SEND_ERROR:产生错误,生成过程被跳过;
STATUS:输出前缀为-的信息; 
FATAL_ERROR:立即终止所有cmake过程。
*/

add_executable指令的语法是:

add_exectable(hello ${
     SRC_LIST})
/*
作用:
生成一个文件名为hello的可执行文件
*/

add_subdirectory指令的语法是:

add_subdirectory(source_dir [binary_dir] [EXECLUDE_FROM_ALL])
/*
作用:
向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置
*/

add_library指令的语法是:

add_library(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
/*
作用:
生成库文件,具体的库文件类型有三种:
SHARED:动态库
STATIC:静态库
MODULE:在使用dyld的系统有效,如果不支持dyld,则被当作SHARED对待

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

set_target_properties指令的语法是:

set_target_properties(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ...)
/*
作用:
1. 设置输出名称;
2. 对于动态库,还可以用来指定动态库版本和API版本
*/

get_target_property指令的语法是:

get_target_property(VAR target property)
/*
作用:
将库文件property属性赋值到变量VAR中,例如:
get_target_property(${OUTPUT_VALUE} hello OUTPUT_NAME)
*/

include_directories指令的语法是:

include_directories([AFTER | BEFORE] [SYSTEM] dir1 dir2 ...)
/*
作用:用来向工程添加多个特定的头文件搜索路径;
注意:默认的行为是追加到当前头文件搜索路径的后面,可以通过以下两种方式控制搜索路径添加的方式:
1. CMAKE_INCLUDE_DIRECTORIES_BEFORE,通过set这个cmake变量为on,可以将添加的头文件搜索路径放在已有路径的前面;
2. 通过AFTER或者BEFORE参数,也可以控制是追加还是置前。
*/

link_directories指令的语法是:

link_directories(dir1 dir2 ...)
/*
作用:添加非标准的共享库搜索路径
*/

target_link_libraries指令的语法是:

target_link_libraries(target library1 <debug | optimized> library2 ...)
/*
作用:用来为target添加需要链接的共享库
*/

install指令的语法是:

1. 目标文件的安装:
install(TARGET targets ... [[ARCHIVE | LIBRARY | RUNTIME] [DESTINATION <dir>] [PERMISSION permissions ...] [CONFIGURATION [Debug | Release | ...]] [COMPONENT <component>] [OPTIONAL]] [...])
举例:
install(TARGET myrun mylib mystaticlib
		RUNTIME DESTINATION bin
		LIBRARY DESTINATION lib
		ARCHIVE DESTINATION libstatic)
该指令会将:
可执行二进制安装到${
     CMAKE_INSTALL_PREFIX}/bin目录
动态库libmylib安装到${
     CMAKE_INSTALL_PREFIX}/lib目录
静态库libmystaticlib安装到${
     CMAKE_INSTALL_PREFIX}/libstatic目录
注意:CMAKE_INSTALL_PREFIX变量的配置需要用到指令为:
	cmake -D CMAKE_INSTALL_PREFIX=/usr .
2. 普通文件的安装:

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

6 Find模块的使用

在涉及到第三方库的程序开发中,往往需要使用find_package指令来简化头文件和共享库的搜索过程。例如,假设main.c文件中include了第三方库curl,则在构建过程中需要指定头文件和共享库的地址,一般来说这有两种解决方式:

6.1 直接法

直接通过include_directories和target_link_libraries指令添加:

// src/CMakeLists.txt
include_directories(/user/include)
target_link_libraries(curltest curl)

这种方法不仅繁琐,而且不要你清楚地知道头文件的存储路径。

6.2 find_package

find_package(CURL)
if(CURL_FOUND)
	include_directories(${
     CURL_INCLUDE_DIR})
	target_link_libraries(curltest ${
     CURL_LIBRARY})
else(CURL_FOUND)
	message(FATAL_ERROR "CURL library not found")
endif(CURL_FOUND)
/*
每一个模块都会定义以下几个变量
1. _FOUND
2. _INCLUDE_DIR 或 _INCLUDES
3. _LIBRARY 或 _LIBRARIES
*/

你可能感兴趣的:(cmake)