archive.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright 2016 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package build
  17. import (
  18. "archive/tar"
  19. "archive/zip"
  20. "compress/gzip"
  21. "fmt"
  22. "io"
  23. "os"
  24. "path/filepath"
  25. "strings"
  26. )
  27. type Archive interface {
  28. // Directory adds a new directory entry to the archive and sets the
  29. // directory for subsequent calls to Header.
  30. Directory(name string) error
  31. // Header adds a new file to the archive. The file is added to the directory
  32. // set by Directory. The content of the file must be written to the returned
  33. // writer.
  34. Header(os.FileInfo) (io.Writer, error)
  35. // Close flushes the archive and closes the underlying file.
  36. Close() error
  37. }
  38. func NewArchive(file *os.File) (Archive, string) {
  39. switch {
  40. case strings.HasSuffix(file.Name(), ".zip"):
  41. return NewZipArchive(file), strings.TrimSuffix(file.Name(), ".zip")
  42. case strings.HasSuffix(file.Name(), ".tar.gz"):
  43. return NewTarballArchive(file), strings.TrimSuffix(file.Name(), ".tar.gz")
  44. default:
  45. return nil, ""
  46. }
  47. }
  48. // AddFile appends an existing file to an archive.
  49. func AddFile(a Archive, file string) error {
  50. fd, err := os.Open(file)
  51. if err != nil {
  52. return err
  53. }
  54. defer fd.Close()
  55. fi, err := fd.Stat()
  56. if err != nil {
  57. return err
  58. }
  59. w, err := a.Header(fi)
  60. if err != nil {
  61. return err
  62. }
  63. if _, err := io.Copy(w, fd); err != nil {
  64. return err
  65. }
  66. return nil
  67. }
  68. // WriteArchive creates an archive containing the given files.
  69. func WriteArchive(name string, files []string) (err error) {
  70. archfd, err := os.Create(name)
  71. if err != nil {
  72. return err
  73. }
  74. defer func() {
  75. archfd.Close()
  76. // Remove the half-written archive on failure.
  77. if err != nil {
  78. os.Remove(name)
  79. }
  80. }()
  81. archive, basename := NewArchive(archfd)
  82. if archive == nil {
  83. return fmt.Errorf("unknown archive extension")
  84. }
  85. fmt.Println(name)
  86. if err := archive.Directory(basename); err != nil {
  87. return err
  88. }
  89. for _, file := range files {
  90. fmt.Println(" +", filepath.Base(file))
  91. if err := AddFile(archive, file); err != nil {
  92. return err
  93. }
  94. }
  95. return archive.Close()
  96. }
  97. type ZipArchive struct {
  98. dir string
  99. zipw *zip.Writer
  100. file io.Closer
  101. }
  102. func NewZipArchive(w io.WriteCloser) Archive {
  103. return &ZipArchive{"", zip.NewWriter(w), w}
  104. }
  105. func (a *ZipArchive) Directory(name string) error {
  106. a.dir = name + "/"
  107. return nil
  108. }
  109. func (a *ZipArchive) Header(fi os.FileInfo) (io.Writer, error) {
  110. head, err := zip.FileInfoHeader(fi)
  111. if err != nil {
  112. return nil, fmt.Errorf("can't make zip header: %v", err)
  113. }
  114. head.Name = a.dir + head.Name
  115. head.Method = zip.Deflate
  116. w, err := a.zipw.CreateHeader(head)
  117. if err != nil {
  118. return nil, fmt.Errorf("can't add zip header: %v", err)
  119. }
  120. return w, nil
  121. }
  122. func (a *ZipArchive) Close() error {
  123. if err := a.zipw.Close(); err != nil {
  124. return err
  125. }
  126. return a.file.Close()
  127. }
  128. type TarballArchive struct {
  129. dir string
  130. tarw *tar.Writer
  131. gzw *gzip.Writer
  132. file io.Closer
  133. }
  134. func NewTarballArchive(w io.WriteCloser) Archive {
  135. gzw := gzip.NewWriter(w)
  136. tarw := tar.NewWriter(gzw)
  137. return &TarballArchive{"", tarw, gzw, w}
  138. }
  139. func (a *TarballArchive) Directory(name string) error {
  140. a.dir = name + "/"
  141. return a.tarw.WriteHeader(&tar.Header{
  142. Name: a.dir,
  143. Mode: 0755,
  144. Typeflag: tar.TypeDir,
  145. })
  146. }
  147. func (a *TarballArchive) Header(fi os.FileInfo) (io.Writer, error) {
  148. head, err := tar.FileInfoHeader(fi, "")
  149. if err != nil {
  150. return nil, fmt.Errorf("can't make tar header: %v", err)
  151. }
  152. head.Name = a.dir + head.Name
  153. if err := a.tarw.WriteHeader(head); err != nil {
  154. return nil, fmt.Errorf("can't add tar header: %v", err)
  155. }
  156. return a.tarw, nil
  157. }
  158. func (a *TarballArchive) Close() error {
  159. if err := a.tarw.Close(); err != nil {
  160. return err
  161. }
  162. if err := a.gzw.Close(); err != nil {
  163. return err
  164. }
  165. return a.file.Close()
  166. }