CMake基础+实战

1. cmake 初始

1.1  背景

      项目的诞生时间是2001年,官方网站www.cmake.org 

1.2  特点

     1. 开放源代码

     2. 跨平台,并可生成native 编译配置文件,在Linux/Unix 平台,生成 makefile,在苹果平台,可以生成xcode,在 Windows 平台,可以生成 MSVC 的工程文件。

     3. 能够管理大型项目, KDE4 就是最好的证明

     4. 高效率 ,比 使用 autotools 高

     5. 可扩展,

1.3  问题

    1, cmake 很简单,但绝对没有听起来或者想象中那么简单。

    2, cmake 跟已有体系的配合并不是特别理想,比如pkgconfig,您在实际使用中会有所体会,虽然有一些扩展可以使用,但并不理想。

 

1.4   建议

   1, 如果你的工程只有几个文件,直接编写Makefile 是最好的选择。

   2, 如果仅仅使用qt 编程,没有必要使用 cmake,因为qmake 管理 Qt 工程的专业性和自动化程度比cmake 要高很多。

1.5 cmake 安装  及 版本要求

$ cmake --version
cmake version 3.19.4

2.  CMake 常用指令

我们将按照 cmake man page 的顺序来介绍各种指令,不再推荐使用的指令将不再介绍

2.1 基本指令

1.  ADD_DEFINITIONS / add_definitions

     向C/C++ 编译器添加 -D 定义,比如:

ADD_DEFINITIONS(-DENABLE_DEBUG     -DABC),参数之间用空格分割。

如果你的代码中定义了#ifdef ENABLE_DEBUG #endif,这个代码块就会生效。

如果要添加其他的编译器开关,可以通过 CMAKE_C_FLAGS 变量和CMAKE_CXX_FLAGS 变量设置。

    2. ADD_DEPENDENCIES / add_dependencies

定义target 依赖的其他target,确保在编译本target 之前,其他的target 已经被构建。

ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)

3. ADD_EXECUTABLE / add_executable

ADD_LIBRARY / add_library

ADD_SUBDIRECTORY / add_subdirectory

4. ADD_TEST / add_test   ;   enable_testing()

ENABLE_TESTING 指令用来控制 Makefile 是否构建test 目标,涉及工程所有目录。语

法很简单,没有任何参数,ENABLE_TESTING(),一般情况这个指令放在工程的主 CMakeLists.txt 中.

ADD_TEST 指令的语法是:

ADD_TEST(testname Exename arg1 arg2 ...)

testname 是自定义的test 名称,Exename 可以是构建的目标文件也可以是外部脚本等等。后面连接传递给可执行文件的参数。

5. AUX_SOURCE_DIRECTORY / aux_source_directory()

AUX_SOURCE_DIRECTORY(dir VARIABLE)

作用是发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表。因为目前cmake 还不能自动发现新添加的源文件。

比如

AUX_SOURCE_DIRECTORY(. SRC_LIST)

ADD_EXECUTABLE(main ${SRC_LIST})

你也可以通过后面提到的FOREACH 指令来处理这个LIST

6. CMAKE_MINIMUM_REQUIRED  /  cmake_minimum_required()

其语法为CMAKE_MINIMUM_REQUIRED(VERSION versionNumber [FATAL_ERROR])

比如CMAKE_MINIMUM_REQUIRED(VERSION 2.5 FATAL_ERROR)

如果cmake 版本小与2.5,则出现严重错误,整个过程中止。

7. EXEC_PROGRAM  /  exec_program()

在 CMakeLists.txt 处理过程中执行命令,并不会在生成的 Makefile 中执行。具体语法为:

EXEC_PROGRAM(Executable [directory in which to run]

[ARGS <arguments to executable>]

[OUTPUT_VARIABLE <var>]

[RETURN_VALUE <var>])

 

用于在指定的目录运行某个程序,通过 ARGS 添加参数,如果要获取输出和返回值,可通过 OUTPUT_VARIABLE 和RETURN_VALUE 分别定义两个变量.

这个指令可以帮助你在CMakeLists.txt 处理过程中支持任何命令,比如根据系统情况去修改代码文件等等。

