CMake入门(三)

六、cmake 常用变量和常用环境变量

1.cmake变量引用的方式

cmake中使用${}进行变量的引用。在IF等语句中,是直接使用变量名而不通过${}取值

2.cmake自定义变量的方式

主要有隐式定义和显式定义两种

隐式定义: PROJECT指令,会隐式的定义<projectname>_BINARY_DIR 和<projectname>_SOURCE_DIR两个变量。

显式定义: SET 指令,构建一个自定义变量,比如: SET(HELLO_SRC main.SOURCE_PATH),PROJECT_BINARY_DIR就可以通过 ${HELLO_SRC}来引用这个自定义变量了

3.cmake常用变量

(1)

CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
<projectname>_BINARY_DIR

这三个变量指代的内容是一致的,如果是 in source 编译,指得就是工程顶层目录,如果是 out-of-source 编译,指的是工程编译发生的目录。PROJECT_BINARY_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。

(2)

CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
<projectname>_SOURCE_DIR

这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。

也就是在 in source 编译时,他跟 CMAKE_BINARY_DIR 等变量一致。 PROJECT_SOURCE_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。

(3)CMAKE_CURRENT_SOURCE_DIR

指的是当前处理的 CMakeLists.txt 所在的路径,比如上面我们提到的 src 子目录。

(4)CMAKE_CURRRENT_BINARY_DIR

如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of-source 编译,他指的是 target 编译目录。

使用我们上面提到的 ADD_SUBDIRECTORY(src bin)可以更改这个变量的值。

使用 SET(EXECUTABLE_OUTPUT_PATH )并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。

(5)CMAKE_CURRENT_LIST_FILE

输出调用这个变量的 CMakeLists.txt 的完整路径

(6)CMAKE_CURRENT_LIST_LINE

输出这个变量所在的行

(7)CMAKE_MODULE_PATH

这个变量用来定义自己的 cmake 模块所在的路径。如果你的工程比较复杂,有可能会自己编写一些 cmake 模块,这些 cmake 模块是随你的工程发布的,为了让 cmake 在处理CMakeLists.txt 时找到这些模块,你需要通过 SET 指令,将自己的 cmake 模块路径设置一下。

比如SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

这时候你就可以通过 INCLUDE 指令来调用自己的模块了。

(8)EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH

分别用来重新定义最终结果的存放目录,前面我们已经提到了这两个变量。

(9)PROJECT_NAME

返回通过 PROJECT 指令定义的项目名称。

4.cmake调用环境变量的方式

使用$ENV{NAME}指令就可以调用系统的环境变量了。比如 MESSAGE(STATUS “HOME dir: $ENV{HOME}”) 设置环境变量的方式是: SET(ENV{变量名} 值)

(1)CMAKE_INCLUDE_CURRENT_DIR

自动添加 CMAKE_CURRENT_BINARY_DIR 和 CMAKE_CURRENT_SOURCE_DIR 到当前处理 的CMakeLists.txt。相当于在每个 CMakeLists.txt 加入: INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})

(2)CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE

将工程提供的头文件目录始终至于系统头文件目录的前面,当你定义的头文件确实跟系统发生冲突时可以提供一些帮助。

(3)CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH 我们在上一节已经提及。

5.系统信息

CMAKE_MAJOR_VERSION,CMAKE 主版本号,比如 2.4.6 中的 2
CMAKE_MINOR_VERSION,CMAKE 次版本号,比如 2.4.6 中的 4
CMAKE_PATCH_VERSION,CMAKE 补丁等级,比如 2.4.6 中的 6
CMAKE_SYSTEM,系统名称,比如 Linux-2.6.22
CMAKE_SYSTEM_NAME,不包含版本的系统名,比如 Linux
CMAKE_SYSTEM_VERSION,系统版本,比如 2.6.22
CMAKE_SYSTEM_PROCESSOR,处理器名称,比如 i686.
UNIX,在所有的类 UNIX 平台为 TRUE,包括 OS X 和 cygwin
WIN32,在所有的 win32 平台为 TRUE,包括 cygwin

