使用二进制形式发布go package

我们在使用Go进行开发的时候, 经常会使用到第三方的库, 这时候我们一般都会通过go get到github.com、bitbucket或者自己私有库中去拉取第三库的源代码。 今天正好群里有网友问能不能将自己开发的库以二进制形式提供给用户,我就顺便整理了一下。

以二进制方式提供库的动机可能是为了保护自己公司的知识产权,也有可能是从安全的角度考虑,避免一些关键信息的泄漏等等,这不是本文讨论的范围。

虽然以前的版本能够曲折的实现使用二进制的库,但是正式提供的方案是在Go 1.7中。 在 Go 1.7 Release Notes提到:

This release adds experimental, minimal support for building programs using binary-only packages, packages distributed in binary form without the corresponding source code. This feature is needed in some commercial settings but is not intended to be fully integrated into the rest of the toolchain. For example, tools that assume access to complete source code will not work with such packages, and there are no plans to support such packages in the “go get” command.

Package build文档中也有相关的介绍:

It is possible to distribute packages in binary form without including the source code used for compiling the package. To do this, the package must be distributed with a source file not excluded by build constraints and containing a "//go:binary-only-package" comment. Like a build constraint, this comment must appear near the top of the file, preceded only by blank lines and other line comments and with a blank line following the comment, to separate it from the package documentation. Unlike build constraints, this comment is only recognized in non-test Go source files.

The minimal source code for a binary-only package is therefore:

1
2
3
4
> //go:binary-only-package
>
> package mypkg
>

The source code may include additional Go code. That code is never compiled but will be processed by tools like godoc and might be useful as end-user documentation.

你只需要提供一个编译好的库, 同时提供为这个package提供一个源文件。这个源文件不用包含任何代码逻辑,只需增加//go:binary-only-package指令即可(注意//go:...之间不要加空格)。 这样用户在使用的时候,就可以直接使用你这个二进制的库了。

当然,为了用户方便,比如一些工具和IDE的支持,你可以将exported的类型定义到这个文件中。并且, 只需提供一些"虚假"的实现即可,只是为了IDE工具知道这个package包含的exported类型而已。

这个特性的实现是由Russ Cox在这两个提交中实现的: CL#22432CL#22433, 他还提交了一个设计文档:Proposal: Binary-Only Package

相关的讨论在: #12186#2775

下面我们以一个实例看看如何使用的。

下面是我们实现业务逻辑的一个package,提供了一些常量、变量、函数和struct类型,把它编译成x.a

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package x
import "fmt"
const PI = 3.14
var (
X = 100
)
func init() {
fmt.Println("x init")
}
func Add(x, y int) int {
return x + y
}
type S struct {
X, Y int
}

这是我们公司的核心资产,靠这个库卖钱呢,所以我们不想把这个源代码公开出去。那么我们可以提供给用户编译好的x.a,并且提供一个dummy的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//go:binary-only-package
package x
const PI = 0.0
var (
X = 0
)
func Add(x, y int) int {
return 0
}
type S struct {
X, Y int
}

用户拿到这个x.a后放在$GOPATH/pkg相应的package下,同时将这个go文件放在$GOPATH/src,这样用户就可以正常使用这个库了。

当然,你最好将你的库打包成一个zip,这样用户unzip一下就可以方便的copy到$GOPATH中。

golang binary-only packages也能搜到一些相关的教程,有人还写了一个样板例子:tcnksm/go-binary-only-package