Merge "Add D8 support"
diff --git a/Android.bp b/Android.bp
index 32b89d1..1f6ebe2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5,6 +5,7 @@
     "fs",
     "finder",
     "jar",
+    "zip",
     "third_party/zip",
     "ui/*",
 ]
diff --git a/android/paths.go b/android/paths.go
index f88d650..7443547 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -199,9 +199,9 @@
 	if pathConfig(ctx).AllowMissingDependencies() {
 		if modCtx, ok := ctx.(ModuleContext); ok {
 			ret := make(Paths, 0, len(paths))
-			intermediates := filepath.Join(modCtx.ModuleDir(), modCtx.ModuleName(), modCtx.ModuleSubDir(), "missing")
+			intermediates := pathForModule(modCtx).withRel("missing")
 			for _, path := range paths {
-				p := ExistentPathForSource(ctx, intermediates, path)
+				p := ExistentPathForSource(ctx, intermediates.String(), path)
 				if p.Valid() {
 					ret = append(ret, p.Path())
 				} else {
@@ -303,6 +303,32 @@
 	return list[:k]
 }
 
+func indexPathList(s Path, list []Path) int {
+	for i, l := range list {
+		if l == s {
+			return i
+		}
+	}
+
+	return -1
+}
+
+func inPathList(p Path, list []Path) bool {
+	return indexPathList(p, list) != -1
+}
+
+func FilterPathList(list []Path, filter []Path) (remainder []Path, filtered []Path) {
+	for _, l := range list {
+		if inPathList(l, filter) {
+			filtered = append(filtered, l)
+		} else {
+			remainder = append(remainder, l)
+		}
+	}
+
+	return
+}
+
 // HasExt returns true of any of the paths have extension ext, otherwise false
 func (p Paths) HasExt(ext string) bool {
 	for _, path := range p {
@@ -546,6 +572,12 @@
 	basePath
 }
 
+func (p OutputPath) withRel(rel string) OutputPath {
+	p.basePath.path = filepath.Join(p.basePath.path, rel)
+	p.basePath.rel = rel
+	return p
+}
+
 var _ Path = OutputPath{}
 
 // PathForOutput joins the provided paths and returns an OutputPath that is
@@ -640,6 +672,10 @@
 
 var _ Path = ModuleOutPath{}
 
+func pathForModule(ctx ModuleContext) OutputPath {
+	return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
+}
+
 // PathForVndkRefDump returns an OptionalPath representing the path of the reference
 // abi dump for the given module. This is not guaranteed to be valid.
 func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string, vndkOrNdk, isSourceDump bool) OptionalPath {
@@ -668,14 +704,15 @@
 // output directory.
 func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
 	p := validatePath(ctx, paths...)
-	return ModuleOutPath{PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), p)}
+	return ModuleOutPath{
+		OutputPath: pathForModule(ctx).withRel(p),
+	}
 }
 
 // ModuleGenPath is a Path representing the 'gen' directory in a module's output
 // directory. Mainly used for generated sources.
 type ModuleGenPath struct {
 	ModuleOutPath
-	path string
 }
 
 var _ Path = ModuleGenPath{}
@@ -687,8 +724,9 @@
 func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath {
 	p := validatePath(ctx, paths...)
 	return ModuleGenPath{
-		PathForModuleOut(ctx, "gen", p),
-		p,
+		ModuleOutPath: ModuleOutPath{
+			OutputPath: pathForModule(ctx).withRel("gen").withRel(p),
+		},
 	}
 }
 
diff --git a/android/testing.go b/android/testing.go
index 519e279..62b91e2 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -65,7 +65,14 @@
 	})
 
 	if module == nil {
-		panic(fmt.Errorf("failed to find module %q variant %q", name, variant))
+		// find all the modules that do exist
+		allModuleNames := []string{}
+		ctx.VisitAllModules(func(m blueprint.Module) {
+			allModuleNames = append(allModuleNames, m.(Module).Name()+"("+ctx.ModuleSubDir(m)+")")
+		})
+
+		panic(fmt.Errorf("failed to find module %q variant %q."+
+			"\nall modules: %v", name, variant, allModuleNames))
 	}
 
 	return TestingModule{module}
@@ -95,7 +102,7 @@
 			outputs = append(outputs, p.Output)
 		}
 		for _, f := range outputs {
-			if f.Base() == file {
+			if f.Rel() == file {
 				return p
 			}
 		}
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index f5858a7..0de5009 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -135,6 +135,7 @@
 			"LOCAL_PROPRIETARY_MODULE":       "proprietary",
 			"LOCAL_VENDOR_MODULE":            "vendor",
 			"LOCAL_EXPORT_PACKAGE_RESOURCES": "export_package_resources",
+			"LOCAL_DEX_PREOPT":               "dex_preopt",
 		})
 }
 