举个简单的例子,我们要在src 目录执行 ls 命令,并把结果和返回值存下来。

可以直接在src/CMakeLists.txt 中添加:

EXEC_PROGRAM(ls ARGS "*.c" OUTPUT_VARIABLE LS_OUTPUT RETURN_VALUE LS_RVALUE)

IF(not LS_RVALUE)

MESSAGE(STATUS "ls result: " ${LS_OUTPUT})

ENDIF(not LS_RVALUE)

在 cmake 生成 Makefile 的过程中,就会执行ls 命令,如果返回0,则说明成功执行,那么就输出ls *.c 的结果。

8. FILE 指令 

文件操作指令,基本语法为:

FILE(WRITE filename "message to write"... )

FILE(APPEND filename "message to write"... )

FILE(READ filename variable)

FILE(GLOB variable [RELATIVE path] [globbing expressions]...)

FILE(GLOB_RECURSE variable [RELATIVE path] [globbing expressions]...)

FILE(REMOVE [directory]...)

FILE(REMOVE_RECURSE [directory]...)

FILE(MAKE_DIRECTORY [directory]...)

FILE(RELATIVE_PATH variable directory file)

FILE(TO_CMAKE_PATH path result)

FILE(TO_NATIVE_PATH path result)

9. INCLUDE  / include()

用来载入CMakeLists.txt 文件,也用于载入预定义的cmake 模块. I

NCLUDE(file1 [OPTIONAL])

INCLUDE(module [OPTIONAL])

 

OPTIONAL 参数的作用是文件不存在也不会产生错误。

你可以指定载入一个文件,如果定义的是一个模块,那么将在 CMAKE_MODULE_PATH 中搜索这个模块并载入。

载入的内容将在处理到INCLUDE 语句 时 直接执行。

2.2  INSTALL / install()  

2.3 FIND_指令

FIND_FILE(<VAR> name1 path1 path2 ...)

VAR 变量代表找到的文件全路径,包含文件名

 

FIND_LIBRARY(<VAR> name1 path1 path2 ...)

VAR 变量表示找到的库全路径,包含库文件名

 

FIND_PATH(<VAR> name1 path1 path2 ...)

VAR 变量代表包含这个文件的路径。

 

FIND_PROGRAM(<VAR> name1 path1 path2 ...)

VAR 变量代表包含这个程序的全路径。

 

FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS] [componets...]])

用来调用预定义在CMAKE_MODULE_PATH 下的Find<name>.cmake 模块,你也可以自己定义Find<name>模块,通过SET(CMAKE_MODULE_PATH dir)将其放入工程的某个目录中供工程使用,我们在后面的章节会详细介绍 FIND_PACKAGE 的使用方法和 Find 模块的

编写。

 

FIND_LIBRARY 示例:

FIND_LIBRARY(libX X11 /usr/lib)

IF(NOT libX)

MESSAGE(FATAL_ERROR “libX not found”)

ENDIF(NOT libX)

2.4 控制指令

1. IF  指令 ,

IF(expression)

...

...

ELSE(expression)

...

...

ENDIF(expression)

凡是出现IF 的地方一定要有对应的ENDIF.出现 ELSEIF 的地方,ENDIF 是可选的。

表达式的使用方法如下:

IF(var),如果变量不是:空,0,N, NO, OFF, FALSE, NOTFOUND 或 <var>_NOTFOUND 时,表达式为真。

IF(NOT var ),与上述条件相反。

IF(var1 AND var2),当两个变量都为真是为真。

IF(var1 OR var2),当两个变量其中一个为真时为真。

IF(COMMAND cmd),当给定的 cmd 确实是命令并可以调用是为真。

IF(EXISTS dir)或者IF(EXISTS file),当目录名或者文件名存在时为真。

IF(file1 IS_NEWER_THAN file2),当file1 比file2 新,或者file1/file2 其中有一个不存在时为真,文件名请使用完整路径。

IF(IS_DIRECTORY dirname),当 dirname 是目录时,为真。

IF(variable MATCHES regex)

IF(string MATCHES regex)

IF(DEFINED variable),如果变量被定义,为真。

 

例子, 判断平台差异:

IF(WIN32)

