CMake使用总结

子目录CMakeLists.txt将从父目录CMakeLists.txt继承设置,如包含头文件,库目录

# t2/src/CMakeLists.txt

ADD_EXECUTABLE(hello main.cpp)

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

INSTALL(TARGETS hello RUNTIME DESTINATION bin)  # 将hello二进制安装至/<prefix>/bin
//t2/src/main.cpp
#include<cstdio>

int main()
{
    printf("hello world from main\n");
    
    return 0;
}
# t2/CMakeLists.txt
PROJECT(HELLO)

MESSAGE (STATUS "This is BINARY dir " ${PROJECT_BINARY_DIR})
MESSAGE (STATUS "This is SOURCE dir " ${PROJECT_SOURCE_DIR})

SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

ADD_SUBDIRECTORY(src bin)#将src子目录加入工程,并指定编译输出为bin目录

INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2) # 将COPYRIGHT、README安装到/<prefix>/share/doc/cmake/t2

INSTALL(PROGRAMS runhello.sh DESTINATION bin) # 将runhello.sh安装到/<prefix>/bin

#安装doc中的hello.txt,这里有两种方式:一是通过在doc目录建立CMakeLists.txt,并将doc目录通过ADD_SUBDIRECTORY加入工程来完成。
#另一种方法是直接在工程目录通过INSTALL(DIRECTORY …) 来完成
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2) # 将doc目录中的hello.txt安装到/<prefix>/share/doc/cmake/t2


PROJECT这个指令隐式的定义了两个cmake变量:
<projectname>_BINARY_DIR以及<projectname>_SOURCE_DIR,这里就是HELLO_BINARY_DIR和HELLO_SOURCE_DIR,如果是内部编译,两个变量指的都是工程所在路径../t2,如果是外部编译,两者所指代的内容会有所不同。
同时cmake系统预定义了PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR变量,值分别跟HELLO_BINARY_DIR与HELLO_SOURCE_DIR一致。
建议使用PROJECT_BINARY_DIR,PROJECT_SOURCE_DIR,即使修改了工程名称,也不会影响这两个变量。如果使用了<projectname>_SOURCE_DIR,修改工程名称后,需要同时修改这些变量。

内部编译in-source build,是直接在工程目录下(如t2)编译,产生的临时文件也在这个目录里。
外部编译out-of-source,只另建一个目录,在那个目录里cmake,如在t2下新建build目录(可以在任何地方建立build目录,不一定必须在该工程目录中),在build里执行:cmake ..,所有的临时文件就都产生在build目录里

通过外部编译,HELLO_SOURCE_DIR仍然指代工程路径, 即../t2;但是 HELLO_BINARY_DIR 则指代编译路径,即 ../t2/build 了。

 

指令:
SET(VAR[VALUE] [CACHE TYPE DOCSTRING [FORCE]])
Set指令是用来显式的定义变量的,如果有多个源文件,也可以定义成SET(SRC_LIST main.cpp t1.cpp t2.cpp)。

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

ADD_EXECUTABLE(hello ${SRC_LIST})
这个工程会生成一个文件名为hello的可执行文件,相关的源文件是SRC_LIST中定义的源文件列表

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令用于向当前工程添加存放源文件的子目录,并可以指定生成文件存放的位置。
上面的例子定义了将src子目录加入工程,并指定编译输出(包含编译中间结果)路径为bin目录。如果不进行bin目录的指定,那么编译结果都将存放在build/src目录(即在build下自动创建一个src目录,这个目录跟原有的src目录对应),指定bin目录后,相当于在编译时将src重命名为bin。

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

应该把这两条指令写在哪个CMakeLists.txt里?把握一个简单的原则,在哪里ADD_EXECUTABLE 或 ADD_LIBRARY,就在哪里加入(要在ADD_..指令之后)。

安装:INSTALL与DCMAKE_INSTALL_PREFIX
DCMAKE_INSTALL_PREFIX变量类似于configure脚本的 –prefix:  CMAKE_INSTALL_PREFIX的默认定义是/usr/local
cmake -DCMAKE_INSTALL_PREFIX=/usr .

