Golang 支持交叉编译,即在一个平台编译,在另一个平台运行,本文介绍 Golang Pkg 编译相关。
使用
go build 在其他目录执行需要指定包名,在 main 下不需要
go build -a -x [pkgname|...]
其中:
go build ./... 编译当前目录下所有的源文件,如果启用Go Modules,执行 go build 会把依赖写入 go.mod 中
参数说明
- GOOS: 目标平台
- mac : darwin
- linux : linux
- windows : windows
- GOARCH :目标平台的体系架构
- 386 也称 x86 : 32 位操作系统
- amd64 也称 x64 : 64 位操作系统,比如电脑一般都是 amd64 架构的
- arm 一般用于嵌入式开发,比如 Android,IOS,Win mobile 等
- 交叉编译不支持
CGO_ENABLED,使用CGO_ENABLED=0设置。
编译命令
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build
# 若是在windows编译,也可以采用如下命令设置变量:
SET CGO_ENABLED=0
SET GOOS=darwin
SET GOARCH=amd64
go build
- 在开启 CGO 的情况下,将 Go 项目编译成一个适用于 Linux amd64 平台的、完全静态链接的可执行文件
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags '-linkmode external -extldflags "-static"' -o xxx .
CGO_ENABLED=1
- 含义:启用 CGO。
- 作用:允许 Go 代码调用 C 代码。
- 为什么重要:
- 如果你的项目依赖了 C 库(例如使用了
go-sqlite3),或者依赖 Go 标准库中某些基于 C 的实现(如某些网络 DNS 解析),必须开启此项。
- 通常 Go 默认就是
1,但如果你的目的是静态链接,很多人习惯设为 0。这个命令强调了即使使用了 C 代码,也要强制静态链接。
GOOS=linux
- 含义:目标操作系统为 Linux。
- 作用:交叉编译,不管你当前是在 Mac (Darwin) 还是 Windows 上,编出来的都是 Linux 程序。
GOARCH=amd64
- 含义:目标架构为 64 位 x86 架构。
- 作用:适配最通用的服务器 CPU 架构。
-o xxx
- 含义:指定输出文件名。
- 作用:编译出来的二进制文件将被命名为
xxx。
.
- 含义:当前目录。
- 作用:指定编译入口为当前目录下的
main 包。
-ldflags 链接参数(核心部分)这是整条命令中最关键的部分,用于控制链接器(Linker)的行为。
-linkmode external
- 含义:使用外部链接器。
- 原理:Go 有自己的内部链接器,但功能相对基础。当指定
external 时,Go 会调用系统安装的 C 链接器(通常是 gcc 或 clang)来完成最终的链接工作。
- 为什么需要:为了实现 CGO 代码的完全静态链接,Go 的内部链接器往往不够用,必须依赖 GCC/Clang 强大的静态链接能力来处理 C 库(如
glibc 或 musl)。
-extldflags "-static"
- 含义:传递给外部链接器的参数。
- 细节:
-extldflags 是 Go 的参数,告诉 Go 把后面的字符串传给外部链接器。
"-static" 是传给 GCC/Clang 的参数。
- 作用:告诉 GCC/Clang 在链接时,寻找库文件的静态版本(.a 文件),而不是动态版本(.so 文件)。这意味着它会把
libc、libpthread 等系统库的所有代码都打包进最终的二进制文件里。
go build tag
使用 go bulid -tag 用来自定义构建配置不同的版本,参考
// +build dev
package main
var version = "DEV"
// +build release
package main
var version = "RELEASE"
package main
import "fmt"
func main() {
fmt.Printf("running %s version", version)
}
go build -tags dev -o main-dev
go build -tags release -o main-release
传参编译
package main
import "fmt"
var version string
func main() {
fmt.Printf("running %s version", version)
}
go build -ldflags '-X main.version="dev"' -o main-dev
查看编译包的信息
go version [-m] [-v] [file ...]
go version -m -v gseo-darwin
gseo-darwin: go1.17.6
path github.com/xiexianbin/gseo
mod github.com/xiexianbin/gseo (devel)
dep cloud.google.com/go
...
build -buildmode=exe
build -compiler=gc
build DefaultGODEBUG=panicnil=1
build CGO_ENABLED=0
build GOARCH=amd64
build GOOS=darwin
build GOAMD64=v1
build vcs=git
build vcs.revision=86e93f43b6184ed07028c739a76c0d4b7a748b97
build vcs.time=2022-06-04T14:13:34Z
build vcs.modified=false
查看汇编代码
go tool compile -S main.go
插件
golang 在运行时实现动态的加载外部库
go build -buildmode=plugin
go tool compile -S main.go
go 指令
go:build
end2end test 可以通过如下控制,可以使用 go test -tags e2e 运行比较费时的测试,而不加时不允许该测试
//go:build e2e
// +build e2e
//go:build amd64 !wasm
//go:build linux && amd64
go:embed
go1.16 支持使用 //go:embed 支持将静态资源文件嵌入到 golang 中,支持的类型:
- 单文件读取:
go:embed hello1.txt
- 多文件读取:
go:embed hello1.txt hello2.txt
- 目录读取:
go:embed somedir
- 贪婪匹配:
go:embed somedir/*
package main
import (
"embed"
"fmt"
// _ "embed"
)
//go:embed hello1.txt
var str1 string
//go:embed hello1.txt
var b []byte
//go:embed hello1.txt
var f1 embed.FS
//go:embed hello1.txt hello2.txt
var f2 embed.FS
//go:embed hello1.txt
//go:embed hello2.txt
var f3 embed.FS
//go:embed somedir
var f4 embed.FS
// //go:embed somedir/*
// var f5 embed.FS
func main() {
print(str1)
print(string(b))
data, _ := f1.ReadFile("hello1.txt")
print(string(data))
data1, _ := f2.ReadFile("hello1.txt")
print(string(data1))
data2, _ := f2.ReadFile("hello2.txt")
fmt.Print(string(data2))
data3, _ := f3.ReadFile("hello1.txt")
print(string(data3))
data41, _ := f4.ReadFile("somedir/hello1.txt")
fmt.Print(string(data41))
data42, _ := f4.ReadFile("somedir/hello2.txt")
fmt.Print(string(data42))
}
go:generate
$ mkdir test && cd test
$ mkdir painkiller
$ cat painkiller/pill.go
package painkiller
//go:generate stringer -type=Pill
type Pill int
const (
Placebo Pill = iota
Aspirin
Ibuprofen
Paracetamol
Acetaminophen = Paracetamol
)
$ cat main.go
package main
import (
"fmt"
"test/painkiller"
)
func main() {
fmt.Println(painkiller.Aspirin)
}
$ go mod init test
$ go get golang.org/x/tools/cmd/stringer
$ go install golang.org/x/tools/cmd/stringer
# 生成 pill_string.go
$ go generate ./...
# 项目结构
$ test tree .
.
├── go.mod
├── go.sum
├── main.go
└── painkiller
├── pill.go
└── pill_string.go
2 directories, 5 files
# 运行 main.go
$ go run ./main.go
Aspirin
# 生成的文件
$ cat painkiller/pill_string.go
// Code generated by "stringer -type=Pill"; DO NOT EDIT.
package painkiller
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Placebo-0]
_ = x[Aspirin-1]
_ = x[Ibuprofen-2]
_ = x[Paracetamol-3]
}
const _Pill_name = "PlaceboAspirinIbuprofenParacetamol"
var _Pill_index = [...]uint8{0, 7, 14, 23, 34}
func (i Pill) String() string {
if i < 0 || i >= Pill(len(_Pill_index)-1) {
return "Pill(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Pill_name[_Pill_index[i]:_Pill_index[i+1]]
}
//go:generate stringer -type=Pill -linecomment -output pill_comment.go
type Pill int
const (
Placebo Pill = iota // 安慰剂
Aspirin // 阿司匹林
Ibuprofen // Ibuprofen
Paracetamol // Paracetamol
Acetaminophen = Paracetamol // Acetaminophen
)
go:linkname
//go:linkname time_now time.now
func time_now() (sec int64, nsec int32, mono int64) {
go:noescape
禁止变量逃逸
//go:noescape
func memmove(to, from unsafe.Pointer, n uintptr)
go:nosplit
声明函数不得包含堆栈溢出检查
//go:nosplit
func key32(p *uintptr) *uint32 {
go:noinline
声明函数禁止进行内联优化,配合 go tool compile -S main.go 可以查看编译指令
//go:noinline
func unexportedPanicForTesting(b []byte, i int) byte {
go:norace
声明函数禁止进行竞态检测
go:notinheap
声明函数不允许从 GC 堆上进行申请内存
line
用于指定源代码中的行号和文件名,通常用于生成代码或者调试时,帮助开发者快速定位问题
//line filename:line
F&Q
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34’ not found (required by xxx)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./xxx .