diff --git a/androidmk/cmd/androidmk/androidmk.go b/androidmk/cmd/androidmk/androidmk.go
index a49f620..5cb3f7a 100644
--- a/androidmk/cmd/androidmk/androidmk.go
+++ b/androidmk/cmd/androidmk/androidmk.go
@@ -80,11 +80,23 @@
 }
 
 func (f *bpFile) setMkPos(pos, end scanner.Position) {
-	if pos.Line < f.mkPos.Line {
-		panic(fmt.Errorf("out of order lines, %q after %q", pos, f.mkPos))
+	// It is unusual but not forbidden for pos.Line to be smaller than f.mkPos.Line
+	// For example:
+	//
+	// if true                       # this line is emitted 1st
+	// if true                       # this line is emitted 2nd
+	// some-target: some-file        # this line is emitted 3rd
+	//         echo doing something  # this recipe is emitted 6th
+	// endif #some comment           # this endif is emitted 4th; this comment is part of the recipe
+	//         echo doing more stuff # this is part of the recipe
+	// endif                         # this endif is emitted 5th
+	//
+	// However, if pos.Line < f.mkPos.Line, we treat it as though it were equal
+	if pos.Line >= f.mkPos.Line {
+		f.bpPos.Line += (pos.Line - f.mkPos.Line)
+		f.mkPos = end
 	}
-	f.bpPos.Line += (pos.Line - f.mkPos.Line)
-	f.mkPos = end
+
 }
 
 type conditional struct {
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go
index 4681a7d..0b86540 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -425,6 +425,41 @@
   }
 }`,
 	},
+	{
+		// the important part of this test case is that it confirms that androidmk doesn't
+		// panic in this case
+		desc: "multiple directives inside recipe",
+		in: `
+ifeq ($(a),true)
+ifeq ($(b),false)
+imABuildStatement: somefile
+	echo begin
+endif # a==true
+	echo middle
+endif # b==false
+	echo end
+`,
+		expected: `
+// ANDROIDMK TRANSLATION ERROR: unsupported conditional
+// ifeq ($(a),true)
+
+// ANDROIDMK TRANSLATION ERROR: unsupported conditional
+// ifeq ($(b),false)
+
+// ANDROIDMK TRANSLATION ERROR: unsupported line
+// rule:       imABuildStatement: somefile
+// echo begin
+//  # a==true
+// echo middle
+//  # b==false
+// echo end
+//
+// ANDROIDMK TRANSLATION ERROR: endif from unsupported contitional
+// endif
+// ANDROIDMK TRANSLATION ERROR: endif from unsupported contitional
+// endif
+		`,
+	},
 }
 
 func reformatBlueprint(input string) string {
diff --git a/cc/cc.go b/cc/cc.go
index 2fafaa2..7163696 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -326,6 +326,11 @@
 
 	// Flags used to compile this module
 	flags Flags
+
+	// When calling a linker, if module A depends on module B, then A must precede B in its command
+	// line invocation. staticDepsInLinkOrder stores the proper ordering of all of the transitive
+	// deps of this module
+	staticDepsInLinkOrder android.Paths
 }
 
 func (c *Module) Init() android.Module {
@@ -540,6 +545,51 @@
 	return name
 }
 
+// orderDeps reorders dependencies into a list such that if module A depends on B, then
+// A will precede B in the resultant list.
+// This is convenient for passing into a linker.
+func orderDeps(directDeps []android.Path, transitiveDeps map[android.Path][]android.Path) (orderedAllDeps []android.Path, orderedDeclaredDeps []android.Path) {
+	// If A depends on B, then
+	//   Every list containing A will also contain B later in the list
+	//   So, after concatenating all lists, the final instance of B will have come from the same
+	//     original list as the final instance of A
+	//   So, the final instance of B will be later in the concatenation than the final A
+	//   So, keeping only the final instance of A and of B ensures that A is earlier in the output
+	//     list than B
+	for _, dep := range directDeps {
+		orderedAllDeps = append(orderedAllDeps, dep)
+		orderedAllDeps = append(orderedAllDeps, transitiveDeps[dep]...)
+	}
+
+	orderedAllDeps = lastUniquePaths(orderedAllDeps)
+
+	// We don't want to add any new dependencies into directDeps (to allow the caller to
+	// intentionally exclude or replace any unwanted transitive dependencies), so we limit the
+	// resultant list to only what the caller has chosen to include in directDeps
+	_, orderedDeclaredDeps = android.FilterPathList(orderedAllDeps, directDeps)
+
+	return orderedAllDeps, orderedDeclaredDeps
+}
+
+func orderStaticModuleDeps(module *Module, deps []*Module) (results []android.Path) {
+	// make map of transitive dependencies
+	transitiveStaticDepNames := make(map[android.Path][]android.Path, len(deps))
+	for _, dep := range deps {
+		transitiveStaticDepNames[dep.outputFile.Path()] = dep.staticDepsInLinkOrder
+	}
+	// get the output file for each declared dependency
+	depFiles := []android.Path{}
+	for _, dep := range deps {
+		depFiles = append(depFiles, dep.outputFile.Path())
+	}
+
+	// reorder the dependencies based on transitive dependencies
+	module.staticDepsInLinkOrder, results = orderDeps(depFiles, transitiveStaticDepNames)
+
+	return results
+
+}
+
 func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
 
 	ctx := &moduleContext{
@@ -985,6 +1035,8 @@
 func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
 
+	directStaticDeps := []*Module{}
+
 	ctx.VisitDirectDeps(func(dep blueprint.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
@@ -1108,7 +1160,8 @@
 			depPtr = &depPaths.LateSharedLibsDeps
 			depFile = ccDep.linker.(libraryInterface).toc()
 		case staticDepTag, staticExportDepTag:
-			ptr = &depPaths.StaticLibs
+			ptr = nil
+			directStaticDeps = append(directStaticDeps, ccDep)
 		case lateStaticDepTag:
 			ptr = &depPaths.LateStaticLibs
 		case wholeStaticDepTag:
@@ -1192,6 +1245,9 @@
 		}
 	})
 
+	// use the ordered dependencies as this module's dependencies
+	depPaths.StaticLibs = append(depPaths.StaticLibs, orderStaticModuleDeps(c, directStaticDeps)...)
+
 	// Dedup exported flags from dependencies
 	depPaths.Flags = firstUniqueElements(depPaths.Flags)
 	depPaths.GeneratedHeaders = android.FirstUniquePaths(depPaths.GeneratedHeaders)
@@ -1417,6 +1473,23 @@
 	return list[totalSkip:]
 }
 
+// lastUniquePaths is the same as lastUniqueElements but uses Path structs
+func lastUniquePaths(list []android.Path) []android.Path {
+	totalSkip := 0
+	for i := len(list) - 1; i >= totalSkip; i-- {
+		skip := 0
+		for j := i - 1; j >= totalSkip; j-- {
+			if list[i] == list[j] {
+				skip++
+			} else {
+				list[j+skip] = list[j]
+			}
+		}
+		totalSkip += skip
+	}
+	return list[totalSkip:]
+}
+
 func getCurrentNdkPrebuiltVersion(ctx DepsContext) string {
 	if ctx.AConfig().PlatformSdkVersionInt() > config.NdkMaxPrebuiltVersionInt {
 		return strconv.Itoa(config.NdkMaxPrebuiltVersionInt)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 94e3e66..b9cdba5 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2,9 +2,12 @@
 
 import (
 	"android/soong/android"
+	"fmt"
 	"io/ioutil"
 	"os"
 	"reflect"
+	"sort"
+	"strings"
 	"testing"
 
 	"github.com/google/blueprint/proptools"
@@ -43,6 +46,7 @@
 	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(libraryFactory))
 	ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(toolchainLibraryFactory))
 	ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(llndkLibraryFactory))
+	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(objectFactory))
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("image", vendorMutator).Parallel()
 		ctx.BottomUp("link", linkageMutator).Parallel()
@@ -50,6 +54,64 @@
 	})
 	ctx.Register()
 
+	// add some modules that are required by the compiler and/or linker
+	bp = bp + `
+		toolchain_library {
+			name: "libatomic",
+			vendor_available: true,
+		}
+
+		toolchain_library {
+			name: "libcompiler_rt-extras",
+			vendor_available: true,
+		}
+
+		toolchain_library {
+			name: "libgcc",
+			vendor_available: true,
+		}
+
+		cc_library {
+			name: "libc",
+			no_libgcc : true,
+			nocrt : true,
+			system_shared_libs: [],
+		}
+		llndk_library {
+			name: "libc",
+			symbol_file: "",
+		}
+		cc_library {
+			name: "libm",
+			no_libgcc : true,
+			nocrt : true,
+			system_shared_libs: [],
+		}
+		llndk_library {
+			name: "libm",
+			symbol_file: "",
+		}
+		cc_library {
+			name: "libdl",
+			no_libgcc : true,
+			nocrt : true,
+			system_shared_libs: [],
+		}
+		llndk_library {
+			name: "libdl",
+			symbol_file: "",
+		}
+
+		cc_object {
+			name: "crtbegin_so",
+		}
+
+		cc_object {
+			name: "crtend_so",
+		}
+
+`
+
 	ctx.MockFileSystem(map[string][]byte{
 		"Android.bp": []byte(bp),
 		"foo.c":      nil,
@@ -57,9 +119,9 @@
 	})
 
 	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
-	fail(t, errs)
+	failIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
-	fail(t, errs)
+	failIfErrored(t, errs)
 
 	return ctx
 }
@@ -79,44 +141,6 @@
 				},
 			},
 		}
-		toolchain_library {
-			name: "libatomic",
-			vendor_available: true,
-		}
-		toolchain_library {
-			name: "libcompiler_rt-extras",
-			vendor_available: true,
-		}
-		cc_library {
-			name: "libc",
-			no_libgcc : true,
-			nocrt : true,
-			system_shared_libs: [],
-		}
-		llndk_library {
-			name: "libc",
-			symbol_file: "",
-		}
-		cc_library {
-			name: "libm",
-			no_libgcc : true,
-			nocrt : true,
-			system_shared_libs: [],
-		}
-		llndk_library {
-			name: "libm",
-			symbol_file: "",
-		}
-		cc_library {
-			name: "libdl",
-			no_libgcc : true,
-			nocrt : true,
-			system_shared_libs: [],
-		}
-		llndk_library {
-			name: "libdl",
-			symbol_file: "",
-		}
 	`)
 
 	ld := ctx.ModuleForTests("libTest", "android_arm_armv7-a-neon_vendor_shared").Rule("ld")
