I'm using the sample code of chapter 13 of The Go Programming Language as below:
$ 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)
}
}
First I compile the .c file:
gcc -I/usr/include -L/usr/lib -lbz2 --shared bzip2.c -fPIC -o libbzip2.so
The linux environment LD_LIBRARY_PATH contains ".", and then go build fails:
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
So how to fix it? I'm using ubuntu 18.04 LTS. Thanks a lot.
Don't run:
go build usebzip2.go
but rather:
go build
(and you don't need to invoke gcc
directly on bzip2.c
). When you use this process, you'll get many more (but different) errors because you have not put in the right directives before the:
import "C"
line. You need a comment (or series of comments) telling cgo about the functions you intend to provide, or providing those functions inline, and to direct the link phase to use -lbz2
. In particular, you will need to:
#include <bzlib.h>
bz2alloc
functionbz2free
functionbz2compress
functionLDFLAGS
to include -lbz2
The actual bz2alloc
and bz2free
are short and simple and therefore can be included directly in this header block:
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"
If you insert this and run go build
you will now see a different and more useful error:
./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)
which is of course because type writer
does not implement Write
.
(There's a completed version of exercise 13.3—not mine—at https://github.com/torbiak/gopl/tree/master/ex13.3. Note that they have augmented theirs to use locking as well, making it safe to call the write function from multiple goroutines simultaneously.)