原因

学会了用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
cp "$WORK\\b002\\_pkg_.a" "C:\\Users\\unknow\\AppData\\Local\\go-build\\af\\af957af9edcde92623760da8bbf015a49bf195d22b117b010e80c51678fda67d-d""C:\\Users\\unknow\\AppData\\Local\\go-build\\af\\af957af9edcde92623760da8bbf015a49bf195d22b117b010e80c51678fda67d-d"fmt.a
packagefile fmt=fmt.a
  1. 正确编译foo.a
go tool compile -p foo -o foo.a -importcfg importcfg.txt foo.go
-p foo
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.
go build -x -v -a foo.go
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