go pkg

what

每个 Go 文件都属于且仅属于一个包。一个包可以由许多以 .go 为扩展名的源文件组成 通常目录名

package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。 package main包下可以有多个文件,但所有文件中只能有一个main()方法,main()方法代表程序入口

导入

1
2
3
4
import (
    "crypto/rand"
    mrand "math/rand" // alternative name mrand avoids conflict
)
1
2
3
4
import (
    _ "github.com/revel/modules/testrunner/app"
    _ "guild_website/app"
)

_操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。

本包

在本包范围内 定义或声明 0 个或多个常量(const)、变量(var)和类型(type),这些对象的作用域都是全局的

导出

首字母大写才能在包外用

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
GOPATH/
    src/
        gopl.io/
            .git/
            ch1/
                helloworld/
                    main.go
                dup/
                    main.go
                ...
        golang.org/x/net/
            .git/
            html/
                parse.go
                node.go
                ...
    bin/
        helloworld
        dup
    pkg/
        darwin_amd64/
        ...
  • $GOPATH/src 存储源代码 $GOPATH/src/工程名
  • pkg子目录用于保存编译后的包的目标文件
  • bin子目录用于保存编译后的可执行程序
  • GOROOT用来指定Go的安装目录 自带的标准库包的位置 目录结构和GOPATH类似

内部包

internal包的内容只能被同一个父目录的包导入

net/http/internal/chunked内部包只能被net/http/httputil或net/http包导入, 但是不能被net/url包导入。不过net/url包却可以导入net/http/httputil包。

vendor

在执行 go build 或 go run 命令时,会按照以下顺序去查找包:

  • 当前包下的 vendor 目录
  • 向上级目录查找,直到找到 src 下的 vendor 目录
  • 在 GOROOT 目录下查找
  • 在 GOPATH 下面查找依赖包

cd $GOPATH/src/工程名/vendor

vendor 参数

程序启动流程

  • 程序的初始化和执行都起始于main包
  • main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次
  • 当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化
  • 执行init函数(如果有的话)
  • 等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化
  • 执行main包中的init函数(如果存在的话),最后执行main函数。

关于init

  • init函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等
  • 每个包可以拥有多个init函数
  • 包的每个源文件也可以拥有多个init函数
  • 同一个包中多个init函数的执行顺序Go语言没有明确的定义(说明)
  • 不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序
  • init函数不能被其他函数调用,而是在main函数执行之前,自动被调用

mod

依赖包的存放位置变更为$GOPATH/pkg 允许同一个package多个版本并存,且多个项目可以共享缓存的 module

GOPROXY=https://goproxy.cn

在 Go 支持 Go Modules 之后,编译时编译器会从工作目录(当前所在目录)开始并逐级向上查找是否具有 go.mod 文件。

  • 如果有,go.mod 文件中声明的 module 名称就视作 go.mod 所在的路径,然后以指定的 main 包为依赖入口,
  • 所有以 go.mod 中声明的 module 名称开头的导入路径都以 go.mod 所在的路径为相对路径进行包的查找导入。
  • 所有需要导入的路径中如果在 go.mod 中指定了版本,则从 $GOPATH/pkg/mod/ 下取得相应版本进行导入,
  • 如果没有被指定则从 $GOPATH/src/$GOROOT/src/ 中进行查找导入。
  • 如果没有,所有依赖均从 $GOPATH/src/$GOROOT/src/ 中进行查找导入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
module yizhisec-tracer-manager

go 1.13

replace src.xxx.com/xxxxxx/sigma-parser => ./sigma

require (
	github.com/ClickHouse/clickhouse-go v1.3.14
	github.com/confluentinc/confluent-kafka-go v1.3.0
	google.golang.org/grpc v1.27.1
	gopkg.in/yaml.v2 v2.4.0
	src.xxx.com/xxxxxx/sigma-parser v0.0.0-00010101000000-000000000000
)



## 常用命令

go mod
- init module-name 可以是当前目录名 生成go.mod
- tidy 拉取缺少的模块,移除不用的模块 可以在代码里面直接获取最新版本
- download download modules to local cache


- go list -m -u all 来检查可以升级的package,
- go get -u need-upgrade-package 升级后会将新的依赖版本更新到go.mod * 也可以使用
- go get -u 升级所有依赖
- go get package@version 将会升级到指定的版本号version






