写得了代码,焊得了板!嵌入式开发工程师必修之代码管理方案(中)

news2024/12/24 21:14:42

目录

2.2 分仓、权限与依赖问题

2.3 基于 Git 进行多仓管理

Git submodule

Git subtree

Script/CMake

Git-Repo

Conan


本文来自

武让 极狐GitLab 高级解决方案架构师

🌟 前一篇文章,作者介绍了嵌入式开发场景的代码管理特点与诉求,以及该场景下的代码管理工具与方式之 SVN 与 Git。前情回顾 👉 嵌入式开发场景下的代码管理方案(上)

本文承上启下,将介绍嵌入式开发场景的代码管理分仓、权限与依赖问题,以及基于 Git 的多仓管理。

2.2 分仓、权限与依赖问题

在上文中我已经简单介绍了 SVN 和 Git 在分仓模式和权限管理上的一些区别,简单来说:

  • SVN 支持目录授权,常用于大仓模式;

  • Git 仅支持仓库授权,常用于分仓模式,也支持大仓模式。

这本是一个简单的选择题:要么留在 SVN,保留现有的目录授权模式;要么使用 Git 分仓,权限也落在不同的仓库上;要么使用 Git 大仓,权限就控制在整个仓库上;直到另一个需求打破了这个平衡:代码复用。

做过 C/C++ 且又做过 Java、Python、Node 等语言开发的技术人员常常会感叹 C/C++ 缺少好的包管理工具,羡慕 Java 有 Maven、Python 有 Pip、Node 有 Npm,而 C/C++ 一直深陷 DLL 地狱、代码版本与交付版本不一致、重复造轮子等各式各样依赖问题的泥潭,不可自拔。

首先 C/C++ 由于其语言特性,构建时常常是系统底层相关,所以打包时需要考虑操作系统、体系架构、编译器版本、构建类型等一系列因素。

且需要关注一些包自身的属性:纯头文件库、静态库还是动态库,以及包的构建参数(比如优化级别、是否开启 exception 和 rtti 的编译选项等),还有指定裁剪性(特性宏)等配置。

另外,由于 C/C++ 的标准库(glibc 和 libstdc++)存在版本兼容性问题,以及 C++ 存在 ABI 兼容性问题,这会让包的版本管理超越语义化版本(SemVer)所能解决的问题范围,导致包的创建者需要在发包的时候为包的兼容性做更多的考虑。

最后,由于 C/C++ 语言在语法上缺乏包级别的模块化机制,会让包的符号冲突以及依赖解决变得困难。如果还不够,再加上交叉编译的场景,绝对会让一个通用 C/C++ 包管理器的复杂度超过其它任何语言。

包管理器的目的就是对包进行版本控制,解决项目间的依赖问题,也能从根本上解决代码复用的问题。但由于 C/C++ 包管理器的复杂度,导致 C/C++ 开发人员在过去很长一段时间内没有包管理工具可用。在这样的背景下,大家对于 C/C++ 的代码复用发展出了很多模式:

  • 基于代码文件目录划分

项目划分好模块,定义好自己的目录,协商出一个 Common 目录作为公共的头文件目录,然后对不同的开发人员分配不同的目录权限就可以分工协作了。

看起来是不是非常眼熟,这就是 SVN 的使用方式。然而这个 Common 目录可能又需要被另一个项目 B 所引用,那么项目 B 整个代码也加进这个代码库。最后所有的模块都耦合到一起,代码库膨胀的很快,导致严重的性能问题。

  • 基于代码复用工具划分

由于 SVN 性能问题,很多开发者开始尝试迁移到 Git,把代码分不到不同的代码库中。但由于没有包管理工具,嵌入式的项目需要先将项目关联的代码库拉到一起,然后再构建。而 Git 本身不提供仓库之间的关联关系,这就对分库之后的聚合管理提出了需求。后来出现了 git submodulegit subtreegit repo 等工具,就是为了解决分仓后,如何把项目再聚合出来,从而实现项目管理和代码复用。

但这些都是在代码文件级别的复用,增加了管理的复杂度。

  • 基于 CMake

