如何在Ubuntu20.04上为多个平台构建Go可执行文件
介绍
Go 编程语言 带有丰富的工具链,使获取包和构建可执行文件变得异常容易。 Go 最强大的功能之一是能够为任何支持 Go 的外国平台交叉构建可执行文件。 这使得测试和包分发变得更加容易,因为您无需访问特定平台即可为其分发包。
在本教程中,您将使用 Go 的工具从版本控制中获取包并自动安装其可执行文件。 然后您将手动构建和安装可执行文件,以便您熟悉该过程。 然后,您将为不同的架构构建可执行文件,并自动化构建过程以创建适用于多个平台的可执行文件。 完成后,您将知道如何为 Windows 和 macOS 以及您想要支持的其他平台构建可执行文件。
先决条件
要遵循本教程,您将需要:
- 按照Ubuntu 20.04初始服务器设置指南设置一台Ubuntu 20.04服务器,包括sudo非root用户和防火墙。
- Go 已安装,如 如何在 Ubuntu 20.04 上安装 Go 中所述。
第 1 步——创建一个简单的 Go 程序
现在 Go 已安装,您可以尝试创建 Hello, World!
。
首先,为您的 Go 工作区创建一个新目录,Go 将在该目录中构建其文件:
mkdir hello
然后进入刚刚创建的目录:
cd hello
导入包时,必须通过代码自己的模块来管理依赖。 您可以通过使用 go mod init
命令创建一个 go.mod
文件来做到这一点:
go mod init hello
接下来,在您喜欢的文本编辑器中创建一个 Hello, World!
Go 文件:
nano hello.go
将以下文本添加到您的 hello.go
文件中:
你好.go
package main import "fmt" func main() { fmt.Println("Hello, World!") }
然后,按 CTRL+X
,然后按 Y
,然后按 ENTER
保存并关闭文件。
测试您的代码以检查它是否打印 Hello, World!
问候语:
go run .
OutputHello, World!
go run
命令从您创建的新 hello
目录和导入的路径中的 .go
源文件列表编译和运行 Go 包。 但是,您也可以使用 go build
制作一个可执行文件,这样可以节省您一些时间。
第 2 步 - 构建可执行文件
go run
命令运行“Hello, World!”的代码程序,但您可能希望将程序构建为二进制文件,以便在系统上的任何位置运行,而不仅仅是从源代码运行。 go build
命令构建可执行文件。
go build hello
和以前一样,没有输出表示操作成功。 可执行文件将在您的当前目录中生成,与包含该包的目录同名。 在这种情况下,可执行文件将被命名为 hello
。
如果您位于包目录中,则可以省略包的路径,只需运行 go build
。
要为可执行文件指定不同的名称或位置,请使用 -o
标志。 让我们构建一个名为 hello
的可执行文件并将其放在当前工作目录中的 build
目录中:
go build -o build/hello hello
此命令创建可执行文件,如果 ./build
目录不存在,还会创建它。
现在让我们看看安装可执行文件。
第 3 步 — 安装可执行文件
构建可执行文件会在当前目录或您选择的目录中创建可执行文件。 安装可执行文件是创建可执行文件并将其存储在 $GOPATH/bin
中的过程。 go install
命令的工作方式与 go build
类似,但 go install
负责将输出文件放置在适合您的位置。
要安装可执行文件,请使用 go install
,后跟包导入路径。 再一次,使用你的“Hello, World!” 程序来试试这个:
go install hello
就像 go build
一样,如果命令成功,您将看不到任何输出。 和以前一样,创建的可执行文件与包含包的目录同名。 但这一次,可执行文件存储在 $GOPATH/bin
中。 如果 $GOPATH/bin
是您的 $PATH
环境变量的一部分,则可执行文件将在您的操作系统上的任何位置可用。 您可以使用 which
命令验证其位置:
which hello
您将看到以下输出:
Output of which/home/sammy/go/bin/hello
现在您了解了 go get
、go build
和 go install
的工作原理以及它们之间的关系,让我们来探索最流行的 Go 功能之一:为其他目标创建可执行文件平台。
第 4 步 — 为不同架构构建可执行文件
go build
命令让您可以在您的平台 上为任何支持 Go 的目标平台 构建可执行文件。 这意味着您可以测试、发布和分发您的应用程序,而无需在您希望使用的目标平台上构建这些可执行文件。
交叉编译通过设置指定目标操作系统和体系结构的所需环境变量来工作。 我们将变量 GOOS
用于目标操作系统,将 GOARCH
用于目标架构。 要构建可执行文件,该命令将采用以下形式:
GOOS=target-OS GOARCH=target-architecture go build package-import-path
在运行 go build
命令之前设置操作系统和架构。 这使您可以仅将环境变量用于当前命令执行。 命令执行后变量未设置或重置。
为了找出可用于构建可执行文件的操作系统和平台,您可以使用 dist
工具:
go tool dist list
这将为您提供由 /
字符分隔的操作系统和架构列表:
Outputaix/ppc64 android/386 android/amd64 android/arm android/arm64 darwin/amd64 darwin/arm64 dragonfly/amd64 freebsd/386 freebsd/amd64 freebsd/arm freebsd/arm64 illumos/amd64 ios/amd64 ios/arm64 js/wasm linux/386 linux/amd64 linux/arm linux/arm64 linux/mips linux/mips64 linux/mips64le linux/mipsle linux/ppc64 linux/ppc64le linux/riscv64 linux/s390x netbsd/386 netbsd/amd64 netbsd/arm netbsd/arm64 openbsd/386 openbsd/amd64 openbsd/arm openbsd/arm64 openbsd/mips64 plan9/386 plan9/amd64 plan9/arm solaris/amd64 windows/386 windows/amd64 windows/arm
警告: 为 Android 交叉编译可执行文件需要 Android NDK,以及一些超出本教程范围的额外设置。
使用列表中的值,我们可以为 Windows 64 位构建 hello,如下所示:
GOOS=windows GOARCH=amd64 go build hello
再一次,没有输出表明操作成功。 可执行文件将在当前目录中创建,使用包名作为其名称。 但是,由于我们为 Windows 构建了这个可执行文件,所以名称以后缀 .exe
结尾。
您的当前目录中应该有一个 hello.exe
文件,您可以使用 ls
命令对其进行验证。
ls hello.exe
您将看到输出中列出的 hello.exe
文件:
Outputhello.exe
注意:您可以使用 -o
标志重命名可执行文件或将其放置在不同的位置。 但是,在为 Windows 构建可执行文件并提供不同名称时,请务必在设置可执行文件名称时明确指定 .exe
后缀。
让我们看一下编写此过程的脚本,以便更轻松地为多个目标环境发布软件。
第 5 步 — 创建脚本以自动进行交叉编译
为许多平台创建可执行文件的过程可能有点乏味,但我们可以创建一个脚本来让事情变得更简单。
该脚本将包导入路径作为参数,遍历操作系统和平台对的预定义列表,并为每一对生成一个可执行文件,将输出放在当前目录中。 每个可执行文件将以包名命名,后跟目标平台和架构,格式为 package-OS-architecture
。 这将是一个通用脚本,您可以在任何项目中使用。
在项目目录中,在文本编辑器中创建一个名为 go-executable-build.bash
的新文件:
nano go-executable-build.bash
我们将从 shebang 行开始我们的脚本。 这一行定义了当它作为可执行文件运行时哪个解释器将解析这个脚本。 添加以下行以指定 bash
应执行此脚本:
go-executable-build.bash
#!/usr/bin/env bash
我们希望将包导入路径作为命令行参数。 为此,我们将使用 $n
变量,其中 n
是一个非负数。 变量 $0
包含您执行的脚本的名称,而 $1
和更大的变量将包含用户提供的参数。 将此行添加到脚本中,它将从命令行获取第一个参数并将其存储在名为 package
的变量中:
go-executable-build.bash
... package=$1
接下来,确保用户提供了此值。 如果未提供该值,请退出脚本并显示说明如何使用脚本的消息:
go-executable-build.bash
... if [[ -z "$package" ]]; then echo "usage: $0 <package-name>" exit 1 fi
此 if
语句检查 $package
变量的值。 如果没有设置,我们使用 echo
打印正确的用法,然后使用 exit
终止脚本。 exit
将返回值作为参数,成功执行时应为 0
,不成功执行时应为任何非零值。 我们在这里使用 1
因为脚本不成功。
注意:如果你想让这个脚本使用预定义的包,改变package
变量指向那个导入路径:
go-executable-build.bash
... package="github.com/user/hello"
接下来,我们要从路径中提取包名。 包导入路径由 /
字符分隔,包名位于路径末尾。 首先,我们将包导入路径拆分为一个数组,使用 /
作为分隔符:
go-executable-build.bash
package_split=(${package//\// })
包名应该是这个新的 $package_split
数组的最后一个元素。 在 Bash 中,您可以使用负数组索引从末尾而不是开头访问数组。 添加此行以从数组中获取包名称并将其存储在名为 package_name
的变量中:
go-executable-build.bash
... package_name=${package_split[-1]}
现在,您需要决定要为哪些平台和架构构建可执行文件。 在本教程中,我们将为 Windows 64 位、Windows 32 位和 64 位 macOS 构建可执行文件。 我们将这些目标放在一个格式为 OS/Platform
的数组中,因此我们可以使用与提取包名称相同的方法将每一对分成 GOOS
和 GOARCH
变量从路径。 将平台添加到脚本中:
go-executable-build.bash
... platforms=("windows/amd64" "windows/386" "darwin/amd64")
接下来,我们将遍历平台数组,将每个平台条目拆分为 GOOS
和 GOARCH
环境变量的值,并使用它们来构建可执行文件。 我们可以使用以下 for
循环来做到这一点:
go-executable-build.bash
... for platform in "${platforms[@]}" do ... done
platform
变量将在每次迭代中包含来自 platforms
数组的条目。 我们需要将 platform
拆分为两个变量 - GOOS
和 GOARCH
。 将以下行添加到 for
循环:
go-executable-build.bash
for platform in "${platforms[@]}" do platform_split=(${platform//\// }) GOOS=${platform_split[0]} GOARCH=${platform_split[1]} done
接下来,我们将通过将包名称与操作系统和体系结构相结合来生成可执行文件的名称。 当我们为 Windows 构建时,我们还需要在文件名中添加 .exe
后缀。 将此代码添加到 for
循环:
go-executable-build.bash
for platform in "${platforms[@]}" do platform_split=(${platform//\// }) GOOS=${platform_split[0]} GOARCH=${platform_split[1]} output_name=$package_name'-'$GOOS'-'$GOARCH if [ $GOOS = "windows" ]; then output_name+='.exe' fi done
设置好变量后,我们使用 go build
创建可执行文件。 将此行添加到 for
循环的主体,就在 done
关键字上方:
go-executable-build.bash
... if [ $GOOS = "windows" ]; then output_name+='.exe' fi env GOOS=$GOOS GOARCH=$GOARCH go build -o $output_name $package done
最后,我们应该检查构建可执行文件是否存在错误。 例如,如果我们尝试构建一个我们没有源代码的包,我们可能会遇到错误。 我们可以检查 go build
命令的返回码是否为非零值。 变量 $?
包含前一个命令执行的返回码。 如果 go build
返回 0
以外的任何值,则有问题,我们将要退出脚本。 将此代码添加到 for
循环中,在 go build
命令之后和 done
关键字上方。
go-executable-build.bash
... env GOOS=$GOOS GOARCH=$GOARCH go build -o $output_name $package if [ $? -ne 0 ]; then echo 'An error has occurred! Aborting the script execution...' exit 1 fi
有了这个,我们现在有了一个脚本,它将从我们的 Go 包构建多个可执行文件。 这是完成的脚本:
go-executable-build.bash
#!/usr/bin/env bash package=$1 if [[ -z "$package" ]]; then echo "usage: $0 <package-name>" exit 1 fi package_split=(${package//\// }) package_name=${package_split[-1]} platforms=("windows/amd64" "windows/386" "darwin/amd64") for platform in "${platforms[@]}" do platform_split=(${platform//\// }) GOOS=${platform_split[0]} GOARCH=${platform_split[1]} output_name=$package_name'-'$GOOS'-'$GOARCH if [ $GOOS = "windows" ]; then output_name+='.exe' fi env GOOS=$GOOS GOARCH=$GOARCH go build -o $output_name $package if [ $? -ne 0 ]; then echo 'An error has occurred! Aborting the script execution...' exit 1 fi done
验证您的文件是否与前面的代码匹配。 然后保存文件并退出编辑器。
在我们可以使用脚本之前,我们必须使用 chmod
命令使其可执行:
chmod +x go-executable-build.bash
最后,通过为 hello 构建可执行文件来测试脚本:
./go-executable-build.bash hello
如果一切顺利,您的当前目录中应该有可执行文件。 无输出表示脚本执行成功。 您可以验证是否使用 ls
命令创建的可执行文件:
ls hello*
您应该看到所有三个版本:
Example ls outputhello-darwin-amd64 hello-windows-386.exe hello-windows-amd64.exe
要更改目标平台,只需更改脚本中的 platforms
变量。
结论
在本教程中,您学习了如何使用 Go 的工具从版本控制系统获取包,以及为不同平台构建和交叉编译可执行文件。
您还创建了一个脚本,可用于为多个平台交叉编译单个包。
为确保您的应用程序正常运行,您可以查看 testing 和 continuous integration like Travis-CI and AppVeyor for testing在 Windows 上。