当前位置: 首页 > 科技观察

通过编译报错总结两个Go程序编译的重要知识

时间:2023-03-19 20:43:44 科技观察

转载本文请联系网管谢bi公众号。最近调研了某APM厂商的Goprobe程序,说通过引入一个包,就可以将probe引入到项目中,全程不需要修改其他代码。没想到我第一次引入包,尝试搭建的时候就翻车了。main.go:10:2:buildconstraintsexcludeallGofilesin/xxx/github.com/xxx/agnet/xxxx编译器在编译时直接排除了某个包下的所有文件。这是什么意思?意思是这个包下没有当前构建环境中构建的Go文件。猜测应该是这个包源码的build标签声明了不允许在Mac环境下构建。打开源码看看。事实上,所有文件的构建标签都是以这种方式声明的。//+buildlinux//+buildamd64这个叫做条件编译,或者约束编译。那么在Mac下想编译一个只能在Linux上运行的可执行文件怎么办呢?Go还支持一种称为交叉编译的功能,即用于跨平台编译。具体如何使用,比如本例中,需要在Mac环境下编译一个Linux系统amd64架构下可以运行的可执行文件,所以要编译:CGO_ENABLED=0GOOS=linuxGOARCH=amd64gobuildmain.go不过我以后想研究为什么不让它在Mac上编译呢?看了这个包的probe,是用CGO实现的,在linux系统下调用了一个用C语言实现的工具命令。看到这里,我也不想再继续研究这个包了,所以为了让这篇文章不那么明显:),我们再回顾一下Go语言的交叉编译和条件编译这两个知识点。交叉编译交叉编译用于为另一个平台生成一个平台上的可执行程序。Go的命令集原生支持交叉编译,使用方法非常简单。比如上面已经演示过的CGO_ENABLED=0GOOS=linuxGOARCH=amd64gobuildmain.go参数说明。CGO的GOOS不能在GOOS中使用:目标平台mac对应darwinlinux对应linuxwindows对应windowsGOARCH:目标平台的架构[386,amd64,arm],目前市面上的个人电脑一般都是amd64的386架构,也叫x8632位操作系统amd64也叫x64,对应64位操作系统arm。这种架构一般用于嵌入式开发。比如Android、IOS、Winmobile、TIZEN等。了解了这些参数之后,我们再来看Mac、Linux、Windows三个平台交叉编译的例子。我没有尝试,因为我家境不好,条件不好。不过,我是在网上找到的。如有错误,请在评论中留言,帮助我更正。Mac下编译,Linux或Windows的可执行程序#linux可执行程序CGO_ENABLED=0GOOS=linuxGOARCH=amd64gobuildmain.go#Windows可执行程序CGO_ENABLED=0GOOS=windowsGOARCH=amd64gobuildmain.goLinux下编译,Mac或Windows上执行#Mac平台可执行程序CGO_ENABLED=0GOOS=darwinGOARCH=amd64gobuildmain.go#Windows可执行程序CGO_ENABLED=0GOOS=windowsGOARCH=amd64gobuildmain.go在Windows下执行,在Mac或Linux下执行,需要写一个批处理程序在里面设置,因为在Windows下是终端做的不支持shell,这点和Mac、Linux有点区别。#Mac执行SETCGO_ENABLED=0SETGOOS=darwinSETGOARCH=amd64gobuildmain.go#Linux执行SETCGO_ENABLED=0SETGOOS=linuxSETGOARCH=amd64gobuildmain.go条件编译交叉编译只是针对一个平台编译可以在其他平台运行的程序。Go是跨平台的语言,它提供的类库也必须是跨平台的。比如程序的系统调用相关的函数,可以根据环境编译出相应的源码。.让编译器只编译满足条件的代码,丢弃不满足条件的代码。这是另一个概念,叫做条件编译。在Go中,它也被称为BuildConstraints。添加构建约束的方式有两种:构建标签(buildtag)文件后缀buildtagbuildtag是一种在源代码文件顶部添加注释的方式,用来判断文件是否参与编译的Constraints。其格式如下://+build注意://+build的下一行必须是空行,否则会被解析为包注释。//+buildlinux//mainpackagecommentpackagemaintags说明:空格分隔表示AND逗号分隔表示OR!表示NOT标签,可以指定为:操作系统,环境变量中GOOS的值,如:linux、darwin、windows等操作系统的体系结构,环境变量中GOARCH的值,如:arch64,x86,i386等。使用的编译器,gc或者gccgo。是否开启CGO,cgo。Golang版本号:比如GoVersion1.1就是go1.1,GoVersion1.12就是go1.12,等等。其他自定义标签,通过gobuild-tags指定的值。比如编译条件是(linuxAND386)OR(darwinAND(NOTcgo))//+buildlinux,386darwin,!cgo另一个文件可以有多个编译约束,比如条件是(linuxORdarwin)ANDamd64//+buildlinuxdarwin//+buildamd64也可以使用ignore标签从编译中排除一个文件。//+buildignorefilesuffix除了编译标签之外,第二种添加编译约束的方式是通过源代码文件的文件名。该解决方案比构造标签解决方案更简单。编译器也会根据文件后缀自动选择编译好的文件:$filename_$GOOS.go$filename_$GOARCH.go$filename_$GOOS_$GOARCH.go$filename:源文件名。$GOOS:表示操作系统,从环境变量中获取。$GOARCH:表示系统架构,从环境变量中获取。切记不要颠倒后缀的顺序。当系统名和架构名同时出现在后缀中时,需要保持$filename_$GOOS_$GOARCH.go的顺序。在Go的每个内置库中,都有许多以不同系统名称结尾的文件。下面是Go的os内置库的部分源码截图:AddingCompilationConstraintstoFileSuffixes添加编译约束的两种方式HowtochooseBuildlabels和filenamesuffixes在功能上有重叠。例如,对于名为mypkg_linux.go的文件,包含构建标记//+buildlinux是多余的。一般来说,当只需要指定一个特定的平台时,我们选择文件名后缀的方式。例如:mypkg_linux.go//只在linux系统上编译mypkg_windows_amd64.go//只在windowsamd64位平台上编译反之,如果你的文件需要指定多平台或多架构,或者需要排除特定平台,我们选择构建标签的方式。比如://在所有类unix平台上编译//+builddarwindragonflyfreebsdlinuxnetbsdopenbsd//在非windows平台上编译//+build!windows,一个编译器报错,写了一篇文章....啊...(咳咳)交叉编译和条件编译(编译约束)两个很重要的知识点介绍一下。其实这两个知识点我早就写过一篇文章了。再来分析一下,希望大家喜欢。参考链接http://www.oskip.com/post/golang/golang-build/https://juejin.cn/post/6844903944808824845https://mp.weixin.qq.com/s/Ys8o4arwIFYB6DPCdiGNNQ