Go模块管理及打包/编译

包管理

创建项目

创建文件夹

1
2
mkdir go-study
cd go-study

项目初始化

1
go mod init go-study

创建main.go文件:在项目根目录中创建一个名为main.go的文件,它将包含项目的入口点代码。

1
touch main.go

编写代码:在main.go文件中编写代码,这里是一个简单的示例:

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
fmt.Println("Hello, world!")
}

运行程序:在命令行中使用go run命令运行main.go文件。

1
go run main.go

如果一切顺利,你应该看到输出Hello, world!

假如项目下有一个zutils 文件夹,文件夹下代码如下

config_utils.go

1
2
3
4
5
6
7
8
9
10
package utils
import "fmt"

func init() {
fmt.Println("config初始化")
}

func ReadConfig() {
fmt.Println("读取配置")
}

引用方式为

1
2
3
import (
myutils "go-study/zutils"
)

调用方法

1
myutils.ReadConfig()

注意

对于 myutils"go-study/zutils"

  • go-studygo mod init go-study初始化的模块名,跟项目的文件夹名无关。
  • zutils是项目下的文件夹名,不是package名。
  • 调用的时候使用的是包名+方法名
  • myutils也不一定要是package名,这个是别名,可以定义为其它名称,假如我们定义为myfunc,则调用方法就是myfunc.ReadConfig()

引用

  1. 每个源代码文件开头都拥有一个package声明,表示该golang代码所属的package。

  2. 要生成golang可执行程序,必须建立一个名为mainpackage,并且在该package中必须包含一个名为main()的函数。

  3. 在golang工程中,同一个路径下只能存在一个package,一个package可以拆成多个源文件组成。

  4. import关键字导入的是package路径,而不是package名

    经常可见的import的目录名和源文件中使用的package名一致容易造成import关键字后即是package名的错觉,真正使用时,这两者可以不同。

  5. 如果package路径的文件夹名和package名不一致的时候要设置别名 如:

    1
    2
    3
    import (
    utils "ZDevOpsGo/zutils"
    )

模块化

GOPATH

这种方式是早期的方式,已经被Go.mod取代。

GOPATH 作为 Go 语言的一个环境变量, 可以理解为个人的工作区,每个工作区中都会有以代码包为基本组织形式的源码文件。

这个变量不能和Go的安装目录一样,这个目录用来存放Go源码,Go的可运行文件,以及相应的编译之后的包文件。

所以这个目录下面有三个子目录:src、bin、pkg

按照约定这三个目录的作用是:

  • src 存放你项目的源码 (xxx.go)
  • pkg 存放编译后生成的文件 (xxx.a)
  • bin 存放编译后生成的可执行文件 (如果你将 $GOPATH/bin 加入到 PATH 变量中后, 则可以直接使用编译好的程序)

也就是说我们创建项目必须要在GOPATH的src下