MESSAGE(STATUS “This is windows.”)

#作一些Windows 相关的操作

ELSE(WIN32)

MESSAGE(STATUS “This is not windows”)

#作一些非Windows 相关的操作

ENDIF(WIN32)

 

可以SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)

 

这时候就可以写成:

IF(WIN32)

ELSE()

ENDIF()

 

如果配合ELSEIF 使用,可能的写法是这样:

IF(WIN32)

#do something related to WIN32

ELSEIF(UNIX)

#do something related to UNIX

ELSEIF(APPLE)

#do something related to APPLE

ENDIF(WIN32)

2. WHILE /while

WHILE 指令的语法是:

WHILE(condition)

COMMAND1(ARGS ...)

COMMAND2(ARGS ...)

...

ENDWHILE(condition)

其真假判断条件可以参考IF 指令。

3. FOREACH  / foreach

三种方式:

1. 列表

FOREACH(loop_var arg1 arg2 ...)

COMMAND1(ARGS ...)

COMMAND2(ARGS ...)

...

ENDFOREACH(loop_var)

 

像我们前面使用的AUX_SOURCE_DIRECTORY 的例子 AUX_SOURCE_DIRECTORY(. SRC_LIST) FOREACH(F ${SRC_LIST})

MESSAGE(${F})

ENDFOREACH(F)

2. 范围

FOREACH(loop_var RANGE total)

ENDFOREACH(loop_var)

从 0 到 total 以1为步进

 

举例如下:

FOREACH(VAR RANGE 10)

MESSAGE(${VAR})

ENDFOREACH(VAR)

最终得到的输出是:

0

1

2

3

...

10

3, 范围和步进

FOREACH(loop_var RANGE start stop [step])

ENDFOREACH(loop_var)

从 start 开始到stop 结束,以step 为步进,

 

举例如下

FOREACH(A RANGE 5 15 3)

MESSAGE(${A})

ENDFOREACH(A)

最终得到的结果是:

5

8

11

14

 

这个指令需要注意的是,直到 遇到ENDFOREACH 指令,整个语句块才会得到真正的执行。

 

 

 

 

 

 

 

 

 

 

 

 

3   小例子 实战 ,仅为了学习语法

3.1  helloworld

创建目录t1 

创建文件t1/main.c  和 t1/CMakeLists.txt

开始构建:

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

执行完后 生成了 Makefile,  在这个目录里 执行:

make 命令

如果你需要看到make 构建的详细过程?  可以使用如下:

make VERBOSE=1  或 VERBOSE=1 make 

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

./hello

3.1. 2 简单的解释

上面例子中的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})

 

PROJECT / project

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

你可以用这个指令定义工程名称,并可指定工程支持的语言,支持的语言列表是可以忽略的,默认情况表示支持所有语言。这个指令隐式的定义了两个 cmake 变量: <projectname>_BINARY_DIR 以及<projectname>_SOURCE_DIR,这里就是 HELLO_BINARY_DIR 和HELLO_SOURCE_DIR

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

建议 以后直接使用PROJECT_BINARY_DIR,PROJECT_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" ...)

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

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

 

ADD_EXECUTABLE /add_executable

ADD_EXECUTABLE(hello ${SRC_LIST})

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

 

3.2 更好一点的 Hello World   (安装 FILES、PROGRAMS、DIRECTORY)等

创建目录 t2

从本小节开始,后面所有的构建我们都将采用 out-of-source 外部构建,约定的构建目录是工程目录下的build 自录。

 

本小节的任务是让前面的Hello World 更像一个工程,我们需要作的是:

1,为工程添加一个子目录src,用来放置工程源代码;

2,添加一个子目录doc,用来放置这个工程的文档hello.txt

3,在工程目录添加文本文件COPYRIGHT, README;

4,在工程目录添加一个runhello.sh 脚本,用来调用hello 二进制

4,将构建后的目标文件放入构建目录的bin 子目录;

5,最终安装这些文件:将hello 二进制与runhello.sh 安装至/usr/bin,将doc 目录的内容以及COPYRIGHT/README 安装到/usr/share/doc/cmake/t2

 

将 t2 工程的 CMakeLists.txt 修改为:

PROJECT(HELLO)

ADD_SUBDIRECTORY(src bin)

3.2.2 解释

ADD_SUBDIRECTORY / add_subdirectory

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

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

指定 bin 目录后,相当于在编译时将src 重命名为bin,所有的中间结果和目标二进制都将存放在bin 目录。

换个地方保存 目标二进制 ?

可以通过SET 指令重新定义:

EXECUTABLE_OUTPUT_PATH 和LIBRARY_OUTPUT_PATH 变量来指定最终的目标二进制的位置(指最终生成的hello 或者最终的共享库

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

 

如何安装? make install

即使最简单的手工编写的Makefile,看起来也是这个样子的:

DESTDIR=

install:

mkdir -p $(DESTDIR)/usr/bin install -m 755 hello $(DESTDIR)/usr/bin

 

你可以通过:

make install

将 hello 直接安装到/usr/bin 目录,也可以通过make install DESTDIR=/tmp/test 将他安装在

/tmp/test/usr/bin 目录,打包时这个方式经常被使用。

cmake 如何 安装呢?

这里需要引入一个新的cmake 指令 INSTALL 和一个非常有用的变量

CMAKE_INSTALL_PREFIX

CMAKE_INSTALL_PREFIX 变量类似于configure 脚本的 –prefix,常见的使用方法看起来是这个样子:

cmake -DCMAKE_INSTALL_PREFIX=/usr .

 

指令 INSTALL/ install

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

INSTALL 指令包含了各种安装类型,我们需要一个个分开解释:

目标文件的安装:

INSTALL(TARGETS targets...

[[ARCHIVE|LIBRARY|RUNTIME]

[DESTINATION <dir>]

[PERMISSIONS permissions...]

[CONFIGURATIONS

[Debug|Release|...]]

[COMPONENT <component>]

[OPTIONAL]

] [...])

 

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

 

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

 

DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候 CMAKE_INSTALL_PREFIX 其实就无效了。如果你希望使用CMAKE_INSTALL_PREFIX 来

 

定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是 ${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>

 

 

举个简单的例子:

INSTALL(TARGETS myrun mylib mystaticlib

RUNTIME DESTINATION bin

LIBRARY DESTINATION lib

ARCHIVE DESTINATION libstatic

)

 

上面的例子会将:

  • 可执行二进制myrun 安装到${CMAKE_INSTALL_PREFIX}/bin
  • 目录动态库libmylib 安装到${CMAKE_INSTALL_PREFIX}/lib 目录
  • 静态库libmystaticlib 安装到${CMAKE_INSTALL_PREFIX}/libstatic 目录

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

 

如果我没有定义CMAKE_INSTALL_PREFIX 会安装到什么地方?

你可以尝试以下,cmake ..;make;make install,你会发现 CMAKE_INSTALL_PREFIX 的默认定义是/usr/local

 

3.3  静态库与动态库构建, 并安装共享库和头文件

创建目录 t3

本节的任务:

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

2,安装头文件与共享库。

cd make/t3

mkdir lib

 

在 t3 目录下建立CMakeLists.txt,内容如下:

PROJECT(HELLOLIB)

ADD_SUBDIRECTORY(lib)

在 lib 目录下建立两个源文件hello.c 与hello.h

hello.c 内容如下:

#include "hello.h"

void hello_func(void) {

printf("Hello World!\n");

return;

}

 

 

hello.h 内容如下:

 

#ifndef HELLO_H_

#define HELLO_H_ (1)

#include <stdio.h>

void hello_func(void);

#endif

 

 

在 lib 目录下建立CMakeLists.txt,内容如下:

SET(LIBHELLO_SRC hello.c)

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})

 

怎么同时生成 同名的 动态库 和静态库?

在本例中,我们需要作的是向lib/CMakeLists.txt 中添加一条:

SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello") 这样,我们就可以同时得到libhello.so/libhello.a 两个库了。

动态库版本号?

按照规则,动态库是应该包含一个版本号的,我们可以看一下系统的动态库,一般情况是

libhello.so.1.2

libhello.so ->libhello.so.1

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

 

为了实现动态库版本号,我们仍然需要使用 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

 

3.3.3 安装共享库 和头文件 ?

