Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 59 additions & 40 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -1042,15 +1042,17 @@ func prePackageBuild(ctx *context, task *packageBuildTask, verbose bool) error {
if task.isDeclOnly() {
pkg.ExportFile = ""
task.skip = true
return nil
aPkg.Summary = summarizePackage(aPkg)
return ctx.collectFingerprint(aPkg)
}
if task.isLinkOnly() && !task.hasSource() {
pkg.ExportFile = ""
if task.kind == cl.PkgLinkExtern {
appendExternalLinkArgs(ctx, aPkg, task.kindParam)
}
task.skip = true
return nil
aPkg.Summary = summarizePackage(aPkg)
return ctx.collectFingerprint(aPkg)
}
if err := ctx.collectFingerprint(aPkg); err != nil {
return err
Expand Down Expand Up @@ -1091,6 +1093,7 @@ func finalizePackageBuild(ctx *context, task *packageBuildTask, verbose bool) (p
if task.kind == cl.PkgLinkExtern {
appendExternalLinkArgs(ctx, aPkg, task.kindParam)
}
aPkg.Summary = summarizePackage(aPkg)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

summarizePackage currently always returns non-nil on this path, so this is fine today. As defensive robustness: if saveToCache were ever reached with pkg.Summary == nil, metadata() returns nil and the entry is written with summary: absent — which tryLoadFromCache then treats as a permanent cache miss (meta.Summary == nil guard). That is a silently non-cacheable entry rather than an error. A short assertion/comment here that Summary must be set before saveToCache would make the invariant explicit.

if err := ctx.saveToCache(aPkg); err != nil && verbose {
fmt.Fprintf(os.Stderr, "warning: failed to save cache for %s: %v\n", aPkg.PkgPath, err)
}
Expand Down Expand Up @@ -1341,39 +1344,47 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa
aPkg = ctx.pkgByID[p.ID]
}
if p.ExportFile != "" && aPkg != nil { // skip packages that only contain declarations
if aPkg.Summary == nil && isRuntimePkg(aPkg.PkgPath) {
return
}
linkedPkgs[p.ID] = true
linkedOrder = append(linkedOrder, aPkg)
}
})
linkedSummaries := make([]*PackageSummary, len(linkedOrder))
for i, aPkg := range linkedOrder {
if aPkg.Summary == nil {
return fmt.Errorf("package %s has no linker summary", aPkg.PkgPath)
}
linkedSummaries[i] = aPkg.Summary
}