6.主要的开关选项

(1)CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS,用来控制IF ELSE 语句的书写方式,在下一节语法部分会讲到。

(2)BUILD_SHARED_LIBS

这个开关用来控制默认的库编译方式,如果不进行设置,使用 ADD_LIBRARY 并没有指定库类型的情况下,默认编译生成的库都是静态库。如果 SET(BUILD_SHARED_LIBS ON)后,默认生成的为动态库。

(3)CMAKE_C_FLAGS

设置C编译选项,也可以通过指令ADD_DEFINITIONS()添加。

(4)CMAKE_CXX_FLAGS

设置 C++编译选项,也可以通过指令 ADD_DEFINITIONS()添加。

7.小结

本章介绍了一些较常用的 cmake 变量,这些变量仅仅是所有 cmake 变量的很少一部分,目 前 cmake 的英文文档也是比较缺乏的,如果需要了解更多的 cmake 变量,更好的方式是阅 读一些成功项目的 cmake 工程文件,比如 KDE4 的代码。

七、复杂的例子:模块的使用和自定义模块

本章我们将着重介绍系统预定义的 Find 模块的使用以及自己编写 Find 模块,系统中提供了其他各种模块,一般情况需要使用 INCLUDE 指令显式的调用,FIND_PACKAGE 指令是一个特例,可以直接调用预定义的模块。

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

在本章,我们准备首先介绍一下 cmake 提供的 FindCURL 模块的使用。然后,基于我们前面的 libhello 共享库,编写一个 FindHello.cmake 模块。

1.使用 FindCURL 模块

在/backup/cmake 目录建立 t5 目录,用于存放我们的 CURL 的例子。

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

#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
FILE *fp;
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()
{
    const char * path = “/tmp/curl-test”;
    const char * mode = “w”;
    fp = fopen(path,mode);
    curl_global_init(CURL_GLOBAL_ALL);
    CURLcode res;
    CURL *curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, “http://www.linux-ren.org”);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
    res = curl_easy_perform(curl);
    curl_easy_cleanup(curl);
}

这段代码的作用是通过 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

你可以通过<name>_FOUND 来判断模块是否被找到,如果没有找到,按照工程的需要关闭某些特性、给出提醒或者中止编译,上面的例子就是报出致命错误并终止构建。

如果<name>_FOUND 为真,则将<name>_INCLUDE_DIR 加入INCLUDE_DIRECTORIES, 将<name>_LIBRARY 加入TARGET_LINK_LIBRARIES 中。

我们再来看一个复杂的例子,通过<name>_FOUND 来控制工程特性:

SET(mySources viewer.c)
SET(optionalSources)
SET(optionalLibs)
FIND_PACKAGE(JPEG)
IF(JPEG_FOUND)
    SET(optionalSources ${optionalSources} jpegview.c)
    INCLUDE_DIRECTORIES( ${JPEG_INCLUDE_DIR} )
    SET(optionalLibs ${optionalLibs} ${JPEG_LIBRARIES} )
    ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT)
ENDIF(JPEG_FOUND)

IF(PNG_FOUND)
    SET(optionalSources ${optionalSources} pngview.c)
    INCLUDE_DIRECTORIES( ${PNG_INCLUDE_DIR} )
    SET(optionalLibs ${optionalLibs} ${PNG_LIBRARIES} )
    ADD_DEFINITIONS(-DENABLE_PNG_SUPPORT)
ENDIF(PNG_FOUND)

ADD_EXECUTABLE(viewer ${mySources} ${optionalSources} )
TARGET_LINK_LIBRARIES(viewer ${optionalLibs}

通过判断系统是否提供了 JPEG 库来决定程序是否支持 JPEG 功能。

2.编写属于自己的 FindHello 模块。

我们在此前的 t3 实例中,演示了构建动态库、静态库的过程并进行了安装。

接下来,我们在 t6 示例中演示如何自定义 FindHELLO 模块并使用这个模块构建工程:

请在建立/backup/cmake/中建立 t6 目录,并在其中建立 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)

