各位老铁们,大家好,今天由我来为大家分享go网站源码分享安装教程,以及2020网站源码的相关问题知识,希望对大家有所帮助。如果可以帮助到大家,还望关注收藏下本站,您的支持是我们最大的动力,谢谢大家了哈,下面我们开始吧!
Go语言的1.5版本在标准命令方面有了重大变更。这倒不是说它们的用法有多大的变化,而是说它们的底层支持已经大变样了。让我们先来对比一下$GOROOT/pkg/tool/<平台相关目录>中的内容。以下简称此目录为Go工具目录。
插播:平台相关目录即以_命名的目录,用于存放因特定平台的不同而不同的代码包归档文件或可执行文件。其中,代表特定平台的操作系统代号,而则代表特定平台的计算架构代号。使用goenv命令便可查看它们在你的计算机中的实际值。
1.4版本的Go工具目录的内容如下:
5a5l6g8caddr2linedistobjdumptour\n5c6a6l8gcgofixpackvet\n5g6c8a8lcovernmpprofyacc
下面是Go1.5版本的:
addr2lineasmcompiledistfixnmpacktourvet\napicgocoverdoclinkobjdumppproftraceyacc
可以看到,1.5版本的目录内容精简了不少。这是因为Go1.5的编译器、链接器都已经完全用Go语言重写了。而在这之前,它们都是用C语言写的,因此不得不为每类平台编写不同的程序并生成不同的文件。例如,8g、6g和5g分别是gc编译器在x86(32bit)、x86-64(64bit)和ARM计算架构的计算机上的实现程序。相比之下,用Go语言实现的好处就是,编译器和链接器都将是跨平台的了。简要来说,Go1.5版本的目录中的文件compile即是统一后的编译器,而文件link则是统一后的链接器。
本教程并不会讲解Go语言的编译器、链接器以及其它工具是怎样被编写出来的,并只会关注于怎样用好包含它们在内的Go语言自带的命令和工具。
为了让讲解更具关联性,也为了让读者能够更容易地理解这些命令和工具,本教程并不会按照这些命令的字典顺序描述它们,而会按照我们在实际开发过程中通常的使用顺序以及它们的重要程度来逐一进行说明。现在,我们就先从gobuild命令开始。
gobuild
gobuild命令用于编译我们指定的源码文件或代码包以及它们的依赖包。
例如,如果我们在执行gobuild命令时不后跟任何代码包,那么命令将试图编译当前目录所对应的代码包。例如,我们想编译goc2p项目的代码包logging。其中一个方法是进入logging目录并直接执行该命令:
hc@ubt:~/golang/goc2p/src/logging$gobuild
因为在代码包logging中只有库源码文件和测试源码文件,所以在执行gobuild命令之后不会在当前目录和goc2p项目的pkg目录中产生任何文件。
插播:Go语言的源码文件有三大类,即:命令源码文件、库源码文件和测试源码文件。他们的功用各不相同,而写法也各有各的特点。命令源码文件总是作为可执行的程序的入口。库源码文件一般用于集中放置各种待被使用的程序实体(全局常量、全局变量、接口、结构体、函数等等)。而测试源码文件主要用于对前两种源码文件中的程序实体的功能和性能进行测试。另外,后者也可以用于展现前两者中程序员的使用方法。
另外一种编译logging包的方法是:
hc@ubt:~/golang/goc2p/src$gobuildlogging
在这里,我们把代码包logging的导入路径作为参数传递给gobuild命令。另一个例子:如果我们要编译代码包cnet/ctcp,只需要在任意目录下执行命令gobuildcnet/ctcp即可。
插播:之所以这样的编译方法可以正常执行,是因为我们已经在环境变量GOPATH中加入了goc2p项目的根目录(即~/golang/goc2p/)。这时,goc2p项目的根目录就成为了一个工作区目录。只有这样,Go语言才能正确识别我们提供的goc2p项目中某个代码包的导入路径。而代码包的导入路径是指,相对于Go语言自身的源码目录(即$GOROOT/src)或我们在环境变量GOPATH中指定的某个目录的src子目录下的子路径。例如,这里的代码包logging的绝对路径是~/golang/goc2p/src/logging。而不论goc2p项目的根文件夹被放在哪儿,logging包的导入路径都是logging。显而易见,我们在称呼一个代码包的时候总是以其导入路径作为其称谓。
言归正传,除了上面的简单用法,我们还可以同时编译多个Go源码文件:
hc@ubt:~/golang/goc2p/src$gobuildlogging/base.gologging/console_logger.gologging/log_manager.gologging/tag.go
但是,使用这种方法会有一个限制。作为参数的多个Go源码文件必须在同一个目录中。也就是说,如果我们想用这种方法既编译logging包又编译basic包是不可能的。不过别担心,在需要的时候,那些被编译目标依赖的代码包会被gobuild命令自动的编译。例如,如果有一个导入路径为app的代码包,同时依赖了logging包和basic包。那么在执行gobuildapp的时候,该命令就会自动地在编译app包之前去检查logging包和basic包的编译状态。如果发现它们的编译结果文件不是最新的,那么该命令就会先去编译这两个代码包,然后再编译app包。
注意,gobuild命令在编译只包含库源码文件的代码包(或者同时编译多个代码包)时,只会做检查性的编译,而不会输出任何结果文件。
另外,gobuild命令既不能编译包含多个命令源码文件的代码包,也不能同时编译多个命令源码文件。因为,如果把多个命令源码文件作为一个整体看待,那么每个文件中的main函数就属于重名函数,在编译时会抛出重复定义错误。假如,在goc2p项目的代码包cmd(此代码包仅用于示例目的,并不会永久存在于该项目中)中包含有两个命令源码文件showds.go和initpkg_demo.go,那么我们在使用gobuild命令同时编译它们时就会失败。示例如下:
hc@ubt:~/golang/goc2p/src/cmd$gobuildshowds.goinitpkg_demo.go\n39;tloadpackage:package/home/hc/golang/goc2p/src/cnet/ctcp:import&34;:cannotimportabsolutepath
从上述示例中的命令提示信息我们可知,以目录绝对路径的形式提供代码包位置是不会被Go命令认可的。
这是由于Go认为本地代码包路径的表示只能以“./”或“../”开始,再或者直接为“.”或“..”,而代码包的代码导入路径又不允许以“/”开始。所以,这种用绝对路径表示代码包位置的方式也就不能被支持了。
上述规则适用于所有Go命令。读者可以自己尝试一下,比如在执行gobuild命令时分别以代码包导入路径、目录相对路径和目录绝对路径的形式提供代码包位置,并查看执行结果。
我们已经通过上面的示例强行的重新安装了cnet/ctcp包及其依赖包。现在我们再来看一下goc2p的项目目录:
$HOME/golang/goc2p:\nbin/\npkg/\nlinux_386/\n/cnet\nctcp.a\nlogging.a\npkgtool.a\nsrc/
我们发现在pkg目录的平台相关目录下多了一个名为cnet的目录,而在这个目录下的就是名为ctcp.a的代码包归档文件。由此我们可知,代码包归档文件的存放目录的相对路径(相对于当前工作区的pkg目录的平台相关目录)即为代码包导入路径除去最后一个元素后的路径。而代码包归档文件的名称即为代码包导入路径中的最后一个元素再加“.a”后缀。再举一个例子,如果代码包导入路径为x/y/z,则它的归档文件存放路径的相对路径即为x/y/,而这个归档文件的名称即为z.a。
回顾代码包pkgtool的归档文件的存放路径。因为它的导入路径中只有一个元素,所以其归档文件就被直接存放到了goc2p项目的pkg目录的平台相关目录下了。
此外,我们还发现pkg目录的平台相关目录下还有一个名为logging.a的文件。很显然,我们并没有显式的安装代码包logging。这是因为goinstall命令在安装指定的代码包之前,会先去安装指定代码包的依赖包。当依赖包被正确安装后,指定的代码包的安装才会开始。由于代码包cnet/ctcp依赖于goc2p项目(即当前工作区)中的代码包logging,所以当代码包logging被成功安装之后,代码包cnet/ctcp才会被安装。
还有一个问题:上述的安装过程涉及到了那么多代码包,那为什么goc2p项目的pkg目录中只包含该项目中代码包的归档文件呢?实际上,goinstall命令会把标准库中的代码包的归档文件存放到Go语言安装目录的pkg子目录中,而把指定代码包依赖的第三方项目的代码包的归档文件存放到当前工作区的pkg目录下。这样就实现了Go语言标准库代码包的归档文件与用户代码包的归档文件,以及处在不同工作区的用户代码包的归档文件之间的分离。
安装命令源码文件
除了安装代码包之外,goinstall命令还可以安装命令源码文件。为了看到安装命令源码文件是goc2p项目目录的变化,我们先把该目录还原到原始状态,即清除bin子目录和pkg子目录下的所有目录和文件。然后,我们来安装代码包helper/ds下的命令源码文件showds.go,如下:
hc@ubt:~/golang/goc2p/src$goinstallhelper/ds/showds.go\ngoinstall:noinstalllocationfor.gofileslistedoncommandline(GOBINnotset)
这次我们没能成功安装。该Go命令认为目录/home/hc/golang/goc2p/src/helper/ds不在环境GOPATH中。我们可以通过Linux的echo命令来查看一下环境变量GOPATH的值:
hc@ubt:~/golang/goc2p/src$echo$GOPATH\n/home/hc/golang/lib:/home/hc/golang/goc2p
环境变量GOPATH的值中确实包含了goc2p项目的根目录。这到底是怎么回事呢?
我通过查看Go命令的源码文件找到了其根本原因。在上一小节我们提到过,在环境变量GOPATH中包含多个工作区目录路径时,我们需要在编译命令源码文件前先对环境变量GOBIN进行设置。实际上,这个环境变量所指的目录路径就是命令程序生成的结果文件的存放目录。goinstall命令会把相应的可执行文件放置到这个目录中。
由于命令gobuild在编译库源码文件后不会产生任何结果文件,所以自然也不用会在意结果文件的存放目录。在该命令编译单一的命令源码文件或者包含一个命令源码文件和多个库源码文件时,在结果文件存放目录无效的情况下会将结果文件(也就是可执行文件)存放到执行该命令时所在的目录下。因此,即使环境变量GOBIN的值无效,我们在执行gobuild命令时也不会见到这个错误提示信息。
然而,goinstall命令中一个很重要的步骤就是将结果文件(归档文件或者可执行文件)存放到相应的目录中。所以,goinstall命令在安装命令源码文件时,如果环境变量GOBIN的值无效,则它会在最后检查结果文件存放目录的时候发现这一问题,并打印与上述示例所示内容类似的错误提示信息,最后直接退出。
这个错误提示信息在我们安装多个库源码文件时也有可能遇到。示例如下:
hc@ubt:~/golang/goc2p/src/pkgtool$goinstallenvir.gofpath.goipath.gopnode.goutil.go\ngoinstall:noinstalllocationfor.gofileslistedoncommandline(GOBINnotset)
而且,在我们为环境变量GOBIN设置了正确的值之后,这个错误提示信息仍然会出现。这是因为,只有在安装命令源码文件的时候,命令程序才会将环境变量GOBIN的值作为结果文件的存放目录。而在安装库源码文件时,在命令程序内部的代表结果文件存放目录路径的那个变量不会被赋值。最后,命令程序会发现它依然是个无效的空值。所以,命令程序会同样返回一个关于“无安装位置”的错误。这就引出一个结论,我们只能使用安装代码包的方式来安装库源码文件,而不能在goinstall命令罗列并安装它们。另外,goinstall命令目前无法接受标记-o以自定义结果文件的存放位置。这也从侧面说明了goinstall命令不支持针对库源码文件的安装操作。
至此,我们对怎样用goinstall命令来安装代码包以及命令源码文件进行了说明。如果你已经熟知了gobuild命令,那么理解这些内容应该不在话下。
goget
hc@ubt:~$gogetgithub.com/hyper-carrot/go_lib/logging
命令goget可以根据要求和实际情况从互联网上下载或更新指定的代码包及其依赖包,并对它们进行编译和安装。在上面这个示例中,我们从著名的代码托管站点Github上下载了一个项目(或称代码包),并安装到了环境变量GOPATH中包含的第一个工作区中。与此同时,我们也知道了这个代码包的导入路径就是github.com/hyper-carrot/go_lib/logging。
一般情况下,为了分离自己与第三方的代码,我们会设置两个或更多的工作区。我们现在有一个目录路径为/home/hc/golang/lib的工作区,并且它是环境变量GOPATH值中的第一个目录路径。注意,环境变量GOPATH中包含的路径不能与环境变量GOROOT的值重复。好了,如果我们使用goget命令下载和安装代码包,那么这些代码包都会被安装在上面这个工作区中。我们暂且把这个工作区叫做Lib工作区。在我们运行gogetgithub.com/hyper-carrot/go_lib/logging之后,这个代码包就应该会被保存在Lib工作的src目录下,并且已经被安装妥当,如下所示:
/home/hc/golang/lib:\nbin/\npkg/\nlinux_386/\ngithub.com/\nhyper-carrot/\ngo_lib/\nlogging.a\nsrc/\ngithub.com/\nhyper-carrot/\ngo_lib/\nlogging/\n…
另一方面,如果我们想把一个项目上传到Github网站(或其他代码托管网站)上并被其他人使用的话,那么我们就应该把这个项目当做一个代码包来看待。其实我们在之前已经提到过原因,goget命令会将项目下的所有子目录和源码文件存放到第一个工作区的src目录下,而src目录下的所有子目录都会是某个代码包导入路径的一部分或者全部。也就是说,我们应该直接在项目目录下存放子代码包和源码文件,并且直接存放在项目目录下的源码文件所声明的包名应该与该项目名相同(除非它是命令源码文件)。这样做可以让其他人使用goget命令从Github站点上下载你的项目之后直接就能使用它。
实际上,像goc2p项目这样直接以项目根目录的路径作为工作区路径的做法是不被推荐的。之所以这样做主要是想让读者更容易的理解Go语言的工程结构和工作区概念,也可以让读者看到另一种项目结构。当然,如果你的项目使用了gb这样的工具那就是另外一回事了。这样的项目的根目录就应该被视为一个工作区(但是你不必把它加入到GOPATH环境变量中)。它应该由gitclone下载到Go语言工作区之外的某处,而不是使用goget命令。
远程导入路径分析
实际上,goget命令所做的动作也被叫做代码包远程导入,而传递给该命令的作为代码包导入路径的那个参数又被叫做代码包远程导入路径。
goget命令不仅可以从像Github这样著名的代码托管站点上下载代码包,还可以从任何命令支持的代码版本控制系统(英文为VersionControlSystem,简称为VCS)检出代码包。任何代码托管站点都是通过某个或某些代码版本控制系统来提供代码上传下载服务的。所以,更严格地讲,goget命令所做的是从代码版本控制系统的远程仓库中检出/更新代码包并对其进行编译和安装。
该命令所支持的VCS的信息如下表:
表0-2goget命令支持的VCS
名称
主命令
说明
Mercurial
hg
Mercurial是一种轻量级分布式版本控制系统,采用Python语言实现,易于学习和使用,扩展性强。
Git
git
Git最开始是LinuxTorvalds为了帮助管理Linux内核开发而开发的一个开源的分布式版本控制软件。但现在已被广泛使用。它是被用来进行有效、高速的各种规模项目的版本管理。
Subversion
svn
Subversion是一个版本控制系统,也是第一个将分支概念和功能纳入到版本控制模型的系统。但相对于Git和Mercurial而言,它只算是传统版本控制系统的一员。
Bazaar
bzr
Bazaar是一个开源的分布式版本控制系统。但相比而言,用它来作为VCS的项目并不多。
goget命令在检出代码包之前必须要知道代码包远程导入路径所对应的版本控制系统和远程仓库的URL。
如果该代码包在本地工作区中已经存在,则会直接通过分析其路径来确定这几项信息。goget命令支持的几个版本控制系统都有一个共同点,那就是会在检出的项目目录中存放一个元数据目录,名称为“.”前缀加其主命令名。例如,Git会在检出的项目目录中加入一个名为“.git”的子目录。所以,这样就很容易判定代码包所用的版本控制系统。另外,又由于代码包已经存在,我们只需通过代码版本控制系统的更新命令来更新代码包,因此也就不需要知道其远程仓库的URL了。对于已存在于本地工作区的代码包,除非要求强行更新代码包,否则goget命令不会进行重复下载。如果想要强行更新代码包,可以在执行goget命令时加入-u标记。这一标记会稍后介绍。
如果本地工作区中不存在该代码包,那么就只能通过对代码包远程导入路径进行分析来获取相关信息了。首先,goget命令会对代码包远程导入路径进行静态分析。为了使分析过程更加方便快捷,goget命令程序中已经预置了几个著名代码托管网站的信息。如下表:
表0-3预置的代码托管站点的信息
名称
主域名
支持的VCS
代码包远程导入路径示例
Bitbucket
bitbucket.org
Git,Mercurial
bitbucket.org/user/projectbitbucket.org/user/project/sub/directory
GitHub
github.com
Git
github.com/user/projectgithub.com/user/project/sub/directory
GoogleCodeProjectHosting
code.google.com
Git,Mercurial,Subversion
code.google.com/p/projectcode.google.com/p/project/sub/directorycode.google.com/p/project.subrepositorycode.google.com/p/project.subrepository/sub/directory
Launchpad
launchpad.net
Bazaar
launchpad.net/projectlaunchpad.net/project/serieslaunchpad.net/project/series/sub/directorylaunchpad.net/~user/project/branchlaunchpad.net/~user/project/branch/sub/directory
IBMDevOpsServices
hub.jazz.net
Git
hub.jazz.net/git/user/projecthub.jazz.net/git/user/project/sub/directory
一般情况下,代码包远程导入路径中的第一个元素就是代码托管网站的主域名。在静态分析的时候,goget命令会将代码包远程导入路径与预置的代码托管站点的主域名进行匹配。如果匹配成功,则在对代码包远程导入路径的初步检查后返回正常的返回值或错误信息。如果匹配不成功,则会再对代码包远程导入路径进行动态分析。至于动态分析的过程,我就不在这里详细展开了。
如果对代码包远程导入路径的静态分析或/和动态分析成功并获取到对应的版本控制系统和远程仓库URL,那么goget命令就会进行代码包检出或更新的操作。随后,goget命令会在必要时以同样的方式检出或更新这个代码包的所有依赖包。
自定义代码包远程导入路径
如果你想把你编写的(被托管在不同的代码托管网站上的)代码包的远程导入路径统一起来,或者不希望让你的代码包中夹杂某个代码托管网站的域名,那么你可以选择自定义你的代码包远程导入路径。这种自定义的实现手段叫做“导入注释”。导入注释的写法示例如下:
packageanalyzer//import&34;
代码包analyzer实际上属于我的一个网络爬虫项目。这个项目的代码被托管在了Github网站上。它的网址是:https://github.com/hyper-carrot/talon。如果用标准的导入路径来下载analyzer代码包的话,命令应该这样写gogetgithub.com/hyper-carrot/talon/analyzer。不过,如果我们像上面的示例那样在该代码包中的一个源码文件中加入导入注释的话,这样下载它就行不通了。我们来看一看这个导入注释。
导入注释的写法如同一条代码包导入语句。不同的是,它出现在了单行注释符//的右边,因此Go语言编译器会忽略掉它。另外,它必须出现在源码文件的第一行语句(也就是代码包声明语句)的右边。只有符合上述这两个位置条件的导入注释才是有效的。再来看其中的引号部分。被双引号包裹的应该是一个符合导入路径语法规则的字符串。其中,hypermind.cn是我自己的一个域名。实际上,这也是用来替换掉我想隐去的代码托管网站域名及部分路径(这里是github.com/hyper-carrot)的那部分。在hypermind.cn右边的依次是我的项目的名称以及要下载的那个代码包的相对路径。这些与其标准导入路径中的内容都是一致的。为了清晰起见,我们再来做下对比。
github.com/hyper-carrot/talon/analyzer//标准的导入路径\nhypermind.cn/talon/analyzer//导入注释中的导入路径
你想用你自己的域名替换掉标准导入路径中的哪部分由你自己说了算。不过一般情况下,被替换的部分包括代码托管网站的域名以及你在那里的用户ID就可以了。这足以达到我们最开始说的那两个目的。
虽然我们在talon项目中的所有代码包中都加入了类似的导入注释,但是我们依然无法通过gogethypermind.cn/talon/analyzer命令来下载这个代码包。因为域名hypermind.cn所指向的网站并没有加入相应的处理逻辑。具体的实现步骤应该是这样的:
编写一个可处理HTTP请求的程序。这里无所谓用什么编程语言去实现。当然,我推荐你用Go语言去做。将这个处理程序与hypermind.cn/talon这个路径关联在一起,并总是在作为响应的HTML文档的头中写入下面这行内容:<metaname=&34;content=&34;>hypermind.cn/talon/analyzer熟悉HTML的读者都应该知道,这行内容会被视为HTML文档的元数据。它实际上goget命令的文档中要求的写法。它的模式是这样的:
<metaname=&34;content=&34;>
实际上,content属性中的import-prefix的位置上应该填入我们自定义的远程代码包导入路径的前缀。这个前缀应该与我们的处理程序关联的那个路径相一致。而vcs显然应该代表与版本控制系统有关的标识。还记得表0-2中的主命令列吗?这里的填入内容就应该该列中的某一项。在这里,由于talon项目使用的是Git,所以这里应该填入git。至于repo-root,它应该是与该处理程序关联的路径对应的Github网站的URL。在这里,这个路径是hypermind.cn/talon,那么这个URL就应该是https://github.com/hyper-carrot/talon。后者也是talon项目的实际网址。
好了,在我们做好上述处理程序之后,gogethypermind.cn/talon/analyzer命令的执行结果就会是正确的。analyzer代码包及其依赖包中的代码会被下载到GOPATH环境变量中的第一个工作区目录的src子目录中,然后被编译并安装。
注意,具体的代码包源码存放路径会是/home/hc/golang/lib/src/hypermind.cn/talon/analyzer。也就是说,存放路径(包括代码包源码文件以及相应的归档文件的存放路径)会遵循导入注释中的路径(这里是hypermind.cn/talon/analyzer),而不是原始的导入路径(这里是github.com/hyper-carrot/talon/analyzer)。另外,我们只需在talon项目的每个代码包中的某一个源码文件中加入导入注释,但这些导入注释中的路径都必须是一致的。在这之后,我们就只能使用hypermind.cn/talon/作为talon项目中的代码包的导入路径前缀了。一个反例如下:
hc@ubt:~$gogetgithub.com/hyper-carrot/talon/analyzer\npackagegithub.com/hyper-carrot/talon/analyzer:codeindirectory/home/hc/golang/lib/src/github.com/hyper-carrot/talon/analyzerexpectsimport&34;
与自定义的代码包远程导入路径有关的内容我们就介绍到这里。从中我们也可以看出,Go语言为了让使用者的项目与代码托管网站隔离所作出的努力。只要你有自己的网站和一个不错的域名,这就很容易搞定并且非常值得。这会在你的代码包的使用者面前强化你的品牌,而不是某个代码托管网站的。当然,使你的代码包导入路径整齐划一是最直接的好处。
OK,言归正传,我下面继续关注goget这个命令本身。
命令特有标记
goget命令可以接受所有可用于gobuild命令和goinstall命令的标记。这是因为goget命令的内部步骤中完全包含了编译和安装这两个动作。另外,goget命令还有一些特有的标记,如下表所示:
表0-4goget命令的特有标记说明
标记名称
标记描述
-d
让命令程序只执行下载动作,而不执行安装动作。
-f
仅在使用-u标记时才有效。该标记会让命令程序忽略掉对已下载代码包的导入路径的检查。如果下载并安装的代码包所属的项目是你从别人那里Fork过来的,那么这样做就尤为重要了。
-fix
让命令程序在下载代码包后先执行修正动作,而后再进行编译和安装。
-insecure
允许命令程序使用非安全的scheme(如HTTP)去下载指定的代码包。如果你用的代码仓库(如公司内部的Gitlab)没有HTTPS支持,可以添加此标记。请在确定安全的情况下使用它。
-t
让命令程序同时下载并安装指定的代码包中的测试源码文件中依赖的代码包。
-u
让命令利用网络来更新已有代码包及其依赖包。默认情况下,该命令只会从网络上下载本地不存在的代码包,而不会更新已有的代码包。
为了更好的理解这几个特有标记,我们先清除Lib工作区的src目录和pkg目录中的所有子目录和文件。现在我们使用带有-d标记的goget命令来下载同样的代码包:
hc@ubt:~$goget-dgithub.com/hyper-carrot/go_lib/logging
现在,让我们再来看一下Lib工作区的目录结构:
/home/hc/golang/lib:\nbin/\npkg/\nsrc/\ngithub.com/\nhyper-carrot/\ngo_lib/\nlogging/\n…
我们可以看到,goget命令只将代码包下载到了Lib工作区的src目录,而没有进行后续的编译和安装动作。这个加入-d标记的结果。
再来看-fix标记。我们知道,绝大多数计算机编程语言在进行升级和演进过程中,不可能保证100%的向后兼容(BackwardCompatibility)。在计算机世界中,向后兼容是指在一个程序或者代码库在更新到较新的版本后,用旧的版本程序创建的软件和系统仍能被正常操作或使用,或在旧版本的代码库的基础上编写的程序仍能正常编译运行的能力。Go语言的开发者们已想到了这点,并提供了官方的代码升级工具——fix。fix工具可以修复因Go语言规范变更而造成的语法级别的错误。关于fix工具,我们将放在本节的稍后位置予以说明。
假设我们本机安装的Go语言版本是1.5,但我们的程序需要用到一个很早之前用Go语言的0.9版本开发的代码包。那么我们在使用goget命令的时候可以加入-fix标记。这个标记的作用是在检出代码包之后,先对该代码包中不符合Go语言1.5版本的语言规范的语法进行修正,然后再下载它的依赖包,最后再对它们进行编译和安装。
标记-u的意图和执行的动作都比较简单。我们在执行goget命令时加入-u标记就意味着,如果在本地工作区中已存在相关的代码包,那么就是用对应的代码版本控制系统的更新命令更新它,并进行编译和安装。这相当于强行更新指定的代码包及其依赖包。我们来看如下示例:
hc@ubt:~$goget-vgithub.com/hyper-carrot/go_lib/logging
因为我们在之前已经检出并安装了这个代码包,所以我们执行上面这条命令后什么也没发生。还记得加入标记-v标记意味着会打印出被构建的代码包的名字吗?现在我们使用标记-u来强行更新代码包:
hc@ubt:~$goget-v-ugithub.com/hyper-carrot/go_lib/logging\ngithub.com/hyper-carrot/go_lib(download)
其中,“(download)”后缀意味着命令从远程仓库检出或更新了该行显示的代码包。如果我们要查看附带-u的goget命令到底做了些什么,还可以加上一个-x标记,以打印出用到的命令。读者可以自己试用一下它。
智能的下载
命令goget还有一个很值得称道的功能。在使用它检出或更新代码包之后,它会寻找与本地已安装Go语言的版本号相对应的标签(tag)或分支(branch)。比如,本机安装Go语言的版本是1.x,那么goget命令会在该代码包的远程仓库中寻找名为“go1”的标签或者分支。如果找到指定的标签或者分支,则将本地代码包的版本切换到此标签或者分支。如果没有找到指定的标签或者分支,则将本地代码包的版本切换到主干的最新版本。
前面我们说在执行goget命令时也可以加入-x标记,这样可以看到goget命令执行过程中所使用的所有命令。不知道读者是否已经自己尝试了。下面我们还是以代码包github.com/hyper-carrot/go_lib为例,并且通过之前示例中的命令的执行此代码包已经被检出到本地。这时我们再次更新这个代码包:
hc@ubt:~$goget-v-u-xgithub.com/hyper-carrot/go_lib\ngithub.com/hyper-carrot/go_lib(download)\ncd/home/hc/golang/lib/src/github.com/hyper-carrot/go_lib\ngitfetch\ncd/home/hc/golang/lib/src/github.com/hyper-carrot/go_lib\ngitshow-ref\ncd/home/hc/golang/lib/src/github.com/hyper-carrot/go_lib\ngitcheckoutorigin/master\nWORK=/tmp/go-build034263530
在上述示例中,goget命令通过gitfetch命令将所有远程分支更新到本地,而后有用gitshow-ref命令列出本地和远程仓库中记录的代码包的所有分支和标签。最后,当确定没有名为“go1”的标签或者分支后,goget命令使用gitcheckoutorigin/master命令将代码包的版本切换到主干的最新版本。下面,我们在本地增加一个名为“go1”的标签,看看goget命令的执行过程又会发生什么改变:
hc@ubt:~$cd~/golang/lib/src/github.com/hyper-carrot/go_lib\nhc@ubt:~/golang/lib/src/github.com/hyper-carrot/go_lib$gittaggo1\nhc@ubt:~$goget-v-u-xgithub.com/hyper-carrot/go_lib\ngithub.com/hyper-carrot/go_lib(download)\ncd/home/hc/golang/lib/src/github.com/hyper-carrot/go_lib\ngitfetch\ncd/home/hc/golang/lib/src/github.com/hyper-carrot/go_lib\ngitshow-ref\ncd/home/hc/golang/lib/src/github.com/hyper-carrot/go_lib\ngitshow-reftags/go1origin/go1\ncd/home/hc/golang/lib/src/github.com/hyper-carrot/go_lib\ngitcheckouttags/go1\nWORK=/tmp/go-build636338114
将这两个示例进行对比,我们会很容易发现它们之间的区别。第二个示例的命令执行过程中使用gitshow-ref查看所有分支和标签,当发现有匹配的信息又通过gitshow-reftags/go1origin/go1命令进行精确查找,在确认无误后将本地代码包的版本切换到标签“go1”之上。
命令goget的这一功能是非常有用的。我们的代码在直接或间接依赖某些同时针对多个Go语言版本开发的代码包时,可以自动的检出其正确的版本。也可以说,goget命令内置了一定的代码包多版本依赖管理的功能。
到这里,我向大家介绍了goget命令的使用方式。goget命令与之前介绍的两个命令一样,是我们编写Go语言程序、构建Go语言项目时必不可少的辅助工具。
goclean
执行goclean命令会删除掉执行其它命令时产生的一些文件和目录,包括:
在使用gobuild命令时在当前代码包下生成的与包名同名或者与Go源码文件同名的可执行文件。在Windows下,则是与包名同名或者Go源码文件同名且带有“.exe”后缀的文件。在执行gotest命令并加入-c标记时在当前代码包下生成的以包名加“.test”后缀为名的文件。在Windows下,则是以包名加“.test.exe”后缀为名的文件。我们会在后面专门介绍gotest命令。如果执行goclean命令时带有标记-i,则会同时删除安装当前代码包时所产生的结果文件。如果当前代码包中只包含库源码文件,则结果文件指的就是在工作区的pkg目录的相应目录下的归档文件。如果当前代码包中只包含一个命令源码文件,则结果文件指的就是在工作区的bin目录下的可执行文件。还有一些目录和文件是在编译Go或C源码文件时留在相应目录中的。包括:“_obj”和“_test”目录,名称为“_testmain.go”、“test.out”、“build.out”或“a.out”的文件,名称以“.5”、“.6”、“.8”、“.a”、“.o”或“.so”为后缀的文件。这些目录和文件是在执行gobuild命令时生成在临时目录中的。如果你忘记了这个临时目录是怎么回事儿,可以再回顾一下前面关于gobuild命令的介绍。临时目录的名称以go-build为前缀。如果执行goclean命令时带有标记-r,则还包括当前代码包的所有依赖包的上述目录和文件。
我们再以goc2p项目的logging为例。为了能够反复体现每个标记的作用,我们会使用标记n。使用标记-n会让命令在执行过程中打印用到的系统命令,但不会真正执行它们。如果想既打印命令又执行命令则需使用标记-x。现在我们来试用一下goclean命令:
hc@ubt:~/golang/goc2p/src$goclean-xlogging\ncd/home/hc/golang/goc2p/src/logging\nrm-flogginglogging.exelogging.testlogging.test.exe
现在,我们加上标记-i:
hc@ubt:~/golang/goc2p/src$goclean-x-ilogging\ncd/home/hc/golang/goc2p/src/logging\nrm-flogginglogging.exelogging.testlogging.test.exe\nrm-f/home/hc/golang/goc2p/pkg/linux_386/logging.a
如果再加上标记-r又会打印出哪些命令呢?请读者自己试一试吧。
godoc与godoc
godoc
godoc命令可以打印附于Go语言程序实体上的文档。我们可以通过把程序实体的标识符作为该命令的参数来达到查看其文档的目的。
插播:所谓Go语言的程序实体,是指变量、常量、函数、结构体以及接口。而程序实体的标识符即是代表它们的名称。标识符又分非限定标识符和限定标识符。其中,限定标识符一般用于表示某个代码包中的程序实体或者某个结构体类型中的方法或字段。例如,标准库代码包io中的名为EOF的变量用限定标识符表示即io.EOF。又例如,如果我有一个sync.WaitGroup类型的变量wg并且想调用它的Add方法,那么可以这样写wg.Add()。其中,wg.Add就是一个限定标识符,而后面的()则代表了调用操作。
下面说明怎样使用godoc命令。先来看一下godoc命令可接受的标记。
表0-5godoc命令的标记说明
标记名称
标记描述
-c
加入此标记后会使godoc命令区分参数中字母的大小写。默认情况下,命令是大小写不敏感的。
-cmd
加入此标记后会使godoc命令同时打印出main包中的可导出的程序实体(其名称的首字母大写)的文档。默认情况下,这部分文档是不会被打印出来的。
-u
加入此标记后会使godoc命令同时打印出不可导出的程序实体(其名称的首字母小写)的文档。默认情况下,这部分文档是不会被打印出来的。
这几个标记的意图都非常简单和明确,大家可以根据实际情况选用。
godoc命令可以后跟一个或两个参数。当然,我们也可以不附加任务参数。如果不附加参数,那么godoc命令会试图打印出当前目录所代表的代码包的文档及其中的包级程序实体的列表。
例如,我要在goc2p项目的loadgen代码包所在目录中运行godoc命令的话,那么就会是这样:
hc@ubt:~/golang/goc2p/src/loadgen$godoc\npackageloadgen//import&34;\n\nfuncNewGenerator(\ncallerlib.Caller,\ntimeoutNstime.Duration,\nlpsuint32,\ndurationNstime.Duration,\nresultChchan*lib.CallResult)(lib.Generator,error)
如果你需要指定代码包或程序实体,那么就需要在godoc命令后附上参数了。例如,只要我本地的goc2p项目的所在目录存在于GOPATH环境变量中,我就可以在任意目录中敲入godocloadgen。如此得到的输出一定是与上面那个示例一致的。
看过loadgen代码包中源码的读者会知道,其中只有一个可导出的程序实体,即NewGenerator函数。这也是上述示例中如此输出的原因。该代码包中的结构体类型myGenerator是不可导出,但是我们只需附加-u标记便可查看它的文档了:
hc@ubt:~$godoc-uloadgen.myGenerator\ntypemyGeneratorstruct{\ncallerlib.Caller//调用器。\ntimeoutNstime.Duration//处理超时时间,单位:纳秒。\nlpsuint32//每秒载荷量。\ndurationNstime.Duration//负载持续时间,单位:纳秒。\nconcurrencyuint32//并发量。\nticketslib.GoTickets//Goroutine票池。\nstopSignchanbyte//停止信号的传递通道。\ncancelSignbyte//取消发送后续结果的信号。\nendSignchanuint64//完结信号的传递通道,同时被用于传递调用执行计数。\ncallCountuint64//调用执行计数。\nstatuslib.GenStatus//状态。\nresultChchan*lib.CallResult//调用结果通道。\n}\n\n载荷发生器的实现。\n\nfunc(gen*myGenerator)Start()\nfunc(gen*myGenerator)Status()lib.GenStatus\nfunc(gen*myGenerator)Stop()(uint64,bool)\nfunc(gen*myGenerator)asyncCall()\nfunc(gen*myGenerator)genLoad(throttle<-chantime.Time)\nfunc(gen*myGenerator)handleStopSign(callCountuint64)\nfunc(gen*myGenerator)init()error\nfunc(gen*myGenerator)interact(rawReq*lib.RawReq)*lib.RawResp\nfunc(gen*myGenerator)sendResult(result*lib.CallResult)bool
如此一来,loadgen.myGenerator类型的文档、字段和方法都尽收眼底。注意,这里我们使用到了限定标识符。下面再进一步,如果你只想查看loadgen.myGenerator类型的init方法的文档,那么只要续写这个限定标识符就可以了,像这样:
hc@ubt:~$godoc-uloadgen.myGenerator.init\nfunc(gen*myGenerator)init()error\n\n初始化载荷发生器。
注意,结构体类型中的字段的文档是无法被单独打印的。另外,godoc命令根据参数查找代码包或程序实体的顺序是:先Go语言根目录(即GOROOT所环境变量指定的那个目录)后工作区目录(即GOPATH环境变量包含的那些目录)。并且,在前者或后者中,godoc命令的查找顺序遵循字典序。因此,如果某个工作区目录中的代码包与标准库中的包重名了,那么它是无法被打印出来的。godoc命令只会打印出第一个匹配的代码包或程序实体的文档。
我们在前面说过,godoc命令还可以接受两个参数。这是一种更加精细的指定代码包或程序实体的方式。一个显著的区别是,如果你想打印标准库代码包net/http中的结构体类型Request的文档,那么可以这样敲入godoc命令:
godochttp.Request
注意,这里并没有写入net/http代码包的导入路径,而只是写入了其中的最后一个元素http。但是如果你把http.Request拆成两个参数(即httpRequest)的话,命令程序就会什么也查不到了。因为这与前一种用法的解析方式是不一样的。正确的做法是,当你指定两个参数时,作为第一个参数的代码包名称必须是完整的导入路径,即:在敲入命令godocnet/httpRequest后,你会得到想要的结果。
最后,在给定两个参数时,godoc会打印出所有匹配的文档,而不是像给定一个参数时那样只打印出第一个匹配的文档。这对于查找只有大小写不同的多个方法(如New和new)的文档来说非常有用。
godoc
命令godoc是一个很强大的工具,同样用于展示指定代码包的文档。在Go语言的1.5版本中,它是一个内置的标准命令。
该命令有两种模式可供选择。如果在执行命令时不加入-http标记,则该命令就以命令行模式运行。在打印纯文本格式的文档到标准输出后,命令执行就结束了。比如,我们用命令行模式查看代码包fmt的文档:
hc@ubt:~$godocfmt
为了节省篇幅,我们在这里略去了文档查询结果。读者可以自己运行一下上述命令。在该命令被执行之后,我们就可以看到编排整齐有序的文档内容了。这包括代码包fmt及其中所有可导出的包级程序实体的声明、文档和例子。
有时候我们只是想查看某一个函数或者结构体类型的文档,那么我们可以将这个函数或者结构体的名称加入命令的后面,像这样:
hc@ubt:~$godocfmtPrintf
或者:
hc@ubt:~$godocosFile
如果我们想同时查看一个代码包中的几个函数的文档,则仅需将函数或者结构体名称追加到命令后面。比如我们要查看代码包fmt中函数Printf和函数Println的文档:
hc@ubt:~$godocfmtPrintfPrintln
如果我们不但想在文档中查看可导出的程序实体的声明,还想看到它们的源码,那么我们可以在执行godoc命令的时候加入标记-src,比如这样:
hc@ubt:~$godoc-srcfmtPrintf
Go语言为程序使用示例代码设立了专有的规则。我们在这里暂不讨论这个规则的细节。只需要知道正因为有了这个专有规则,使得godoc命令可以根据这些规则提取相应的示例代码并把它们加入到对应的文档中。如果我们想在查看代码包net中的结构体类型Listener的文档的同时查看关于它的示例代码,那么我们只需要在执行命令时加入标记-ex。使用方法如下:
hc@ubt:~$godoc-exnet/httpFileServer
注意,我们在使用godoc命令时,只能把代码包和程序实体的标识符拆成两个参数。也就是说,godoc命令不支持前文所述的godoc命令的单参数用法。
在实际的Go语言环境中,我们可能会遇到一个命令源码文件所产生的可执行文件与代码包重名的情况。比如,这里介绍的标准命令go和官方代码包go。现在我们要明确的告诉godoc命令要查看可执行文件go的文档,我们需要在名称前加入cmd/前缀:
hc@ubt:~$godoccmd/go
另外,如果我们想查看HTML格式的文档,就需要加入标记-html。当然,这样在命令行模式下的查看效果是很差的。但是,如果仔细查看的话,可以在其中找到一些相应源码的链接地址。
一般情况下,godoc命令会去Go语言根目录和环境变量GOPATH包含的工作区目录中查找代码包。我们可以通过加入标记-goroot来制定一个Go语言根目录。这个被指定的Go语言根目录仅被用于当次命令的执行。示例如下:
hc@ubt:~$godoc-goroot=&34;fmt
现在让我们来看看另外一种模式。如果我们在执行命令时加上-http标记则会启用另一模式。这种模式被叫做Web服务器模式,它以Web页面的形式提供Go语言文档。
我们使用如下命令启动这个文档Web服务器:
hc@ubt:~/golang/goc2p$godoc-http=:6060
标记-http的值:6060表示启动的Web服务器使用本机的6060端口。之后,我们就可以通过在网络浏览器的地址栏中输入http://localhost:6060来查看以网页方式展现的Go文档了。
图0-1本机的Go文档Web服务首页
这与Go语言官方站点的Web服务页面如出一辙。这使得我们在不方便访问Go语言官方站点的情况下也可以查看Go语言文档。并且,更便利的是,通过本机的Go文档Web服务,我们还可以查看所有本机工作区下的代码的文档。比如,goc2p项目中的代码包pkgtool的页面如下图:
图0-2goc2p项目中的pkgtool包的Go文档页面
现在,我们在本机开启Go文档Web服务器,端口为9090。命令如下:
hc@ubt:~$godoc-http=:9090-index
注意,要使用-index标记开启搜索索引。这个索引会在服务器启动时创建并维护。如果不加入此标记,那么无论在Web页面还是命令行终端中都是无法进行查询操作的。
索引中提供了标识符和全文本搜索信息(通过正则表达式为可搜索性提供支持)。全文本搜索结果显示条目的最大数量可以通过标记-maxresults提供。标记-maxresults默认值是10000。如果不想提供如此多的结果条目,可以设置小一些的值。甚至,如果不想提供全文本搜索结果,可以将标记-maxresults的值设置为0,这样服务器就只会创建标识符索引,而根本不会创建全文本搜索索引了。标识符索引即为对程序实体名称的索引。
正因为在使用了-index标记的情况下文档服务器会在启动时创建索引,所以在文档服务器启动之后还不能立即提供搜索服务,需要稍等片刻。在索引为被创建完毕之前,我们的搜索操作都会得到提示信息“Indexinginprogress:resultmaybeinaccurate”。
如果我们在本机用godoc命令启动了Go文档Web服务器,且IP地址为192.168.1.4、端口为9090,那么我们就可以在另一个命令行终端甚至另一台能够与本机联通的计算机中通过如下命令进行查询了。查询命令如下:
hc@ubt:~$godoc-q-server=&34;Listener
命令的最后为要查询的内容,可以是任何你想搜索的字符串,而不仅限于代码包、函数或者结构体的名称。
标记-q开启了远程查询的功能。而标记-server=&34;则指明了远程文档服务器的IP地址和端口号。实际上,如果不指明远程查询服务器的地址,那么该命令会自行将地址“:6060”和“golang.org”作为远程查询服务器的地址。这两个地址即是默认的本机文档Web站点地址和官方的文档Web站点地址。所以执行如下命令我们也可以查询到标准库的信息:
hc@ubt:~$godoc-qfmt
命令godoc还有很多可用的标记,但在通常情况下并不常用。读者如果有兴趣,可以在命令行环境下敲入godoc并查看其文档。
gorun
gorun命令可以编译并运行命令源码文件。由于它其中包含了编译动作,因此它也可以接受所有可用于gobuild命令的标记。除了标记之外,gorun命令只接受Go源码文件作为参数,而不接受代码包。与gobuild命令和goinstall命令一样,gorun命令也不允许多个命令源码文件作为参数,即使它们在同一个代码包中也是如此。而原因也是一致的,多个命令源码文件会都有main函数声明。
如果命令源码文件可以接受参数,那么在使用gorun命令运行它的时候就可以把它的参数放在它的文件名后面,像这样:
hc@ubt:~/golang/goc2p/src/helper/ds$gorunshowds.go-p~/golang/goc2p
在上面的示例中,我们使用gorun命令运行命令源码文件showds.go。这个命令源码文件可以接受一个名称为“p”的参数。我们用“-p”这种形式表示“p”是一个参数名而不是参数值。它与源码文件名之间需要用空格隔开。参数值会放在参数名的后面,两者成对出现。它们之间也要用空格隔开。如果有第二个参数,那么第二个参数的参数名与第一个参数的参数值之间也要有一个空格。以此类推。
gorun命令只能接受一个命令源码文件以及若干个库源码文件(必须同属于main包)作为文件参数,且不能接受测试源码文件。它在执行时会检查源码文件的类型。如果参数中有多个或者没有命令源码文件,那么gorun命令就只会打印错误提示信息并退出,而不会继续执行。
在通过参数检查后,gorun命令会将编译参数中的命令源码文件,并把编译后的可执行文件存放到临时工作目录中。
编译和运行过程
为了更直观的体现出gorun命令中的操作步骤,我们在执行命令时加入标记-n,用于打印相关命令而不实际执行。现在让我们来模拟运行goc2p项目中的代码包helper/ds的命令源码文件showds.go。示例如下:
hc@ubt:~/golang/goc2p/src/helper/ds$gorun-nshowds.go\n\ncommand-line-arguments\n”开始的是注释信息。我们看到信息中有三行注释信息,并在中间行出现了内容“command-line-arguments”。我们在讲gobuild命令的时候说过,编译命令在分析参数的时候如果发现第一个参数是Go源码文件而不是代码包时,会在内部生成一个名为“command-line-arguments”的虚拟代码包。所以这里的注释信息就是要告诉我们下面的几行信息是关于虚拟代码包“command-line-arguments”的。
打印信息中的“$WORK”表示临时工作目录的绝对路径。为了存放对虚拟代码包“command-line-arguments”的编译结果,命令在临时工作目录中创建了名为command-line-arguments的子目录,并在其下又创建了_obj子目录和_obj/exe子目录。
然后,命令程序使用Go语言工具目录compile命令对命令源码文件showds.go进行了编译,并把结果文件存放到了$WORK目录下,名为command-line-arguments.a。其中,compile是Go语言自带的编程工具。
在编译成功之后,命令程序使用链接命令link生成最终的可执行文件,并将其存于$WORK/command-line-arguments/_obj/exe/目录中。打印信息中的最后一行表示,命令运行了生成的可执行文件。
通过对这些打印出来的命令的解读,我们了解了临时工作目录的用途以和内容。
在上面的示例中,我们只是让gorun命令打印出运行命令源码文件showds.go过程中需要执行的命令,而没有真正运行它。如果我们想真正运行命令源码文件showds.go并且想知道临时工作目录的位置,就需要去掉标记-n并且加上标记-work。当然,如果依然想看到过程中执行的命令,可以加上标记-x。如果读者已经看过之前我们对gobuild命令的介绍,就应该知道标记-x与标记-n一样会打印出过程执行的命令,但不同的这些命令会被真正的执行。调整这些标记之后的命令就像这样:
hc@ubt:~/golang/goc2p/src/helper/ds$gorun-x-workshowds.go
当命令真正执行后,临时工作目录中就会出现实实在在的内容了,像这样:
/tmp/go-build604555989:\ncommand-line-arguments/\n_obj/\nexe/\nshowds\ncommand-line-arguments.a
由于上述命令中包含了-work标记,所以我们可以从其输出中找到实际的工作目录(这里是/tmp/go-build604555989)。有意思的是,我们恰恰可以通过运行命令源码文件showds.go来查看这个临时工作目录的目录树:
hc@ubt:~/golang/goc2p/src/helper/ds$gorunshowds.go-p/tmp/go-build604555989
读者可以自己试一试。
我们在前面介绍过,命令源码文件如果可以接受参数,则可以在执行gorun命令运行这个命令源码文件时把参数名和参数值成对的追加在后面。实际上,如果在命令后追加参数,那么在最后执行生成的可执行文件的时候也会追加一致的参数。例如,如果这样执行命令:
hc@ubt:~/golang/goc2p/src/helper/ds$gorun-nshowds.go-p~/golang/goc2p
那么带-x或-n标记的命令程序打印的最后一个命令就是:
$WORK/command-line-arguments/_obj/exe/showds-p/home/hc/golang/goc2p
可见,gorun命令会把追加到命令源码文件后面的参数原封不动的传给对应的可执行文件。
以上简要展示了一个命令源码文件从编译到运行的全过程。请记住,gorun命令包含了两个动作:编译命令源码文件和运行对应的可执行文件。
gotest
gotest命令用于对Go语言编写的程序进行测试。这种测试是以代码包为单位的。当然,这还需要测试源码文件的帮助。关于怎样编写并写好Go程序测试代码,我们会在本章的第二节加以详述。在这里,我们只讨论怎样使用命令启动测试。
gotest命令会自动测试每一个指定的代码包。当然,前提是指定的代码包中存在测试源码文件。关于测试源码文件方面的知识,在我的图书《Go并发编程实战》中有详细介绍。测试源码文件是名称以“_test.go”为后缀的、内含若干测试函数的源码文件。测试函数一般是以“Test”为名称前缀并有一个类型为“testing.T”的参数声明的函数.
现在,我们来测试goc2p项目中的几个代码包。在使用gotest命令时指定代码包的方式与其他命令无异——使用代码包导入路径。如果需要测试多个代码包,则需要在它们的导入路径之间加入空格以进行分隔。示例如下:
hc@ubt:~$gotestbasiccnet/ctcppkgtool\nokbasic0.012s\nokcnet/ctcp2.014s\nokpkgtool0.014s
gotest命令在执行完所有的代码包中的测试文件之后,会以代码包为单位打印出测试概要信息。在上面的示例中,对应三个代码包的三行信息的第一列都是“ok”。这说明它们都通过了测试。每行的第三列显示运行相应测试所用的时间,以秒为单位。我们还可以在代码包目录下运行不加任何参数的运行gotest命令。其作用和结果与上面的示例是一样的。
另外,我们还可以指定测试源码文件来进行测试。这样的话,gotest命令只会执行指定文件中的测试,像这样:
hc@ubt:~/golang/goc2p/src/pkgtool$gotestenvir_test.go\n39;/usr/local/go/src/pkg&39;tloadpackage:packagecnet:nobuildableGosourcefilesin/home/hc/golang/goc2p/src/cnet/\npkgtool
这时,golist命令报告了一个错误——代码包cnet对应的目录下没有Go源码文件。但是命令还是把代码包pkgtool的导入路径打印出来了。然而,当我们在执行golist命令并加入标记-e时,即使参数中包含有不完整的代码包,命令也不会提示错误。示例如下:
hc@ubt:~$golist-ecnetpkgtool\ncnet\npkgtool
标记-e的作用是以容错模式加载和分析指定的代码包。在这种情况下,命令程序如果在加载或分析的过程中遇到错误只会在内部记录一下,而不会直接把错误信息打印出来。我们为了看到错误信息可以使用-json标记。这个标记的作用是把代码包的结构体实例用JSON的样式打印出来。
这里解释一下,JSON的全称是JavascriptObjectNotation。它一种轻量级的承载数据的格式。JSON的优势在于语法简单、短小精悍,且非常易于处理。JSON还是一种纯文本格式,独立于编程语言。正因为如此,得到了绝大多数编程语言和浏览器的支持,应用非常广泛。Go语言当然也不例外,在它的标准库中有专门用于处理和转换JSON格式的数据的代码包encoding/json。关于JSON格式的具体内容,读者可以去它的官方网站查看说明。
在了解了这些基本概念之后,我们来试用一下-json标记。示例如下:
hc@ubt:~$golist-e-jsoncnet\n{\n&34;:&34;,\n&34;:&34;,\n&34;:true,\n&34;:&34;,\n&34;:true,\n&34;:{\n&34;:[\n&34;\n],\n&34;:&34;,\n&34;:&34;\n}\n}
在上述JSON格式的代码包信息中,对于结构体中的字段的显示是不完整的。因为命令程序认为我们指定cnet就是不完整的。在名为Error的字段中,我们可以看到具体说明。Error字段的内容其实也是一个结构体。在JSON格式下,这种嵌套的结构体被完美的展现了出来。Error字段所指代的结构体实例的Err字段说明了cnet不完整的原因。这与我们在没有使用-e标记的情况下所打印出来的错误提示信息是一致的。我们再来看Incomplete字段。它的值为true。这同样说明cnet是一个不完整的代码包。
实际上,在从这个代码包结构体实例到JSON格式文本的转换过程中,所有的值为其类型的空值的字段都已经被忽略了。
现在我们使用带-json标记的golist命令列出代码包cnet/ctcp的信息:
hc@ubt:~$golist-jsoncnet/ctcp\n{\n&34;:&34;,\n&34;:&34;,\n&34;:&34;,\n&34;:&34;,\n&34;:true,\n&34;:&34;,\n&34;:[\n&34;,\n&34;\n],\n&34;:[\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;\n],\n&34;:[\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;\n],\n&34;:[\n&34;\n],\n&34;:[\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;,\n&34;\n]\n}
由于cnet/ctcp包是一个完整有效的代码包,所以我们不使用-e标记也是没有问题的。在上面打印的cnet/ctcp包的信息中没有Incomplete字段。这是因为完整的代码包中的Incomplete字段的其类型的空值false。它已经在转换过程中被忽略掉了。另外,在cnet/ctcp包的信息中我们看到了很多其它的字段。现在我就来看看在Go命令程序中的代码包结构体都有哪些公开的字段。如下表。
表0-7代码表结构体中的基本字段
字段名称
字段类型
字段描述
Dir
字符串(string)
代码包对应的目录。
ImportPath
字符串(string)
代码包的导入路径。
ImportComment
字符串(string)
代码包声明语句右边的用于自定义导入路径的注释。
Name
字符串(string)
代码包的名称。
Doc
字符串(string)
代码包的文档字符串。
Target
字符串(string)
代码包的安装路径。
Shlib
字符串(string)
包含该代码包的共享库(sharedlibrary)的名称。
Goroot
布尔(bool)
该代码包是否在Go语言安装目录下。
Standard
布尔(bool)
该代码包是否属于标准库的一部分。
Stale
布尔(bool)
该代码包的最新代码是否未被安装。
Root
字符串(string)
该代码包所属的工作区或Go安装目录的路径。
表0-8代码包结构体中与源码文件有关的字段
字段名称
字段类型
字段描述
GoFiles
字符串切片([]string)
Go源码文件的列表。不包含导入了代码包“C”的源码文件和测试源码文件。
CgoFiles
字符串切片([]string)
导入了代码包“C”的源码文件的列表。
IgnoredGoFiles
字符串切片([]string)
忽略编译的源码文件的列表。
CFiles
字符串切片([]string)
名称中有“.c”后缀的源码文件的列表。
CXXFiles
字符串切片([]string)
名称中有“.cc”、“.cxx”或“.cpp”后缀的源码文件的列表。
MFiles
字符串切片([]string)
名称中“.m”后缀的源码文件的列表。
HFiles
字符串切片([]string)
名称中有“.h”后缀的源码文件的列表。
SFiles
字符串切片([]string)
名称中有“.s”后缀的源码文件的列表。
SwigFiles
字符串切片([]string)
名称中有“.swig”后缀的文件的列表。
SwigCXXFiles
字符串切片([]string)
名称中有“.swigcxx”后缀的文件的列表。
SysoFiles
字符串切片([]string)
名称中有“.syso”后缀的文件的列表。这些文件是需要被加入到归档文件中的。
表0-9代码包结构体中与Cgo指令有关的字段
字段名称
字段类型
字段描述
CgoCFLAGS
字符串切片([]string)
需要传递给C编译器的标记的列表。针对Cgo。
CgoCPPFLAGS
字符串切片([]string)
需要传递给C预处理器的标记的列表。针对Cgo。
CgoCXXFLAGS
字符串切片([]string)
需要传递给C++编译器的标记的列表。针对Cgo。
CgoLDFLAGS
字符串切片([]string)
需要传递给链接器的标记的列表。针对Cgo。
CgoPkgConfig
字符串切片([]string)
pkg-config的名称的列表。针对Cgo。
表0-10代码包结构体中与依赖信息有关的字段
字段名称
字段类型
字段描述
Imports
字符串切片([]string)
该代码包中的源码文件显式导入的依赖包的导入路径的列表。
Deps
字符串切片([]string)
所有的依赖包(包括间接依赖)的导入路径的列表。
表0-11代码包结构体中与错误信息有关的字段
字段名称
字段类型
字段描述
Incomplete
布尔(bool)
代码包是否是完整的,也即在载入或分析代码包及其依赖包时是否有错误发生。
Error
*PackageError类型
载入或分析代码包时发生的错误。
DepsErrors
[]*PackageError类型
载入或分析依赖包时发生的错误。
表0-12代码包结构体中与测试源码文件有关的字段
字段名称
字段类型
字段描述
TestGoFiles
字符串切片([]string)
代码包中的测试源码文件的名称列表。
TestImports
字符串切片([]string)
代码包中的测试源码文件显示导入的依赖包的导入路径的列表。
XTestGoFiles
字符串切片([]string)
代码包中的外部测试源码文件的名称列表。
XTestImports
字符串切片([]string)
代码包中的外部测试源码文件显示导入的依赖包的导入路径的列表。
代码包结构体中定义的字段很多,但有些时候我们只需要查看其中的一些字段。那要怎么做呢?标记-f可以满足这个需求。比如这样:
hc@ubt:~$golist-f{{.ImportPath}}cnet/ctcp\ncnet/ctcp
实际上,-f标记的默认值就是{{.ImportPath}}。这也正是我们在使用不加任何标记的golist命令时依然能看到指定代码包的导入路径的原因了。
标记-f的值需要满足标准库的代码包`text/template中定义的语法。比如,{{.S}}代表根结构体的S字段的值。在golist命令的场景下,这个根结构体就是指定的代码包所对应的结构体。如果S字段的值也是一个结构体的话,那么{{.S.F}}就代表根结构体的S字段的值中的F字段的值。如果我们要查看cnet/ctcp包中的命令源码文件和库源码文件的列表,可以这样使用-f标记:
hc@ubt:~$golist-f{{.GoFiles}}cnet/ctcp\n[base.gotcp.go]
如果我们想查看不完整的代码包cnet的错误提示信息,还可以这样:
hc@ubt:~$golist-e-f{{.Error.Err}}cnet\nnobuildableGosourcefilesin/home/hc/golang/goc2p/src/cnet
我们还可以利用代码包text/template中定义的强大语法让golist命令输出定制化更高的代码包信息。比如:
hc@ubt:~$golist-e-f&39;cnet\nThepackagecnetisincomplete!\n\n“`bash\nhc@ubt:~$golist-f&34;,&39;cnet/ctcp\nTheimportsofpackagecnet/ctcpis[bufio,bytes,errors,logging,net,sync,time].
其中,join是命令程序在text/template包原有语法之上自定义的语法,在底层使用标准库代码包strings中的Join函数。关于更多的语法规则,请读者查看代码包text/template的相关文档。
另外,-tags标记也可以被golist接受。它与我们在讲gobuild命令时提到的-tags标记是一致的。读者可以查看代码包`go/build“的文档以了解细节。
golist命令很有用。它可以为我们提供指定代码包的更深层次的信息。这些信息往往是我们无法从源码文件中直观看到的。
关于go网站源码分享安装教程的内容到此结束,希望对大家有所帮助。