安装的内容可以包括目标二进制、动态库、静态库以及文件、目录、脚本
1)目标文件的安装:
INSTALL(TARGETS targets...
[ [ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
] [...])
参数中的TARGETS后面跟的就是可执行二进制、动态库、静态库。ARCHIVE特指静态库,LIBRARY特指动态库,RUNTIME特指可执行目标二进制。DESTINATION定义安装的路径,如果路径以/开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX就无效了。不以 “/ ”开头,那么安装后的路径就是:${CMAKE_INSTALL_PREFIX}/<DESTINATION定义的路径>;
举个简单的例子:
INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic
)
特别注意的是你不需要关心TARGETS具体生成的路径,只需要写上TARGETS名称就可以了,即只写"myrun" "mylib" "mystaticlib",而不用加具体的路径。

2)普通文件的安装:
INSTALL(FILES files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
文件名是此指令所在路径下的相对路径。
如果默认不定义权限PERMISSIONS,安装后的权限为:
OWNER_WRITE, OWNER_READ, GROUP_READ,和WORLD_READ,即644 (-rw-r--r--)权限。

3)非目标文件的可执行程序安装(比如脚本之类):
INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
跟上面的FILES指令使用方法一样,唯一的不同是安装后权限为:
OWNER_EXECUTE, GROUP_EXECUTE, 和WORLD_EXECUTE,即755 (-rwxrw-rw-)权限 

4)目录的安装:
INSTALL(DIRECTORY dirs... DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])

这里主要介绍其中的DIRECTORY、PATTERN以及PERMISSIONS参数。
DIRECTORY后面连接的是相对路径,但务必注意:abc和abc/是有很大区别的。如果目录名不以/结尾,那么这个目录将被安装为目标路径下的abc;如果目录名以/结尾,代表将这个目录中的内容安装到目标路径,但不包括这个目录本身。
PATTERN用于使用正则表达式进行过滤,PERMISSIONS用于指定PATTERN过滤后的文件权限。

例子:
INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*" PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ) 

