Support in soong_zip to write byte buffers in addition to copying files
This will enable writing a modified manifest without having to create a temporary file first
Bug: 64536066
Test: soong_zip --jar -o /tmp/out.zip -C . -l files.list && \
# make sure that the output is binary equal
# with and without this patch
Change-Id: I559d653e0e72e641e1ee6745924cb835bb0a355b
diff --git a/cmd/soong_zip/soong_zip.go b/cmd/soong_zip/soong_zip.go
index bb2a70f..f7dc9e0 100644
--- a/cmd/soong_zip/soong_zip.go
+++ b/cmd/soong_zip/soong_zip.go
@@ -286,6 +286,13 @@
sort.SliceStable(mappings, less)
}
+type readerSeekerCloser interface {
+ io.Reader
+ io.ReaderAt
+ io.Closer
+ io.Seeker
+}
+
func (z *zipWriter) write(out string, pathMappings []pathMapping, manifest string) error {
f, err := os.Create(out)
if err != nil {
@@ -434,6 +441,7 @@
}
}
+// imports (possibly with compression) <src> into the zip at sub-path <dest>
func (z *zipWriter) writeFile(dest, src string, method uint16) error {
var fileSize int64
var executable bool
@@ -454,7 +462,31 @@
executable = s.Mode()&0100 != 0
}
+ r, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+
+ header := &zip.FileHeader{
+ Name: dest,
+ Method: method,
+ UncompressedSize64: uint64(fileSize),
+ }
+
+ if executable {
+ header.SetMode(0700)
+ }
+
+ return z.writeFileContents(header, r)
+}
+
+// writes the contents of r according to the specifications in header
+func (z *zipWriter) writeFileContents(header *zip.FileHeader, r readerSeekerCloser) (err error) {
+
+ header.SetModTime(z.time)
+
if z.directories {
+ dest := header.Name
dir, _ := filepath.Split(dest)
err := z.writeDirectory(dir)
if err != nil {
@@ -468,28 +500,19 @@
// Pre-fill a zipEntry, it will be sent in the compressChan once
// we're sure about the Method and CRC.
ze := &zipEntry{
- fh: &zip.FileHeader{
- Name: dest,
- Method: method,
-
- UncompressedSize64: uint64(fileSize),
- },
- }
- ze.fh.SetModTime(z.time)
- if executable {
- ze.fh.SetMode(0700)
+ fh: header,
}
- r, err := os.Open(src)
- if err != nil {
- return err
- }
-
- ze.allocatedSize = fileSize
+ ze.allocatedSize = int64(header.UncompressedSize64)
z.cpuRateLimiter.Request()
z.memoryRateLimiter.Request(ze.allocatedSize)
- if method == zip.Deflate && fileSize >= minParallelFileSize {
+ fileSize := int64(header.UncompressedSize64)
+ if fileSize == 0 {
+ fileSize = int64(header.UncompressedSize)
+ }
+
+ if header.Method == zip.Deflate && fileSize >= minParallelFileSize {
wg := new(sync.WaitGroup)
// Allocate enough buffer to hold all readers. We'll limit
@@ -499,7 +522,7 @@
// Calculate the CRC in the background, since reading the entire
// file could take a while.
//
- // We could split this up into chuncks as well, but it's faster
+ // We could split this up into chunks as well, but it's faster
// than the compression. Due to the Go Zip API, we also need to
// know the result before we can begin writing the compressed
// data out to the zipfile.
@@ -517,6 +540,9 @@
var dict []byte
if start >= windowSize {
dict, err = ioutil.ReadAll(io.NewSectionReader(r, start-windowSize, windowSize))
+ if err != nil {
+ return err
+ }
}
wg.Add(1)
@@ -526,12 +552,15 @@
close(ze.futureReaders)
// Close the file handle after all readers are done
- go func(wg *sync.WaitGroup, f *os.File) {
+ go func(wg *sync.WaitGroup, closer io.Closer) {
wg.Wait()
- f.Close()
+ closer.Close()
}(wg, r)
} else {
- go z.compressWholeFile(ze, r, compressChan)
+ go func() {
+ z.compressWholeFile(ze, r, compressChan)
+ r.Close()
+ }()
}
return nil
@@ -601,8 +630,7 @@
return buf, nil
}
-func (z *zipWriter) compressWholeFile(ze *zipEntry, r *os.File, compressChan chan *zipEntry) {
- defer r.Close()
+func (z *zipWriter) compressWholeFile(ze *zipEntry, r io.ReadSeeker, compressChan chan *zipEntry) {
crc := crc32.NewIEEE()
_, err := io.Copy(crc, r)
@@ -619,13 +647,13 @@
return
}
- readFile := func(r *os.File) ([]byte, error) {
- _, err = r.Seek(0, 0)
+ readFile := func(reader io.ReadSeeker) ([]byte, error) {
+ _, err := reader.Seek(0, 0)
if err != nil {
return nil, err
}
- buf, err := ioutil.ReadAll(r)
+ buf, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
}