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

打印 上一主题 下一主题

主题 869|帖子 869|积分 2607

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

  • C/C++语言自己、标准库、以及操作体系API险些干不了什么,除非你真的想从零开始造轮子。
  • 开始找一些现成的实现组成依赖库。最好看能不能找到预编译包或者安装包,纵然找到了,由于二进制兼容的问题,你也不一定能够利用。
  • 如果没找到预编译包或者安装包,那么就必要自己从源代码进行构建了。如果提供了CMake的构建方式就挺好,万一没有提供,就得自己想办法构造工程进行构建。
  • 注意,依赖库自己是必要依赖库的!比如,你构建GDAL的时候会发现PROJ是GDAL的必须依赖,等你开始构建PROJ的时候又发现sqlite3是PROJ必须依赖,而你预备构建sqlite3的时候发现sqlite3是不提供CMake方式的。
  • 不谈构建过程中处理的一系列问题。等你把依赖库构建完成了,你就得考虑如何引入了。如果你利用动态链接库,你必要进行头文件、动态库导入库以及动态库干系的配置。如果头文件错了,你会发现无法编译;如果动态库导入库错误,你会发现无法链接;如果动态库不精确,你会发现无法运行。
  • 末了开始在源代码中include头文件,调用依赖库干系的功能进行操作。
嗯,说真话笔者光是写这些步骤都觉得有点汗出如浆了。这要是换成利用Python或者JavaScript进行开发,一个install指令,一个import语句就全部搞定,也难怪C/C++开发的效率一直被步伐员诟病呢。不外,C/C++领域也不是一直在固步自封,Windows体系下也可以利用一些包管理器,例如vcpkg、Conan、Chocolatey等。个人以为,这些包管理器正在逐渐成熟过程中,不外尚必要一些时间完善,有兴趣的同学可以进行试用。
别的一种方式就是像笔者一样,尝试构造一个属于自己或者自己团队的依赖库管理工具。这样做的缘故原由有三:

  • 不同环境下的C/C++包存在二进制兼容的问题。
  • 构建Release带调试信息的构建成果,以及符号库文件。
  • 有些库包很少见,通用的包管理器不一定收纳。
