cmake学习

这篇博客介绍了CMake在构建C程序中的应用,从单个源文件到多文件、库的构建,再到目录结构管理和动态库的使用。通过实例展示了CMakeLists.txt的编写,包括项目设置、源文件管理、库的生成与链接、输出路径控制等。文章强调了CMake在跨平台构建中的重要性,并鼓励逐步学习和实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

cmake学习

1.同一目录多个源文件

目录结构
├── CMakeLists.txt
├── main.c
├── Num.c
└── Num.h
式例代码
main.c
#include <stdio.h>
# include "Num.h"
int main() {
    printf("Hello, World!\n");
    add(2,8);
    return 0;
}

代码Num.h
//
// Created by deepin on 2020/3/27.
//
#ifndef MAKE01_NUM_H
#define MAKE01_NUM_H
int add(int a,int b);
int sub(int a,int b);
int mul(int a,int b);
int div(int a,int b);
#endif //MAKE01_NUM_H

代码Num.c
#include <stdio.h>

int add(int a,int b){
    int c = a+b;
    printf("%d+%d=%d\n",a,b,c);
    return c;
}
int sub(int a,int b){
    int c = a-b;
    printf("%d-%d=%d\n",a,b,c);
    return c;
}
int mul(int a,int b){
    int c = a*b;
    printf("%d*%d=%d\n",a,b,c);
    return c;
}
int div(int a,int b){
    int c = a/b;
    printf("%d/%d=%d\n",a,b,c);
    return c;
}
编译命令
不使用cmake
//编译
gcc main.c Num.c
//运行
./a.out

使用cmake
  • 创建 CMakeLists.txt

  • 编写命令

  • cmake_minimum_required(VERSION 3.15) #cmkae最低版本
    project(make01 C) # 项目名称 make01
    
    set(CMAKE_C_STANDARD 11)# 使用c11标准
    
    add_executable(make01 main.c Num.h Num.c) #生成make01的可执行文件,编译源文件包括main.c Num.h Num.c #注意.h文件在这种情况下可以省略
    
  • 当前目录执行cmke .

  • 在得到Makefile之后执行make,产生了可执行文件

看来不学习一下cmake是不行了,一点一点来吧,找个最简单的C程序,慢慢复杂化,试试看:

例子一

单个源文件 main.c

例子二

==>分解成多个 main.c hello.h hello.c

例子三

==>先生成一个静态库,链接该库

例子四

==>将源文件放置到不同的目录

例子五

==>控制生成的程序和库所在的目录

例子六

==>使用动态库而不是静态库

例子一
一个经典的C程序,如何用cmake来进行构建程序呢?

//main.c
#include <stdio.h>
int main()
{
    printf("Hello World!/n");
    return 0;
}
编写一个 CMakeList.txt 文件(可看做cmake的工程文件)project(HELLO)
set(SRC_LIST main.c)
add_executable(hello ${SRC_LIST})
然后,建立一个任意目录(比如本目录下创建一个build子目录),在该build目录下调用cmake

注意:为了简单起见,我们从一开始就采用cmake的 out-of-source 方式来构建(即生成中间产物与源代码分离),并始终坚持这种方法,这也就是此处为什么单独创建一个目录,然后在该目录下执行 cmake 的原因
cmake .. -G"NMake Makefiles"
nmake
或者

cmake .. -G"MinGW Makefiles"
make
即可生成可执行程序 hello(.exe)

目录结构

+
| 
+--- main.c
+--- CMakeList.txt
|
/--+ build/
   |
   +--- hello.exe
cmake 真的不太好用哈,使用cmake的过程,本身也就是一个编程的过程,只有多练才行。

我们先看看:前面提到的这些都是什么呢?

CMakeList.txt
第一行 project 不是强制性的,但最好始终都加上。这一行会引入两个变量

HELLO_BINARY_DIR 和 HELLO_SOURCE_DIR
同时,cmake自动定义了两个等价的变量

PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR

因为是out-of-source方式构建,所以我们要时刻区分这两个变量对应的目录

可以通过message来输出变量的值

message(${PROJECT_SOURCE_DIR})
set 命令用来设置变量

add_exectuable 告诉工程生成一个可执行文件。

add_library 则告诉生成一个库文件。

注意:CMakeList.txt 文件中,命令名字是不区分大小写的,而参数和变量是大小写相关的。
cmake命令
cmake 命令后跟一个路径(..),用来指出 CMakeList.txt 所在的位置。

由于系统中可能有多套构建环境,我们可以通过-G来制定生成哪种工程文件,通过 cmake -h 可得到详细信息。

要显示执行构建过程中详细的信息(比如为了得到更详细的出错信息),可以在CMakeList.txt内加入:

SET( CMAKE_VERBOSE_MAKEFILE on )
或者执行make时

$ make VERBOSE=1
或者

$ export VERBOSE=1
$ make

