当前位置: 首页 > ai >正文

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

1. 通用脚本

在之前的文章《CMake构建学习笔记21-通用的CMake构建脚本》中我们创建了一个通用的cmake构建脚本cmake-build.ps1

param([string]$SourceLocalPath,[string]$BuildDir,[string]$Generator,[string]$InstallDir,[string]$SymbolDir,[string[]]$PdbFiles,[hashtable]$CMakeCacheVariables,[bool]$MultiConfig = $false  # 控制是否使用多配置类型
)# 清除旧的构建目录
if (Test-Path $BuildDir) {Remove-Item -Path $BuildDir -Recurse -Force
}
New-Item -ItemType Directory -Path $BuildDir# 构建CMake命令行参数
$CMakeArgs = @("-B", "`"$BuildDir`"","-G", "`"$Generator`"","-A", "x64"
)if ($MultiConfig) {$CMakeArgs += "-DCMAKE_CONFIGURATION_TYPES=RelWithDebInfo"
}
else {$CMakeArgs += "-DCMAKE_BUILD_TYPE=RelWithDebInfo"
}$CMakeArgs += ("-DCMAKE_PREFIX_PATH=`"$InstallDir`"","-DCMAKE_INSTALL_PREFIX=`"$InstallDir`""
)# 添加额外的CMake缓存变量
foreach ($key in $CMakeCacheVariables.Keys) {$CMakeArgs += "-D$key=$($CMakeCacheVariables[$key])"
}# 配置CMake
cmake $SourceLocalPath $CMakeArgs# 构建阶段,指定构建类型
cmake --build $BuildDir --config RelWithDebInfo --parallel# 安装阶段,指定构建类型和安装目标
cmake --build $BuildDir --config RelWithDebInfo --target install# 复制符号库
foreach ($file in $PdbFiles) {  Write-Output $fileif (Test-Path $file) {Copy-Item -Path $file -Destination $SymbolDir}else {Write-Output "Warning: PDB file not found: $file"}
}# 清理构建目录
#Remove-Item -Path $BuildDir -Recurse -Force

在《CMake构建学习笔记22-libxml2库的构建》这篇文章中使用这个脚本构建了libxml2库:

