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 $file
if (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 $MultiConfig if ($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. 其他

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

CMake构建学习笔记24-使用通用脚本构建PROJ和GEOS的更多相关文章

  1. Linux Shell输出颜色字符学习笔记(附Python脚本实现自动化定制生成)

    齿轮发出咔嚓一声,向前进了一格.而一旦向前迈进,齿轮就不能倒退了.这就是世界的规则. 0x01背景 造了个轮子:御剑师傅的ipintervalmerge的Python版本.觉得打印的提示信息如果是普通 ...

  2. Android自动化学习笔记:编写MonkeyRunner脚本的几种方式

    ---------------------------------------------------------------------------------------------------- ...

  3. python 学习笔记 12 -- 写一个脚本获取城市天气信息

    近期在玩树莓派,前面写过一篇在树莓派上使用1602液晶显示屏,那么可以显示后最重要的就是显示什么的问题了. 最easy想到的就是显示时间啊,CPU利用率啊.IP地址之类的.那么我认为呢,假设可以显示当 ...

  4. SpringCloud学习笔记(6):使用Zuul构建服务网关

    简介 Zuul是Netflix提供的一个开源的API网关服务器,SpringCloud对Zuul进行了整合和增强.服务网关Zuul聚合了所有微服务接口,并统一对外暴露,外部客户端只需与服务网关交互即可 ...

  5. [原创]java WEB学习笔记24:MVC案例完整实践(part 5)---删除操作的设计与实现

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  6. Java学习笔记之使用反射+泛型构建通用DAO

    PS:最近简单的学了学后台Servlet+JSP.也就只能学到这里了.没那么多精力去学SSH了,毕竟Android还有很多东西都没学完.. 学习内容: 1.如何使用反射+泛型构建通用DAO. 1.使用 ...

  7. Catlike学习笔记(1.4)-使用Unity构建分形

    又两个星期没写文章了,主要是沉迷 Screeps 这个游戏,真的是太好玩了导致我这两个礼拜 Github 小绿点几乎天天刷.其实想开一个新坑大概把自己写 AI 的心路历程记录下,不过觉得因为要消耗太多 ...

  8. C++学习笔记24:makefile文件

    makefile make命令:负责c/c++程序编译与链接 make根据指定命令进行建构 建构规则文件:GNUmakefile , makefile,Makefile makefile 文件格式 m ...

  9. 网站构建学习笔记(0)——基本概念了解及资源学习(copy自w3school)

    一.学习方面 1.WWW - 万维网 什么是 WWW? WWW 指万维网(World Wide Web) 万维网常被称为Web Web 是由遍布全球的计算机所组成的网络 所有 Web 中的计算机都可以 ...

  10. 《Spring实战》学习笔记-第五章:构建Spring web应用

    之前一直在看<Spring实战>第三版,看到第五章时发现很多东西已经过时被废弃了,于是现在开始读<Spring实战>第四版了,章节安排与之前不同了,里面应用的应该是最新的技术. ...

随机推荐

  1. 东方财富服务端开发暑期实习面试,已拿offer!!

    这是一位球友投稿的东方财富服务端开发暑期实习面经,问的内容还是比较多的,难度也相对比较大.下面是正文. 最近参与了东方财富服务端开发暑期实习的招聘,并最终收获了 Offer.整个过程持续了大约一周,从 ...

  2. 1. 如何通过SSH连接到vector rob

    #anki #vector #robot #digital-dream-labs ssh连接到vector机器人操作系统: 将机器人放到充电底座上,连接电源,机器人自动启动,成功后,双击背部按钮打开蓝 ...

  3. java--jdbc优化

    BeanUtils组件 程序中对javabean的操作很频繁, 所以apache提供了一套开源的api,方便对javabean的操作!即BeanUtils组件. BeanUtils组件, 作用是简化j ...

  4. sql server的 安装说明与基本工具概念

    shared memory(共享内存) 代表连接本机的数据库:Named Pipes(命名管道)针对局域网内的数据库链接:tcp/ip 针对互联网内的数据库链接 ------数据库的操作: 1.联机事 ...

  5. 论文解读:MASS-EDITING MEMORY IN A TRANSFORMER(MEMIT)

      论文发表于人工智能顶会ICLR(原文链接).在模型编辑方法中,过去工作主要局限于更新单个事实.因此,基于ROME,本文开发了MEMIT,在大模型GPT-J(6B)和GPT-NeoX(20B)上实现 ...

  6. 前端开发系列111-工程化篇之Yeoman脚手架工具使用入门

    Yeoman是一款流行的前端的脚手架工具. 脚手架工具可以用来快速的自动生成项目的必要文件和基础文件结构.Yeoman使用的内建命令为yo,同时它自己也是一个包管理工具和自动化任务工具,它基于特定的模 ...

  7. runnable & callable

    简介 简单来说这两个接口都是执行多线程里面使用的东西. 参考链接 https://blog.csdn.net/qq_41357573/article/details/88887566 区别 Java多 ...

  8. NOIP2024 游记

    没有时间为 CSP2024 哀悼了,随即到达战场的是 NOIP2024! 2024.11.24 听从 cy 的谆谆教诲,一场模拟赛没打,直接摆烂. 2024.11.29 明天怎么复赛了?!? 摆烂到底 ...

  9. fowsniff WP

    下载地址: https://download.vulnhub.com/fowsniff/Fowsniff_CTF_ova.7z category:重要 awk剪切得到字典,巩固awk使用技巧 motd ...

  10. MySQL 16“order by”是怎么工作的?

    假设要查询城市是"杭州"的所有人名字,并且按照姓名排序返回前1000个人的姓名与年龄.那么SQL语句可以写为: select city,name,age from t where ...