CMAKE简明教程

@firestaradmin 2020年12月18日

客路青山外,行舟绿水前。

潮平两岸阔,风正一帆悬。

海日生残夜,江春入旧年。

乡书何处达?归雁洛阳边。

​ ————《次北固山下》作者:王湾


CMAKE简明教程

一、CMake 构建

例程目录介绍

这是一个 CMake 的多目录,多子工程(应用),库模式链接和不使用库模式链接的例子。

工程目录如下图:

image-20201218114413028

  • app:该目录下有两个demo (应用程序or子工程)文件夹
  • app/demo1:该目录为demo1 应用程序的目录,会调用外部的驱动文件,和LIB库文件
  • app/demo2:同上
  • bin:可执行程序输出目录。(可在CMakeLists文件中修改)
  • build:CMake 输出文件夹
  • driver:驱动文件(本例程,该驱动就是实现一个myadd() 函数)
  • lib:lib库文件(实现了一个mysub() 函数)

其实driver 文件夹和lib 文件夹都是给APP 提供函数功能的,区别在于driver中的文件我们不编译为lib库,所以driver 文件夹中不需要CMakeLists 文件。


顶层CMake 文件

该文件位于 ./CMakeLists.txt 内容如下:

# 设置最低需求版本
cmake_minimum_required(VERSION 3.19)
# 设置工程名称
project (CMAKE_DEMO)
 
# include dir
# 添加头文件搜索路径,会继承到子目标
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/driver/include)   
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)            

# 添加子目录下的CMakeLists.txt
add_subdirectory(app/demo1)
add_subdirectory(app/demo2)
add_subdirectory(lib)


# 这里在子cmake中 链接,所以这里注释了
# 添加链接库
# target_link_libraries(demo1 myMathLib)

# 添加链接库
# target_link_libraries(demo2 myMathLib)

APP 下的子工程 CMake 文件

./app/demo1/CMakeLists.txt :

cmake_minimum_required(VERSION 3.19)

project (demo1)
 
# 添加头文件搜索路径
include_directories(.)

# aux_source_directory 会搜索目录下所有源文件
aux_source_directory(./ SOURCE_FILE)
# CMAKE_CURRENT_SOURCE_DIR 指向当前目录
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/../../driver/src/ SOURCE_FILE2)

# CMAKE_SOURCE_DIR 指向总工程的根目录,设置 EXECUTABLE_OUTPUT_PATH 变量,将可执行文件存放到该目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin)


# 输出可执行文件
add_executable(demo1 ${SOURCE_FILE} ${SOURCE_FILE2})

# 添加链接库
target_link_libraries(demo1 myMathLib)

./app/demo2/CMakeLists.txt :

cmake_minimum_required(VERSION 3.19)

project (demo2)
 
# 添加头文件搜索路径
include_directories(.)

# aux_source_directory 会搜索目录下所有源文件
# aux_source_directory(./ SOURCE_FILE)
# CMAKE_CURRENT_SOURCE_DIR 指向当前目录
# aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/../../driver/src/ SOURCE_FILE2)
# 有时候可能不需要目录下的所有源文件,可以使用SET 来指定源文件. CMAKE_SOURCE_DIR 指向总工程的根目录
set(SRC_LIST 
    main.c
    ${CMAKE_SOURCE_DIR}/driver/src/mymath.c
    )

# CMAKE_SOURCE_DIR 指向总工程的根目录,设置 EXECUTABLE_OUTPUT_PATH 变量,将可执行文件存放到该目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin)


# 输出可执行文件
add_executable(demo2 ${SRC_LIST})

# 添加链接库
target_link_libraries(demo2 myMathLib)

LIB 下的CMake 文件

./lib/CMakeLists.txt:

cmake_minimum_required(VERSION 3.19)

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)

# 生成链接库
# add_library(<name> [STATIC | SHARED | MODULE]
#                    [EXCLUDE_FROM_ALL]
#                    [<source>...])
# 默认 不填写是静态库 也就是 STATIC
# STATIC:静态创建库,此模式该库会集成到应用程序中。
# SHARED:动态创建库,会生成.dll .so .a 等lib文件,需要将文件和程序放在同一目录,才可运行程序。
# MODULE:在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待。
# EXCLUDE_FROM_ALL:这个库不会被默认构建,除非有其他的组件依赖或者手工构建。
add_library (myMathLib ${DIR_LIB_SRCS})
# add_library (myMathLib STATIC ${DIR_LIB_SRCS})

二、编译输出

1、在根目录创建 build 目录,进入后输入如下命令:

cmake ..
或者
cmake .. -G "MingW Makefiles"

-G 指定编译器,控制台输入 cmake --help 即可查询编译器名称

2、cmake 后使用make 进行编译链接

win10 下:

