imx6ull/linux应用编程学习(13) CMAKE

什么是cmake?

        cmake 工具通过解析 CMakeLists.txt 自动帮我们生成 Makefile,可以实现跨平台的编译。cmake 就是用来产生 Makefile 的工具,解析 CMakeLists.txt 自动生成 Makefile:

cmake 的使用方法
 

cmake 就是一个工具命令,在 Ubuntu 系统下通过 apt-get 命令可以在线安装,如下所示
 

sudo apt-get install cmake

利用cmake --version命令检查:

使用实例1.单个源文件:

一个经典的 C 程序“Hello World”,如何用 cmake 来进行构建呢?

首先利用touch命令创建俩文件(mkdir是创建文件目录),后利用vi工具将代码写入。

touch main.c
touch CMakeLists.txt
//main.c
#include <stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}

        现在我们需要新建一个CMakeLists.txt文件, CMakeLists.txt文件会被cmake工具解析,就好比是Makefile文件会被 make 工具解析一样; CMakeLists.txt 创建完成之后,在文件中写入如下内容:

project(HELLO)
add_executable(hello ./main.c)

写入完成之后,保存退出,当前工程目录结构如下所示:
 

├── CMakeLists.txt
└── main.c

在我们的工程目录下有两个文件,源文件 main.c 和 CMakeLists.txt,接着我们在工程目录下直接执行cmake 命令,如下所示:
 

cmake ./

可见

成功生成了文件。有了 Makefile 之后,接着我们使用 make 工具编译我们的工程

make

利用file hello 可见:

其中有arm,需要在arm开发板上运行,利用scp指令将文件传至开发板

开发板执行./hello,可见:

是不是有疑问,为什么是在开发板上运行而不是Ubuntu上?

这是因为电脑安装了交叉编译链,所以生成了arm环境下的编译文件。需要在ubuntu上运行也很简单;

我们先来看看CMakeLists.txt文件里的语句的意思:

project(HELLO)

        这条指令定义了一个名为 HELLO 的项目。它会告诉 CMake 这是一个新项目,并且所有接下来的指令都将在这个项目的上下文中执行。多个参数使用空格分隔而不是逗号“,”

add_executable(hello ./main.c)

        这条指令用于定义一个可执行目标文件。它告诉 CMake 要创建一个名为 hello 的可执行文件,并且这个可执行文件是由 main.c 源文件编译而来的。第一个参数表示生成的可执行文件对应的文件名,第二个参数表示对应的源文件; 所以 add_executable(hello ./main.c)表示需要生成一个名为 hello 的可执行文件,所需源文件为当前目录下的 main.c

因为我们之前设置的环境默认为交叉编译链的 ,所以只要在里面声明就行

将之前编译生成的文件删除。

后修改 CMakeLists.txt文件,改为

set(CMAKE_C_COMPILER "gcc")
set(CMAKE_CXX_COMPILER "g++")project(HELLO)
add_executable(hello ./main.c)

第一句指定c语言的利用gcc,c++的利用g++编译器。

后make,执行file hello可见:

环境为x86-64.成功

在ubuntu执行

./hello

但是各位是否有没有发现,当我们改编译器重新编译的时候,需要把生成文件删除特别麻烦,所以我们可以用一个更加简便的方法:

使用 out-of-source 方式构建
 

        我们需要将构建过程生成的文件与源文件分离开来, 不让它们混杂在一起,也就是使用 out-of-source 方式构建。

        将 cmake 编译生成的文件清理下,然后在工程目录下创建一个 build 目录

然后我们进入build文件夹进行编译:

利用

cmake ../

可以编译上级文件夹

日后如果要清除,直接返回上级目录 执行:

