cgo 调用共享库:找不到库或函数?
cgo calling share library: cannot find lib or function?
我正在使用 The Go Programming Language 第 13 章的示例代码如下:
$ cat bzip2.c
#include <bzlib.h>
int bz2compress(bz_stream *s, int action,
char *in, unsigned *inlen, char *out, unsigned *outlen) {
s->next_in = in;
s->avail_in = *inlen;
s->next_out = out;
s->avail_out = *outlen;
int r = BZ2_bzCompress(s, action);
*inlen -= s->avail_in;
*outlen -= s->avail_out;
s->next_in = s->next_out = NULL;
return r;
}
$ cat usebzip2.go
// Package bzip provides a writer that uses bzip2 compression (bzip.org).
package main
import "C"
import (
"io"
"log"
"os"
"testing"
"unsafe"
)
type writer struct {
w io.Writer // underlying output stream
stream *C.bz_stream
outbuf [64 * 1024]byte
}
// Close flushes the compressed data and closes the stream.
// It does not close the underlying io.Writer.
func (w *writer) Close() error {
if w.stream == nil {
panic("closed")
}
defer func() {
C.BZ2_bzCompressEnd(w.stream)
C.bz2free(w.stream)
w.stream = nil
}()
for {
inlen, outlen := C.uint(0), C.uint(cap(w.outbuf))
r := C.bz2compress(w.stream, C.BZ_FINISH, nil, &inlen,
(*C.char)(unsafe.Pointer(&w.outbuf)), &outlen)
if _, err := w.w.Write(w.outbuf[:outlen]); err != nil {
return err
}
if r == C.BZ_STREAM_END {
return nil
}
}
}
// NewWriter returns a writer for bzip2-compressed streams.
func NewWriter(out io.Writer) io.WriteCloser {
const blockSize = 9
const verbosity = 0
const workFactor = 30
w := &writer{w: out, stream: C.bz2alloc()}
C.BZ2_bzCompressInit(w.stream, blockSize, verbosity, workFactor)
return w
}
func main() {
w := NewWriter(os.Stdout)
if _, err := io.Copy(w, os.Stdin); err != nil {
log.Fatalf("bzipper: %v\n", err)
}
if err := w.Close(); err != nil {
log.Fatalf("bzipper: close: %v\n", err)
}
}
首先我编译.c文件:
gcc -I/usr/include -L/usr/lib -lbz2 --shared bzip2.c -fPIC -o libbzip2.so
linux环境LD_LIBRARY_PATH包含“.”,然后go build失败:
go build usebzip2.go
# command-line-arguments
/tmp/go-build677611698/b001/_x002.o: In function `_cgo_22d5d7fabfe4_Cfunc_bz2compress':
/tmp/go-build/cgo-gcc-prolog:118: undefined reference to `bz2compress'
collect2: error: ld returned 1 exit status
那么如何解决呢?我正在使用 ubuntu 18.04 LTS。非常感谢。
不要 运行:
go build usebzip2.go
而是:
go build
(并且您不需要直接在 bzip2.c
上调用 gcc
)。当你使用这个过程时,你会得到更多(但不同的)错误,因为你没有在:
之前输入正确的指令
import "C"
行。您需要一条注释(或一系列注释)告诉 cgo 您打算提供的功能,或内联提供这些功能,并指示 link 阶段使用 -lbz2
。特别是,您需要:
#include <bzlib.h>
- 提供一个
bz2alloc
函数
- 提供一个
bz2free
函数
- 为您的
bz2compress
函数提供声明
- 设置
LDFLAGS
以包含 -lbz2
实际的 bz2alloc
和 bz2free
简短,因此可以直接包含在这个 header 块中:
package main
/*
#cgo LDFLAGS: -lbz2
#include <bzlib.h>
#include <stdlib.h>
bz_stream *bz2alloc() { return calloc(1, sizeof(bz_stream)); }
int bz2compress(bz_stream *s, int action,
char *in, unsigned *intlen, char *out, unsigned *outlen);
void bz2free(bz_stream* s) { free(s); }
*/
import "C"
如果你插入这个和 运行 go build
你现在会看到一个不同的更有用的错误:
./usebzip2.go:60:2: cannot use w (type *writer) as type io.WriteCloser in return argument:
*writer does not implement io.WriteCloser (missing Write method)
这当然是因为 type writer
没有实现 Write
.
(练习 13.3 的完整版本——不是我的——在 https://github.com/torbiak/gopl/tree/master/ex13.3。请注意,他们已经扩充了他们的版本以使用锁定,从而可以安全地同时从多个 goroutine 调用 write 函数。)
我正在使用 The Go Programming Language 第 13 章的示例代码如下:
$ cat bzip2.c
#include <bzlib.h>
int bz2compress(bz_stream *s, int action,
char *in, unsigned *inlen, char *out, unsigned *outlen) {
s->next_in = in;
s->avail_in = *inlen;
s->next_out = out;
s->avail_out = *outlen;
int r = BZ2_bzCompress(s, action);
*inlen -= s->avail_in;
*outlen -= s->avail_out;
s->next_in = s->next_out = NULL;
return r;
}
$ cat usebzip2.go
// Package bzip provides a writer that uses bzip2 compression (bzip.org).
package main
import "C"
import (
"io"
"log"
"os"
"testing"
"unsafe"
)
type writer struct {
w io.Writer // underlying output stream
stream *C.bz_stream
outbuf [64 * 1024]byte
}
// Close flushes the compressed data and closes the stream.
// It does not close the underlying io.Writer.
func (w *writer) Close() error {
if w.stream == nil {
panic("closed")
}
defer func() {
C.BZ2_bzCompressEnd(w.stream)
C.bz2free(w.stream)
w.stream = nil
}()
for {
inlen, outlen := C.uint(0), C.uint(cap(w.outbuf))
r := C.bz2compress(w.stream, C.BZ_FINISH, nil, &inlen,
(*C.char)(unsafe.Pointer(&w.outbuf)), &outlen)
if _, err := w.w.Write(w.outbuf[:outlen]); err != nil {
return err
}
if r == C.BZ_STREAM_END {
return nil
}
}
}
// NewWriter returns a writer for bzip2-compressed streams.
func NewWriter(out io.Writer) io.WriteCloser {
const blockSize = 9
const verbosity = 0
const workFactor = 30
w := &writer{w: out, stream: C.bz2alloc()}
C.BZ2_bzCompressInit(w.stream, blockSize, verbosity, workFactor)
return w
}
func main() {
w := NewWriter(os.Stdout)
if _, err := io.Copy(w, os.Stdin); err != nil {
log.Fatalf("bzipper: %v\n", err)
}
if err := w.Close(); err != nil {
log.Fatalf("bzipper: close: %v\n", err)
}
}
首先我编译.c文件:
gcc -I/usr/include -L/usr/lib -lbz2 --shared bzip2.c -fPIC -o libbzip2.so
linux环境LD_LIBRARY_PATH包含“.”,然后go build失败:
go build usebzip2.go
# command-line-arguments
/tmp/go-build677611698/b001/_x002.o: In function `_cgo_22d5d7fabfe4_Cfunc_bz2compress':
/tmp/go-build/cgo-gcc-prolog:118: undefined reference to `bz2compress'
collect2: error: ld returned 1 exit status
那么如何解决呢?我正在使用 ubuntu 18.04 LTS。非常感谢。
不要 运行:
go build usebzip2.go
而是:
go build
(并且您不需要直接在 bzip2.c
上调用 gcc
)。当你使用这个过程时,你会得到更多(但不同的)错误,因为你没有在:
import "C"
行。您需要一条注释(或一系列注释)告诉 cgo 您打算提供的功能,或内联提供这些功能,并指示 link 阶段使用 -lbz2
。特别是,您需要:
#include <bzlib.h>
- 提供一个
bz2alloc
函数 - 提供一个
bz2free
函数 - 为您的
bz2compress
函数提供声明 - 设置
LDFLAGS
以包含-lbz2
实际的 bz2alloc
和 bz2free
简短,因此可以直接包含在这个 header 块中:
package main
/*
#cgo LDFLAGS: -lbz2
#include <bzlib.h>
#include <stdlib.h>
bz_stream *bz2alloc() { return calloc(1, sizeof(bz_stream)); }
int bz2compress(bz_stream *s, int action,
char *in, unsigned *intlen, char *out, unsigned *outlen);
void bz2free(bz_stream* s) { free(s); }
*/
import "C"
如果你插入这个和 运行 go build
你现在会看到一个不同的更有用的错误:
./usebzip2.go:60:2: cannot use w (type *writer) as type io.WriteCloser in return argument:
*writer does not implement io.WriteCloser (missing Write method)
这当然是因为 type writer
没有实现 Write
.
(练习 13.3 的完整版本——不是我的——在 https://github.com/torbiak/gopl/tree/master/ex13.3。请注意,他们已经扩充了他们的版本以使用锁定,从而可以安全地同时从多个 goroutine 调用 write 函数。)