乐趣区

关于golang:13-理解包导入路径的含义

Go 语言是应用包(package)作为根本单元来组织源码的,Go 程序就是这些包链接起来而构建的。与 C 语言的头文件蕴含机制相比则是“先进”了许多。

即使是每次编译都是从头开始。防止了 C 语言那种通过头文件剖析依赖的微小开销。Go 语言以包为根本构建单元的构建模型,依赖剖析非常简单。

一 构建过程

Go 编译速度快从三个方面剖析:
1.Go 要求每个源文件在结尾处显式地 import 所有依赖的包,Go 编译器不用读取和解决整个文件就能够确定其依赖的包列表;

2.Go 要求包之间不能存在循环依赖。因为无环,包能够被独自编译,也能够并行编译;

3. 已编译的 Go 包对应的指标文件 (xxx.o 或 xxx.a) 中,录了如下
1 该包自身的导出符号信息。
2 还记录了其所依赖包的导出符号信息。

这样,Go 编译器在编译某包 M 时,针对 M 依赖的每个包导入(比方:导入包 N),只需读取一个指标文件即可(比方:N 包编译成的指标文件,该指标文件中曾经蕴含了 N 包的依赖包的导出信息),而无需再读取其余文件中的信息了。

通过 package 关键字申明 Go 源文件所属的包:
// xx.go
package x
… …

上述源码示意:文件 xx.go 是包 x 的一部分。
应用 import 关键字导入依赖的规范库包或第三方包:
import (

"fmt"     // 规范库包导入“x/y/z"  // 第三方包导入

)

func main() {

c.Func1()
fmt.Println("Go!Go!Go!")

}

看到下面代码都会想到将 import 前面的”z”、“fmt”与 c.FuncName()和 fmt.Println()中的 c 和 fmt 认同一个语法元素:包名。然而当前深刻学习 Go 语言后,发现并非这样。比方施行分布式音讯框架 nsq 提供的官网 client 包时,包导入如下:
import“github.com/nsqio/go-nsq”
然而应用导出函数的时候,咱们不是 go-nsq.FuncName(), 而是 nsq.FuncName:
consumer,_:=nsq.NewConsumer(“write_order”, “ch”, config)

你可能会问最初一个分段到底代表什么?是包名称?是一个门路?
1 Go 程序构建过程

和其余支流动态编译语言一样,Go 语言的程序构建简略说是由编译(compile)和链接(link)两个阶段。

一个非 main 包在编译后会对应生成一个.a 文件,该文件能够了解为是 Go 包的指标文件(是通过 pack 工具($GOROOT/pkg/tool/darwin_amd64/pack)对.o 文件打包后造成的.a). 默认状况下在编译过程中.a 文件生成长期目录下,除非应用 go install 装置到 $GOPATH/pkg 下(Go1.11 版本前),否则你看不到.a 文件。如果构建可执行程序,那么.a 文件会在构建可执行程序的链接阶段起应用。

规范库包的源码文件在 $GOROOT/src 上面,而对应的 .a 文件寄存在 $GOROOT/pkg/darwin_amd64 下(以 MacOS 上为例; 如果是 linux,则是 linux_amd64)

那么构建 Go 程序时,编译器会从新编译依赖包的源文件还是间接链接包的.a 文件呢?
在应用第三方包的时候,当第三方包源代码存在且对应的.a 已装置的状况下,编译器链接的仍是依据第三方包最新源代码编译进去的.a 文件,而不是之前曾经装置到
$GOPATH/pkg/darwin_amd64 上面的指标文件。

那 Go 规范库中的包也是这样的吗?
默认状况下对于规范库中的包,编译器间接链接的是 $GOROOT/pkg/darwin_amd64 下的.a 文件。

二 路径名? 包名?

通过下面的常识,晓得了编译器在编译过程中必然要应用的是编译单元(包)所依赖的包的源码。而编译器要找到依赖包的源码文件就须要晓得依赖包的源码门路。这个门路由两局部组成:

1. 根底搜寻门路
2. 包导入门路

根底搜寻包是一个全局的设置,上面介绍一下:

所有包(规范库还是第三方包)的源码根底搜寻门路都包含 $GOROOT/src

在上述根底搜寻门路的根底上,不同版本的根底搜寻门路有不同:
1.11 之前

   $GOPATH/src

1.11-1.112 有三种:

  经典 gopath 模式,GO111MODULE=off,$GOPATH/src
  module-aware 模式,GO111MODULE=on,$GOPATH/pkg/mod
  auto 模式,GO111MODULE=auto,在 $GOPATH/src 门路下,与 gopath 模式雷同;在 $GOPATH/src 门路外且蕴含 go.mod,与 module-aware 模式雷同。

1.13 有两种

经典 gopath 模式,GO111MODULE=off,$GOPATH/src
module-aware 模式下,GO111MODULE=on/auto,$GOPATH/pkg/mod;

1.13 后

只有 module-aware 模式,即只在 module 缓存的目录下搜寻包的源码。

搜寻门路第二局部,是位于每个包源码文件头部的包导入门路。根底搜寻门路与包导入门路联合在一起,Go 编译器就能够确定一个包的所有依赖包的源码门路的汇合,这样汇合形成了 Go 编译器的源码搜寻门路空间。

三 同一源码的依赖包在同一源码搜寻门路下包名抵触,怎么办?

package main

import (

    "github.com/xxx/p1/pkg/pkg11"“github.com/xxx/p2/pkg/pkg11"

)

func main() {

    pkg1.Func1()

}
当你有这样的 import 的时候,运行 main,间接报错。会通知你包抵触。如何解决呢?

package main

import (

    p1 "github.com/xxx/p1/pkg/pkg11"
    p2“github.com/xxx/p2/pkg/pkg11"

)

func main() {

    p1.Func1()
    p2.Func1()

}
这样就 ok 了,解决了问题。

退出移动版