github.phpd.cn/thought-machine/please@v12.2.0+incompatible/tools/jarcat/tar/tar.go (about)

     1  // Package tar implements a tarball writer for Please.
     2  // This is not really dissimilar to the standard command-line tar utility,
     3  // but we would like some of the GNU tar flags which we can't rely on for all
     4  // platforms that we support, plus we'd like finer control over timestamps
     5  // and directories.
     6  package tar
     7  
     8  import (
     9  	"archive/tar"
    10  	"compress/gzip"
    11  	"io"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  	"time"
    16  )
    17  
    18  // mtime is the time we attach for the modification time of all files.
    19  var mtime = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
    20  
    21  // Write writes a tarball to output with all the files found in inputDir.
    22  // If prefix is given the files are all placed into a single directory with that name.
    23  // If compress is true the output will be gzip-compressed.
    24  func Write(output string, srcs []string, prefix string, compress bool) error {
    25  	f, err := os.Create(output)
    26  	if err != nil {
    27  		return err
    28  	}
    29  	defer f.Close()
    30  	if compress {
    31  		w := gzip.NewWriter(f)
    32  		defer w.Close()
    33  		return write(w, output, srcs, prefix)
    34  	}
    35  	return write(f, output, srcs, prefix)
    36  }
    37  
    38  // write writes a tarball to the given writer with all the files found in inputDir.
    39  // If prefix is given the files are all placed into a single directory with that name.
    40  func write(w io.Writer, output string, srcs []string, prefix string) error {
    41  	tw := tar.NewWriter(w)
    42  	defer tw.Close()
    43  
    44  	for _, src := range srcs {
    45  		strip := filepath.Dir(src)
    46  		if err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
    47  			if err != nil {
    48  				return err
    49  			} else if info.IsDir() {
    50  				return nil // ignore directories
    51  			} else if abs, _ := filepath.Abs(path); abs == output {
    52  				return nil // don't write the output tarball into itself :)
    53  			}
    54  			hdr, err := tar.FileInfoHeader(info, "") // We don't write symlinks into plz-out/tmp, so the argument doesn't matter.
    55  			if err != nil {
    56  				return err
    57  			}
    58  			// Set name appropriately (recall that FileInfoHeader does not set the full path).
    59  			hdr.Name = strings.TrimLeft(strings.TrimPrefix(path, strip), "/")
    60  			if prefix != "" {
    61  				hdr.Name = filepath.Join(prefix, hdr.Name)
    62  			}
    63  			// Zero out all timestamps.
    64  			hdr.ModTime = mtime
    65  			hdr.AccessTime = mtime
    66  			hdr.ChangeTime = mtime
    67  			// Strip user/group ids.
    68  			hdr.Uid = 0
    69  			hdr.Gid = 0
    70  			// Setting the user/group write bits helps consistency of output.
    71  			hdr.Mode |= 0220
    72  			if err := tw.WriteHeader(hdr); err != nil {
    73  				return err
    74  			}
    75  			f, err := os.Open(path)
    76  			if err != nil {
    77  				return err
    78  			}
    79  			defer f.Close()
    80  			_, err = io.Copy(tw, f)
    81  			return err
    82  		}); err != nil {
    83  			return err
    84  		}
    85  	}
    86  	return nil
    87  }