GO-代码打包

Go ,编译型语言,性能很好,原生高并发,跨平台,语法简单,有自动GC,相对安全的指针操作。其编译特性在目前各种代码/数据保密需求的环境下同样也成为一种优势。
Python ,解释性语言,语法简单,更加贴近英语的书写习惯,不过性能不好,但又因为他的基于C解释器,很容易去和C的库进行通讯,因此也被称为“胶水语言”。

实例代码

为了便于操作和理解,一下所有打包过程均基于如下示例代码(main.go)进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int = 200
var ret int
/* 调用函数并返回最大值 */
ret = max(a, b)
fmt.Printf( "最大值是 : %d\n", ret )
}
/* 函数返回两个数的最大值 */
//export max
func max(num1, num2 int) int {
/* 定义局部变量 */
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}

非常重要:,export 表示把go的函数映射到python的函数调用,如果没有export(格式必须是 //export func_name),那么就不能生成.h文件,python也就无法调用该函数
同时有几个注意事项:

  1. //export add 在函数定义之前添加上注释来告诉编译器哪些定义可以被 C 引用,注意 // 和 export 之前不能有空格,否则会导出失败的。
  2. main() main 函数一定不能少,即使没有任何一行代码也没事;
  3. import "C" 这个必须要加载 Go 源文件前,这一点必须做,应该就是告诉编译器我要即将编译的软件需要做为 C 的库而不直接是二进制。这个包也提供一些功能让 Go 去直接操作 C 的数据结构等等。

编译成可执行文件(直接运行)

go本身支持对代码进行打包,所以可以直接通过build 将代码打包可执行文件。

1
2
3
go build main.go
# 指定打包后文件名称,默认命名为go代码文件前缀
go build -o max main.go

跨平台交叉编译

和其他的编程语言不同,很多编程语言都是基于编译平台生成可执行的二进制文件,并不能跨平台生成可执行文件(比如没办法再windows下生成Linux可用的执行文件),但是再使用go进行编译的过程中发现,GO支持这个功能,可以实现windows进行代码开发,编译后直接再Linux上运行。

Mac 下编译 Linux 和 Windows 64位可执行程序

1
2
3
4
# 编译成Linux可用
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
# 编译成Windows可用
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build

Linux 下编译 Mac 和 Windows 64位可执行程序

1
2
3
4
# 编译成Mac可用
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build
# 编译成Windows可用
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build

Windows 下编译 Mac 和 Linux 64位可执行程序

1
2
3
4
5
6
7
8
9
10
#编译成Mac可用
SET CGO_ENABLED=0
SET GOOS=darwin
SET GOARCH=amd64
go build
# 编译成Linux可用
SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=amd64
go build

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

编译成共享静态库(cython调用)

编译成c可以调用的库,在go代码的导入阶段必须执行 import "C"

1
go build  --buildmode=c-archive   -o test.lib.a test.go

其中,–buildmode=c-archive 告诉 Go 来编译一个静态库,-o 是输出文件的名字,这里我们输出为 library.a。此时,目录下应该有一个 library.a 的文件和 library.h 的头文件。

打包成动态链接库(python调用)

1
go build  --buildmode=c-shared -o library.so test.go

打包完成后会生成有两个文件 library.solibrary.h
然后我们就可以在python代码中调用相关的功能了

1
2
3
4
5
6
7
8
9
10
11
import ctypes
lib = ctypes.cdll.LoadLibrary("library.so") # 对应生成的动态链接库代码
# 简单的调用
print(lib.max(1, 2)) # max就是对应打包代码中 export的函数
# 系统的调用,需要对函数的输入和输出的数据类型进行声明
GoInt64 = ctypes.c_int64
GoInt = GoInt64
add = lib.add
add.argtypes = [GoInt64, GoInt64]
add.restype = GoInt64
res = add(GoInt(1), GoInt(2))

在整体使用的时候,需要注意相关变量的定义,这部分后续有具体业务使用时,在进行细化补充。
其中各类数值类型在不同语言环境下的对应关系如下,

ctypes Python C Go.h Go
c_bool bool _Bool bool bool
c_byte int char GoInt8 int8
c_ubyte unsigned char int GoUint8 uint8
c_short short int GoInt16 int16
c_ushort unsigned short int GoUint16 uint16
c_int int int GoInt32 int32
c_uint int unsigned int GoUint32 uint32
c_ulong int unsigned long GoUint32 uint32
c_longlong int __int64 or long long GoInt64 or GoInt int64 or int
c_ulonglong int unsigned __int64 or unsigned long long GoUint64 uint64
c_size_t int size_t SIZE_TYPE GoUintptr uintptr
c_ssize_t int ssize_t or Py_ssize_t Go中无定义
c_float float float GoFloat32 float32
c_double float double GoFloat64 float64
c_longdouble float long double GoFloat64 float64
无定义 float float _Complex GoComplex64 complex64
无定义 float double _Complex GoComplex128 complex128 or complex
其中各类字符串类型在不同语言环境下的对应关系如下,
ctypes Python C
——— ——————– ————————-
c_char 长度为1的bytes char
c_wchar 长度为1的string wchar_t
c_char_p bytes object or None char* (NUL terminated)
c_wchar_p string or None wchar_t* (NUL terminated)
c_char 长度为1的bytes char
除了上述内容,还需要注意一些结构体的对应、指针、切片、通道的处理等问题,后续可以更详细的研究后述的相关参考资料。
参考资料:https://zhuanlan.zhihu.com/p/518374146
-------------本文结束感谢您的阅读-------------