找回密码
 立即注册
首页 业界区 业界 CMake构建学习笔记24-使用通用脚本构建PROJ和GEOS ...

CMake构建学习笔记24-使用通用脚本构建PROJ和GEOS

表弊捞 昨天 19:56
1. 通用脚本

在之前的文章《CMake构建学习笔记21-通用的CMake构建脚本》中我们创建了一个通用的cmake构建脚本cmake-build.ps1:
  1. param(
  2.     [string]$SourceLocalPath,
  3.     [string]$BuildDir,
  4.     [string]$Generator,
  5.     [string]$InstallDir,
  6.     [string]$SymbolDir,
  7.     [string[]]$PdbFiles,
  8.     [hashtable]$CMakeCacheVariables,
  9.     [bool]$MultiConfig = $false  # 控制是否使用多配置类型
  10. )
  11. # 清除旧的构建目录
  12. if (Test-Path $BuildDir) {
  13.     Remove-Item -Path $BuildDir -Recurse -Force
  14. }
  15. New-Item -ItemType Directory -Path $BuildDir
  16. # 构建CMake命令行参数
  17. $CMakeArgs = @(
  18.     "-B", "`"$BuildDir`"",
  19.     "-G", "`"$Generator`"",
  20.     "-A", "x64"
  21. )
  22. if ($MultiConfig) {
  23.     $CMakeArgs += "-DCMAKE_CONFIGURATION_TYPES=RelWithDebInfo"
  24. }
  25. else {
  26.     $CMakeArgs += "-DCMAKE_BUILD_TYPE=RelWithDebInfo"
  27. }
  28. $CMakeArgs += (
  29.     "-DCMAKE_PREFIX_PATH=`"$InstallDir`"",
  30.     "-DCMAKE_INSTALL_PREFIX=`"$InstallDir`""
  31. )
  32. # 添加额外的CMake缓存变量
  33. foreach ($key in $CMakeCacheVariables.Keys) {
  34.     $CMakeArgs += "-D$key=$($CMakeCacheVariables[$key])"
  35. }
  36. # 配置CMake
  37. cmake $SourceLocalPath $CMakeArgs
  38. # 构建阶段,指定构建类型
  39. cmake --build $BuildDir --config RelWithDebInfo --parallel
  40. # 安装阶段,指定构建类型和安装目标
  41. cmake --build $BuildDir --config RelWithDebInfo --target install
  42. # 复制符号库
  43. foreach ($file in $PdbFiles) {  
  44.     Write-Output $file
  45.     if (Test-Path $file) {
  46.         Copy-Item -Path $file -Destination $SymbolDir
  47.     }
  48.     else {
  49.         Write-Output "Warning: PDB file not found: $file"
  50.     }
  51. }
  52. # 清理构建目录
  53. #Remove-Item -Path $BuildDir -Recurse -Force
