- /
- Skills/
- 后端编程/
- 使用go build来编译go程序/
使用go build来编译go程序
目录
这篇文件准备揭开 go build 命令的面纱,帮助你了解如何通过 go build 来得到一个可执行程序。
同时,在上一篇文章中介绍使用 go modules 来管理 go 工程并编写第一个 helloworld 示例程序时,留下了一个悬念,为什么使用 hellohello 来作为程序文件的名称?
还是那个 helloworld #
下面仍然使用上文中的例子 helloworld。
~/Projects/go/hello
➜ ls
go.mod hellohello.go
其中 go.mod 内容如下:
// go.mod
module world
go 1.16
hellohello.go 代码如下:
// hellohello.go
package main
import "fmt"
func main() {
fmt.Println("hello world 😀😀😀")
}
我们知道,使用 go run hellohello.go
可以编译并运行 hellohello.go 中所定义的逻辑。但是生成的可执行程序在操作系统的临时文件目录下,
如 临时文件目录/go-build2507969658/b001/exe/hellohello
,这个可执行程序是用来测试的。
为了在当前目录下生成可执行程序,要使用开发工具链的 go build 工具。
- go build hellohello.go
在这个 helloworld 示例程序目录下,执行 go build hellohello.go
,会生成一个叫 hellohello 的可执行程序。如果你的文件名叫 helloworld,那么生成的可执行程序名称就为 helloworld。
- go build
在 helloworld 示例程序目录下,执行 go build
,会生成一个叫 world 的可执行程序,这个名称是 go.mod 中定义的模块名称。 如果把模块名称定义为 world/youwu.today,那么生成的可执行程序叫 youwu.today
- 其它的
以上只不过是为了方便的默认规则。当然,你还可以额外指定生成的可执行程序的名称,如使用go build -o 路径
来指定路径:
~/Projects/go/hello
➜ go build -o .
~/Projects/go/hello
➜ ls
~/Projects/go/hello
➜ ls
go.mod hellohello hellohello.go world
使用 go build -o 名称
来指定生成的程序名:
~/Projects/go/hello
➜ go build -o h
~/Projects/go/hello
➜ ls
go.mod h hellohello hellohello.go world
假设你的工程里面有多个 main 函数,需要生成多个可执行程序,那么使用 go build -o 输出名称 go文件的路径或位置
。比如你可能会看到别人开源的 cli 工具,在 cmd 目录下有好多个子程序,使用了非常复杂的构建脚本来生成多个可执行程序,下次就知道了,通过一个 go build
无法完成的,使用 shell 或者 bat 脚本自己写一个执行脚本。
交叉编译 #
交叉编译( cross compiling )中的 交叉 cross,是 跨平台( cross platform ) 的意思。go 语言在设计的时候,就被设计成为可以在一个系统上编译,然后拿到其它系统系统上执行,类似于 java 的 jar 文件。不同的是,jar 文件是一个 字节码 class 文件的 zip 压缩包,它要在 java vm 虚拟机中解析运行。而 go 所编译出现的是独立的单个可以二进制可执行文件。
比如在 windows 上完成开发,然后编译交叉编译成多 windows 版本(本机开发测试)、linux 版本(生成部署)。 命令比较简单,指明 操作系统 与 CPU架构 类型即可,如下,
在 windows 上编译可在 linux 上运行的 64 位可执行程序:
set GOOS=linux
set GOARCH=amd64
go build -o hello hello.go
在 linux 上编译可在 windows 上运行的 64 位可执行程序:
GOOS=windows GOARCH=amd64 go build -o hello.exe hello.go
在 macos 上的命令与 linux 相同,通过在命令前面添加环境变量。
不过,是否最终可以成功编译,还是要看编译的内容是否引用到操作系统的底层、CGO 等需要 C/C++ 语言编译环境。不过,如果只是写一般的 go 应用程序,上面的交叉编译是没有问题的。如果你碰到了无法正确编译,说明你碰到了不在命令本身而是层次更高的编程与三方库使用的问题。
GOOS
、GOARCH
的所有可能项,可以使用 go tool dist list
这个命令查看。
zsh ❯ go version
go version go1.20.2 darwin/amd64
zsh > go tool dist list
os | 386 | amd64 | arm | arm64 |
---|---|---|---|---|
android | ✔ | ✔ | ✔ | ✔ |
darwin | ✔ | ✔ | ||
dragonfly | ✔ | |||
freebsd | ✔ | ✔ | ✔ | ✔ |
illumos | ✔ | |||
ios | ✔ | ✔ | ||
linux | ✔ | ✔ | ✔ | ✔ |
netbsd | ✔ | ✔ | ✔ | ✔ |
openbsd | ✔ | ✔ | ✔ | ✔ |
plan9 | ✔ | ✔ | ✔ | |
solaris | ✔ | |||
windows | ✔ | ✔ | ✔ | ✔ |
还有一些非 intel、ARM 架构的 芯片。比如 mips,不过在90年代,它非常流行,广泛用于嵌入式、街机、打印机、机顶盒、电视机等。loong64 是指龙芯,ppc 是指 powerpc。而 js/wasm
是指可以被 js调用的可浏览器运行或者 wasm 运行时中运行的 web 汇编。
- aix/ppc64
- freebsd/riscv64
- js/wasm
- linux/loong64
- linux/mips
- linux/mips64
- linux/mips64le
- linux/mipsle
- linux/ppc64
- linux/ppc64le
- linux/riscv64
- linux/s390x
- openbsd/mips64
补充知识 #
若你是 go 编程新手,看了本文以及
go modules
,其中的 go run
和 go build
都没有向你指明要求。没错,就是 main package main function。
这个隐含的规则非常关键,因为它会决定是否可以正常编译。
golang 的规定是这样的: 一个 go 程序中,如果定义了 main package main function,那么它会作为整个程序的入口,所有的用户过程都是从这里开始。
执行 go build
或者 go run
命令时,会查找指向目录是不是一个 main package,再查找这个 main package 是否有一个 main 函数。
举两个反例来辅助理解。
- 修改为非 main package
// hellohello.go
package youwutoday
import "fmt"
func main() {
fmt.Println("hello world 😀😀😀")
}
使用 go build
或者 go run
得到的结果表示,go build
不会生成可执行程序(因为此时不是 main package),而 go run
则提示不是 main package 不能执行。
~/Projects/go/hello
➜ go build
~/Projects/go/hello
➜ ls
go.mod hellohello.go
~/Projects/go/hello
➜ go run hellohello.go
go run: cannot run non-main package
- 修改为无 main 函数
// hellohello.go
package main
import "fmt"
func youwutoday() {
fmt.Println("hello world 😀😀😀")
}
从 go build
或者 go run
得到的结果来看,没有 main 函数根本无法继续。
➜ go run hellohello.go
# command-line-arguments
runtime.main_main·f: function main is undeclared in the main package
~/Projects/go/hello
➜ go build
# world
runtime.main_main·f: function main is undeclared in the main package