Go1.20编译静态库过程记录

文章描述了在Go1.20版本中编译静态库遇到的问题及解决步骤,包括如何处理标准库的路径问题,使用`gotoolcompile`和`gotoollink`命令进行编译和链接,以及创建importcfg文件来指定依赖。作者还提供了一个Python脚本来自动化处理库文件的复制,最终成功生成并验证了静态库foo.a的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原因

学会了用Go编译动态库给Python和C调用,就想着编译一个静态库试试。在网上翻了很久,找到的帖子都不能用,至少2023年以前及Go1.20以前的都不能用。在 Go 1.20 之前,标准库被安装到、$GOROOT/pkg/$GOOS_$GOARCH,从 Go 1.20 开始,默认情况下,标准库被构建和缓存,但未安装。因此,$GOROOT/pkg/下根本没有$GOOS_$GOARCH文件夹,而是被缓存到go-build文件夹,我的是C:\Users\unknow\AppData\Local\go-build。为了保持干净,我也不打算把标准库安装到$GOROOT/pkg/$GOOS_$GOARCH。

目录、文件说明

e:\gogo\src\staticlib\go.mod
e:\gogo\src\staticlib\main.go
e:\gogo\src\staticlib\abc\foo.go

想法是:把abc\foo.go编译为静态库foo.a,然后由main.go去掉用foo.a实现功能。
main.go内容:

package main
import "foo"
func main() {
	foo.Bar()
}

foo.go内容:

package foo
import "fmt"
func Bar() {
	fmt.Println("foobar.")
}

编译foo.a出错

刚开始按照网上找的命令第一步就出错, 提示找不到fmt包

cd foo
go tool compile -pack -p foo foo.go
foo.go:3:8: could not import fmt (file not found)

但是,go build foo.go 不会出错。

解决编译

1.追踪go build 过程

go build -x -v -a foo.go 2>&1 | more > br.txt

将go build 的全部步骤保存到br.txt这个文件, 发现在编译foo.go之前编译了并转存了标准库到缓存文件夹
在这里插入图片描述
2. 找到foo.go需要用到的fmt
通过命令cp "$WORK\\b002\\_pkg_.a" "C:\\Users\\unknow\\AppData\\Local\\go-build\\af\\af957af9edcde92623760da8bbf015a49bf195d22b117b010e80c51678fda67d-d" 找到缓存后的fmt.a及其名称。
"C:\\Users\\unknow\\AppData\\Local\\go-build\\af\\af957af9edcde92623760da8bbf015a49bf195d22b117b010e80c51678fda67d-d"复制到abc目录,并改名为fmt.a.
3. 创建importcfg文件供编译器使用
importcfg.txt

packagefile fmt=fmt.a
  1. 正确编译foo.a
go tool compile -p foo -o foo.a -importcfg importcfg.txt foo.go

此命令执行成功后会在当前文件夹下(foo)生成foo.a库文件。此处如果不使用 -p foo指定导入路径,在编译main.go的时候会找不到包名错误:

go tool compile -o ma.o -I abc main.go
<unknown line number>: internal compiler error: have package "<unlinkable>" (0xc0000609b0), want package "foo" (0xc0003d9a40)
  1. 编译main.go
    回到staticlib目录下,执行
go tool compile -o ma.o -I abc main.go

如果成功,就能生成ma.o目标文件。

链接

链接使用的命令是:

go tool link -o ma.exe -L abc ma.o

但是会提示找不到很多标准库,如errors.o, 在这个示例中,这些库有42个

D:\Go\pkg\tool\windows_amd64\link.exe: cannot open file D:\Go\pkg\windows_amd64\errors.o: open D:\Go\pkg\windows_amd64\errors.o: The system cannot find the path specified.

解决思路跟找fmt.a是一样的,也是从go build -x -v -a foo.go的结果中找到这些库,只是工作量比较大。
为此,我用python写了一个粗糙的脚本完成这些工作。只需要执行一次,就会把需要的库文件复制到staticlib\abc\module文件夹下,以后就直接指定路径即可。

import os
import shutil
import re
import time
TotalFiles=0
FOOLIB="go tool compile  -pack -importcfg importcfg.txt -p foo  foo.go"
GOBUILD="go build -x -v -a foo.go 2>&1 | more > br.txt"
if not os.path.exists("br.txt"):
    os.system(GOBUILD)
PKGFILE="findstr packagefile br.txt > pkgfile.txt"
if not os.path.exists("pkgfile.txt"):
    os.system(PKGFILE)
CPFILE=r"findstr ^cp.*internal$  br.txt >cpfile.txt"
if not os.path.exists("cpfile.txt"):
    os.system(CPFILE)
if os.path.exists("module"):
    shutil.rmtree("module")
    os.mkdir("module")
FD={}
f=open("pkgfile.txt", "r")
tclist=f.read().splitlines()
f.close()
for x in tclist:
   v=x.split("=")[0].split(" ")[1].replace("/","\\")
   k=x.split("=")[1].split("\\")[-2]
   if not k in FD:
       FD[k]=[v]
    
f=open("cpfile.txt", "r")
fclist=f.read().splitlines()
f.close()
reg=re.compile(r'^cp.*# internal$')
for x in fclist:
    if reg.match(x):
        fk=x.split(" ")[1].split("\\\\")[1]
        fv=x.split(" ")[2].replace("\\\\","\\").strip('"')
        if fk in FD:
            FD[fk].append(fv)
           
for k,v in FD.items():
    dst = os.path.join("module","\\".join(v[0].split("\\")[:-1]))
    fn = os.path.join(dst, v[0].split("\\")[-1]+".a")
    fv = v[1]
    print(fv,"=>", fn)
    if not os.path.exists(dst):
        os.makedirs(dst)
    shutil.copyfile(fv, fn)
    TotalFiles += 1 
    time.sleep(1)

print(f"Total Files {TotalFiles} Copied to 'module'")
print("Create importcfg.txt:")
with open("importcfg.txt", "w") as f:
    f.write("packagefile fmt=module\\fmt.a\n")
result = os.popen(FOOLIB)
if result.read() == "" :
    print("build successful")
    print("now can build main.go with foo.a")
    print("go tool compile -o main.o -I abc main.go")
    print("go tool link -o m.exe -L abc -L abc\module main.o")
    os.chdir("..")
    MAINOBJ="go tool compile -o main.o -I abc main.go"
    r = os.popen(MAINOBJ)
    if r.read()=="":
        MAINEXE="go tool link -o m.exe -L abc -L abc\module main.o"
        r = os.popen(MAINEXE)
        if r.read()=="":
            r = os.popen("m.exe")
            print(r.read())
    else:
        print(r.read())
else:
    print(result.read())

执行后,目录结构变为

e:\gogo\src\staticlib\go.mod
e:\gogo\src\staticlib\main.go
e:\gogo\src\staticlib\main.o
e:\gogo\src\staticlib\m.exe
e:\gogo\src\staticlib\abc\foo.go
e:\gogo\src\staticlib\abc\foo.a
e:\gogo\src\staticlib\abc\module\*.a

验证foo.a

在e:\gogo\src\staticlib下新建目录def,将foo.a 复制到此目录下。新建main.go,内容为:

import "foo"
func main(){
	foo.Bar()
}

编译:

go tool compile -o main.o -I . main.go

链接:

go tool link -o m.exe -L . -L ..\abc\module main.o

成功生成m.exe并实现功能。

E:\gogo\src\staticlib\def\foo.a
E:\gogo\src\staticlib\def\m.exe
E:\gogo\src\staticlib\def\main.go
E:\gogo\src\staticlib\def\main.o
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值