CMake 完整入门教程(五)

CMake 使用实例
13.1 例子一
一个经典的 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 的过程,本身也就是一个编程的过程,只有多练才
行。
我们先看看:前面提到的这些都是什么呢?
13.1.1 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 文件中,命令名字是不区分大小写的,而参数和变量是大小写
相关的。
13.1.2 cmake 命令
cmake 命令后跟一个路径 (..) ,用来指出 CMakeList.txt 所在的位置。
由于系统中可能有多套构建环境,我们可以通过 -G 来制定生成哪种工程文件,通
cmake -h 可得到详细信息。 要显示执行构建过程中详细的信息 ( 比如为了得到更详细的出错信息 ) ,可以在
CMakeList.txt 内加入:
SET( CMAKE_VERBOSE_MAKEFILE on )
或者执行 make
$ make VERBOSE=1
或者
$ export VERBOSE=1
$ make
13.2 例子二
一个源文件的例子一似乎没什么意思,拆成 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
例子很简单,没什么可说的。
13.3 例子三
接前面的例子,我们将 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")
就可以了
13.4 例子四
在前面,我们成功地使用了库,可是源代码放在同一个路径下,还是不太正规,怎么办
呢?分开放呗
我们期待是这样一种结构 +
|
+--- 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 ,用来指明头文件所在的路
径。
13.5 例子五
前面还是有一点不爽:如果想让可执行文件在 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")
13.6 例子六
在例子三至五中,我们始终用的静态库,那么用动态库应该更酷一点吧。 试着写一下
如果不考虑 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")
恩,剩下来的工作就和原来一样了。
14 CMake 的局限性
世界上没有完美的东西。 CMake 也有其局限性:
- CMake 并不遵守 GNU 规则。这使得 CMake 对一些开源软件的支持不够。例如,
CMake 生成出的工程在 Linux 下不支持 make uninstall
- CMake 和其他编译系统会打架。处理起来不容易。例如,如果不用 Qt 自带的
QMake ,而是用 CMake 去编译 Qt 工程,那是一件费时费力的事情。
- CMake 不会认识开发者在 IDE 中新增加的文件。必须通过修改 CMakeLists.txt ,才
能让 CMake 知道有新的文件。
15 常用网络资源
15.1 CMake 文档
http://www.cmake.org/cmake/help/v2.8.8/cmake.html
15.2 CMake Wiki
http://www.cmake.org/Wiki/CMake 15.3 CMake 入门
http://zh.wikibooks.org/wiki/CMake_%E5%85%A5%E9%96%80
16 附录
引用资源列表如下。时间仓促,难免有遗漏。如果您发现我引用了你的文章请告知。如果
您不希望您的文章被引用也请告知。可以到我的博客留言
http://blog.csdn.net/dbzhang800/article/details/6314073
http://tzc.is-programmer.com/show/476.html
http://blog.csdn.net/vagrxie/article/details/4743484
http://www.cs.swarthmore.edu/~adanner/tips/cmake.php
http://www.cnblogs.com/doujiu/archive/2009/11/04/1596155.html
http://blog.csdn.net/onion_autotest/article/details/7222954
http://www.cnblogs.com/coderfenghc/archive/2013/01/20/2846621.html
http://blog.csdn.net/gubenpeiyuan/article/details/8667279
http://name5566.com/1795.html
http://blog.sina.com.cn/s/blog_9ce5a1b501015avz.html
http://nullget.sourceforge.net/?q=node/94
http://blog.csdn.net/gubenpeiyuan/article/details/8667035
http://blogs.gnome.org/swilmet/2012/09/05/switch-from-cmake-to-autotools/
http://tech.uc.cn/?p=914

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/355024.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

导航页配置服务Dashy本地部署并实现公网远程访问

文章目录 简介1. 安装Dashy2. 安装cpolar3.配置公网访问地址4. 固定域名访问 简介 Dashy 是一个开源的自托管的导航页配置服务&#xff0c;具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一些网站聚合起来放在一起&#xff0c;形成自己的导航…

Unity 中介者模式 (实例详解)

文章目录 简介实例1&#xff1a;玩家与UI交互实例2&#xff1a;战斗模块中的攻击事件协调实例3&#xff1a;游戏场景中的事件广播实例4&#xff1a;模块间通信 - 地图导航与角色移动实例5&#xff1a;UI模块间同步 - 菜单切换与选项状态 简介 在Unity游戏开发中&#xff0c;中…

SpringBoot内置工具类

Collections java.util包下的Collections类&#xff0c;该类主要用于操作集合或者返回集合 一、排序 List<Integer> list new ArrayList<>();list.add(2);list.add(1);list.add(3);Collections.sort(list);//升序System.out.println(list);Collections.reverse(…

【机器学习】欠拟合与过拟合

过拟合&#xff1a;模型在训练数据上表现良好对不可见数据的泛化能力差。 欠拟合&#xff1a;模型在训练数据和不可见数据上泛化能力都很差。 欠拟合常见解决办法&#xff1a; &#xff08;1&#xff09;增加新特征&#xff0c;可以考虑加入特征组合、高次特征&#xff0c;以…

MySql45讲-08.事务到底是隔离的还是不隔离的?(结合MVCC视频)

命令的启动时机 begin/start transaction 命令并不是一个事务的起点&#xff0c;在执行到它们之后的第一个操作InnoDB表的语句&#xff0c;事务才真正启动。如果你想要马上启动一个事务&#xff0c;可以使用start transaction with consistent snapshot 这个命令。 事务的版本…