复制代码
在《CMake构建学习笔记22-libxml2库的构建》这篇文章中使用这个脚本构建了libxml2库:
  1. param(   
  2.     [string]$Name = "libxml2-v2.14.4",
  3.     [string]$SourceDir = "../Source",
  4.     [string]$Generator,
  5.     [string]$InstallDir,  
  6.     [string]$SymbolDir
  7. )
  8. # 根据 $Name 动态构建路径
  9. $zipFilePath = Join-Path -Path $SourceDir -ChildPath "$Name.zip"
  10. $SourcePath = Join-Path -Path $SourceDir -ChildPath $Name
  11. $BuildDir = Join-Path -Path "." -ChildPath $Name
  12. # 解压ZIP文件到指定目录
  13. if (!(Test-Path $SourcePath)) {
  14.     Expand-Archive -LiteralPath $zipFilePath -DestinationPath $SourceDir -Force
  15. }
  16. # 检查目标文件是否存在,以判断是否安装
  17. $DstFilePath = "$InstallDir/bin/libxml2.dll"
  18. if (Test-Path $DstFilePath) {
  19.     Write-Output "The current library has been installed."
  20.     exit 1
  21. }
  22. # 复制符号库
  23. $PdbFiles = @(
  24.     "$BuildDir/RelWithDebInfo/libxml2.pdb"
  25. )
  26. # 额外构建参数
  27. $CMakeCacheVariables = @{
  28.     BUILD_SHARED_LIBS = "ON"
  29.     LIBXML2_WITH_ZLIB = "ON"
  30.     LIBXML2_WITH_ICONV = "ON"
  31.     LIBXML2_WITH_HTTP = "ON"
  32. }
  33. # 调用通用构建脚本
  34. . ./cmake-build.ps1 -SourceLocalPath $SourcePath `
  35.     -BuildDir $BuildDir `
  36.     -Generator $Generator `
  37.     -InstallDir $InstallDir `
  38.     -SymbolDir $SymbolDir `
  39.     -PdbFiles $PdbFiles `
  40.     -CMakeCacheVariables $CMakeCacheVariables `
  41.     -MultiConfig $true
复制代码
因为提供了cmake构建方式的程序的构建行为是比较统一的,这个构建libxml2库的脚本可以进一步封装,形成一个通用的调用cmake-build.ps1构建程序的脚本。cmake-build.ps1只是包含了调用cmake执行构建的内容,但是其实整个构建过程需要做的事情很多,比如安装符号库、安装程序的依赖库等等,这些过程指的再封装一层构建的脚本。笔者封装的脚本build-common.ps1如下:
  1. # build-library.ps1
  2. param(
  3.     [Parameter(Mandatory=$true)]
  4.     [string]$Name,
  5.     [Parameter(Mandatory=$true)]
  6.     [string]$SourceDir,
  7.     [Parameter(Mandatory=$true)]
  8.     [string]$InstallDir,
  9.     [string]$SymbolDir,
  10.     [string]$Generator,
  11.     [string]$MSBuild,
  12.     [hashtable]$CMakeCacheVariables = @{},
  13.     [string[]]$PdbFiles = @(),
  14.     [string]$TargetDll,  # 用于判断是否已安装的 DLL 路径
  15.     [bool]$MultiConfig = $false,  # 控制是否使用多配置类型
  16.     [bool]$Force = $false,        # 是否强制重新构建
  17.     [bool]$Cleanup = $true,        # 是否在构建完成后删除源码和构建目录   
  18.     [string[]]$Librarys = @()  # 可选的依赖库数组,例如:-Librarys "zlib", "libjpeg"
  19. )
  20. # 动态路径构建
  21. $zipFilePath = Join-Path -Path $SourceDir -ChildPath "$Name.zip"
  22. $SourcePath = Join-Path -Path $SourceDir -ChildPath $Name
  23. $BuildDir = Join-Path -Path "." -ChildPath $Name
  24. # 检查是否已经安装(通过目标 DLL)
  25. if (-not $Force -and $TargetDll -and (Test-Path $TargetDll)) {
  26.     Write-Output "Library already installed: $TargetDll"
  27.     exit 0
  28. }
  29. # 创建所有依赖库的容器
  30. if ($Librarys.Count -gt 0) {
  31.     . "./BuildRequired.ps1"
  32.     BuildRequired -Librarys $Librarys
  33. }
  34. # 确保源码目录存在:解压 ZIP
  35. if (!(Test-Path $SourcePath)) {
  36.     if (!(Test-Path $zipFilePath)) {
  37.         Write-Error "Archive not found: $zipFilePath"
  38.         exit 1
  39.     }
  40.     Write-Output "Extracting $zipFilePath to $SourceDir..."
  41.     Expand-Archive -LiteralPath $zipFilePath -DestinationPath $SourceDir -Force
  42. }
  43. # 如果是强制构建,且构建目录已存在,先删除旧的构建目录(确保干净构建)
  44. if ($Force -and (Test-Path $BuildDir)) {
  45.     Write-Output "Force mode enabled. Removing previous build directory: $BuildDir"
  46.     Remove-Item $BuildDir -Recurse -Force -ErrorAction SilentlyContinue
  47. }
  48. # 遍历并添加前缀
  49. $PdbFiles = $PdbFiles | ForEach-Object {
  50.     Join-Path -Path $BuildDir -ChildPath $_
  51. }
  52. # 调用通用 CMake 构建脚本
  53. Write-Output "Starting build for $Name..."
  54. . ./cmake-build.ps1 -SourceLocalPath $SourcePath `
  55.     -BuildDir $BuildDir `
  56.     -Generator $Generator `
  57.     -InstallDir $InstallDir `
  58.     -SymbolDir $SymbolDir `
  59.     -PdbFiles $PdbFiles `
  60.     -CMakeCacheVariables $CMakeCacheVariables `
  61.     -MultiConfig $MultiConfig
  62. if ($LASTEXITCODE -ne 0) {
  63.     Write-Error "Build failed for $Name."
  64.     exit $LASTEXITCODE
  65. }
  66. # 构建成功后,根据 Cleanup 开关决定是否删除
  67. if ($Cleanup) {
  68.     Write-Output "Build succeeded. Cleaning up temporary directories..."
  69.     if (Test-Path $SourcePath) {
  70.         Remove-Item $SourcePath -Recurse -Force -ErrorAction SilentlyContinue
  71.         Write-Output "Removed source directory: $SourcePath"
  72.     }
  73.     if (Test-Path $BuildDir) {
  74.         Remove-Item $BuildDir -Recurse -Force -ErrorAction SilentlyContinue
  75.         Write-Output "Removed build directory: $BuildDir"
  76.     }
  77. }
  78. Write-Output "Build completed for $Name."
复制代码
这段脚本干了很多零碎的事情,但是对于一个完整的构建系统是必须的,比如判断是否需要强制构建、是否需要清理中间文件、安装程序的依赖库、安装符号库等等。另外,脚本的使用源代码其实是从压缩包解压出来的,这是因为笔者需要将源代码文件也值得放在git中进行管理,使用源代码压缩包更为方便。
2. 构建geos、proj

