1、编译
编译并不仅仅是执行“go build” 命令,还有一些须额外注意的内容。
如习惯使用 GDB 这类调试器,建议编译时添加 -gcflags “-N -l” 参数阻止优化和内联,否则调试时会有各种“找不到”的情况。
package main
func test(x *int) {
println(*x)
}
func main() {
x := 0x100
test(&x)
}
go build -gcflags “-N -l -m”
输出:
./dataTest.go:3:11: test x does not escape
./dataTest.go:9:7: main &x does not escape
而当发布时,参数 -ldflags “-w -s” 会让链接器剔除符号表和调试信息,除能减少可执行文件大小外,还可稍微增加反汇编的难度。
go build -gcflags “-m” -ldflags “-w -s”
输出:
./dataTest.go:3:6: can inline test
./dataTest.go:7:6: can inline main
./dataTest.go:9:6: inlining call to test
./dataTest.go:3:11: test x does not escape
./dataTest.go:9:7: main &x does not escape
交叉编译
所谓交叉编译,是指在一个平台下编译出其他平台所需的可执行文件。这对于 UNIX-like 开发人员很重要,因为我们习惯使用 Mac 或 其他桌面环境。
自从 Go 实现自举后,交叉编译变得很重要。只续使用 GOOS、GOPATH 环境变量指定目标平台和架构就行。
go env GOOS
输出:
darwin
go build && file dataTest
输出: dataTest: Mach-O 64-bit executable x86_64
GOOS=linux go build && file dataTest
输出:ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
GOOS=windows GOARCH=386 go build && file dataTest.exe
输出:PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows
建议用 go install 命令为目标平台预编译后标准库,避免 go build 每次都须完整编译。
GOOS=linux go install std
GOOS=linux go install cmd
注意: 交叉编译不支持 CGO.
条件编译
除在代码中用 runtime.GOOS 进行判断外,编译器本身就支持文件级别的条件编译。
方法1:将平台和架构信息添加到主文件名尾部。
main.go
package main
func main() {
hello()
}
hello_darwin.go
package main
func hello() {
println("hello mac.")
}
hello_linux.go
package main
func hello() {
println("hello linux.")
}
GOOS=darwin go build -x
./main
输出: hello mac.
GOOS=linux go build -x
./main
输出: hello linux.
方法2:使用 build 编译指令。
与用文件名区分多版本类似,build 编译指令告知编译器:当前源码文件只能用于指定环境。它一样可用来区分多版本,切控制指令更加丰富和灵活。
a.go
// +build windows <------- 其后面必须有空格
package main
func hello() {
println("hello windows.")
}
b.go
// +build linux darwin
package main
func hello() {
println("hello, unix.")
}
可添加多条build指令,表示多个 AND 条件。在单一指令里,空格表示OR条件,逗号表示AND,感叹号表示 NOT。
// +build linux darwin
// +build 386,!cgo
其相当于:
(linux OR darwin) AND (386 AND (NOT cgo))
除 GOOS、GOARCH 外,可用条件还有编译器、版本号等。
// +build ignore
// +build gccgo
// +build go1.5
方法3:使用自定义 tag 指令。
除预定义build 指令外,也可通过命令行 tags 参数传递自定义指令。
main.go
package main
func main() {
hello()
}
debug.go
// +build !release
package main
func hello() {
println("debug version.")
}
release.go
// +build release
package main
func hello() {
println("release version.")
}
log.go
// +build log
package main
func init() {
println("logging ...")
}
go build && ./main
输出: debug version.
go build -tags “release log” && ./main
输出:
logging …
release version.
预处理
简单点说,就是用 go generate 命令扫描源码文件,找出所有“go:generate”注释,提取其中的命令并执行。
- 命令必须放在 .go 源文件中。
- 命令必须以 “//go:generate” 开头(双斜线后不能有空格)。
- 每个文件可有多条generate 命令。
- 命令支持环境变量。
- 必须显式执行 go generate 命令。
- 按文件名顺序提取命令并执行。
- 串行执行,出错后终止后续命令的执行。
a.go
//go:generate echo $GOPATH
//go:generate ls -lh
package main
func hello() {
println("hello world")
}
b.go
//go:generate uname -a
package main
func init() {
}
go generate -n
输出:
echo /Users/www/go
ls -lh
uname -a