CMake之CMakeLists.txt编写入门

2023-05-16

自定义变量

主要有隐式定义和显式定义两种。
隐式定义的一个例子是PROJECT指令,它会隐式的定义< projectname >_BINARY_DIR< projectname >_SOURCE_DIR两个变量;显式定义使用SET指令构建自定义变量,比如:SET(HELLO_SRCmain.c)就可以通过${HELLO_SRC}来引用这个自定义变量了。

变量引用方式

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

常用变量

CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
< projectname >_BINARY_DIR
这三个变量指代的内容是一致的,如果是in-source编译,指得就是工程顶层目录;如果是out-of-source编译,指的是工程编译发生的目录。PROJECT_BINARY_DIR跟其它指令稍有区别,目前可以认为它们是一致的。

CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
< projectname >_SOURCE_DIR
这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。也就是在in-source编译时,他跟CMAKE_BINARY_DIR等变量一致。PROJECT_SOURCE_DIR跟其它指令稍有区别,目前可以认为它们是一致的。
(out-of-source build与in-source build相对,指是否在CMakeLists.txt所在目录进行编译。)

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

CMAKE_CURRRENT_BINARY_DIR
如果是in-source编译,它跟CMAKE_CURRENT_SOURCE_DIR一致;如果是out-of-source编译,指的是target编译目录。使用ADD_SUBDIRECTORY(src bin)可以更改这个变量的值。使用SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。

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

CMAKE_CURRENT_LIST_LINE
输出这个变量所在的行

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

EXECUTABLE_OUTPUT_PATH
新定义最终结果的存放目录

LIBRARY_OUTPUT_PATH
新定义最终结果的存放目录

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

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

系统信息

  • 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,包括OSX和cygwin
  • WIN32,在所有的Win32平台为TRUE,包括cygwin

主要的开关选项

  1. CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS
    用来控制IF ELSE语句的书写方式。

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

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

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

cMake常用指令

这里引入更多的cmake指令,为了编写的方便,将按照cmakeman page 的顺序介绍各种指令,不再推荐使用的指令将不再介绍。

基本指令

PROJECT(HELLO)
指定项目名称,生成的VC项目的名称,使用${HELLO_SOURCE_DIR}表示项目根目录。

INCLUDE_DIRECTORIES
指定头文件的搜索路径,相当于指定gcc的-I参数
INCLUDE_DIRECTORIES(${HELLO_SOURCE_DIR}/Hello) #增加Hello为include目录

TARGET_LINK_LIBRARIES
添加链接库,相同于指定-l参数
TARGET_LINK_LIBRARIES(demoHello) #将可执行文件与Hello连接成最终文件demo

LINK_DIRECTORIES
动态链接库或静态链接库的搜索路径,相当于gcc的-L参数
LINK_DIRECTORIES(${HELLO_BINARY_DIR}/Hello)#增加Hello为link目录

ADD_DEFINITIONS
向C/C++编译器添加-D定义,比如:
ADD_DEFINITIONS(-DENABLE_DEBUG-DABC)
参数之间用空格分割。如果代码中定义了:

#ifdef ENABLE_DEBUG

#endif

这个代码块就会生效。如果要添加其他的编译器开关,可以通过CMAKE_C_FLAGS变量和CMAKE_CXX_FLAGS变量设置。

ADD_DEPENDENCIES*
定义target依赖的其它target,确保在编译本target之前,其它的target已经被构建。ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)

ADD_EXECUTABLE
ADD_EXECUTABLE(helloDemo demo.cxx demo_b.cxx)
指定编译,好像也可以添加.o文件,将cxx编译成可执行文件

ADD_LIBRARY
ADD_LIBRARY(Hellohello.cxx) #将hello.cxx编译成静态库如libHello.a

ADD_SUBDIRECTORY
ADD_SUBDIRECTORY(Hello) #包含子目录

ADD_TEST
ENABLE_TESTING
ENABLE_TESTING指令用来控制Makefile是否构建test目标,涉及工程所有目录。语法很简单,没有任何参数,ENABLE_TESTING()一般放在工程的主CMakeLists.txt中。
ADD_TEST指令的语法是:ADD_TEST(testnameExename arg1 arg2 …)
testname是自定义的test名称,Exename可以是构建的目标文件也可以是外部脚本等等,后面连接传递给可执行文件的参数。

