Add '--jar' flag to soong_zip to move META-INF to the top of the zip

Bug: 64536066
Test: soong_zip --jar -o /tmp/out.zip -C . -l files.list && \
      zipdetails /tmp/out.zip | less \
      # and check that the META-INF entries are earlier in \
      # the list than other entries

Change-Id: Id5c6ea9ce8c3a6fbfb8366db753e6603a076dbf8
diff --git a/cmd/soong_zip/soong_zip.go b/cmd/soong_zip/soong_zip.go
index ef436a9..7405728 100644
--- a/cmd/soong_zip/soong_zip.go
+++ b/cmd/soong_zip/soong_zip.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"compress/flate"
+	"errors"
 	"flag"
 	"fmt"
 	"hash/crc32"
@@ -28,10 +29,12 @@
 	"runtime"
 	"runtime/pprof"
 	"runtime/trace"
+	"sort"
 	"strings"
 	"sync"
 	"time"
 
+	"android/soong/jar"
 	"android/soong/third_party/zip"
 )
 
@@ -135,6 +138,7 @@
 	relativeRoot = flag.String("C", "", "path to use as relative root of files in next -f or -l argument")
 	parallelJobs = flag.Int("j", runtime.NumCPU(), "number of parallel threads to use")
 	compLevel    = flag.Int("L", 5, "deflate compression level (0-9)")
+	emulateJar   = flag.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
 
 	fArgs            fileArgs
 	nonDeflatedFiles = make(uniqueSet)
@@ -271,6 +275,13 @@
 	return nil
 }
 
+func jarSort(mappings []pathMapping) {
+	less := func(i int, j int) (smaller bool) {
+		return jar.EntryNamesLess(mappings[i].dest, mappings[j].dest)
+	}
+	sort.SliceStable(mappings, less)
+}
+
 func (z *zipWriter) write(out string, pathMappings []pathMapping, manifest string) error {
 	f, err := os.Create(out)
 	if err != nil {
@@ -306,6 +317,18 @@
 		z.cpuRateLimiter.Stop()
 		z.memoryRateLimiter.Stop()
 	}()
+
+	if manifest != "" {
+		if !*emulateJar {
+			return errors.New("must specify --jar when specifying a manifest via -m")
+		}
+		pathMappings = append(pathMappings, pathMapping{"META-INF/MANIFEST.MF", manifest, zip.Deflate})
+	}
+
+	if *emulateJar {
+		jarSort(pathMappings)
+	}
+
 	go func() {
 		var err error
 		defer close(z.writeOps)
@@ -317,14 +340,6 @@
 				return
 			}
 		}
-
-		if manifest != "" {
-			err = z.writeFile("META-INF/MANIFEST.MF", manifest, zip.Deflate)
-			if err != nil {
-				z.errors <- err
-				return
-			}
-		}
 	}()
 
 	zipw := zip.NewWriter(f)