一些构建工具的发展,为 C/C++ 的代码复用引入了更好的方式。例如 CMake 从 3.0 版本开始被称之为“Modern CMake”,是因为它引入了 target 的概念,以及基于 target 建立起了构建的依赖可见性和传播控制机制。这些都更好的支持了代码在构建上的模块化,借助 CMake 的 ExternalProject 和 find_package 特性,使得我们可以从指定的 http 或者 git 分支下载、构建、安装和引用代码库。

但是这种复用方式,对于间接依赖的管理仍旧是不足的,没有办法做到全链条的依赖解析、依赖追溯、冲突判决,以及基于变更进行最小范围的重构建和发布管理。

  • 基于包管理工具

解铃还须系铃人,问题发展到最后还是回到问题本身,C/C++ 没有好的包管理工具,那就做一个。

Conan 是一款出色的开源 C/C++ 包管理器。它吸收了很多现代化包管理器的设计思想,探索解决通用 C/C++ 包管理器的各种挑战。它需要使用 Python 进行配置,目前在国内的普及度还不算高,相关的文档教程也不是很齐全,相对来说有一定的门槛,但 Conan 可以说是目前 C/C++ 唯一可用的包管理工具,也可能是真正的破局者。

这就是为什么一些软件企业从 SVN 迁移到 Git 没有那么大阻力,而从事嵌入式开发的企业则不同的原因。归根到底是 C/C++ 缺乏好的依赖管理手段,而企业、管理者、开发人员一直都面临代码复用这个问题,并希望通过从 SVN 迁移到 Git 来解决这个问题。但显然这个问题仅依靠 SVN 或者 Git 自身是无法解决的。

2.3 基于 Git 进行多仓管理

既然 Git 现在是代码管理的主流方案,并且依靠 Git 自身无法解决分仓后的多仓管理问题和代码复用问题,那就需要借助一些其他的工具和方法,其实都是上文中提到过的。虽然这些工具和方法本身不够完善,但对于处于不同阶段不同场景的企业和用户,可以有个参考,毕竟软件世界没有银弹。

Git submodule

Git submodule 可以让一个 Git 仓库作为另一个仓库的子目录,从而实现在一个代码库中引用其他的代码库进行复用。

Git submodule 的特点如下:

  • 在主库中通过 git submodule add <子库 git 地址> 命令实现引用子库代码;

  • 在主库中通过 .gitmodule 文件来记录主库和子库的引用关系。

图片

  • 主库只是引用了子库的 SHA,并没有直接拷贝子库代码,所以子库的代码变更内容在主库上不可见,只是在本地拉取时将对应的子库拉取到本地。所以在 Git 服务器上看不到完整的项目代码,这也意味着无法实现对于整个项目的代码评审。

图片

图片

  • Clone 主库不会自动 Clone 子库,除非在 Clone 主库时:

    执行 git submodule update ;

    执行 git clone --recursive

    添加 Git 全局配置 git config --global submodule.recurse true

  • 子库 commit/push 也需要在主库 pull/commit/push,容易遗忘导致代码未同步或者主库关联了旧的子库:

    可在主库执行 git submodule foreach 'git pull origin master' 更新所有子库;

    由于该问题导致主库、子库在解决冲突、切换分支时变得非常复杂,且容易出错。

个人不建议在实际项目中使用 Git submodule,除非能有效解决以上问题,或者可在少量的项目中进行试用再进行决策。

Git subtree

Git subtree 与 Git submodule 功能类似,但目前 Git subtree 在开发人员中的呼声高于 Git submodule。

Git subtree 的特点如下:

  • 在主库中通过 git subtree add --prefix=<主库子目录> <子库git地址> <子库分支> 命令实现引用子库代码。

图片

  • 主库拷贝了子库的代码,所以在 Git 服务器上可以看到完整的项目代码,也可以实现整个项目的代码评审。所以 Git submodule is Link, Git subtree is Copy. 也意味着 Git subtree 的性能略差,会增加主库的大小。

图片

  • 无法通过默认的 Git 命令将主库的代码变更同步到子库,需要适应新的工作流。

# 添加子库
git subtree add --prefix=<主库子目录> <子库git地址> <子库分支>
# 从子库中拉取
git subtree pull --prefix=<主库子目录> <子库git地址> <子库分支>
# 推送到子库
git subtree push --prefix=<主库子目录> <子库git地址> <子库分支>
  • 可通过一些工具和方法简化工作流:

  1. 设置 Git别名以简化操作:

# 在 .gitconfig 文件中添加
[alias] gits = subtree
# 然后执行命令
gits add --prefix=<主库子目录> <子库git地址> <子库分支>

    2. 设置子库为主库的远端别名以简化操作:

# 添加子库为主库的remote别名
git remote add -f subA <subA>.git
# 然后执行命令
git subtree add --prefix=A subA master
git subtree pull --prefix=A subA master

   3. 使用第三方工具git-subrepo。

个人建议可以在依赖场景不复杂的中小型项目中使用 Git subtree,以避免性能问题,它的体验和工作方式相对比较友好。

Script/CMake

最简单的办法往往最有效,通过脚本来拉取或者相关子库的代码,将脚本放在主库中,按需执行,比如拉取相关子库代码:

git clone -b <子库A分支> <子库A git地址> <本地目录A>
git clone -b <子库B分支> <子库B git地址> <本地目录B>

或者通过 CMake 的 FetchContent 来实现,可以参考《C++ 工程依赖管理新方向:CMake & Git | KC 的废墟堆》,基于 FetchContent 可以再封装一套脚本。

个人建议也是可以在依赖场景不复杂的中小型项目中使用,比较轻量和灵活,但有一定的技术门槛,甚至需要专人来做,这对做传统嵌入式开发的团队是个挑战。

Git-Repo

Git-Repo 是 Google 开源的一款 Git 客户端工具,是为了搭配 Google 开源的代码管理工具 Gerrit 进行使用,而 Gerrit 是为了管理 Android 的开源项目 AOSP 而设计的。

Google 是为数不多的坚持大仓模式(Monorepo)的巨头公司,AOSP 也是一个大型项目,一个项目包含了数百个代码库,彼此之间存在依赖关系。所以为了更好的管理这些仓库,Google 形成了自己独特的 AOSP 工作流,Gerrit 通过一个项目清单 Manifest.xml 来组织仓库关系,Git-Repo 就可以通过 Manifest.xml 来实现代码的批量拉取和推送。

Git-Repo 和 Gerrit 主要是解决了多仓管理的问题,除了对仓库进行批量操作,Gerrit 还支持跨代码库进行评审,所以 AOSP 从流程到系统到工具都是相辅相成的。Git-Repo 可以在一定程度上解决代码复用问题,不过 Gerrit 本身不是商业化产品,没有厂商技术支持,且 Gerrit 的用户体验较差,复杂度较高,所以当嵌入式项目使用 AOSP 专用的工具,又显得有点水土不服。

借鉴 Git-Repo 和 Manifest.xml 的思想,阿里使用 Golang 重写了一个 Git-Repo-Go 工具,可以在 GitLab/极狐GitLab、GitHub 等以 Git 为底层的代码管理系统上获得 Git-Repo 批量操作代码库的体验。

但是上文中也提到,Git-Repo 和 Gerrit 是相辅相成的,Gerrit 支持跨代码库进行代码评审,支持更丰富的权限管理模式,为多仓下的代码管理和评审提供了基础。而 GitLab/极狐GitLab、GitHub 等代码管理系统目前还是以分仓模式为主,原生不提供这种业务功能,所以导致 Git-Repo-Go 仅仅是实现了 Git-Repo 的部分功能,这时不免怀疑这么折腾为啥不直接用 Gerrit。

Conan

终极方案,如果项目依赖相对复杂,需要在项目级别进行代码评审,且要考虑依赖解析、循环依赖、依赖追踪等问题,那么以上工具方案都不用考虑了,它们都无法从根本上解决问题,所以 Conan 这个工具必须死磕下来,不管是头文件、静态库还是动态库的管理,Conan 都能在一定程度上满足,虽然它本身具备一定的复杂性,但目前没有更好的路可以走了。

当然如果愿意快速解决问题,知名制品库厂商 JFrog 提供了 Conan Center 以及相关解决方案,我就不多打广告了。如果愿意折腾,Conan 本身开源,且可以通过 SonaType 的开源制品库 Nexsus 实现,这也变相提供了另一种“低成本”的方案。