如果没有在同一个CMakeLists.txt中打开ENABLE_TESTING()指令,任何ADD_TEST都是无效的。比如前面的Helloworld例子,可以在工程主CMakeLists.txt中添加

ADD_TEST(mytest ${PROJECT_BINARY_DIR}/bin/main)
ENABLE_TESTING

生成Makefile后,就可以运行make test来执行测试了。

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

AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(main ${SRC_LIST})

可以通过后面提到的FOR EACH指令来处理这个LIST。

CMAKE_MINIMUM_REQUIRED
语法为CMAKE_MINIMUM_REQUIRED(VERSION versionNumber [FATAL_ERROR])
比如:CMAKE_MINIMUM_REQUIRED(VERSION 2.5 FATAL_ERROR)
如果cmake版本小与2.5,则出现严重错误,整个过程中止。

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_VARIABLERETURN_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的结果。关于IF语句,后面的控制指令会提到。

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

FILE(WRITEfilename "message to write"... )
FILE(APPENDfilename "message to write"... )
FILE(READfilename variable)
FILE(GLOBvariable [RELATIVE path] [globbing expression_r_rs]...)
FILE(GLOB_RECURSEvariable [RELATIVE path] [globbing expression_r_rs]...)
FILE(REMOVE[directory]...)
FILE(REMOVE_RECURSE[directory]...)
FILE(MAKE_DIRECTORY[directory]...)
FILE(RELATIVE_PATHvariable directory file)
FILE(TO_CMAKE_PATHpath result)
FILE(TO_NATIVE_PATHpath result)

INCLUDE指令
用来载入CMakeLists.txt文件,也用于载入预定义的cmake模块。

INCLUDE(file1[OPTIONAL])
INCLUDE(module[OPTIONAL])

OPTIONAL参数的作用是文件不存在也不会产生错误,可以指定载入一个文件,如果定义的是一个模块,那么将在CMAKE_MODULE_PATH中搜索这个模块并载入,载入的内容将在处理到INCLUDE语句是直接执行。

INSTALL指令

FIND_指令
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(libXX11 /usr/lib)
IF(NOT libX)
    MESSAGE(FATAL_ERROR "libX not found")
ENDIF(NOT libX)

控制指令

IF指令,基本语法为:

IF(expression_r_r)
    #THEN section.
    COMMAND1(ARGS…)
    COMMAND2(ARGS…)ELSE(expression_r_r)
    #ELSE section.
    COMMAND1(ARGS…)
    COMMAND2(ARGS…)ENDIF(expression_r_r)

另外一个指令是ELSEIF,总体把握一个原则,凡是出现IF的地方一定要有对应的ENDIF,出现ELSEIF的地方,ENDIF是可选的。表达式的使用方法如下:

IF(var)  如果变量不是:空, 0, N, NO, OFF, FALSE, NOTFOUND 或 <var>_NOTFOUND时,表达式为真。
IF(NOT var), 与上述条件相反。
IF(var1AND var2), 当两个变量都为真是为真。
IF(var1OR var2), 当两个变量其中一个为真时为真。
IF(COMMANDcmd), 当给定的cmd确实是命令并可以调用是为真。
IF(EXISTS dir) or IF(EXISTS file), 当目录名或者文件名存在时为真。
IF(file1IS_NEWER_THAN file2), 当file1比file2新,或者file1/file2其中有一个不存在时为真文件名请使用完整路径。
IF(IS_DIRECTORY dirname),  当dirname是目录时为真。
IF(variableMATCHES regex)

IF(string MATCHES regex) 当给定的变量或者字符串能够匹配正则表达式regex时为真。比如:

IF("hello" MATCHES "hello")
    MESSAGE("true")
ENDIF("hello" MATCHES "hello")
IF(variable LESS number)
IF(string LESS number)
IF(variable GREATER number)
IF(string GREATER number)
IF(variable EQUAL number)
IF(string EQUAL number)

数字比较表达式

