XCaddy源码
XCaddy是什么?¶
xcaddy的全称是Custom Caddy Builder。简单来说,XCaddy是一个用于定制化编译Caddy Web Server的命令行构建工具。主要是用于caddy的插件打包进caddy。
用过caddy的应该都知道在下载caddy的时候会让选择caddy的操作系统及需要的插件。然后caddy官方服务器会根据你的选择即是编译,将选择的插件和caddy本体编译到选定的操作系统格式,最终获得一个单一的二进制程序。xcaddy所能作的工作就是作为这个过程的Builder工具。
源码分析¶
输入信息的结构体定义¶
首先定义一个Builder结构体:
type Builder struct {
Compile
CaddyVersion string `json:"caddy_version,omitempty"`
Plugins []Dependency `json:"plugins,omitempty"`
Replacements []Replace `json:"replacements,omitempty"`
TimeoutGet time.Duration `json:"timeout_get,omitempty"`
TimeoutBuild time.Duration `json:"timeout_build,omitempty"`
RaceDetector bool `json:"race_detector,omitempty"`
SkipCleanup bool `json:"skip_cleanup,omitempty"`
}
其中Compile
包含编译参数相关的信息:
type Compile struct {
Platform
Cgo bool `json:"cgo,omitempty"`
}
由此可见主要是平台信息:
type Platform struct {
OS string // 操作系统名称
Arch string // 计算机架构
ARM string // arm版本
}
CaddyVersion
用于指定caddy的版本信息
Plugins
用于指定需要的插件列表:
type Dependency struct {
PackagePath string // 插件路径
Version string // 插件版本
}
`Replacements`用于替换插件路径的path(使用golang的应该知道是什么东西,详细的可以`go rename --help`):
type ReplacementPath string
type Replace struct {
Old ReplacementPath // import中被替换掉的路径
New ReplacementPath // import中用于替换的新路径
}
TimeoutGet
用于指定拉取插件的超时时间
TimeoutBuild
用于指定构建的超时时间
RaceDetector
:用于指定是否开启detector
SkipCleanUp
:用于指定是否cleanup
将上面所有的字段都展开一下,Builder包含下面的配置字段信息:
- OS :操作系统
- Arch :计算机架构
- ARM : arm版本
- Cgo : 是否开启cgo
- CaddyVersion :caddy版本
- Plugins :插件信息列表[(插件1路径,插件1版本),(插件2路径,插件2版本)]
- Replacements :路径替换列表[(old1->new1),(old2->new2)]
- TimeoutGet :插件代码拉取超时时间
- TimeoutBuild: 构建超时时间
- RaceDetector:是否开启Detector
- SkipCleanup:是否跳过CleanUp
这也是构建工作所需要的最多输入配置信息。
可用方法¶
作为构建器的结构体有以下方法:
Build(ctx context.Context, outputFile string) error
- 按照config配置构建生成caddy二进制文件
- ``
完整的逻辑流程¶
xcaddy rake整个流程的代码在xcaddy/cmd/main.go和(b Builder) Build(ctx context.Context, outputFile string) error
。
首先:预先设置Signal的捕获¶
func trapSignals(ctx context.Context, cancel context.CancelFunc) {
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt)
select {
case <-sig:
log.Printf("[INFO] SIGINT: Shutting down")
cancel()
case <-ctx.Done():
return
}
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go trapSignals(ctx, cancel)
在Build开始之前预先设置context,go程运行捕获signal。一个是接收到os.Interrupt 信号,终止程序运行;一种是xcaddy完成build停止程序的运行。
接着:命令行参数解析¶
当满足len(os.Args) > 1 && os.Args[1] == "build"
的时候进行真正的build,若不然进行dev的测试
Build流程¶
- 解析命令行,接收Build参数(主要就是Builder struct的配置字段)
- 根据接收的参数实例化Builder struct
- 执行Build过程
- 通过环境变量填充缺省的Build参数,设置build构建所需环境变量
- 准备Build环境(主要是准备临时文件夹、go mod init ,go mod edit, path路径的设置)
- 执行
go build
- clean up
- 通过打印版本信息验证程序Build成功(只有编译目标是当前平台的时候才验证,跨平台编译的不作此动作)
Dev流程¶
学到了什么¶
xcaddy本身并没用到什么特殊的技术,就是一个简单的构建流程脚本。但如果日后我们需要编写自己的Builder工具,xcaddy仍有可借鉴的技巧:
对于一个常见的Build工具
- 首先定义Builder Struct,清晰看出所需的数据P
- Builder Struct实现绑定的方法
- Builder Struct各部分分解为独立的struct,各部分struct实现各自需要的方法
- 在cli-main中rake整个build流程