在build/ 目录下 输入以下命令(以MinGW 为例):

mingw32-make
mingw32-make demo1
mingw32-make clean
  • mingw32-make:默认编译全部 target
  • mingw32-make demo1:只编译 demo1
  • mingw32-make clean:删除所有编译

Linux 下:

mingw32-make
make demo1
make clean
  • make:默认编译全部 target
  • make demo1:只编译 demo1
  • make clean:删除所有编译

三、文件内容

1、demo

./app/demo1/main.c

#include <stdio.h>
#include "main.h"
#include "mymath.h"
#include "mymath2.h"


void main()
{
    int add_temp, sub_temp;
    add_temp = myadd(NUM1, 10);
    sub_temp = mysub(NUM1, 10);
    printf("======= demo1: ========\r\n");
    printf("add result= %d\r\n", add_temp);
    printf("sub result= %d\r\n", sub_temp);

    char c;
    c = getchar();
}

./app/demo1/main.h

#ifndef __MAIN_H_
#define __MAIN_H_

#define NUM1 100

#endif // !__MAIN_H_

2、driver

./driver/include/mymath.h

#ifndef __MYMATH_H_
#define __MYMATH_H_
int myadd(int a, int b);


#endif // !__MYMATH_H_

./driver/include/mymath.c

#include "mymath.h"

int myadd(int a, int b)
{
    return a+b;
}

3、lib

./lib/mymath2.h

#ifndef __MYMATH2_H_
#define __MYMATH2_H_
int mysub(int a, int b);


#endif // !__MYMATH2_H_

./lib/mymath2.c

#include "mymath2.h"

int mysub(int a, int b)
{
    return a-b;
}

四、 配合SH 脚本

build.sh


#!/bin/bash
BUILD_PROJECT=c_test
NINJA_EXE=$(pwd)/tools/ninja.exe
# echo ${NINJA_EXE} 
CMAKE_EXE="D:/Program Files/CMAKE/bin/cmake.exe"

# because path have space, so use '' 
CMAKE_OPTIONS="-DCMAKE_MAKE_PROGRAM=${NINJA_EXE}"
# echo ${CMAKE_OPTIONS}



echo "clean first ..."
. clean.sh


echo "mkdir build"
if [ ! -d "./build/" ];then
    mkdir build
else
    echo "build folder already exist"
fi
cd build
"${CMAKE_EXE}" -S .. -G "Ninja" ${CMAKE_OPTIONS}
# ${NINJA_EXE} -j 64 
# ${NINJA_EXE} -j 64 c_test
${NINJA_EXE} -j 64 ${BUILD_PROJECT} > ../build_log.txt 2>&1

clean.sh


#!/bin/bash
echo "clean build ..."

rm -rf ./build/



五、使用ninja 并且放入工程文件下



# 设置最低需求版本
cmake_minimum_required(VERSION 3.19)


# set(CMAKE_SYSTEM_NAME Generic)

# specify make program
set(CMAKE_MAKE_PROGRAM ${CMAKE_CURRENT_SOURCE_DIR}/tools/ninja.exe)

# specify the cross compiler
set(CMAKE_C_COMPILER "D:/Program Files/mingw64/bin/gcc.exe")
set(CMAKE_CXX_COMPILER "D:/Program Files/mingw64/bin/g++.exe")

# generate flags from user variables
set(COMPILE_FLAGS "-W")
set(CMAKE_C_FLAGS "${COMPILE_FLAGS} ")
set(CMAKE_CXX_FLAGS "${COMPILE_FLAGS} ")





# 设置工程名称
project (c_cpp_demo)
 
# include dir
# 添加头文件搜索路径,会继承到子目标
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/driver/include)   
# include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)            
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib/c/ug_log)            

# 添加子目录下的CMakeLists.txt
# add_subdirectory(app/demo1)
# add_subdirectory(app/demo2)
# add_subdirectory(lib)
add_subdirectory(app/c_test)



# 这里在子cmake中 链接,所以这里注释了
# 添加链接库
# target_link_libraries(demo1 myMathLib)

# 添加链接库
# target_link_libraries(demo2 myMathLib)

OTHER:


添加外部目录为子目录:

如果要添加的子目录是同级或者外部,直接用 add_subdirectory 就不行了。

原来add_subdirectory还有一个 binary_dir参数(一般这个参数用不到,所以从来没关注过)

这个参数用来指定source_dir在输出文件夹中的位置,如果没有指定的时候,就用source_dir的值。

如果要添加外部文件夹,binary_dir就必须指定。

如 要添加上级的src目录:

# mySrc.out参数用于指定外部文件夹在输出文件夹中的位置
add_subdirectory(${CMAKE_SOURCE_DIR}/../mySrc mySrc.out)

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!