例子二
一个源文件的例子一似乎没什么意思,拆成3个文件再试试看:

hello.h 头文件

#ifndef DBZHANG_HELLO_
#define DBZHANG_HELLO_
void hello(const char* name);
#endif //DBZHANG_HELLO_
hello.c
#include <stdio.h>
#include "hello.h"

void hello(const char * name)
{
    printf ("Hello %s!/n", name);
}
main.c
#include "hello.h"
int main()
{
    hello("World");
    return 0;
}

然后准备好CMakeList.txt 文件

project(HELLO)
set(SRC_LIST main.c hello.c)
add_executable(hello ${SRC_LIST})
执行cmake的过程同上,目录结构

+
| 
+--- main.c
+--- hello.h
+--- hello.c
+--- CMakeList.txt
|
/--+ build/
   |
   +--- hello.exe
例子很简单,没什么可说的。

例子三
接前面的例子,我们将 hello.c 生成一个库,然后再使用会怎么样?

改写一下前面的CMakeList.txt文件试试:

project(HELLO)
set(LIB_SRC hello.c)
set(APP_SRC main.c)
add_library(libhello ${LIB_SRC})
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)

和前面相比,我们添加了一个新的目标 libhello,并将其链接进hello程序

然后想前面一样,运行cmake,得到

+
| 
+--- main.c
+--- hello.h
+--- hello.c
+--- CMakeList.txt
|
/--+ build/
   |
   +--- hello.exe
   +--- libhello.lib
里面有一点不爽,对不?

因为我的可执行程序(add_executable)占据了 hello 这个名字,所以 add_library 就不能使用这个名字了
然后,我们去了个libhello 的名字,这将导致生成的库为 libhello.lib(或 liblibhello.a),很不爽
想生成 hello.lib(或libhello.a) 怎么办?
添加一行

set_target_properties(libhello PROPERTIES OUTPUT_NAME “hello”)
就可以了

例子四
在前面,我们成功地使用了库,可是源代码放在同一个路径下,还是不太正规,怎么办呢?分开放呗

我们期待是这样一种结构

+
|
+--- CMakeList.txt
+--+ src/
|  |
|  +--- main.c
|  /--- CMakeList.txt
|
+--+ libhello/
|  |
|  +--- hello.h
|  +--- hello.c
|  /--- CMakeList.txt
|
/--+ build/

哇,现在需要3个CMakeList.txt 文件了,每个源文件目录都需要一个,还好,每一个都不是太复杂

顶层的CMakeList.txt 文件

project(HELLO)
add_subdirectory(src)
add_subdirectory(libhello)
src 中的 CMakeList.txt 文件
include_directories(${PROJECT_SOURCE_DIR}/libhello)
set(APP_SRC main.c)
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)
libhello 中的 CMakeList.txt 文件
set(LIB_SRC hello.c)
add_library(libhello ${LIB_SRC})
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

恩,和前面一样,建立一个build目录,在其内运行cmake,然后可以得到

build/src/hello.exe
build/libhello/hello.lib
回头看看,这次多了点什么,顶层的 CMakeList.txt 文件中使用 add_subdirectory 告诉cmake去子目录寻找新的CMakeList.txt 子文件

在 src 的 CMakeList.txt 文件中,新增加了include_directories,用来指明头文件所在的路径。

例子五
前面还是有一点不爽:如果想让可执行文件在 bin 目录,库文件在 lib 目录怎么办?

就像下面显示的一样:

   + build/
   |
   +--+ bin/
   |  |
   |  /--- hello.exe
   |
   /--+ lib/
      |
      /--- hello.lib

一种办法:修改顶级的 CMakeList.txt 文件
project(HELLO)
add_subdirectory(src bin)
add_subdirectory(libhello lib)
不是build中的目录默认和源代码中结构一样么,我们可以指定其对应的目录在build中的名字。

这样一来:build/src 就成了 build/bin 了,可是除了 hello.exe,中间产物也进来了。还不是我们最想要的。

另一种方法:不修改顶级的文件,修改其他两个文件
src/CMakeList.txt 文件

include_directories(${PROJECT_SOURCE_DIR}/libhello)
#link_directories(${PROJECT_BINARY_DIR}/lib)
set(APP_SRC main.c)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)
libhello/CMakeList.txt 文件

set(LIB_SRC hello.c)
add_library(libhello ${LIB_SRC})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

例子六
在例子三至五中,我们始终用的静态库,那么用动态库应该更酷一点吧。 试着写一下

如果不考虑windows下,这个例子应该是很简单的,只需要在上个例子的 libhello/CMakeList.txt 文件中的add_library命令中加入一个SHARED参数:

add_library(libhello SHARED ${LIB_SRC})
可是,我们既然用cmake了,还是兼顾不同的平台吧,于是,事情有点复杂:

修改 hello.h 文件

