ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

13 理解包导入路径的含义

2021-01-21 13:57:52  阅读:161  来源: 互联网

标签:文件 13 GOPATH 路径 导入 pkg Go 源码


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 工具( G O R O O T / p k g / t o o l / d a r w i n a m d 64 / p a c k ) 对 . o 文 件 打 包 后 形 成 的 . a ) . 默 认 情 况 下 在 编 译 过 程 中 . a 文 件 生 成 临 时 目 录 下 , 除 非 使 用 g o i n s t a l l 安 装 到 GOROOT/pkg/tool/darwin_amd64/pack)对.o文件打包后形成的.a).默认情况下在编译过程中.a文件生成临时目录下,除非使用go install安装到 GOROOT/pkg/tool/darwina​md64/pack)对.o文件打包后形成的.a).默认情况下在编译过程中.a文件生成临时目录下,除非使用goinstall安装到GOPATH/pkg下(Go1.11版本前),否则你看不到.a文件。如果构建可执行程序,那么.a文件会在构建可执行程序的链接阶段起使用。

标准库包的源码文件在 G O R O O T / s r c 下 面 , 而 对 应 的 . a 文 件 存 放 在 GOROOT/src 下面,而对应的 .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, G O P A T H / p k g / m o d a u t o 模 式 , G O 111 M O D U L E = a u t o , 在 GOPATH/pkg/mod auto 模式,GO111MODULE=auto,在 GOPATH/pkg/modauto模式,GO111MODULE=auto,在GOPATH/src 路径下,与 gopath 模式相同;在$GOPATH/src 路径外且包含 go.mod,与 module-aware 模式相同。

1.13有两种
经典gopath 模式,GO111MODULE=off, G O P A T H / s r c m o d u l e − a w a r e 模 式 下 , G O 111 M O D U L E = o n / a u t o , GOPATH/src module-aware 模式下,GO111MODULE=on/auto, GOPATH/srcmodule−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了,解决了问题。

在这里插入图片描述

标签:文件,13,GOPATH,路径,导入,pkg,Go,源码
来源: https://blog.csdn.net/weixin_43633725/article/details/112936487

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有