<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tony Bai &#187; 6g</title>
	<atom:link href="http://tonybai.com/tag/6g/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Wed, 15 Apr 2026 23:35:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Go语言开源十周年</title>
		<link>https://tonybai.com/2019/11/09/go-opensource-10-years/</link>
		<comments>https://tonybai.com/2019/11/09/go-opensource-10-years/#comments</comments>
		<pubDate>Sat, 09 Nov 2019 01:36:49 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[6g]]></category>
		<category><![CDATA[6l]]></category>
		<category><![CDATA[cgi]]></category>
		<category><![CDATA[cloud]]></category>
		<category><![CDATA[CNCF]]></category>
		<category><![CDATA[Cpp]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[etcd]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go10th]]></category>
		<category><![CDATA[GoBot]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[golangbridge]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[istio]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[machine-learning]]></category>
		<category><![CDATA[Make]]></category>
		<category><![CDATA[Makefile]]></category>
		<category><![CDATA[Opensource]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[prometheus]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[RussCox]]></category>
		<category><![CDATA[terraform]]></category>
		<category><![CDATA[TinyGo]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[云计算]]></category>
		<category><![CDATA[人工智能]]></category>
		<category><![CDATA[开源]]></category>
		<category><![CDATA[机器学习]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=2820</guid>
		<description><![CDATA[本文翻译自Go官方博客上Russ Cox代表Go核心团队发表的“Go Turns 10&#8243;一文。 生日快乐，Go！ 这个周末，我们庆祝Go正式对外发布10周年，即Go作为开源编程语言和构建现代网络软件生态系统的10周年诞辰。 为了纪念这一时刻，Go gopher的创建者Renee French(用下面的新作)描绘了这个令人愉快的场景： 庆祝Go十周年让我回想起2009年11月上旬，那时我们正准备与世界分享Go。我们不知道会发生什么样的反应，是否有人会关心这种新生的小语言。我希望即使没有人最终使用Go，我们也至少会引起人们对一些好的想法的关注，尤其是Go的并发和接口，这些想法可能会影响后续语言。 当看到人们对Go感到兴奋，我便查看了C、C++、Perl、Python和Ruby等流行语言的历史，并研究了每种语言花了多长时间才被广泛采用。例如，在我看来，Perl在1990年代中后期就已经完全形成了，带有CGI脚本和Web，但它于1987年首次发布。这种模式在我所研究的几乎所有语言中都重现了：在新语言真正腾飞之前，需要大约十年的时间进行安静、稳定的改进和传播。 (当时的)我想知道：十年后的Go会在哪里？ 今天，我们可以回答这个问题：Go无处不在，全世界至少有100万开发人员在使用它。 Go最初的目标是网络系统基础架构，现在我们称为云软件(cloud software)。如今，每个主要的云计算平台提供商都使用用Go语言编写的核心云基础架构，例如Docker，Etcdhttps://etcd.io/，Istio，Kubernetes，Prometheus和Terraform。Cloud Native Computing Foundation的大多数项目都是用Go编写的。无数公司也在使用Go将自己的工作迁移到云上，从初创公司从头开始构建到大企业更新软件堆栈。Go还发现对其的采用已经远远超出了最初的云计算目标，其使用范围从使用GoBot和TinyGo控制小型嵌入式系统到使用GRAIL进行大规模的大数据分析和机器学习进行癌症检测，以及介于两者之间的所有内容。 这一切都说明Go超越了我们最疯狂的梦想。Go的成功不仅仅在于语言。这是关于语言，生态系统，尤其是社区的共同努力。 在2009年，该语言是一个不错的主意，并带有一个实现的工作草图。那时候go命令还不存在：我们使用命令6g编译源码和6l链接二进制文件，并借助Makefile实现这个过程的自动化。我们在语句末尾键入分号。整个程序在垃圾回收期间停止，然后努力利用两个CPU核。当时Go只能在Linux和Mac，32位和64位x86和32位ARM上运行。 在过去的十年中，在世界各地的Go开发人员的帮助下，我们已经将这一想法和草图发展为拥有出色的工具，生产级质量实现，先进的垃圾收集器和得到广泛移植的高效语言，Go支持12种操作系统和10种CPU体系结构。 任何编程语言都需要蓬勃发展的生态系统的支持。开源发布是该生态系统的种子，但是自那时以来，许多人贡献了自己的时间和才干，用出色的教程，书籍，课程，博客文章，播客，工具，集成以及可重复使用的、支持go get的Go包来填充Go生态系统。没有这个生态系统的支持，Go永远不可能成功。 当然，生态系统需要蓬勃发展的社区的支持。在2019年，全球有数十个Go（技术）会议，以及超过150个Go聚会组织和90000名参会人员。 GoBridge和Going Who Go通过指导，培训和会议奖学金帮助将新的声音带入Go社区。仅今年一年，他们就在讲习班上向数百名来自传统团体的人们进行了培训，在这些讲习班上，社区成员教导和指导刚接触Go的人。 全球有超过一百万的Go开发人员，全球各地的公司都在寻求雇用更多的人。实际上，人们经常告诉我们，学习Go帮助他们获得了技术行业的第一份工作。最后，我们为Go感到最自豪的不是设计完善的功能或巧妙的代码，而是Go在这么多人的生活中产生的积极影响。我们旨在创建一种可以帮助我们成为更好的开发人员的语言，我们很高兴Go帮助了许多其他人。 恰逢Go开源十周年的时刻，我希望每个人都花一点时间来庆祝Go社区以及我们所取得的一切。我代表Google的整个Go团队，感谢过去十年来加入我们的每个人。让我们开启下一个更加不可思议的十年吧！ 我的网课“Kubernetes实战：高可用集群搭建、配置、运维与应用”在慕课网上线了，感谢小伙伴们学习支持！ 我爱发短信：企业级短信平台定制开发专家 https://tonybai.com/ smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。 著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个链接地址：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。 Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily 我的联系方式： 微博：https://weibo.com/bigwhite20xx 微信公众号：iamtonybai 博客：tonybai.com github: https://github.com/bigwhite 微信赞赏： 商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。 &#169; 2019, [...]]]></description>
			<content:encoded><![CDATA[<p>本文翻译自<a href="https://blog.golang.org">Go官方博客</a>上<a href="https://research.swtch.com/">Russ Cox</a>代表Go核心团队发表的<a href="https://blog.golang.org/10years">“Go Turns 10&#8243;</a>一文。</p>
<p><strong>生日快乐，Go！</strong></p>
<p>这个周末，我们庆祝<a href="https://opensource.googleblog.com/2009/11/hey-ho-lets-go.html">Go正式对外发布</a>10周年，即Go作为开源编程语言和构建现代网络软件生态系统的10周年诞辰。</p>
<p>为了纪念这一时刻，<a href="https://blog.golang.org/gopher">Go gopher</a>的创建者<a href="https://twitter.com/reneefrench">Renee French</a>(用下面的新作)描绘了这个令人愉快的场景：</p>
<p><img src="https://tonybai.com/wp-content/uploads/gopher10th-small.jpg" alt="img{512x368}" /></p>
<p>庆祝Go十周年让我回想起2009年11月上旬，那时我们正准备与世界分享Go。我们不知道会发生什么样的反应，是否有人会关心这种新生的小语言。我希望即使没有人最终使用Go，我们也至少会引起人们对一些好的想法的关注，尤其是Go的并发和接口，这些想法可能会<a href="https://tonybai.com/2019/11/04/the-legacy-of-go/">影响后续语言</a>。</p>
<p>当看到人们对Go感到兴奋，我便查看了<a href="https://tonybai.com/tag/c">C</a>、<a href="https://tonybai.com/tag/cpp">C++</a>、Perl、<a href="https://tonybai.com/tag/python">Python</a>和Ruby等流行语言的历史，并研究了每种语言花了多长时间才被广泛采用。例如，在我看来，Perl在1990年代中后期就已经完全形成了，带有CGI脚本和Web，但它于1987年首次发布。这种模式在我所研究的几乎所有语言中都重现了：在新语言真正腾飞之前，需要大约十年的时间进行安静、稳定的改进和传播。</p>
<p>(当时的)我想知道：十年后的Go会在哪里？</p>
<p>今天，我们可以回答这个问题：Go无处不在，全世界<a href="https://research.swtch.com/gophercount">至少有100万开发人员</a>在使用它。</p>
<p>Go最初的目标是网络系统基础架构，现在我们称为云软件(cloud software)。如今，每个主要的云计算平台提供商都使用用Go语言编写的核心云基础架构，例如<a href="https://tonybai.com/tag/docker">Docker</a>，<a href="">Etcd</a>https://etcd.io/，<a href="https://tonybai.com/2018/01/03/an-intro-of-microservices-governance-by-istio/">Istio</a>，<a href="https://coding.imooc.com/class/284.html">Kubernetes</a>，<a href="https://prometheus.io/">Prometheus</a>和<a href="https://www.terraform.io/">Terraform</a>。<a href="https://www.cncf.io/">Cloud Native Computing Foundation</a>的大多数项目都是用Go编写的。无数公司也在使用Go将自己的工作迁移到云上，从初创公司从头开始构建到大企业更新软件堆栈。Go还发现对其的采用已经远远超出了最初的云计算目标，其使用范围从使用<a href="https://gobot.io/">GoBot</a>和<a href="https://tinygo.org/">TinyGo</a>控制小型嵌入式系统到<a href="https://medium.com/grail-eng/bigslice-a-cluster-computing-system-for-go-7e03acd2419b">使用GRAIL进行大规模的大数据分析和机器学习</a>进行癌症检测，以及介于两者之间的所有内容。</p>
<p>这一切都说明Go超越了我们最疯狂的梦想。Go的成功不仅仅在于语言。这是关于语言，生态系统，尤其是社区的共同努力。</p>
<p>在2009年，该语言是一个不错的主意，并带有一个实现的工作草图。那时候go命令还不存在：我们使用命令6g编译源码和6l链接二进制文件，并借助Makefile实现这个过程的自动化。我们在语句末尾键入分号。整个程序在垃圾回收期间停止，然后努力利用两个CPU核。当时Go只能在Linux和Mac，32位和64位x86和32位ARM上运行。</p>
<p>在过去的十年中，在世界各地的Go开发人员的帮助下，我们已经将这一想法和草图发展为拥有出色的工具，生产级质量实现，<a href="https://blog.golang.org/ismmkeynote">先进的垃圾收集器</a>和得到广泛移植的高效语言，Go支持<a href="https://golang.org/doc/install/source#introduction">12种操作系统和10种CPU体系结构</a>。</p>
<p>任何编程语言都需要蓬勃发展的生态系统的支持。开源发布是该生态系统的种子，但是自那时以来，许多人贡献了自己的时间和才干，用出色的教程，书籍，课程，博客文章，播客，工具，集成以及可重复使用的、支持go get的Go包来填充Go生态系统。没有这个生态系统的支持，Go永远不可能成功。</p>
<p>当然，生态系统需要蓬勃发展的社区的支持。在2019年，全球有数十个Go（技术）会议，以及<a href="https://www.meetup.com/pro/go">超过150个Go聚会组织和90000名参会人员</a>。 <a href="https://golangbridge.org/">GoBridge</a>和<a href="https://medium.com/@carolynvs/www-loves-gobridge-ccb26309f667">Going Who Go</a>通过指导，培训和会议奖学金帮助将新的声音带入Go社区。仅今年一年，他们就在讲习班上向数百名来自传统团体的人们进行了培训，在这些讲习班上，社区成员教导和指导刚接触Go的人。</p>
<p>全球有<a href="https://research.swtch.com/gophercount">超过一百万的Go开发人员</a>，全球各地的公司都在寻求雇用更多的人。实际上，人们经常告诉我们，学习Go帮助他们获得了技术行业的第一份工作。最后，我们为Go感到最自豪的不是设计完善的功能或巧妙的代码，而是Go在这么多人的生活中产生的积极影响。我们旨在创建一种可以帮助我们成为更好的开发人员的语言，我们很高兴Go帮助了许多其他人。</p>
<p>恰逢Go开源十周年的时刻，我希望每个人都花一点时间来庆祝Go社区以及我们所取得的一切。我代表Google的整个Go团队，感谢过去十年来加入我们的每个人。让我们开启下一个更加不可思议的十年吧！</p>
<p><img src="https://tonybai.com/wp-content/uploads/gopher10th-pin-small.jpg" alt="img{512x368}" /></p>
<hr />
<p>我的网课“<a href="https://coding.imooc.com/class/284.html">Kubernetes实战：高可用集群搭建、配置、运维与应用</a>”在慕课网上线了，感谢小伙伴们学习支持！</p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/<br />
smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<p>微博：https://weibo.com/bigwhite20xx<br />
微信公众号：iamtonybai<br />
博客：tonybai.com<br />
github: https://github.com/bigwhite</p>
<p>微信赞赏：<br />
<img src="https://tonybai.com/wp-content/uploads/wechat-zanshang-code-512x512.jpg" alt="img{512x368}" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2019, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2019/11/09/go-opensource-10-years/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>理解Golang包导入</title>
		<link>https://tonybai.com/2015/03/09/understanding-import-packages/</link>
		<comments>https://tonybai.com/2015/03/09/understanding-import-packages/#comments</comments>
		<pubDate>Mon, 09 Mar 2015 07:27:30 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[6g]]></category>
		<category><![CDATA[6l]]></category>
		<category><![CDATA[fmt]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GOPATH]]></category>
		<category><![CDATA[GOROOT]]></category>
		<category><![CDATA[import]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[nsq]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[包]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[编译器]]></category>
		<category><![CDATA[链接器]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=1694</guid>
		<description><![CDATA[Golang使用包（package）这种语法元素来组织源码，所有语法可见性均定义在package这个级别，与Java 、python等语言相比，这算不上什么创新，但与C传统的include相比，则是显得&#8220;先进&#8221;了许多。 Golang中包的定义和使用看起来十分简单： 通过package关键字定义包： &#160;&#160; &#160;&#160;&#160; package xxx 使用import关键字，导入要使用的标准库包或第三方依赖包。 &#160;&#160; import &#34;a/b/c&#34; &#160;&#160; import &#34;fmt&#34; &#160;&#160; c.Func1() &#160;&#160; fmt.Println(&#34;Hello, World&#34;) 很多Golang初学者看到上面代码，都会想当然的将import后面的&#34;c&#34;、&#34;fmt&#34;当成包名，将其与c.Func1()和 fmt.Println()中的c和fmt认作为同一个语法元素：包名。但在深入Golang后，很多人便会发现事实上并非如此。比如在使用实时分布式消 息平台nsq提供的go client api时： 我们导入的路径如下： &#160;&#160; import &#8220;github.com/bitly/go-nsq&#8221; 但在使用其提供的export functions时，却用nsq做前缀包名： &#160;&#160; q, _ := nsq.NewConsumer(&#34;write_test&#34;, &#34;ch&#34;, config) 人们不禁要问：import后面路径中的最后一个元素到底代表的是啥? 是包名还是仅仅是一个路径？我们一起通过试验来理解一下。&#160; 实验环境：darwin_amd64 , go 1.4。 初始试验环境目录结果如下： GOPATH = /Users/tony/Test/Go/pkgtest/ pkgtest/ &#160;&#160;&#160; pkg/ &#160;&#160;&#160; src/ &#160; &#160;&#160;&#160;&#160; [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://golang.org">Golang</a>使用包（package）这种语法元素来组织源码，所有语法可见性均定义在package这个级别，与Java 、python等语言相比，这算不上什么创新，但与C传统的include相比，则是显得&ldquo;先进&rdquo;了许多。</p>
<p><a href="http://tonybai.com/tag/golang">Golang</a>中包的定义和使用看起来十分简单：</p>
<p>通过package关键字定义包：<br />
	&nbsp;&nbsp;<br />
	<font face="Courier New">&nbsp;&nbsp;&nbsp; package xxx</font></p>
<p>使用import关键字，导入要使用的标准库包或第三方依赖包。</p>
<p><font face="Courier New">&nbsp;&nbsp; import &quot;a/b/c&quot;<br />
	&nbsp;&nbsp; import &quot;fmt&quot;</font></p>
<p><font face="Courier New">&nbsp;&nbsp; c.Func1()<br />
	&nbsp;&nbsp; fmt.Println(&quot;Hello, World&quot;)</font></p>
<p>很多Golang初学者看到上面代码，都会想当然的将import后面的&quot;c&quot;、&quot;fmt&quot;当成包名，将其与c.Func1()和 fmt.Println()中的c和fmt认作为同一个语法元素：包名。但在深入Golang后，很多人便会发现事实上并非如此。比如在使用实时分布式消 息平台nsq提供的go client api时：</p>
<p>我们导入的路径如下：</p>
<p><font face="Courier New">&nbsp;&nbsp; import &ldquo;github.com/bitly/go-nsq&rdquo;</font></p>
<p>但在使用其提供的export functions时，却用<a href="http://github.com/bitly/nsq">nsq</a>做前缀包名：</p>
<p><font face="Courier New">&nbsp;&nbsp; q, _ := nsq.NewConsumer(&quot;write_test&quot;, &quot;ch&quot;, config)</font></p>
<p>人们不禁要问：import后面路径中的最后一个元素到底代表的是啥? 是包名还是仅仅是一个路径？我们一起通过试验来理解一下。&nbsp; 实验环境：<font face="Courier New">darwin_amd64 , go 1.4</font>。</p>
<p>初始试验环境目录结果如下：</p>
<p><font face="Courier New"><font face="Courier New">GOPATH = /Users/tony/Test/Go/pkgtest/</font><br />
	pkgtest/<br />
	&nbsp;&nbsp;&nbsp; pkg/<br />
	&nbsp;&nbsp;&nbsp; src/<br />
	&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; libproj1/<br />
	&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foo/<br />
	&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; foo1.go<br />
	&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; app1/<br />
	&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; main.go</font><br />
	&nbsp;&nbsp;&nbsp;<br />
	<b>一、编译时使用的是包源码还是.a</b></p>
<p>我们知道一个非main包在编译后会生成一个.a文件（在临时目录下生成，除非使用go install安装到$GOROOT或$GOPATH下，否则你看不到.a），用于后续可执行程序链接使用。</p>
<p>比如Go标准库中的包对应的源码部分路径在：$GOROOT/src，而标准库中包编译后的.a文件路径在$GOROOT/pkg/darwin_amd64下。一个奇怪的问题在我脑袋中升腾起来，编译时，编译器到底用的是.a还是源码？</p>
<p>我们先以用户自定义的package为例做个小实验。</p>
<p><font face="Courier New">$GOPATH/src/<br />
	&nbsp;&nbsp;&nbsp; libproj1/foo/<br />
	&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &#8211; foo1.go<br />
	&nbsp;&nbsp;&nbsp; app1<br />
	&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &#8211; main.go</font></p>
<p><font face="Courier New">//foo1.go<br />
	package foo</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func Foo1() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Println(&quot;Foo1&quot;)<br />
	}</font></p>
<p><font face="Courier New">// main.go<br />
	package main</font></p>
<p><font face="Courier New">import (<br />
	&nbsp;&nbsp;&nbsp; &quot;libproj1/foo&quot;<br />
	)</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; foo.Foo1()<br />
	}</font></p>
<p>执行<font face="Courier New">go install libproj1/foo</font>，Go编译器编译foo包，并将foo.a安装到<font face="Courier New">$GOPATH/pkg/darwin_amd64/libproj1</font>下。<br />
	编译app1：<font face="Courier New">go build app1</font>，在app1目录下生成app1*可执行文件，执行app1，我们得到一个初始预期结果：</p>
<p><font face="Courier New">$./app1<br />
	Foo1</font></p>
<p>现在我们无法看出使用的到底是foo的源码还是foo.a，因为目前它们的输出都是一致的。我们修改一下foo1.go的代码：</p>
<p><font face="Courier New">//foo1.go<br />
	package foo</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func Foo1() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Println(&quot;Foo1 &#8211; modified&quot;)<br />
	}</font></p>
<p>重新编译执行app1，我们得到结果如下：</p>
<p><font face="Courier New">$./app1<br />
	Foo1 &#8211; modified</font></p>
<p>实际测试结果告诉我们：<i><b>(1)在使用第三方包的时候，当源码和.a均已安装的情况下，编译器链接的是源码。</b></i></p>
<p>那么是否可以只链接.a，不用第三方包源码呢？我们临时删除掉libproj1目录，但保留之前install的libproj1/foo.a文件。</p>
<p>我们再次尝试编译app1，得到如下错误：</p>
<p><font face="Courier New">$go build app1<br />
	main.go:5:2: cannot find package &quot;libproj1/foo&quot; in any of:<br />
	&nbsp;&nbsp;&nbsp; /Users/tony/.Bin/go14/src/libproj1/foo (from $GOROOT)<br />
	&nbsp;&nbsp;&nbsp; /Users/tony/Test/Go/pkgtest/src/libproj1/foo (from $GOPATH)</font></p>
<p>编译器还是去找源码，而不是.a，因此我们要依赖第三方包，就必须搞到第三方包的源码，这也是Golang包管理的一个特点。</p>
<p>其实通过编译器的详细输出我们也可得出上面结论。我们在编译app1时给编译器传入<font face="Courier New">-x -v</font>选项：</p>
<p><font face="Courier New">$go build -x -v app1<br />
	WORK=/var/folders/2h/xr2tmnxx6qxc4w4w13m01fsh0000gn/T/go-build797811168<br />
	libproj1/foo<br />
	mkdir -p $WORK/libproj1/foo/_obj/<br />
	mkdir -p $WORK/libproj1/<br />
	cd /Users/tony/Test/Go/pkgtest/src/libproj1/foo<br />
	/Users/tony/.Bin/go14/pkg/tool/darwin_amd64/6g -o $WORK/libproj1/foo.a -trimpath $WORK -p libproj1/foo -complete -D _/Users/tony/Test/Go/pkgtest/src/libproj1/foo -I $WORK -pack ./foo1.go ./foo2.go<br />
	app1<br />
	mkdir -p $WORK/app1/_obj/<br />
	mkdir -p $WORK/app1/_obj/exe/<br />
	cd /Users/tony/Test/Go/pkgtest/src/app1<br />
	/Users/tony/.Bin/go14/pkg/tool/darwin_amd64/6g -o $WORK/app1.a -trimpath $WORK -p app1 -complete -D _/Users/tony/Test/Go/pkgtest/src/app1 -I $WORK -I /Users/tony/Test/Go/pkgtest/pkg/darwin_amd64 -pack ./main.go<br />
	cd .<br />
	<b>/Users/tony/.Bin/go14/pkg/tool/darwin_amd64/6l -o $WORK/app1/_obj/exe/a.out -L $WORK -L /Users/tony/Test/Go/pkgtest/pkg/darwin_amd64 -extld=clang $WORK/app1.a</b><br />
	mv $WORK/app1/_obj/exe/a.out app1</font></p>
<p>可以看到编译器6g首先在临时路径下编译出依赖包foo.a，放在$WORK/libproj1下。但我们在最后6l链接器的执行语句中并未显式看到app1链接的是$WORK/libproj1下的foo.a。但是从6l链接器的-L参数来看：<font face="Courier New"><b>-L $WORK -L /Users/tony/Test/Go/pkgtest/pkg/darwin_amd64，</b></font>我们发现$WORK目录放在了前面，我们猜测6l首先搜索到的时$WORK下面的libproj1/foo.a。</p>
<p>为了验证我们的推论，我们按照编译器输出，按顺序手动执行了一遍如上命令，但在最后执行6l命令时，去掉了-L $WORK：</p>
<p><font face="Courier New"><b>/Users/tony/.Bin/go14/pkg/tool/darwin_amd64/6l -o $WORK/app1/_obj/exe/a.out -L /Users/tony/Test/Go/pkgtest/pkg/darwin_amd64 -extld=clang $WORK/app1.a</b></font></p>
<p>这样做的结果是：</p>
<p><font face="Courier New">$./app1<br />
	Foo1</font></p>
<p>编译器链接了$GOPATH/pkg下的foo.a。<b>(2)到这里我们明白了所谓的使用第三方包源码，实际上是链接了以该最新源码编译</b><b>的临时目录下的.a文件而已。</b></p>
<p>Go标准库中的包也是这样么？对于标准库，比如fmt而言，编译时，到底使用的时$GOROOT/src下源码还是$GOROOT/pkg下已经编译好的.a呢？</p>
<p>我们不妨也来试试，一个最简单的hello world例子：<br />
	//main.go<br />
	import &quot;fmt&quot;</p>
<p>func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Println(&quot;Hello, World&quot;)<br />
	}</p>
<p>我们先将$GOROOT/src/fmt目录rename 为fmtbak，看看go compiler有何反应？<br />
	go build -x -v ./</p>
<p>$go build -x -v ./<br />
	WORK=/var/folders/2h/xr2tmnxx6qxc4w4w13m01fsh0000gn/T/go-build957202426<br />
	main.go:4:8: cannot find package &quot;fmt&quot; in any of:<br />
	&nbsp;&nbsp;&nbsp; /Users/tony/.Bin/go14/src/fmt (from $GOROOT)<br />
	&nbsp;&nbsp;&nbsp; /Users/tony/Test/Go/pkgtest/src/fmt (from $GOPATH)<br />
	&nbsp;<br />
	找不到fmt包了。显然标准库在编译时也是必须要源码的。不过与自定义包不同的是，即便你修改了fmt包的源码（未重新编译GO安装包），用户源码编译时，也不会尝试重新编译fmt包的，依旧只是在链接时链接已经编译好的fmt.a。通过下面的gc输出可以验证这点：</p>
<p>$go build -x -v ./<br />
	WORK=/var/folders/2h/xr2tmnxx6qxc4w4w13m01fsh0000gn/T/go-build773440756<br />
	app1<br />
	mkdir -p $WORK/app1/_obj/<br />
	mkdir -p $WORK/app1/_obj/exe/<br />
	cd /Users/tony/Test/Go/pkgtest/src/app1<br />
	/Users/tony/.Bin/go14/pkg/tool/darwin_amd64/6g -o $WORK/app1.a -trimpath $WORK -p app1 -complete -D _/Users/tony/Test/Go/pkgtest/src/app1 -I $WORK -pack ./main.go<br />
	cd .<br />
	/Users/tony/.Bin/go14/pkg/tool/darwin_amd64/6l -o $WORK/app1/_obj/exe/a.out -L $WORK -extld=clang $WORK/app1.a<br />
	mv $WORK/app1/_obj/exe/a.out app1</p>
<p>可以看出，编译器的确并未尝试编译标准库中的fmt源码。</p>
<p><b>二、目录名还是包名？</b></p>
<p>从第一节的实验中，我们得知了编译器在编译过程中依赖的是包源码的路径，这为后续的实验打下了基础。下面我们再来看看，Go语言中import后面路径中最后的一个元素到底是包名还是路径名？</p>
<p>本次实验目录结构：<br />
	<font face="Courier New">$GOPATH<br />
	&nbsp;&nbsp;&nbsp; src/<br />
	&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; libproj2/<br />
	&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; foo/<br />
	&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; foo1.go<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app2/<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; main.go</font></p>
<p>按照Golang语言习惯，一个go package的所有源文件放在同一个目录下，且该目录名与该包名相同，比如libproj1/foo目录下的package为foo，foo1.go、 foo2.go&#8230;共同组成foo package的源文件。但目录名与包名也可以不同，我们就来试试不同的。</p>
<p>我们建立libproj2/foo目录，其中的foo1.go代码如下：</p>
<p><font face="Courier New">//foo1.go</font><br />
	<font face="Courier New">package bar</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func Bar1() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Println(&quot;Bar1&quot;)<br />
	}</font></p>
<p>注意：这里package名为bar，与目录名foo完全不同。</p>
<p>接下来就给app2带来了难题：该如何import bar包呢？</p>
<p>我们假设import路径中的最后一个元素是包名，而非路径名。</p>
<p><font face="Courier New">//app2/main.go</font></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import (<br />
	&nbsp;&nbsp;&nbsp; &quot;libproj2/bar&quot;<br />
	)</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; bar.Bar1()<br />
	}</font></p>
