第一章:Mac配置Go开发环境为什么总失败?揭秘系统Shell差异、Xcode Command Line Tools依赖与Go Modules初始化三大断点
Mac上配置Go开发环境频频失败,往往并非Go本身问题,而是被三个隐蔽断点卡住:默认Shell切换导致环境变量失效、Xcode Command Line Tools缺失引发构建链断裂、以及Go Modules在非模块路径下静默降级为GOPATH模式。
Shell差异导致GOBIN与PATH失效
macOS Catalina及后续版本默认使用zsh,但许多教程仍按bash习惯修改~/.bash_profile。若未同步更新~/.zshrc,go install生成的二进制文件将无法全局调用:
# ✅ 正确做法:统一写入zsh配置(检查当前shell:echo $SHELL)
echo 'export GOPATH=$HOME/go' >> ~/.zshrc
echo 'export PATH=$PATH:$GOPATH/bin' >> ~/.zshrc
source ~/.zshrc # 立即生效,无需重启终端
Xcode Command Line Tools是底层编译基石
Go标准库中net, os/user等包依赖系统C工具链。未安装时,go build可能报错xcrun: error: invalid active developer path:
# ✅ 必须执行(即使已装Xcode,也需显式安装CLT)
xcode-select --install # 弹窗安装,完成后验证:
xcode-select -p # 应输出 /Library/Developer/CommandLineTools
Go Modules初始化时机错误
在未初始化模块的目录中运行go run main.go,Go会自动回退到GOPATH模式,导致go mod tidy无响应或依赖解析异常: |
场景 | 行为 | 解决方案 |
|---|---|---|---|
当前目录无go.mod且不在$GOPATH/src下 |
静默启用module-aware模式但不创建go.mod |
手动初始化:go mod init example.com/myapp |
|
当前目录有go.mod但GO111MODULE=off |
强制禁用Modules | 永久启用:go env -w GO111MODULE=on |
务必在项目根目录执行go mod init后再添加依赖,否则go get不会写入go.mod——这是最常被忽略的“假成功”陷阱。
第二章:深度解析macOS系统Shell差异对Go环境配置的隐性影响
2.1 理清zsh与bash历史演进及默认Shell切换机制
起源与定位差异
- Bash(1989):GNU项目核心Shell,POSIX兼容性强,Linux发行版长期默认;
- Zsh(1990):扩展Bash功能,强化补全、主题、插件生态(如Oh My Zsh),但非POSIX严格子集。
默认Shell切换机制
系统级切换依赖chsh命令与/etc/shells白名单:
# 查看合法Shell列表
cat /etc/shells
# 输出示例:
# /bin/bash
# /bin/zsh # 必须存在才可设为默认
chsh -s /bin/zsh会验证目标路径是否在/etc/shells中,否则报错“shell not listed in /etc/shells”。
Shell演进关键节点
| 版本 | Bash | Zsh |
|---|---|---|
| 初始发布 | 1.04 (1989) | 1.0 (1990) |
| 补全能力 | 基础文件/命令 | 上下文感知+插件驱动 |
| 配置加载 | ~/.bashrc |
~/.zshrc + ~/.zprofile |
graph TD
A[用户登录] --> B{读取/etc/passwd中的shell字段}
B --> C[/bin/bash]
B --> D[/bin/zsh]
C --> E[加载~/.bashrc]
D --> F[加载~/.zshenv → ~/.zprofile → ~/.zshrc]
2.2 PATH环境变量在不同Shell中的加载顺序与profile文件链式调用实践
不同 Shell 启动时读取的初始化文件存在显著差异,直接影响 PATH 的最终构成。
Shell 类型与配置文件加载路径
- Login Shell(如 SSH 登录):依次读取
/etc/profile→~/.bash_profile→~/.bash_login→~/.profile - Non-login Interactive Shell(如终端中执行
bash):仅读取~/.bashrc - Zsh:默认加载
~/.zshenv→~/.zprofile→~/.zshrc
典型链式追加示例
# ~/.bash_profile 中常见写法(避免重复追加)
if [ -f ~/.bashrc ]; then
source ~/.bashrc # 显式触发 .bashrc 加载
fi
export PATH="$HOME/bin:$PATH" # 优先级最高
此处
source ~/.bashrc确保交互式配置复用;$HOME/bin插入PATH前端,使本地二进制优先于系统命令。
各 Shell 初始化文件优先级对比
| Shell | Login 读取顺序 | PATH 最终生效位置 |
|---|---|---|
| bash | /etc/profile → ~/.bash_profile |
~/.bash_profile 末行 |
| zsh | /etc/zshenv → ~/.zprofile |
~/.zprofile 或 ~/.zshrc |
graph TD
A[Shell 启动] --> B{Login?}
B -->|Yes| C[/etc/profile]
C --> D[~/.bash_profile]
D --> E[~/.bashrc]
B -->|No| F[~/.bashrc]
2.3 Go二进制路径在交互式/非交互式Shell下的可见性验证与修复方案
Go 工具链生成的二进制(如 go install 安装的命令)常因 $PATH 加载时机差异,在非交互式 Shell(如 CI 脚本、systemd 服务、SSH 命令执行)中不可见。
验证路径可见性差异
# 交互式 Shell 中生效(通常已 source ~/.bashrc)
echo $PATH | grep -o '/home/user/go/bin'
# 非交互式 Shell 中失效(未加载 shell 初始化文件)
ssh localhost 'echo $PATH | grep -o "/home/user/go/bin"' # 输出为空
该命令直接对比两种 Shell 模式下 $PATH 是否包含 Go 的 bin 目录;ssh 默认启动非登录非交互式 Shell,跳过 ~/.bashrc,导致路径缺失。
典型修复方式对比
| 方案 | 适用场景 | 是否持久 | 风险 |
|---|---|---|---|
修改 /etc/environment |
系统级服务、所有用户 | ✅ | 仅支持 KEY=VALUE,不支持变量扩展 |
在 systemd service 中显式设置 Environment=PATH=... |
服务部署 | ✅ | 需手动同步 Go 路径变更 |
使用 bash -i -c 'your-go-cmd' |
临时调试 | ❌ | 性能开销,不推荐生产 |
推荐修复流程
graph TD
A[检测当前 Shell 类型] --> B{是否为 non-interactive?}
B -->|是| C[检查 /etc/environment 或 service Environment]
B -->|否| D[确认 ~/.bashrc 是否 export GOPATH/bin]
C --> E[追加 /home/user/go/bin 到 PATH]
核心原则:非交互式环境必须显式声明路径,不可依赖 shell 初始化文件自动加载。
2.4 终端应用(Terminal/iTerm2)与IDE(VS Code/GoLand)启动Shell模式差异实测
启动Shell类型对比
| 环境 | 默认Shell进程 | 是否加载~/.zshrc |
SHELL环境变量值 |
|---|---|---|---|
| macOS Terminal | login shell (-zsh) |
✅ | /bin/zsh |
| iTerm2 | 可配为 login/non-login | ⚠️(需勾选“Login shell”) | 依赖配置 |
| VS Code 集成终端 | non-login shell | ❌(仅执行$SHELL) |
/bin/zsh(但不读rc) |
| GoLand 终端 | non-login shell | ❌ | /usr/bin/zsh(同上) |
环境变量加载验证命令
# 检查是否为login shell
shopt -q login_shell && echo "login" || echo "non-login"
# 输出:Terminal/iTerm2(登录模式) → "login";VS Code → "non-login"
shopt -q login_shell查询bash/zsh内置标志位;-q静默判断,返回0表示true。该标志决定/etc/zprofile→~/.zprofile→~/.zshrc链式加载行为。
Shell初始化路径差异
graph TD
A[启动终端] --> B{是否login shell?}
B -->|是| C[/etc/zprofile → ~/.zprofile → ~/.zshrc]
B -->|否| D[$SHELL直接执行 → 仅source ~/.zshenv]
2.5 Shell配置冲突诊断:go env输出异常与shellcheck静态分析联动排查
当 go env 输出中 GOROOT 或 GOPATH 显示为空、路径错误或与 ~/.bashrc 中设置不一致时,往往源于 shell 初始化脚本加载顺序混乱。
常见冲突根源
.bashrc与.bash_profile相互覆盖export语句被条件分支意外跳过- 多层
source引入重复/矛盾定义
联动诊断流程
# 检查实际生效的 GOPATH(绕过 alias/函数干扰)
\go env GOPATH # 反斜杠跳过 shell 函数封装
\go强制调用二进制而非可能被重定义的函数;避免go()函数内部误设环境变量导致go env假阳性。
静态检查协同验证
| 工具 | 检查项 | 触发场景 |
|---|---|---|
shellcheck |
SC2155(未用 local 声明) |
函数内 GOPATH= 覆盖全局变量 |
go env |
GOCACHE 路径不存在 |
export 后目录未创建 |
graph TD
A[go env 输出异常] --> B{是否在交互式 shell 中?}
B -->|是| C[运行 shellcheck -s bash ~/.bashrc]
B -->|否| D[检查 /etc/profile.d/*.sh 加载顺序]
C --> E[定位未加 local 的 export]
第三章:Xcode Command Line Tools——被忽视的Go编译基础设施依赖
3.1 Go源码构建与cgo启用条件下对clang、ar、ranlib等底层工具链的真实依赖图谱
当 CGO_ENABLED=1 时,Go 构建流程不再仅依赖 go tool compile/link,而是动态调度外部工具链:
工具链调用触发条件
clang:编译.c/.cc文件(含#include "C"的 Go 源)ar:归档 C 静态库(如libfoo.a)供链接器消费ranlib:为 macOS 上的静态库生成符号索引(ar不自动完成)
典型构建链路(mermaid)
graph TD
A[go build -v] --> B{cgo_enabled?}
B -->|yes| C[clang -x c -fPIC -o _cgo_main.o]
C --> D[ar rcs lib_cgo_lib.a _cgo_main.o]
D --> E[ranlib lib_cgo_lib.a]
E --> F[go tool link -extld=clang ...]
关键环境变量影响
| 变量 | 默认值 | 作用 |
|---|---|---|
CC |
clang (macOS) / gcc (Linux) |
指定 C 编译器 |
AR |
ar |
归档工具,必须支持 -r, -c, -s |
RANLIB |
ranlib (macOS) / 空 (Linux) |
符号表重建 |
# 示例:显式指定 macOS 工具链
CC=clang AR=ar RANLIB=ranlib go build -ldflags="-extld=clang"
该命令强制 go build 调用 clang 作为链接器,并确保 ranlib 介入静态库符号索引——缺失 RANLIB 将导致 macOS 下 undefined symbol 错误。
3.2 xcode-select –install静默失败的六种典型场景及对应权限/网络/证书根因分析
常见静默失败场景归类
- 系统完整性保护(SIP)拦截
/usr/bin/xcode-select符号链接重写 - macOS 14+ 默认禁用未签名命令行工具安装通道(需显式授权)
- 企业环境强制代理拦截
https://developer.apple.com/download/more/重定向 /Library/Developer/CommandLineTools目录存在但权限为root:wheel 700,非管理员用户无法写入临时包- Apple Root CA 证书过期(如
DST Root CA X3已失效),导致 HTTPS 握手失败 softwareupdate后台服务被launchctl disable system/com.apple.softwareupdate禁用
权限与证书诊断代码
# 检查证书信任链是否完整(关键诊断步骤)
security verify-cert -p ssl -l "https://developer.apple.com"
# 输出含 'Certificate chain verification failed' 即表明根证书缺失或过期
该命令调用系统 Keychain Services API,参数
-p ssl指定 SSL 策略,-l执行远程验证;失败时返回非零码且无交互提示,构成“静默失败”的典型表征。
| 场景编号 | 根因类型 | 触发条件 |
|---|---|---|
| #3 | 网络 | 代理返回 302 但未携带 Location 头 |
| #5 | 证书 | /System/Roots/ 中缺失 Apple Root CA 2023 |
3.3 非标准安装路径(如自定义Xcode.app位置)下CLT符号链接修复与GOOS=darwin交叉编译兼容性验证
当 Xcode.app 被移至 /Applications/Xcode-15.3.app 等非默认路径时,Command Line Tools(CLT)的符号链接常失效,导致 go build -o main -ldflags="-s -w" -buildmode=exe 在 GOOS=darwin 下静默链接失败。
修复 CLT 符号链接
# 查找真实 CLT 路径(需先运行 xcode-select --install)
sudo xcode-select --switch /Applications/Xcode-15.3.app/Contents/Developer
# 验证是否生效
xcode-select -p # 应输出 /Applications/Xcode-15.3.app/Contents/Developer
该命令强制重置 xcrun 工具链根路径,使 clang、ld 等工具可被 Go 的 cgo 构建流程正确发现;--switch 参数不依赖 /usr/bin/xcode-select 的硬编码路径假设。
兼容性验证要点
| 检查项 | 命令 | 预期输出 |
|---|---|---|
| CLT 可用性 | xcrun --find clang |
/Applications/Xcode-15.3.app/.../clang |
| Go 交叉链接能力 | CGO_ENABLED=1 GOOS=darwin go build -o test-darwin . |
无错误,生成 Mach-O 二进制 |
构建链依赖关系
graph TD
A[GOOS=darwin] --> B[CGO_ENABLED=1]
B --> C[xcrun --find ld]
C --> D[/Applications/Xcode-15.3.app/.../ld]
D --> E[Mach-O linkage success]
第四章:Go Modules初始化阶段的高频断点与工程化规避策略
4.1 GOPROXY配置失效的四层拦截:系统代理、shell环境变量、git全局配置、Go内部HTTP客户端绕过逻辑
Go模块下载看似简单,实则在四层机制下可能悄然绕过 GOPROXY 设置:
四层拦截优先级(由高到低)
- Go 内部 HTTP 客户端对
*.go域名的硬编码直连逻辑 - Git 全局配置中
http.<url>.proxy覆盖 HTTP 代理行为 - Shell 环境变量
HTTPS_PROXY/NO_PROXY的透传影响 - 系统级代理(如 macOS Network Preferences 或 Windows 代理策略)
关键绕过逻辑示例
# 当 NO_PROXY 包含 "proxy.golang.org" 时,即使 GOPROXY=xxx,Go 仍直连
export NO_PROXY="localhost,127.0.0.1,proxy.golang.org"
此设置触发 Go 源码中
net/http/transport.go的shouldBypassProxy判断,跳过代理链,直接 DNS 解析模块域名(如golang.org/x/net),导致GOPROXY形同虚设。
绕过路径示意(mermaid)
graph TD
A[go get golang.org/x/net] --> B{Go HTTP Client}
B --> C[检查 NO_PROXY 匹配]
C -->|匹配成功| D[直连 golang.org]
C -->|不匹配| E[走 GOPROXY]
| 层级 | 配置位置 | 是否可被 GOPROXY 覆盖 |
|---|---|---|
| Go 内部逻辑 | src/net/http/transport.go |
❌ 不可覆盖 |
| Git 配置 | git config --global http.https://goproxy.io.proxy |
✅ 但会劫持 HTTPS 请求 |
4.2 go mod init时module path推导错误与go.work多模块工作区初始化冲突的协同调试
当在嵌套目录中执行 go mod init 而未显式指定 module path 时,Go 会基于当前路径推导(如 ~/proj/api → api),易与 go.work 中已声明的模块路径(如 example.com/backend)产生命名冲突。
常见冲突场景
go.mod的module example.com/api与go.work中use ./api路径不一致go.work初始化后,子模块go mod init忽略工作区上下文,生成孤立 module path
冲突诊断流程
# 检查当前推导结果(无参数时)
go mod init
# 输出:go: creating new go.mod: module <basename>
此命令默认取目录名作 module path,不感知 go.work。若工作区已
use ./auth,但auth/下执行go mod init得到module auth,则导入路径import "auth"将无法被主模块解析。
推荐协同初始化步骤
- 先在工作区根目录创建
go.work:go work init - 显式初始化各模块:
go work use ./service ./auth - 进入子目录后,必须带参数:
go mod init example.com/service
| 错误操作 | 后果 |
|---|---|
go mod init(无参) |
module path 为 service |
go work use ./service |
期望 example.com/service |
graph TD
A[执行 go mod init] --> B{是否在 go.work 管理路径内?}
B -->|否| C[纯本地推导:目录名→module]
B -->|是| D[仍忽略工作区,需显式指定]
4.3 CGO_ENABLED=0与CGO_ENABLED=1双模式下vendor目录生成行为差异及vendor一致性校验脚本编写
Go 构建时 CGO_ENABLED 状态直接影响 go mod vendor 对依赖的解析路径与符号链接处理逻辑。
行为差异核心表现
CGO_ENABLED=1:保留含 C 代码的模块(如net,os/user),可能引入系统级依赖,vendor/中包含完整cgo相关构建文件(如.h,.c,CFLAGS);CGO_ENABLED=0:强制纯 Go 模式,跳过所有 cgo 依赖分支,vendor/仅含纯 Go 实现(如net的poll/fd_poll_runtime.go替代fd_unix.go),体积更小、跨平台性更强。
| 场景 | vendor 是否包含 runtime/cgo |
是否包含 net 的 cgo_linux.go |
vendor 大小趋势 |
|---|---|---|---|
CGO_ENABLED=1 |
✅ | ✅ | 较大 |
CGO_ENABLED=0 |
❌ | ❌(使用 net 纯 Go fallback) |
较小 |
一致性校验脚本(核心逻辑)
#!/bin/bash
# 校验 vendor 在双模式下是否语义等价(忽略 cgo 相关文件)
CGO0_HASH=$(find vendor -name "*.go" ! -path "*/cgo/*" -type f | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)
CGO1_HASH=$(find vendor -name "*.go" ! -path "*/cgo/*" -type f | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)
[ "$CGO0_HASH" = "$CGO1_HASH" ] && echo "✅ vendor Go 源码一致" || echo "❌ vendor Go 源码存在差异"
该脚本剔除
cgo目录后对所有.go文件做排序哈希比对,确保纯 Go 部分在双模式下完全一致。关键参数:! -path "*/cgo/*"排除 cgo 专用文件;sort | xargs sha256sum保证哈希顺序无关性。
graph TD
A[执行 go mod vendor] --> B{CGO_ENABLED=1?}
B -->|是| C[包含 cgo 目录与系统适配文件]
B -->|否| D[仅保留纯 Go 实现路径]
C & D --> E[校验脚本过滤 cgo 后比对 Go 源哈希]
4.4 Go 1.21+中GODEBUG=gocacheverify=1与GONOSUMDB组合使用引发的私有模块拉取中断复现与安全策略平衡方案
当 GODEBUG=gocacheverify=1 启用时,Go 构建器强制校验本地模块缓存($GOCACHE)中 .mod 文件的 checksum 是否匹配 sum.golang.org 签名;而 GONOSUMDB=*.corp.example.com 会跳过私有域名的校验——但不跳过 gocacheverify 的本地缓存一致性检查。
复现关键路径
# 在私有模块已缓存后启用双标志
GODEBUG=gocacheverify=1 GONOSUMDB="*.internal" go build ./cmd/app
🔍 逻辑分析:
gocacheverify=1读取$GOCACHE/vcs/.../info中缓存的sumdb签名哈希,若私有模块从未经 sumdb 记录(必然),则校验失败并清空缓存,触发go mod download重拉——但GONOSUMDB又禁止向 sumdb 查询,形成死锁式中断。
安全策略平衡建议
- ✅ 优先使用
GOPRIVATE=*.corp.example.com替代GONOSUMDB(保留校验能力) - ✅ 配合私有
sumdb镜像或go.sum预置机制 - ❌ 避免
gocacheverify=1与GONOSUMDB共存于 CI/CD 流水线
| 方案 | 校验强度 | 私有模块兼容性 | 运维复杂度 |
|---|---|---|---|
GONOSUMDB + gocacheverify=1 |
⚠️ 中断 | ❌ 不可用 | 低 |
GOPRIVATE + gocacheverify=1 |
✅ 强校验 | ✅ 支持 | 中 |
GOPROXY=direct + GOSUMDB=off |
❌ 无校验 | ✅ 支持 | 低 |
graph TD
A[go build] --> B{GODEBUG=gocacheverify=1?}
B -->|Yes| C[读取缓存sum签名]
C --> D{签名来自sum.golang.org?}
D -->|No| E[校验失败→清缓存→重拉]
E --> F{GONOSUMDB匹配该模块?}
F -->|Yes| G[拒绝查询sumdb→拉取中断]
第五章:构建稳定、可复现、团队协同的Mac Go开发环境黄金标准
统一版本管理:goenv + GOTOOLCHAIN 双轨控制
在团队中,我们强制要求所有成员通过 goenv 管理 Go 主版本(如 1.21.13, 1.22.7),同时利用 Go 1.21+ 原生支持的 GOTOOLCHAIN=go1.22.7 环境变量实现项目级工具链锁定。CI 流水线中插入校验步骤:
# 验证本地环境与 go.mod 中 toolchain 声明一致
if [[ "$(go version)" != *"go1.22.7"* ]] || [[ "$GOTOOLCHAIN" != "go1.22.7" ]]; then
echo "❌ Go version mismatch: expected go1.22.7" >&2
exit 1
fi
依赖治理:go mod vendor + sum.golang.org 离线镜像
所有项目启用 GOVCS=gitlab.com:ssh;github.com:https 并配置私有 sum.golang.org 镜像服务(基于 goproxy.io 定制)。Makefile 中固化 vendor 流程:
| 步骤 | 命令 | 说明 |
|---|---|---|
| 初始化 | make vendor-init |
拉取 go.sum 所有 checksum,校验并缓存至内部 blob 存储 |
| 更新 | make vendor-update PKG=github.com/gorilla/mux@v1.8.0 |
自动重写 go.mod、更新 vendor/、提交 go.sum 差异 |
| 校验 | make vendor-verify |
对比 vendor/modules.txt 与 go list -m -f '{{.Path}} {{.Version}}' all 输出 |
开发容器化:devcontainer.json 标准模板
团队共享 .devcontainer/devcontainer.json,预装 gopls v0.14.3, staticcheck v0.4.0, gofumpt v0.5.0,并挂载加密的 ~/.netrc(用于私有模块认证):
{
"image": "ghcr.io/org/go-dev:mac-m1-v2",
"features": {
"ghcr.io/devcontainers/features/go:1": { "version": "1.22.7" }
},
"customizations": {
"vscode": {
"extensions": ["golang.go", "ms-azuretools.vscode-docker"]
}
}
}
构建可复现性:reproducible-builds.sh 脚本
该脚本注入确定性构建参数,禁用时间戳与路径泄漏:
#!/bin/bash
export CGO_ENABLED=0
export GOOS=darwin && export GOARCH=arm64
export GODEBUG="mmap=1"
go build -trimpath -ldflags="-s -w -buildid=" -o ./dist/app ./cmd/app
团队协作规范:.golangci.yml 全局策略
所有仓库继承组织级 linter 配置,禁止 golint(已废弃),强制启用 govulncheck 和 errcheck:
linters-settings:
errcheck:
check-type-assertions: true
check-blank: false
govulncheck:
mode: binary
timeout: 300s
环境健康检查:check-env.sh 自动诊断
运行时检测 Xcode Command Line Tools 版本、Homebrew 包完整性、Go proxy 设置有效性,并生成 HTML 报告:
flowchart TD
A[check-env.sh] --> B{Xcode CLT ≥ 14.3?}
B -->|Yes| C[Homebrew doctor]
B -->|No| D[Abort with install link]
C --> E{All golang-* casks installed?}
E -->|Yes| F[Run go env -json]
E -->|No| G[Auto-install missing tools]
F --> H[Generate report.html]
本地开发代理:mitmproxy 拦截模块请求
为调试私有模块拉取失败问题,开发者可一键启动拦截代理:
mitmdump --mode reverse:https://proxy.golang.org \
--set block_global=false \
--set console_eventlog_verbosity=debug \
-s ./scripts/log-go-requests.py
该脚本记录每次 GET /@v/list 请求及响应体,自动归档至 ~/go-debug/logs/2024-06-15/。
IDE 配置同步:VS Code Settings Sync
通过 GitHub Gist ID 同步以下关键设置:
"go.toolsManagement.autoUpdate": true"go.gopath": "/Users/${USER}/go""gopls": {"build.experimentalWorkspaceModule": true}
所有新成员首次启动 VS Code 即自动加载统一配置,无需手动调整。
CI/CD 流水线验证矩阵
GitHub Actions 使用 macos-14 runner,交叉验证四类环境组合:
| OS Version | Go Version | Build Mode | Target |
|---|---|---|---|
| macOS 14.5 | 1.21.13 | go build |
arm64 |
| macOS 14.5 | 1.22.7 | go build -trimpath |
amd64 |
| macOS 13.6 | 1.21.13 | go test -race |
arm64 |
| macOS 14.5 | 1.22.7 | govulncheck ./... |
— |