@@ -339,3 +363,245 @@
 		}
 	}
 }
+
+var staticLinkDepOrderTestCases = []struct {
+	// This is a string representation of a map[moduleName][]moduleDependency .
+	// It models the dependencies declared in an Android.bp file.
+	in string
+
+	// allOrdered is a string representation of a map[moduleName][]moduleDependency .
+	// The keys of allOrdered specify which modules we would like to check.
+	// The values of allOrdered specify the expected result (of the transitive closure of all
+	// dependencies) for each module to test
+	allOrdered string
+
+	// outOrdered is a string representation of a map[moduleName][]moduleDependency .
+	// The keys of outOrdered specify which modules we would like to check.
+	// The values of outOrdered specify the expected result (of the ordered linker command line)
+	// for each module to test.
+	outOrdered string
+}{
+	// Simple tests
+	{
+		in:         "",
+		outOrdered: "",
+	},
+	{
+		in:         "a:",
+		outOrdered: "a:",
+	},
+	{
+		in:         "a:b; b:",
+		outOrdered: "a:b; b:",
+	},
+	// Tests of reordering
+	{
+		// diamond example
+		in:         "a:d,b,c; b:d; c:d; d:",
+		outOrdered: "a:b,c,d; b:d; c:d; d:",
+	},
+	{
+		// somewhat real example
+		in:         "bsdiff_unittest:b,c,d,e,f,g,h,i; e:b",
+		outOrdered: "bsdiff_unittest:c,d,e,b,f,g,h,i; e:b",
+	},
+	{
+		// multiple reorderings
+		in:         "a:b,c,d,e; d:b; e:c",
+		outOrdered: "a:d,b,e,c; d:b; e:c",
+	},
+	{
+		// should reorder without adding new transitive dependencies
+		in:         "bin:lib2,lib1;             lib1:lib2,liboptional",
+		allOrdered: "bin:lib1,lib2,liboptional; lib1:lib2,liboptional",
+		outOrdered: "bin:lib1,lib2;             lib1:lib2,liboptional",
+	},
+	{
+		// multiple levels of dependencies
+		in:         "a:b,c,d,e,f,g,h; f:b,c,d; b:c,d; c:d",
+		allOrdered: "a:e,f,b,c,d,g,h; f:b,c,d; b:c,d; c:d",
+		outOrdered: "a:e,f,b,c,d,g,h; f:b,c,d; b:c,d; c:d",
+	},
+	// tiebreakers for when two modules specifying different orderings and there is no dependency
+	// to dictate an order
+	{
+		// if the tie is between two modules at the end of a's deps, then a's order wins
+		in:         "a1:b,c,d,e; a2:b,c,e,d; b:d,e; c:e,d",
+		outOrdered: "a1:b,c,d,e; a2:b,c,e,d; b:d,e; c:e,d",
+	},
+	{
+		// if the tie is between two modules at the start of a's deps, then c's order is used
+		in:         "a1:d,e,b1,c1; b1:d,e; c1:e,d;   a2:d,e,b2,c2; b2:d,e; c2:d,e",
+		outOrdered: "a1:b1,c1,e,d; b1:d,e; c1:e,d;   a2:b2,c2,d,e; b2:d,e; c2:d,e",
+	},
+	// Tests involving duplicate dependencies
+	{
+		// simple duplicate
+		in:         "a:b,c,c,b",
+		outOrdered: "a:c,b",
+	},
+	{
+		// duplicates with reordering
+		in:         "a:b,c,d,c; c:b",
+		outOrdered: "a:d,c,b",
+	},
+	// Tests to confirm the nonexistence of infinite loops.
+	// These cases should never happen, so as long as the test terminates and the
+	// result is deterministic then that should be fine.
+	{
+		in:         "a:a",
+		outOrdered: "a:a",
+	},
+	{
+		in:         "a:b;   b:c;   c:a",
+		allOrdered: "a:b,c; b:c,a; c:a,b",
+		outOrdered: "a:b;   b:c;   c:a",
+	},
+	{
+		in:         "a:b,c;   b:c,a;   c:a,b",
+		allOrdered: "a:c,a,b; b:a,b,c; c:b,c,a",
+		outOrdered: "a:c,b;   b:a,c;   c:b,a",
+	},
+}
+
+// converts from a string like "a:b,c; d:e" to (["a","b"], {"a":["b","c"], "d":["e"]}, [{"a", "a.o"}, {"b", "b.o"}])
+func parseModuleDeps(text string) (modulesInOrder []android.Path, allDeps map[android.Path][]android.Path) {
+	// convert from "a:b,c; d:e" to "a:b,c;d:e"
+	strippedText := strings.Replace(text, " ", "", -1)
+	if len(strippedText) < 1 {
+		return []android.Path{}, make(map[android.Path][]android.Path, 0)
+	}
+	allDeps = make(map[android.Path][]android.Path, 0)
+
+	// convert from "a:b,c;d:e" to ["a:b,c", "d:e"]
+	moduleTexts := strings.Split(strippedText, ";")
+
+	outputForModuleName := func(moduleName string) android.Path {
+		return android.PathForTesting(moduleName)
+	}
+
+	for _, moduleText := range moduleTexts {
+		// convert from "a:b,c" to ["a", "b,c"]
+		components := strings.Split(moduleText, ":")
+		if len(components) != 2 {
+			panic(fmt.Sprintf("illegal module dep string %q from larger string %q; must contain one ':', not %v", moduleText, text, len(components)-1))
+		}
+		moduleName := components[0]
+		moduleOutput := outputForModuleName(moduleName)
+		modulesInOrder = append(modulesInOrder, moduleOutput)
+
+		depString := components[1]
+		// convert from "b,c" to ["b", "c"]
+		depNames := strings.Split(depString, ",")
+		if len(depString) < 1 {
+			depNames = []string{}
+		}
+		var deps []android.Path
+		for _, depName := range depNames {
+			deps = append(deps, outputForModuleName(depName))
+		}
+		allDeps[moduleOutput] = deps
+	}
+	return modulesInOrder, allDeps
+}
+
+func TestStaticLinkDependencyOrdering(t *testing.T) {
+	for _, testCase := range staticLinkDepOrderTestCases {
+		errs := []string{}
+
+		// parse testcase
+		_, givenTransitiveDeps := parseModuleDeps(testCase.in)
+		expectedModuleNames, expectedTransitiveDeps := parseModuleDeps(testCase.outOrdered)
+		if testCase.allOrdered == "" {
+			// allow the test case to skip specifying allOrdered
+			testCase.allOrdered = testCase.outOrdered
+		}
+		_, expectedAllDeps := parseModuleDeps(testCase.allOrdered)
+
+		// For each module whose post-reordered dependencies were specified, validate that
+		// reordering the inputs produces the expected outputs.
+		for _, moduleName := range expectedModuleNames {
+			moduleDeps := givenTransitiveDeps[moduleName]
+			orderedAllDeps, orderedDeclaredDeps := orderDeps(moduleDeps, givenTransitiveDeps)
+
+			correctAllOrdered := expectedAllDeps[moduleName]
+			if !reflect.DeepEqual(orderedAllDeps, correctAllOrdered) {
+				errs = append(errs, fmt.Sprintf("orderDeps returned incorrect orderedAllDeps."+
+					"\nInput:    %q"+
+					"\nmodule:   %v"+
+					"\nexpected: %s"+
+					"\nactual:   %s",
+					testCase.in, moduleName, correctAllOrdered, orderedAllDeps))
+			}
+
+			correctOutputDeps := expectedTransitiveDeps[moduleName]
+			if !reflect.DeepEqual(correctOutputDeps, orderedDeclaredDeps) {
+				errs = append(errs, fmt.Sprintf("orderDeps returned incorrect orderedDeclaredDeps."+
+					"\nInput:    %q"+
+					"\nmodule:   %v"+
+					"\nexpected: %s"+
+					"\nactual:   %s",
+					testCase.in, moduleName, correctOutputDeps, orderedDeclaredDeps))
+			}
+		}
+
+		if len(errs) > 0 {
+			sort.Strings(errs)
+			for _, err := range errs {
+				t.Error(err)
+			}
+		}
+	}
+}
+func failIfErrored(t *testing.T, errs []error) {
+	if len(errs) > 0 {
+		for _, err := range errs {
+			t.Error(err)
+		}
+		t.FailNow()
+	}
+}
+
+func getOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
+	for _, moduleName := range moduleNames {
+		module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
+		output := module.outputFile.Path()
+		paths = append(paths, output)
+	}
+	return paths
+}
+
+func TestLibDeps(t *testing.T) {
+	ctx := testCc(t, `
+	cc_library {
+		name: "a",
+		static_libs: ["b", "c", "d"],
+	}
+	cc_library {
+		name: "b",
+	}
+	cc_library {
+		name: "c",
+		static_libs: ["b"],
+	}
+	cc_library {
+		name: "d",
+	}
+
+	`)
+
+	variant := "android_arm64_armv8-a_core_static"
+	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
+	actual := moduleA.staticDepsInLinkOrder
+	expected := getOutputPaths(ctx, variant, []string{"c", "b", "d"})
+
+	if !reflect.DeepEqual(actual, expected) {
+		t.Errorf("staticDeps orderings were not propagated correctly"+
+			"\nactual:   %v"+
+			"\nexpected: %v",
+			actual,
+			expected,
+		)
+	}
+
+}
diff --git a/cc/library.go b/cc/library.go
index e963ecb..1434f2c 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -64,6 +64,12 @@
 		// export headers generated from .proto sources
 		Export_proto_headers bool
 	}