利用上一节了解到的INSTALL 指令,我们向lib/CMakeLists.txt 中添加如下指令:

INSTALL(TARGETS hello hello_static

LIBRARY DESTINATION lib

ARCHIVE DESTINATION lib)

INSTALL(FILES hello.h DESTINATION include/hello)

 

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

 

通过:

cmake -DCMAKE_INSTALL_PREFIX=/usr ..

make

make install

 

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

 

3.3 使用外部 共享库 和 头文件 ?

创建目录t4

重复以前的步骤,建立src 目录,编写源文件main.c,内容如下:

#include "hello.h"

int main(void) {

hello_func();

return 0;

}

 

 

编写工程主文件CMakeLists.txt

PROJECT(NEWHELLO)

ADD_SUBDIRECTORY(src)

 

编写src/CMakeLists.txt

ADD_EXECUTABLE(main main.c)

INCLUDE_DIRECTORIES(/usr/include/hello)

TARGET_LINK_LIBRARIES(main hello)

# 或

#TARGET_LINK_LIBRARIES(main libhello.so)

 

进入build 目录重新进行构建。

cmake ..

make

让我们来检查一下main 的链接情况:

 

ldd bin/main

linux-gate.so.1 =>  (0xb7ee7000)

libhello.so.1 => /usr/lib/libhello.so.1 (0xb7ece000)

libc.so.6 => /lib/libc.so.6 (0xb7d77000)

/lib/ld-linux.so.2 (0xb7ee8000)

 

 

指令 :

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

默认的行为是追加到当前的头文件搜索路径的后面,你可以通过两种方式来进行控制搜索路径添加的方式:

1,CMAKE_INCLUDE_DIRECTORIES_BEFORE,通过SET 这个cmake 变量为on,可以将添加的头文件搜索路径放在已有路径的前面。

2,通过AFTER 或者BEFORE 参数,也可以控制是追加还是置前。

 

TARGET_LINK_LIBRARIES / target_link_libraries

TARGET_LINK_LIBRARIES(target library1

<debug | optimized> library2

...)

 

特殊的 环境变量 CMAKE_INCLUDE_PATH 和CMAKE_LIBRARY_PATH

果头文件没有存放在常规路径(/usr/include, /usr/local/include 等),则可以通过这些变量就行弥补。

我们以本例中的hello.h 为例,它存放在/usr/include/hello 目录,所以直接查找肯定是找不到的。

前面我们直接使用了绝对路径INCLUDE_DIRECTORIES(/usr/include/hello)告诉工程这个头文件目录。

 

为了将程序更智能一点,我们可以使用 CMAKE_INCLUDE_PATH 来进行,使用bash 的方法如下:

export CMAKE_INCLUDE_PATH=/usr/include/hello

然后在头文件中将INCLUDE_DIRECTORIES(/usr/include/hello)替换为:

FIND_PATH(myHeader hello.h)

IF(myHeader)

INCLUDE_DIRECTORIES(${myHeader})

ENDIF(myHeader)

 

3.4  cmake模块 的使用 

创建目录 t5

介绍系统预定义的Find 模块的使用以及自己编写Find 模块,

一般情况需要使用INCLUDE 指令显式的调用,

FIND_PACKAGE 指令,可以直接调用预定义的模块。

其实使用纯粹依靠cmake 本身提供的基本指令来管理工程是一件非常复杂的事情,所以, cmake 设计成了可扩展的架构,可以通过编写一些通用的模块来扩展cmake.

首先介绍一下cmake 提供的 FindCURL 模块的使用。

 

建立src 目录,并建立src/main.c,内容如下:

 

/*

* get the content of www.baidu.com and write to /tmp/curl-test

*/

#include <curl/curl.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

FILE *fp = NULL;

int write_data(void *ptr, size_t size, size_t nmemb, void *stream) {

 int written = fwrite(ptr, size, nmemb, (FILE *)fp);

 return written;

}

int main(void) {

 const char * path = "/tmp/curl-test";

 const char * mode = "w";

 fp = fopen(path, mode);

 curl_global_init(CURL_GLOBAL_ALL);

 CURL *curl = curl_easy_init();

 curl_easy_setopt(curl, CURLOPT_URL, "http://www.baidu.com");

 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);

 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);

 CURLcode res = curl_easy_perform(curl);

 curl_easy_cleanup(curl);

 return 0;

}

 