<p>编译app2：</p>
<p><font face="Courier New">$go build -x -v app2<br />
	WORK=/var/folders/2h/xr2tmnxx6qxc4w4w13m01fsh0000gn/T/go-build736904327<br />
	main.go:5:2: cannot find package &quot;libproj2/bar&quot; in any of:<br />
	&nbsp;&nbsp;&nbsp; /Users/tony/.Bin/go14/src/libproj2/bar (from $GOROOT)<br />
	&nbsp;&nbsp;&nbsp; /Users/tony/Test/Go/pkgtest/src/libproj2/bar (from $GOPATH)</font></p>
<p>编译失败，在两个路径下无法找到对应libproj2/bar包。</p>
<p>我们的假设错了，我们把它改为路径：</p>
<p><font face="Courier New">//app2/main.go</font></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import (<br />
	&nbsp;&nbsp;&nbsp; &quot;libproj2/foo&quot;<br />
	)</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; bar.Bar1()<br />
	}</font></p>
<p>再编译执行：</p>
<p><font face="Courier New">$go build app2<br />
	$app2<br />
	Bar1</font></p>
<p>这回编译顺利通过，执行结果也是OK的。这样我们得到了结论：<b>(3)import后面的最后一个元素应该是路径，就是目录，并非包名</b>。</p>
<p>go编译器在这些路径(libproj2/foo)下找bar包。这样看来，go语言的惯例只是一个特例，即恰好目录名与包名一致罢了。也就是说下面例子中的两个foo含义不同：</p>
<p><font face="Courier New">import &quot;libproj1/foo&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; foo.Foo()<br />
	}</font></p>
