Go 语言的编译模式,简单的来说就是在使用 go build
或 go install
两个命令编译代码的时候要生成什么样的文件。
使用方式简单来说就是
go build -buildmode=<mode>
或
go install -buildmode=<mode>
例如我们今天要讲的 C 语言源码归档模式 c-archive
。
其实吧, C 语言源码归档模式 c-archive
就是把 go
语言的代码编译成 C 语言静态链接库
。
c-charve 模式的几个主要点:
- 编译的时候
-buildmode=c-archive
- 编译结果只会有一个
.h
和一个.a
文件,这就是archive
归档的由来,也就是不管go
源码里导入了多少第三方库和标准库,多少代码,结果只有 2 个文件 - 所有的 结构
struct
和接口interface
和函数func
默认是不导出的,也就是你不能调用,只有添加了// export <方法名>
注释的东西才会被导出。 - 所有需要导出的方法必须处于
main
包下,其它包下的方法是不会被导出的。 go
语言包里必须包含import "C"
代码
范例
-
首先使用下面的命令行创建项目
mkdir bm cd bm go mod init bm touch main.go
-
使用你最喜欢的编辑器打开
main.go
文件然后复制下面的代码// 文件 main.go package main import "C" // 这是必须的 import "fmt" /** * 所有需要在静态库中使用的代码,都必须 export */ //export SayHello func SayHello(name string) { fmt.Printf("func in Golang SayHello says: Hello, %s!\n", name) } //export SayHelloByte func SayHelloByte(name []byte) { fmt.Printf("func in Golang SayHelloByte says: Hello, %s!\n", string(name)) } //export SayBye func SayBye() { fmt.Println("func in Golang SayBye says: Bye!") } func main() { // main() 方式没有任何实质代码,但又是必须的 }
-
然后我们使用下面的命令来编译
go build -buildmode=c-archive
编译有点耗时,具体多久取决于你加载了多少类库,编译完成后我们可以在当前名录下发现几个新文件
yufei@twle.cn bm % ls bm.a bm.h go.mod main.go main.md
-
bm.a
和bm.h
就是我们编译出来的文件。bm.a
是一个二进制文件,没啥好看的,我们来看看bm.h
/* Code generated by cmd/cgo; DO NOT EDIT. */ /* package bm */ #line 1 "cgo-builtin-export-prolog" #include <stddef.h> /* for ptrdiff_t below */ #ifndef GO_CGO_EXPORT_PROLOGUE_H #define GO_CGO_EXPORT_PROLOGUE_H #ifndef GO_CGO_GOSTRING_TYPEDEF typedef struct { const char *p; ptrdiff_t n; } _GoString_; #endif #endif /* Start of preamble from import "C" comments. */ /* End of preamble from import "C" comments. */ /* Start of boilerplate cgo prologue. */ #line 1 "cgo-gcc-export-header-prolog" #ifndef GO_CGO_PROLOGUE_H #define GO_CGO_PROLOGUE_H typedef signed char GoInt8; typedef unsigned char GoUint8; typedef short GoInt16; typedef unsigned short GoUint16; typedef int GoInt32; typedef unsigned int GoUint32; typedef long long GoInt64; typedef unsigned long long GoUint64; typedef GoInt64 GoInt; typedef GoUint64 GoUint; typedef __SIZE_TYPE__ GoUintptr; typedef float GoFloat32; typedef double GoFloat64; typedef float _Complex GoComplex64; typedef double _Complex GoComplex128; /* static assertion to make sure the file is being used on architecture at least with matching size of GoInt. */ typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; #ifndef GO_CGO_GOSTRING_TYPEDEF typedef _GoString_ GoString; #endif typedef void *GoMap; typedef void *GoChan; typedef struct { void *t; void *v; } GoInterface; typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; #endif /* End of boilerplate cgo prologue. */ #ifdef __cplusplus extern "C" { #endif extern void SayHello(GoString p0); extern void SayHelloByte(GoSlice p0); extern void SayBye(); #ifdef __cplusplus } #endif
内容非常长,总的来说分为三大块:
C
语言开发相关的头文件导入boilerplate cgo prologue
Go
语言相关内置的类型在C
语言下重新定义extern
相关的函数就是我们自定义的需要导出的函数
-
那要怎么使用我们导出的
bm.a
和bm.h
文件呢?在当前目录下新建一个
main.c
文件,然后,如果你会
C
语言开发,那就相当的简单了,话不多说,直接上代码// file main.c #include <stdio.h> #include "bm.h" int main() { printf("This is a C Application.\n"); GoString name = {(char*)"Jane", 4}; SayHello(name); GoSlice buf = {(void*)"Jane", 4, 4}; SayHelloByte(buf); SayBye(); return 0; }
几个注意点:
- 传递给静态库里的函数的参数必须是 *重新定义的符合 Go 类型的类型
。 比如上面的
{(char)"Jane", 4}` 字符串。
接着我们使用
gcc
来编译gcc main.c bm.a
然后运行一下
// linux or mac ./a.out // window ./a
输出结果如下
This is a C Application. func in Golang SayHello says: Hello, Jane! func in Golang SayHelloByte says: Hello, Jane! func in Golang SayBye says: Bye!
- 传递给静态库里的函数的参数必须是 *重新定义的符合 Go 类型的类型
这样我们就了解了 c-archive
模式。
目前尚无回复