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  }