# 命令

## get

`go get`命令获取的代码是真实的本地存储仓库,而不仅仅只是复制源文件

`go get -u`命令只是简单地保证每个包是最新版本

go get github.com/golang/lint/golint

1
2
3
4
5
6
7
8

做精确的版本依赖管理,使用**vendor的目录**用于存储依赖包的固定版本的源代码,对本地依赖的包的版本更新也是谨慎和持续可控的

## build
命令编译命令行参数指定的每个包 检测包是可以正确编译 **不会重新编译没有发生变化的包,这可以使后续构建更快捷**

包的名字是main,go build将调用链接器在当前目录创建一个可执行程序;以**导入路径的最后一段作为可执行程序的名字**

cd 到main.go目录 go build

1
2
3
4
5
6
7
8
9
10
11

`go build -i`命令将安装每个目标所依赖的包。

`go run`命令实际上是结合了构建和运行的两个步骤


## install

和`go build`命令很相似,但是它会保存每个包的编译成果,而不是将它们都丢弃。
被编译的包会被保存到$GOPATH/pkg目录下,目录路径和
src目录路径对应,可执行程序被保存到$GOPATH/bin目录。

mod详解

版本号格式

img

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//module path, 一般采用仓库+module name的方式定义。这样我们获取一个module的时候,就可以到它的仓库中去查询,或者让go proxy到仓库中去查询。

// 如果你的版本已经大于等于2.0.0,按照Go的规范,你应该加上major的后缀,module path改成下面的方式
// module github.com/panicthis/modfile/v2

module github.com/panicthis/modfile
// go directive 指名你的代码所需要的Go的最低版本
go 1.16

// require段中列出了项目所需要的各个依赖库以及它们的版本
require (
  // 只是发布了v2.2.1的tag,并没有+incompatible后缀 这些库采用了go.mod的管理,但是不幸的是,虽然这些库的版major版本已经大于等于2了,但是他们的module path中依然没有添加v2、v3这样的后缀。
	github.com/cenk/backoff v2.2.1+incompatible
	github.com/coreos/bbolt v1.3.3
  // 库中的版本号就是一个伪版本号v0.0.0-20200330080233-e4ea8bd1cbed,这是go module为它生成的一个类似符合语义化版本2.0.0版本,实际这个库并没有发布这个版本
  // 这里的20200330080233是这次提交的时间,格式是yyyyMMddhhmmss, 而e4ea8bd1cbed就是这个版本的commit id,通过这个字段,就可以确定这个库的特定的版本。
	github.com/edwingeng/doublejump v0.0.0-20200330080233-e4ea8bd1cbed
  // 有些库后面加了indirect后缀,这又是什么意思的。如果用一句话总结,间接的使用了这个库,但是又没有被列到某个go.mod中
  
  // 当前项目依赖A,但是A的go.mod遗漏了B /A没有go.mod / A又依赖B,当对A降级的时候,降级的A不再依赖B B就标记indirect注释
	github.com/stretchr/objx v0.3.0 // indirect
	github.com/stretchr/testify v1.7.0
	go.etcd.io/bbolt v1.3.6 // indirect
	go.etcd.io/etcd/client/v2 v2.305.0-rc.1
	go.etcd.io/etcd/client/v3 v3.5.0-rc.1
	golang.org/x/net v0.0.0-20210610132358-84b48f89b13b // indirect
	golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 // indirect
)

// 你想在你的项目中跳过某个依赖库的某个版本,你就可以使用这个段
exclude (
	go.etcd.io/etcd/client/v2 v2.305.0-rc.0
	go.etcd.io/etcd/client/v3 v3.5.0-rc.0
)

// retract是go 1.16中新增加的内容,借用学术界期刊撤稿的术语,宣布撤回库的某个版本。
retract (
    v1.0.0 // 废弃的版本,请使用v1.1.0
)

replace (
  // etcd使用的grpc版本有问题,你也可以通过replace替换成所需的grpc版本。
  github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.3
  github.com/panicthis/A v1.1.0 => github.com/panicthis/R v1.8.0
  // 子项目
  src.yizhisec.com/applications/tdas-new/research/sigma-parser => ./sigma
)