1
touch $GOPATH/src/demo01/main.go`

这样就会非常不方便

go.mod是golang1.11版本新引入的官方包管理工具用于解决之前没有地方记录依赖包具体版本的问题,方便依赖包的管理。

Go.mod

Go.mod其实就是一个Modules,关于Modules的官方定义为:

  • Modules是相关Go包的集合,是源代码交换和版本控制的单元。go命令直接支持使用Modules,包括记录和解析对其他模块的依赖性。

    Modules替换旧的基于GOPATH的方法,来指定使用哪些源文件。

  • Modules和传统的GOPATH不同,不需要包含例如src,bin这样的子目录,一个源代码目录甚至是空目录都可以作为Modules,只要其中包含有go.mod文件。

使用的前提条件

  • go的版本升级为1.11以上
  • 设置GO111MODULE

GO111MODULE有三个值:off, on和auto(默认值)。

  • GO111MODULE=off,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。

  • GO111MODULE=on,go命令行会使用modules,而一点也不会去GOPATH目录下查找。

  • GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。

    启用module功能可以分为两种情形:

    • 当前目录在GOPATH/src之外且该目录包含go.mod文件
    • 当前文件在包含go.mod文件的目录下面

模块管理

所有命令

常用

1
2
3
4
5
6
# 在当前目录初始化包管理器
go mod init psvmc.cn/myblog
# 下载模块到本地缓存
go mod download
# 添加缺失的模块并移除不使用的模块
go mod tidy

不常用

1
2
3
4
5
6
7
8
# 项目所有依赖的报告
go mod graph
# 把依赖复制到项目下的vendor目录
go mod vendor
# 校验一个模块是否被篡改过,查询某个常见的模块出错是否已被篡改
go mod verify
# 查看为什么需要依赖某模块,查询某个不常见的模块是否是哪个模块的引用
go mod why

示例

创建

1
2
3
4
mkdir myblog
cd myblog
go mod init psvmc.cn/myblog
go get github.com/kataras/iris/v12@master

创建main.go

输入如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "github.com/kataras/iris/v12"

func main() {
app := iris.Default()
app.Use(myMiddleware)

app.Handle("GET", "/", func(ctx iris.Context) {
ctx.JSON(iris.Map{"message": "hello world"})
})

// Listens and serves incoming http requests
// on http://localhost:8080.
app.Run(iris.Addr(":8080"))
}

func myMiddleware(ctx iris.Context) {
ctx.Application().Logger().Infof("Runs before %s", ctx.Path())
ctx.Next()
}

运行

1
go run main.go

或者编译后运行exe文件

1
go build

这样就可以访问

http://127.0.0.1:8080/

实际会生成go.mod

1
2
3
4
5
6
7
8
module psvmc.cn/myblog

go 1.16

require (
github.com/kataras/iris/v12 v12.1.8 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
)

编译

默认编译当前目录下的所有go文件

1
go build

编译同目录的多个源码文件时,可以在 go build 的后面提供多个文件名,go build 会编译这些源码,输出可执行文件

go build+文件列表的格式如下:

1
go build file1.go file2.go
  1. 如果是非main包,当你执行go build之后,它不会产生任何文件。

  2. 如果是main包,当你执行go build之后,它就会在当前目录下生成一个可执行文件exe

    如果你需要在$GOPATH/bin下生成相应的文件,需要执行go install,或者使用go build -o 路径/xxx.exe xxx.go

  3. 如果某个项目文件夹下有多个文件,而你只想编译某个文件,就可在go build之后加上文件名,

    例如go build xxx.go;go build命令默认会编译当前目录下的所有go文件。

  4. 你也可以指定编译输出的文件名。我们可以指定go build -o xxxx.exe,默认情况是模块名,如:module psvmc.cn/myblog会生成myblog.exe

  5. go build会忽略目录下以_.开头的go文件。

编译到对应平台

GOOS:目标平台的操作系统(darwin、freebsd、linux、windows)
GOARCH:目标平台的体系架构(386、amd64、arm) 交叉编译不支持 CGO 所以要禁用它

生成同平台下的

1
go build

如果在Windows上生成其他平台

目标Windows

build_win.bat

1
2
3
set GOOS=windows
set GOARCH=amd64
go build

目标Linux

在Windows上编译Linux环境需要安装GCC环境。

安装TDM-GCC 9.2.0 release

选择中间的那个,可以生成32位和64位的。

https://jmeubank.github.io/tdm-gcc/articles/2020-03/9.2.0-release

image-20221004144750261

64位Linux:

build_linux64.bat

1
2
3
4
set GOOS=linux
set GOARCH=amd64
set CGO_ENABLED=0
go build

ARM架构:

build_arm.bat

1
2
3
4
5
set GOOS=linux
set GOARCH=arm
set GOARM=6
set CGO_ENABLED=0
go build

一键打包脚本

仅程序

build_linux64.bat

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
@echo off  
set TARGET_FILE=xh-control-ws
set TARGET_DIR=_dist
set TARGET_DIR_PATH=%cd%\%TARGET_DIR%\

echo ---------------------------------
echo Delete Origin Dir
IF EXIST %TARGET_DIR_PATH% rd /S /Q %TARGET_DIR_PATH%
IF NOT EXIST %TARGET_DIR% mkdir %TARGET_DIR%

echo ---------------------------------
echo Build API
set GOOS=linux
set GOARCH=amd64
set CGO_ENABLED=0
go build

echo Copy API
move %TARGET_FILE% %TARGET_DIR_PATH% > NUL
xcopy /e /i "config" %TARGET_DIR_PATH%config > NUL

echo ---------------------------------
echo Success

pause

程序和WEB

build_linux64.bat

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
@echo off  
set TARGET_DIR=_dist
set TARGET_DIR_PATH=%cd%\%TARGET_DIR%\

echo Delete Origin Dir
IF EXIST %TARGET_DIR_PATH% rd /S /Q %TARGET_DIR_PATH%
IF NOT EXIST %TARGET_DIR% mkdir %TARGET_DIR%

echo ---------------------------------
echo Build API
set GOOS=linux
set GOARCH=amd64
set CGO_ENABLED=0
go build

echo Copy API
move "z-wiki" %TARGET_DIR_PATH% > NUL
xcopy /e /i "config" %TARGET_DIR_PATH%config > NUL

echo ---------------------------------
echo Build WEB
cd "z-wiki-web"
call npm run build > NUL
cd ..

echo Copy WEB
xcopy /e /i "web" %TARGET_DIR_PATH%web > NUL
move %TARGET_DIR_PATH%web\config_release.js %TARGET_DIR_PATH%web\config.js > NUL

echo ---------------------------------
echo Success

pause

运行

1
2
3
nohup ./myblog &
# 或者
nohup ./myblog > myblog.log 2>&1 &

其他命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#将后台任务切换到前台执行
fg
#可以将一个正在前台执行的命令放到后台,并且处于暂停状态。
ctrl + z
#将一个在后台暂停的命令,变成在后台继续执行。
#如果后台中有多个命令,可以用bg %jobnumber将选中的命令调出
bg
#查看后台运行的状态,jobs -l选项可显示所有任务的PID
jobs -l
#查看进程
ps -ef | grep "./myblog"
#或者
ps aux | grep "./myblog"

#杀掉对应的进程,
kill -9 进程id

更高级的用法如下:

这个表示直接通过command获取进程id并直接kill掉

1
ps aux | grep "./myblog" | grep -v grep | awk '{print $2}' | xargs kill -9