rm -rf build/*

这样子编译文件就删光啦!

使用示例二:多个源文件
 

        我们再加入一个 hello.h 头文件和 hello.c 源文件。在 hello.c 文件中定义了一个函数 hello,然后在 main.c 源文件中将会调用该函数

touch hello. c hello.h生成俩文件

hello.h 文件内容

#ifndef __TEST_HELLO_
#define __TEST_HELLO_
void hello(const char *name);
#endif //__TEST_HELLO_

hello.c 文件内容(注意,%s指的是将name换成字符串,!在后面)
 

#include <stdio.h>
#include "hello.h"
void hello(const char *name)
{
printf("Hello %s!\n", name);
}

main.c 文件内容

#include "hello.h"
int main(void)
{
hello("World");
return 0;
}

然后准备好 CMakeLists.txt 文件
(如果你默认环境是gcc,想要在开发板运行,你只要把gcc换成你的编译工具链就行,比如set(CMAKE_C_COMPILER " arm-linux-gnueabihf-gcc"))

set(CMAKE_C_COMPILER "gcc")
set(CMAKE_CXX_COMPILER "g++")
project(HELLO)
set(SRC_LIST main.c hello.c)
add_executable(hello ${SRC_LIST})

set(SRC_LIST main.c hello.c) 是 CMakeLists.txt 文件中的一条命令,用于定义一个变量并给它赋值。在这个例子中,SRC_LIST 变量被设置为包含两个源文件 main.chello.c

SRC_LIST

  • SRC_LIST 是变量名。你可以使用任何合适的名称来代表你的变量。
  • 这个变量通常用来存储源文件列表,以便在后续命令中引用。

add_executable(hello ${SRC_LIST})这行代码定义了一个名为 hello 的可执行文件,并使用 SRC_LIST 变量中的源文件来构建该可执行文件。add_executable 函数将 main.chello.c 编译并链接为一个名为 hello 的可执行文件。

cmake ../

make,file hello查看文件

编译成功。

使用示例三:生成库文件

        在本例中,除了生成可执行文件 hello 之外,我们还需要将 hello.c 编译为静态库文件或者动态库文件,在示例二的基础上对 CMakeLists.txt 文件进行修改,如下所示:

set(CMAKE_C_COMPILER "gcc")
set(CMAKE_CXX_COMPILER "g++")project(HELLO)
add_library(libhello hello.c)
add_executable(hello main.c)
target_link_libraries(hello libhello)

        进入到 build 目录下,执行 cmake、再执行 make 编译工程,编译完成之后,在 build 目录下就会生成可执行文件 hello 和库文件,如下所示:

本例中我们使用到了 add_library 命令和 target_link_libraries 命令。

        add_library 命令用于生成库文件,在本例中我们传入了两个参数,第一个参数表示库文件的名字,需要注意的是,这个名字是不包含前缀和后缀的名字; 在 Linux 系统中,库文件的前缀是 lib,动态库文件的后缀是.so,而静态库文件的后缀是.a; 所以,意味着最终生成的库文件对应的名字会自动添加上前缀和后缀。

        第二个参数表示库文件对应的源文件。

        本例中, add_library 命令生成了一个静态库文件 liblibhello.a,如果要生成动态库文件,可以这样做:

add_library(libhello SHARED hello.c) #生成动态库文件
add_library(libhello STATIC hello.c) #生成静态库文件

        target_link_libraries 命令为目标指定依赖库,在本例中, hello.c 被编译为库文件, 并将其链接进 hello 程序

总结

  • add_library(libhello hello.c):定义一个名为 libhello 的库,使用 hello.c 源文件。

  • add_executable(hello main.c):定义一个名为 hello 的可执行文件,使用 main.c 源文件。
  • target_link_libraries(hello libhello):将 libhello 库链接到 hello 可执行文件,使得可执行文件可以调用库中的函数。

作用是什么呢?

代码复用

  • 静态库(Static Library):将常用的函数和逻辑封装在一个静态库中,可以在多个项目中复用这些函数,而不需要每次都复制代码。
  • 动态库(Dynamic Library):动态库在不同的程序之间共享库中的代码,节省内存空间,并且可以在程序运行时加载和更新。

如果在CMakeLists.txt文件中添加下面这条命令:

set_target_properties(libhello PROPERTIES OUTPUT_

可以将生成的库为 liblibhello.a改成libhello.a。

使用示例四:将源文件组织到不同的目录
 

        上面的示例中,我们已经加入了多个源文件,但是这些源文件都是放在同一个目录下,这样还是不太正规,我们应该将这些源文件按照类型、功能、模块给它们放置到不同的目录下,于是笔者将工程源码进行了整理,当前目录结构如下所示:

├── build #build 目录
├── CMakeLists.txt
├── libhello
│ ├── CMakeLists.txt
│ ├── hello.c
│ └── hello.h
└── src
├── CMakeLists.txt
└── main.c

        在工程目录下,我们创建了 src 和 libhello 目录,并将 hello.c 和 hello.h 文件移动到 libhello 目录下,将main.c 文件移动到 src 目录下,并且在顶层目录、 libhello 目录以及 src 目录下都有一个 CMakeLists.txt 文件。CMakeLists.txt 文件的数量从 1 个一下变成了 3 个,我们来看看每一个 CMakeLists.txt 文件的内容。


顶层 CMakeLists.txt
 
cmake_minimum_required(VERSION 3.5)
project(HELLO)
add_subdirectory(libhello)
add_subdirectory(src)

src 目录下的 CMakeLists.txt
 

include_directories(${PROJECT_SOURCE_DIR}/libhello)
add_executable(hello main.c)
target_link_libraries(hello libhello)

libhello 目录下的 CMakeLists.txt
 

add_library(libhello hello.c)
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

        顶层 CMakeLists.txt 中使用了 add_subdirectory 命令, 该命令告诉 cmake 去子目录中寻找新的CMakeLists.txt 文件并解析它;而在 src 的 CMakeList.txt 文件中,新增加了 include_directories 命令用来指明头文件所在的路径,并且使用到了 PROJECT_SOURCE_DIR 变量,该变量指向了一个路径,从命名上可知,该变量表示工程源码的目录。

        和前面一样,进入到 build 目录下进行构建、编译,最终会得到可执行文件 hello(build/src/hello)和库文件 libhello.a(build/libhello/libhello.a):

├── build
│ ├── libhello
│ │ └── libhello.a
│ └── src
│ └── hello
├── CMakeLists.txt
├── libhello
│ ├── CMakeLists.txt
│ ├── hello.c
│ └── hello.h
└── src
├── CMakeLists.txt
└── main.c

使用实例5:将生成的可执行文件和库文件放置到单独的目录下
 

        在默认情况下, make 编译生成的可执行文件和库文件会与 cmake 命令产生的中间文件(CMakeCache.txt、 CmakeFiles、 cmake_install.cmake 以及 Makefile 等)混在一起,也就是它们在同一个目录下; 如果我想让可执行文件单独放置在 bin 目录下,而库文件单独放置在 lib 目录下,就像下面这样:

├── build
├── lib
│ └── libhello.a
└── bin
└── hello

        将库文件存放在 build 目录下的 lib 目录中,而将可执行文件存放在 build 目录下的 bin 目录中,这个时候又该怎么做呢?这个时候我们可以通过两个变量来实现,将 src 目录下的 CMakeList.txt 文件进行修改,如下所示:

include_directories(${PROJECT_SOURCE_DIR}/libhello)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(hello main.c)
target_link_libraries(hello libhello)

然后再对 libhello 目录下的 CMakeList.txt 文件进行修改,如下所示:
 

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

        修改完成之后,再次按照步骤对工程进行构建、编译,此时便会按照我们的要求将生成的可执行文件hello 放置在 build/bin 目录下、库文件 libhello.a 放置在 build/lib 目录下。 最终的目录结构就如下所示:

├── build
│ ├── bin
│ │ └── hello
│ └── lib
│ └── libhello.a
├── CMakeLists.txt
├── libhello
│ ├── CMakeLists.txt
│ ├── hello.c
│ └── hello.h
└── src
├── CMakeLists.txt
└── main.c

        其实实现这个需求非常简单,通过对 LIBRARY_OUTPUT_PATH 和 EXECUTABLE_OUTPUT_PATH变 量 进 行 设 置 即 可 完 成 ; EXECUTABLE_OUTPUT_PATH 变 量 控 制 可 执 行 文 件 的 输 出 路 径 , 而LIBRARY_OUTPUT_PATH 变量控制库文件的输出路径。

CMakeLists.txt 语法规则
 

篇幅较长,建议看手册

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么将aac文件弄成mp3格式?把aac改成MP3格式的四种方法

怎么将aac文件弄成mp3格式&#xff1f;手头有一些aac格式的音频文件&#xff0c;但由于某些设备或软件不支持这种格式&#xff0c;你希望将它们转换成更为通用的MP3格式。而且音频格式的转换在现在已经是一个常见且必要的操作。aac是一种相对较新的音频编码格式&#xff0c;通常…

大模型增量预训练新技巧-解决灾难性遗忘

大模型增量预训练新技巧-解决灾难性遗忘 机器学习算法与自然语言处理 2024年03月21日 00:02 吉林 以下文章来源于NLP工作站 &#xff0c;作者刘聪NLP NLP工作站. AIGC前沿知识分享&落地经验总结 转载自 | NLP工作站 作者 | 刘聪NLP 目前不少开源模型在通用领域具有不错…

el-scrollbar实现自动滚动到底部(AI聊天)

目录 项目背景 实现步骤 实现代码 完整示例代码 项目背景 chatGPT聊天消息展示滚动面板&#xff0c;每次用户输入提问内容或者ai进行流式回答时需要不断的滚动到底部确保展示最新的消息。 实现步骤 采用element ui 的el-scrollbar作为聊天消息展示组件。 通过操作dom来实…

理解算法复杂度:空间复杂度详解

引言 在计算机科学中&#xff0c;算法复杂度是衡量算法效率的重要指标。时间复杂度和空间复杂度是算法复杂度的两个主要方面。在这篇博客中&#xff0c;我们将深入探讨空间复杂度&#xff0c;了解其定义、常见类型以及如何进行分析。空间复杂度是衡量算法在执行过程中所需内存…

昇思25天学习打卡营第19天|Diffusion扩散模型

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) Diffusion扩散模型 本文基于Hugging Face&#xff1a;The Annotated Diffusion Model一文翻译迁移而来&#xff0c;同时参考了由浅入深了解Diffusion Model一文。 本教程在Jupyter Notebook上成…

昇思MindSpore学习笔记5-02生成式--RNN实现情感分类

摘要&#xff1a; 记录MindSpore AI框架使用RNN网络对自然语言进行情感分类的过程、步骤和方法。 包括环境准备、下载数据集、数据集加载和预处理、构建模型、模型训练、模型测试等。 一、概念 情感分类。 RNN网络模型 实现效果&#xff1a; 输入: This film is terrible 正…

放大镜案例

放大镜 <!DOCTYPE html> <html lang"zh-cn"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>商品放大镜</title><link rel&qu…

如何使用allure生成测试报告

第一步下载安装JDK1.8&#xff0c;参考链接JDK1.8下载、安装和环境配置教程-CSDN博客 第二步配置allure环境&#xff0c;参考链接allure的安装和使用(windows环境)_allure windows-CSDN博客 第三步&#xff1a; 第四步&#xff1a; pytest 查看目前运行的测试用例有无错误 …

如何使用 pytorch 创建一个神经网络

我已发布在&#xff1a;如何使用 pytorch 创建一个神经网络 SapientialM.Github.io 构建神经网络 1 导入所需包 import os import torch from torch import nn from torch.utils.data import DataLoader from torchvision import datasets, transforms2 检查GPU是否可用 dev…

Yolov10训练,转化onnx,推理

yolov10对于大目标的效果好&#xff0c;小目标不好 一、如果你训练过yolov5&#xff0c;yolov8&#xff0c;的话那么你可以直接用之前的环境就行 目录 一、如果你训练过yolov5&#xff0c;yolov8&#xff0c;的话那么你可以直接用之前的环境就行 二、配置好后就可以配置文件…

前端JS特效第24集:jquery css3实现瀑布流照片墙特效

jquery css3实现瀑布流照片墙特效&#xff0c;先来看看效果&#xff1a; 部分核心的代码如下(全部代码在文章末尾)&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8" /> <title>jquerycss3实现瀑…

Nginx:负载均衡小专题

运维专题 Nginx&#xff1a;负载均衡小专题 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/…

【专项刷题】— 位运算

常见类型介绍&#xff1a; & &#xff1a;有 0 就是 0 | &#xff1a;有 1 就是 1 ^ &#xff1a;相同为 0 &#xff0c;相异为 1 或者 无进位相加给定一个数确定它的二进制位的第x个数是0还是1&#xff1a;将一个数的二进制的第x位改成1&#xff1a;将一个数的二进制的第x…

Windows10/11家庭版开启Hyper-V虚拟机功能详解

Hyper-V是微软的一款虚拟机软件&#xff0c;可以使我们在一台Windows PC上&#xff0c;在虚拟环境下同时运行多个互相之间完全隔离的操作系统&#xff0c;这就实现了在Windows环境下运行Linux以及其他OS的可能性。和第三方虚拟机软件&#xff0c;如VMware等相比&#xff0c;Hyp…

大模型知识问答: 文本分块要点总结

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型技术趋势、算法项目落地经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。 总结链接如…

C++ 信号量和锁的区别

网上关于信号量和锁的区别&#xff0c;写的比较官方晦涩难懂&#xff0c;对于这个知识点吸收难&#xff0c;通过示例&#xff0c;我们看到信号量&#xff0c;可以控制同一时刻的线程数量&#xff0c;就算同时开启很多线程&#xff0c;依然可以的达到线程数可控 #include <i…

初识c++(命名空间,缺省参数,函数重载)

一、命名空间 1、namespace的意义 在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将都存在于全 局作用域中&#xff0c;可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化&#xff0c;以避免命名 冲突…

世界商用飞机机型大全-使用Java抓取FlightAware后的答案

目录 前言 一、数据说明 1、实时航班飞机机型数据 2、网页结构分析 二、使用Java进行信息抓取 1、定义页面PageVO对象 2、爬取属性定义 3、启动信息抓取组件 三、成果分析 1、商业飞行的飞机机型的种类 2、飞机种类排名前十名 3、航班数排名后十名 4、看中国国产大飞…

你真的会信息收集嘛,4k字渗透测试信息收集10大技巧

前言 在渗透测试中&#xff0c;信息收集是非常关键的一步&#xff0c;它为后续的漏洞发现和利用提供了重要的基础。以下是非常详细的信息收集方式&#xff1a; 一、被动信息收集 被动信息收集是指在不与目标系统直接交互的情况下&#xff0c;通过公开渠道获取目标系统的相关…

基于51单片机的四路抢答器Protues仿真设计

一、设计背景 近年来随着科技的飞速发展&#xff0c;单片机的应用正在不断的走向深入。本文阐述了基于51单片机的八路抢答器设计。本设计中&#xff0c;51单片机充当了核心控制器的角色&#xff0c;通过IO口与各个功能模块相连接。按键模块负责检测参与者的抢答动作&#xff0c…