github.com/bir3/gocompiler@v0.9.2202/extra/extract_stdlib/zstd.go (about) 1 package extract_stdlib 2 3 import ( 4 "archive/tar" 5 "fmt" 6 "io" 7 "io/fs" 8 "os" 9 "path" 10 "path/filepath" 11 12 "github.com/bir3/gocompiler/extra/compress/zstd" 13 "github.com/bir3/gocompiler/src/cmd/gocmd/lockedfile" 14 ) 15 16 /* 17 size go stdlib tarfile: 18 37 MB full-notest.tar 19 # no *_test.go and testdata folders (else 56 MB) 20 # no src/cmd 21 # version: go1.20 22 23 -> compressed with zstd v1.5.5 : 24 7.9 MB full-notest-3.zst x4.7 compression # -3 --ultra 25 5.7 MB full-notest-21.zst x6.5 compression # -21 --ultra 26 27 -> decompression: 76 milliseconds on macbook air m1 28 */ 29 30 func ExtractStdlib(file fs.File, dir string) error { 31 32 donefile := path.Join(dir, "done") 33 if _, err := os.Stat(donefile); err == nil { 34 return nil // already extracted 35 } 36 37 lockfile := path.Join(dir, "done.lock") 38 lf, err := lockedfile.Create(lockfile) 39 if err != nil { 40 return fmt.Errorf("Error creating lockfile: %w", err) 41 } 42 defer lf.Close() 43 44 zstdReader, err := zstd.NewReader(file) 45 if err != nil { 46 return fmt.Errorf("Error creating zstd reader: %w", err) 47 } 48 defer zstdReader.Close() 49 50 tarReader := tar.NewReader(zstdReader) 51 52 for { 53 header, err := tarReader.Next() 54 if err == io.EOF { 55 break 56 } 57 if err != nil { 58 fmt.Errorf("Error reading tar header - %w", err) 59 } 60 61 target := path.Join(dir, header.Name) 62 switch header.Typeflag { 63 case tar.TypeReg: // regular file 64 case tar.TypeDir: 65 if err := os.MkdirAll(target, 0755); err != nil { 66 return fmt.Errorf("Error creating directory %s - %w", target, err) 67 } 68 continue 69 default: 70 return fmt.Errorf("unknown tar item type %d", header.Typeflag) 71 } 72 73 // TODO: if python script creates "tar.TypeDir" entry, we can avoid 74 // next stat/mkdir 75 76 // Create any necessary parent directories for the file 77 parent := filepath.Dir(target) 78 if _, err := os.Stat(parent); os.IsNotExist(err) { 79 if err := os.MkdirAll(parent, 0755); err != nil { 80 return fmt.Errorf("Error creating directory %s - %w", parent, err) 81 } 82 } 83 84 // Extract the file 85 outFile, err := os.Create(target) 86 if err != nil { 87 return fmt.Errorf("Error creating file %s - %w", target, err) 88 } 89 90 if _, err := io.Copy(outFile, tarReader); err != nil { 91 return fmt.Errorf("Error extracting file %s - %w", target, err) 92 } 93 94 err = outFile.Close() 95 if err != nil { 96 return fmt.Errorf("Error closing file %s - %w", target, err) 97 } 98 // note: we do not replicate chmod (e.g. executable flag) 99 } 100 f3, err := os.Create(donefile) 101 if err != nil { 102 return fmt.Errorf("Error creating donefile: %s - %w", donefile, err) 103 } 104 err = f3.Close() 105 if err != nil { 106 return fmt.Errorf("Error closing donefile: %s - %w", donefile, err) 107 } 108 return nil 109 }