builder: refactor package compile job

This commit moves the calculation of the package action ID (cache key)
into a separate job. At the moment, this won't have a big effect but
this change is necessary for some future changes I want to make.
Этот коммит содержится в:
Ayke van Laethem 2022-05-26 14:38:34 +02:00 коммит произвёл Ron Evans
родитель 777d3f3ea5
коммит 9dd249a431

Просмотреть файл

@ -209,8 +209,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Add jobs to compile each package. // Add jobs to compile each package.
// Packages that have a cache hit will not be compiled again. // Packages that have a cache hit will not be compiled again.
var packageJobs []*compileJob var packageJobs []*compileJob
packageBitcodePaths := make(map[string]string) packageActionIDJobs := make(map[string]*compileJob)
packageActionIDs := make(map[string]string)
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" { if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
version := goenv.Version version := goenv.Version
@ -235,6 +234,24 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
} }
sort.Strings(undefinedGlobals) sort.Strings(undefinedGlobals)
// Action ID jobs need to know the action ID of all the jobs the package
// imports.
var importedPackages []*compileJob
for _, imported := range pkg.Pkg.Imports() {
job, ok := packageActionIDJobs[imported.Path()]
if !ok {
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
}
importedPackages = append(importedPackages, job)
}
// Create a job that will calculate the action ID for a package compile
// job. The action ID is the cache key that is used for caching this
// package.
packageActionIDJob := &compileJob{
description: "calculate cache key for package " + pkg.ImportPath,
dependencies: importedPackages,
run: func(job *compileJob) error {
// Create a cache key: a hash from the action ID below that contains all // Create a cache key: a hash from the action ID below that contains all
// the parameters for the build. // the parameters for the build.
actionID := packageAction{ actionID := packageAction{
@ -253,34 +270,32 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
for filePath, hash := range pkg.FileHashes { for filePath, hash := range pkg.FileHashes {
actionID.FileHashes[filePath] = hex.EncodeToString(hash) actionID.FileHashes[filePath] = hex.EncodeToString(hash)
} }
for _, imported := range pkg.Pkg.Imports() { for i, imported := range pkg.Pkg.Imports() {
hash, ok := packageActionIDs[imported.Path()] actionID.Imports[imported.Path()] = importedPackages[i].result
if !ok {
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
}
actionID.Imports[imported.Path()] = hash
} }
buf, err := json.Marshal(actionID) buf, err := json.Marshal(actionID)
if err != nil { if err != nil {
panic(err) // shouldn't happen return err // shouldn't happen
} }
hash := sha512.Sum512_224(buf) hash := sha512.Sum512_224(buf)
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:]) job.result = hex.EncodeToString(hash[:])
return nil
},
}
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
// Determine the path of the bitcode file (which is a serialized version // Now create the job to actually build the package. It will exit early
// of a LLVM module). // if the package is already compiled.
bitcodePath := filepath.Join(cacheDir, "pkg-"+hex.EncodeToString(hash[:])+".bc")
packageBitcodePaths[pkg.ImportPath] = bitcodePath
// The package has not yet been compiled, so create a job to do so.
job := &compileJob{ job := &compileJob{
description: "compile package " + pkg.ImportPath, description: "compile package " + pkg.ImportPath,
run: func(*compileJob) error { dependencies: []*compileJob{packageActionIDJob},
run: func(job *compileJob) error {
job.result = filepath.Join(cacheDir, "pkg-"+packageActionIDJob.result+".bc")
// Acquire a lock (if supported). // Acquire a lock (if supported).
unlock := lock(bitcodePath + ".lock") unlock := lock(job.result + ".lock")
defer unlock() defer unlock()
if _, err := os.Stat(bitcodePath); err == nil { if _, err := os.Stat(job.result); err == nil {
// Already cached, don't recreate this package. // Already cached, don't recreate this package.
return nil return nil
} }
@ -401,7 +416,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Write to a temporary path that is renamed to the destination // Write to a temporary path that is renamed to the destination
// file to avoid race conditions with other TinyGo invocatiosn // file to avoid race conditions with other TinyGo invocatiosn
// that might also be compiling this package at the same time. // that might also be compiling this package at the same time.
f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath)) f, err := ioutil.TempFile(filepath.Dir(job.result), filepath.Base(job.result))
if err != nil { if err != nil {
return err return err
} }
@ -421,13 +436,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil { if err != nil {
// WriteBitcodeToFile doesn't produce a useful error on its // WriteBitcodeToFile doesn't produce a useful error on its
// own, so create a somewhat useful error message here. // own, so create a somewhat useful error message here.
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath) return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, job.result)
} }
err = f.Close() err = f.Close()
if err != nil { if err != nil {
return err return err
} }
return os.Rename(f.Name(), bitcodePath) return os.Rename(f.Name(), job.result)
}, },
} }
packageJobs = append(packageJobs, job) packageJobs = append(packageJobs, job)
@ -451,8 +466,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// anything, it only links the bitcode files together. // anything, it only links the bitcode files together.
ctx := llvm.NewContext() ctx := llvm.NewContext()
mod = ctx.NewModule("main") mod = ctx.NewModule("main")
for _, pkg := range lprogram.Sorted() { for _, pkgJob := range packageJobs {
pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath]) pkgMod, err := ctx.ParseBitcodeFile(pkgJob.result)
if err != nil { if err != nil {
return fmt.Errorf("failed to load bitcode file: %w", err) return fmt.Errorf("failed to load bitcode file: %w", err)
} }