首页 / Wails 入门教程 / 手动构建 Manual Builds

Wails 入门教程

手动构建 Manual Builds

本教程共 42 篇 · 第 38 篇 · 更新于 2026-08-03

Wails桌面开发手动构建go build构建流程

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,内部都跑同一条流水线:

  1. 安装前端依赖
  2. 构建前端项目
  3. 生成构建资源
  4. 编译应用
  5. 压缩应用(可选)

下面逐个拆。

阶段一:安装前端依赖

CLI 的逻辑:

  • 给了 -s 就跳过
  • wails.jsonfrontend:install,没配就跳过
  • 检查前端目录有没有 package.json,没有就跳过
  • 算一遍 package.json 的 MD5,跟已存在的 package.json.md5 比对
  • 内容没变、node_modules 也在,就跳过;否则执行安装命令
  • 给了 -f 强制执行

那个 MD5 缓存文件是 CLI 自己维护的,用来避免每次构建都跑一遍 npm install

手动等价:

cd frontend
npm install

阶段二:构建前端项目

CLI 读 wails.jsonfrontend: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 不存在时,用 winiconappicon.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 devwails 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 的任何参数都能立刻知道它在动哪一步。

构建与分发这一部分到这里就完了。下一章开始进入调试与排错,把开发过程中最容易卡住的地方系统过一遍。