针对上面的模块让我们再来回顾一下 FIND_PACKAGE 指令:

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

前面的 CURL 例子中我们使用了最简单的 FIND_PACKAGE 指令,其实他可以使用多种参数, QUIET 参数,对应与我们编写的 FindHELLO 中的 HELLO_FIND_QUIETLY,如果不指定这个参数,就会执行: MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}") REQUIRED 参数,其含义是指这个共享库是否是工程必须的,如果使用了这个参数,说明这个链接库是必备库,如果找不到这个链接库,则工程不能编译。对应于FindHELLO.cmake 模块中的HELLO_FIND_REQUIRED 变量。

同样,我们在上面的模块中定义了 HELLO_FOUND, HELLO_INCLUDE_DIR,HELLO_LIBRARY 变量供开发者在 FIND_PACKAGE 指令中使用。 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)

为了能够让工程找到 FindHELLO.cmake 模块(存放在工程中的 cmake 目录) 我们在主工程文件 CMakeLists.txt 中加入:

SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

3.使用自定义的 FindHELLO 模块构建工程

仍然采用外部编译的方式,建立 build 目录,进入目录运行:

cmake ..

我们可以从输出中看到:

Found Hello: /usr/lib/libhello.so

如果我们把上面的 FIND_PACKAGE(HELLO)修改为 FIND_PACKAGE(HELLO QUIET),则不会看到上面的输出。

接下来就可以使用 make 命令构建工程,运行:

./src/hello 可以得到输出

Hello World。

说明工程成功构建。

4.如果没有找到 hello library 呢?

我们可以尝试将/usr/lib/libhello.x 移动到/tmp 目录,这样,按照 FindHELLO 模块的定义,就找不到 hello library 了,我们再来看一下构建结果:

cmake ..

仍然可以成功进行构建,但是这时候是没有办法编译的。

修改 FIND_PACKAGE(HELLO)为 FIND_PACKAGE(HELLO REQUIRED),将 hello library 定义为工程必须的共享库。

这时候再次运行 cmake ..

我们得到如下输出: CMake Error: Could not find hello library.

因为找不到 libhello.x,所以,整个 Makefile 生成过程被出错中止。

5.小结

在本节中,我们学习了如何使用系统提供的 Find模块并学习了自己编写 Find<NAME>模块以及如何在工程中使用这些模块。

CMake是一个跨平台的开源构建系统,它帮助开发者在多种操作系统上自动化软件项目的构建过程。以下是CMake入门的一些步骤和基本概念: 1. **安装CMake**:首先,你需要从CMake官网下载并安装适用于你的系统的版本。对于Windows、Mac OS和Linux都有预打包的二进制包可供选择。 2. **创建CMakeLists.txt**:这是CMake的核心,它是描述你的项目结构和依赖关系的文本文件。通常放在项目根目录下。 3. **配置生成器**:CMake支持多种构建系统,如Unix Makefiles、Visual Studio等。通过命令行指定`cmake -G`选项,告诉CMake你想要哪种生成器。 4. **生成构建文件**:运行`cmake`命令初始化CMake流程,生成对应的构建文件(如Makefile或Visual Studio解决方案文件)。 5. **构建项目**:根据选定的构建系统(如make或msbuild),运行`make`或`cmake --build .`命令来实际构建你的程序。 6. **添加变量和指令**:CMake提供了许多变量和函数,如设置源代码路径、添加库依赖、设置编译选项等,可以灵活调整构建配置。 7. **了解基本语法**:学习CMake的基本语法,如IF()条件语句、ADD_EXECUTABLE()添加可执行文件、INCLUDE()导入外部CMakeLists.txt等。 8. **实践项目**:通过参与一些小型项目或者使用官方的示例来熟悉CMake的工作流程。 **相关问题--** 1. CMake有哪些常见的错误及其解决办法是什么? 2. 如何在CMakeLists.txt中处理不同平台的差异? 3. 怎样在已经存在的CMake项目中添加新的模块或依赖?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒟蒻菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值