golang学习2:使用package和module

说明

我因为是golang的新手,而golang的包管理之前经过很多变化,我也不是很明白。
go1.11发布了官方的包管理工具 Go Module, 从 Go 1.13 开始,Module模式将成为默认模式。所以这就变成了golang标准的包管理工具。

我自己根据自己想到的情况摸索下使用方法。

package和module的概念

package(包)类似于c++的命名空间,每个package可以有多个源码文件,但是必须位于同一层目录。package内可以有子目录,只不过子目录下就是另一个package的源码了。package内可以使用同一个package的所有资源,不需要像c一样先声明再使用。当import一个package后就可以使用其中的大写字母开头的函数和变量了。
module(模块)就像一个语言级的项目工程文件,每个程序的顶层都要有一个,这个文件主要为了记录该项目依赖的包名和包版本。

一般在代码根目录下执行“go mod init NAME”来生成本module的go.mod文件。如果增删了依赖项,可以用“go mod tidy”来更新go.mod文件。

这是我自己生成的内容:

$ cat go.mod
module HAHA  //这里HAHA是我的module的名字

go 1.16

CASE1:单个本地module,包含子package

我现在磁盘随机的一个位置建立了一个文件夹,在里面编写我的代码

$ tree
.
├── go.mod
├── main.go
└── mymod1
    └── mymod1.go

1 directory, 3 files

$ cat main.go
package main

import (
        "HAHA/mymod1"
        "fmt"
)

func main() {
        fmt.Printf("Hello, world.\n")
        mymod1.Do_mymod1()
}

$ cat mymod1/mymod1.go
package mymod1

import (
        "fmt"
)

func Do_mymod1() {
        fmt.Printf("I'm mymod1\n")
}

$ go build main.go //此时会生成二进制可执行文件main
$ ./main //执行该文件
Hello, world.
I'm mymod1

注意:我的环境是go1.16,我用相对目录"./mymod1"就提示找不到包。网上有人使用相对目录导入package的例子,而我只能通过包名来导入mymod1这个package。可能是golang版本差异导致的,或者它们的环境下没开GO111MODULE。我试着修改GO111MODULE反正改不了,不知道是不是go1.16给限制死了,也没心情测试GO111MODULE=off的情况了。

把要导入的package放到GOROOT/src应该可以找到,但是这是系统标准库的目录,自己不应该改动这个目录。

$ go build main.go
main.go:5:2: package mymod2 is not in GOROOT (/usr/local/go/src/mymod2)

CASE2:使用网络上的module

上面的情况是在同一个module内使用另一个package, 我们也可以从网上导入一个module来使用。这里我选择参考链接《Go Module详细使用教程,包管理不在难》里的例子:

package main

import (
	"net/http"
	
	"github.com/labstack/echo"
)

func main() {
	e := echo.New()
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, World!")
	})
	e.Logger.Fatal(e.Start(":1323"))
}

执行 go run server.go 运行代码会发现 go mod 会自动查找依赖自动下载。这些内容会被安装在GOPATH/pkg/mod目录下,并且go.mod也会记录下它们的版本号。

这里就体现出go的强大之处了,可以自动从网上下载依赖。而且如果你把自己的代码上传的github等网站,别人引用你的代码也就会很方便。

CASE3:使用本地的module

如果我本地的网络不好,或者某些公司内部无法联网,那怎么引用别的module呢。其实也可以预先下载到本地然后引用的。

这样的例子在:https://github.com/drov0/GolangLocalModulesExample

代码目录如下:

$ tree
.
├── README.md
├── go.mod
├── hello
│   ├── go.mod
│   └── hello.go
├── main.go
└── utils
    ├── addAndGreet.go
    └── go.mod

可以看到这里有三个go.mod, 表示三个module。最外层的module会引用后面两个。
main.go的内容如下:

package main

import (
	"example.org/hello"
	"example.org/utils"
	"fmt"
)

func main() {
	fmt.Println(hello.Hello("martin"))
	fmt.Println(utils.AddAndGreet("martin", 2, 3))
}

如何才能让go在本地查找这两个模块呢,关键是在go.mod中使用replace关键字告诉go编译器从本地查找这两个模块。最外层的go.mod内容如下:

module example.com/localmodexample

go 1.13

require (
	example.org/hello v0.0.0
	example.org/utils v0.0.0

)

replace (
	example.org/hello => ./hello
	example.org/utils => ./utils
)

CASE4-失败的例子:把本地的子package改成module

有了上面的例子,我想着把上面“CASE1”里mymod1改成module岂不是很简单,我直接在import的时候用相对路径引入,然后在go.mod里添加对mymod1的require不就行了吗,replace操作我手动处理好了, 应该不用replace这个字段了。结果很打脸,就是不行的。

$ tree
.
├── go.mod
├── main.go
└── mymod1
    ├── go.mod
    └── mymod1.go

1 directory, 4 files

$ cat go.mod
module HAHA

go 1.16

require (
        mymod1 v0.0.0
)

$ cat main.go
package main

import (
        //"HAHA/mymod1"
        "./mymod1"
        "fmt"
)

func main() {
        fmt.Printf("Hello, world.\n")
        mymod1.Do_mymod1()
}

$ go build main.go  ######啊啊啊啊报错了#####
go: mymod1@v0.0.0: missing go.sum entry; to add it:
        go mod download mymod1

我把replace字段理解成C语言里的宏预处理了,以为它会对import关键字后面的内容进行简单的字符替换,实际上不是这样的。把它理解成GCC里的编译选项-L更合适,也就是指明库的位置。 当然这个位置可以是当前目录下,也可以是别的任意位置。

CASE5-成功的例子:把本地的子package改成module

按照“CASE3”里的思路重新改写CASE1的代码:

$ tree
.
├── go.mod
├── main.go
└── mymod1
    ├── go.mod
    └── mymod1.go

1 directory, 4 files

$ cat go.mod
module HAHA

go 1.16

require (
        mymod1 v0.0.0
)

replace (
        mymod1 => ./mymod1
)

$ cat main.go
package main

import (
        //"HAHA/mymod1"
        "mymod1"
        "fmt"
)

func main() {
        fmt.Printf("Hello, world.\n")
        mymod1.Do_mymod1()
}

$ ./main
Hello, world.
I'm mymod1

后记

golang在1.11版本前的包管理方式比较孱弱,1.11版本才引入mod模式。网上很多代码都是非mod模式下的,不具有参考价值。作为新手,后面只关注mod模式下如何使用功能代码即可,老的那么多历史包袱不管也罢。

作为C/C++程序员,学习golang本身也是开眼界的过程。go本身是一个比较完备的语言,有比较多的辅助命令,等以后用到了再学即可,没必要刚开始就看个遍。

自己一个人摸索比较麻烦,也没个人讨论下。如果本文有错误也正常,毕竟是学习笔记。

参考材料

Go Modules wiki

Golang Package 与 Module 简介

Go Module详细使用教程,包管理不在难

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值