Merge "Convert soong java from soong_zip to jar"
diff --git a/java/builder.go b/java/builder.go
index 017f64f..efe0a6b 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -41,8 +41,9 @@
 				`${config.JavacWrapper}${config.JavacCmd} ${config.CommonJdkFlags} ` +
 				`$javacFlags $bootClasspath $classpath ` +
 				`-source $javaVersion -target $javaVersion ` +
-				`-d $outDir -s $annoDir @$out.rsp || ( rm -rf "$outDir"; exit 41 ) && ` +
-				`find $outDir -name "*.class" > $out`,
+				`-d $outDir -s $annoDir @$out.rsp && ` +
+				`find $outDir -type f | sort | ${config.JarArgsCmd} $outDir > $out`,
+			CommandDeps:    []string{"${config.JavacCmd}", "${config.JarArgsCmd}"},
 			Rspfile:        "$out.rsp",
 			RspfileContent: "$in",
 		},
@@ -50,17 +51,17 @@
 
 	jar = pctx.AndroidStaticRule("jar",
 		blueprint.RuleParams{
-			Command:     `${config.SoongZipCmd} -o $out -d $jarArgs`,
-			CommandDeps: []string{"${config.SoongZipCmd}"},
+			Command:     `${config.JarCmd} $operation ${out}.tmp $manifest $jarArgs && ${config.Zip2ZipCmd} -t -i ${out}.tmp -o ${out} && rm ${out}.tmp`,
+			CommandDeps: []string{"${config.JarCmd}"},
 		},
-		"jarCmd", "jarArgs")
+		"operation", "manifest", "jarArgs")
 
 	dx = pctx.AndroidStaticRule("dx",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-				`${config.DxCmd} --dex --output=$outDir $dxFlags $in || ( rm -rf "$outDir"; exit 41 ) && ` +
-				`find "$outDir" -name "classes*.dex" | sort > $out`,
-			CommandDeps: []string{"${config.DxCmd}"},
+				`${config.DxCmd} --dex --output=$outDir $dxFlags $in && ` +
+				`find "$outDir" -name "classes*.dex" | sort | ${config.JarArgsCmd} ${outDir} > $out`,
+			CommandDeps: []string{"${config.DxCmd}", "${config.JarArgsCmd}"},
 		},
 		"outDir", "dxFlags")
 
@@ -74,11 +75,18 @@
 	extractPrebuilt = pctx.AndroidStaticRule("extractPrebuilt",
 		blueprint.RuleParams{
 			Command: `rm -rf $outDir && unzip -qo $in -d $outDir && ` +
-				`find $outDir -name "*.class" > $classFile && ` +
-				`find $outDir -type f -a \! -name "*.class" -a \! -name "MANIFEST.MF" > $resourceFile || ` +
-				`(rm -rf $outDir; exit 42)`,
+				`find $outDir -name "*.class" | sort | ${config.JarArgsCmd} ${outDir} > $classFile && ` +
+				`find $outDir -type f -a \! -name "*.class" -a \! -name "MANIFEST.MF" | sort | ${config.JarArgsCmd} ${outDir} > $resourceFile`,
+			CommandDeps: []string{"${config.JarArgsCmd}"},
 		},
 		"outDir", "classFile", "resourceFile")
+
+	fileListToJarArgs = pctx.AndroidStaticRule("fileListToJarArgs",
+		blueprint.RuleParams{
+			Command:     `${config.JarArgsCmd} -f $in -p ${outDir} -o $out`,
+			CommandDeps: []string{"${config.JarjarCmd}"},
+		},
+		"outDir")
 )
 
 func init() {
@@ -95,11 +103,15 @@
 }
 
 type jarSpec struct {
-	fileList, dir android.Path
+	android.ModuleOutPath
 }
 
-func (j jarSpec) soongJarArgs() string {
-	return "-C " + j.dir.String() + " -l " + j.fileList.String()
+func (j jarSpec) jarArgs() string {
+	return "@" + j.String()
+}
+
+func (j jarSpec) path() android.Path {
+	return j.ModuleOutPath
 }
 
 func TransformJavaToClasses(ctx android.ModuleContext, srcFiles android.Paths, srcFileLists android.Paths,
@@ -129,7 +141,7 @@
 		},
 	})
 
-	return jarSpec{classFileList, classDir}
+	return jarSpec{classFileList}
 }
 
 func TransformClassesToJar(ctx android.ModuleContext, classes []jarSpec,
@@ -141,13 +153,14 @@
 	jarArgs := []string{}
 
 	for _, j := range classes {
-		deps = append(deps, j.fileList)
-		jarArgs = append(jarArgs, j.soongJarArgs())
+		deps = append(deps, j.path())
+		jarArgs = append(jarArgs, j.jarArgs())
 	}
 
+	operation := "cf"
 	if manifest.Valid() {
+		operation = "cfm"
 		deps = append(deps, manifest.Path())
-		jarArgs = append(jarArgs, "-m "+manifest.String())
 	}
 
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
@@ -156,7 +169,9 @@
 		Output:      outputFile,
 		Implicits:   deps,
 		Args: map[string]string{
-			"jarArgs": strings.Join(jarArgs, " "),
+			"jarArgs":   strings.Join(jarArgs, " "),
+			"operation": operation,
+			"manifest":  manifest.String(),
 		},
 	})
 
