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 }