如何分发具有 C 依赖项的 Go 模块
How to distribute a Go module with C dependencies
我有一个 Go 包——称之为 foo
——我围绕一些现有的 C 代码构建了它,我正在尝试确定如何最好地分发它。
一些背景知识...这是我的目录结构的简化版本:
foo/
|_ include/
|_ <several other header files>
|_ libs/
|_ <several .so files>
|_ foo.c
|_ foo.h
|_ foo.go
foo.go
的头颅
package foo
// #cgo CFLAGS: -I${SRCDIR}/include
// #cgo LDFLAGS: ${SRCDIR}/_foo.so -L${SRCDIR}/libs
// #include <stdlib.h>
// #include "foo.h"
import "C"
...
其中 _foo.so
是作为构建过程的一部分生成的动态库。我能够 运行 并很好地测试我的代码,但我希望能够分发它并在一个单独的项目中使用它,一个使用 go mod
的项目。这是事情变得有点棘手的地方,我对 Go 的理解变得脆弱。从概念上讲,我只是希望能够将 foo/
目录(.so
文件和所有文件)分发到另一个应用程序可以找到并导入它的地方,但是我有几个绊脚石 运行 变成:
- 我不能把它变成一个二进制包,因为我们使用的是 Go 1.13,据我所知,该功能在 1.12 之后被删除了。
.so
文件的构建过程非常重要,我不希望其他开发人员不得不花费 30 分钟以上的时间来构建该对象。
我找到了很多CGO演示和博客,但是关于如何分发CGO包的解释并不多。有什么想法吗?
值得注意的是,这是我公司的,环境是标准的,所以我不必控制不同的OS等
编辑
我忘记提及的是我尝试过将库烘焙到 Docker 图像的 GOPATH
中,然后从该基础构建依赖于它的微服务。但是,当使用 -mod=vendor
构建服务时,无法在 GOPATH
:
中找到库
RUN GOOS=linux \
GOARCH=amd64 \
CGO_ENABLED=1 \
GOFLAGS=-mod=vendor \
GO111MODULE=on \
go build \
-o service ./cmd/serve/main.go
./service
# build foo: cannot load foo: open /.../svc/vendor/foo: no such file or directory
对于将来可能 运行 遇到类似问题的任何人,以下是我为完成这项工作所采取的步骤。
- 我将生成的工件烘焙到基础 docker 图像中,a la:
FROM golang:1.13.4
COPY ./foo /go/src/foo/foo
COPY ./foo.mod /go/src/foo
这解决了必须将 .so
文件提交到源代码管理的问题,而不是简单地将 docker 图像推送到注册表。
- 需要此库的任何服务都是从该基础映像构建的:
FROM my-foo-base:latest
COPY ./vendor ./vendor
COPY ./go.mod ./
COPY ./go.sum ./
# Put foo in /vendor so it's discoverable, and put its libs where they
# can be discovered
RUN mv /go/src/foo ./vendor && \
mv ./vendor/foo/foo/libs/*.so /usr/lib/ && \
mv ./vendor/foo/foo/include/* /usr/include/
# Copy in app code
COPY ./my-app ./my-app
# Required so the app can find the _foo.so shared object
ENV LD_LIBRARY_PATH="/vendor/foo/foo:/usr/lib:${LD_LIBRARY_PATH}"
# Build into a single binary
RUN GOOS=linux \
GOARCH=amd64 \
CGO_ENABLED=1 \
GOFLAGS=-mod=vendor \
GO111MODULE=on \
go build \
-o service ./cmd/serve/main.go
这有效,服务能够发现 CGO 包。
我有一个 Go 包——称之为 foo
——我围绕一些现有的 C 代码构建了它,我正在尝试确定如何最好地分发它。
一些背景知识...这是我的目录结构的简化版本:
foo/
|_ include/
|_ <several other header files>
|_ libs/
|_ <several .so files>
|_ foo.c
|_ foo.h
|_ foo.go
foo.go
package foo
// #cgo CFLAGS: -I${SRCDIR}/include
// #cgo LDFLAGS: ${SRCDIR}/_foo.so -L${SRCDIR}/libs
// #include <stdlib.h>
// #include "foo.h"
import "C"
...
其中 _foo.so
是作为构建过程的一部分生成的动态库。我能够 运行 并很好地测试我的代码,但我希望能够分发它并在一个单独的项目中使用它,一个使用 go mod
的项目。这是事情变得有点棘手的地方,我对 Go 的理解变得脆弱。从概念上讲,我只是希望能够将 foo/
目录(.so
文件和所有文件)分发到另一个应用程序可以找到并导入它的地方,但是我有几个绊脚石 运行 变成:
- 我不能把它变成一个二进制包,因为我们使用的是 Go 1.13,据我所知,该功能在 1.12 之后被删除了。
.so
文件的构建过程非常重要,我不希望其他开发人员不得不花费 30 分钟以上的时间来构建该对象。
我找到了很多CGO演示和博客,但是关于如何分发CGO包的解释并不多。有什么想法吗?
值得注意的是,这是我公司的,环境是标准的,所以我不必控制不同的OS等
编辑
我忘记提及的是我尝试过将库烘焙到 Docker 图像的 GOPATH
中,然后从该基础构建依赖于它的微服务。但是,当使用 -mod=vendor
构建服务时,无法在 GOPATH
:
RUN GOOS=linux \
GOARCH=amd64 \
CGO_ENABLED=1 \
GOFLAGS=-mod=vendor \
GO111MODULE=on \
go build \
-o service ./cmd/serve/main.go
./service
# build foo: cannot load foo: open /.../svc/vendor/foo: no such file or directory
对于将来可能 运行 遇到类似问题的任何人,以下是我为完成这项工作所采取的步骤。
- 我将生成的工件烘焙到基础 docker 图像中,a la:
FROM golang:1.13.4
COPY ./foo /go/src/foo/foo
COPY ./foo.mod /go/src/foo
这解决了必须将 .so
文件提交到源代码管理的问题,而不是简单地将 docker 图像推送到注册表。
- 需要此库的任何服务都是从该基础映像构建的:
FROM my-foo-base:latest
COPY ./vendor ./vendor
COPY ./go.mod ./
COPY ./go.sum ./
# Put foo in /vendor so it's discoverable, and put its libs where they
# can be discovered
RUN mv /go/src/foo ./vendor && \
mv ./vendor/foo/foo/libs/*.so /usr/lib/ && \
mv ./vendor/foo/foo/include/* /usr/include/
# Copy in app code
COPY ./my-app ./my-app
# Required so the app can find the _foo.so shared object
ENV LD_LIBRARY_PATH="/vendor/foo/foo:/usr/lib:${LD_LIBRARY_PATH}"
# Build into a single binary
RUN GOOS=linux \
GOARCH=amd64 \
CGO_ENABLED=1 \
GOFLAGS=-mod=vendor \
GO111MODULE=on \
go build \
-o service ./cmd/serve/main.go
这有效,服务能够发现 CGO 包。