💡 后续推送剧透:围绕企业对于嵌入式开发场景的诉求,极狐GitLab 提供了一整套解决方案,可以较好的解决嵌入式开发场景下的种种问题,下期将为你介绍。

欢迎订阅关注~

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

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

相关文章

文件加密最简单的方法有哪些?

文件加密是保护敏感信息和数据安全的重要方法之一。以下是文件加密的几种简单方法&#xff1a; 密码保护&#xff1a; 最简单的方式是使用密码来保护文件。许多应用程序(如Microsoft Office)允许你为文件设置密码&#xff0c;确保只有知道密码的人可以访问文件内容。 压缩工具加…

上海交大ACM班总教头团队重磅新作,带你动手学机器学习(文末赠书4本)

目录 0 写在前面1 什么是机器学习&#xff1f;2 ACM 班总教头&#xff1a;俞勇3 动手学习机器学习赠书活动 0 写在前面 机器学习强基计划聚焦深度和广度&#xff0c;加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理&#xff1b;“广”在分析多个机器…

【全站最全】被苹果、谷歌和Microsoft停产的产品(二)

2019 Field Trip 2012 – 2019 APP Field Trip was a mobile app that acted as a virtual tour guide by cross referencing multiple sources of information to provide users information about points of interest near them. AdSense (mobile app) 2013 – 2019 A…

怎么制作sip网络寻呼话筒,sip任意呼叫主机,

怎么制作sip网络寻呼话筒&#xff0c;sip任意呼叫主机&#xff0c; 所需材料一&#xff1a;SV-2103VP sip网络音频模块 功能如下&#xff1a; SV-2101VP/ SV-2103VP使用了AT32F437VGT7处理器构架加专业的双向音频Codec编解码器&#xff0c; 处理器负责数据的传输&#xff0c…

里式替换原则(LSP)

目录 简介: 作用: 过程: 总结: 简介: 里式替换原则&#xff08;Liskov Substitution Principle&#xff0c;简称LSP&#xff09;的提出者是美国计算机科学家Barbara Liskov。Barbara Liskov是一位计算机科学家&#xff0c;麻省理工学院教授&#xff0c;也是美国第一个计算机…

成功解决修改已经push到远程git仓库的commit message

1.使用 Git 命令行进入要修改的项目目录。 2.运行 git log 命令查看提交历史&#xff0c;找到要修改的提交的哈希值&#xff08;commit hash&#xff09;。 3.运行 git rebase -i <commit hash> 命令&#xff0c;将 <commit hash> 替换为要修改的提交的哈希值。这将…

用户、权限和Vim编辑器

用户 用户分类 超级管理员&#xff1a;可以登录&#xff0c;拥有所有权限&#xff0c;用户Id为0 普通用户&#xff1a;可以登录&#xff0c;但只能操作家目录&#xff0c;用户Id为1000 程序用户&#xff1a;不能登录&#xff0c;用于管理程序&#xff0c;用户Id为1~999 添…

春秋云镜:CVE-2019-9042(Sitemagic CMS v4.4 任意文件上传漏洞)

一、题目 靶标介绍&#xff1a; Sitemagic CMS v4.4 index.php?SMExtSMFiles 存在任意文件上传漏洞&#xff0c;攻击者可上传恶意代码执行系统命令。 进入题目&#xff1a; admin/admin /index.php?SMExtSMFiles&SMTemplateTypeBasic&SMExecModeDedicated&SMFil…

龙芯2K1000LA移植交叉编译环境以及QT

嵌入式大赛结束了&#xff0c;根据这次比赛中记的凌乱的笔记&#xff0c;整理了一份龙芯2K1000LA的环境搭建过程&#xff0c;可能笔记缺少了一部分步骤或者错误&#xff0c;但是大致步骤可以当作参考。 一、交叉编译工具链 下载连接&#xff1a;龙芯 GNU 编译工具链 | 龙芯开…

第七章:借阅管理【基于Servlet+JSP的图书管理系统】

借阅管理 1. 借书卡 1.1 查询借书卡 借书卡在正常的CRUD操作的基础上&#xff0c;我们还需要注意一些特殊的情况。查询信息的时候。如果是管理员则可以查询所有的信息&#xff0c;如果是普通用户则只能查看自己的信息。这块的控制在登录的用户信息 然后就是在Dao中处理的时候需…

