// Package modules 是内核模块的**登记表**(不含任何具体模块的实现)。 // // ## 设计意图(用户设计) // // > 每个内核模块显式各自声明依赖 … 最先初始化的 core 自然不依赖任何模块, // > 下一步初始化的依赖 core 以此类推 // // ## 怎么加一个新模块(这是本设计的**验收标准**) // // ① 新建一个文件(如 modules/mymod.go) // ② 写一个类型,实现 kernelboot.Module(Name/Requires/Provides/Init) // ③ 在同文件的 init() 里调 modules.Register(&MyMod{}) // ④ 在 modules/imports.go 加一行 `_ "...modules"` —— **仅此而已** // // **不需要**编辑任何中央清单:依赖写在模块**自己**的 Requires() 里。 // // ★ 为何要"防漏 import"的判据(见 registry_test.go): // 忘了 import ⇒ 模块**根本不登记** ⇒ 它不参与装配 ⇒ 依赖它的模块报 // "缺依赖"(响亮),但**不依赖它的模块会静默少了那个设施** // (如插件注册表没建 ⇒ 插件全部加载不了,而错误信息指向插件而非根因)。 // ⇒ 判据必须自动扫描目录,要求每个模块文件都被 import。 package modules import ( "fmt" "sort" "sync" "github.com/JianFeeeee/HologramAgent/internal/kernelboot" ) var ( regMu sync.Mutex registry []kernelboot.Module seenName = map[string]bool{} ) // Register 由模块在自己的 init() 里调用(自注册)。 // // 重复注册同一模块名会**记录**并在 BuildModules 时报错 —— 不用 panic: // panic 发生在 init 阶段,栈信息对使用者毫无意义(他只想知道哪个模块重名)。 func Register(m kernelboot.Module) { if m == nil { return } regMu.Lock() defer regMu.Unlock() if seenName[m.Name()] { dupErr = append(dupErr, m.Name()) return } seenName[m.Name()] = true registry = append(registry, m) } // dupErr 累积重名模块(BuildModules 时统一报告)。 var dupErr []string // BuildModules 返回全部已登记模块(按名字排序,保证可复现)。 // // ## 为什么排序 // // 分层结果的**层内顺序**若依赖 map/切片顺序,同一份声明两次构建会得到 // 不同顺序 ⇒ 不可审计(设计 §2.5:图要能进 PR diff 比对)。 // 排序让结果稳定。 func BuildModules() ([]kernelboot.Module, error) { regMu.Lock() defer regMu.Unlock() if len(dupErr) > 0 { names := append([]string(nil), dupErr...) sort.Strings(names) return nil, fmt.Errorf("kernelboot/modules: 模块名重复注册: %v"+ "(同一个模块被 import 了两次?或两个模块取了同名)", names) } out := append([]kernelboot.Module(nil), registry...) sort.Slice(out, func(i, j int) bool { return out[i].Name() < out[j].Name() }) if len(out) == 0 { return nil, fmt.Errorf("kernelboot/modules: 没有任何模块被登记 —— " + "通常是没有 import 模块包(见本文件顶部的'怎么加一个新模块')") } return out, nil } // Names 返回已登记模块名(诊断/判据用)。 func Names() []string { regMu.Lock() defer regMu.Unlock() out := make([]string, 0, len(registry)) for _, m := range registry { out = append(out, m.Name()) } sort.Strings(out) return out }