@@ -180,7 +195,7 @@
 		},
 	})
 
-	return jarSpec{outputFile, outDir}
+	return jarSpec{outputFile}
 }
 
 func TransformDexToJavaLib(ctx android.ModuleContext, resources []jarSpec,
@@ -191,12 +206,12 @@
 	var jarArgs []string
 
 	for _, j := range resources {
-		deps = append(deps, j.fileList)
-		jarArgs = append(jarArgs, j.soongJarArgs())
+		deps = append(deps, j.path())
+		jarArgs = append(jarArgs, j.jarArgs())
 	}
 
-	deps = append(deps, dexJarSpec.fileList)
-	jarArgs = append(jarArgs, dexJarSpec.soongJarArgs())
+	deps = append(deps, dexJarSpec.path())
+	jarArgs = append(jarArgs, dexJarSpec.jarArgs())
 
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:        jar,
@@ -204,7 +219,8 @@
 		Output:      outputFile,
 		Implicits:   deps,
 		Args: map[string]string{
-			"jarArgs": strings.Join(jarArgs, " "),
+			"operation": "cf",
+			"jarArgs":   strings.Join(jarArgs, " "),
 		},
 	})
 
@@ -246,5 +262,21 @@
 		},
 	})
 
-	return jarSpec{classFileList, classDir}, jarSpec{resourceFileList, classDir}
+	return jarSpec{classFileList}, jarSpec{resourceFileList}
+}
+
+func TransformFileListToJarSpec(ctx android.ModuleContext, dir, fileListFile android.Path) jarSpec {
+	outputFile := android.PathForModuleOut(ctx, fileListFile.Base()+".jarArgs")
+
+	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+		Rule:        fileListToJarArgs,
+		Description: "file list to jar args",
+		Output:      outputFile,
+		Input:       fileListFile,
+		Args: map[string]string{
+			"outDir": dir.String(),
+		},
+	})
+
+	return jarSpec{outputFile}
 }
diff --git a/java/config/config.go b/java/config/config.go
index 44651cb..90d0fb5 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -53,7 +53,8 @@
 	pctx.SourcePathVariable("JarCmd", "${JavaToolchain}/jar")
 	pctx.SourcePathVariable("JavadocCmd", "${JavaToolchain}/javadoc")
 
-	pctx.StaticVariable("SoongZipCmd", filepath.Join("${bootstrap.ToolDir}", "soong_zip"))
+	pctx.StaticVariable("Zip2ZipCmd", filepath.Join("${bootstrap.ToolDir}", "zip2zip"))
+	pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
 	pctx.HostBinToolVariable("DxCmd", "dx")
 	pctx.HostJavaToolVariable("JarjarCmd", "jarjar.jar")
 
diff --git a/java/resources.go b/java/resources.go
index 60dc934..f1c9d06 100644
--- a/java/resources.go
+++ b/java/resources.go
@@ -63,7 +63,7 @@
 
 			pattern := filepath.Join(dir.String(), "**/*")
 			bootstrap.GlobFile(ctx, pattern, excludes, fileListFile.String(), depFile)
-			jarSpecs = append(jarSpecs, jarSpec{fileListFile, dir})
+			jarSpecs = append(jarSpecs, TransformFileListToJarSpec(ctx, dir, fileListFile))
 		}
 	}
 
diff --git a/scripts/jar-args.sh b/scripts/jar-args.sh
new file mode 100755
index 0000000..9f05394
--- /dev/null
+++ b/scripts/jar-args.sh
@@ -0,0 +1,64 @@
+#!/bin/bash -e
+
+# Script that takes a list of files on stdin and converts it to arguments to jar on stdout
+# Usage:
+#        find $dir -type f | sort | jar-args.sh $dir > jar_args.txt
+#        jar cf out.jar @jar_args.txt
+
+case $(uname) in
+  Linux)
+    extended_re=-r
+    ;;
+  Darwin)
+    extended_re=-E
+    ;;
+  *) echo "unknown OS:" $(uname) >&2 && exit 1;;
+esac
+
+if [ "$1" == "--test" ]; then
+  in=$(mktemp)
+  expected=$(mktemp)
+  out=$(mktemp)
+  cat > $in <<EOF
+a
+a/b
+a/b/'
+a/b/"
+a/b/\\
+a/b/#
+a/b/a
+EOF
+  cat > $expected <<EOF
+
+-C 'a' 'b'
+-C 'a' 'b/\\''
+-C 'a' 'b/"'
+-C 'a' 'b/\\\\'
+-C 'a' 'b/#'
+-C 'a' 'b/a'
+EOF
+  cat $in | $0 a > $out
+
+  if cmp $out $expected; then
+    status=0
+    echo "PASS"
+  else
+    status=1
+    echo "FAIL"
+    echo "got:"
+    cat $out
+    echo "expected:"
+    cat $expected
+  fi
+  rm -f $in $expected $out
+  exit $status
+fi
+
+# In order, the regexps:
+#   - Strip $1/ from the beginning of each line, and everything from lines that just have $1
+#   - Escape single and double quotes, '#', ' ', and '\'
+#   - Prefix each non-blank line with -C $1
+sed ${extended_re} \
+  -e"s,^$1(/|\$),," \
+  -e"s,(['\\]),\\\\\1,g" \
+  -e"s,^(.+),-C '$1' '\1',"