+	Target struct {
+		Vendor struct {
+			// version script for this vendor variant
+			Version_script *string `android:"arch_variant"`
+		}
+	}
 }
 
 type LibraryMutatedProperties struct {
@@ -455,7 +461,11 @@
 		deps.StaticLibs = append(deps.StaticLibs, library.Properties.Shared.Static_libs...)
 		deps.SharedLibs = append(deps.SharedLibs, library.Properties.Shared.Shared_libs...)
 	}
-
+	if ctx.useVndk() {
+		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
+		deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Vendor.Exclude_shared_libs)
+		deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
+	}
 	return deps
 }
 
@@ -491,6 +501,9 @@
 	unexportedSymbols := android.OptionalPathForModuleSrc(ctx, library.Properties.Unexported_symbols_list)
 	forceNotWeakSymbols := android.OptionalPathForModuleSrc(ctx, library.Properties.Force_symbols_not_weak_list)
 	forceWeakSymbols := android.OptionalPathForModuleSrc(ctx, library.Properties.Force_symbols_weak_list)
+	if ctx.useVndk() && library.Properties.Target.Vendor.Version_script != nil {
+		versionScript = android.OptionalPathForModuleSrc(ctx, library.Properties.Target.Vendor.Version_script)
+	}
 	if !ctx.Darwin() {
 		if versionScript.Valid() {
 			flags.LdFlags = append(flags.LdFlags, "-Wl,--version-script,"+versionScript.String())
diff --git a/cc/linker.go b/cc/linker.go
index 6ec5630..1cf3f61 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -89,6 +89,10 @@
 			// list of shared libs that should not be used to build
 			// the vendor variant of the C/C++ module.
 			Exclude_shared_libs []string
+
+			// list of static libs that should not be used to build
+			// the vendor variant of the C/C++ module.
+			Exclude_static_libs []string
 		}
 	}
 }