<p>import中的<b>foo</b>只是一个文件系统的路径罢了。而下面foo.Foo()中的<b>foo</b>则是包名。而这个包是在libproj1/foo目录下的源码中找到的。</p>
<p>再类比一下标准库包fmt。</p>
<p><font face="Courier New">import &quot;fmt&quot;<br />
	fmt.Println(&quot;xxx&quot;)</font></p>
<p>这里上下两行中虽然都是&ldquo;fmt&quot;，但同样含义不同，一个是路径 ，对于标准库来说，是$GOROOT/src/fmt这个路径。而第二行中的fmt则是包名。gc会在$GOROOT/src/fmt路径下找到fmt包的源文件。</p>
<p><b>三、import m &quot;lib/math&quot; </b></p>
<p>Go language specification中关于import package时列举的一个例子如下：</p>
<p><font face="Courier New">Import declaration&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Local name of Sin</font></p>
<p><font face="Courier New">import&nbsp;&nbsp; &quot;lib/math&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; math.Sin<br />
	import m &quot;lib/math&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m.Sin<br />
	import . &quot;lib/math&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sin</font></p>
<p>我们看到<font face="Courier New">import m &quot;lib/math&quot;&nbsp; m.Sin</font>一行。我们说过lib/math是路径，import语句用m替代lib/math，并在代码中通过m访问math包中的导出函数Sin。</p>
<p>那m到底是包名还是路径呢？既然能通过m访问Sin，那m肯定是包名了，Right！那import m &quot;lib/math&quot;该如何理解呢？&nbsp;</p>
<p>根据上面一、二两节中得出的结论，我们尝试理解一下m：<b>(4)m指代的是lib/math路径下唯一的那个包</b>。</p>
<p>一个目录下是否可以存在两个包呢？我们来试试。</p>
<p>我们在libproj1/foo下新增一个go源文件，bar1.go：</p>
<p><font face="Courier New">package bar</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func Bar1() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Println(&quot;Bar1&quot;)<br />
	}</font></p>
