GO-02.代码打包

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
24
25
26
27
28
29
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
11
#编译成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
12
13
14
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

-------------本文结束感谢您的阅读-------------