@@ -135,6 +139,9 @@
 	if ctx.useVndk() {
 		deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Vendor.Exclude_shared_libs)
 		deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Vendor.Exclude_shared_libs)
+		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs)
+		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor.Exclude_static_libs)
+		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs)
 	}
 
 	if ctx.ModuleName() != "libcompiler_rt-extras" {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index b8b5ffa..74f4bdb 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -342,6 +342,10 @@
 		diagSanitizers = append(diagSanitizers, "address")
 	}
 
+	if Bool(sanitize.Properties.Sanitize.Thread) {
+		sanitizers = append(sanitizers, "thread")
+	}
+
 	if Bool(sanitize.Properties.Sanitize.Coverage) {
 		flags.CFlags = append(flags.CFlags, "-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp")
 	}
@@ -408,6 +412,8 @@
 	runtimeLibrary := ""
 	if Bool(sanitize.Properties.Sanitize.Address) {
 		runtimeLibrary = config.AddressSanitizerRuntimeLibrary(ctx.toolchain())
+	} else if Bool(sanitize.Properties.Sanitize.Thread) {
+		runtimeLibrary = config.ThreadSanitizerRuntimeLibrary(ctx.toolchain())
 	} else if len(diagSanitizers) > 0 {
 		runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(ctx.toolchain())
 	}
diff --git a/java/androidmk.go b/java/androidmk.go
index e349de4..97924f3 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -36,6 +36,9 @@
 				}
 				if library.dexJarFile != nil {
 					fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String())
+					if library.deviceProperties.Dex_preopt == nil || *library.deviceProperties.Dex_preopt == false {
+						fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false")
+					}
 				}
 				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", library.deviceProperties.Sdk_version)
 			},
diff --git a/java/builder.go b/java/builder.go
index 9086d51..4aaf841 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -160,7 +160,7 @@
 	srcFiles android.Paths, srcJars classpath,
 	flags javaBuilderFlags) {
 
-	classDir := android.PathForModuleOut(ctx, "classes-kt")
+	classDir := android.PathForModuleOut(ctx, "kotlinc", "classes")
 
 	inputs := append(android.Paths(nil), srcFiles...)
 	inputs = append(inputs, srcJars...)
@@ -184,7 +184,7 @@
 	flags javaBuilderFlags, deps android.Paths) {
 
 	transformJavaToClasses(ctx, outputFile, srcFiles, srcJars, flags, deps,
-		"", "javac", javac)
+		"javac", "javac", javac)
 }
 
 func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath,
@@ -196,7 +196,7 @@
 	}
 
 	transformJavaToClasses(ctx, outputFile, srcFiles, srcJars, flags, nil,
-		"-errorprone", "errorprone", errorprone)
+		"errorprone", "errorprone", errorprone)
 }
 
 // transformJavaToClasses takes source files and converts them to a jar containing .class files.
@@ -211,7 +211,7 @@
 func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
 	srcFiles android.Paths, srcJars classpath,
 	flags javaBuilderFlags, deps android.Paths,
-	intermediatesSuffix, desc string, rule blueprint.Rule) {
+	intermediatesDir, desc string, rule blueprint.Rule) {
 
 	deps = append(deps, srcJars...)
 
@@ -237,8 +237,8 @@
 			"bootClasspath": bootClasspath,
 			"sourcepath":    srcJars.JavaSourcepath(),
 			"classpath":     flags.classpath.JavaClasspath(),
-			"outDir":        android.PathForModuleOut(ctx, "classes"+intermediatesSuffix).String(),
-			"annoDir":       android.PathForModuleOut(ctx, "anno"+intermediatesSuffix).String(),
+			"outDir":        android.PathForModuleOut(ctx, intermediatesDir, "classes").String(),
+			"annoDir":       android.PathForModuleOut(ctx, intermediatesDir, "anno").String(),
 			"javaVersion":   flags.javaVersion,
 		},
 	})