// packages.Visit with a post callback yields dependencies before importers.
// Reverse that order so static archives are linked after the objects that use them.
for i := len(linkedOrder) - 1; i >= 0; i-- {
aPkg := linkedOrder[i]
p := aPkg.Package
summary := linkedSummaries[i]
// Defer linking runtime packages unless we actually need the runtime.
if isRuntimePkg(p.PkgPath) {
rtLinkArgs = append(rtLinkArgs, aPkg.LinkArgs...)
if aPkg.ArchiveFile != "" {
rtLinkInputs = append(rtLinkInputs, aPkg.ArchiveFile)
if isRuntimePkg(summary.PkgPath) {
rtLinkArgs = append(rtLinkArgs, summary.LinkArgs...)
if summary.ArchiveFile != "" {
rtLinkInputs = append(rtLinkInputs, summary.ArchiveFile)
}
continue
}
// Only let non-runtime packages influence whether runtime is needed.
need1, need2 := aPkg.isNeedRuntimeOrPyInit()
needRuntime = needRuntime || need1
needPyInit = needPyInit || need2
needAbiInit |= aPkg.LPkg.NeedAbiInit
for k, _ := range aPkg.LPkg.MethodByIndex {
methodByIndex[k] = none{}
needRuntime = needRuntime || summary.NeedRuntime
needPyInit = needPyInit || summary.NeedPyInit
needAbiInit |= summary.NeedAbiInit
for _, method := range summary.MethodByIndex {
methodByIndex[method] = none{}
}
for k, _ := range aPkg.LPkg.MethodByName {
methodByName[k] = none{}
for _, method := range summary.MethodByName {
methodByName[method] = none{}
}

linkArgs = append(linkArgs, aPkg.LinkArgs...)
if aPkg.ArchiveFile != "" {
archiveInputs = append(archiveInputs, aPkg.ArchiveFile)
linkArgs = append(linkArgs, summary.LinkArgs...)
if summary.ArchiveFile != "" {
archiveInputs = append(archiveInputs, summary.ArchiveFile)
}
}

Expand All @@ -1390,17 +1401,17 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa
var pcLineInfo []pcLineRecord
var funcInfoStubs []funcInfoStubRecord
if ctx.buildConf.PCLNMode != PCLNNone {
funcInfo = prepareFuncInfoTableRecords(collectFuncInfo(linkedOrder), nil)
pcLineInfo = collectPCLineInfo(linkedOrder)
funcInfoStubs = collectFuncInfoStubRecords(linkedOrder, funcInfo)
funcInfo = prepareFuncInfoTableRecords(collectFuncInfoSummaries(linkedSummaries), nil)
pcLineInfo = collectPCLineInfoSummaries(linkedSummaries)
funcInfoStubs = collectFuncInfoStubRecordsSummaries(linkedSummaries, funcInfo)
}
entryPkg := genMainModule(ctx, llssa.PkgRuntime, pkg, &genConfig{
rtInit: needRuntime,
pyInit: needPyInit,
abiInit: needAbiInit,
methodByIndex: methodByIndex,
methodByName: methodByName,
abiSymbols: linkedModuleGlobals(linkedOrder),
abiSymbols: linkedPackageGlobals(linkedSummaries),
funcInfo: funcInfo,
pcLineInfo: pcLineInfo,
funcInfoStubs: funcInfoStubs,
Expand Down Expand Up @@ -1440,7 +1451,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa
}
}
}
linkArgs = append(linkArgs, cSharedExportArgs(ctx, linkedOrder)...)
linkArgs = append(linkArgs, cSharedExportArgsSummaries(ctx, linkedSummaries)...)

err = linkObjFiles(ctx, outputPath, linkInputs, linkArgs, verbose)
if err != nil {
Expand Down Expand Up @@ -1498,19 +1509,22 @@ func dceEntryRootCandidates(pkgs []Package, needRuntime bool) []string {
}

func linkedModuleGlobals(pkgs []Package) map[string]none {
if len(pkgs) == 0 {
return linkedPackageGlobals(summariesForPackages(pkgs))
}

func linkedPackageGlobals(summaries []*PackageSummary) map[string]none {
if len(summaries) == 0 {
return nil
}
seen := make(map[string]none)
for _, pkg := range pkgs {
if pkg == nil || pkg.LPkg == nil {
for _, summary := range summaries {
if summary == nil {
continue
}
for g := pkg.LPkg.Module().FirstGlobal(); !g.IsNil(); g = gllvm.NextGlobal(g) {
if g.IsDeclaration() {
continue
for _, name := range summary.GlobalSymbols {
if name != "" {
seen[name] = none{}
}
seen[g.Name()] = none{}
}
}
return seen
Expand Down Expand Up @@ -1589,22 +1603,26 @@ func linkObjFiles(ctx *context, app string, objFiles, linkArgs []string, verbose
// shared-library link roots. They live in package archives and otherwise remain
// unreferenced, so the linker can omit both their object files and symbols.
func cSharedExportArgs(ctx *context, pkgs []*aPackage) []string {
return cSharedExportArgsSummaries(ctx, summariesForPackages(pkgs))
}

func cSharedExportArgsSummaries(ctx *context, summaries []*PackageSummary) []string {
if ctx == nil || ctx.buildConf == nil || ctx.buildConf.BuildMode != BuildModeCShared {
return nil
}
exports := make(map[string]none)
for _, pkg := range pkgs {
if pkg == nil || pkg.LPkg == nil {
for _, summary := range summaries {
if summary == nil {
continue
}
for _, name := range pkg.LPkg.ExportFuncs() {
for _, name := range summary.CSharedExports {
if name != "" {
exports[name] = none{}
}
}
if ctx.mode == ModeTest && pkg.Package != nil && pkg.Name == "main" && strings.HasSuffix(pkg.PkgPath, ".test") {
exports[pkg.PkgPath+".init"] = none{}
exports[pkg.PkgPath+".main"] = none{}
if ctx.mode == ModeTest && summary.Name == "main" && strings.HasSuffix(summary.PkgPath, ".test") {
exports[summary.PkgPath+".init"] = none{}
exports[summary.PkgPath+".main"] = none{}
}
}
names := make([]string, 0, len(exports))
Expand Down Expand Up @@ -2238,9 +2256,10 @@ func registerAltSSAPkgs(prog *ssa.Program, patches cl.Patches, alts []*packages.

type aPackage struct {
*packages.Package
SSA *ssa.Package
AltPkg *packages.Cached
LPkg llssa.Package
SSA *ssa.Package
AltPkg *packages.Cached
LPkg llssa.Package
Summary *PackageSummary

NeedRt bool
NeedPyInit bool
Expand Down
19 changes: 14 additions & 5 deletions internal/build/collect.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,9 @@ func (c *context) tryLoadFromCache(pkg *aPackage) bool {
if c.packageCacheDisabled(pkg.ID) {
return false
}
if c.buildConf != nil && (c.buildConf.BuildMode == BuildModeCArchive || c.buildConf.BuildMode == BuildModeCShared) {
return false
}

// Main packages are intentionally not written to the build cache because
// each executable's entry module is linked against the current main archive.
Expand Down Expand Up @@ -377,6 +380,9 @@ func (c *context) tryLoadFromCache(pkg *aPackage) bool {
if err != nil {
return false
}
if meta.Summary == nil {
return false
}

// Use the .a archive directly for linking (no extraction needed)
pkg.ArchiveFile = paths.Archive
Expand All @@ -385,6 +391,7 @@ func (c *context) tryLoadFromCache(pkg *aPackage) bool {
pkg.NeedPyInit = meta.NeedPyInit
pkg.Meta = pkgMeta
pkg.CacheHit = true
pkg.Summary = summaryFromMetadata(pkg, meta)

return true
}
Expand All @@ -399,6 +406,7 @@ func parseManifestMetadata(content string) (*cacheArchiveMetadata, error) {
meta.LinkArgs = append([]string(nil), data.Metadata.LinkArgs...)
meta.NeedRt = data.Metadata.NeedRt
meta.NeedPyInit = data.Metadata.NeedPyInit
meta.Summary = data.Metadata.Summary
}
return meta, nil
}
Expand Down Expand Up @@ -452,6 +460,7 @@ type cacheArchiveMetadata struct {
LinkArgs []string
NeedRt bool
NeedPyInit bool
Summary *packageSummaryMetadata
}

// saveToCache saves a built package to cache.
Expand Down Expand Up @@ -511,16 +520,16 @@ func (c *context) saveToCache(pkg *aPackage) error {
return fmt.Errorf("decode manifest: %w", err)
}

if pkg.Summary == nil {
pkg.Summary = summarizePackage(pkg)
}
meta := &manifestMetadata{
LinkArgs: append([]string(nil), pkg.LinkArgs...),
NeedRt: pkg.NeedRt,
NeedPyInit: pkg.NeedPyInit,
Summary: pkg.Summary.metadata(),
}
if len(meta.LinkArgs) == 0 && !meta.NeedRt && !meta.NeedPyInit {
data.Metadata = nil
} else {
data.Metadata = meta
}
data.Metadata = meta

manifestWithMeta, err := buildManifestYAML(data)
if err != nil {
Expand Down
Loading
Loading