在 macOS 上从源码构建 PowerShell:环境引导、Start-PSBuild 与产物验证实战指南
本文以 PowerShell 仓库官方文档 docs/building/macos.md 为骨架,结合仓库内 build.psm1 的真实实现,系统讲解如何在 Apple Silicon(arm64)与 Intel(x64)macOS 主机上自托管 PowerShell、用 Start-PSBootstrap 安装编译依赖与 .NET SDK,并借助 Start-PSBuild 完成构建、定位产物、排查 ulimit 文件句柄等经典问题。读完本文你将具备从源码构建并验证 pwsh 的完整动手能力。
定位与前置约束
官方构建文档按平台拆分,docs/building/macos.md 明确自身是对 Linux 构建指南 的补充——在 macOS 上构建 PowerShell 的流程与 Linux 几乎完全一致,差异主要集中在:包管理器选择(Homebrew / MacPorts)、所需原生依赖名称(OpenSSL、GNU WGet)、以及产物目录中的运行时标识(RID)。
关于系统版本,原文档指出 .NET Core 2.x 时代仅支持 macOS 10.13+;该下限随仓库所依赖的 .NET 版本而变化。以当前分支为准,仓库核心项目 System.Management.Automation 的 TargetFramework 为 net11.0(见 PowerShell.Common.props),因此实际支持范围由你所安装的 .NET SDK 决定,不必拘泥于 10.13 这个历史数字。
整个构建链路不依赖图形界面,全部通过命令行完成,与 CI 中执行的是同一套 build.psm1 逻辑。
一、环境准备:包管理器与自托管 pwsh
1.1 Homebrew 与 MacPorts 二选一
macOS 上缺少 Linux 发行版自带的包管理机制,构建依赖需要借助 Homebrew(brew)或 MacPorts(port)。这一步并非可选:仓库构建模块在初始化环境对象时会主动探测系统里是否存在这两个命令,检测逻辑位于 build.psm1:
if ($environment.IsMacOS) {
$environment += @{'UsingHomebrew' = bool}
$environment += @{'UsingMacports' = bool}
$environment += @{
'OSArchitecture' = if ((uname -v) -match 'ARM64') { 'arm64' } else { 'x64' }
}
if (-not($environment.UsingHomebrew -or $environment.UsingMacports)) {
throw "Neither Homebrew nor MacPorts is installed on this system, visit https://brew.sh/ or https://www.macports.org/ to continue"
}
}
从源码可以看到两个关键信息:
brew与port至少要有其一,否则模块直接throw中止;- macOS 的处理器架构是通过
uname -v是否包含ARM64来判断的,这决定了后续构建使用osx-arm64还是osx-x64的 RID(运行时标识),下文会再次用到。
1.2 安装自托管的 PowerShell(self-hosted)
后续所有引导与构建命令都需要在 PowerShell 会话中执行(因为是调用 build.psm1 中定义的 PowerShell 函数),所以机器上必须先有一份可用版本的 pwsh——这就是"自托管(self-hosted)"的含义:用已发布的 PowerShell 去编译出开发版 PowerShell。
安装方式为常规的 macOS 安装包(pkg)或二进制归档,仓库内也提供了辅助脚本,例如 tools/installpsh-osx.sh(配套的通用入口是 tools/install-powershell.sh,Linux 指南中的用法可参考 docs/building/linux.md)。安装完成后,在终端中先验证:
pwsh
若能正常进入 PS> 提示符,即可继续。
二、用 Start-PSBootstrap 引导编译环境
在仓库根目录启动 PowerShell,导入构建模块并执行引导:
Import-Module ./build.psm1
Start-PSBootstrap
2.1 它到底做了什么
Start-PSBootstrap 定义于 build.psm1,职责是"按所选场景安装 PowerShell 的构建依赖",其参数签名显示当前版本支持的场景集合:
[ValidateSet("Package", "DotNet", "Both", "Tools", "All")]
[string]$Scenario = "Package"
Package(默认):安装打包工具链所需的原生依赖;DotNet:仅安装 .NET SDK;Both:Package + DotNet;Tools:安装 .NET 全局工具(如dotnet-format);All:以上全部。
具体到 macOS 分支,源码实现位于 build.psm1:
} elseif ($environment.IsMacOS) {
if ($environment.UsingHomebrew) {
$baseCommand = "brew install --quiet"
} elseif ($environment.UsingMacports) {
$baseCommand = "$sudo port -q install"
}
# wget for downloading dotnet
$Deps += "wget"
# .NET Core required runtime libraries
$Deps += "openssl"
# Install dependencies
# ignore exitcode, because they may be already installed
Start-NativeExecution ([ScriptBlock]::Create("$baseCommand $Deps")) -IgnoreExitcode
}
与原文档描述一一对应,可以确认:
- 安装 OpenSSL 与 GNU WGet:
brew install --quiet wget openssl(Homebrew 路径)或sudo port -q install wget openssl(MacPorts 路径)。wget 用于后续下载 .NET SDK;openssl 是 PowerShell/.NET 运行时在 macOS 上的必需库。 - 原生依赖已装时不报错:命令以
-IgnoreExitcode执行,重复运行Start-PSBootstrap是安全的。
随后,若场景包含 DotNet(或 Both/All),模块会进入 ".NET SDK" 安装阶段(build.psm1)。此处与原文档所述"先卸载旧版 .NET CLI,再下载安装新版"对应的实现并非强制卸载,而是版本探测 + 按需安装:
- 调用
Find-Dotnet(build.psm1)在 PATH 中查找已装 SDK; - 用
Find-RequiredSDK比对已装版本与仓库锁定的dotnetCLIRequiredVersion; - 只有"未安装 / 版本不符 / 显式
-Force"三种情况才会执行Install-Dotnet(build.psm1),把 SDK 装到~/.dotnet目录下。
2.2 让 dotnet 进入 PATH
SDK 被安装到 ~/.dotnet 后,仅在 Start-PSBuild 内部调用时模块会自动定位它;如果你想在构建之外直接使用 dotnet(例如手动跑 dotnet restore、dotnet run),需要把它加入环境变量。在原文档的基础上,给出同时兼容 Bash 与 zsh 的做法,写入 ~/.zshrc 或 ~/.bash_profile:
export PATH="$HOME/.dotnet:$PATH"
之后重开终端或执行 source ~/.zshrc 使配置生效。
2.3 常见报错:error: Too many open files
这是 macOS 构建文档单独记录的第一个实战问题。由于 NuGet 客户端的历史缺陷(文档脚注指向 dotnet/cli 的 issue #809),dotnet restore 需要同时打开大量文件,超过 macOS 默认进程级文件句柄上限(256)时即报 Too many open files。
官方推荐两条修复路径:
-
临时修复(当前会话生效):
ulimit -n 2048 -
永久修复:把上述命令追加到 shell 的启动配置(
~/.zshrc/~/.bash_profile)中。
文档同时强调构建模块不会替你做这件事(见 PowerShell 仓库 issue #847),因此必须由使用者自行设置——养成先 ulimit -n 2048、再执行 dotnet restore 相关命令的习惯即可避免此坑。
三、执行构建:Start-PSBuild -UseNuGetOrg
环境就绪后,在仓库根目录的 pwsh 会话中执行:
Import-Module ./build.psm1
Start-PSBuild -UseNuGetOrg
3.1 为什么默认要带 -UseNuGetOrg
Start-PSBuild 定义于 build.psm1,参数 UseNuGetOrg 的说明是"使用 nuget.org 而非 PowerShell 私有源做包还原"。其执行路径非常直观(build.psm1):
if ($UseNuGetOrg) {
Switch-PSNugetConfig -Source Public
} else {
Write-Verbose -Message "Using default feeds which are Microsoft, use `-UseNuGetOrg` to switch to Public feeds" -Verbose
}
原因正如文档底部注释所述:PowerShell 项目默认引用私有 Azure Artifacts 源上的包,该源需要身份认证;公开用户(无内部账号)无法还原。-UseNuGetOrg 会通过 Switch-PSNugetConfig -Source Public 重写 NuGet 源配置,改用公开的 nuget.org——这也是文档与构建模块里反复强调的标准用法。
3.2 Start-PSBuild 内部的关键检查链
Start-PSBuild 在真正调用 dotnet publish 之前会依次做多项前置校验,理解这些校验能帮你快速定位失败原因(build.psm1):
Find-Dotnet:把~/.dotnet中的工具加入本次会话 PATH;precheck 'git':验证 git 在 PATH 中;precheck 'dotnet':验证 .NET SDK 已安装;Find-RequiredSDK:比对已装 SDK 与仓库锁定版本是否一致,不一致则输出红色告警并给出"删除~/.dotnet后重跑Start-PSBootstrap/Start-PSBuild -Clean"的修复指引。
其余值得注意的参数(均可在 build.psm1 中查到完整说明)包括:
| 参数 | 作用 |
|---|---|
-Runtime |
显式指定目标 RID,合法值含 osx-x64、osx-arm64、linux-x64 等;缺省时自动推导 |
-Configuration |
Debug(默认)/ Release / CodeCoverage / StaticAnalysis |
-Clean |
构建前执行 git clean -fdX 清理工作目录 |
-ResGen |
构建前重新生成 resx 资源的强类型 C# 绑定 |
-TypeGen |
构建前重新生成 CorePsTypeCatalog.cs 类型目录 |
-SMAOnly |
只重新编译 System.Management.Automation.dll,便于引擎层快速迭代 |
-Output |
自定义 dotnet publish 的输出目录 |
-Detailed |
向 dotnet 传递 --verbosity d 输出详细日志 |
在 macOS 上,若未显式传 -Runtime,模块会结合 1.1 节探测到的架构自动选择 osx-x64 或 osx-arm64(可对照 build.psm1 中的 RID 白名单)。
3.3 顶层项目与产物定位
构建实际是针对顶层宿主项目执行的 dotnet publish。在 macOS/Linux 上,这个顶层项目是 src/powershell-unix/powershell-unix.csproj,其关键属性包括:
AssemblyName为pwsh;- 编译入口为 src/powershell/Program.cs;
- 声明支持的
RuntimeIdentifiers为linux-x64;osx-x64(arm64 支持由仓库整体配置提供); - 发布时会把
src/Modules/Unix、src/Modules/Shared下的内置模块、src/Schemas/PSMaml帮助架构文件及许可证文本一并拷入产物。
因此构建成功后,可执行文件位于(原文档给出的路径):
./src/powershell-unix/bin/Debug/net6.0/osx-x64/publish/pwsh
需要说明的是,路径中 net6.0 是原文档写作时的 TargetFramework 目录,会随分支锁定的 .NET SDK 变化——当前分支的 TargetFramework 为 net11.0(见 PowerShell.Common.props),实际产物目录应为 bin/Debug/net11.0/osx-x64/publish/pwsh。直接运行它即可启动你亲手编译出的 pwsh:
./src/powershell-unix/bin/Debug/net11.0/osx-x64/publish/pwsh
运行后输入 $PSVersionTable 即可看到 Git 提交信息对应的开发版版本号。
3.4 若干高级参数与发布形态
-ForMinimalSize:产出体积最小化的构建,官方仅对linux-x64、win7-x64、osx-x64、osx-arm64等少量 RID 放行(见 build.psm1);-StopDevPowerShell:先结束正在运行的、由旧产物启动的 pwsh 进程,避免"文件被占用"导致的编译失败(build.psm1);-SkipRoslynAnalyzers/-SkipExperimentalFeatureGeneration:分别跳过 Roslyn 分析器执行、跳过用刚构建的 pwsh 生成 experimental-features 清单的步骤(后者在修改解析/编译逻辑、产物暂时无法启动时尤为有用)。
3.5 清理常见状态问题
- 若你拉了新提交后报"缺少字符串资源/找不到类型"之类的错误,往往是因为仓库内的
gen资源目录与CorePsTypeCatalog.cs类型目录过期,可运行Start-PSBuild -Clean -ResGen -TypeGen重新生成(两个预生成步骤的机制详见 docs/building/internals.md 的 ResGen 与 Type Catalog 章节); - 若 SDK 版本告警(3.2 节第 4 条),删除
~/.dotnet后依次执行Start-PSBootstrap与Start-PSBuild -Clean。
四、构建验证与测试
构建成功不等于功能正确。官方模块同样提供了测试入口:
Start-PSPester -UseNuGetOrg
Start-PSxUnit
Start-PSPester(build.psm1)运行仓库的跨平台 Pester 测试套件,测试脚本位于 test/powershell(含 Language、engine、Modules、Provider 等子目录),同样需要-UseNuGetOrg以切换到公开 NuGet 源;Start-PSxUnit(build.psm1)运行托管层的 xUnit 单元测试,测试工程见 test/xUnit/xUnit.tests.csproj。
对绝大多数源码改动场景,Start-PSPester 是回归验证的第一道关卡。若只是想快速确认"自编译产物可用",直接运行产物 pwsh 并执行几条简单命令即可完成冒烟验证。
五、常见问题速查
| 现象 | 原因与解法 |
|---|---|
Import-Module ./build.psm1 报错 |
必须在 pwsh 会话中、且在仓库根目录执行;确认自托管 PowerShell 已安装 |
| 模块 throw "Neither Homebrew nor MacPorts is installed..." | 构建环境探测要求至少一种包管理器,先安装 Homebrew 或 MacPorts |
dotnet restore 报 Too many open files |
NuGet 客户端历史缺陷,先 ulimit -n 2048(建议写入 shell 配置永久生效) |
| 提示已装 dotnet 版本与 required version 不符 | 删除 ~/.dotnet 后重跑 Start-PSBootstrap,再 Start-PSBuild -Clean |
Start-PSBuild 卡在包还原或报源认证错误 |
忘记加 -UseNuGetOrg,默认私有 Azure Artifacts 源需要认证 |
| 拉取新提交后出现缺失字符串/类型目录报错 | 运行 Start-PSBuild -ResGen -TypeGen 重新生成预编译资源 |
结语
macOS 构建是 PowerShell 全平台构建体系中与 Linux 几乎共享同一条流水线的分支:Start-PSBootstrap 负责包管理器差异(Homebrew/MacPorts)、原生依赖(wget、openssl)与 .NET SDK 的就位,Start-PSBuild -UseNuGetOrg 完成从源码到可执行 pwsh 的编译发布。理解 build.psm1 中的架构探测、RID 推导与 SDK 版本校验逻辑,能让你在遇到问题时不必盲目重装,而是快速定位到具体环节。更底层的构建内部机制(ResGen、Type Catalog、Native 组件打包等)可继续阅读 docs/building/internals.md 作深入了解。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00