#ifndef DBZHANG_HELLO_
#define DBZHANG_HELLO_
#if defined _WIN32
    #if LIBHELLO_BUILD
        #define LIBHELLO_API __declspec(dllexport)
    #else
        #define LIBHELLO_API __declspec(dllimport)
    #endif
#else
    #define LIBHELLO_API
#endif
LIBHELLO_API void hello(const char* name);
#endif //DBZHANG_HELLO_
修改 libhello/CMakeList.txt 文件
set(LIB_SRC hello.c)
add_definitions("-DLIBHELLO_BUILD")
add_library(libhello SHARED ${LIB_SRC})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

一、同一目录

├── CMakeLists.txt
├── Integer.cpp
├── Integer.h
├── main.cpp
├── Number.cpp
├── Number.h
├── Num.c
├── Num.h
cmake_minimum_required(VERSION 3.15)
project(make01)#asdfgad
#
set(CMAKE_C_STANDARD 11)
#aux_source_directory(src SRC)
#aux_source_directory(head HEAR)
#add_subdirectory(src/num)
# src/num/Integer.h src/num/Integer.cpp src/num/Number.h src/num/Number.cpp
add_executable(make01 main.cpp Num.h Num.c Number.h  Integer.h Integer.cpp)

二、

├── CMakeLists.txt
├── main
├── main.c
├── testFunc1.c
├── testFunc1.h
├── testFunc.c
└── testFunc.h
cmake_minimum_required (VERSION 2.8)
project (demo)
aux_source_directory(. SRC_LIST)
add_executable(main ${SRC_LIST})

三、不同目录

├── CMakeLists.txt
├── main
├── main.c
├── testfunc
│   ├── testFunc.c
│   └── testFunc.h
└── testfunc1
    ├── testFunc1.c
    └── testFunc1.h
cmake_minimum_required (VERSION 2.8)
project (demo)
# 头文件目录
include_directories (testfunc testfunc1)
aux_source_directory (testfunc SRC_LIST)
aux_source_directory (testfunc1 SRC_LIST1)
add_executable (main main.c ${SRC_LIST} ${SRC_LIST1})

四、

├── bin
│   └── main
├── CMakeLists.txt
├── include
│   ├── testFunc1.h
│   └── testFunc.h
└── src
    ├── CMakeLists.txt
    ├── main.c
    ├── testFunc1.c
    └── testFunc.c
#外层
cmake_minimum_required (VERSION 2.8)
project (demo)
add_subdirectory (src)

#里层
aux_source_directory (. SRC_LIST)
include_directories (../include)
add_executable (main ${SRC_LIST})
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

├── bin
├── CMakeLists.txt
├── include
│   ├── testFunc1.h
│   └── testFunc.h
├── lib
│   ├── libtestFunc.a
│   └── libtestFunc.so
└── src
    ├── testFunc1.c
    └── testFunc.c
#动静态库制作
cmake_minimum_required (VERSION 3.5)
project (demo)
set (SRC_LIST ${PROJECT_SOURCE_DIR}/src/testFunc.c)
include_directories (include)
add_library (testFunc_shared SHARED ${SRC_LIST})
add_library (testFunc_static STATIC ${SRC_LIST})
set_target_properties (testFunc_shared PROPERTIES OUTPUT_NAME "testFunc")
set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

六、

├── bin
│   └── main
├── CMakeLists.txt
├── include
│   ├── testFunc1.h
│   └── testFunc.h
├── lib
│   ├── libtestFunc1.so
│   └── libtestFunc_static.a
└── src
    ├── main.c
    ├── testFunc1.c
    └── testFunc.c
cmake_minimum_required (VERSION 3.5)
project (demo)
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
set (SRC_LIST ${PROJECT_SOURCE_DIR}/src/main.c)
set (SHARD_SRC_LIST ${PROJECT_SOURCE_DIR}/src/testFunc1.c)
set (STATIC_SRC_LIST ${PROJECT_SOURCE_DIR}/src/testFunc.c)
include_directories (include)
#生成动/静态库
add_library (testFunc1_shared SHARED ${SHARD_SRC_LIST})
add_library (testFunc_static STATIC ${STATIC_SRC_LIST})
set_target_properties (testFunc1_shared PROPERTIES OUTPUT_NAME "testFunc1")
#set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
find_library(TESTFUNC1_LIB testFunc1 HINTS ${PROJECT_SOURCE_DIR}/lib)
find_library(TESTFUNC_LIB testFunc HINTS ${PROJECT_SOURCE_DIR}/lib)
add_executable (main ${SRC_LIST})
target_link_libraries (main ${TESTFUNC1_LIB} testFunc_static)

在这里插入图片描述

在这里插入图片描述

本文开发环境:deppinv15.5

开发ide clion

最后两张图片为搬运

注意cmake没有的可以自己下载安装,我就是用clion自带的cmake(加入环境变量即可)

原文地址(本人自己的博客网站)

https://www.pingyuanren.top/article/25

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值