首页 > 其他分享 >CMake构建学习笔记14-依赖库管理工具

CMake构建学习笔记14-依赖库管理工具

时间:2024-09-03 21:03:36浏览次数:8  
标签:MSBuild CMake 14 LibrarySet 管理工具 Add 构建 null string

如果说做C/C++开发最大的痛点是什么,那么一定是缺少一个官方的统一的包管理器。认真的说,如果你要用C/C++干点什么,至少需要(Windows系统下):

  1. C/C++语言本身、标准库、以及操作系统API几乎干不了什么,除非你真的想从零开始造轮子。
  2. 开始找一些现成的实现组成依赖库。最好看能不能找到预编译包或者安装包,即使找到了,由于二进制兼容的问题,你也不一定能够使用。
  3. 如果没找到预编译包或者安装包,那么就需要自己从源代码进行构建了。如果提供了CMake的构建方式就挺好,万一没有提供,就得自己想办法组织工程进行构建。
  4. 注意,依赖库本身是需要依赖库的!比如,你构建GDAL的时候会发现PROJ是GDAL的必须依赖,等你开始构建PROJ的时候又发现sqlite3是PROJ必须依赖,而你准备构建sqlite3的时候发现sqlite3是不提供CMake方式的。
  5. 不谈构建过程中处理的一系列问题。等你把依赖库构建完成了,你就得考虑如何引入了。如果你使用动态链接库,你需要进行头文件、动态库导入库以及动态库相关的配置。如果头文件错了,你会发现无法编译;如果动态库导入库错误,你会发现无法链接;如果动态库不正确,你会发现无法运行。
  6. 最后开始在源代码中include头文件,调用依赖库相关的功能进行操作。

嗯,说实话笔者光是写这些步骤都觉得有点汗流浃背了。这要是换成使用Python或者JavaScript进行开发,一个install指令,一个import语句就全部搞定,也难怪C/C++开发的效率一直被程序员诟病呢。不过,C/C++领域也不是一直在固步自封,Windows系统下也可以使用一些包管理器,例如vcpkg、Conan、Chocolatey等。个人认为,这些包管理器正在逐渐成熟过程中,不过尚需要一些时间完善,有兴趣的同学可以进行试用。

另外一种方式就是像笔者一样,尝试组织一个属于自己或者自己团队的依赖库管理工具。这样做的原因有三:

  1. 不同环境下的C/C++包存在二进制兼容的问题。
  2. 构建Release带调试信息的构建成果,以及符号库文件。
  3. 有些库包很少见,通用的包管理器不一定收纳。

那么具体如何实现呢?其实不用想的太复杂,我们将所有需要的构建成果都构建到同一个目录,并且将这个目录设置成环境变量。这样,在我们日常的程序中就可以依托这个环境变量配置依赖库。这意味着所有团队成员的代码工程的配置都可以是一样的,我们就可以忽略掉不同软硬件环境的不同,实现了代码项目的一致性。

所以,关键就是将这些常见的组件构建组织起来。不能使用CMake的GUI工具,因为不同的库各自有自己独特的构建选项,最好将其通过脚本记录。不妨将构建的脚本写的完善一点,自动化一点,代码文件从哪里来,最后的构建成果输出到哪里。例如,前面博文中构建libzip库的完整Powershell脚本libzip.ps1为:

param(   
    [string]$SourceAddress = "https://github.com/nih-at/libzip/archive/refs/tags/v1.10.1.zip",
    [string]$SourceZipPath = "../Source/libzip-1.10.1.zip",
    [string]$SourceLocalPath = "./libzip-1.10.1",
    [string]$Generator,
    [string]$MSBuild,
    [string]$InstallDir,
    [string]$SymbolDir 
)

# 检查目标文件是否存在,以判断是否安装
$DstFilePath = "$InstallDir/bin/zip.dll"
if (Test-Path $DstFilePath) {
    Write-Output "The current library has been installed."
    exit 1
} 

# 创建所有依赖库的容器
. "./BuildRequired.ps1"
$Librarys = @("zlib") 
BuildRequired -Librarys $Librarys

. "./DownloadAndUnzip.ps1"
DownloadAndUnzip -SourceLocalPath $SourceLocalPath -SourceZipPath $SourceZipPath -SourceAddress $SourceAddress

# 清除旧的构建目录
$BuildDir = $SourceLocalPath + "/build"  
if (Test-Path $BuildDir) {
    Remove-Item -Path $BuildDir -Recurse -Force
}
New-Item -ItemType Directory -Path $BuildDir

# 转到构建目录
Push-Location $BuildDir

