从源码构建Golang的完整指南

从源码构建Golang的完整指南 我一直在进行一个涉及Go语言垃圾回收机制的研究项目。我们已经成功从源代码构建了Go。现在,我们正尝试对源代码进行微小的修改,并观察构建是否成功。作为开始,我们在mgc.go文件的gcinit函数的最开头添加了一条日志打印语句。

func gcinit() {
	log.Print("In Function GCINT ")
}

添加日志打印后,构建失败并出现以下错误:

In Function GCINIT 
            In Function GCINIT shift.go:12:6: (i8 + 1) (8 bits) too small for shift of 8
FAIL
FAIL    cmd/vet    9.460s
FAIL
go tool dist: Failed: exit status 1

我们也尝试过使用fmt.printf,但到目前为止都没有成功。如果有人能提供帮助,我们将非常感激。


更多关于从源码构建Golang的完整指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html

12 回复

你好,我尝试使用debuglog,但还是失败了。

更多关于从源码构建Golang的完整指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你能详细说明一下复现步骤吗?我也想从源代码构建它……看起来很有意思

谢谢。我会根据您的建议尝试使用它。如果有效,我会告诉您。

如果你找到了这个场景的解决方案,请告诉我。我确实被这个问题困住了。

你说得对,问题确实如此。我们只在 gcinit 函数中添加了一行代码,这就导致了失败。如果我们移除这行代码,构建就能正常进行。

你好 @jaintapauljp

我猜想可能是 fmt 包导致了问题。

你尝试过使用内置的 print 或 println 函数吗?

抱歉回复晚了。恐怕我已经没有其他想法了。使用 debuglog 只是我查看源码时的一个快速想法。也许有从源码构建 Go 经验的人能提供进一步的帮助。

你好 @christophberger,感谢你的建议。它仍然失败,并显示以下信息:

  --- FAIL: TestVet/rangeloop (0.46s)
        vet_test.go:146: error check failed: 
            Unmatched Errors:

这些错误似乎来自于对 vet 命令的源代码运行 go test

我看不出你修改的 mgc.go 与属于 vet 命令单元测试的文件 shift.govet_test.go 之间有任何联系。

我的理解是否正确:如果你在 runtime/mgc.gofunc gcinit() 中添加一个对内置函数 println() 的调用,rangeloop 测试就会可复现地失败?

其他所有代码都是原始未修改的吗?

目前我只能确定,任何向标准输出(可能也包括标准错误)写入数据的函数都会导致多个测试失败。

我不得不进行推测,但文件 debuglog.go(位于 src/runtime 目录下)看起来很有意思。它提供了一个内存中的调试器。当应用程序发生恐慌时,日志输出会被写入标准输出。

从文件的注释来看,debuglog.go 似乎非常适合记录运行时的内部活动:

dlog 可以用于运行时的各种高度受限的场景:在信号处理程序中使用是安全的,可以在写屏障内部使用,可以在栈实现内部使用,也可以在必须递归 nosplit 的地方使用。

我出于好奇,按照从源代码安装 Go - Go 编程语言中的步骤安装了 Go 源代码。

然后我修改了 src/runtime/mgc.go,加入了 println 调用:

 func gcinit() {
	println("GCINIT")
	// rest of gcinit

接着我在 goroot 目录下运行了 ./all.bash。编译成功了。

第一个失败的测试是:

ok  	regexp/syntax	1.042s
--- FAIL: TestLockRankGenerated (0.15s)
    lockrank_test.go:20: exit status 1
--- FAIL: TestSUID (0.11s)
    crash_test.go:138: running go build -o /var/folders/_m/dgnkqt8d3j10svk5c06px4vc0000gn/T/go-build2564578370/testsuid.exe
    security_test.go:82: building testsuid []: exit status 1
        # runtime
        ../../mgc.go:153:13: newline in string
        ../../mgc.go:153:13: syntax error: unexpected newline in argument list; possibly missing comma or )
        ../../mgc.go:154:2: syntax error: unexpected if at end of statement

其他测试以这些方式失败:

--- FAIL: TestTrampoline (1.00s)
    link_test.go:690: unexpected output (default):
        GCINIT
        hello
--- FAIL: TestRuntimeTypeAttrInternal (0.77s)
    dwarf_test.go:776: could not parse type address from program output "GCINIT\n4311591584": strconv.ParseUint: parsing "GCINIT\n4311591584": invalid syntax
--- FAIL: TestHello (27.07s)
    pack_test.go:221: incorrect output: "GCINIT\nhello world\n", want "hello world\n"
FAIL

看起来首要问题是 print / println 干扰了标准输出。

从错误信息看,问题出在cmd/vet工具的编译阶段,而不是你的日志代码本身。这是因为Go的构建系统在编译标准库和工具链时,会先编译cmd/vet等工具,而mgc.go中的修改影响了这些工具的编译。

关键问题在于log.Printruntime包的初始化阶段可能不可用。mgc.go属于runtime包,而runtime包在Go启动过程中很早被初始化,此时日志系统可能还未准备好。

以下是两种解决方案:

方案1:使用内部打印函数 使用runtime包内部的打印函数,这些函数在早期初始化阶段可用:

func gcinit() {
    print("In Function GCINIT\n")
}

或者使用带时间戳的版本:

func gcinit() {
    println("In Function GCINIT")
}

方案2:如果必须使用log包,需要确保正确导入 确保在文件顶部正确导入了log包,并且构建时包含必要的依赖:

package runtime

import "log"

func gcinit() {
    log.Print("In Function GCINIT ")
}

但需要注意的是,即使这样可能仍然会失败,因为log包依赖于runtime包本身,可能存在循环依赖问题。

构建命令建议: 使用-a标志强制重新构建所有依赖:

cd /path/to/go/src
./make.bash -a

或者使用dist工具:

cd /path/to/go/src
./all.bash

调试建议: 如果问题仍然存在,可以尝试先构建一个最小的修改来验证构建系统:

func gcinit() {
    // 先注释掉log语句,确保基础构建正常
    // log.Print("In Function GCINIT ")
    
    // 或者使用绝对安全的内部函数
    if false {
        print("debug")
    }
}

然后逐步添加功能,观察构建何时失败。这有助于确定是代码问题还是构建系统问题。

错误信息中的shift.go:12:6提示可能还有其他文件存在编译问题,建议检查是否有其他未提交的修改影响了编译。

回到顶部