<p>我们重新构建一下这个目录下的包：</p>
<p><font face="Courier New">$go build libproj1/foo<br />
	can&#39;t load package: package libproj1/foo: found packages bar1.go (bar) and foo1.go (foo) in /Users/tony/Test/Go/pkgtest/src/libproj1/foo</font></p>
<p>我们收到了错误提示，编译器在这个路径下发现了两个包，这是不允许的。</p>
<p>我们再作个实验，来验证我们对m含义的解释。</p>
<p>我们建立app3目录，其main.go的源码如下：</p>
<p><font face="Courier New">//main.go<br />
	package main</font></p>
<p><font face="Courier New">import m &quot;libproj2/foo&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; m.Bar1()<br />
	}</font></p>
<p>libproj2/foo路径下的包的包名为bar，按照我们的推论，m指代的就是bar这个包，通过m我们可以访问bar的Bar1导出函数。</p>
<p>编译并执行上面main.go：</p>
<p><font face="Courier New">$go build app3<br />
	$app3<br />
	Bar1</font></p>
<p>执行结果与我们推论完全一致。</p>
<p>附录：6g, 6l文档位置：</p>
<p><font face="Courier New">6g &#8211; $GOROOT/src/cmd/gc/doc.go<br />
	6l &#8211; $GOROOT/src/cmd/ld/doc.go</font></p>