那么详细如何实现呢?实在不用想的太复杂,我们将全部必要的构建成果都构建到同一个目录,而且将这个目录设置成环境变量。这样,在我们日常的步伐中就可以依托这个环境变量配置依赖库。这意味着全部团队成员的代码工程的配置都可以是一样的,我们就可以忽略掉不同软硬件环境的不同,实现了代码项目的一致性。
所以,关键就是将这些常见的组件构建构造起来。不能利用CMake的GUI工具,因为不同的库各自有自己独特的构建选项,最好将其通过脚本记载。不妨将构建的脚本写的完善一点,自动化一点,代码文件从那里来,末了的构建成果输出到那里。例如,前面博文中构建libzip库的完备Powershell脚本libzip.ps1为:
  1. param(   
  2.     [string]$SourceAddress = "https://github.com/nih-at/libzip/archive/refs/tags/v1.10.1.zip",
  3.     [string]$SourceZipPath = "../Source/libzip-1.10.1.zip",
  4.     [string]$SourceLocalPath = "./libzip-1.10.1",
  5.     [string]$Generator,
  6.     [string]$MSBuild,
  7.     [string]$InstallDir,
  8.     [string]$SymbolDir
  9. )
  10. # 检查目标文件是否存在,以判断是否安装
  11. $DstFilePath = "$InstallDir/bin/zip.dll"
  12. if (Test-Path $DstFilePath) {
  13.     Write-Output "The current library has been installed."
  14.     exit 1
  15. }
  16. # 创建所有依赖库的容器
  17. . "./BuildRequired.ps1"
  18. $Librarys = @("zlib")
  19. BuildRequired -Librarys $Librarys
  20. . "./DownloadAndUnzip.ps1"
  21. DownloadAndUnzip -SourceLocalPath $SourceLocalPath -SourceZipPath $SourceZipPath -SourceAddress $SourceAddress
  22. # 清除旧的构建目录
  23. $BuildDir = $SourceLocalPath + "/build"  
  24. if (Test-Path $BuildDir) {
  25.     Remove-Item -Path $BuildDir -Recurse -Force
  26. }
  27. New-Item -ItemType Directory -Path $BuildDir
  28. # 转到构建目录
  29. Push-Location $BuildDir
  30. try {
  31.     # 配置CMake  
  32.     cmake .. -G "$Generator" -A x64 `
  33.         -DCMAKE_BUILD_TYPE=RelWithDebInfo `
  34.         -DCMAKE_PREFIX_PATH="$InstallDir" `
  35.         -DCMAKE_INSTALL_PREFIX="$InstallDir" `
  36.         -DBUILD_DOC=OFF `
  37.         -DBUILD_EXAMPLES=OFF `
  38.         -DBUILD_REGRESS=OFF `
  39.         -DENABLE_OPENSSL=OFF
  40.     # 构建阶段,指定构建类型
  41.     cmake --build . --config RelWithDebInfo
  42.     # 安装阶段,指定构建类型和安装目标
  43.     cmake --build . --config RelWithDebInfo --target install
  44.     # 复制符号库
  45.     $PdbFiles = @(
  46.         "./lib/RelWithDebInfo/zip.pdb"        
  47.     )
  48.     foreach ($file in $PdbFiles) {  
  49.         Write-Output $file
  50.         Copy-Item -Path $file -Destination $SymbolDir
  51.     }   
  52. }
  53. finally {
  54.     # 返回原始工作目录
  55.     Pop-Location
  56. }
复制代码
不得不说Powershell脚本真的很强盛,你以致可以引入第三方脚本中的函数,例如这里的BuildRequired表示预先安装当前库的依赖库(实在就是调用其他库的构建脚本),而DownloadAndUnzip表示从远端把源代码下载下来而且解压到指定文件夹。接下来创建构建文件夹、配置CMake项目、构建项目以及安装项目。末了,我们把这个库符号库给移动到安装目录中去。
就像这样一个一个把必要的依赖库构建脚本写好。偶然候必要修改一下构建选项也没什么关系,修改下对应的内容重新构建就好了,这就是写脚本的长处。不外,这样一个一个调用脚本也说不上对库包进行管理了,对比一下一些比较完善的包管理器例如npm,可以再写一个总的用于管理的脚本,将以上构建脚本管理起来,如下Powershell脚本BuildCppDependency.ps1所示:
  1. param(   
  2.     [string]$Generator,
  3.     [string]$MSBuild,
  4.     [string]$InstallDir,
  5.     [string]$SymbolDir,   
  6.     [string]$Install,   
  7.     [string]$List
  8. )
  9. # 创建所有的库的容器
  10. $LibrarySet = [System.Collections.Generic.SortedSet[string]]::new()
  11. $LibrarySet.Add("zlib") > $null
  12. $LibrarySet.Add("libpng") > $null
  13. $LibrarySet.Add("libjpeg") > $null
  14. $LibrarySet.Add("libtiff") > $null
  15. $LibrarySet.Add("giflib") > $null
  16. $LibrarySet.Add("freetype") > $null
  17. $LibrarySet.Add("OpenSceneGraph") > $null
  18. $LibrarySet.Add("eigen") > $null
  19. $LibrarySet.Add("osgQt5") > $null
  20. $LibrarySet.Add("osgQt") > $null
  21. $LibrarySet.Add("minizip") > $null
  22. $LibrarySet.Add("libzip") > $null
  23. $LibrarySet.Add("opencv") > $null
  24. #$LibrarySet.Add("protobuf") > $null
  25. #$LibrarySet.Add("abseil-cpp") > $null
  26. # 检查是否传递了$Install参数
  27. if ($PSBoundParameters.ContainsKey('Install')) {   
  28.     # 比较时忽略大小写
  29.     if ($Install.ToLower() -eq "-all".ToLower()) {
  30.         Write-Output "All libraries will be installed soon..."
  31.         foreach ($item in $LibrarySet) {
  32.             Write-Output "Find the library named $item and start installing..."        
  33.             # 动态构建脚本文件名并执行
  34.             $BuildScript = "./$item.ps1";           
  35.             & $BuildScript -Generator $Generator -MSBuild $MSBuild -InstallDir $InstallDir -SymbolDir $SymbolDir
  36.         }
  37.     }
  38.     else {
  39.         # 查找某个字符串
  40.         if ($LibrarySet.Contains("$Install")) {
  41.             Write-Output "Find the library named $Install and start installing..."        
  42.             # 动态构建脚本文件名并执行
  43.             $BuildScript = "./$Install.ps1";           
  44.             & $BuildScript -Generator $Generator -MSBuild $MSBuild -InstallDir $InstallDir -SymbolDir $SymbolDir
  45.         }
  46.         else {
  47.             Write-Output "Cannot find library named $Install !"
  48.         }
  49.     }
  50. }
  51. elseif ($PSBoundParameters.ContainsKey('List')) {      
  52.     if ($List.ToLower() -eq "-all".ToLower()) {
  53.         Write-Output "The list of all libraries that can currently be installed in the repository is as follows:"
  54.         foreach ($item in $LibrarySet) {
  55.             Write-Output $item
  56.         }
  57.     }
  58. }
  59. else {
  60.     Write-Host "Please enter the parameters!"
  61. }
复制代码
再次感叹下Powershell脚本的强盛,你以致可以看到System.Collections.Generic.SortedSet,没错,这就是.Net提供的容器,通过Powershell可以也调用它。上述脚本提供了根本的检察和安装功能,例如检察能安装的库,可利用如下指令:
  1. ./BuildCppDependency.ps1 -List -all
复制代码
安装特定的库:
  1. ./BuildCppDependency.ps1 -Generator "Visual Studio 16 2019" `
  2. -MSBuild "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" `
  3. -InstallDir "$env:GISBasic" `
  4. -SymbolDir "$env:GISBasic/symbols" `
  5. -Install libzip
复制代码
安装全部的库:
  1. ./BuildCppDependency.ps1 -Generator "Visual Studio 16 2019" `
  2. -MSBuild "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" `
  3. -InstallDir "$env:GISBasic" `
  4. -SymbolDir "$env:GISBasic/symbols" `
  5. -Install -all
复制代码
实在一个完善的包管理工具必要的功能照旧很多的,也非常复杂。例如包的安装是很容易,如何进行卸载呢?如何升级如何降级呢?是不是可以与IDE进行结合,自动导入依赖库而且进行配置呢?这些问题,就留待以后考虑吧。
末了,就将写的依赖库管理工具构建脚本贡献给各人参考吧:地址

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

飞不高

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表