将icons目录安装到 <prefix>/share/myproj,将scripts/中的内容安装到<prefix>/share/myproj 不包含目录名为CVS的目录,对于scripts/*文件指定权限为 OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ.

清理:直接删除文件夹:rm -rf build, 然后:mkdir build,然后:cd build && cmake ..   

静态库和动态库

//hello.cpp
#include "hello.h"
#include"iostream"
using namespace std;

void HelloFunc()
{
    cout << "Hello World\n";
}
//hello.h
#ifndef HELLO_H
#define HELLO_H

#include <stdio.h>

void HelloFunc();

#endif

 

#t3/lib/CMakeLists.txt
SET (LIBHELLO_SRC hello.cpp)

# SET (LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

# 添加动态库,不加SHARED也可以,默认未动态库
ADD_LIBRARY (hello SHARED ${LIBHELLO_SRC})
# 添加静态库
# 仍然用hello作为target名时,是不能成功的,因为hello作为一个target是不能重名的,故把hello修改为hello_static
ADD_LIBRARY (hello_static STATIC ${LIBHELLO_SRC})
# 按照一般的习惯,静态库名字跟动态库名字应该是一致的,只是扩展名不同;
# 所以,希望 "hello_static" 在输出时,以"hello"的名字显示,故设置如下:
SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")

GET_TARGET_PROPERTY (OUTPUT_VALUE hello_static OUTPUT_NAME)
MESSAGE (STATUS "This is the hello_static OUTPUT_NAME: " ${OUTPUT_VALUE})

# cmake在构建一个新的target时,会尝试清理掉其他使用这个名字的库,因此,在构建libhello.a时,就会清理掉libhello.so.
# 为了解决这个问题:
SET_TARGET_PROPERTIES (hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES (hello PROPERTIES CLEAN_DIRECT_OUTPUT 1) 

# 按照规则,动态库是应该包含一个版本号,VERSION指动态库版本,SOVERSION指API版本
SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1)

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

 

#t3/CMakeLists.txt
#PROJECT(HELLOLIB)

# 通过在主工程文件CMakeLists.txt中修改ADD_SUBDIRECTORY (lib) 指令来指定一个编译输出位置;
# 指定本工程中动态库libhello.so生成的位置,即 build/lib;
ADD_SUBDIRECTORY(lib)

# 也可以通过变更为其他的位置,如
# ADD_SUBDIRECTORY(lib lib_new)
# 则,动态库libhello.so生成的位置变为 build/lib_new;

ADD_SUBDIRECTORY(src)
#t3/src/CMakeLists.txt
#添加非标准的共享库搜索路径
link_directories(/root/cmakedemo/t3/build/inst/lib)
INCLUDE_DIRECTORIES(../build/inst/include/hello)

ADD_EXECUTABLE(main main.cpp)

#用来为target添加需要链接的共享库
#TARGET_LINK_LIBRARIES(main hello) #链接到的是动态库
TARGET_LINK_LIBRARIES(main libhello.a) #链接静态库

 

//t3/src/main.cpp
#include "hello.h"

int main()
{
    HelloFunc();

    return 0;
}


ADD_LIBRARY(libname [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
你不需要写全 libhello.so,只需要填写hello即可,cmake系统会自动为你生成 libhello.X
类型有三种:
SHARED,动态库;文件扩展名常为 "*.so";
STATIC,静态库,文件扩展名常为 "*.a";
MODULE,在使用dyld的系统有效,如果不支持dyld,则被当作SHARED对待。
EXCLUDE_FROM_ALL参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手动构建。

SET_TARGET_PROPERTIES (target1 target2 ...PROPERTIES prop1 value1 prop2 value2 ...)
GET_TARGET_PROPERTY (VAR target property)

动态库版本号
SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1)
VERSION指动态库版本,SOVERSION指API版本。
结果
在build/lib目录会生成(自动生成了软链接):
libhello.so.1.2
libhello.so.1->libhello.so.1.2
libhello.so ->libhello.so.1 

通过:
cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..
make
sudo make install
就可以将头文件和共享库安装到系统目录/usr/local/lib和/usr/local/include/hello中了。其实CMAKE_INSTALL_PREFIX的默认值就是/usr/local/

INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的后面

补充:

6.       条件语句:
if(var) #var 非empty 0 N No OFF FALSE... #非运算使用NOT
       …
else()/elseif() … endif(var)
7.       循环语句
Set(VAR a b c)
Foreach(f ${VAR})       …Endforeach(f)
8.       循环语句
WHILE() … ENDWHILE()

五、      内部变量
CMAKE_C_COMPILER:指定C编译器
CMAKE_CXX_COMPILER:
CMAKE_C_FLAGS:编译C文件时的选项,如-g;也可以通过add_definitions添加编译选项
CMAKE_BUILD_TYPE: build 类型(Debug, Release, ...),CMAKE_BUILD_TYPE=Debug
BUILD_SHARED_LIBS:Switch between shared and static libraries

内置变量的使用:
>> 在CMakeLists.txt中指定,使用set
>> cmake命令中使用,如cmake -DBUILD_SHARED_LIBS=OFF

六、      命令
add_definitions:添加编译参数
>> add_definitions(-DDEBUG)将在gcc命令行添加DEBUG宏定义;
>> add_definitions( “-Wall -ansi –pedantic –g”)
 

八、      FAQ
>> aux_source_directory(<dir> <variable>)
>> 将dir中所有源文件(不包括头文件)保存到变量variable中(不包括子目录)

6)  怎样区分debug、release版本
>>建立debug/release两目录,分别在其中执行cmake -DCMAKE_BUILD_TYPE=Debug(或Release),需要编译不同版本时进入不同目录执行make即可;Debug版会使用参数-g;Release版使用-O3 –DNDEBUG

如果将优化程度调到最高需要设置参数-O3,最低是 -O0 即不做优化;添加调试信息的参数是 -g -ggdb

CMAKE_BUILD_TYPE ,可以的取值是 Debug Release RelWithDebInfo 和 MinSizeRel。当这个变量值为 Debug 的时候,CMake 会使用变量 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_C_FLAGS_DEBUG 中的字符串作为编译选项生成 Makefile ,当这个变量值为 Release 的时候,工程会使用变量 CMAKE_CXX_FLAGS_RELEASE 和 CMAKE_C_FLAGS_RELEASE 选项生成 Makefile。

>> 另一种设置方法——例如DEBUG版设置编译参数DDEBUG
IF(DEBUG_mode)
    add_definitions(-DDEBUG)
ENDIF()
在执行cmake时增加参数即可,例如cmake -D DEBUG_mode=ON

9)  怎样添加编译依赖项
用于确保编译目标项目前依赖项必须先构建好
>>add_dependencies

18)        怎样根据OS指定编译选项
>> IF( APPLE ); IF( UNIX ); IF( WIN32 )

更多:https://www.hahack.com/codes/cmake/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值