[Note] CMake的简单使用

2019-03-30  本文已影响0人  赶时间的闹钟

当前环境 macOScmake 3.9.4

一 gcc, make, cmake 的区别

  1. gcc 是一个编译器套件,用于编译多种语言的源文件
  2. gcc 需要编译多个文件时,编译指令很多时,则会使用 makefile 按照特定规则来对指令(gcc操作指令)进行整合,使用 make 工具来执行指令组。
  3. 由于 makefile 依赖于确定的编译器套件(如指令中的gcc ...),这时出现了通用工具CMake(根据实际的平台/环境选择编译套件并进行编译),与之搭配的是 CMakeLists.txt,输出是makefile
  4. 依次次进化和依赖的关系

二 编译一个 .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})

文件准备完毕,cdbuild目录执行

$ 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

上一篇下一篇

猜你喜欢

热点阅读