@@ -288,7 +288,7 @@
 func TransformDesugar(ctx android.ModuleContext, outputFile android.WritablePath,
 	classesJar android.Path, flags javaBuilderFlags) {
 
-	dumpDir := android.PathForModuleOut(ctx, "desugar_dumped_classes")
+	dumpDir := android.PathForModuleOut(ctx, "desugar", "classes")
 
 	javaFlags := ""
 	if ctx.AConfig().UseOpenJDK9() {
diff --git a/java/java.go b/java/java.go
index 76e574e..6973915 100644
--- a/java/java.go
+++ b/java/java.go
@@ -143,6 +143,10 @@
 	// If true, export a copy of the module as a -hostdex module for host testing.
 	Hostdex *bool
 
+	// If false, prevent dexpreopting and stripping the dex file from the final jar.  Defaults to
+	// true.
+	Dex_preopt *bool
+
 	// When targeting 1.9, override the modules to use with --system
 	System_modules *string
 }
@@ -502,6 +506,8 @@
 
 	var jars android.Paths
 
+	jarName := ctx.ModuleName() + ".jar"
+
 	if srcFiles.HasExt(".kt") {
 		// If there are kotlin files, compile them first but pass all the kotlin and java files
 		// kotlinc will use the java files to resolve types referenced by the kotlin files, but
@@ -515,7 +521,7 @@
 		flags.kotlincClasspath = append(flags.kotlincClasspath, deps.kotlinStdlib...)
 		flags.kotlincClasspath = append(flags.kotlincClasspath, deps.classpath...)
 
-		kotlinJar := android.PathForModuleOut(ctx, "classes-kt.jar")
+		kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
 		TransformKotlinToClasses(ctx, kotlinJar, srcFiles, srcJars, flags)
 		if ctx.Failed() {
 			return
@@ -536,13 +542,13 @@
 			// a rebuild when error-prone is turned off).
 			// TODO(ccross): Once we always compile with javac9 we may be able to conditionally
 			//    enable error-prone without affecting the output class files.
-			errorprone := android.PathForModuleOut(ctx, "classes-errorprone.list")
+			errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
 			RunErrorProne(ctx, errorprone, javaSrcFiles, srcJars, flags)
 			extraJarDeps = append(extraJarDeps, errorprone)
 		}
 
 		// Compile java sources into .class files
-		classes := android.PathForModuleOut(ctx, "classes-compiled.jar")
+		classes := android.PathForModuleOut(ctx, "javac", jarName)
 		TransformJavaToClasses(ctx, classes, javaSrcFiles, srcJars, flags, extraJarDeps)
 		if ctx.Failed() {
 			return
@@ -570,7 +576,7 @@
 	}
 
 	if len(resArgs) > 0 {
-		resourceJar := android.PathForModuleOut(ctx, "res.jar")
+		resourceJar := android.PathForModuleOut(ctx, "res", jarName)
 		TransformResourcesToJar(ctx, resourceJar, resArgs, resDeps)
 		if ctx.Failed() {
 			return
@@ -592,7 +598,7 @@
 		// Optimization: skip the combine step if there is nothing to do
 		outputFile = jars[0]
 	} else {
-		combinedJar := android.PathForModuleOut(ctx, "classes.jar")
+		combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
 		TransformJarsToJar(ctx, combinedJar, jars, manifest, false)
 		outputFile = combinedJar
 	}
@@ -600,7 +606,7 @@
 	if j.properties.Jarjar_rules != nil {
 		jarjar_rules := android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
 		// Transform classes.jar into classes-jarjar.jar
-		jarjarFile := android.PathForModuleOut(ctx, "classes-jarjar.jar")
+		jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName)
 		TransformJarJar(ctx, jarjarFile, outputFile, jarjar_rules)
 		outputFile = jarjarFile
 		if ctx.Failed() {
@@ -658,17 +664,17 @@
 
 		flags.desugarFlags = strings.Join(desugarFlags, " ")
 
-		desugarJar := android.PathForModuleOut(ctx, "classes-desugar.jar")
+		desugarJar := android.PathForModuleOut(ctx, "desugar", jarName)
 		TransformDesugar(ctx, desugarJar, outputFile, flags)
 		outputFile = desugarJar
 		if ctx.Failed() {
 			return
 		}
 
-		// Compile classes.jar into classes.dex and then javalib.jar
-		javalibJar := android.PathForModuleOut(ctx, "javalib.jar")
-		TransformClassesJarToDexJar(ctx, javalibJar, desugarJar, flags)
-		outputFile = javalibJar
+		// Compile classes.jar into classes.dex and then a dex jar
+		dexJar := android.PathForModuleOut(ctx, "dex", jarName)
+		TransformClassesJarToDexJar(ctx, dexJar, desugarJar, flags)
+		outputFile = dexJar
 		if ctx.Failed() {
 			return
 		}
diff --git a/java/java_test.go b/java/java_test.go
index d64688f..6dde938 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -142,7 +142,7 @@
 	case strings.HasSuffix(name, ".jar"):
 		return name
 	default:
-		return filepath.Join(buildDir, ".intermediates", name, "android_common", "classes-compiled.jar")
+		return filepath.Join(buildDir, ".intermediates", name, "android_common", "javac", name+".jar")
 	}
 }
 
@@ -173,8 +173,8 @@
 		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
 	}
 
-	bar := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "classes-compiled.jar")
-	baz := filepath.Join(buildDir, ".intermediates", "baz", "android_common", "classes-compiled.jar")
+	bar := ctx.ModuleForTests("bar", "android_common").Rule("javac").Output.String()
+	baz := ctx.ModuleForTests("baz", "android_common").Rule("javac").Output.String()
 
 	if !strings.Contains(javac.Args["classpath"], bar) {
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], bar)
@@ -465,12 +465,12 @@
 		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
 	}
 
-	bar := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "classes-compiled.jar")
+	bar := ctx.ModuleForTests("bar", "android_common").Rule("javac").Output.String()
 	if !strings.Contains(javac.Args["classpath"], bar) {
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], bar)
 	}
 
-	baz := filepath.Join(buildDir, ".intermediates", "baz", "android_common", "classes-compiled.jar")
+	baz := ctx.ModuleForTests("baz", "android_common").Rule("javac").Output.String()
 	if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
 		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
 	}
@@ -530,8 +530,8 @@
 				}
 			`+test.extra)
 
-			foo := ctx.ModuleForTests("foo", "android_common").Output("classes.jar")
-			fooRes := ctx.ModuleForTests("foo", "android_common").Output("res.jar")
+			foo := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
+			fooRes := ctx.ModuleForTests("foo", "android_common").Output("res/foo.jar")
 
 			if !inList(fooRes.Output.String(), foo.Inputs.Strings()) {
 				t.Errorf("foo combined jars %v does not contain %q",
@@ -563,7 +563,7 @@
 		}
 	`)
 
-	fooRes := ctx.ModuleForTests("foo", "android_common").Output("res.jar")
+	fooRes := ctx.ModuleForTests("foo", "android_common").Output("res/foo.jar")
 
 	expected := "-C res -f res/a -f res/b"
 	if fooRes.Args["jarArgs"] != expected {
@@ -572,7 +572,7 @@
 
 	}
 
