Go 语言中程序是怎么编译的?

在当今快速发展的软件开发领域,Go 语言(又称 Golang)已经成为了开发高性能应用程序的热门选择。由 Google 开发并在 2009 年公开发布,Go 语言因其简洁的语法、出色的并发支持以及优秀的性能而受到广泛欢迎。尽管 Go 的语法相对简单,但它的编译过程和模块管理系统可能会让初学者感到有些困惑,特别是那些从动态语言背景转来的开发者。

本文旨在深入探讨 Go 语言的编译机制和最新的模块管理系统——Go Modules。通过详细的示例和步骤,我们将演示从简单的 “Hello World” 程序到使用第三方库的更复杂项目的开发过程。我们将开始于基本的编译命令,探索如何手动处理依赖,然后过渡到使用 Go Modules 管理依赖,这在现代 Go 项目开发中已成为标准实践。


文章目录

      • 1、编译和运行 Go 程序的基础
      • 2、Go Modules 机制
        • 2.1、复杂项目下 Go 程序的编译
        • 2.2、go mod
        • 2.3、go mod 示例


1、编译和运行 Go 程序的基础

在上一篇我们实现 HelloWorld 程序的过程中,我们在运行 “helloworld” 程序之前,是输入了一个 go build 命令的:

$go build main.go

对此,如果有使用 C/C++ 和 gcc 或 clang 的开发经验的同学家就会发现 Go 的编译步骤与它们非常类似。都是在编译成功后,生成一个二进制可执行文件。

在 Linux 系统、macOS 系统或 Windows 系统的 PowerShell 中,可以通过执行以下 ls 命令来查看这个新生成的可执行文件:

$ls
main*    main.go

可以看到,在上面显示的文件里面有我们刚刚创建的、以 .go 为后缀的源代码文件,还有刚生成的可执行文件(Windows 系统下为 main.exe,其余系统下为 main)。

如果你主要使用的是像 Ruby、Python 或 JavaScript 这样的动态语言,那么你可能不太习惯需要在运行程序之前先进行编译的步骤。

Go 是编译型语言,这意味着我们必须先编译 Go 程序,才能将生成的可执行文件交给其他人,在没有安装 Go 的环境中也能运行这些程序。相对地,如果你提供给别人的是 .rb.py.js 文件,他们就需要在自己的环境中安装相应的 Ruby、Python 或 JavaScript 解释器来执行这些动态语言的源代码。

当然,Go 也借鉴了动态语言的一些对开发者体验较好的特性,比如基于源码文件的直接执行,Go 提供了 run 命令可以直接运行 Go 源码文件,比如我们也可以使用下面命令直接基于 main.go 运行:

$go run main.go
hello, world

不过像 go run 这类命令更多用于开发调试阶段,真正的交付成果还是需要使用 go build 命令构建的。

然而,在我们的生产环境中,编译 Go 程序通常不像之前提到的基于单个 Go 源文件构建像"hello, world"这样的简单示例那么直接。随着项目接近真实的生产条件,它的规模会增大、涉及的协作人员会增多,同时项目的依赖及其版本也会变得更为复杂。


2、Go Modules 机制

2.1、复杂项目下 Go 程序的编译

现在,我们将启动一个名为"hellomod"的新项目,在这个项目中,我们会使用两个第三方库:zapfasthttp。这将使 go build 的构建过程更具挑战性。

如同我们之前的"helloworld"示例一样,我们可以通过以下命令来创建“hellomod”项目:

$mkdir hellomod
$cd hellomod

接着,我们在"hellomod"下创建并编辑我们的示例源码 main.go 文件:

package mainimport ("github.com/valyala/fasthttp""go.uber.org/zap"
)var logger *zap.Loggerfunc init() {logger, _ = zap.NewProduction()
}func fastHTTPHandler(ctx *fasthttp.RequestCtx) {logger.Info("hello, go module", zap.ByteString("uri", ctx.RequestURI()))
}func main() {fasthttp.ListenAndServe(":8081", fastHTTPHandler)
}

这个示例中,我们创建了一个在 8081 端口上监听的 HTTP 服务。当向该服务发起请求时,它会在终端的标准输出上打印一段访问日志。可

以看出,相比于“hello, world”示例,这个项目明显更加复杂。但目前,我们不需要深入了解每行代码的具体作用。只要应该我们在这个稍微复杂的示例中使用了两个第三方依赖库:zapfasthttp

接下来,让我们尝试使用编译"hello, world"时的方法来编译“hellomod”项目中的 main.go 源文件,看看 Go 编译器会给出什么样的输出结果:

$go build main.go
main.go:4:3: no required module provides package github.com/valyala/fasthttp: go.mod file not found in current directory or any parent directory; see 'go help modules'
main.go:5:3: no required module provides package go.uber.org/zap: go.mod file not found in current directory or any parent directory; see 'go help modules'

从编译器的输出来看,go build 似乎在找一个名为 go.mod 的文件,来解决程序对第三方包的依赖决策问题。

2.2、go mod

Go 1.11 版本推出 modules 机制,简称 mod,更加易于管理项目中所需要的模块。模块是存储在文件树中的 Go 包的集合,其根目录中包含 go.mod 文件。 go.mod 文件定义了模块的模块路径,它也是用于根目录的导入路径,以及它的依赖性要求。每个依赖性要求都被写为模块路径和特定语义版本。

Go 1.11 开始,Go 允许在 $GOPATH/src 外的任何目录下使用 go.mod 创建项目。在 $GOPATH/src 中,为了兼容性,Go 命令仍然在旧的 GOPATH 模式下运行。从 Go 1.13 开始,go.mod模式将成为默认模式。

2.3、go mod 示例

现在,我们将通过以下命令为“hellomod”示例程序创建一个 go.mod 文件:

go mod init github.com/lizhengi/hellomod
go: creating new go.mod: module github.com/lizhengi/hellomod
go: to add module requirements and sums:go mod tidy

Ps:注意模块命名方式,如果你的项目要发布到 github,则命名为:github.com/<USERNAME>/<MODULE_NAME>

此时可以看到,go mod init 命令的执行结果是在当前目录下生成了一个 go.mod 文件:

$cat go.mod
module github.com/lizhengi/hellomodgo 1.17

实际上,一个 module 是多个包的集合,这些包与 module 一起进行版本控制、发布和分发。go.mod 文件所在的目录被视为声明的 module 的根目录。此时的 go.mod 文件内容相对简单,首行是用来声明 module 路径(module path)的。Module 本质上引入了命名空间的概念,这意味着 module 下每个包的导入路径都由 module path 和包所在的子目录名共同构成。

例如,在 hellomod 下有一个子目录 pkg/pkg1,那么 pkg1 中的包的导入路径将是 github.com/lizhengi/hellomod/pkg/pkg1。此外,go.mod 文件的最后一行是 Go 版本指示符,表示该 module 是基于特定 Go 版版本的 module 语义编写的。

此时,我们执行一下构建,Go 编译器输出结果是这样的

$go build main.go
main.go:4:2: no required module provides package github.com/valyala/fasthttp; to add it:go get github.com/valyala/fasthttp
main.go:5:2: no required module provides package go.uber.org/zap; to add it:go get go.uber.org/zap

你会看到,Go 编译器提示源码依赖 fasthttp 和 zap 两个第三方包,但是 go.mod 中没有这两个包的版本信息,我们需要按提示手工添加信息到 go.mod 中。

这个时候,除了按提示手动添加外,我们也可以使用 go mod tidy 命令,让 Go 工具自动添加:

$go mod tidy                       
go: finding module for package go.uber.org/zap
go: finding module for package github.com/valyala/fasthttp
go: downloading github.com/valyala/fasthttp v1.54.0
go: downloading go.uber.org/zap v1.27.0
go: found github.com/valyala/fasthttp in github.com/valyala/fasthttp v1.54.0
go: found go.uber.org/zap in go.uber.org/zap v1.27.0
go: downloading go.uber.org/multierr v1.10.0
go: downloading github.com/stretchr/testify v1.8.1
go: downloading go.uber.org/goleak v1.3.0
go: downloading github.com/andybalholm/brotli v1.1.0
go: downloading github.com/klauspost/compress v1.17.7
go: downloading github.com/valyala/bytebufferpool v1.0.0
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/pmezard/go-difflib v1.0.0

Ps:超时的话换个代理即可

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct

Go 工具不仅下载并添加了 hellomod 直接依赖的 zap 和 fasthttp 包的信息,还下载了这两个包的相关依赖包。go mod tidy 执行后,我们 go.mod 的最新内容变成了这个样子:

module github.com/bigwhite/hellomodgo 1.16require (github.com/valyala/fasthttp v1.28.0go.uber.org/zap v1.18.1
)

