[Note] CMake的简单使用
当前环境
macOS
和cmake 3.9.4
一 gcc, make, cmake 的区别
-
gcc
是一个编译器套件,用于编译多种语言的源文件 - 当
gcc
需要编译多个文件时,编译指令很多时,则会使用makefile
按照特定规则来对指令(gcc
操作指令)进行整合,使用make
工具来执行指令组。 - 由于
makefile
依赖于确定的编译器套件(如指令中的gcc ...
),这时出现了通用工具CMake(根据实际的平台/环境选择编译套件并进行编译),与之搭配的是CMakeLists.txt
,输出是makefile
- 依次次进化和依赖的关系
二 编译一个 .cpp/.c 文件
随便在一个文件夹中新建两个文件,命令行cd到这个文件夹中
$ touch CMakeLists.txt test.cpp # CMakeLists.txt 必须
$ tree
.
├── CMakeLists.txt
└── test.cpp
CMakeLists.txt
# 添加需要编译的源文件 test.cpp 和 输出的可执行文件名字 exe_main
ADD_EXECUTABLE(exe_main test.cpp)
test.cpp
#include<stdio.h>
int main(int argc, char const *argv[])
{
printf("Using cmake ...\n");
return 0;
}
文件准备完毕,接着就是编译了
$ cmake ./
$ make
$./exe_main
看到最后效果,其实也是$ gcc test.cpp -o exe_main
一样的效果嘛,不过像上面说的,cmake
加入了跨平台/环境
最后奉上全部材料
一个简单的例子.png
因为执行编译动作时,产生了很多文件,所以建议新建一个
build
目录,到里面执行编译指令,以免混乱了我们的源文件目录。也想说的是,这里的CMakeLists.txt
写法并不标准哦,仅供学习 ~
三 编译多个.c/.cpp文件
经过上面简单粗暴的方法之后,总感觉文件一多了不稳妥,所以接下来吧源文件和头文件分开(注意:多文件连接,容易出现名字冲突:
duplicate symbol ...
新建文件u.cpp u.h
分别放到对应的文件夹中,最后目录结构为
.
├── CMakeLists.txt
├── build
├── include
│ └── u.h
└── src
├── test.cpp
└── u.cpp
u.h
文件
int add(int i, int j);
u.cpp
文件
#include "../include/u.h"
int add(int i, int j) {
return i + j;
}
test.cpp
文件
#include<stdio.h>
#include "../include/u.h"
int main(int argc, char const *argv[])
{
printf("Using cmake ...%d\n", add(13, 14));
return 0;
}
CMakeLists.txt
文件修改成
# 指定 cmake 版本,预防某些用户使用较低版本的来编译该项目
cmake_minimum_required(VERSION 3.2)
# 指定一个项目名称,后面可以通过 `${PROJECT_NAME}` 来引用
PROJECT(my_project)
# 指定头文件目录,改参数为头文件所在的文件夹名字
INCLUDE_DIRECTORIES(include)
# 源文件目录
AUX_SOURCE_DIRECTORY(src DIR_SRCS)
# 选择性设置环境变量,然后通过 ${CUSTOM_VAR} 来引用
# SET(CUSTOM_VAR ${DIR_SRCS})
# 添加需要编译的源文件 test.cpp 和 输出的可执行文件名字 exe_main
ADD_EXECUTABLE(${PROJECT_NAME} ${DIR_SRCS})
文件准备完毕,cd
到 build
目录执行
$ cmake ../
$ make
$ ./my_project
Using cmake ...27 # 输出
四 编译动态库态库
在上面的基础上继续,当前的目录结构为($ tree -L 2
)
.
├── CMakeLists.txt
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── Makefile
│ └── cmake_install.cmake
├── include
│ └── u.h
└── src
├── test.cpp
└── u.cpp
我们把 u.cpp
编译成动态库,则修改 CMakeLists.txt
如:
# 指定 cmake 版本,预防某些用户使用较低版本的来编译该项目
cmake_minimum_required(VERSION 3.2)
# 指定一个项目名称,后面可以通过 `${PROJECT_NAME}` 来引用
PROJECT(my_project)
# 指定头文件目录,改参数为头文件所在的文件夹名字
INCLUDE_DIRECTORIES(include)
# 源文件目录
AUX_SOURCE_DIRECTORY(src DIR_SRCS)
# 选择性设置环境变量,然后通过 ${CUSTOM_VAR} 来引用
# SET(CUSTOM_VAR ${DIR_SRCS})
# 添加需要编译的源文件 test.cpp 和 输出的可执行文件名字 exe_main
# ADD_EXECUTABLE(${PROJECT_NAME} ${DIR_SRCS})
# 设置一个动态库名字,因为 `ADD_LIBRARY` 中的参数不给设置字符常量
SET(DYLIB_NAME dytest)
# 生成一个动态库
ADD_LIBRARY(${DYLIB_NAME}
SHARED
src/u.cpp include/u.h)
文件准备完毕,像上面在build
目录进行编译就行。
五 连接动态库
同样是修改 CMakeLists.txt
# 增加下面两行
# 添加需要编译的源文件 test.cpp 和 输出的可执行文件名字 exe_main
ADD_EXECUTABLE(exe_main src/test.cpp)
# 连接
TARGET_LINK_LIBRARIES(exe_main ${DYLIB_NAME})
文件准备完毕,到 build
里编译
六 编译并连接静态库(静态库部分合并了 四、五)
和动态库的编译和链接只相差一个单词,那就是将 SHARED
改成 STATIC
,然后改一下库的名字statictest
,完结!
这里提醒一下就是,产生的库就在执行
make
指令的目录,动态库是libxxx.dylib
,静态库就是libstatictest.a
七 连接现有的(第三方)静态库和动态库
我们就就地取材,将上面输出的静态库和动态库直接拿来用,这里折腾了好久,因为导入第三方库时的语句选择问题,所以贴完整的 CMakeLists.txt
吧,静态库和动态库是差不多的,换一下名字就可以了
# 指定 cmake 版本,预防某些用户使用较低版本的来编译该项目
cmake_minimum_required(VERSION 3.2)
# 指定一个项目名称,后面可以通过 `${PROJECT_NAME}` 来引用
PROJECT(my_project)
# 指定头文件目录,改参数为头文件所在的文件夹名字
INCLUDE_DIRECTORIES(include)
# 源文件目录
AUX_SOURCE_DIRECTORY(src DIR_SRCS)
# 选择性设置环境变量,然后通过 ${CUSTOM_VAR} 来引用
# SET(CUSTOM_VAR ${DIR_SRCS})
# 添加需要编译的源文件 test.cpp 和 输出的可执行文件名字 exe_main
# ADD_EXECUTABLE(${PROJECT_NAME} ${DIR_SRCS})
# 设置一个动态库名字,因为 `ADD_LIBRARY` 中的参数不给设置字符常量
SET(LIB_NAME statictest)
# 生成一个库
# ADD_LIBRARY(${LIB_NAME}
# SHARED
# src/u.cpp include/u.h)
# 连接一个现有的库
# 这种方法是可以的
LINK_DIRECTORIES(lib)
# ADD_LIBRARY(${LIB_NAME}
# SHARED
# IMPORTED)
# 这里以 `${CMAKE_CURRENT_SOURCE_DIR}` 为目录开始
#SET_TARGET_PROPERTIES(${LIB_NAME}
# PROPERTIES IMPORTED_LOCATION
# ${CMAKE_CURRENT_SOURCE_DIR}/lib/libdytest.dylib)
# 添加需要编译的源文件 test.cpp 和 输出的可执行文件名字 exe_main
ADD_EXECUTABLE(exe_main
src/test.cpp)
# 连接
TARGET_LINK_LIBRARIES(exe_main
${LIB_NAME})
和目录 ($ tree -L 2
)
.
├── CMakeLists.txt
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── Makefile
│ ├── cmake_install.cmake
│ └── exe_main
├── include
│ └── u.h
├── lib
│ ├── libdytest.dylib
│ └── libstatictest.a
└── src
├── test.cpp
└── u.cpp
最后发现,macOS
命令行上对.so
文件链接不成功,总会报一个库没有加载的错误,这大概就是macOS
对.dylib 和 .so
的不同处理吧!很多细节还需要补充,有空再继续吧 ~
[Note] Android Studio 中的 CMake 简单使用
Github