在实现了通用脚本build-common.ps1之后,构建程序就非常容易了,比如构建geos的脚本如下:
  1. # geos.ps1
  2. param(   
  3.     [string]$Name = "geos-3.12.2",
  4.     [string]$SourceDir = "../Source",
  5.     [string]$Generator,
  6.     [string]$InstallDir,  
  7.     [string]$SymbolDir,  
  8.     [bool]$Force = $false,        # 是否强制重新构建
  9.     [bool]$Cleanup = $true        # 是否在构建完成后删除源码和构建目录
  10. )
  11. # 目标文件
  12. $DllPath = "$InstallDir/bin/geos_c.dll"
  13. # 依赖库数组
  14. $Librarys = @()  
  15. # 符号库文件
  16. $PdbFiles = @(
  17.     "bin/RelWithDebInfo/geos.pdb",
  18.     "bin/RelWithDebInfo/geos_c.pdb"
  19. )
  20. # 额外构建参数
  21. $CMakeCacheVariables = @{
  22.     BUILD_TESTING = "OFF"
  23. }
  24. . ./build-common.ps1 -Name $Name `
  25.     -SourceDir $SourceDir `
  26.     -InstallDir $InstallDir `
  27.     -SymbolDir $SymbolDir `
  28.     -Generator $Generator `
  29.     -TargetDll $DllPath `
  30.     -PdbFiles $PdbFiles `
  31.     -CMakeCacheVariables $CMakeCacheVariables `
  32.     -MultiConfig $false `
  33.     -Force $Force `
  34.     -Cleanup $Cleanup `
  35.     -Librarys $Librarys
复制代码
在这个脚本中,$SourceDir是源代码压缩包所在的文件夹,$Name是压缩包和压缩包内文件夹的名称。而构建proj的脚本如下:
  1. # proj.ps1
  2. param(   
  3.     [string]$Name = "proj-9.4.1",
  4.     [string]$SourceDir = "../Source",
  5.     [string]$Generator,
  6.     [string]$InstallDir,  
  7.     [string]$SymbolDir,  
  8.     [bool]$Force = $false,        # 是否强制重新构建
  9.     [bool]$Cleanup = $true        # 是否在构建完成后删除源码和构建目录
  10. )
  11. # 目标文件
  12. $DllPath = "$InstallDir/bin/proj_9_4.dll"
  13. # 依赖库数组
  14. $Librarys = @("nlohmann-json", "sqlite", "libtiff")
  15. # 符号库文件
  16. $PdbFiles = @(
  17.     "bin/RelWithDebInfo/proj_9_4.pdb"      
  18. )
  19. # 额外构建参数
  20. $CMakeCacheVariables = @{  
  21.     BUILD_TESTING  = "OFF"
  22.     ENABLE_CURL    = "OFF"
  23.     BUILD_PROJSYNC = "OFF"
  24. }
  25. . ./build-common.ps1 -Name $Name `
  26.     -SourceDir $SourceDir `
  27.     -InstallDir $InstallDir `
  28.     -SymbolDir $SymbolDir `
  29.     -Generator $Generator `
  30.     -TargetDll $DllPath `
  31.     -PdbFiles $PdbFiles `
  32.     -CMakeCacheVariables $CMakeCacheVariables `
  33.     -MultiConfig $false `
  34.     -Force $Force `
  35.     -Cleanup $Cleanup `
  36.     -Librarys $Librarys
复制代码
proj必须依赖于sqlite,具体的构建办法可参看《CMake构建学习笔记23-SQLite库的构建》。因为库程序本身就可能会依赖别的依赖库,所以在这里干脆实现了在构建库之前,也构建该库的依赖库,具体实在build-common.ps1中实现:
  1. if ($Librarys.Count -gt 0) {
  2.     . "./BuildRequired.ps1"
  3.     BuildRequired -Librarys $Librarys
  4. }
复制代码
BuildRequired.ps1也是个构建脚本,具体内容非常简单,就是调用依赖库的构建脚本:
  1. function BuildRequired {
  2.     param (
  3.         [string[]]$Librarys
  4.     )
  5.     Write-Output "------------------------------------------------"  
  6.     Write-Output "Start installing all required dependencies..."     
  7.     foreach ($item in $Librarys) {
  8.         Write-Output "Find the library named $item and start installing..."        
  9.         # 动态构建脚本文件名并执行
  10.         $BuildScript = "./$item.ps1";           
  11.         & $BuildScript -Generator $Generator -InstallDir $InstallDir -SymbolDir $SymbolDir      
  12.     }
  13.     Write-Output "All required dependencies have been installed."   
  14.     Write-Output "------------------------------------------------"  
  15. }
复制代码
3. 其他

提供的脚本太多,笔者确实也觉得有点太绕了,反而不如前面的文章的脚本内容直观。不过这也符合编程的基本思路吧,开始的程序都很简单直接,后来随着功能的增多,慢慢就变得越来越抽象难以理解。以上脚本都收录在项目中,可参考使用。

来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

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