使用Golang archive进行项目构建的方法与实践

使用Golang archive进行项目构建的方法与实践 一个 Golang 项目 xxx/api 已通过命令构建成归档文件 api.a:go build -buildmode archive -o api.a a.go b.go

我想在另一个主项目中导入包 xxx/api 并链接到 api.a,而无需访问其源代码。该如何操作?

21 回复

各位,真的需要你们的帮助。

更多关于使用Golang archive进行项目构建的方法与实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这不符合我的要求

据我所知,你无法做到这一点。

如果不能,那么 -buildmode=archive 的目的是什么?

c-archive 仅支持包含 main 函数的 main 包 😞

我尝试了CGO但没有成功。您能建议如何编写另一个Go文件吗?

尝试使用另一个包名,并且不要使用 main 函数(在非 main 包中反正也没用)

根据我对cgo的理解,你应该可以使用生成的文件,不过你需要编写另一个Go文件,将C头文件包装在一个Go包中。

插件我记得是仅限Linux的。

所以你唯一的选择是开源发布,或者用C语言包装你的Go库,然后再用Go语言包装这个C包装器。

我不使用CGO,不过根据我所读到的内容,你需要:

  1. import "C"
  2. #include "theGenerated.h"
  3. 编写调用头文件中C函数的函数。

NobbZ:

如果我没记错的话,插件仅支持 Linux。

为了更准确,根据 pkg 文档:

目前插件仅支持 Linux、FreeBSD 和 macOS。

感谢。将扩展工作放入插件中,对于Linux和macOS来说似乎是一个可接受的解决方案。但我们的应用程序是跨平台的,也使用gioui支持iOS和Android,而插件在这些平台上不再适用。

为了构建一个静态链接/可链接的归档文件,以便与其他编译器工具链一起使用。

也许你可以使用 cgo 创建一个接口,该接口调用你静态链接的归档文件,不过这需要手动设置。

Go 本身不支持预编译库。

我不知道。我不使用这类归档文件。对我来说,它们一直是“反向FFI”的一种手段。我不关心它们。我只是告诉你我在一次非常粗略的谷歌搜索中发现了什么。

但也许我们这里遇到了一个XY问题……你究竟想实现什么?你为什么想链接到这个归档文件?

我想要实现的是:

一个没有源代码的Go静态库,将其链接到另一个Go主包中。

原因:

该库是我们工作的核心部分,我们不希望扩展开发者能够访问其源代码。并且,出于安全考虑(防止他人基于共享库编译其他程序)以及便于分发,不推荐使用共享库。

如果使用 -buildmode=c-archive 编译,则无法再通过 CGO 链接到 Go 主包中 😞

您能否指导如何将 -buildmode=archive 生成的归档文件链接到 C 程序中?然后,是否有可能使用 CGO 将 GCC 生成的 C 归档文件链接到 Go 主包?

Golang 从 Go 1.7 版本开始,作为一个实验性功能,提供了仅包含二进制文件的包,这正好支持了您所要求的功能,不过在之后的几个版本中,该支持已被取消(https://github.com/golang/go/issues/28152)。

也许您可以重新设计您的“关键组件”,让使用者编写插件,而不是直接调用您的代码。

找到了这个……关于archive以及如何在C语言中使用它…… 链接在此

Go golang 学习 7:编译成可供C语言调用的库

说明 当使用 go buildgo install 命令编译代码时,你可以使用 -buildmode 来指定生成什么文件。 go build -buildmode=<mode> 或者 go install -buildmode=<mode> 使用 go help buildmode 来查看所有支持的…

  • api.go

    package main
    import "C"
    //export Test
    func Test() {}
    func main() {}
    
  • 构建

    go build -buildmode=c-archive -o libapi.a api.go
    
  • main.go

    package main
    // #include "libapi.h"
    // #cgo LDFLAGS: -L. -lapi
    import "C"
    func main() {
        C.Test()
    }
    
  • 构建

    go build -o main main.go
    
  • 错误

    duplicate symbol '__cgo_panic' in:
    $WORK/b001/_cgo_main.o
    ./libapi.a(go.o)
    duplicate symbol '__cgo_topofstack' in:
    $WORK/b001/_cgo_main.o
    ./libapi.a(go.o)
    duplicate symbol '_crosscall2' in:
    $WORK/b001/_cgo_main.o
    ./libapi.a(go.o)
    duplicate symbol '__cgo_wait_runtime_init_done' in:
    $WORK/b001/_cgo_main.o
    ./libapi.a(000006.o)
    ld: 4 duplicate symbols for architecture arm64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    make: *** [main] Error 2
    

在Go项目中链接预编译的归档文件,可以通过以下步骤实现:

1. 创建链接配置文件

在需要使用归档文件的项目中,创建 go.mod 文件(如果尚未存在):

go mod init yourproject

2. 设置链接参数

在项目的 go.mod 文件中添加 replace 指令和链接配置:

// go.mod
module yourproject

go 1.21

replace xxx/api => ./vendor/xxx/api

require xxx/api v0.0.0

3. 创建归档文件目录结构

在项目根目录下创建对应的目录结构,并放置归档文件:

mkdir -p vendor/xxx/api
cp /path/to/api.a vendor/xxx/api/

4. 创建go.mod占位文件

vendor/xxx/api 目录中创建一个简单的 go.mod 文件:

// vendor/xxx/api/go.mod
module xxx/api

go 1.21

5. 创建占位源文件

创建包含包声明的占位文件,让Go工具能够识别这个包:

// vendor/xxx/api/api.go
package api

// 这个文件仅用于包声明
// 实际实现在 api.a 归档文件中

6. 在主项目中使用

现在可以在主项目中导入并使用这个包:

// main.go
package main

import (
    "fmt"
    "xxx/api"
)

func main() {
    // 调用api包中的函数
    result := api.SomeFunction()
    fmt.Println(result)
}

7. 构建时指定链接器参数

构建主项目时,Go会自动链接归档文件。如果需要手动指定,可以使用:

go build -ldflags="-linkmode=external" .

或者创建一个构建脚本:

#!/bin/bash
# build.sh
export GOFLAGS="-ldflags=-linkmode=external"
go build -o myapp .

8. 验证链接

可以通过以下命令验证是否成功链接了归档文件:

# 查看最终二进制文件包含的符号
go tool nm ./myapp | grep api

注意事项

  1. 确保归档文件是用相同版本的Go编译器构建的
  2. 归档文件中的包名必须与导入路径匹配
  3. 如果归档文件依赖其他包,需要确保这些包也可用
  4. 跨平台构建时需要注意架构和操作系统的匹配

这种方法允许你在不访问源代码的情况下使用预编译的Go包,适用于需要保护源代码或分发二进制库的场景。

回到顶部