您的位置:首页 > 编程语言 > Go语言

Golang使用pkg-config自动获取头文件和链接库的方法

2016-06-20 00:00 567 查看
为了能够重用已有的C语言库,我们在使用Golang开发项目或系统的时候难免会遇到Go和C语言混合编程,这时很多人都会选择使用cgo。 话说cgo这个东西可算得上是让人又爱又恨,好处在于它可以让你快速重用已有的C语言库,无需再用Golang重造一遍轮子,而坏处就在于它会在一定程度 上削弱你的系统性能。关于cgo的种种劣迹,Dave Cheney大神在他的博客上有一篇专门的文章《cgo is not Go》,感兴趣的同学可以看一看。但话说回来,有时候为了快速开发满足项目需求,使用cgo也实在是不得已而为之。

在Golang中使用cgo调用C库的时候,如果需要引用很多不同的第三方库,那么使用#cgo CFLAGS:和#cgo LDFLAGS:的方式会引入很多行代码。首先这会导致代码很丑陋,最重要的是如果引用的不是标准库,头文件路径和库文件路径写死的话就会很麻烦。一旦第 三方库的安装路径变化了,Golang的代码也要跟着变化,所以使用pkg-config无疑是一种更为优雅的方法,不管库的安装路径有何变化,我们都不 需要修改Go代码,接下来本博主就用一个简单的例子来说明如何在cgo命令中使用pkg-config。

首先假定我们在路径/home/ubuntu/third-parties/hello下安装了一个名称为hello的第三方C语言库,其目录结构如下所示,在hello_world.h中只定义了一个接口函数hello,该函数接收一个char *字符串作为变量并调用printf将其打印到标准输出。

# tree /home/ubuntu/third-parties/hello/

/home/ubuntu/third-parties/hello/

├── include

│ └── hello_world.h

└── lib

├── libhello.so

└── pkgconfig

└── hello.pc

为了保证pkg-config能够找到这个C语言库,我们要为这个库生成一个描述文件,也就是lib/pkgconfig目录下的hello.pc,其内容如下,有不了解该配置文件内容的看客们可以去搜索一下pkg-config的相关文档。

# cat hello.pc

prefix=/home/ubuntu/third-parties/hello

exec_prefix=${prefix}

libdir=${exec_prefix}/lib

includedir=${exec_prefix}/include

Name: hello

Description: The hello library just for testing pkgconfig

Version: 0.1

Libs: -lhello -L${libdir}

Cflags: -I${includedir}

完成pkg-config描述文件的创建后,还需要将该描述文件的路径信息添加到PKG_CONFIG_PATH环境变量中,只有这样 pkg-config才能正确获取这个C语言库的相关信息。此外,我们还需要将该C语言库的库文件路径添加到LD_LIBRARY_PATH环境变量中, 具体命令如下:

# export PKG_CONFIG_PATH=/home/ubuntu/third-parties/hello/lib/pkgconfig

# pkg-config --list-all | grep libhello

libhello libhello - The hello library just for testing pkgconfig

# export LD_LIBRARY_PATH=/home/ubuntu/third-parties/hello/lib

在完成以上一系列准备工作之后,我们就可以开始编写Golang代码了,以下是Golang调用C语言接口的代码示例,我们只需要#cgo pkg-config: libhello和#include < hello_world.h >两行语句即可实现对hello函数的调用。如果C语言库的安装路径发生了变化,只需修改hello.pc这个描述文件即可,Golang代码无需重新修改和编译。

package main

// #cgo pkg-config: libhello

// #include < stdlib.h >

// #include < hello_world.h >

import "C"

import (

"unsafe"

)

func main() {

msg := "Hello, world!"

cmsg := C.CString(msg)

C.hello(cmsg)

C.free(unsafe.Pointer(cmsg))

}

最后,编译该程序代码,查看可执行程序是否正确链接了C语言库,执行程序验证能否正确调用库函数功能。

# go build hello_world.go

# ldd hello_world

linux-vdso.so.1 => (0x00007ffff63d3000)

libhello.so => /home/ubuntu/third-parties/hello/lib/libhello.so (0x00007fc31c0e1000)

libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fc31bec3000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc31bafe000)

/lib64/ld-linux-x86-64.so.2 (0x00007fc31c2e3000)

# ./hello_world

Hello, world!

在以上步骤中需要关注的有两个地方:1)创建C语言库的pkg-config配置文件并将配置文件的路径添加到环境变量 PKG_CONFIG_PATH中;2)C语言库文件的路径添加到环境变量LD_LIBRARY_PATH中,如果没有这一步,Go语言程序可以编译成 功,但是可执行文件无法正确连接到C语言库,会出现如下情况:

# ldd hello_world

linux-vdso.so.1 => (0x00007fffa49e2000)

libhello.so => not found

libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007feb0fe93000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007feb0face000)

/lib64/ld-linux-x86-64.so.2 (0x00007feb100b1000)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: