CMAKE使用总结

原创
2017/05/16 16:40
阅读数 3K

参考文档

CMake Practice.pdf

最简单的例子

目录结构

.
├── CMakeLists.txt
└── main.cpp

main.cpp

#include <iostream>
int main()
{
    std::cout <<"Hello World" << std::endl;
    return 0;
}

CMakeLists.txt

PROJECT (PRACTISE1)
SET(SRC_LIST main.cpp)
MESSAGE(STATUS "This is BINARY dir "${PRACTISE1_BINARY_DIR})
MESSAGE(STATUS "This is BINARY dir "${PROJECT_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir "${PRACTISE1_SOURCE_DIR})
MESSAGE(STATUS "This is SOURCE dir "${PROJECT_SOURCE_DIR})
ADD_EXECUTABLE(practise1 ${SRC_LIST})

在目录执行cmake命令

$ cmake .

输出

...
-- This is BINARY dir xxx/practise1
-- This is BINARY dir xxx/practise1
-- This is SOURCE dir xxx/practise1
-- This is SOURCE dir xxx/practise1
...

总结

  • CMAKE语法中的指令关键字不分大小写,但是传入的参数是分大小写的, 如果传入的参数是列表可以用空格或者分号隔开, 参数也可以使用双引号传入(处理带有空格字符的参数的时候很有用)。
  • PROJECT用来设置工程名字,可以与后面ADD_EXECUTABLE中的目标文件名不一样。
  • SET用来定义一个变量,如果定义的是列表可以用空格或者分号隔开,定义的变量可以在后面用${变量名}来进行取值。
  • MESSAGE用来打印日志,分别有SEND_ERROR | STATUS | FATAL_ERROR三中类型,其中FATAL_ERROR会让构建程序终止。
  • ADD_EXECUTABLE根据设置生成指定的可执行文件。
  • 默认变量${PROJECT_BINARY_DIR}指的是目标make文件的生成目录。
  • 默认变量${PROJECT_SOURCE_DIR}指的是工程CMakeLists.txt所在目录。
  • <工程名>_BINARY_DIR和<工程名>_SOURCE_DIR是 PROJECT 指令隐式定义的两个变量, 等同于PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,一般推荐使用后面这两个,因为工程名会变动。
  • cmake之后如果运行make命令会发现目标文件生成在${PROJECT_BINARY_DIR}
  • 清理使用 make clean

工程化组织

目录结构

.
├── bin
├── build
├── CMakeLists.txt
└── src
    ├── CMakeLists.txt
    └── main.cpp

根目录下面的CMakeLists.txt

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin
ADD_EXECUTABLE(practise2 main.cpp)

src目录下面的CMakeLists.txt

PROJECT(PRACTISE2)
MESSAGE(STATUS "PROJECT_BINARY_DIR= "${PROJECT_BINARY_DIR})
MESSAGE(STATUS "PROJECT_SOURCE_DIR= "${PROJECT_SOURCE_DIR})
ADD_SUBDIRECTORY(src)

到build目录下面进行cmake

$ cd build
$ cmake ..
$ make

输出

-- PROJECT_BINARY_DIR= xxx/practise2/build
-- PROJECT_SOURCE_DIR= xxx/practise2

practise2最后生成在bin目录中

总结

  • 运行cmake命令所在的目录将是目标make文件的生成目录,即${PROJECT_BINARY_DIR}。
  • ADD_SUBDIRECTORY指令第一个参数指的是需要添加的子目录, 子目录下面也因该有CMakeLists.txt, 第二个指make生成的最终文件存放目录,实际执行时文件被保存到了${PROJECT_BINARY_DIR}/bin下 第三个参数EXCLUDE_FROM_ALL,用来将这个目录排除
  • 通过改变变量EXECUTABLE_OUTPUT_PATH来修改可执行文件的生成目录,一般是结合ADD_EXECUTABLE指令使用, LIBRARY_OUTPUT_PATH修改库的生成目录,一般是结合ADD_LIBRARY指令使用。

生成动态库和静态库

基本指令

  • 生成库文件
ADD_LIBRARY(库名 SHARED 源文件列表)

把上面的ADD_EXECUTABLE修改为ADD_LIBRARY就可以生成库文件了, 第二个参数SHARED表示生成动态库,STATIC表示生成静态库

  • 指定库文件的生成目录
SET(LIBRARY_OUTPUT_PATH <路径>)
  • 修改输出名字
ADD_LIBRARY(hello_static STATIC hello.c)
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")

原本会输出 libhello_static.a,加上后面这个后,库文件名变为libhello.a

  • 设置动态库的版本号
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

VERSION 指代动态库版本,SOVERSION 指代 API 版本。

  • 同时生成动态库和静态库的CMakeLists.txt
SET(LIBHELLO_SRC hello.cpp)
SET(LIBRARY_OUTPUT_PATH lib)  #目标文件生成目录设置为lib
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello") #对目标文件重命名
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)  #不清理上个版本的生成文件
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) #不清理上个版本的生成文件

