Hướng dẫn cài thư viện dll code block

Chi tiết về sự khác biệt giữa thư viện tĩnh và động bạn đọc có thể xem thêm tại đây: What-is-the-difference-between-static-and-dynamic-linking.

Sau đây chúng ta sẽ đi vào cách dùng thư viện tĩnh và thư viện động trong CGO.

2.9.1. Dùng thư viện C tĩnh

Nếu mã nguồn C/C++ được dùng trong CGO có kích thước nhỏ thì cách đưa trực tiếp chúng vào chương trình là một ý tưởng phổ biến nhất, nhưng nhiều lúc chúng ta không tự xây dựng mã nguồn, hoặc quá trình xây dựng mã nguồn C/C++ rất phức tạp thì đây là lúc thư viện C tĩnh phát huy thế mạnh của mình.

Ở ví dụ đầu tiên, chúng ta sẽ xây dựng một thư viện tĩnh đơn giản được gọi là

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

0, chỉ có một hàm

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

1 trong thư viện dùng để lấy modulo của một tổng hai số cho một số thứ ba, những files của thư viện

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

0 đặt trong cùng một thư mục:

number/number.h : header chứa prototype của hàm:

int number_add_mod[int a, int b, int mod];

number/number.c: phần hiện thực hàm như sau:


# include "number.h"
int number_add_mod[int a, int b, int mod] {
    return [a+b]%mod;
}

Bởi vì CGO dùng lệnh GCC để biên dịch và liên kết mã nguồn C và Go lại, do đó thư viện tĩnh cần phải tương thích với GCC compiler.

Thư viện tĩnh

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

3 có thể được sinh ra bằng lệnh sau:

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

Sau khi sinh ra thư viện tĩnh mang tên

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

3, chúng ta dùng nó trong CGO.

main.go được tạo ra như sau:

package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}

Hai lệnh

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

5 trên dùng để biên dịch và liên kết mã nguồn với nhau:

  • Cờ

    // di chuyển tới thư mục mã nguồn $ cd ./number // biên dịch ra file object từ file mã nguồn $ gcc -c -o number.o number.c // lệnh tạo ra thư viện tĩnh libnumber.a từ file object // chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar $ ar rcs libnumber.a number.o

    6 : khai báo đường dẫn đến thư mục mã nguồn.
  • Cờ

    // di chuyển tới thư mục mã nguồn $ cd ./number // biên dịch ra file object từ file mã nguồn $ gcc -c -o number.o number.c // lệnh tạo ra thư viện tĩnh libnumber.a từ file object // chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar $ ar rcs libnumber.a number.o

    7 : khai báo đường dẫn đến thư viện tĩnh

    // di chuyển tới thư mục mã nguồn $ cd ./number // biên dịch ra file object từ file mã nguồn $ gcc -c -o number.o number.c // lệnh tạo ra thư viện tĩnh libnumber.a từ file object // chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar $ ar rcs libnumber.a number.o

    3 với search path

    // di chuyển tới thư mục mã nguồn $ cd ./number // biên dịch ra file object từ file mã nguồn $ gcc -c -o number.o number.c // lệnh tạo ra thư viện tĩnh libnumber.a từ file object // chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar $ ar rcs libnumber.a number.o

    9.

Chú ý rằng: đường dẫn trong liên kết không thể dùng relative path mà phải dùng một absolute path, ngoài ra đường dẫn không được chứa bất kỳ khoảng trắng nào.

Ví dụ :

package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}

0

Kết quả như sau:

$ go run main.go
3

Nếu chúng ta sử dụng thư viện tĩnh từ bên thứ ba, chúng ta cần phải tải chúng và cài đặt thư viện tĩnh đến một nơi phù hợp, sau đó đặc tả location của header files và libraries qua cờ

package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}

1 và


# include "number.h"
int number_add_mod[int a, int b, int mod] {
    return [a+b]%mod;
}

9 trong lệnh

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

5.

Trong môi trường Linux, có một lệnh pkg-config được dùng để truy vấn các tham số compile và link khi dùng các thư viện tĩnh, chúng ta có thể dùng lệnh pkg-config trực tiếp trong lệnh

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

5 để generate compilation và linking parameters, bạn có thể customize lệnh pkg-config với biến môi trường

package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}

5.

2.9.2. Sử dụng thư viện C động

Ý tưởng của thư viện động là shared library, các process khác nhau có thể chia sẻ trên cùng một tài nguyên bộ nhớ trên RAM hoặc đĩa cứng, nhưng hiện nay giá thành đĩa cứng và RAM cũng tương đối rẻ, nên hai vai trò sẽ trở nên không đáng quan tâm, do đó đâu là giá trị của thư viện động ở đây?

Từ góc nhìn của việc phát triển thư viện, thư viện động có thể tách biệt nhau và giảm thiểu rủi ro của việc xung đột trong khi liên kết, với những nền tảng như Windows, thư viện động là một cách khả thi để mở rộng các nền tảng biên dịch như

package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}

6.

Trong môi trường

package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}

6 dưới MacOS hoặc Linux, chúng ta có thể sinh ra thư viện động của một số thư viện với những lệnh sau:

$ cd number
$ gcc -shared -o libnumber.so number.c

Bởi vì, base names của thư viện động và tĩnh là

package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}