-	barRes := ctx.ModuleForTests("bar", "android_common").Output("res.jar")
+	barRes := ctx.ModuleForTests("bar", "android_common").Output("res/bar.jar")
 
 	expected = "-C . -f res/a"
 	if barRes.Args["jarArgs"] != expected {
@@ -625,7 +625,7 @@
 
 	kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
 	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	jar := ctx.ModuleForTests("foo", "android_common").Output("classes.jar")
+	jar := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
 
 	if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" ||
 		kotlinc.Inputs[1].String() != "b.kt" {
diff --git a/ui/logger/logger.go b/ui/logger/logger.go
index 7d9687b..15c413d 100644
--- a/ui/logger/logger.go
+++ b/ui/logger/logger.go
@@ -38,6 +38,7 @@
 	"path/filepath"
 	"strconv"
 	"sync"
+	"syscall"
 )
 
 type Logger interface {
@@ -93,10 +94,19 @@
 // existing files to <filename>.#.<ext>, keeping up to maxCount files.
 // <filename>.1.<ext> is the most recent backup, <filename>.2.<ext> is the
 // second most recent backup, etc.
-//
-// TODO: This function is not guaranteed to be atomic, if there are multiple
-// users attempting to do the same operation, the result is undefined.
 func CreateFileWithRotation(filename string, maxCount int) (*os.File, error) {
+	lockFileName := filepath.Join(filepath.Dir(filename), ".lock_"+filepath.Base(filename))
+	lockFile, err := os.OpenFile(lockFileName, os.O_RDWR|os.O_CREATE, 0666)
+	if err != nil {
+		return nil, err
+	}
+	defer lockFile.Close()
+
+	err = syscall.Flock(int(lockFile.Fd()), syscall.LOCK_EX)
+	if err != nil {
+		return nil, err
+	}
+
 	if _, err := os.Lstat(filename); err == nil {
 		ext := filepath.Ext(filename)
 		basename := filename[:len(filename)-len(ext)]
diff --git a/ui/logger/logger_test.go b/ui/logger/logger_test.go
index 0f88ab3..dc6f2e9 100644
--- a/ui/logger/logger_test.go
+++ b/ui/logger/logger_test.go
@@ -67,7 +67,7 @@
 		t.Fatalf("Failed to read dir: %v", err)
 	}
 	sort.Strings(names)
-	expected := []string{"build.1.log", "build.2.log", "build.3.log", "build.log"}
+	expected := []string{".lock_build.log", "build.1.log", "build.2.log", "build.3.log", "build.log"}
 	if !reflect.DeepEqual(names, expected) {
 		t.Errorf("File list does not match.")
 		t.Errorf("     got: %v", names)
diff --git a/ui/tracer/microfactory.go b/ui/tracer/microfactory.go
index 320d9d8..acb9be4 100644
--- a/ui/tracer/microfactory.go
+++ b/ui/tracer/microfactory.go
@@ -28,7 +28,7 @@
 
 	f, err := os.Open(filename)
 	if err != nil {
-		t.log.Println("Error opening microfactory trace:", err)
+		t.log.Verboseln("Error opening microfactory trace:", err)
 		return
 	}
 	defer f.Close()
diff --git a/cmd/soong_zip/Android.bp b/zip/Android.bp
similarity index 85%
copy from cmd/soong_zip/Android.bp
copy to zip/Android.bp
index 3e69336..d708089 100644
--- a/cmd/soong_zip/Android.bp
+++ b/zip/Android.bp
@@ -12,14 +12,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-blueprint_go_binary {
-    name: "soong_zip",
+subdirs = ["cmd"]
+
+bootstrap_go_package {
+    name: "soong-zip",
+    pkgPath: "android/soong/zip",
     deps: [
         "android-archive-zip",
         "soong-jar",
     ],
     srcs: [
-        "soong_zip.go",
+        "zip.go",
         "rate_limit.go",
     ],
 }
+
diff --git a/cmd/soong_zip/Android.bp b/zip/cmd/Android.bp
similarity index 86%
rename from cmd/soong_zip/Android.bp
rename to zip/cmd/Android.bp
index 3e69336..6029a69 100644
--- a/cmd/soong_zip/Android.bp
+++ b/zip/cmd/Android.bp
@@ -15,11 +15,9 @@
 blueprint_go_binary {
     name: "soong_zip",
     deps: [
-        "android-archive-zip",
-        "soong-jar",
+        "soong-zip",
     ],
     srcs: [
-        "soong_zip.go",
-        "rate_limit.go",
+        "main.go",
     ],
 }
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
new file mode 100644
index 0000000..348728c
--- /dev/null
+++ b/zip/cmd/main.go
@@ -0,0 +1,171 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+
+	"android/soong/zip"
+)
+
+type byteReaderCloser struct {
+	*bytes.Reader
+	io.Closer
+}
+
+type pathMapping struct {
+	dest, src string
+	zipMethod uint16
+}
+
+type uniqueSet map[string]bool
+
+func (u *uniqueSet) String() string {
+	return `""`
+}
+
+func (u *uniqueSet) Set(s string) error {
+	if _, found := (*u)[s]; found {
+		return fmt.Errorf("File %q was specified twice as a file to not deflate", s)
+	} else {
+		(*u)[s] = true
+	}
+
+	return nil
+}
+
+type file struct{}
+
+type listFiles struct{}
+
+type dir struct{}
+
+func (f *file) String() string {
+	return `""`
+}
+
+func (f *file) Set(s string) error {
+	if *relativeRoot == "" {
+		return fmt.Errorf("must pass -C before -f")
+	}
+
+	fArgs = append(fArgs, zip.FileArg{
+		PathPrefixInZip:     filepath.Clean(*rootPrefix),
+		SourcePrefixToStrip: filepath.Clean(*relativeRoot),
+		SourceFiles:         []string{s},
+	})
+
+	return nil
+}
+
+func (l *listFiles) String() string {
+	return `""`
+}
+
+func (l *listFiles) Set(s string) error {
+	if *relativeRoot == "" {
+		return fmt.Errorf("must pass -C before -l")
+	}
+
+	list, err := ioutil.ReadFile(s)
+	if err != nil {
+		return err
+	}
+
+	fArgs = append(fArgs, zip.FileArg{
+		PathPrefixInZip:     filepath.Clean(*rootPrefix),
+		SourcePrefixToStrip: filepath.Clean(*relativeRoot),
+		SourceFiles:         strings.Split(string(list), "\n"),
+	})
+
+	return nil
+}
+
+func (d *dir) String() string {
+	return `""`
+}
+
+func (d *dir) Set(s string) error {
+	if *relativeRoot == "" {
+		return fmt.Errorf("must pass -C before -D")
+	}
+
+	fArgs = append(fArgs, zip.FileArg{
+		PathPrefixInZip:     filepath.Clean(*rootPrefix),
+		SourcePrefixToStrip: filepath.Clean(*relativeRoot),
+		GlobDir:             filepath.Clean(s),
+	})
+
+	return nil
+}
+
+var (
+	out          = flag.String("o", "", "file to write zip file to")
+	manifest     = flag.String("m", "", "input jar manifest file name")
+	directories  = flag.Bool("d", false, "include directories in zip")
+	rootPrefix   = flag.String("P", "", "path prefix within the zip at which to place files")
+	relativeRoot = flag.String("C", "", "path to use as relative root of files in following -f, -l, or -D arguments")
+	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            zip.FileArgs
+	nonDeflatedFiles = make(uniqueSet)
+
+	cpuProfile = flag.String("cpuprofile", "", "write cpu profile to file")
+	traceFile  = flag.String("trace", "", "write trace to file")
+)
+
+func init() {
+	flag.Var(&listFiles{}, "l", "file containing list of .class files")
+	flag.Var(&dir{}, "D", "directory to include in zip")
+	flag.Var(&file{}, "f", "file to include in zip")
+	flag.Var(&nonDeflatedFiles, "s", "file path to be stored within the zip without compression")
+}
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "usage: zip -o zipfile [-m manifest] -C dir [-f|-l file]...\n")
+	flag.PrintDefaults()
+	os.Exit(2)
+}
+
+func main() {
+	flag.Parse()
+
+	err := zip.Run(zip.ZipArgs{
+		FileArgs:                 fArgs,
+		OutputFilePath:           *out,
+		CpuProfileFilePath:       *cpuProfile,
+		TraceFilePath:            *traceFile,
+		EmulateJar:               *emulateJar,
+		AddDirectoryEntriesToZip: *directories,
+		CompressionLevel:         *compLevel,
+		ManifestSourcePath:       *manifest,
+		NumParallelJobs:          *parallelJobs,
+		NonDeflatedFiles:         nonDeflatedFiles,
+	})
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err.Error())
+		os.Exit(1)
+	}
+}
diff --git a/cmd/soong_zip/rate_limit.go b/zip/rate_limit.go
similarity index 99%
rename from cmd/soong_zip/rate_limit.go
rename to zip/rate_limit.go
index 9cb5fdd..0ea2ef0 100644
--- a/cmd/soong_zip/rate_limit.go
+++ b/zip/rate_limit.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package zip
 
 import (
 	"fmt"
diff --git a/cmd/soong_zip/soong_zip.go b/zip/zip.go
similarity index 84%
rename from cmd/soong_zip/soong_zip.go
rename to zip/zip.go
index 2bcc9a5..95520fe 100644
--- a/cmd/soong_zip/soong_zip.go
+++ b/zip/zip.go
@@ -12,13 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package zip
 
 import (
 	"bytes"
 	"compress/flate"
 	"errors"
-	"flag"
 	"fmt"
 	"hash/crc32"
 	"io"
@@ -26,7 +25,6 @@
 	"log"
 	"os"
 	"path/filepath"
-	"runtime"
 	"runtime/pprof"
 	"runtime/trace"
 	"sort"
@@ -83,122 +81,6 @@
 	return nil
 }
 
-type file struct{}
-
-type listFiles struct{}
-
-type dir struct{}
-
-func (f *file) String() string {
-	return `""`
-}
-
-func (f *file) Set(s string) error {
-	if *relativeRoot == "" {
-		return fmt.Errorf("must pass -C before -f")
-	}
-
-	fArgs = append(fArgs, FileArg{
-		PathPrefixInZip:     filepath.Clean(*rootPrefix),
-		SourcePrefixToStrip: filepath.Clean(*relativeRoot),
-		SourceFiles:         []string{s},
-	})
-
-	return nil
-}
-
-func (l *listFiles) String() string {
-	return `""`
-}
-
-func (l *listFiles) Set(s string) error {
-	if *relativeRoot == "" {
-		return fmt.Errorf("must pass -C before -l")
-	}
-
-	list, err := ioutil.ReadFile(s)
-	if err != nil {
-		return err
-	}
-
-	fArgs = append(fArgs, FileArg{
-		PathPrefixInZip:     filepath.Clean(*rootPrefix),
-		SourcePrefixToStrip: filepath.Clean(*relativeRoot),
-		SourceFiles:         strings.Split(string(list), "\n"),
-	})
-
-	return nil
-}
-
-func (d *dir) String() string {
-	return `""`
-}
-
-func (d *dir) Set(s string) error {
-	if *relativeRoot == "" {
-		return fmt.Errorf("must pass -C before -D")
-	}
-
-	fArgs = append(fArgs, FileArg{
-		PathPrefixInZip:     filepath.Clean(*rootPrefix),
-		SourcePrefixToStrip: filepath.Clean(*relativeRoot),
-		GlobDir:             filepath.Clean(s),
-	})
-
-	return nil
-}
-
-var (
-	out          = flag.String("o", "", "file to write zip file to")
-	manifest     = flag.String("m", "", "input jar manifest file name")
-	directories  = flag.Bool("d", false, "include directories in zip")
-	rootPrefix   = flag.String("P", "", "path prefix within the zip at which to place files")
-	relativeRoot = flag.String("C", "", "path to use as relative root of files in following -f, -l, or -D arguments")
-	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)
-
-	cpuProfile = flag.String("cpuprofile", "", "write cpu profile to file")
-	traceFile  = flag.String("trace", "", "write trace to file")
-)
-
-func init() {
-	flag.Var(&listFiles{}, "l", "file containing list of .class files")
-	flag.Var(&dir{}, "D", "directory to include in zip")
-	flag.Var(&file{}, "f", "file to include in zip")
-	flag.Var(&nonDeflatedFiles, "s", "file path to be stored within the zip without compression")
-}
-
-func usage() {
-	fmt.Fprintf(os.Stderr, "usage: soong_zip -o zipfile [-m manifest] -C dir [-f|-l file]...\n")
-	flag.PrintDefaults()
-	os.Exit(2)
-}
-
-func main() {
-	flag.Parse()
-
-	err := Run(ZipArgs{
-		FileArgs:                 fArgs,
-		OutputFilePath:           *out,
-		CpuProfileFilePath:       *cpuProfile,
-		TraceFilePath:            *traceFile,
-		EmulateJar:               *emulateJar,
-		AddDirectoryEntriesToZip: *directories,
-		CompressionLevel:         *compLevel,
-		ManifestSourcePath:       *manifest,
-		NumParallelJobs:          *parallelJobs,
-		NonDeflatedFiles:         nonDeflatedFiles,
-	})
-	if err != nil {
-		fmt.Fprintln(os.Stderr, err.Error())
-		os.Exit(1)
-	}
-}
-
 type FileArg struct {
 	PathPrefixInZip, SourcePrefixToStrip string
 	SourceFiles                          []string