make之后会同时生成两个文件

    ├── lib
    │   ├── libhello.a
    │   └── libhello.so

windows下DLL生成说明

由于windows调用dll一般是需要dll文件,带有symbol的lib文件,以及函数声明的头文件,因此需要做额外的配置,CMAKE在动态库编译的之前会预定义一个宏 {库名}_EXPORTS,判断这个宏来决定dll中的函数是不是导出函数,有了导出函数vs才会额外的生成lib文件。

以下代码兼容windows和linux dynamic.h

#ifndef __DYNAMIC_H_
#define __DYNAMIC_H_

#ifdef _WIN32
#ifdef dynamic_EXPORTS
#define __DLL_API __declspec(dllexport)
#else
#define __DLL_API __declspec(dllimport)
#endif
#endif //_WINDOWS

#ifdef  __linux__
#ifdef dynamic_EXPORTS
#define __DLL_API  __attribute__((visibility("default")))
#else
#define __DLL_API
#endif
#endif //__linux__

__DLL_API void fun_dynamic();

#endif

dynamic_EXPORTS这个宏是cmake自动根据库名生成的,正好可以用来作为是导出函数的判断

dynamic.cpp

#include "dynamic.h"
#include <iostream>

__DLL_API void fun_dynamic()
{
	std::cout << "msg from dynamic" << std::endl;
}

引用外部头文件和库文件

指令语法

  • 引用外部头文件
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)

这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径 中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的 后面,你可以通过两种方式来进行控制搜索路径添加的方式: 1,CMAKE_INCLUDE_DIRECTORIES_BEFORE,通过 SET 这个 cmake 变量为 on,可以 将添加的头文件搜索路径放在已有路径的前面。 2,通过 AFTER 或者 BEFORE 参数,也可以控制是追加还是置前。

  • 链接库文件

添加非标准的共享库搜索路径

LINK_DIRECTORIES(directory1 directory2 ...)

为 target 添加需要链接的库,target可以是可执行文件,也可以是库文件

TARGET_LINK_LIBRARIES(target library1
                     <debug | optimized> library2
                     ...)

目录结构

├── bin
├── build
├── CMakeLists.txt
├── libs
└── src
    ├── CMakeLists.txt
    ├── libdynamic
    │   ├── CMakeLists.txt
    │   ├── dynamic.cpp
    │   └── dynamic.h
    ├── libstatic
    │   ├── CMakeLists.txt
    │   ├── static.cpp
    │   └── static.h
    └── main
        ├── CMakeLists.txt
        └── main.cpp

本工程由一个可执行文件和一个静态库和一个动态库构成,可执行文件调用了这两个库文件,可执行文件和动态库最后生成在bin目录下,静态库生成在libs目录。

根目录的 CMakeLists.txt

PROJECT(PRACTISE3)
MESSAGE(STATUS "PROJECT_BINARY_DIR= "${PROJECT_BINARY_DIR})
MESSAGE(STATUS "PROJECT_SOURCE_DIR= "${PROJECT_SOURCE_DIR})
ADD_SUBDIRECTORY(src)

src目录的CMakeLists.txt

ADD_SUBDIRECTORY(libstatic)
ADD_SUBDIRECTORY(libdynamic)
ADD_SUBDIRECTORY(main)

动态库的CMakeLists.txt

SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
ADD_LIBRARY(dynamic SHARED dynamic.cpp)

设定生成目录为根目录下面的bin

静态库的CMakeLists.txt

SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/libs)
ADD_LIBRARY(static STATIC static.cpp)

设定生成目录为根目录下面的libs

可执行文件的CMakeLists.txt

INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/libstatic ${PROJECT_SOURCE_DIR}/src/libdynamic)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
ADD_EXECUTABLE(practise3 main.cpp)
TARGET_LINK_LIBRARIES(practise3 static)
TARGET_LINK_LIBRARIES(practise3 dynamic)

设定了头文件的查找目录,并在最后指定了需要链接的动态库和静态库,库的名字应该与ADD_LIBRARY指令中使用的名字一致

make的输出

Scanning dependencies of target static
[ 33%] Building CXX object src/libstatic/CMakeFiles/static.dir/static.o
Linking CXX static library ../../../libs/libstatic.a
[ 33%] Built target static
Scanning dependencies of target dynamic
[ 66%] Building CXX object src/libdynamic/CMakeFiles/dynamic.dir/dynamic.o
Linking CXX shared library ../../../bin/libdynamic.so
[ 66%] Built target dynamic
Scanning dependencies of target practise3
[100%] Building CXX object src/main/CMakeFiles/practise3.dir/main.o
Linking CXX executable ../../../bin/practise3
[100%] Built target practise3

自动根据各个项目的包含关系决定构建的顺序

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部