IF(variable STRLESS string)
IF(string STRLESS string)
IF(variable STRGREATER string)
IF(string STRGREATER string)
IF(variable STREQUAL string)
IF(string STREQUAL string)

按照字母序的排列进行比较。

IF(DEFINED variable),如果变量被定义,为真。
一个小例子,用来判断平台差异:

IF(WIN32)
    MESSAGE(STATUS“This is windows.”) #作一些Windows相关的操作
ELSE(WIN32)
    MESSAGE(STATUS“This is not windows”) #作一些非Windows相关的操作
ENDIF(WIN32)

上述代码用来控制在不同的平台进行不同的控制,但是阅读起来却并不是那么舒服, ELSE(WIN32)之类的语句很容易引起歧义。
这就用到了我们在 常用变量 一节提到的CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS开关。可以SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTSON),这时候就可以写成:

IF(WIN32)

ELSE()

ENDIF()

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

IF(WIN32)
    #dosomething related to WIN32
ELSEIF(UNIX)
    #dosomething related to UNIX
ELSEIF(APPLE)
    #dosomething related to APPLE
ENDIF(WIN32)

WHILE指令

WHILE指令的语法是:

WHILE(condition)
    COMMAND1(ARGS…)
    COMMAND2(ARGS…)ENDWHILE(condition)

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

FOREACH指令

FOREACH指令的使用方法有三种形式:
(1)列表

FOREACH(loop_vararg1 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(VARRANGE 10)
    MESSAGE(${VAR})
ENDFOREACH(VAR)

最终得到的输出是:

0
1
2
3
4
5
6
7
8
9
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指令,整个语句块才会得到真正的执行。

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

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

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

首先介绍一下cmake提供的FindCURL模块的使用,然后基于前面的libhello共享库,编写一个FindHello.cmake模块。
(一)使用FindCURL模块
在/backup/cmake目录建立t5目录,用于存放CURL的例子。
建立src目录,并建立src/main.c,内容如下:

#include<curl/curl.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

FILE*fp;

intwrite_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);
    CURL coderes;
    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_DIRECTORIESTARGET_LINK_LIBRARIES指令添加:

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

INCLUDE_DIRECTORIES(/usr/include)
TARGET_LINK_LIBRARIES(curltestcurl)

然后建立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(mySourcesviewer.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功能。

(二)编写属于自己的FindHello模块
接下来在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_FOUNDTRUE)
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指令中使用。

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

#include<hello.h>
int main()
{
    HelloFunc();
    return 0;
}

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

FIND_PACKAGE(HELLO)
IF(HELLO_FOUND)
    ADD_EXECUTABLE(hellomain.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)

(三)使用自定义的FindHELLO模块构建工程
仍然采用外部编译的方式,建立build目录,进入目录运行:

cmake ..

我们可以从输出中看到:

FoundHello: /usr/lib/libhello.so

如果我们把上面的FIND_PACKAGE(HELLO)修改为FIND_PACKAGE(HELLO QUIET),

不会看到上面的输出。接下来就可以使用make命令构建工程,运行:

./src/hello

可以得到输出

HelloWorld

说明工程成功构建。

(四)如果没有找到hellolibrary呢?
我们可以尝试将/usr/lib/libhello.x移动到/tmp目录,这样按照FindHELLO模块的定义,找不到hellolibrary了,我们再来看一下构建结果:

cmake ..

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

修改FIND_PACKAGE(HELLO)FIND_PACKAGE(HELLO REQUIRED),将hellolibrary定义为工程必须的共享库。
这时候再次运行

cmake ..

我们得到如下输出:

CMakeError: Could not find hello library.

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

一些问题

1.怎样区分debug、release版本
建立debug/release两目录,分别在其中执行cmake -D CMAKE_BUILD_TYPE=Debug(或Release),需要编译不同版本时进入不同目录执行make即可:

Debug版会使用参数-g;
Release版使用-O3–DNDEBUG

另一种设置方法——例如DEBUG版设置编译参数DDEBUG

IF(DEBUG_mode)
    add_definitions(-DDEBUG)
ENDIF()

在执行cmake时增加参数即可,例如cmake -D DEBUG_mode=ON

