func TestZipWriter(t *testing.T) {
	// Have to write an actual file for zip.OpenReader to use later.
	f, err := ioutil.TempFile("", "zip_writer_test")
	if err != nil {
		t.Fatalf("Failed to create temp file: %s", err)
	}
	filename := f.Name()
	defer os.Remove(filename)
	w := zip.NewWriter(f)
	if err := AddZipFile(w, "src/build/java/test_data/test.zip", nil, nil, "", true, nil); err != nil {
		t.Fatalf("Failed to add zip file: %s", err)
	}
	if err := w.Close(); err != nil {
		t.Fatalf("Failed to close zip file: %s", err)
	}
	w.Close()
	f.Close()

	r, err := zip.OpenReader(filename)
	if err != nil {
		t.Fatalf("Failed to reopen zip file: %s", err)
	}
	defer r.Close()

	files := []struct{ Name, Prefix string }{
		{"build_step.go", "// Implementation of Step interface."},
		{"incrementality.go", "// Utilities to help with incremental builds."},
	}
	for i, f := range r.File {
		if f.Name != files[i].Name {
			t.Errorf("File %d has wrong name: expected %s, was %s", i, files[i].Name, f.Name)
		}
		fr, err := f.Open()
		if err != nil {
			t.Errorf("Failed to reopen file %d [%s]: %s", i, f.Name, err)
		} else {
			buf := new(bytes.Buffer)
			if _, err = io.Copy(buf, fr); err != nil {
				t.Errorf("Failed to read full contents of file %d [%s]: %s", i, f.Name, err)
			} else if !strings.HasPrefix(buf.String(), files[i].Prefix) {
				t.Errorf("File %d [%s] didn't start with expected prefix: was %s", buf.String()[:20])
			}
			fr.Close()
		}
	}
}
Exemple #2
0
// AddZipFile copies the contents of a zip file into an existing zip writer.
func AddZipFile(w *zip.Writer, filepath string, include, exclude []string, stripPrefix string, strict bool, renameDirs map[string]string) error {
	r, err := zip.OpenReader(filepath)
	if err != nil {
		return err
	}
	defer r.Close()

	// Reopen file to get a directly readable version without decompression.
	r2, err := os.Open(filepath)
	if err != nil {
		return err
	}
	defer r2.Close()

outer:
	for _, f := range r.File {
		log.Debug("Found file %s (from %s)", f.Name, filepath)
		// This directory is very awkward. We need to merge the contents by concatenating them,
		// we can't replace them or leave them out.
		if strings.HasPrefix(f.Name, "META-INF/services/") ||
			strings.HasPrefix(f.Name, "META-INF/spring") ||
			f.Name == "META-INF/please_sourcemap" {
			if err := concatenateFile(w, f); err != nil {
				return err
			}
			continue
		}
		if !shouldInclude(f.Name, include, exclude) {
			continue outer
		}
		hasTrailingSlash := strings.HasSuffix(f.Name, "/")
		isDir := hasTrailingSlash || f.FileInfo().IsDir()
		if isDir && !hasTrailingSlash {
			f.Name = f.Name + "/"
		}
		if existing, present := getExistingFile(w, f.Name); present {
			// Allow duplicates of directories. Seemingly the best way to identify them is that
			// they end in a trailing slash.
			if isDir {
				continue
			}
			// Allow skipping existing files that are exactly the same as the added ones.
			// It's unnecessarily awkward to insist on not ever doubling up on a dependency.
			// TODO(pebers): Bit of a hack ignoring it when CRC is 0, would be better to add
			//               the correct CRC when added through WriteFile.
			if existing.CRC32 == f.CRC32 || existing.CRC32 == 0 {
				log.Info("Skipping %s / %s: already added (from %s)", filepath, f.Name, existing.ZipFile)
				continue
			}
			if strict {
				log.Error("Duplicate file %s (from %s, already added from %s); crc %d / %d", f.Name, filepath, existing.ZipFile, f.CRC32, existing.CRC32)
				return fmt.Errorf("File %s already added to destination zip file (from %s)", f.Name, existing.ZipFile)
			}
			continue
		}
		for before, after := range renameDirs {
			if strings.HasPrefix(f.Name, before) {
				f.Name = path.Join(after, strings.TrimPrefix(f.Name, before))
				if isDir {
					f.Name = f.Name + "/"
				}
				break
			}
		}
		f.Name = strings.TrimPrefix(f.Name, stripPrefix)
		// Java tools don't seem to like writing a data descriptor for stored items.
		// Unsure if this is a limitation of the format or a problem of those tools.
		f.Flags = 0
		addExistingFile(w, f.Name, filepath, f.CompressedSize64, f.UncompressedSize64, f.CRC32)

		start, err := f.DataOffset()
		if err != nil {
			return err
		}
		if _, err := r2.Seek(start, 0); err != nil {
			return err
		}
		// Make these deterministic.
		f.FileHeader.ModifiedDate = 0
		f.FileHeader.ModifiedTime = 0
		if err := addFile(w, &f.FileHeader, r2, f.CRC32); err != nil {
			return err
		}
	}
	return nil
}