archive.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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 {
  39. switch {
  40. case strings.HasSuffix(file.Name(), ".zip"):
  41. return NewZipArchive(file)
  42. case strings.HasSuffix(file.Name(), ".tar.gz"):
  43. return NewTarballArchive(file)
  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(basename, ext string, files []string) error {
  70. archfd, err := os.Create(basename + ext)
  71. if err != nil {
  72. return err
  73. }
  74. defer archfd.Close()
  75. archive := NewArchive(archfd)
  76. if archive == nil {
  77. return fmt.Errorf("unknown archive extension: %s", ext)
  78. }
  79. fmt.Println(basename + ext)
  80. if err := archive.Directory(basename); err != nil {
  81. return err
  82. }
  83. for _, file := range files {
  84. fmt.Println(" +", filepath.Base(file))
  85. if err := AddFile(archive, file); err != nil {
  86. return err
  87. }
  88. }
  89. return archive.Close()
  90. }
  91. type ZipArchive struct {
  92. dir string
  93. zipw *zip.Writer
  94. file io.Closer
  95. }
  96. func NewZipArchive(w io.WriteCloser) Archive {
  97. return &ZipArchive{"", zip.NewWriter(w), w}
  98. }
  99. func (a *ZipArchive) Directory(name string) error {
  100. a.dir = name + "/"
  101. return nil
  102. }
  103. func (a *ZipArchive) Header(fi os.FileInfo) (io.Writer, error) {
  104. head, err := zip.FileInfoHeader(fi)
  105. if err != nil {
  106. return nil, fmt.Errorf("can't make zip header: %v", err)
  107. }
  108. head.Name = a.dir + head.Name
  109. w, err := a.zipw.CreateHeader(head)
  110. if err != nil {
  111. return nil, fmt.Errorf("can't add zip header: %v", err)
  112. }
  113. return w, nil
  114. }
  115. func (a *ZipArchive) Close() error {
  116. if err := a.zipw.Close(); err != nil {
  117. return err
  118. }
  119. return a.file.Close()
  120. }
  121. type TarballArchive struct {
  122. dir string
  123. tarw *tar.Writer
  124. gzw *gzip.Writer
  125. file io.Closer
  126. }
  127. func NewTarballArchive(w io.WriteCloser) Archive {
  128. gzw := gzip.NewWriter(w)
  129. tarw := tar.NewWriter(gzw)
  130. return &TarballArchive{"", tarw, gzw, w}
  131. }
  132. func (a *TarballArchive) Directory(name string) error {
  133. a.dir = name + "/"
  134. return a.tarw.WriteHeader(&tar.Header{
  135. Name: a.dir,
  136. Mode: 0755,
  137. Typeflag: tar.TypeDir,
  138. })
  139. }
  140. func (a *TarballArchive) Header(fi os.FileInfo) (io.Writer, error) {
  141. head, err := tar.FileInfoHeader(fi, "")
  142. if err != nil {
  143. return nil, fmt.Errorf("can't make tar header: %v", err)
  144. }
  145. head.Name = a.dir + head.Name
  146. if err := a.tarw.WriteHeader(head); err != nil {
  147. return nil, fmt.Errorf("can't add tar header: %v", err)
  148. }
  149. return a.tarw, nil
  150. }
  151. func (a *TarballArchive) Close() error {
  152. if err := a.tarw.Close(); err != nil {
  153. return err
  154. }
  155. if err := a.gzw.Close(); err != nil {
  156. return err
  157. }
  158. return a.file.Close()
  159. }