一键打包利器:gopack - 极简Go程序编译与压缩工具
一键打包利器:gopack - 极简Go程序编译与压缩工具
在Go项目开发中,频繁的编译、打包和压缩操作常常打断我们的开发流。为了解决这个问题,我写了一个小工具——一个极简的Go程序打包工具,它能自动完成编译、时间戳注入和二进制压缩等操作。
功能亮点
-
智能编译
- 自动添加构建时间戳(ISO8601格式)
- 使用
-trimpath
移除绝对路径 - 默认添加
-ldflags="-s -w"
减小体积
-
自动命名
- 输入
main.go
→ 输出main.exe
- 支持自定义输出文件名
- 输入
-
UPX智能压缩
- 自动检测系统UPX工具
- 使用
--best
最高压缩级别 - 未安装时友好跳过
-
操作简化
- 单命令完成全流程
- 内置帮助文档(
--help
)
使用示例
基本用法:
gopack main.go
# 输出: main.exe (带构建时间戳)
自定义输出名:
gopack main.go myapp.exe
# 输出: myapp.exe
查看帮助:
gopack --help
技术实现解析
核心流程分为四步:
- 参数处理
if len(os.Args) < 2 {fmt.Println("用法: gopack <go文件> [输出文件]")os.Exit(1)
}
- 编译注入
ldflags := fmt.Sprintf("-s -w -X main.buildTime=%s", timestamp)
cmd := exec.Command("go", "build", "-ldflags", ldflags, ...)
- UPX检测
upxPath, err := exec.LookPath("upx") // 智能检测
if err != nil {fmt.Println("UPX未找到,跳过压缩")
} else {exec.Command(upxPath, "--best", outputFile)
}
- 构建时间戳
timestamp := time.Now().Format("2006-01-02T15:04:05Z")
// 格式示例: 2025-06-22T14:30:00Z
在代码中使用构建时间:
var buildTime stringfunc main() {fmt.Printf("构建于: %s\n", buildTime)
}
结合Makefile:
build:gopack -o dist/myapp cmd/main.go
附录
完整代码
package mainimport ("flag""fmt""log""os""os/exec""path/filepath""time"
)func usage() {fmt.Println("Usage: gopack [PATH] [OPTIONS] ")fmt.Println("\nOptions:")fmt.Println(" --move move to bin dir")fmt.Println(" --help Display this help message")fmt.Println("\nExample:")fmt.Println(" gopack main.go main.exe")
}func main() {help := flag.Bool("help", false, "Display this help message")flag.Parse()if *help {usage()os.Exit(0)}if len(os.Args) < 2 {fmt.Println("用法: gopack <go文件> [输出文件]")os.Exit(1)}goFile := os.Args[1]if _, err := os.Stat(goFile); os.IsNotExist(err) {log.Fatalf("文件 %s 不存在", goFile)}// 判断是否传入了 outputFile 参数var outputFile stringif len(os.Args) > 2 {outputFile = os.Args[2]} else {// 获取输出文件名,默认为去掉 .go 后缀并加上 .exebaseName := filepath.Base(goFile)outputFile = filepath.Join(".", baseName[:len(baseName)-3]+".exe")}// 获取当前时间戳timestamp := time.Now().Format("2006-01-02T15:04:05Z")// 设置 Build Infoldflags := fmt.Sprintf("-s -w -X main.buildTime=%s", timestamp)// 编译 Go 程序compileCmd := exec.Command("go", "build", "-ldflags", ldflags, "-trimpath", "-o", outputFile, goFile)compileCmd.Stdout = os.StdoutcompileCmd.Stderr = os.Stderrif err := compileCmd.Run(); err != nil {log.Fatalf("编译 Go 程序失败: %v", err)}// 检查 UPX 是否存在upxPath, err := exec.LookPath("upx")if err != nil {fmt.Println("UPX 未找到,跳过压缩步骤")} else {// 使用 UPX 压缩二进制文件upxCmd := exec.Command(upxPath, "--best", outputFile)upxCmd.Stdout = os.StdoutupxCmd.Stderr = os.Stderrif err := upxCmd.Run(); err != nil {log.Fatalf("使用 UPX 压缩二进制文件失败: %v", err)}}fmt.Printf("打包和压缩 %s 成功\n", outputFile)
}