<p style='text-align:left'>&copy; 2015, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2015/03/09/understanding-import-packages/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Golang跨平台交叉编译</title>
		<link>https://tonybai.com/2014/10/20/cross-compilation-with-golang/</link>
		<comments>https://tonybai.com/2014/10/20/cross-compilation-with-golang/#comments</comments>
		<pubDate>Mon, 20 Oct 2014 08:40:52 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[6g]]></category>
		<category><![CDATA[6l]]></category>
		<category><![CDATA[Build]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Cgo]]></category>
		<category><![CDATA[Compile]]></category>
		<category><![CDATA[Compiler]]></category>
		<category><![CDATA[Darwin]]></category>
		<category><![CDATA[GCC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[GOARCH]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GOOS]]></category>
		<category><![CDATA[GOROOT]]></category>
		<category><![CDATA[linker]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Opensource]]></category>
		<category><![CDATA[开源]]></category>
		<category><![CDATA[编译器]]></category>
		<category><![CDATA[链接器]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=1571</guid>
		<description><![CDATA[近期在某本书上看到Go跨平台交叉编译的强大功能，于是想自己测试一下。以下记录了测试过程以及一些结论，希望能给大家带来帮助。 我的Linux环境如下： uname -a Linux ubuntu-Server-14 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux $ go version go version go1.3.1 linux/amd64 跨平台交叉编译涉及两个重要的环境变量：GOOS和GOARCH，分别代表Target Host OS和Target Host ARCH，如果没有显式设置这些环境变量，我们通过go env可以看到go编译器眼中这两个环境变量的当前值： $ go env GOARCH=&#34;amd64&#34; GOOS=&#34;linux&#34; GOHOSTARCH=&#34;amd64&#34; GOHOSTOS=&#34;linux&#34; &#8230; &#8230; 这里还有两个变量GOHOSTOS和GOHOSTARCH，分别表示的是当前所在主机的的OS和CPU ARCH。我的Go是采用安装包安装的，因此默认情况下，这两组环境变量的值都是来自当前主机的信息。 现在我们就来交叉编译一下：在linux/amd64平台下利用Go编译器编译一个可以运行在linux/amd64下的程序，样例程序如下： //testport.go package main import ( &#160;&#160;&#160;&#160;&#160;&#160;&#160; &#34;fmt&#34; &#160;&#160;&#160;&#160;&#160;&#160;&#160; &#34;os/exec&#34; &#160;&#160;&#160;&#160;&#160;&#160;&#160; &#34;bytes&#34; [...]]]></description>
			<content:encoded><![CDATA[<p>近期在某本书上看到<a href="http://golang.org">Go</a>跨平台交叉编译的强大功能，于是想自己测试一下。以下记录了测试过程以及一些结论，希望能给大家带来帮助。</p>
<p>我的Linux环境如下：</p>
<p><font face="Courier New">uname -a<br />
	Linux ubuntu-Server-14 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux</font></p>
<p><font face="Courier New">$ go version<br />
	go version go1.3.1 linux/amd64</font></p>
<p>跨平台交叉编译涉及两个重要的环境变量：GOOS和GOARCH，分别代表Target Host OS和Target Host ARCH，如果没有显式设置这些环境变量，我们通过go env可以看到go编译器眼中这两个环境变量的当前值：</p>
<p><font face="Courier New">$ go env<br />
	GOARCH=&quot;amd64&quot;<br />
	GOOS=&quot;linux&quot;</font><br />
	<font face="Courier New">GOHOSTARCH=&quot;amd64&quot;<br />
	GOHOSTOS=&quot;linux&quot;</font><br />
	&#8230; &#8230;</p>
<p>这里还有两个变量GOHOSTOS和GOHOSTARCH，分别表示的是当前所在主机的的OS和CPU ARCH。我的Go是采用安装包安装的，因此默认情况下，这两组环境变量的值都是来自当前主机的信息。</p>
<p>现在我们就来交叉编译一下：在linux/amd64平台下利用Go编译器编译一个可以运行在linux/amd64下的程序，样例程序如下：</p>
<p><font face="Courier New">//testport.go<br />
	package main</font></p>
<p><font face="Courier New">import (<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;fmt&quot;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;os/exec&quot;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;bytes&quot;<br />
	)</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd := exec.Command(&quot;uname&quot;, &quot;-a&quot;)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var out bytes.Buffer<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd.Stdout = &amp;out</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err := cmd.Run()<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if err != nil {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.Println(&quot;Err when executing uname command&quot;)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.Println(&quot;I am running on&quot;, out.String())<br />
	}</font></p>
<p>在Linux/amd64下编译运行：</p>
<p><font face="Courier New">$ go build -o testport_linux testport.go<br />
	$ testport_linux<br />
	I am running on Linux ubuntu-Server-14 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux</font></p>
<p>接下来，我们来尝试在Linux/amd64上编译一个可以运行在darwin/amd64上的程序。我只需修改GOOS和GOARCH两个标识目标主机OS和ARCH的环境变量：</p>
<p><font face="Courier New">$ GOOS=darwin GOARCH=amd64 go build -o testport_darwin testport.go<br />
	go build runtime: darwin/amd64 must be bootstrapped using make.bash</font></p>
<p>编译器报错了！提示darwin/amd64必须通过make.bash重新装载。显然，通过安装包安装到linux/amd64下的Go编译器还无法直接交叉编译出darwin/amd64下可以运行的程序，我们需要做一些准备工作。我们找找make.bash在哪里！</p>
<p>我们到Go的$GOROOT路径下去找make.bash，Go的安装路径下的组织很简约，扫一眼便知make.sh大概在$GOROOT/src下，打开make.sh，我们在文件头处看到如下一些内容：</p>
<p><font face="Courier New"># Environment variables that control make.bash:<br />
	#<br />
	# GOROOT_FINAL: The expected final Go root, baked into binaries.<br />
	# The default is the location of the Go tree during the build.<br />
	#<br />
	# GOHOSTARCH: The architecture for host tools (compilers and<br />
	# binaries).&nbsp; Binaries of this type must be executable on the current<br />
	# system, so the only common reason to set this is to set<br />
	# GOHOSTARCH=386 on an amd64 machine.<br />
	#<br />
	# GOARCH: The target architecture for installed packages and tools.<br />
	#<br />
	# GOOS: The target operating system for installed packages and tools.</font><br />
	&#8230; &#8230;</p>
<p>make.bash头并未简要说明文件的用途，但名为make.xx的文件想必是用来构建Go编译工具的。这里提到几个环境变量可以控制 make.bash的行为，显然GOARCH和GOOS更能引起我们的兴趣。我们再回过头来输出testport.go编译过程的详细信息：</p>
<p><font face="Courier New">$ go build -x -o testport_linux testport.go<br />
	WORK=/tmp/go-build286732099<br />
	mkdir -p $WORK/command-line-arguments/_obj/<br />
	cd /home/tonybai/Test/Go/porting<br />
	/usr/local/go/pkg/tool/linux_amd64/6g -o $WORK/command-line-arguments.a -trimpath $WORK -p command-line-arguments -complete -D _/home/tonybai/Test/Go/porting -I $WORK -pack ./testport.go<br />
	cd .<br />
	/usr/local/go/pkg/tool/linux_amd64/6l -o testport_linux -L $WORK -extld=gcc $WORK/command-line-arguments.a</font></p>
<p>我们发现Go实际上用的是$GOROOT/pkg/tool/linux_amd64下的6g（编译器）和6l（链接器）来完成整个编译过程的，看到6g 和6l所在目录名为linux_amd64，我们可以大胆猜测编译darwin/amd64 go程序应该使用的是$GOROOT/pkg/tool/darwin_amd64下的工具。不过在我在$GOROOT/pkg/tool下没有发现 darwin_amd64目录，也就是说我们通过安装包安装的Go仅自带了for linux_amd64的编译工具，要想交叉编译出for darwin_amd64的程序，我们需要通过make.bash来手工编译出这些工具。</p>
<p><font face="Courier New">tonybai@ubuntu-Server-14:/usr/local/go/pkg$ ls<br />
	linux_amd64&nbsp; linux_amd64_race&nbsp; obj&nbsp; tool</font></p>
<p><font face="Courier New">tonybai@ubuntu-Server-14:/usr/local/go/pkg/tool$ ls<br />
	linux_amd64</font></p>
<p>根据前面make.bash的用法说明，我们来尝试构建一下：</p>
<p><font face="Courier New">cd $GOROOT/src<br />
	sudo GOOS=darwin GOARCH=amd64 ./make.bash</font></p>
<p><font face="Courier New"># Building C bootstrap tool.<br />
	cmd/dist</font></p>
<p><font face="Courier New"># Building compilers and Go bootstrap tool for host, linux/amd64.<br />
	&#8230; &#8230;<br />
	cmd/cc<br />
	cmd/gc<br />
	cmd/6l<br />
	cmd/6a<br />
	cmd/6c<br />
	cmd/6g<br />
	pkg/runtime<br />
	&#8230; &#8230;<br />
	cmd/go<br />
	pkg/runtime (darwin/amd64)</font></p>
<p><font face="Courier New"># Building packages and commands for host, linux/amd64.<br />
	runtime<br />
	&#8230; &#8230;<br />
	text/scanner</font></p>
<p><font face="Courier New"># Building packages and commands for darwin/amd64.<br />
	runtime<br />
	errors<br />
	&#8230; &#8230;<br />
	testing/quick<br />
	text/scanner</font></p>
<p><font face="Courier New">&#8212;<br />
	Installed Go for darwin/amd64 in /usr/local/go<br />
	Installed commands in /usr/local/go/bin</font></p>
<p>编译后，我们再来试试编译for darwin_amd64的程序：</p>
<p><font face="Courier New">$ GOOS=darwin GOARCH=amd64 go build -x -o testport_darwin testport.go<br />
	WORK=/tmp/go-build972764136<br />
	mkdir -p $WORK/command-line-arguments/_obj/<br />
	cd /home/tonybai/Test/Go/porting<br />
	/usr/local/go/pkg/tool/linux_amd64/6g -o $WORK/command-line-arguments.a -trimpath $WORK -p command-line-arguments -complete -D _/home/tonybai/Test/Go/porting -I $WORK -pack ./testport.go<br />
	cd .<br />
	/usr/local/go/pkg/tool/linux_amd64/6l -o testport_darwin -L $WORK -extld=gcc $WORK/command-line-arguments.a</font></p>
<p>将文件copy到我的Mac Air下执行：</p>
<p><font face="Courier New">$chmod +x testport_darwin<br />
	$testport_darwin<br />
	I am running on Darwin TonydeMacBook-Air.local 13.1.0 Darwin Kernel Version 13.1.0: Thu Jan 16 19:40:37 PST 2014; root:xnu-2422.90.20~2/RELEASE_X86_64 x86_64</font></p>
<p>编译虽然成功了，但从-x输出的详细编译过程来看，Go编译连接使用的工具依旧是linux_amd64下的6g和6l，为什么没有使用darwin_amd64下的6g和6l呢？原来$GOROOT/pkg/tool/darwin_amd64下根本就没有6g和6l：</p>
<p><font face="Courier New">/usr/local/go/pkg/tool/darwin_amd64$ ls<br />
	addr2line&nbsp; cgo&nbsp; fix&nbsp; nm&nbsp; objdump&nbsp; pack&nbsp; yac</font>c</p>
<p>但查看一下<font face="Courier New">pkg/tool/linux_amd64/</font>下程序的更新时间：</p>
<p>/usr/local/go/pkg/tool/linux_amd64$ ls -l<br />
	<font face="Courier New">&#8230; &#8230;<br />
	-rwxr-xr-x 1 root root 2482877 10月 20 15:12 6g<br />
	-rwxr-xr-x 1 root root 1186445 10月 20 15:12 6l<br />
	&#8230; &#8230;</font></p>
<p>我们发现6g和6l都是被刚才的make.bash新编译出来的，我们可以得出结论：新6g和新6l目前既可以编译本地程序（linux/amd64)，也可以编译darwin/amd64下的程序了，例如重新编译testport_linux依旧ok：</p>
<p><font face="Courier New">$ go build -x -o testport_linux testport.go<br />
	WORK=/tmp/go-build636762567<br />
	mkdir -p $WORK/command-line-arguments/_obj/<br />
	cd /home/tonybai/Test/Go/porting<br />
	/usr/local/go/pkg/tool/linux_amd64/6g -o $WORK/command-line-arguments.a -trimpath $WORK -p command-line-arguments -complete -D _/home/tonybai/Test/Go/porting -I $WORK -pack ./testport.go<br />
	cd .<br />
	/usr/local/go/pkg/tool/linux_amd64/6l -o testport_linux -L $WORK -extld=gcc $WORK/command-line-arguments.a</font></p>
<p>如果我们还想给Go编译器加上交叉编译windows/amd64程序的功能，我们再执行一次make.bash：</p>
<p><font face="Courier New">sudo GOOS=windows GOARCH=amd64 ./make.bash</font></p>
<p>编译成功后，我们来编译一下Windows程序：</p>
<p><font face="Courier New">$ GOOS=windows GOARCH=amd64 go build -x -o testport_windows.exe testport.go<br />
	WORK=/tmp/go-build626615350<br />
	mkdir -p $WORK/command-line-arguments/_obj/<br />
	cd /home/tonybai/Test/Go/porting<br />
	/usr/local/go/pkg/tool/linux_amd64/6g -o $WORK/command-line-arguments.a -trimpath $WORK -p command-line-arguments -complete -D _/home/tonybai/Test/Go/porting -I $WORK -pack ./testport.go<br />
	cd .<br />
	/usr/local/go/pkg/tool/linux_amd64/6l -o testport_windows.exe -L $WORK -extld=gcc $WORK/command-line-arguments.a</font></p>
<p>把testport_windows.exe扔到Windows上执行，结果：</p>
<p><font face="Courier New">Err when executing uname command</font></p>
<p>显然Windows下没有uname命令，提示执行出错。</p>
<p>至此，我的Go编译器具备了在Linux下编译windows/amd64和darwin/amd64的能力。如果你还想增加其他平台的能力，就像上面那样操作执行make.bash即可。</p>
<p>如果在go源文件中有与C语言的交互代码，那么交叉编译功能是否还能奏效呢？毕竟C在各个平台上的运行库、链接库等都是不同的。我们先来看看这个例子，我们使用之前在《<a href="http://tonybai.com/2014/10/12/discussion-on-shared-mem-support-in-docker/">探讨docker容器对共享内存的支持情况</a>》一文中的一个例子：</p>
<p><font face="Courier New">//testport_cgoenabled.go<br />
	package main</font></p>
<p><font face="Courier New">//#include &lt;stdio.h&gt;<br />
	//#include &lt;sys/types.h&gt;<br />
	//#include &lt;sys/mman.h&gt;<br />
	//#include &lt;fcntl.h&gt;<br />
	//<br />
	//#define SHMSZ&nbsp;&nbsp;&nbsp;&nbsp; 27<br />
	//<br />
	//int shm_rd()<br />
	//{<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char c;<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *shm = NULL;<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *s = NULL;<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int fd;<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((fd = open(&quot;./shm.txt&quot;, O_RDONLY)) == -1)&nbsp; {<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	//<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shm = (char*)mmap(shm, SHMSZ, PROT_READ, MAP_SHARED, fd, 0);<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!shm) {<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -2;<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	//<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(fd);<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s = shm;<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i = 0;<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (i = 0; i &lt; SHMSZ &#8211; 1; i++) {<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf(&quot;%c &quot;, *(s + i));<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf(&quot;\n&quot;);<br />
	//<br />
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
	//}<br />
	import &quot;C&quot;</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i := C.shm_rd()<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.Println(&quot;Mmap Share Memory Read Error:&quot;, i)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.Println(&quot;Mmap Share Memory Read Ok&quot;)<br />
	}</font></p>
<p>我们先编译出一个本地可运行的程序：</p>
<p><font face="Courier New">$ go build -x -o testport_cgoenabled_linux testport_cgoenabled.go<br />
	WORK=/tmp/go-build977176241<br />
	mkdir -p $WORK/command-line-arguments/_obj/<br />
	cd /home/tonybai/Test/Go/porting<br />
	CGO_LDFLAGS=&quot;-g&quot; &quot;-O2&quot; /usr/local/go/pkg/tool/linux_amd64/cgo -objdir $WORK/command-line-arguments/_obj/ &#8212; -I $WORK/command-line-arguments/_obj/ testport_cgoenabled.go<br />
	/usr/local/go/pkg/tool/linux_amd64/6c -F -V -w -trimpath $WORK -I $WORK/command-line-arguments/_obj/ -I /usr/local/go/pkg/linux_amd64 -o $WORK/command-line-arguments/_obj/_cgo_defun.6 -D GOOS_linux -D GOARCH_amd64 $WORK/command-line-arguments/_obj/_cgo_defun.c<br />
	gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -print-libgcc-file-name<br />
	gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -I $WORK/command-line-arguments/_obj/ -g -O2 -o $WORK/command-line-arguments/_obj/_cgo_main.o -c $WORK/command-line-arguments/_obj/_cgo_main.c<br />
	gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -I $WORK/command-line-arguments/_obj/ -g -O2 -o $WORK/command-line-arguments/_obj/_cgo_export.o -c $WORK/command-line-arguments/_obj/_cgo_export.c<br />
	gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -I $WORK/command-line-arguments/_obj/ -g -O2 -o $WORK/command-line-arguments/_obj/testport_cgoenabled.cgo2.o -c $WORK/command-line-arguments/_obj/testport_cgoenabled.cgo2.c<br />
	gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -o $WORK/command-line-arguments/_obj/_cgo_.o $WORK/command-line-arguments/_obj/_cgo_main.o $WORK/command-line-arguments/_obj/_cgo_export.o $WORK/command-line-arguments/_obj/testport_cgoenabled.cgo2.o -g -O2<br />
	/usr/local/go/pkg/tool/linux_amd64/cgo -objdir $WORK/command-line-arguments/_obj/ -dynimport $WORK/command-line-arguments/_obj/_cgo_.o -dynout $WORK/command-line-arguments/_obj/_cgo_import.c<br />
	/usr/local/go/pkg/tool/linux_amd64/6c -F -V -w -trimpath $WORK -I $WORK/command-line-arguments/_obj/ -I /usr/local/go/pkg/linux_amd64 -o $WORK/command-line-arguments/_obj/_cgo_import.6 -D GOOS_linux -D GOARCH_amd64 $WORK/command-line-arguments/_obj/_cgo_import.c<br />
	gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -o $WORK/command-line-arguments/_obj/_all.o $WORK/command-line-arguments/_obj/_cgo_export.o $WORK/command-line-arguments/_obj/testport_cgoenabled.cgo2.o -g -O2 -Wl,-r -nostdlib /usr/lib/gcc/x86_64-linux-gnu/4.8/libgcc.a<br />
	/usr/local/go/pkg/tool/linux_amd64/6g -o $WORK/command-line-arguments.a -trimpath $WORK -p command-line-arguments -D _/home/tonybai/Test/Go/porting -I $WORK -pack $WORK/command-line-arguments/_obj/_cgo_gotypes.go $WORK/command-line-arguments/_obj/testport_cgoenabled.cgo1.go<br />
	pack r $WORK/command-line-arguments.a $WORK/command-line-arguments/_obj/_cgo_import.6 $WORK/command-line-arguments/_obj/_cgo_defun.6 $WORK/command-line-arguments/_obj/_all.o # internal<br />
	cd .<br />
	/usr/local/go/pkg/tool/linux_amd64/6l -o testport_cgoenabled_linux -L $WORK -extld=gcc $WORK/command-line-arguments.a</font></p>
<p>输出了好多日志！不过可以看出Go编译器先调用CGO对Go源码中的C代码进行了编译，然后才是常规的Go编译，最后通过6l链接在一起。Cgo似乎直接使用了Gcc。我们再来试试跨平台编译：</p>
<p><font face="Courier New">$ GOOS=darwin GOARCH=amd64 go build -x -o testport_cgoenabled_darwin testport_cgoenabled.go<br />
	WORK=/tmp/go-build124869433<br />
	can&#39;t load package: no buildable Go source files in /home/tonybai/Test/Go/porting</font></p>
<p>当我们编译for Darwin/amd64平台的程序时，Go无法像之前那样的顺利完成编译，而是提示错误。从网上给出的资料来看，如果Go源码中包含C互操作代码，那么 目前依旧无法实现交叉编译，因为cgo会直接使用各个平台的本地c编译器去编译Go文件中的C代码。默认情况下，make.bash会置 CGO_ENABLED=0。</p>
<p>如果你非要将CGO_ENABLED设置为1去编译go的话，至少我得到了如下错误，导致无法编译通过：</p>
<p><font face="Courier New">$ sudo CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 ./make.bash &#8211;no-clean<br />
	&#8230; &#8230;<br />
	# Building packages and commands for darwin/amd64.<br />
	&#8230; &#8230;<br />
	37: error: &#39;AI_MASK&#39; undeclared (first use in this function)</font><br />
	&nbsp;</p>
<p style='text-align:left'>&copy; 2014, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2014/10/20/cross-compilation-with-golang/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