此时,go.mod 文件已经包含了 hellomod 直接依赖的包的信息。此外,在 hellomod 目录下还出现了一个名为 go.sum 的文件,这个文件记录了 hellomod 的直接依赖和间接依赖包的版本 hash 值,这用于校验本地包的真实性。在构建过程中,如果本地依赖包的 hash 值与 go.sum 文件中的记录不匹配,构建会被拒绝。

有了 go.mod 以及 hellomod 依赖的包版本信息后,我们再来执行构建:

$go build main.go
go: downloading go.uber.org/zap v1.27.0
go: downloading github.com/valyala/fasthttp v1.54.0
go: downloading go.uber.org/multierr v1.10.0
go: downloading github.com/andybalholm/brotli v1.1.0
go: downloading github.com/klauspost/compress v1.17.7
go: downloading github.com/valyala/bytebufferpool v1.0.0
$ls
go.mod    go.sum    main*    main.go

这次我们成功构建出了可执行文件 main,运行这个文件,新开一个终端窗口,在新窗口中使用 curl 命令访问该 http 服务:curl localhost:8081/foo/bar,我们就会看到服务端输出如下日志:

$./main
{"level":"info","ts":1716950836.531074,"caller":"hellomod/main.go:15","msg":"hello, go module","uri":"/foo/bar"}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/844065.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# TcpClient

TcpClient 自己封装的话&#xff0c;还是比较麻烦的&#xff0c;可以基于线程&#xff0c;也可以基于异步写&#xff0c;最好的办法是网上找个插件&#xff0c;我发现一个插件还是非常好用的&#xff1a;STTech.BytesIO.Tcp 下面是这个插件作者的帖子&#xff0c;有兴趣的可以…

Mac连接虚拟机(Linux系统)

1.确定虚拟机的IP地址 ifconfig //终端命令&#xff0c;查询ip地址 sudo apt install net-tools 安装完成后再次执行 ifconfig&#xff1a; 2.安装SSH&#xff08;加密远程登录协议&#xff09; (1).安装OpenSSH服务器软件包&#xff1a; sudo apt-get install openssh-ser…

监控易助力实现智能工厂数字化升级项目案例

随着工业4.0和智能制造的浪潮席卷全球&#xff0c;包头铝业&#xff08;以下简称“包铝”&#xff09;积极响应行业变革&#xff0c;启动了智能工厂升级项目。该项目旨在通过数字化转型&#xff0c;实现IT基础设施、动环设施以及物联网设施的互联互通&#xff0c;进而提升生产效…

小心审核失败,网上申请流量卡千万要注意这些!

最近有朋友私信小编&#xff1a;在网上申请的流量卡&#xff0c;信息填写完成提交审核呢&#xff0c;是不是就能发卡呢&#xff1f; 答案&#xff1a;不一定。 网上申请流量卡时&#xff0c;填写申请信息并提交审核这只是网报程序成功&#xff0c;接下来还要运营商审核&#…

python面向对象编程OOP

面向对象编程&#xff08;Object-Oriented Programming&#xff0c;简称OOP&#xff09;是Python编程中一种重要的编程范式。OOP通过使用类和对象来组织代码&#xff0c;使代码更易于管理和维护。下面是一些Python面向对象编程的关键概念和示例&#xff1a; 1. 类和对象 **类…

【惊艳视界】Perfectly Clear Workbench:让您的图像瞬间焕发生机!

在数字化时代&#xff0c;图像已成为我们生活中不可或缺的一部分。无论是摄影爱好者&#xff0c;还是专业设计师&#xff0c;都渴望拥有一款能够轻松提升图像质量的神奇工具。今天&#xff0c;我们为您带来了一款图像清晰处理软件的佼佼者——Perfectly Clear Workbench&#x…

最新!!2024上半年软考【高级】系统分析师 综合知识真题解析

2024上半年软考考试已经结束了&#xff0c;为大家整理了网友回忆版的系统分析师选择题真题及答案&#xff0c;总共30道题。 上半年考试的宝子们可以对答案预估分数&#xff01;准备下半年考的宝子可以提前把握考试知识点和出题方向&#xff0c;说不定会遇到相同考点的题目&…

【全开源】知识付费问答社区(FastAdmin+ThinkPHP)

此系统是一款基于FastAdmin和ThinkPHP开发的知识付费问答社区系统&#xff0c;提供全部前后台无加密源代码&#xff0c;拥有强大的付费提问、付费阅读、付费查看、付费邀请、全文搜索等功能模块&#xff0c;其整合了强大的标签模块和专区模块&#xff0c;让问题和文章更好的归类…

【WP|2】WordPress 高级函数和方法解析