这段代码的作用是通过curl 取回www.linux-ren.org 的首页并写入/tmp/curl-test文件中。

 

 

建立主工程文件CMakeLists.txt

PROJECT(CURLTEST)

ADD_SUBDIRECTORY(src)

 

建立src/CMakeLists.txt

ADD_EXECUTABLE(curltest main.c)

 

现在自然是没办法编译的,我们需要添加 curl 的头文件路径和库文件。

 

方法1:

直接通过INCLUDE_DIRECTORIES 和TARGET_LINK_LIBRARIES 指令添加:我们可以直接在src/CMakeLists.txt 中添加:

INCLUDE_DIRECTORIES(/usr/include)

TARGET_LINK_LIBRARIES(curltest curl)

 

然后建立build 目录进行外部构建即可。

 

现在我们要探讨的是使用cmake 提供的 FindCURL 模块。

 

方法2,使用FindCURL 模块。

 

向 src/CMakeLists.txt 中添加:

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)

 

对于系统预定义的Find<name>.cmake 模块,使用方法一般如上例所示:

 

每一个模块都会定义以下几个变量

  • <name>_FOUND
  • <name>_INCLUDE_DIR or <name>_INCLUDES
  • <name>_LIBRARY or <name>_LIBRARIES

 

3.5  自定义 cmake 模块

创建目录 t6

t6 示例中演示如何自定义FindHELLO 模块并使用这个模块构建工程

建立cmake 目录用于存放我们自己定义的FindHELLO.cmake 模块,同时建立src 目录,用于存放我们的源文件。

 

1,定义 cmake/FindHELLO.cmake 模块

FIND_PATH(HELLO_INCLUDE_DIR hello.h /usr/include/hello /usr/local/include/hello)

FIND_LIBRARY(HELLO_LIBRARY NAMES hello PATH /usr/lib /usr/local/lib)

 

IF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)

SET(HELLO_FOUND TRUE)

ENDIF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)

 

IF (HELLO_FOUND)

IF (NOT HELLO_FIND_QUIETLY)

MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}")

ENDIF (NOT HELLO_FIND_QUIETLY)

ELSE (HELLO_FOUND)

IF (HELLO_FIND_REQUIRED)

MESSAGE(FATAL_ERROR "Could not find hello library")

ENDIF (HELLO_FIND_REQUIRED)

ENDIF (HELLO_FOUND)

 

OK,下面建立src/main.c,内容为:

#include <hello.h>

int main()

{

HelloFunc();

return 0;

}

 

 

建立src/CMakeLists.txt 文件,内容如下:

FIND_PACKAGE(HELLO)

IF(HELLO_FOUND)

ADD_EXECUTABLE(hello main.c)

INCLUDE_DIRECTORIES(${HELLO_INCLUDE_DIR})

TARGET_LINK_LIBRARIES(hello ${HELLO_LIBRARY})

ENDIF(HELLO_FOUND)

 

我们在主工程文件CMakeLists.txt 中加入:

SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

 

 

 

 

 

 

5. 基本语法规则

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

2,指令(参数1 参数 2...)

    1. 参数使用括弧括起,参数之间使用空格或分号分开。

             以上面的ADD_EXECUTABLE 指令为例,如果存在另外一个 func.c 源文件,就要写成:

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

 

参数 要不要加 双引号?

SET(SRC_LIST main.c)也可以写成 SET(SRC_LIST “main.c”)  ;是没有区别的,但是假设一个源文件的文件名是 fu nc.c(文件名中间包含了空格)。

这种情况,就必须写成:

SET(SRC_LIST "fu nc.c")

3,指令是大小写无关的,参数和变量是大小写相关的

 

8 内部构建 与 外部构建

外部构建,如 t1 中  创建 build , 进入build ,执行 cmake ..

通过外部构建 ,HELLO_SOURCE_DIR  仍然指工程路径,即cmake/t1

HELLO_BINARY_DIR  即 cmake/t1/build

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值