数据结构—基础知识(14):森林、树与二叉树的转换

数据结构—基础知识&#xff08;14&#xff09;&#xff1a;森林、树与二叉树的转换 将树转换为二叉树进行处理&#xff0c;利用二叉树的算法来实现对树的操作。由于树和二叉树都可以用二叉链表作存储结构&#xff0c;则以二叉链表作媒介可以导出树与二叉树之间的一个对应关系…

Nginx负载均衡下的webshell连接

一、上传AntSword-Labs-master搭建负载均衡实验环境 搭建好docker环境&#xff0c;并且配置好docker-compose 我的Redhat的docker版本&#xff1a; 查看当前环境下的文件是否正确&#xff1a; 接着执行docker compose up -d 拉取环境 访问成功页面&#xff1a; 进入docker容器…

RabbitMQ 死信交换机的详述➕应用

&#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于RabbitMQ的相关操作吧 目录 &#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 一.什么是死信交换机 二. 死信队列…

C++1.0

思维导图 提示输入一个字符串&#xff0c;统计该字符中大写&#xff0c;小写字母个数&#xff0c;数字个数&#xff0c;空格个数以及特殊字符个数&#xff0c;要求使用C风格字符串完成 #include <iostream>using namespace std;int main() {cout << "请输入一…

路由懒加载(React和Vue)

1、为了提升性能&#xff0c;将懒加载的文件单独打包 在webpack.config.js配置打包成chunks // 打包到不同的chunks optimization: {// 将动态加载(懒加载)的文件(imort())单独打包splitChunks: {chunks: "all",},// 避免分割缓存失效runtimeChunk: {name: (entrypo…

数据结构(C语言版)代码实现(五)——双向循环链表的部分实现

目录 参考材料与格式 线性表的有关知识 头文件 库、宏定义、数据类型声明 线性表的双向链表存储结构 构造空链表 销毁链表 链表长度 按位查找 插入元素 删除元素 打印链表 完整头文件DuLinkList.h 测试函数&#xff08;主函数&#xff09; 测试结果 收获 参考材…

电磁兼容(EMC):产品如何做到可靠的防静电设计

工业产品所应用的电磁环境之恶劣。要想产品在如此恶劣的电磁环境下正常工作&#xff0c;需要具备强大的抗干扰能力方能胜任。其中以静电干扰最为常见且棘手。本文将手把手教你如何将工业产品做到可靠的防静电设计。 1 了解静电 你想要打倒对手&#xff0c;必须先深入地了解他…

Redis 学习笔记 2:Java 客户端

Redis 学习笔记 2&#xff1a;Java 客户端 常见的 Redis Java 客户端有三种&#xff1a; Jedis&#xff0c;优点是API 风格与 Redis 命令命名保持一致&#xff0c;容易上手&#xff0c;缺点是连接实例是线程不安全的&#xff0c;多线程场景需要用线程池来管理连接。Redisson&…

预训练语言模型transformer

预训练语言模型的学习方法有三类&#xff1a;自编码&#xff08;auto-encode, AE)、自回归&#xff08;auto regressive, AR&#xff09;&#xff0c;Encoder-Decoder结构。 决定PTM模型表现的真正原因主要有以下几点&#xff1a; 更高质量、更多数量的预训练数据增加模型容量…

双非本科准备秋招(8.2)——JVM1

第一天系统学习JVM&#xff01;今天学了JVM是什么&#xff0c;学习JVM的作用&#xff0c;运行时的数据区域&#xff08;重点&#xff09;&#xff0c;内存溢出。明天学GC。 运行时数据区域 整体认识 JDK1.7 JDK1.8 先写一下每个线程私有的三个数据区&#xff0c;分别是程序计…

Docker—入门及Centos7安装

1、Docker入门 1.1、Docker是什么&#xff1f; Docker是基于Go语言实现的云开源项目。 Docker的主要目标是“Build&#xff0c;Ship&#xff0c;and Run Any App,Anywhere”&#xff0c;也就是通过对应组件的封装、分发、部署、运行等生命周期的管理&#xff0c;使用户的APP&…

模板笔记 ST表 区间选数k

本题链接&#xff1a;用户登录 题目&#xff1a; 样例&#xff1a; 输入 5 3 1 1 2 2 3 1 2 3 3 1 5 输出 4 6 思路&#xff1a; . 根据题意&#xff0c;给出数组&#xff0c;以及多个区间&#xff0c;问这些区间中&#xff0c;最小值之和 和 最大值之和&#xff0c;…

CHS_02.2.3.2_1+进程互斥的软件实现方法

CHS_02.2.3.2_1进程互斥的软件实现方法 知识总览如果没有注意进程互斥&#xff1f;单标志法双标志先检查法双标志后检查法Peterson 算法 知识回顾 在这个小节中 我们会学习进程互斥的几种软件实现方法 知识总览 那我们会学习单标志法 双标志 先检查 双标志后检查和Peterson算法…

前端工程化基础(三):Webpack基础

Webpack和打包过程 学习webpack主要是为了了解目前前端开发的整体流程&#xff0c;实际开发中&#xff0c;我们并不需要去手动配置&#xff0c;因为框架的脚手架都已经帮助我们完成了配置 内置模块path 该模块在Webpack中会经常使用 从路径中获取信息 const path require(&qu…

前端Vue v-for 的使用

目录 ​编辑 简介 使用方式 基本使用 v-for"(item, index)中item和index作用 示例 迭代对象 示例 结果 前言-与正文无关 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&#xff0c;我们往往容易陷入…