介绍
Go 编程语言 带有丰富的工具链,使获取包和构建可执行文件变得异常容易。 Go 最强大的功能之一是能够为任何支持 Go 的外国平台交叉构建可执行文件。 这使得测试和包分发变得更加容易,因为您无需访问特定平台即可为其分发包。
在本教程中,您将使用 Go 的工具从版本控制中获取包并自动安装其可执行文件。 然后您将手动构建和安装可执行文件,以便您熟悉该过程。 然后,您将为不同的架构构建可执行文件,并自动化构建过程以创建适用于多个平台的可执行文件。 完成后,您将知道如何为 Windows 和 macOS 以及您想要支持的其他平台构建可执行文件。
先决条件
要遵循本教程,您将需要:
- 按照 Ubuntu 16.04 初始服务器设置指南 设置一台 Ubuntu 16.04 服务器,包括 sudo 非 root 用户和防火墙。
- Go 已安装,如 如何在 Ubuntu 16.04 上安装 Go 1.6 中所述。
第 1 步 — 从版本控制安装 Go 程序
在我们可以从 Go 包创建可执行文件之前,我们必须获取它的源代码。 go get
工具可以从 GitHub 等版本控制系统中获取包。 在后台,go get
将包克隆到 $GOPATH/src/
目录的子目录中。 然后,如果适用,它会通过构建其可执行文件并将其放置在 $GOPATH/bin
目录中来安装包。 如果您按照先决条件教程中的说明配置了 Go,则 $GOPATH/bin
目录包含在您的 $PATH
环境变量中,这确保您可以从系统上的任何位置使用已安装的包。
go get
命令的语法是 go get package-import-path
。 package-import-path
是一个唯一标识包的字符串。 它通常是软件包在 Github 等远程存储库中的位置,或者是您机器上 $GOPATH/src/
目录中的目录。
通常将 go get
与 -u
标志一起使用,它指示 go get
获取包及其依赖项,或者如果它们已经存在于机器上,则更新这些依赖项。
在本教程中,我们将安装 Caddy,一个用 Go 编写的 Web 服务器。 根据 Caddy 的说明 x,我们将使用 github.com/mholt/caddy/caddy
作为包导入路径。 使用 go get
获取并安装 Caddy:
go get -u github.com/mholt/caddy/caddy
该命令需要一些时间才能完成,但在它获取包并安装它时,您不会看到任何进度。 没有输出实际上表明命令执行成功。
命令完成后,您会在 $GOPATH/src/github.com/mholt/caddy
找到 Caddy 的源代码。 此外,由于 Caddy 有一个可执行文件,它是自动创建并存储在 $GOPATH/bin
目录中的。 通过使用 which
打印可执行文件的位置来验证这一点:
which caddy
您将看到以下输出:
Output/home/sammy/work/bin/caddy
注意:go get
命令从 Git 存储库的默认分支安装包,在许多情况下是 master
或开发中的分支。 在使用 go get
之前,请务必查看通常位于存储库的 README
文件中的说明。
您可以使用 git checkout
等 Git 命令在使用 go get
命令获得的源上选择不同的分支。 查看教程 如何使用 Git 分支 以了解有关如何切换分支的更多信息。
让我们更详细地看一下安装 Go 包的过程,首先从我们已经获得的包创建可执行文件开始。
第 2 步 - 构建可执行文件
go get
命令在一个命令中为我们下载了源代码并为我们安装了 Caddy 的可执行文件。 但是您可能希望自己重新构建可执行文件,或者使用自己的代码构建可执行文件。 go build
命令构建可执行文件。
虽然我们已经安装了 Caddy,但让我们手动重新构建它,以便我们熟悉这个过程。 执行go build
,指定包导入路径:
go build github.com/mholt/caddy/caddy
和以前一样,没有输出表示操作成功。 可执行文件将在您的当前目录中生成,与包含该包的目录同名。 在这种情况下,可执行文件将被命名为 caddy
。
如果您位于包目录中,则可以省略包的路径,只需运行 go build
。
要为可执行文件指定不同的名称或位置,请使用 -o
标志。 让我们构建一个名为 caddy-server
的可执行文件并将其放在当前工作目录中的 build
目录中:
go build -o build/caddy-server github.com/mholt/caddy/caddy
此命令创建可执行文件,如果 ./build
目录不存在,还会创建它。
现在让我们看看安装可执行文件。
第 3 步 — 安装可执行文件
构建可执行文件会在当前目录或您选择的目录中创建可执行文件。 安装可执行文件是创建可执行文件并将其存储在 $GOPATH/bin
中的过程。 go install
命令的工作方式与 go build
类似,但 go install
负责将输出文件放置在适合您的位置。
要安装可执行文件,请使用 go install
,后跟包导入路径。 再一次,使用 Caddy 来试试这个:
go install github.com/mholt/caddy/caddy
就像 go build
一样,如果命令成功,您将看不到任何输出。 和以前一样,创建的可执行文件与包含包的目录同名。 但这一次,可执行文件存储在 $GOPATH/bin
中。 如果 $GOPATH/bin
是您的 $PATH
环境变量的一部分,则可执行文件将在您操作系统的任何位置可用。 您可以使用 which
命令验证其位置:
which caddy
您将看到以下输出:
Output of which/home/sammy/work/bin/caddy
现在您了解了 go get
、go build
和 go install
的工作原理以及它们之间的关系,让我们来探索最流行的 Go 功能之一:为其他目标创建可执行文件平台。
第 4 步 — 为不同架构构建可执行文件
go build
命令让您可以在您的平台 上为任何支持 Go 的目标平台 构建可执行文件。 这意味着您可以测试、发布和分发您的应用程序,而无需在您希望使用的目标平台上构建这些可执行文件。
交叉编译通过设置指定目标操作系统和体系结构的所需环境变量来工作。 我们将变量 GOOS
用于目标操作系统,将 GOARCH
用于目标架构。 要构建可执行文件,该命令将采用以下形式:
env GOOS=target-OS GOARCH=target-architecture go build package-import-path
env
命令在修改后的环境中运行程序。 这使您可以仅将环境变量用于当前命令执行。 命令执行后变量未设置或重置。
下表显示了您可以使用的 GOOS
和 GOARCH
的可能组合:
GOOS - 目标操作系统
|
GOARCH - 目标平台
|
---|---|
android
|
arm
|
darwin
|
386
|
darwin
|
amd64
|
darwin
|
arm
|
darwin
|
arm64
|
dragonfly
|
amd64
|
freebsd
|
386
|
freebsd
|
amd64
|
freebsd
|
arm
|
linux
|
386
|
linux
|
amd64
|
linux
|
arm
|
linux
|
arm64
|
linux
|
ppc64
|
linux
|
ppc64le
|
linux
|
mips
|
linux
|
mipsle
|
linux
|
mips64
|
linux
|
mips64le
|
netbsd
|
386
|
netbsd
|
amd64
|
netbsd
|
arm
|
openbsd
|
386
|
openbsd
|
amd64
|
openbsd
|
arm
|
plan9
|
386
|
plan9
|
amd64
|
solaris
|
amd64
|
windows
|
386
|
windows
|
amd64
|
警告: 为 Android 交叉编译可执行文件需要 Android NDK,以及一些超出本教程范围的额外设置。
使用表中的值,我们可以为 Windows 64 位构建 Caddy,如下所示:
env GOOS=windows GOARCH=amd64 go build github.com/mholt/caddy/caddy
再一次,没有输出表明操作成功。 可执行文件将在当前目录中创建,使用包名作为其名称。 但是,由于我们为 Windows 构建了这个可执行文件,所以名称以后缀 .exe
结尾。
您的当前目录中应该有 caddy.exe
文件,您可以使用 ls
命令进行验证。
ls caddy.exe
您将看到输出中列出的 caddy.exe
文件:
Outputcaddy.exe
注意:您可以使用 -o
标志重命名可执行文件或将其放置在不同的位置。 但是,在为 Windows 构建可执行文件并提供不同名称时,请务必在设置可执行文件名称时明确指定 .exe
后缀。
让我们看一下编写此过程的脚本,以便更轻松地为多个目标环境发布软件。
第 5 步 — 创建脚本以自动进行交叉编译
为许多平台创建可执行文件的过程可能有点乏味,但我们可以创建一个脚本来让事情变得更简单。
该脚本将包导入路径作为参数,遍历操作系统和平台对的预定义列表,并为每一对生成一个可执行文件,将输出放在当前目录中。 每个可执行文件将以包名命名,后跟目标平台和架构,格式为 package-OS-architecture
。 这将是一个通用脚本,您可以在任何项目中使用。
切换到您的主目录并在文本编辑器中创建一个名为 go-executable-build.bash
的新文件:
cd ~ 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
最后,通过为 Caddy 构建可执行文件来测试脚本:
./go-executable-build.bash github.com/mholt/caddy/caddy
如果一切顺利,您的当前目录中应该有可执行文件。 无输出表示脚本执行成功。 您可以验证是否使用 ls
命令创建的可执行文件:
ls caddy*
您应该看到所有三个版本:
Example ls outputcaddy-darwin-amd64 caddy-windows-386.exe caddy-windows-amd64.exe
要更改目标平台,只需更改脚本中的 platforms
变量。
结论
在本教程中,您学习了如何使用 Go 的工具从版本控制系统获取包,以及为不同平台构建和交叉编译可执行文件。
您还创建了一个脚本,可用于为多个平台交叉编译单个包。
为确保您的应用程序正常运行,您可以查看 testing 和 continuous integration like Travis-CI and AppVeyor for testing在 Windows 上。
如果您对 Caddy 以及如何使用它感兴趣,请查看 如何在 Ubuntu 16.04 上使用 Caddy 托管网站。