Go 编译构建的一些细节
发现自己竟然没有怎么认真研究过 go 的编译构建命令。
结论前置
- go run 专门用来运行命令源码文件的命令,一般用来运行单个文件
- go build 主要是用于测试编译。编译某个包或者项目,在当前目录下生成可执行文件
- go install 编译并安装代码包或者源码文件的。
- go get 用于从远程代码仓库(比如 Github )上下载代码包并更新 mod
- go 1.17 之后 go get 只下载源码,并将依赖添加到 go.mod。get install 用来下载和安装三方库
go 命令常用选项
下面是细节。
go run
专门用来运行命令源码文件的命令,不是用来运行所有 go 源码文件的
go run 命令只能接受一个命令源码文件以及若干个库源码文件(必须同属于 main 包)作为文件参数,且不能接受测试源码文件。它在执行时会检查源码文件的类型。如果参数中有多个或者没有命令源码文件,那么 go run 命令就只会打印错误提示信息并退出,而不会继续执行。
go run -n
是 go run
命令的一个选项,用于执行编译和运行 Go 源文件,但不实际运行。它会打印出编译时将要执行的命令,而不会真正运行该命令。这对于调试构建脚本或查看编译命令是否正确非常有用。
- 临时文件生成: 在使用
go run
命令时,会生成临时工作目录(通常在系统的临时目录下),其中包含编译过程中产生的临时文件和中间文件。 - 导入文件(importcfg 文件):
importcfg
文件用于指定程序在编译时所需的导入包。这个文件在编译过程中被使用,而不是在运行时。 - compile 命令和生成 link 文件: 在编译过程中,
compile
命令用于将源码文件编译成归档文件(archive)。生成的link
文件记录了编译过程中用到的依赖信息。 - 生成可执行文件: 最后,
exe
文件是通过连接编译生成的归档文件和link
文件,并将其与源码文件一起打包成可执行文件。 - go run 命令生成的文件:
go run
命令会在执行过程中生成两个文件,一个是编译生成的归档文件(通常具有.a
扩展名),另一个是最终的可执行文件。
举例:
go run app/user/*.go
等价于什么呢?
go run app/user/*.go
命令用于直接运行指定目录下的所有 Go 源代码文件。它的等价操作包括:
- 编译并运行每个
.go
文件中的代码。 - 如果代码中有
main
函数,则执行该函数;如果没有,则不执行。 - 该命令只在开发阶段使用,不会生成可执行文件,而是临时编译和执行代码。
因此,go run app/user/*.go
与手动编译并执行每个文件的操作等价。
问题:会递归查找所有目录吗?
不会。go run app/user/*.go
只会在 app/user/
目录下查找直接位于该目录下的 .go
文件,而不会递归查找其子目录中的文件。如果需要递归查找所有子目录中的 .go
文件,可以使用 ./...
来代替 *
,例如 go run ./...
。go run ./...
是一个 Go 语言的命令,用于编译并运行当前目录(以及子目录)中的所有 Go 源码文件。这个命令会查找当前目录下的所有 .go
文件,并且假定它们属于同一个包,然后尝试编译它们。
这里的 ./...
是一个模式匹配表达式,用于匹配当前目录及其所有子目录中的所有文件和目录。在 Go 命令中,这种模式匹配通常用于指定源码文件的路径。
具体来说:
.
表示当前目录。...
(三个点)是一个通配符,表示递归地匹配当前目录下的所有子目录。
因此,go run ./...
命令会执行以下操作:
- 编译当前目录及其所有子目录中的所有
.go
文件。 - 将编译后的代码链接成一个可执行文件。
- 运行这个可执行文件。
这个命令在处理包含多个源码文件的包时非常有用,尤其是当你想要快速运行和测试整个包中的代码时。然而,需要注意的是,这个命令不会递归地编译子目录中的子目录,它只编译当前目录及其直接文件和子目录中的 .go
文件。
如果你的 Go 项目结构中,每个功能或模块位于不同的包中,那么使用 go run ./...
可能不会按预期工作,因为它会尝试编译所有匹配到的源码文件,而不管它们是否属于同一个包。在这种情况下,你可能需要更精细地指定要编译的文件或目录。
再看 go build
另外,使用 dlv 进行远程调试的话,必须 build 出可执行文件 main 之后,才可以。
所以这个时候需要执行
go
go build -o main
然后运行就可以开始监听了。
区别于 go build main.go
区分 Go 编译单个文件和目录下所有文件
go build main.go
和 go build -o main
都是用于构建 Go 应用程序的命令,但它们之间有一些区别:
go build main.go
:这个命令告诉 Go 编译器要编译名为main.go
的文件,并生成一个默认的可执行文件,文件名与包名相同(如果main.go
中的包名是main
,则生成的可执行文件为main
)。go build -o main
:这个命令告诉 Go 编译器要编译当前目录下的所有文件,并将生成的可执行文件命名为main
。使用-o
标志可以指定生成的可执行文件的名称,而不是使用默认的包名。
总的来说,go build main.go
适用于单个文件的构建,而 go build -o main
适用于多个文件的构建,并且可以指定生成的可执行文件的名称。
-
用途:
go build
命令主要用于编译测试。它会编译指定的源码文件或代码包及其依赖。 -
普通包 vs. main 包:
- 对于普通包:执行
go build
后不会生成任何文件。
举例:
- 对于 main 包:只执行
go build
会在当前目录下生成一个可执行文件。要在$GOPATH/bin
目录下生成相应的可执行文件,需要执行go install
或者使用go build -o 路径/可执行文件
。
- 对于普通包:执行
-
单文件 vs. 多文件:
- 如果某个文件夹下有多个文件,而只想编译其中的某一个文件,可以在
go build
之后加上文件名,例如go build a.go
。 - 默认情况下,
go build
会编译当前目录下的所有 go 文件。
- 如果某个文件夹下有多个文件,而只想编译其中的某一个文件,可以在
-
指定输出文件名: 可以使用
-o
标志指定编译输出的文件名。默认情况下,输出文件名为包名(对于非 main 包)或第一个源文件的文件名(对于 main 包)。 -
忽略文件:
go build
会忽略目录下以"_"或"."开头的 go 文件。 -
跨平台编译: 如果源代码需要针对不同的操作系统进行处理,可以根据不同的操作系统后缀来命名文件。
-
库源码文件编译: 对于库源码文件,
go build
只会进行检查性的编译,而不会生成任何结果文件。 -
示例: 执行
go build
编译命令源码文件时,会在该命令的执行目录中生成一个可执行文件。如果跟了代码包导入路径作为参数,则该代码包及其依赖都会被编译。 -
执行过程: 类似于
go run
,go build
在编译后会将生成的可执行文件重命名并移动到当前目录下。
参考:
https://zhuanlan.zhihu.com/p/619500945
go install 和 go get
go get
和 go install
是 Go 语言中两个常用的命令,它们虽然有一些相似之处,但在功能上有一些不同。
-
go get:
go get
命令用于从远程仓库中获取并安装指定的包或模块。它通常用于获取项目的依赖项。- 如果执行
go get
命令时指定了包或模块的路径,它将尝试从远程仓库中下载该包或模块,并将其放置在$GOPATH/src
目录下的相应位置。 go get
还会安装该包或模块所依赖的其他包或模块。
-
go install:
go install
命令用于编译并安装指定的包或可执行文件。- 如果执行
go install
命令时指定了包的路径,它将编译该包并将生成的二进制文件安装到$GOPATH/bin
目录(或$GOBIN
目录)下。 - 如果执行
go install
命令时指定了可执行文件的路径,它将编译该可执行文件并将生成的二进制文件安装到$GOPATH/bin
目录(或$GOBIN
目录)下。
在实践中,通常情况下,我们使用 go get
来获取项目的依赖项,而使用 go install
来构建并安装我们自己的代码或可执行文件。
go install 命令
-
用途: 编译并安装代码包或者源码文件。
-
流程: 分为两步:
- 第一步是生成结果文件(可执行文件或者.a 包)。
- 第二步是将编译好的结果移到
$GOPATH/pkg
或者$GOPATH/bin
。
-
可执行文件: 一般由带有
main
函数的 Go 文件产生,具有函数入口,可以直接运行。 -
.a 应用包: 一般由不包含
main
函数的 Go 文件产生,没有函数入口,只能被调用。
go get 命令
-
用途: 从远程代码仓库(如 GitHub)下载并安装代码包。
-
注意:
go get
命令将当前代码包下载到$GOPATH
中的第一个工作区的src
目录,并进行安装。- 从 Go 1.17 版本开始,
go get
仅用于下载库和更新 mod 文件,并不会执行安装操作。
其他命令
go clean 命令是用来移除当前源码包里面编译生成的文件,这些文件包括
_obj/ 旧的 object 目录,由 Makefiles 遗留
_test/ 旧的 test 目录,由 Makefiles 遗留
_testmain.go 旧的 gotest 文件,由 Makefiles 遗留
test.out 旧的 test 记录,由 Makefiles 遗留
build.out 旧的 test 记录,由 Makefiles 遗留
*.[568ao] object 文件,由 Makefiles 遗留
DIR(.exe) 由 go build 产生
DIR.test(.exe) 由 go test -c 产生
MAINFILE(.exe) 由 go build MAINFILE.go 产生
go fmt 命令主要是用来帮你格式化所写好的代码文件。
go test 命令,会自动读取源码目录下面名为*_test.go 的文件,生成并运行测试用的可执行文件。默认的情况下,不需要任何的参数,它会自动把你源码包下面所有 test 文件测试完毕,当然你也可以带上参数,详情请参考 go help testflag
go doc 命令其实就是一个很强大的文档工具。
go fix 用来修复以前老版本的代码到新版本,例如 go1 之前老版本的代码转化到 go1
go version 查看 go 当前的版本
go env 查看当前 go 的环境变量
go list 列出当前全部安装的 package