博客系统前端页面(项目实战系列1)

目录 前言&#xff1a; 1.前端 1.1博客列表页 1.1.1博客列表页效果预览图 1.1.2实现导航栏 1.1.3实现版心个人信息博客列表 1.2博客详情页 1.2.1博客详情页效果预览图 1.2.2实现导航栏 版心个人信息 1.2.3实现博客正文 1.3登录页 1.3.1登录页效果预览图 1.3.2导航…

❤echarts柱状图的使用及详细配置

❤ echarts柱状图的使用及详细配置 一、Echarts 柱形图详细配置 1、 简单引入 import * as echarts from echarts;// 5.4区别4引入方式 // 结构 <div id"echartzhu" style"width: 100%;height: 200px;"></div>// 渲染 this.echartzhu(echa…

使用ffmpeg将WebM文件转换为MP4文件的简单应用程序

tiktok网上下载的short视频是webm格式的&#xff0c;有些程序无法处理该程序&#xff0c;比如roop程序&#xff0c;本文介绍了如何使用wxPython库创建一个简单的GUI应用程序&#xff0c;用于将WebM文件转换为MP4文件。这个应用程序使用Python编写&#xff0c;通过调用FFmpeg命令…

【校招VIP】TCP/IP模型之常用协议和端口

考点介绍&#xff1a; 大厂测试校招面试里经常会出现TCP/IP模型的考察&#xff0c;TCP/IP协议是网络基础知识&#xff0c;是互联网的基石&#xff0c;不管你是做开发、运维还是信息安全的&#xff0c;TCP/IP 协议都是你绕不过去的一环&#xff0c;程序员需要像学会看书写字一样…

数据库——Redis 没有使用多线程?为什么不使用多线程?

文章目录 Redis6.0 之后为何引入了多线程&#xff1f; 虽然说 Redis 是单线程模型&#xff0c;但是&#xff0c; 实际上&#xff0c;Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。 不过&#xff0c;Redis 4.0 增加的多线程主要是针对一些大键值对的删除操作的命令&a…

Vue 中hash 模式与 history 模式的区别

hash 模式&#xff1a; - 地址中永远带着 # 号&#xff0c;不美观。 - 兼容性比较好。 - 通过手机 app 分享地址时&#xff0c;如果 app 效验严格&#xff0c;该地址会被标记为不合法。 history 模式&#xff1a; - 地址干净&#xff0c;美观。 - 兼容性和 hash 模式相比…

媒体梦工厂软件教程:轻松合并视频,打造完美影片

在如今数字化时代&#xff0c;视频编辑已经成为人们日常生活和工作中不可或缺的部分。无论是在社交媒体上与朋友分享精彩时刻&#xff0c;还是在业务领域展示专业技能&#xff0c;人们对于视频的需求愈发增长。而对于那些想要将多个视频片段合并成一个完美影片的人来说&#xf…

常见前端面试之VUE面试题汇总五

13. assets 和 static 的区别 相同点&#xff1a; assets 和 static 两个都是存放静态资源文件。项目中所 需要的资源文件图片&#xff0c;字体图标&#xff0c;样式文件等都可以放在这两个文件 下&#xff0c;这是相同点 不相同点&#xff1a;assets 中存放的静态资源文件在…

【3维视觉】网格的谱分解和应用(GFT图傅里叶变换)

网格的谱分解即网格的频率分解&#xff0c;我们学过信号的傅里叶变换&#xff0c;将信号从空域变换到频域。二维图像由离散傅里叶变换DFT(Discrete Fourier Transform)。在图信号领域&#xff0c;也有图的傅里叶变换GFT(Graph Fourier Transform)&#xff0c;网格可以看作是图&…

数据生成 | MATLAB实现MCMC马尔科夫蒙特卡洛模拟的数据生成

数据生成 | MATLAB实现MCMC马尔科夫蒙特卡洛模拟的数据生成 目录 数据生成 | MATLAB实现MCMC马尔科夫蒙特卡洛模拟的数据生成生成效果基本描述模型描述程序设计参考资料 生成效果 基本描述 1.MATLAB实现MCMC马尔科夫蒙特卡洛模拟的数据生成&#xff1b; 2.马尔科夫链蒙特卡洛方…