func unpackIndex(ctx context.Context, pack *indexpack.Archive, dir string) error { fetcher := pack.Fetcher(ctx) return pack.ReadUnits(ctx, formatKey, func(u interface{}) error { unit, ok := u.(*apb.CompilationUnit) if !ok { return fmt.Errorf("%T is not a CompilationUnit", u) } idx, err := kindex.FromUnit(unit, fetcher) if err != nil { return fmt.Errorf("error creating kindex: %v", err) } path := kindexPath(dir, idx) if !*quiet { log.Println("Writing compilation unit to", path) } f, err := vfs.Create(ctx, path) if err != nil { return fmt.Errorf("error creating output file: %v", err) } if _, err := idx.WriteTo(f); err != nil { f.Close() // try to close file before returning return fmt.Errorf("error writing output file: %v", err) } return f.Close() }) }
// fetchAndStore reads the contents of path and stores them into a, returning // the digest of the contents. The path to digest mapping is cached so that // repeated uses of the same file will avoid redundant work. func (e *Extractor) fetchAndStore(ctx context.Context, path string, a *indexpack.Archive) (string, error) { if digest, ok := e.fmap[path]; ok { return digest, nil } data, err := vfs.ReadFile(ctx, path) if err != nil { // If there's an alternative installation path, and this is a path that // could potentially be there, try that. if i := strings.Index(path, "/pkg/"); i >= 0 && e.AltInstallPath != "" { alt := e.AltInstallPath + path[i:] data, err = vfs.ReadFile(ctx, alt) // fall through to the recheck below } } if err != nil { return "", err } name, err := a.WriteFile(ctx, data) if err != nil { return "", err } digest := strings.TrimSuffix(name, filepath.Ext(name)) if e.fmap == nil { e.fmap = map[string]string{path: digest} } return digest, err }
func packIndex(ctx context.Context, pack *indexpack.Archive, idx *kindex.Compilation) error { for _, data := range idx.Files { if path, err := pack.WriteFile(ctx, data.Content); err != nil { return fmt.Errorf("error writing file %v: %v", data.Info, err) } else if !*quiet { log.Println("Wrote file to", path) } } path, err := pack.WriteUnit(ctx, formatKey, idx.Proto) if err != nil { return fmt.Errorf("error writing compilation unit: %v", err) } fmt.Println(strings.TrimSuffix(path, ".unit")) return nil }
// Store writes the compilation units of p to the specified archive and returns // its unit file names. This has the side-effect of updating the required // inputs of the compilations so that they contain the proper digest values. func (p *Package) Store(ctx context.Context, a *indexpack.Archive) ([]string, error) { const formatKey = "kythe" var unitFiles []string for _, cu := range p.Units { // Pack the required inputs into the archive. for _, ri := range cu.RequiredInput { // Check whether we already did this, so Store can be idempotent. // // When addFiles first adds the required input to the record, we // know its path but have not yet fetched its contents -- that step // is deferred until we are ready to store them for output (i.e., // now). Once we have fetched the file contents, we'll update the // field with the correct digest value. We only want to do this // once, per input, however. path := ri.Info.Digest if !strings.Contains(path, "/") { continue } // Fetch the file and store it into the archive. We may get a // cache hit here, handled by fetchAndStore. digest, err := p.ext.fetchAndStore(ctx, path, a) if err != nil { return nil, err } ri.Info.Digest = digest } // Pack the compilation unit into the archive. fn, err := a.WriteUnit(ctx, formatKey, cu) if err != nil { return nil, err } unitFiles = append(unitFiles, fn) } return unitFiles, nil }