2.怎样设置条件编译
例如debug版设置编译选项DEBUG,并且更改不应改变CMakelist.txt
使用option command,eg:

option(DEBUG_mode"ON for debug or OFF for release" ON)
IF(DEBUG_mode)
    add_definitions(-DDEBUG)
ENDIF()

使其生效的方法:首先cmake生成makefile,然后make edit_cache编辑编译选项;Linux下会打开一个文本框,可以更改,改完后再make生成目标文件——emacs不支持make edit_cache

局限:这种方法不能直接设置生成的makefile,而是必须使用命令在make前设置参数;对于debug、release版本,相当于需要两个目录,分别先cmake一次,然后分别makeedit_cache一次;

期望的效果:在执行cmake时直接通过参数指定一个开关项,生成相应的makefile

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CMake之CMakeLists.txt编写入门 的相关文章

  • 在ROS中基于颜色做简单的物体识别

    ROS无法直接进行图像处理 xff0c 需要借助于opencv xff0c 要使用cv bridge把ROS 的图像数据格式转为Opencv可以使用的数据格式 即是一个提供ROS和OpenCV库提供之间的接口的开发包 然后可以将opencv
  • Docker下运行GUI应用

    Docker本身的工作模式是命令行的 xff0c 因为主要的使用场景可能是做服务器后端方面的比较多 但有时候我们会有在docker容器里运行一些图形界面的软件 xff0c 比如使用ROS时候需要gazebo和rviz的可视化这时候就需要使用
  • Linux 烤机

    Linux 烧机测试 我发现这方面的知识比较少 xff0c 在Google只看到来自http blog jpps tcc edu tw的文章 好东西要珍惜 xff0c 特此转载 再一次对博主表示感谢 剛組裝了一部6核心的電腦 AMD 105
  • 小狼毫输入法

    找了一些五笔输入法 xff0c 都不太理想 xff0c 今天又找了一个输入法 xff1a 小狼毫 用了一下这个输入法 xff0c 带来了一些惊喜 这个输入法并不是一个新的输入法 xff0c 而是一个输入法框架 xff0c 集合了众多输入法
  • 关于航模的几点总结积累

    关于航模的几点积累 xff1a 一 关于机型 xff1a 1 固定翼飞行器分类 按外观 xff1a 像真机 xff0c 非像真机 按主翼位置 xff1a 上单翼 xff0c 中单翼 xff0c 下单翼 按动力来源 xff1a 电动 xff0
  • 移植FreeRTOS时出现了Undefined symbol xTaskGetSchedulerState (referred from delay.o).

    当我们使用正点原子进行FreeRTOS移植实验时 xff0c 会出现了freertos axf Error L6218E Undefined symbol xTaskGetSchedulerState referred from delay
  • 为什么结构体指针需要malloc申请空间

    例如此时 xff1a int init struct Node head 结构体指针 初始化 struct Node newnode 61 struct Node malloc sizeof struct Node if NULL 61 6
  • centos7.5配置本地镜像源

    1 把完整版镜像ISO文件加载到光驱 2 创建挂载路径 xff1a root 64 localhost mkdir mnt cdrom 3 挂载ISO镜像到设置好的挂载路径 xff1a root 64 localhost mount dev
  • docker镜像使用方法(查看、删除、更新、创建)

    docker镜像使用方法 xff08 查看和删除 xff09 ps ef grep docker 查看docker进程 docker info 查看docker信息 docker images 列出本地主机上的镜像 docker searc
  • h3c网络设备中dis lldp nei list查看邻居

    可查看本端对端的接口 xff0c 设备名字 可以先在网关搜集ARP表 xff0c 根据ARP表里面的IP MAC 端口号后再使用dis lldp nei list查看邻居 xff0c 对比一下MAC表 xff0c 就知道是哪台设备了 前提是
  • H3C认证网络工程师(H3CNE)

    H3C认证网络工程师 H3CNE 1 sysname 更改系统名 2 display clock 显示系统时间 3 clock datetime 修改系统时间 4 display cur 显示当前运行的配置 5 save 保存配置 6 re
  • centos增加一个新磁盘

    1 在CAS或VMWARE等虚拟化平台里为虚拟机增加一个新磁盘 2 使用lsblk查看系统中是否有新增的硬盘 xff0c 如果没有 xff0c 重启一下系统就可以了 3 分区 xff1a fdisk dev sdb 按照提示输入m调出菜单
  • cas下linux虚拟机在线扩容磁盘

    cas下linux在线扩容磁盘 xff1a 1 修改虚拟机配置 xff0c 扩大需要扩容的磁盘 xff0c 然后以root进入系统 xff0c lsblk查看扩容的空间 2 使用growpart扩展分区 root 64 zgy growpa
  • 堆叠+链路聚合+OSPF

    1 配置IRF堆叠 swa inter range ten gig1 0 49 to ten gig1 0 52 shutdown swa irf port 1 1 port group inter ten gig 1 0 49 port
  • 正则表达式基础语法例子

    literal 功能 xff1a 匹配文本字符串的字面值 literal 例子 xff1a foo re1 re2 功能 xff1a 匹配正则表达式 re1 或者 re2 例子 xff1a foo bar 功能 xff1a 匹配任何字符 除
  • ubuntu升级python3.5到python3.10

    wget https www python org ftp python 3 10 0 Python 3 10 0 tgz tar zxvf Python 3 10 0 tgz cd Python 3 10 0 configure conf
  • CAS5.3单点登录(二)连接mysql数据库

    上一章说cas服务器中的登录帐号名是配置中写死的 xff0c 实际情况中不太可能仅有一个用户 xff0c 而是将所有用户存在一个数据库表中 xff0c 以便对登录帐号进行增删改的处理 本章就来讲述如何配置cas连接数据库 xff08 一 x
  • 【Python】argparse模块详解

    Python argparse模块详解 argparse 是一个用来解析命令行参数的 Python 库 xff0c 它是 Python 标准库的一部分 基于 python 2 7 的 stdlib 代码 argparse 模块使编写用户友好
  • 车辆路径问题与相关算法总结

    车辆路径优化是解决物流中运输环节效率问题的方案和方法 最近在学习车辆路径优化问题 xff0c 这个问题虽然很普遍 xff0c 但是没有一篇文章能够让我对问题有个全面的了解 xff0c 尤其是解决方法 xff0c 需要查阅很多论文才能整理全
  • 蓝桥杯51单片机学习——keil的工程创建的基本操作(通俗易懂)

    1 创建项目文件夹 2 选择编程的硬件 51一般选择atml 中的 89c52 即可 3 新建编程文件 4 编程文件关联 然后选择刚刚创建的编程文件 xff08 一定要是 xff1a 后缀为 c xff09 5 编译下载 xff08 一般用