try {
    # 配置CMake  
    cmake .. -G "$Generator" -A x64 `
        -DCMAKE_BUILD_TYPE=RelWithDebInfo `
        -DCMAKE_PREFIX_PATH="$InstallDir" `
        -DCMAKE_INSTALL_PREFIX="$InstallDir" `
        -DBUILD_DOC=OFF `
        -DBUILD_EXAMPLES=OFF `
        -DBUILD_REGRESS=OFF `
        -DENABLE_OPENSSL=OFF

    # 构建阶段,指定构建类型
    cmake --build . --config RelWithDebInfo

    # 安装阶段,指定构建类型和安装目标
    cmake --build . --config RelWithDebInfo --target install

    # 复制符号库
    $PdbFiles = @(
        "./lib/RelWithDebInfo/zip.pdb"        
    ) 
    foreach ($file in $PdbFiles) {  
        Write-Output $file
        Copy-Item -Path $file -Destination $SymbolDir
    }    
}
finally {
    # 返回原始工作目录
    Pop-Location
}

不得不说Powershell脚本真的很强大,你甚至可以引入第三方脚本中的函数,例如这里的BuildRequired表示预先安装当前库的依赖库(其实就是调用其他库的构建脚本),而DownloadAndUnzip表示从远端把源代码下载下来并且解压到指定文件夹。接下来创建构建文件夹、配置CMake项目、构建项目以及安装项目。最后,我们把这个库符号库给移动到安装目录中去。

就像这样一个一个把需要的依赖库构建脚本写好。有时候需要修改一下构建选项也没什么关系,修改下对应的内容重新构建就好了,这就是写脚本的好处。不过,这样一个一个调用脚本也说不上对库包进行管理了,对比一下一些比较完善的包管理器例如npm,可以再写一个总的用于管理的脚本,将以上构建脚本管理起来,如下Powershell脚本BuildCppDependency.ps1所示:

param(   
    [string]$Generator,
    [string]$MSBuild,
    [string]$InstallDir,
    [string]$SymbolDir,   
    [string]$Install,   
    [string]$List
)

# 创建所有的库的容器
$LibrarySet = [System.Collections.Generic.SortedSet[string]]::new()
$LibrarySet.Add("zlib") > $null
$LibrarySet.Add("libpng") > $null
$LibrarySet.Add("libjpeg") > $null
$LibrarySet.Add("libtiff") > $null
$LibrarySet.Add("giflib") > $null
$LibrarySet.Add("freetype") > $null
$LibrarySet.Add("OpenSceneGraph") > $null
$LibrarySet.Add("eigen") > $null
$LibrarySet.Add("osgQt5") > $null
$LibrarySet.Add("osgQt") > $null
$LibrarySet.Add("minizip") > $null
$LibrarySet.Add("libzip") > $null
$LibrarySet.Add("opencv") > $null
#$LibrarySet.Add("protobuf") > $null
#$LibrarySet.Add("abseil-cpp") > $null

# 检查是否传递了$Install参数
if ($PSBoundParameters.ContainsKey('Install')) {   
    # 比较时忽略大小写
    if ($Install.ToLower() -eq "-all".ToLower()) {
        Write-Output "All libraries will be installed soon..."
        foreach ($item in $LibrarySet) {
            Write-Output "Find the library named $item and start installing..."        
            # 动态构建脚本文件名并执行
            $BuildScript = "./$item.ps1";           
            & $BuildScript -Generator $Generator -MSBuild $MSBuild -InstallDir $InstallDir -SymbolDir $SymbolDir
        }
    }
    else {
        # 查找某个字符串
        if ($LibrarySet.Contains("$Install")) {
            Write-Output "Find the library named $Install and start installing..."        
            # 动态构建脚本文件名并执行
            $BuildScript = "./$Install.ps1";           
            & $BuildScript -Generator $Generator -MSBuild $MSBuild -InstallDir $InstallDir -SymbolDir $SymbolDir
        }
        else {
            Write-Output "Cannot find library named $Install !"
        }
    }
}
elseif ($PSBoundParameters.ContainsKey('List')) {       
    if ($List.ToLower() -eq "-all".ToLower()) {
        Write-Output "The list of all libraries that can currently be installed in the repository is as follows:"
        foreach ($item in $LibrarySet) {
            Write-Output $item
        }
    }
}
else {
    Write-Host "Please enter the parameters!"
}

再次感叹下Powershell脚本的强大,你甚至可以看到System.Collections.Generic.SortedSet,没错,这就是.Net提供的容器,通过Powershell可以也调用它。上述脚本提供了基本的查看和安装功能,例如查看能安装的库,可使用如下指令:

./BuildCppDependency.ps1 -List -all

安装特定的库:

./BuildCppDependency.ps1 -Generator "Visual Studio 16 2019" `
-MSBuild "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" `
-InstallDir "$env:GISBasic" `
-SymbolDir "$env:GISBasic/symbols" `
-Install libzip

安装所有的库:

./BuildCppDependency.ps1 -Generator "Visual Studio 16 2019" `
-MSBuild "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" `
-InstallDir "$env:GISBasic" `
-SymbolDir "$env:GISBasic/symbols" `
-Install -all

其实一个完善的包管理工具需要的功能还是很多的,也非常复杂。例如包的安装是很容易,如何进行卸载呢?如何升级如何降级呢?是不是可以与IDE进行结合,自动导入依赖库并且进行配置呢?这些问题,就留待以后考虑吧。

最后,就将写的依赖库管理工具构建脚本贡献给大家参考吧:地址

标签:MSBuild,CMake,14,LibrarySet,管理工具,Add,构建,null,string
From: https://www.cnblogs.com/charlee44/p/18395448

相关文章

  • 【读书笔记-《30天自制操作系统》-14】Day15
    本篇内容开始讲解多任务。本篇内容结构很简单,先讲解任务切换的原理,再讲解任务切换的代码实践。但是涉及到的知识不少,理解上也有些难度。1.任务切换与多任务原理1.1多任务与任务切换所谓多任务,指的是操作系统同时运行多个任务。但是这种说法实际上是不准确的。如果只有......
  • 20240903_190143 从清华到MIT知识点
    分词库的安装下载只需要一次即可pipinstalljieba分词的使用精准模式默认二级使用精准模式importjiebali=jieba.lcut(句子)全模式importjiebali=jieba.lcut(句子,cut_all=True)词频统计li=["a","b","a"]d={}forwinli: #查看这个w在字典中有几......
  • 电力系统机组组合优化调度(IEEE14节点、IEEE30节点、IEEE118节点)(Matlab代码实现)
     ......
  • 车载测试协议:ISO-14229、ISO-15765、ISO-11898、ISO-26262【车企实操项目学习】
      FOTA模块中OTA的知识点:1.测试过程中发现哪几类问题?   可能就是一个单键的ecu,比如升了一个门的ecu,他的升了之后就关不上,还有就是升级组合ecu的时候,c屏上不显示进度条。2.在做ota测试的过程中,会做网络通信的测试吗?   网络通信测试的话,有做,但是目前我的......
  • 最近(2024.08.14-2024.08.25 )面试感悟
    简历最近(2024.08.14-2024.08.25)除了周末,都在面试的路上,平均每天3、4场面试,边面试边回顾知识点边完善简历,在哀鸿遍野的招聘市场里,简历做了调整,突出自己的优势,比如读过spring源码要能清楚的说出来、比如对jvm内存模型的了解,及为什么采用对应的垃圾回收算法;比如遇到的jvm内存及解决......
  • Day14|第六章 二叉树 part02| 226.翻转二叉树| 101. 对称二叉树| 104.二叉树的最大深
    226.翻转二叉树(递归只能前序或者后序,中序不行)classSolution{publicTreeNodeinvertTree(TreeNoderoot){if(root==null)returnnull;swap(root);invertTree(root.left);invertTree(root.right);//swap(root);......
  • 南沙信奥塞陈老师解一本通题:1408:素数回文数的个数
     【题目描述】求11到n之间(包括n),既是素数又是回文数的整数有多少个。【输入】一个大于11小于1000的整数n。【输出】11到n之间的素数回文数个数。【输入样例】23【输出样例】1【提示】提示:回文数指左右对称的数,如:292,333。 #include<bits/stdc++......
  • 摄像头接入到GB28181/GB35114平台LiveGBS后,如何配置从LiveGBS向上级联共享给上级海康
    @目录1、GB/T28181级联是什么2、搭建GB28181国标流媒体平台3、获取上级平台接入信息3.1、如何提供信息给上级3.2、上级国标平台如何添加下级域3.2、接入LiveGBS示例4、配置国标级联4.1、国标级联菜单4.2、添加上级平台4.3、编辑上级平台级联4.4、共享通道给上级平台(选择通道)4.5、......
  • 2024年最佳本地营销策略的14个专家建议
    本地营销对于任何企业都非常重要——无论您是在市中心开设的夫妻店,还是大型的全国连锁店。您都希望能被寻找您产品或服务的人看到和找到,他们往往是在本地进行搜索。事实上,几乎一半的谷歌搜索都带有本地意图。那么,今年有哪些最佳的本地营销趋势和策略能帮助您取得成功呢?今天,我......
  • LiveGBS GB28181/GB35114流媒体平台中关于接口鉴权和流媒体地址鉴权的配置和使用
    @目录1、安全控制1.1、HTTP接口鉴权1.2、流地址鉴权2、401Unauthorized2.1、携带token调用接口2.1.1、获取鉴权token2.1.2、调用其它接口2.1.2.1、携带CookieToken2.1.2.2、携带URLToken2.2、play页面携带token2.3、携带StreamToken播放视频流2.3.1、获取视频流地址2.3.2、获取......