8, chỉ phần hậu tố là sẽ khác. Do đó trong mã nguồn của ngôn ngữ Go sẽ giống chính xác với phiên bản thư viện tĩnh.

package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}
package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}

9 sẽ tự động tìm

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

3 hoặc

$ go run main.go
3

1 ở bước liên kết trong thời gian biên dịch.

Với nền tảng Windows, chúng ta có thể dùng công cụ VC để sinh ra thư viện động [sẽ có một số thư viện Windows phức tạp chỉ có thể được build với

$ go run main.go
3

2]. Đầu tiên, chúng ta phải tạo một file định nghĩa cho

$ go run main.go
3

3 để quản lý các kí hiệu dùng để export thư viện động.

Nội dung của file

$ go run main.go
3

4 như sau:

LIBRARY number.dll
EXPORTS
number_add_mod

Dòng đầu tiên

$ go run main.go
3

5 sẽ chỉ ra tên của file và tên của thư viện động, và sau đó là mệnh đề

$ go run main.go
3

6 theo sau bởi một danh sách các tên dùng để export.

Giờ đây, chúng ta có thể dùng những lệnh sau để tạo ra thư viện động [cần dùng

$ go run main.go
3

2 tools].

$ cl /c number.c
$ link /DLL /OUT:number.dll number.obj number.def

Vào lúc này, một export library

$ go run main.go
3

8 sẽ sinh ra

$ go run main.go
3

9 cùng lúc. Nhưng trong

package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}

9, chúng ta không thể dùng

$ cd number
$ gcc -shared -o libnumber.so number.c

1 trong định dạng

$ cd number
$ gcc -shared -o libnumber.so number.c

2.

Để sinh ra định dạng

$ cd number
$ gcc -shared -o libnumber.so number.c

3 cho việc export library cần dùng

$ cd number
$ gcc -shared -o libnumber.so number.c

4:

$ dlltool -dllname number.dll --def number.def --output-lib libnumber.a

Một khi

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

3 được sinh ra, có thể dùng

// di chuyển tới thư mục mã nguồn
$ cd ./number
// biên dịch ra file object từ file mã nguồn
$ gcc -c -o number.o number.c
// lệnh tạo ra thư viện tĩnh libnumber.a từ file object
// chi tiết về lệnh ar có thể xem tại //linux.die.net/man/1/ar
$ ar rcs libnumber.a number.o

9 thông qua các link parameters.

Nên chú ý rằng, tại thời điểm thực thi, thư viện động cần được đặt ở cùng nơi để system có thể thấy. Trên Windows, bạn có thể đặt dynamic library và executable program trong cùng một thư mục, hoặc thêm đường dẫn tuyệt đối của thư mục trong khi dynamic library được đưa vào biến môi trường PATH. Trong MacOS, bạn cần phải thiết lập biến môi trường DYLD_LIBRARY_PATH. Trong hệ thống Linux, bạn cần thiết lập biến LD_LIBRARY_PATH.

2.9.3. Exporting thư việc C tĩnh

CGO không chỉ được dùng trong thư viện C tĩnh, các export functions được hiện thực bởi Go hoặc C static libraries. Chúng ta có thể dùng Go để hiện thực modulo addition function như phần trước ở ví dụ như sau đây.

Tạo

$ cd number
$ gcc -shared -o libnumber.so number.c

7 với nội dung như sau:


# include "number.h"
int number_add_mod[int a, int b, int mod] {
    return [a+b]%mod;
}

0

Theo như mô tả của tài liệu CGO, chúng ta cần export C function trong main package. Với cách xây dựng thư viện C tĩnh, hàm main trong main package được bỏ qua, và hàm C sẽ đơn giản được export. Xây dựng các lệnh sau:


# include "number.h"
int number_add_mod[int a, int b, int mod] {
    return [a+b]%mod;
}

1

Khi sinh ra thư viện tĩnh

$ cd number
$ gcc -shared -o libnumber.so number.c

8, cgo cũng sẽ sinh ra file

$ cd number
$ gcc -shared -o libnumber.so number.c

9.

Nội dung của

$ cd number
$ gcc -shared -o libnumber.so number.c

9 sẽ như sau:


# include "number.h"
int number_add_mod[int a, int b, int mod] {
    return [a+b]%mod;
}

2

Phần ngữ pháp

package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}

1 được thêm vào để dùng đồng thời trên cả ngôn ngữ C và C++. Nội dung của phần lõi sẽ định nghĩa hàm number_add_mod để được export.

Sau đó chúng ta tạo ra file _test_main.c để kiểm tra việc sinh ra C static library [bên dưới là phần prefix dùng cho việc xây dựng C static library để bỏ qua file đó].


# include "number.h"
int number_add_mod[int a, int b, int mod] {
    return [a+b]%mod;
}

3

Biên dịch và chạy chúng với những lệnh sau:


# include "number.h"
int number_add_mod[int a, int b, int mod] {
    return [a+b]%mod;
}

4

2.9.4. Exporting thư viện C động

Quá trình exporting một thư viện động bằng CGO sẽ tương tự như một thư viện tĩnh, ngoại trừ build mode sẽ thay đổi c-shared và output file name được đổi thành

package main
//
# cgo CFLAGS: -I./number
//
# cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//
# include "number.h"
import "C"
import "fmt"
func main[] {
    fmt.Println[C.number_add_mod[10, 5, 12]]
}

2.

Chủ Đề