【CMake 入门与进阶(2)】CMake编译设置——多个源文件编译及生成库文件(附代码)

news2024/11/25 1:07:48

多个源文件       

        上篇我们学习了单个源文件的cmake 的编译,不过一个源文件的例子似乎没什么意思,我们再加入一个hello.h 头文件和 hello.c 源文件。在 hello.c 文件中 定义了一个函数 hello,然后在 main.c 源文件中将会调用该函数:

        ⚫ hello.h 文件内容

#ifndef __TEST_HELLO_
#define __TEST_HELLO_

void hello(const char *name);

#endif //__TEST_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(void){
 hello("World");
 return 0;
}

        ⚫ 然后准备好 CMakeLists.txt 文件

project(HELLO)
set(SRC_LIST main.c hello.c)
add_executable(hello ${SRC_LIST})

        工程目录结构如下所示:

├── build //文件夹
├── CMakeLists.txt
├── hello.c
├── hello.h
└── main.c

        同样,进入到build目录下,执行cmake、再执行make编译工程,最终就会得到可执行文件 hello。

        在本例子中,CMakeLists.txt 文件中使用到了 set 命令,set 命令用于设置变量,如果变量不存在则创建该变量并设置它;在本例中,我们定义了一个 SRC_LIST 变量,SRC_LIST 变量是一个源文件列表,记录生成可执行文件 hello 所需的源文件 main.c 和 hello.c,而在 add_executable 命令引用了该变量;当然我们也可以不去定义 SRC_LIST 变量,直接将源文件列表写在 add_executable 命令中,如下:

add_executable(hello main.c hello.c)

生成库文件

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

project(HELLO)
add_library(libhello hello.c)
add_executable(hello main.c)
target_link_libraries(hello libhello)

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

         目录结构如下所示:

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

CMakeLists.txt 文件解释

        本例中我们使用到了 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 程序。

修改生成的库文件名字

         本例中有一点非常不爽,生成的库为 liblibhello.a,名字非常不好看;如果想生成 libhello.a 该怎么办? 直接修改 add_library 命令的参数,像下面这样可以吗?

add_library(hello hello.c)

        答案是不行的,因为 hello 这个目标已经存在了(add_executable(hello main.c)),目标名对于整个工程来说是唯一的,不可出现相同名字的目标,所以这种方法肯定是不行的,实际上我们只需要在 CMakeLists.txt 文件中添加下面这条命令即可:

set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

        set_target_properties 用于设置目标的属性,这里通过 set_target_properties 命令对 libhello 目标的 OUTPUT_NAME 属性进行了设置,将其设置为 hello。

        我们进行实验,此时 CMakeLists.txt 文件中的内容如下所示:

cmake_minimum_required(VERSION 3.5)
project(HELLO)
add_library(libhello SHARED hello.c)
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")
add_executable(hello main.c)
target_link_libraries(hello libhello)

        除了添加 set_target_properties 命令之外,我们还加入了 cmake_minimum_required 命令,该命令用于设置当前工程的 cmake 最低版本号要求,当然这个并不是强制性的,但是最好还是加上。进入到 build 目录下, 使用 cmake+make 编译整个工程,编译完成之后会发现,生成的库文件为 libhello.a,而不是 liblibhello.a。

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

将源文件组织到不同的目录

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

├── 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

将生成的可执行文件和库文件放置到单独的目录下

        前面还有一点不爽,在默认情况下,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 变量控制库文件的输出路径。

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

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

相关文章

客服都要下岗了? 当ChatGPT遇见私有数据,秒变AI智能客服!

用ChatGPT搭建基于私有数据的WorkPlus AI客服机器人这个想法&#xff0c;源于WorkPlus售前工作需求。在ChatGPT之前&#xff0c;其实对话式AI一直在被广泛使用在客服场景&#xff0c;只不过不大智能而已。比如你应该看到不少电商客服产品&#xff0c;就有类似的功能&#xff0c…

车站信息管理系统(面向对象程序设计python版)

一、基本概述 1.项目背景 随着大数据时代的发展,大数据抓取了人们最想要的信息,数据查询能帮助用户获取更有用的信息,让每个人都能享受到大数据带给生活的高效和便捷。 2.设计目的 为了大大缩减人们出行选择站点所需时间,为了让人们在陌生地区,在对当地交通不熟的情况…

Redis数据类型之(哈希Hash和集合Set)

Redis数据类型之&#xff08;哈希Hash和集合Set&#xff09; 一定注意看红色注意项。 哈希&#xff08;Hash&#xff09;: Redis hash 是一个 string 类型的 field&#xff08;字段&#xff09; 和 value&#xff08;值&#xff09; 的映射表&#xff0c;hash 特别适合用于存…

promethues 之PromQL数据类型介绍(二)

promethues 之PromQL数据类型介绍(二) 1、PromQL 介绍 PromQL是promethues 监控系统内置的一种查询语言&#xff0c;类似于MySQL的SQL语句&#xff0c;该语言仅用于读取数据。PromQL是我们学习Promethues最困难也是最重要的部分。当Promethues从系统和服务收集到指标数据时&…

PIP-Net:用于可解释图像分类的基于patch的直观原型

文章目录 PIP-Net: Patch-Based Intuitive Prototypes for Interpretable Image Classification摘要本文方法模型结构Self-Supervised Pre-Training of PrototypesTraining PIP-NetScoring Sheet ReasoningCompact Explanations 实验结果 PIP-Net: Patch-Based Intuitive Proto…

bug 记录 - 接口被重复调用,响应时长不同,结果被覆盖的问题

发现问题与调试过程 需求&#xff1a;输入框中输入关键字&#xff0c;根据关键字去调用接口&#xff0c;返回模糊查询的结果集合。问题&#xff1a;输入的关键字越少&#xff0c;接口响应时间越长。例如&#xff1a;输入“阿”&#xff0c;接口响应时间大概是 5 秒&#xff0c…

【计算机网络中ip概念总结】【平时我们说的ip 到底是什么】【计算机网络中 ip地址是什么】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

【Linux】重定向dup

文章目录 前言重定向的原理dup函数添加重定向功能到myshell 前言 了解重定向之前需要明白文件描述符的工作规则&#xff0c;可以看这篇文章&#xff1a;文件系统 最关键的一点是&#xff1a;在进程中&#xff0c;在文件描述符表中&#xff0c;会将最小的、没有被使用的数组元…

vscode整合gitee

vscode需要下载的插件 第一个可以多仓库进行操作 第二个主要是用于仓库的管理和展示 vscode的gitee操作 1、按F1&#xff0c;搜索gitee 2、根据提示进行操作 标1的是第一个插件的操作 标2的是第二个插件的操作 绑定用户私钥 两个插件绑定私钥的方式不同&#xff0c; gitee的私…

这本数智平台白皮书讲透了大型企业数智化升级业务痛点

在以“升级企业数智化底座”为主题的2023用友BIP技术大会上&#xff0c;用友联合全球权威咨询机构IDC共同发布《建设数字中国 升级数智底座——企业数智化底座白皮书》&#xff0c;在这本数智平台白皮书里深入剖析了大型企业的数智化升级痛点。 大型企业普遍具有广域的业务覆盖…

六级备考15天|CET-6|翻译真题练习|北京大兴国际机场|9:15~10:20

目录 中文 英文 词汇 订正 解析 练习 中文 英文 词汇 put sth. into use 投入使用 距离south of地点 “...以南....公里处” construction 开工建设 the giant project 巨型工程 on the site …

LED显示屏驱动IC基本原理

LED显示屏驱动IC&#xff08;Integrated Circuit&#xff0c;集成电路&#xff09;是一种专门设计用于控制和驱动LED显示屏的电子元件。LED显示屏驱动IC的基本原理涉及到LED的电流控制、亮度调节、扫描控制和图像数据处理等方面。 以下是LED显示屏驱动IC的基本原理的详细说明&a…

只需简单几步,就能在报表工具FastReport .NET 中使用 RFID 标签

FastReport 是功能齐全的报表控件&#xff0c;可以帮助开发者可以快速并高效地为.NET&#xff0c;VCL&#xff0c;COM&#xff0c;ActiveX应用程序添加报表支持&#xff0c;由于其独特的编程原则&#xff0c;现在已经成为了Delphi平台最优秀的报表控件&#xff0c;支持将编程开…

喜讯丨计讯物联5G物联网数据网关TG463荣登2022年度中国物联网行业创新产品榜

近日&#xff0c;备受瞩目的2022‘物联之星’中国物联网产业年度榜单颁奖典礼在上海世博展览馆会场隆重举行。经由申报筛选、网络人气投票、专家评委投票等多重环节&#xff0c;计讯物联旗下5G物联网数据网关TG463荣登2022年度中国物联网行业创新产品榜。 作为中国物联网行业…

chatgpt赋能python:Python编写抽奖程序——让你的活动更加有趣

Python编写抽奖程序——让你的活动更加有趣 在现代社会中&#xff0c;抽奖活动已经成为了许多商家和组织吸引关注、增强互动的重要手段。而使用Python编写抽奖程序可以帮助我们更加方便地进行这一活动。本文将介绍Python编写抽奖程序的方法&#xff0c;以及如何在实际应用中优…

【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等)、趣味项目实现、学术应用项目实现

【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧&#xff08;调参、画图等、趣味项目实现、学术应用项目实现 对于深度强化学习这块规划为&#xff1a; 基础单智能算法教学&#xff08;gym环境为主&#xff09;主流多智能算法教学…

Hive和Hadoop关系

Hive是基于Hadoop的一个数据仓库工具&#xff0c;用来进行数据提取、转化、加载&#xff0c;这是一种可以查询和分析存储在Hadoop中的大规模数据的机制。Hive数据仓库工具能将结构化的数据文件映射为一张数据库表&#xff0c;并提供SQL查询功能&#xff0c;能将SQL语句转变成Ma…

vue组件库 vue marquee vue跑马灯 vue走马灯 ​vue-marquee-text-component​中文

vue2运行安装 npm install vue-marquee-text-component1.2.0 Name说明DescriptionConfigduration持续时间动画持续时间&#xff5b;类型&#xff1a;数字&#xff0c;默认值&#xff1a;15&#xff5d;Animation Duration{ type: Number, default: 15 }repeatrepeat重复插槽的…

chatgpt赋能python:Python内置函数:如何查找和使用?

Python内置函数&#xff1a;如何查找和使用&#xff1f; 作为一名有10年Python编程经验的工程师&#xff0c;我想与大家分享一下Python内置函数的使用技巧。Python内置函数是指已经定义好的函数&#xff0c;无需另外安装也无需导入就可以直接在Python中使用的函数。这篇文章将…

chatgpt赋能python:Python编程教程:如何用Python写抢购程序

Python编程教程&#xff1a;如何用Python写抢购程序 随着网购的流行和限量商品的推出&#xff0c;抢购已经成为了一个非常热门的话题。有些人甚至会通过软件或程序来提高他们成功抢到商品的机会。在本篇文章中&#xff0c;我们将介绍如何用Python编写一个简单易用的抢购程序&a…