param(    [string]$Name = "libxml2-v2.14.4",[string]$SourceDir = "../Source",[string]$Generator,[string]$InstallDir,  [string]$SymbolDir 
)# 根据 $Name 动态构建路径
$zipFilePath = Join-Path -Path $SourceDir -ChildPath "$Name.zip"
$SourcePath = Join-Path -Path $SourceDir -ChildPath $Name
$BuildDir = Join-Path -Path "." -ChildPath $Name# 解压ZIP文件到指定目录
if (!(Test-Path $SourcePath)) {Expand-Archive -LiteralPath $zipFilePath -DestinationPath $SourceDir -Force
}# 检查目标文件是否存在,以判断是否安装
$DstFilePath = "$InstallDir/bin/libxml2.dll"
if (Test-Path $DstFilePath) {Write-Output "The current library has been installed."exit 1
} # 复制符号库
$PdbFiles = @("$BuildDir/RelWithDebInfo/libxml2.pdb"
) # 额外构建参数
$CMakeCacheVariables = @{BUILD_SHARED_LIBS = "ON"LIBXML2_WITH_ZLIB = "ON"LIBXML2_WITH_ICONV = "ON"LIBXML2_WITH_HTTP = "ON"
}# 调用通用构建脚本
. ./cmake-build.ps1 -SourceLocalPath $SourcePath `-BuildDir $BuildDir `-Generator $Generator `-InstallDir $InstallDir `-SymbolDir $SymbolDir `-PdbFiles $PdbFiles `-CMakeCacheVariables $CMakeCacheVariables `-MultiConfig $true

因为提供了cmake构建方式的程序的构建行为是比较统一的,这个构建libxml2库的脚本可以进一步封装,形成一个通用的调用cmake-build.ps1构建程序的脚本。cmake-build.ps1只是包含了调用cmake执行构建的内容,但是其实整个构建过程需要做的事情很多,比如安装符号库、安装程序的依赖库等等,这些过程指的再封装一层构建的脚本。笔者封装的脚本build-common.ps1如下:

# build-library.ps1
param([Parameter(Mandatory=$true)][string]$Name,[Parameter(Mandatory=$true)][string]$SourceDir,[Parameter(Mandatory=$true)][string]$InstallDir,[string]$SymbolDir,[string]$Generator,[string]$MSBuild,[hashtable]$CMakeCacheVariables = @{},[string[]]$PdbFiles = @(),[string]$TargetDll,  # 用于判断是否已安装的 DLL 路径[bool]$MultiConfig = $false,  # 控制是否使用多配置类型[bool]$Force = $false,        # 是否强制重新构建[bool]$Cleanup = $true,        # 是否在构建完成后删除源码和构建目录   [string[]]$Librarys = @()  # 可选的依赖库数组,例如:-Librarys "zlib", "libjpeg"
)# 动态路径构建
$zipFilePath = Join-Path -Path $SourceDir -ChildPath "$Name.zip"
$SourcePath = Join-Path -Path $SourceDir -ChildPath $Name
$BuildDir = Join-Path -Path "." -ChildPath $Name# 检查是否已经安装(通过目标 DLL)
if (-not $Force -and $TargetDll -and (Test-Path $TargetDll)) {Write-Output "Library already installed: $TargetDll"exit 0
}# 创建所有依赖库的容器
if ($Librarys.Count -gt 0) {. "./BuildRequired.ps1"BuildRequired -Librarys $Librarys
}# 确保源码目录存在:解压 ZIP
if (!(Test-Path $SourcePath)) {if (!(Test-Path $zipFilePath)) {Write-Error "Archive not found: $zipFilePath"exit 1}Write-Output "Extracting $zipFilePath to $SourceDir..."Expand-Archive -LiteralPath $zipFilePath -DestinationPath $SourceDir -Force
}# 如果是强制构建,且构建目录已存在,先删除旧的构建目录(确保干净构建)
if ($Force -and (Test-Path $BuildDir)) {Write-Output "Force mode enabled. Removing previous build directory: $BuildDir"Remove-Item $BuildDir -Recurse -Force -ErrorAction SilentlyContinue
}# 遍历并添加前缀
$PdbFiles = $PdbFiles | ForEach-Object {Join-Path -Path $BuildDir -ChildPath $_
}# 调用通用 CMake 构建脚本
Write-Output "Starting build for $Name..."
. ./cmake-build.ps1 -SourceLocalPath $SourcePath `-BuildDir $BuildDir `-Generator $Generator `-InstallDir $InstallDir `-SymbolDir $SymbolDir `-PdbFiles $PdbFiles `-CMakeCacheVariables $CMakeCacheVariables `-MultiConfig $MultiConfigif ($LASTEXITCODE -ne 0) {Write-Error "Build failed for $Name."exit $LASTEXITCODE
}# 构建成功后,根据 Cleanup 开关决定是否删除
if ($Cleanup) {Write-Output "Build succeeded. Cleaning up temporary directories..."if (Test-Path $SourcePath) { Remove-Item $SourcePath -Recurse -Force -ErrorAction SilentlyContinue Write-Output "Removed source directory: $SourcePath"}if (Test-Path $BuildDir) { Remove-Item $BuildDir -Recurse -Force -ErrorAction SilentlyContinue Write-Output "Removed build directory: $BuildDir"}
}Write-Output "Build completed for $Name."

这段脚本干了很多零碎的事情,但是对于一个完整的构建系统是必须的,比如判断是否需要强制构建、是否需要清理中间文件、安装程序的依赖库、安装符号库等等。另外,脚本的使用源代码其实是从压缩包解压出来的,这是因为笔者需要将源代码文件也值得放在git中进行管理,使用源代码压缩包更为方便。

2. 构建geos、proj

在实现了通用脚本build-common.ps1之后,构建程序就非常容易了,比如构建geos的脚本如下:

# geos.ps1
param(    [string]$Name = "geos-3.12.2",[string]$SourceDir = "../Source",[string]$Generator,[string]$InstallDir,  [string]$SymbolDir,  [bool]$Force = $false,        # 是否强制重新构建[bool]$Cleanup = $true        # 是否在构建完成后删除源码和构建目录
)# 目标文件
$DllPath = "$InstallDir/bin/geos_c.dll"# 依赖库数组
$Librarys = @()  # 符号库文件
$PdbFiles = @("bin/RelWithDebInfo/geos.pdb","bin/RelWithDebInfo/geos_c.pdb"
) # 额外构建参数
$CMakeCacheVariables = @{BUILD_TESTING = "OFF"
}. ./build-common.ps1 -Name $Name `-SourceDir $SourceDir `-InstallDir $InstallDir `-SymbolDir $SymbolDir `-Generator $Generator `-TargetDll $DllPath `-PdbFiles $PdbFiles `-CMakeCacheVariables $CMakeCacheVariables `-MultiConfig $false `-Force $Force `-Cleanup $Cleanup `-Librarys $Librarys

在这个脚本中,$SourceDir是源代码压缩包所在的文件夹,$Name是压缩包和压缩包内文件夹的名称。而构建proj的脚本如下:

# proj.ps1
param(    [string]$Name = "proj-9.4.1",[string]$SourceDir = "../Source",[string]$Generator,[string]$InstallDir,  [string]$SymbolDir,  [bool]$Force = $false,        # 是否强制重新构建[bool]$Cleanup = $true        # 是否在构建完成后删除源码和构建目录
)# 目标文件
$DllPath = "$InstallDir/bin/proj_9_4.dll"# 依赖库数组
$Librarys = @("nlohmann-json", "sqlite", "libtiff")# 符号库文件
$PdbFiles = @("bin/RelWithDebInfo/proj_9_4.pdb"      
) # 额外构建参数
$CMakeCacheVariables = @{  BUILD_TESTING  = "OFF"ENABLE_CURL    = "OFF"BUILD_PROJSYNC = "OFF"
}. ./build-common.ps1 -Name $Name `-SourceDir $SourceDir `-InstallDir $InstallDir `-SymbolDir $SymbolDir `-Generator $Generator `-TargetDll $DllPath `-PdbFiles $PdbFiles `-CMakeCacheVariables $CMakeCacheVariables `-MultiConfig $false `-Force $Force `-Cleanup $Cleanup `-Librarys $Librarys

proj必须依赖于sqlite,具体的构建办法可参看《CMake构建学习笔记23-SQLite库的构建》。因为库程序本身就可能会依赖别的依赖库,所以在这里干脆实现了在构建库之前,也构建该库的依赖库,具体实在build-common.ps1中实现:

if ($Librarys.Count -gt 0) {. "./BuildRequired.ps1"BuildRequired -Librarys $Librarys
}

BuildRequired.ps1也是个构建脚本,具体内容非常简单,就是调用依赖库的构建脚本:

function BuildRequired {param ([string[]]$Librarys)Write-Output "------------------------------------------------"  Write-Output "Start installing all required dependencies..."     foreach ($item in $Librarys) { Write-Output "Find the library named $item and start installing..."        # 动态构建脚本文件名并执行$BuildScript = "./$item.ps1";           & $BuildScript -Generator $Generator -InstallDir $InstallDir -SymbolDir $SymbolDir       }Write-Output "All required dependencies have been installed."   Write-Output "------------------------------------------------"  
}

3. 其他

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

http://www.xdnf.cn/news/19707.html

相关文章:

  • Unity开发保姆级教程:C#脚本+物理系统+UI交互,3大模块带你通关游戏开发
  • Spring Boot配置error日志发送至企业微信
  • char、short、int等整型类型取值范围
  • Java继承
  • 【YOLO】数据增强bug
  • mysql第五天学习 Mysql全局优化总结
  • AI+教育:用BERT构建个性化错题推荐系统
  • 多线程同步安全机制
  • 进程管理和IPC
  • 嵌入式|RTOS教学——FreeRTOS基础1:准备工作
  • 解锁产品说明书的“视觉密码”:多模态 RAG 与 GPT-4 的深度融合 (AI应用与技术系列)
  • 深度学习与 OpenCV 的深度羁绊:从技术协同到代码实践
  • k8s知识点总结3
  • 数据结构_循环队列_牺牲一个存储空间_不牺牲额外的存储空间 Circular Queue(C语言实现_超详细)
  • 【Linux】Linux开发必备:Git版本控制与GDB调试全指南
  • 物联网时序数据存储方案:Apache IoTDB 集群部署全流程 + TimechoDB 优势解读
  • 代码质量保障:使用Jest和React Testing Library进行单元测试
  • 服务器固件全景地图:从BIOS到BMC,升级背后的安全与性能革命
  • 日志分析与安全数据上传脚本
  • 飞算JavaAI真能帮小白搞定在线图书借阅系统?开发效果大揭秘!
  • PgManage:一款免费开源、跨平台的数据库管理工具
  • 什么是 Java 的反射机制?它有什么优缺点?
  • 普通大学生的 Web3 实习怎么找?行业指南与实践技巧这里看
  • Redis 哨兵 (基于 Docker)
  • 梯度波导_FDTD_学习_代码
  • 嵌入式 - 硬件:51单片机
  • 实训云上搭建分布式Hadoop集群[2025] 实战笔记
  • 【llama.cpp】qwen2_vl_surgery.py详解
  • Web 开发 17
  • C++中的“平凡”之美:std::unique_ptr源码探秘