在之前的文章中&#xff0c;我们讨论了 WordPress 的一些常用函数和方法。这篇文章将进一步探讨一些高级函数和方法&#xff0c;这些函数和方法能够帮助开发者更加精细地控制和定制 WordPress 网站&#xff0c;以实现更复杂和高级的功能。 一、高级主题函数 1. add_theme_supp…

python办公自动化——(三)替换PPT文档中图形数据-折线图

数据替换前 数据替换后 代码实现 # 单折线 pathE:\\13 python 下侧双x轴折线图\\ prs Presentation(path双x轴测试-01.pptx) data_timepd.read_excel(path"数据.xlsx",sheet_name单折线)ppt_9prs.slides…

DSP6657 GPIO学习

0 设备 创龙C6657Artix-7工业评估板 SEED-XDS560v2 1 实现功能 控制评估底板 LED1 、 LED2 、 LED3 每隔 0.5s 将状态同时翻转一次。 采用查询的方式控制评估版的KEY2控制LED2亮灭。 2 代码 2.1 GPIO驱动 2.1.1 c66x_gpio.c /* Compiler Header files */ #include …

vue3数字滚动依赖

名称&#xff1a;vue3-count-to 安装: npm install vue3-count-to --save 在main.js中全局注册: github:https://github.com/PanJiaChen/vue-countTo // main.js全局注册 import countTo from vue3-count-to app.use(countTo)在用到地页面引入使用 <template><count…

css让padding、border不占据宽度

CSS3 新增了 box-sizing 属性。 以前&#xff0c;如果指定 div 的宽度为 div { width: 120px;height: 120px;padding: 10px; } 则包含 padding&#xff0c;div 的实际宽度为 120px。 有时我们不希望 padding 影响到 div 的实际宽度。以前只能手动计算 width&#xf…

MySQL入门学习-查询进阶.DISTINCT

在 MySQL 中&#xff0c;DISTINCT 关键字用于查询结果中去除重复的记录。 一、在查询进阶中&#xff0c;除了 DISTINCT 之外&#xff0c;还有以下关键字&#xff1a; - GROUP BY&#xff1a; 根据指定的字段对结果进行分组&#xff0c;通常与聚合函数一起使用&#xff0c;例…

kexin2024年5月22日

在CLion上调试程序 使用程序的模板来调试程序 在下图中输入作为console窗口输入输入数据。 下面将程序记录一下 首先的是模板 //main.c /** * Description: * Caution&#xff1a;本地调试时&#xff0c;只编译运行这一个文件&#xff0c;不要链接solution.c&#xff01;…

初学JavaScript

什么是JavaScrip&#xff1a; JavaScript 是一种高级编程语言&#xff0c;主要用于网页开发。它是一种动态、弱类型的语言&#xff0c;可以在客户端&#xff08;浏览器&#xff09;中运行&#xff0c;并与 HTML 和 CSS 一起用于创建交互式网页。JavaScript 可以添加动态功能&a…

vba 基本操作

1. 获取多有的sheets 并对sheet 循环处理 Dim sheets As sheets Dim sheet As Worksheet Dim name As String Set sheets ThisWorkbook.Worksheets For Each sheet In sheetsIf sheet.name <> "Sheet1" Thenname sheet.nameEnd If Next sheet2. 添加一个工…

电脑误删除的文件怎么恢复?6个方法图文详解!

“我在电脑上误删除了一些比较重要的文件&#xff0c;现在不知道应该怎么操作了&#xff0c;有没有可以分享一下经验的朋友呀&#xff1f;” 在数字化世界的浪潮中&#xff0c;电脑成为了我们处理、存储和分享信息的重要工具。然而&#xff0c;随着我们对电脑的依赖日益加深&am…

深入理解@TableField注解的使用-MybatisPlus教程

TableField注解是MyBatis-Plus框架提供的一个功能&#xff0c;用于指定实体类属性与数据库表列的映射关系。当实体类的属性名称和数据库表的列名称不一致&#xff0c;或者需要指定一些特殊的处理逻辑时&#xff0c;可以使用TableField注解。 以下是TableField注解的一些常见用…

cfa三级大神复习经验分享系列(二)

嫌文章太长&#xff0c;我给大家一个备考简略总结&#xff0c;看完可以关闭。资料&#xff1a;note真题&#xff08;三级不用&#xff09;基础/强化班 note看两遍例题动手做两遍&#xff0c; 真题动手做三遍 其他&#xff0c;没有了&#xff0c;做好这些 高分pass 一&#xff…