系统学习cmkae的项目(完善中)

2 篇文章 0 订阅

一直没有掌握了ros中的cmake机制,故系统学习一下自己编写cmakelist.txt。
知乎网友回答写的非常不错。

根据项目地址进行学习。

在学习过程中,要升级cmake版本,我使用的是ubuntu14,升级前的cmake版本为2.8.12.2
按照网友的方法升级成为 3.17.0


学习前对编译的理解:

关于库与可执行文件:在c++工程中,只有带main函数的文件才会生成可执行程序,而另外一些代码,我们将其打包成一个东西,叫做库。
将库打包成静态库或动态库后,在结合头文件,我们就能利用他两生成可执行程序。下面举个例子,使用cmake来生成一个可执行程序。

  1. 生成库文件

(在一个文件夹下建立一个hello.c的源文件和CMakeLists.txt)
hello.c内容如下:

#include<stdio.h>
void HelloFunc()
{
printf("Hello World\n");
}

CMakeLists.txt内容如下:

add_library(hello hello.c)

然后,我们在该目录下执行cmake .make即可以看到生成libhello.a文件。

  1. 编写main.c文件,作为生成可执行文件的源文件。编写头文件,以利用刚生成的库文件。
    main.c内容如下:
#include "hello.h"
void main()
{
HelloFunc();
}

hello.h内容如下:

#ifndef HELLO_H
#define HELLO_H
void HelloFunc();
#endif
  1. 在原先的CMakeLists.txt下添加:
 add_library(hello hello.c)
add_executable(hello_world main.c)
target_link_libraries(hello_world libhello.a)

第二句表示生成可执行文件hello
第三句表示将目标文件链接到生成的静态库文件上。以使用库文件中的函数。



cmake指令学习

例1 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(projectname [CXX] [C] [Java])
你可以用这个指令定义工程名称,并可指定工程支持的语言,支持的语言列表是可以忽略的,默认情况表示支持所有语言。
这个指令隐式的定义了两个 cmake 变量:

<projectname>_BINARY_DIR 以及<projectname>_SOURCE_DIR

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([SEND_ERROR | STATUS | FATAL_ERROR] “message to display”
…)
这个指令用于向终端输出用户定义的信息,包含了三种类型:

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

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

ADD_SUBDIRECTORY 指令
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除,比如,工程的 example,可能就需要工程构建完成后,再进入 example 目录单独进行构建(当然,你也可以通过定义依赖来解决此类问题)。

换个地方保存目标二进制
不论是 SUBDIRS 还是 ADD_SUBDIRECTORY 指令(不论是否指定编译输出目录),我们都可以通过 SET 指令重新定义 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH 变量来指定最终的目标二进制的位置(指最终生成的 hello 或者最终的共享库,不包含编译生成的中间文件)

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

表示可执行二进制的输出路径为 build/bin 和库的输出路径为 build/lib.

如何安装:

安装的需要有两种,一种是从代码编译后直接 make install 安装,一种是打包时的指定
目录安装。
你可以通过:
make install
将 hello 直接安装到/usr/bin 目录,也可以通过 make install DESTDIR=/tmp/test 将他安装在 /tmp/test/usr/bin 目录,打包时这个方式经常被使用

那么我们的 HelloWorld 应该怎么进行安装呢?
这里需要引入一个新的 cmake 指令 INSTALL 和一个非常有用的变量
CMAKE_INSTALL_PREFIX。
CMAKE_INSTALL_PREFIX 变量类似于 configure 脚本的 – prefix,常见的使用方法看起来是这个样子:

cmake -DCMAKE_INSTALL_PREFIX=/usr

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

  • 目标文件的安装:
 INSTALL(TARGETS targets... [[ARCHIVE|LIBRARY|RUNTIME]
   [DESTINATION <dir>] [PERMISSIONS permissions...] [CONFIGURATIONS
   [Debug|Release|...]] [COMPONENT <component>] [OPTIONAL] ] [...])
  1. 参数中的 TARGETS 后面跟的就是我们通过 ADD_EXECUTABLE 或者 ADD_LIBRARY 定义的 目标文件,可能是可执行二进制、动态库、静态库。
  2. 目标类型也就相对应的有三种,
    ARCHIVE 特指静态库,
    LIBRARY 特指动态库,
    RUNTIME特指可执行目标二进制
  3. 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 目录
  • 普通文件的安装:
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 权限。

  • 非目标文件的可执行程序安装(比如脚本之类):
INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])

跟上面的 FILES 指令使用方法一样,唯一的不同是安装后权限为:
OWNER_EXECUTE, GROUP_EXECUTE, 和 WORLD_EXECUTE,即 755 权限

  • 目录的安装:
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...]] [...])

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.

静态库与动态库构建

SET(LIBHELLO_SRC hello.c)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello PROPERTIES OUTPUT_NAME "hello")

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC}) 添加动态库
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC}) 添加静态库,但是,当此命令会和动态库的名字hello重合,故使用SET_TARGET_PROPERTIES命令:
其基本语法是:

SET_TARGET_PROPERTIES(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)

这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本。
在本例中,我们需要作的是向 lib/CMakeLists.txt 中添加一条:
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME “hello”)
这样,我们就可以同时得到 libhello.so/libhello.a 两个库了。

利用已经建立好的静态库与动态库:

ADD_EXECUTABLE(main main.c)
INCLUDE_DIRECTORIES(/usr/include/hello)
#TARGET_LINK_LIBRARIES(main libhello.so)
TARGET_LINK_LIBRARIES(main libhello.a)
  • 通过 INCLUDE_DIRECTORIES 指令加入非标准的头文件搜索路径。

  • 通过 LINK_DIRECTORIES 指令加入非标准的库文件搜索路径。

  • 通过 TARGET_LINK_LIBRARIES 为库或可执行二进制加入库链接。

特殊的环境变量 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH
这两个是环境变量而不是 cmake 变量。使用方法是要在 bash 中用 export 或者在 csh 中使用 set 命令设置或者CMAKE_INCLUDE_PATH=/home/include cmake …等方式。

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

cmake 自定义变量的方式:
主要有隐式定义和显式定义两种,

  • 前面举了一个隐式定义的例子,就是 PROJECT 指令,他会隐式的定义<projectname>_BINARY_DIR<projectname>_SOURCE_DIR 两个变量。
  • 显式定义的例子我们前面也提到了,使用 SET 指令,就可以构建一个自定义变量了。 比如: SET(HELLO_SRC main.SOURCE_PATHc), PROJECT_BINARY_DIR 可以通过${HELLO_SRC}来引用这个自定义变量了.

cmake 常用变量

CMAKE_BINARY_DIR 
PROJECT_BINARY_DIR
<projectname>_BINARY_DIR

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

CMAKE_CURRENT_SOURCE_DIR

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

CMAKE_CURRRENT_BINARY_DIR

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

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 指令来调用自己的模块了。

PROJECT_NAME

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值