随机推荐

  • ioS开发--Warning警告处理

    去除警告的方法 xff1a code class cpp span class hljs preprocessor span class hljs keyword pragma span clang diagnostic push span
  • Java -- 抽象类

    抽象类 xff1a 包含抽象方法类 xff0c 叫抽象类 没有方法体的方法 xff0c 叫抽象方法 关键字 abstract 抽象方法 xff1a 权限修饰符 abstract 返回值 方法名 形参列表 例如 xff1a public ab
  • 利用Winsock实现UDP通信

    UDP通信的C语言实现 UDP通信的定义Winsock用于通信的结构体服务端socket建立服务端程序源码用户端socket建立用户端程序源码信息的接收与发送UDP通信的特点 UDP通信的定义 Internet 协议集支持一个无连接的传输协
  • C++的string类型中关于append函数、push_back函数和+=运算符的区别

    append 43 61 push back全字符串 xff08 string xff09 部分字符串 xff08 substring xff09 字符数组 xff08 char array xff09 单个字符 xff08 char xf
  • 深度学习中查看显卡使用情况

    命令 xff1a nvidia smi 功能 xff1a 显示服务器上的GPU的情况 命令 xff1a nvidia smi l 功能 xff1a 定时更新显示服务器上的GPU的情况 命令 xff1a watch n 3 nvidia sm
  • Shell脚本入门(一)--- 变量赋值、调取、echo$计算

    Shell脚本入门 xff08 一 xff09 文章目录 Shell脚本入门 xff08 一 xff09 64 toc shell 脚本变量赋值echo 计算 作业 xff1a 获取主机基本信息及分区使用率 shell 脚本 shell 变
  • C和C++中 struct的区别

    1 xff1a C 43 43 中不需要加struct就可以定义变量 xff0c 而c需要加struct 2 xff1a C 43 43 结构体内部可以使用函数
  • C和C++的const

    1 C语言的const修饰的变量都有空间 xff0c 全局的在常量区 xff0c 局部的在栈区 xff1b 2 C语言的const修饰的全局变量具有外部链接属性 xff0c extern const int a xff1b 即可使用 xff
  • 连接SSH失败的原因以及方法

    一 检查用户名密码 连接失败时可以先检查验证信息 xff0c 步骤如下 xff1a 1 运行软件 xff0c 在会话管理中找到连接失败的会话 右键单击会话名 xff0c 点击属性 图1 xff1a 查看属性 2 在弹出的对话框点击用户身份验
  • 虚地址空间

  • tensorflow2.0 学习笔记-利用tf.data.Dataset API读取numpy array文件

    tensorflow2 0 学习笔记 数据读取1 利用tf data Dataset API读取numpy array文件读取numpy array数据 利用tf data Dataset API读取numpy array文件 读取nump
  • matplotlib绘图中文乱码问题--解决方案(Windows)

    最近使用python绘图时 xff0c 出现中文乱码问题 xff0c 结合在网上搜索理解后 xff0c 按照如下步骤 xff0c 成功解决 解决方案 xff1a 步骤一 xff1a 找到Mircosoft YaHei UI字体文件 一般 在
  • Python数据分析之--运动员数据揭秘(一)

    在网易云课堂看了城市数据团的课程 xff0c 对理解利用pytthon进行数据分析的基本流程很有帮助 xff0c 因此进行复盘总结 xff0c 加深自己的理解 xff0c 巩固相关操作 分析资料及工具 xff1a Spyder Python
  • 缓慢变化维

    一 什么是缓慢变化维 xff1f 缓慢变化维 xff08 Slowly Changing Dimensions SCD xff09 它的提出是因为在现实世界中 xff0c 维度的属性并不是静态的 xff0c 它会随着时间的流失发生缓慢的变化
  • 一个小例子带你入门-Tableau

    声明 xff1a 本文是学习W3Cschool教程整理所得 xff0c 非原创 xff0c 原文链接 xff1a W3Cschool 创建任何Tableau数据分析报告涉及三个基本步骤 连接到数据源 它涉及定位数据并使用适当类型的连接来读取
  • SQL 必知必会--函数篇

    对SQL的基础函数做复习回顾 xff0c 本篇涉及的函数知识如下 xff1a 好了 xff0c 下面开始复习 xff1a SQL Aggregate 函数计算从列中取得的值 xff0c 返回一个单一的 值 Max 函数 作用 xff1a 返
  • WPS的Linux Mint版(Ubuntu)提示“系统缺失字体”的解决方法

    wps的Linux Mint版 Ubuntu 版安装成功后 xff0c 可能每次启动的时候都会提示 xff1a 系统缺失字体 xff0c 如图 xff1a 解决方法 1 首先下载字体包并解压 链接 https pan baidu com s
  • SQL必知必会--中级篇(二)

    接上一篇SQL必知必会 中级篇 xff08 一 xff09 xff0c 继续对sql知识进行整理复习 本篇包含知识点如图 xff1a 一 SQL 约束 用于规定表中的数据规 则 xff1b 如果存在违反约束的数据行为 xff0c 行为会被约
  • 静态网页个人简历

    程序员的简历是不用随身带的 首先 xff0c 作为程序员自己的简历是比别人特别的 xff1b 程序员应该是有思想 xff0c 有高情商的手工艺人 作为程序员简历是随身带的代码 xff0c 用代码书写的简历就像是一份随身携带着的简历 xff0
  • CMake之CMakeLists.txt编写入门

    自定义变量 主要有隐式定义和显式定义两种 隐式定义的一个例子是PROJECT指令 xff0c 它会隐式的定义 lt projectname gt BINARY DIR和 lt projectname gt SOURCE DIR两个变量 xf