标签:绝对路径 Requires 令行 int lan extern 相同 def 编译
直接使用源码
;链接静态库
;链接动态库
。直接使用源码就是在import "C"
之前的注释部分包含C代码,或者在当前包中包含C/C++源文件。链接静态库和动态库的方式比较类似,都是通过在LDFLAGS选项指定要链接的库方式链接。本节我们主要关注在CGO中如何使用静态库和动态库相关的问题。静态库因为是静态链接,最终的目标程序并不会产生额外的运行时依赖,也不会出现动态库特有的跨运行时资源管理的错误
。不过静态库对链接阶段会有一定要求:静态库一般包含了全部的代码,里面会有大量的符号,如果不同静态库之间出现了符号冲突则会导致链接的失败
。number
,库中只有一个number_add_mod
函数,用于表示数论中的模加法运算。number库的文件都在number目录下。int number_add_mod(int a, int b, int mod);
number/number.c
对应函数的实现:#include "number.h"
int number_add_mod(int a, int b, int mod) {
return (a+b)%mod;
}
$ cd ./number
$ gcc -c -o number.o number.c
$ ar rcs libnumber.a number.o
package main
//#cgo CFLAGS: -I./number
//#cgo LDFLAGS: -L${SRCDIR} -lnumber
//
//#include "number.h"
import "C"
import "fmt"
func main() {
fmt.Println(C.number_add_mod(10, 5, 12))
}
CFLAGS通过-I./number将number库对应头文件所在的目录加入头文件检索路径
。LDFLAGS通过-L${SRCDIR}/number将编译后number静态库所在目录加为链接库检索路径,-lnumber表示链接libnumber.a静态库
。需要注意的是,在链接部分的检索路径不能使用相对路径(C/C++代码的链接程序所限制),我们必须通过cgo特有的${SRCDIR}变量将源文件对应的当前目录路径展开为绝对路径(因此在windows平台中绝对路径不能有空白符号)。z_link_number_c.c
文件如下:#include "./number/number.c"
go get
或go build
之类命令的时候,CGO就是自动构建number库对应的代码。这种技术是在不改变静态库源代码组织结构的前提下,将静态库转化为了源代码方式引用。这种CGO包是最完美的。pkg-config
命令可以查询要使用某个静态库或动态库时的编译和链接参数。我们可以在#cgo命令中直接使用pkg-config
命令来生成编译和链接参数。而且还可以通过PKG_CONFIG
环境变量定制pkg-config
命令。因为不同的操作系统对pkg-config
命令的支持不尽相同,通过该方式很难兼容不同的操作系统下的构建参数。不过对于Linux等特定的系统,pkg-config
命令确实可以简化构建参数的管理。关于pkg-config的使用细节在此我们不深入展开,大家可以自行参考相关文档。macOS和Linux
系统下的gcc环境,我们可以用以下命令创建number库的的动态库:$ cd number
$ gcc -shared -o libnumber.so number.c
package main
//#cgo CFLAGS: -I./number
//#cgo LDFLAGS: -L${SRCDIR} -lnumber
//
//#include "number.h"
import "C"
import "fmt"
func main() {
fmt.Println(C.number_add_mod(10, 5, 12))
}
LIBRARY number.dll
EXPORTS
number_add_mod
LIBRARY指明动态库的文件名
,然后的EXPORTS语句之后是要导出的符号名列表
。$ cl /c number.c
$ link /DLL /OUT:number.dll number.obj number.def
number.lib
的导出库。但是在CGO中我们无法使用lib格式的链接库。mingw
工具箱中的dlltool
命令完成:$ dlltool -dllname number.dll --def number.def --output-lib libnumber.a
DYLD_LIBRARY_PATH
环境变量。而对于Linux系统来说,需要设置LD_LIBRARY_PATH
环境变量。package main
import "C"
func main() {}
//export number_add_mod
func number_add_mod(a, b, mod C.int) C.int {
return (a + b) % mod
}
$ go build -buildmode=c-archive -o number.a
#ifdef __cplusplus
extern "C" {
#endif
extern int number_add_mod(int p0, int p1, int p2);
#ifdef __cplusplus
}
#endif
_test_main.c
的C文件用于测试生成的C静态库(用下划线作为前缀名是让为了让go build构建C静态库时忽略这个文件):#include "number.h"
#include <stdio.h>
int main() {
int a = 10;
int b = 5;
int c = 12;
int x = number_add_mod(a, b, c);
printf("(%d+%d)%%%d = %d\n", a, b, c, x);
return 0;
}
$ gcc -o a.out _test_main.c number.a
$ ./a.out
c-shared
,输出文件名改为number.so
而已:$ go build -buildmode=c-shared -o number.so
$ gcc -o a.out _test_main.c number.so
$ ./a.out
go help buildmode
命令可以查看C静态库和C动态库的构建说明:-buildmode=c-archive
Build the listed main package, plus all packages it imports,
into a C archive file. The only callable symbols will be those
functions exported using a cgo //export comment. Requires
exactly one main package to be listed.
-buildmode=c-shared
Build the listed main package, plus all packages it imports,
into a C shared library. The only callable symbols will
be those functions exported using a cgo //export comment.
Requires exactly one main package to be listed.
main
包导出,然后才能在生成的头文件包含声明的语句。但是很多时候我们可能更希望将不同类型的导出函数组织到不同的Go包中,然后统一导出为一个静态库或动态库。我们需要自己提供导出C函数对应的头文件
(因为CGO无法为非main包的导出函数生成头文件)。package number
import "C"
//export number_add_mod
func number_add_mod(a, b, mod C.int) C.int {
return (a + b) % mod
}
package main
import "C"
import (
"fmt"
_ "./number"
)
func main() {
println("Done")
}
//export goPrintln
func goPrintln(s *C.char) {
fmt.Println("goPrintln:", C.GoString(s))
}
$ go build -buildmode=c-archive -o main.a
main.a
静态库的同时,也会生成一个main.h
头文件。但是main.h头文件中只有main包中导出的goPrintln函数的声明,并没有number子包导出函数的声明。其实number_add_mod函数在生成的C静态库中是存在的,我们可以直接使用。#include <stdio.h>
void goPrintln(char*);
int number_add_mod(int a, int b, int mod);
int main() {
int a = 10;
int b = 5;
int c = 12;
int x = number_add_mod(a, b, c);
printf("(%d+%d)%%%d = %d\n", a, b, c, x);
goPrintln("done");
return 0;
}
手工方式声明了goPrintln和number_add_mod两个导出函数
。这样我们就实现了从多个Go包导出C函数了。标签:绝对路径 Requires 令行 int lan extern 相同 def 编译
原文地址:https://www.cnblogs.com/binHome/p/12994846.html