手动构建 Manual Builds
本教程共 42 篇 · 第 38 篇 · 更新于 2026-08-03
38. 手动构建 Manual Builds
本节目标
- 说清楚 CLI 构建流水线的五个阶段,每一步 CLI 到底做了什么
- 会用原生
go build加前端命令手动完成一次完整构建 - 记住三个平台的默认编译参数,知道
-tags和-ldflags该怎么写 - 理解 Windows 上
.syso文件的作用和位置要求 - 判断什么场景值得手动构建,什么场景老老实实用 CLI
38-1 什么时候需要手动构建
wails build 已经很省事了,为什么还要手动?几个现实场景:
接入既有构建体系。 公司用 Bazel、Make 或者自研的构建平台,要求所有产物走统一入口。塞一个 wails build 进去有时候会跟已有的缓存、沙箱机制打架。
需要 CLI 没暴露的编译参数。 CLI 会给你追加 -tags desktop,production,追加不了就是追加不了。想完全掌控 go build 的每一个参数,只能自己来。
排查 CLI 行为。 构建结果不对,想知道是 CLI 的哪一步出了问题,手动走一遍最快定位。
理解框架。 这条不功利,但很实在。把流水线拆开看一遍,前面几章那些参数的行为你就不需要背了,能自己推。
Note手动构建不是「更高级的做法」。日常开发用 CLI 效率更高,它帮你处理了图标生成、清单文件、绑定同步这些琐事。手动构建是有明确需求时才动用的手段。
38-2 CLI 的五个阶段
无论 wails build 还是 wails dev,内部都跑同一条流水线:
- 安装前端依赖
- 构建前端项目
- 生成构建资源
- 编译应用
- 压缩应用(可选)
下面逐个拆。
阶段一:安装前端依赖
CLI 的逻辑:
- 给了
-s就跳过 - 读
wails.json的frontend:install,没配就跳过 - 检查前端目录有没有
package.json,没有就跳过 - 算一遍
package.json的 MD5,跟已存在的package.json.md5比对 - 内容没变、
node_modules也在,就跳过;否则执行安装命令 - 给了
-f强制执行
那个 MD5 缓存文件是 CLI 自己维护的,用来避免每次构建都跑一遍 npm install。
手动等价:
cd frontend
npm install
阶段二:构建前端项目
CLI 读 wails.json 的 frontend:build 并在前端目录执行,-s 同样会跳过。
手动等价:
cd frontend
npm run build
产物落在 frontend/dist,Go 侧通过 //go:embed all:frontend/dist 把它嵌进二进制。
Warning
frontend/dist必须存在且非空,否则go:embed会在编译期直接报错。CLI 有-skipembedcreate这个开关,说明它平时会帮你创建缺失的 embed 目录和占位文件。手动构建没人替你做这件事。
阶段三:生成构建资源
-nopackage 会跳过整个阶段。
CLI 做的事:
build/appicon.png不存在就创建一个默认的- Windows 平台:
build/windows/icon.ico不存在时,用 winicon 从appicon.png生成,尺寸覆盖 256、128、64、48、32、16 - Windows 平台:
build/windows/<项目名>.manifest不存在时创建一份默认清单 - Windows 平台:用 winres 把图标和清单打包成
.syso文件,供链接器使用
手动等价(只有 Windows 需要):
# 生成 ico
go install github.com/leaanthony/winicon/cmd/winicon@latest
winicon -o build/windows/icon.ico -i build/appicon.png
# 生成 syso
go install github.com/tc-hib/go-winres@latest
go-winres make --in build/windows/winres.json
Warning
.syso文件必须跟main.go在同一个目录下。Go 的链接器会自动把当前包目录里的.syso链进去,放错位置就是静默失效——编译不报错,但图标和清单都没了。
macOS 和 Linux 不需要 .syso。macOS 的图标是在打包 .app 时放进 Contents/Resources 的,Linux 的图标由 .desktop 文件引用系统图标目录。
阶段四:编译应用
这是核心。CLI 的行为:
- 给了
-clean就删掉 build 目录重建 wails dev用的默认参数:-tags dev -gcflags "all=-N -l"wails build用的默认参数:-tags desktop,production -ldflags "-w -s"- Windows 上 ldflags 变成
-w -s -H windowsgui - 你通过
-tags、-ldflags传的值追加在默认值后面 -o原样传给go build-compiler指定的编译器替换掉go
手动等价,生产构建:
# Linux / macOS
go build -tags desktop,production -ldflags "-w -s" -o build/bin/myapp
# Windows
go build -tags desktop,production -ldflags "-w -s -H windowsgui" -o build/bin/myapp.exe
开发构建:
go build -tags dev -gcflags "all=-N -l" -o build/bin/myapp-dev
几个参数的含义:
desktop,production这两个 tag 决定 Wails 走桌面模式和生产资源加载路径,漏了会得到一个行为古怪的程序-w -s去掉 DWARF 调试信息和符号表,体积能小一大截-H windowsgui让 Windows 程序不带控制台窗口-gcflags "all=-N -l"关闭优化和内联,方便断点调试
Linux 上别忘了 WebKit 的 ABI 标签:
go build -tags desktop,production,webkit2_41 -ldflags "-w -s" -o build/bin/myapp
阶段五:压缩应用
CLI 在给了 -upx 时调用 UPX,-upxflags 可以替换默认参数。
手动等价:
upx --best build/bin/myapp
第 33 章提过的两个坑照旧:Windows 杀软误报、Apple Silicon 兼容性问题。
38-3 一份完整的手动构建脚本
把五步串起来。以 Linux 为例:
#!/bin/bash
set -e
# 1. 安装前端依赖
cd frontend
npm install
# 2. 构建前端
npm run build
cd ..
# 3. Linux 无需生成 syso,跳过
# 4. 编译
go build -tags desktop,production,webkit2_41 \
-ldflags "-w -s" \
-trimpath \
-o build/bin/myapp
# 5. 压缩(可选)
# upx --best build/bin/myapp
echo "构建完成:build/bin/myapp"
Windows 版本的差异在第 3 步和编译参数:
#!/bin/bash
set -e
cd frontend && npm install && npm run build && cd ..
go-winres make --in build/windows/winres.json
go build -tags desktop,production \
-ldflags "-w -s -H windowsgui" \
-trimpath \
-o build/bin/myapp.exe
写成 Makefile 会更顺手:
.PHONY: frontend build clean
frontend:
cd frontend && npm install && npm run build
build: frontend
go build -tags desktop,production -ldflags "-w -s" -trimpath -o build/bin/myapp
clean:
rm -rf build/bin frontend/dist
38-4 绑定文件怎么办
手动构建有个 CLI 帮你做、go build 不会做的事:生成 wailsjs 目录下的绑定代码和 TypeScript 类型。
正常用 wails dev 或 wails build 时这一步是自动的,Go 代码一改,前端的函数签名和 models 就跟着更新。纯手动流程里没人触发它,前端就会用着旧的绑定。
补救办法是单独跑一次生成命令:
wails generate module
Note这条命令在 v2 的常规流程里用不到——CLI 会自动生成。只有在完全脱离 CLI 构建时,才需要手动补这一步。别把它当成日常开发的必要环节。
所以「手动构建」在实践中往往是半手动:绑定生成还是借 CLI 的力,编译环节自己控制。完全脱离 CLI 意味着你要自己维护绑定的同步,成本不低。
38-5 macOS 的 .app 组装
Linux 和 Windows 手动构建产出的就是最终产物,macOS 不一样——go build 只给你一个可执行文件,.app 包需要自己组装。
目录结构照第 35 章那份来搭:
APP=build/bin/MyApp.app
mkdir -p "$APP/Contents/MacOS"
mkdir -p "$APP/Contents/Resources"
go build -tags desktop,production -ldflags "-w -s" -o "$APP/Contents/MacOS/MyApp"
cp build/darwin/Info.plist "$APP/Contents/Info.plist"
cp build/darwin/iconfile.icns "$APP/Contents/Resources/iconfile.icns"
Info.plist 里的 CFBundleExecutable 要跟 Contents/MacOS/ 下的文件名一致,对不上系统就找不到入口。
组装完再走签名、公证流程,跟 CLI 构建的产物没有区别。
常见误区
漏了 -tags desktop,production。 编出来的程序可能能启动,但资源加载路径不对,表现成白屏或者行为异常。这是手动构建最常见的错误。
.syso 放错目录。 不在 main.go 同级目录,链接器不会捡它,图标和清单静默丢失。
frontend/dist 是空的就直接编译。 go:embed 编译期报错,信息还挺绕。先确认前端构建成功。
以为手动构建能绕过交叉编译限制。 不能。CGO 的约束在 go build 这一层,换个入口一样过不去。
手动流程里忘了同步绑定。 Go 方法改了签名,前端还调旧的,运行时报错。
小结
CLI 的构建流水线就五步:装依赖、编前端、生成资源、编译、压缩。每一步都有对应的手动命令,没有黑魔法。
编译参数记住这两组就够了:生产用 -tags desktop,production -ldflags "-w -s",Windows 上 ldflags 多一个 -H windowsgui;开发用 -tags dev -gcflags "all=-N -l"。
Windows 的 .syso、macOS 的 .app 组装,是手动构建里最容易出岔子的两处平台差异。
理解这条流水线的最大收益,是你以后看 wails build 的任何参数都能立刻知道它在动哪一步。
构建与分发这一部分到这里就完了。下一章开始进入调试与排错,把开发过程中最容易卡住的地方系统过一遍。