学习一门新的编程语言最令人欣慰的部分之一就是最终运行一个可执行文件并获得预期的输出。刚开始学习Go编程语言时,我先阅读了一些示例程序来熟悉语法,然后尝试编写一些小测试程序。随着时间的推移,这种方法帮助我熟悉了编译和构建程序的过程。Go的构建选项提供了一种更好地控制构建过程的方法。他们还可以提供额外的信息,帮助将流程分解成更小的部分。在这篇文章中,我将演示我使用的一些选项。注意:我使用“构建”和“编译”这两个词来表示同一件事。Go入门我使用的Go版本是1.16.7。但是,此处给出的命令也适用于最新版本。如果您没有安装Go,可以从Go网站下载并按照说明进行安装。您可以通过打开命令提示符并键入以下内容来验证您安装的版本:$goversion您应该得到类似于以下内容的输出,具体取决于您安装的版本:goversiongo1.16.7linux/如何编译和执行基本的amd64上的Go程序我将从一个示例Go程序开始,它只是在屏幕上打印“HelloWorld”,如下所示:$cathello.gopackagemainimport"fmt"funcmain()Println("HelloWorld")}在讨论更高级的选项之前,我将解释如何编译这个Go示例程序。我使用了build命令,后跟Go程序的源文件名,在本例中为hello.go,如下所示:$gobuildhello.go如果一切正常,您应该会看到在当前目录中创建了一个名为hello的可执行文件。您可以使用file命令验证它是否为ELF二进制可执行文件格式(在Linux平台上)。你也可以直接执行它,你会看到它输出“HelloWorld”。$lshellohello.go$file./hello./hello:ELF64-bitLSBexecutable,x86-64,version1(SYSV),staticlylinked,notstripped$./helloHelloWorldGo提供了一个方便的运行命令,所以你只想看到程序运行并获得预期的输出,而不想生成最终的二进制文件。请记住,即使您在当前目录中看不到可执行文件,Go仍会在某处编译生成可执行文件并运行它,然后将其从系统中删除。我将在本文后面的章节中进行解释。$gorunhello.goHelloWorld$lshello.goMoredetails上面的命令就像一阵风一样,一下子跑完了我的程序。但是,如果你想知道Go在编译这些程序的过程中做了什么,Go提供了一个-x选项,它会打印出Go为产生这个可执行文件所做的一切。快速浏览一下就会发现Go在/tmp中创建了一个临时工作目录并构建了可执行文件,然后将其移动到Go源程序所在的当前目录。$gobuild-xhello.goWORK=/tmp/go-build1944767317mkdir-p$WORK/b001/<>mkdir-p$WORK/b001/exe/cd./usr/lib/golang/pkg/tool/linux_amd64/link-o$WORK\/b001/exe/a.out-importcfg$WORK/b001\/importcfg.link-buildmode=exe-buildid=K26hEYzgDkqJjx2Hf-wz/\nDueg0kBjIygx25rYwbK/W-eJaGIOdPEWgwC6o546\/K26hDkqH-jxg-extld=gcc/root/.cache/go-build/cc\/cc72cb2f4fbb61229885fc434995964a7a4d6e10692a23cc0ada6707c5d3435b-d/usr/lib/golang/pkg/tool/linux_amd64/buildid-w$WORK\/blm./b001/exe/a.outhellorm-r$WORK/b001/这有助于解开程序运行后在当前目录下没有生成可执行文件的谜团。使用-x表明可执行文件确实是在/tmp工作目录中创建和执行的。然而,与构建命令不同的是,可执行文件并没有移动到当前目录,看起来好像没有创建可执行文件。$gorun-xhello.gomkdir-p$WORK/b001/exe/cd./usr/lib/golang/pkg/tool/linux_amd64/link-o$WORK/b001\/exe/hello-importcfg$WORK/b001/importcfg.link-s-w-buildmode=exe-buildid=hK3wnAP20DapUDeuvAAS/E_TzkbzwXz6tM5dEC8Mx\/7HYBzuaDGVdaZwSMEWAa/hK3wnAP20DapUDeuvAAS-extld=gcc\/root/.cache/go-build/75/\7531fcf5e48444eed677bfc5cda1276a52b73c62ebac3aa99da3c4094fa57dc3-d$WORK/b001/exe/helloHelloWorld在不生成可执行文件的情况下模拟编译假设您不想编译程序并生成实际的二进制文件,但您确实希望查看过程中的所有步骤。您可以使用-n构建选项来执行此操作,该选项打印通常的执行步骤而不实际创建二进制文件。$gobuild-nhello.gosavetemporarydirectory许多工作发生在/tmp工作目录中,一旦可执行文件创建并运行,它就会被删除。但是,如果您想查看在编译期间创建了哪些文件怎么办?Go提供了一个-work选项,可以在编译程序时使用。-work选项除了运行程序外,还会打印工作目录的路径,但之后不会删除工作目录,因此您可以切换到该目录并检查编译期间创建的所有文件。$gorun-workhello.goWORK=/tmp/go-build3209320645HelloWorld$find/tmp/go-build3209320645/tmp/go-build3209320645/tmp/go-build3209320645/b001/tmp/go.link3209320645/im/portb001/tmp/go-build3209320645/b001/exe/tmp/go-build3209320645/b001/exe/hello$/tmp/go-build3209320645/b001/exe/helloHelloWorld其他编译选项如果你说,你想手动编译程序,而不是使用Go的构建和运行命令最终得到一个可由您的操作系统(在本例中为Linux)直接运行的可执行文件。你是怎么做到的?这个过程可以分为两部分:编译和链接。您可以使用工具选项来查看它是如何工作的。首先,使用工具编译命令生成生成的ar存档,其中包含.o中间文件。接下来,在hello.o文件上执行工具链接命令以生成最终的可执行文件,然后您可以运行它。$gotoolcompilehello.go$filehello.ohello.o:当前ararchive$arthello.o__.PKGDEF_go_.o$gotoollink-ohellohello.o$filehellohello:ELF64位LSB可执行文件,x86-64,version1(SYSV),staticlylinked,notstripped$./helloHelloWorld如果想进一步查看基于hello.o文件的可执行文件的链接过程,可以使用-v选项,它搜索包含在每个Go可执行文件中的runtime.a文件。$gotoollink-v-ohellohello.oHEADER=-H5-T0x401000-R0x1000在/usr/lib/golang/pkg/linux_amd64/runtime.a82052符号中搜索runtime.a,18774个可访问的1个包符号,1106个散列符号,77185个非包符号,3760个外部符号81968个liveness数据交叉编译选项既然我已经解释了一个Go程序的编译过程,接下来,我将通过在实际构建命令之前提供GOOS和GOARCH这两个来演示Go是如何工作的允许您为不同的硬件体系结构和操作系统构建可执行文件的环境变量。什么用途?例如,您可能会发现为ARM(arch64)架构制作的可执行文件无法在Intel(x86_64)架构上运行,并且会生成Exec格式错误。这些选项使构建跨平台二进制文件变得轻而易举:$GOOS=linuxGOARCH=arm64gobuildhello.go$file./hello./hello:ELF64-bitLSBexecutable,ARMaarch64,version1(SYSV),静态链接,未剥离$./hellobash:./hello:cannotexecutebinaryfile:Execformaterror$uname-mx86_64你可以阅读我之前的博文,了解更多关于我与Go交叉编译的经验。查看低级汇编指令源代码并不会直接转换为可执行文件,尽管它会生成一种中间汇编格式,然后最终汇编成可执行文件。在Go中,这被映射到中间汇编格式而不是低级硬件汇编指令。要查看此中间程序集格式,请在使用构建命令时提供-gcflags选项,后跟-S。此命令将显示使用的汇编指令:$gobuild-gcflags="-S"hello.go#command-line-arguments"".mainSTEXTsize=138args=0x0locals=0x58funcid=0x00x000000000(/test/hello.go:5)TEXT"".main(SB),ABIInternal,$88-00x000000000(/test/hello.go:5)MOVQ(TLS),CX0x000900009(/test/hello.go:5)CMPQSP,16(CX)0x000d00013(/test/hello.go:5)PCDATA$0,$-20x000d00013(/test/hello.go:5)JLS128<>你也可以使用objdump-s选项查看编译后的可执行程序的汇编指令,如下:$lshellohello.go$gotoolobjdump-smain.mainhelloTEXTmain.main(SB)/test/hello.gohello.go:50x4975a064488b0c25f8ffffffMOVQFS:0xfffffff8,CXhello.go:50x4975a9483b6110CMPQ0x10(CX),SPhello.go:50x4975ad7671JBE0x4949675:5你好。4883ec58SUBQ$0x58,SPhello.go:60x4975d84889442448MOVQAX,0x48(SP)<>拆分二进制文件以减小其大小Go二进制文件通常非常大例如,一个简单的“HelloWorld”程序将产生一个1.9M大小的二进制文件。$gobuildhello.go$$du-shhello1.9Mhello$$filehellohello:ELF64-bitLSBexecutable,x86-64,version1(SYSV),staticlylinked,notstripped$为了减小大小,您可以分离执行期间不需要的信息。使用-ldflags和-s-w选项可以使生成的二进制文件稍微小一些,只有1.3M。$gobuild-ldflags="-s-w"hello.go$$du-shhello1.3Mhello$$filehellohello:ELF64位LSB可执行文件,x86-64,版本1(SYSV),静态链接,剥离$Summary我希望这篇文章向您介绍了一些方便的Go编译选项,并帮助您更好地理解Go编译过程。有关构建过程和其他有趣选项的更多信息,请参阅Go命令帮助:AshrafChemban在Pixabay上的$gohelpbuildTitleimage。