Merge "Fix NDK gtest name."
diff --git a/android/config.go b/android/config.go
index b5ec975..1e5dd52 100644
--- a/android/config.go
+++ b/android/config.go
@@ -710,6 +710,10 @@
 	return coverage
 }
 
+func (c *deviceConfig) PgoAdditionalProfileDirs() []string {
+	return c.config.ProductVariables.PgoAdditionalProfileDirs
+}
+
 func (c *config) IntegerOverflowDisabledForPath(path string) bool {
 	if c.ProductVariables.IntegerOverflowExcludePaths == nil {
 		return false
diff --git a/android/module.go b/android/module.go
index 9de5294..6f247ab 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1176,15 +1176,24 @@
 func (ctx *androidModuleContext) ExpandSourcesSubDir(srcFiles, excludes []string, subDir string) Paths {
 	prefix := PathForModuleSrc(ctx).String()
 
-	for i, e := range excludes {
-		j := findStringInSlice(e, srcFiles)
-		if j != -1 {
-			srcFiles = append(srcFiles[:j], srcFiles[j+1:]...)
+	expandedExcludes := make([]string, 0, len(excludes))
+
+	for _, e := range excludes {
+		if m := SrcIsModule(e); m != "" {
+			module := ctx.GetDirectDepWithTag(m, SourceDepTag)
+			if module == nil {
+				// Error will have been handled by ExtractSourcesDeps
+				continue
+			}
+			if srcProducer, ok := module.(SourceFileProducer); ok {
+				expandedExcludes = append(expandedExcludes, srcProducer.Srcs().Strings()...)
+			} else {
+				ctx.ModuleErrorf("srcs dependency %q is not a source file producing module", m)
+			}
+		} else {
+			expandedExcludes = append(expandedExcludes, filepath.Join(prefix, e))
 		}
-
-		excludes[i] = filepath.Join(prefix, e)
 	}
-
 	expandedSrcFiles := make(Paths, 0, len(srcFiles))
 	for _, s := range srcFiles {
 		if m := SrcIsModule(s); m != "" {
@@ -1194,22 +1203,33 @@
 				continue
 			}
 			if srcProducer, ok := module.(SourceFileProducer); ok {
-				expandedSrcFiles = append(expandedSrcFiles, srcProducer.Srcs()...)
+				moduleSrcs := srcProducer.Srcs()
+				for _, e := range expandedExcludes {
+					for j, ms := range moduleSrcs {
+						if ms.String() == e {
+							moduleSrcs = append(moduleSrcs[:j], moduleSrcs[j+1:]...)
+						}
+					}
+				}
+				expandedSrcFiles = append(expandedSrcFiles, moduleSrcs...)
 			} else {
 				ctx.ModuleErrorf("srcs dependency %q is not a source file producing module", m)
 			}
 		} else if pathtools.IsGlob(s) {
-			globbedSrcFiles := ctx.Glob(filepath.Join(prefix, s), excludes)
+			globbedSrcFiles := ctx.Glob(filepath.Join(prefix, s), expandedExcludes)
 			for i, s := range globbedSrcFiles {
 				globbedSrcFiles[i] = s.(ModuleSrcPath).WithSubDir(ctx, subDir)
 			}
 			expandedSrcFiles = append(expandedSrcFiles, globbedSrcFiles...)
 		} else {
-			s := PathForModuleSrc(ctx, s).WithSubDir(ctx, subDir)
-			expandedSrcFiles = append(expandedSrcFiles, s)
+			p := PathForModuleSrc(ctx, s).WithSubDir(ctx, subDir)
+			j := findStringInSlice(p.String(), expandedExcludes)
+			if j == -1 {
+				expandedSrcFiles = append(expandedSrcFiles, p)
+			}
+
 		}
 	}
-
 	return expandedSrcFiles
 }
 
diff --git a/android/variable.go b/android/variable.go
index 2c2a0cf..328074a 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -205,6 +205,8 @@
 	ExtraVndkVersions []string `json:",omitempty"`
 
 	NamespacesToExport []string `json:",omitempty"`
+
+	PgoAdditionalProfileDirs []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/cc/builder.go b/cc/builder.go
index 0646132..8f253ca 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -172,7 +172,7 @@
 		},
 		"windresCmd", "flags")
 
-	_ = pctx.SourcePathVariable("sAbiDumper", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/header-abi-dumper")
+	_ = pctx.SourcePathVariable("sAbiDumper", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-dumper")
 
 	// -w has been added since header-abi-dumper does not need to produce any sort of diagnostic information.
 	sAbiDump = pctx.AndroidStaticRule("sAbiDump",
@@ -182,7 +182,7 @@
 		},
 		"cFlags", "exportDirs")
 
-	_ = pctx.SourcePathVariable("sAbiLinker", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/header-abi-linker")
+	_ = pctx.SourcePathVariable("sAbiLinker", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-linker")
 
 	sAbiLink = pctx.AndroidStaticRule("sAbiLink",
 		blueprint.RuleParams{
@@ -193,7 +193,7 @@
 		},
 		"symbolFilter", "arch", "exportedHeaderFlags")
 
-	_ = pctx.SourcePathVariable("sAbiDiffer", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/header-abi-diff")
+	_ = pctx.SourcePathVariable("sAbiDiffer", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-diff")
 
 	sAbiDiff = pctx.AndroidRuleFunc("sAbiDiff",
 		func(config android.Config) (blueprint.RuleParams, error) {
@@ -256,6 +256,7 @@
 	systemIncludeFlags string
 
 	groupStaticLibs bool
+	arGoldPlugin    bool
 
 	stripKeepSymbols       bool
 	stripKeepMiniDebugInfo bool
@@ -512,6 +513,9 @@
 	if !ctx.Darwin() {
 		arFlags += " -format=gnu"
 	}
+	if flags.arGoldPlugin {
+		arFlags += " --plugin ${config.LLVMGoldPlugin}"
+	}
 	if flags.arFlags != "" {
 		arFlags += " " + flags.arFlags
 	}
diff --git a/cc/cc.go b/cc/cc.go
index 9cc7dfa..f65441f 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -142,6 +142,7 @@
 	LdFlagsDeps android.Paths // Files depended on by linker flags
 
 	GroupStaticLibs bool
+	ArGoldPlugin    bool // Whether LLVM gold plugin option is passed to llvm-ar
 }
 
 type ObjectLinkerProperties struct {
@@ -210,6 +211,7 @@
 	selectedStl() string
 	baseModuleName() string
 	getVndkExtendsModuleName() string
+	isPgoCompile() bool
 }
 
 type ModuleContext interface {
@@ -407,6 +409,13 @@
 	return false
 }
 
+func (c *Module) isPgoCompile() bool {
+	if pgo := c.pgo; pgo != nil {
+		return pgo.Properties.PgoCompile
+	}
+	return false
+}
+
 func (c *Module) isVndkSp() bool {
 	if vndkdep := c.vndkdep; vndkdep != nil {
 		return vndkdep.isVndkSp()
@@ -506,6 +515,10 @@
 	return ctx.mod.isVndk()
 }
 
+func (ctx *moduleContextImpl) isPgoCompile() bool {
+	return ctx.mod.isPgoCompile()
+}
+
 func (ctx *moduleContextImpl) isVndkSp() bool {
 	return ctx.mod.isVndkSp()
 }
diff --git a/cc/compiler.go b/cc/compiler.go
index 0d8e3a1..ef22285 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -202,6 +202,7 @@
 	deps.GeneratedHeaders = append(deps.GeneratedHeaders, compiler.Properties.Generated_headers...)
 
 	android.ExtractSourcesDeps(ctx, compiler.Properties.Srcs)
+	android.ExtractSourcesDeps(ctx, compiler.Properties.Exclude_srcs)
 
 	if compiler.hasSrcExt(".proto") {
 		deps = protoDeps(ctx, deps, &compiler.Proto, Bool(compiler.Properties.Proto.Static))
@@ -396,45 +397,42 @@
 		flags.GlobalFlags = append(flags.GlobalFlags, tc.ToolchainCflags())
 	}
 
-	if !ctx.useSdk() {
-		cStd := config.CStdVersion
-		if String(compiler.Properties.C_std) == "experimental" {
-			cStd = config.ExperimentalCStdVersion
-		} else if String(compiler.Properties.C_std) != "" {
-			cStd = String(compiler.Properties.C_std)
-		}
-
-		cppStd := String(compiler.Properties.Cpp_std)
-		switch String(compiler.Properties.Cpp_std) {
-		case "":
-			cppStd = config.CppStdVersion
-		case "experimental":
-			cppStd = config.ExperimentalCppStdVersion
-		case "c++17", "gnu++17":
-			// Map c++17 and gnu++17 to their 1z equivalents, until 17 is finalized.
-			cppStd = strings.Replace(String(compiler.Properties.Cpp_std), "17", "1z", 1)
-		}
-
-		if !flags.Clang {
-			// GCC uses an invalid C++14 ABI (emits calls to
-			// __cxa_throw_bad_array_length, which is not a valid C++ RT ABI).
-			// http://b/25022512
-			cppStd = config.GccCppStdVersion
-		} else if ctx.Host() && !flags.Clang {
-			// The host GCC doesn't support C++14 (and is deprecated, so likely
-			// never will). Build these modules with C++11.
-			cppStd = config.GccCppStdVersion
-		}
-
-		if compiler.Properties.Gnu_extensions != nil && *compiler.Properties.Gnu_extensions == false {
-			cStd = gnuToCReplacer.Replace(cStd)
-			cppStd = gnuToCReplacer.Replace(cppStd)
-		}
-
-		flags.ConlyFlags = append([]string{"-std=" + cStd}, flags.ConlyFlags...)
-		flags.CppFlags = append([]string{"-std=" + cppStd}, flags.CppFlags...)
+	cStd := config.CStdVersion
+	if String(compiler.Properties.C_std) == "experimental" {
+		cStd = config.ExperimentalCStdVersion
+	} else if String(compiler.Properties.C_std) != "" {
+		cStd = String(compiler.Properties.C_std)
 	}
 
+	cppStd := String(compiler.Properties.Cpp_std)
+	switch String(compiler.Properties.Cpp_std) {
+	case "":
+		cppStd = config.CppStdVersion
+	case "experimental":
+		cppStd = config.ExperimentalCppStdVersion
+	case "c++17", "gnu++17":
+		// Map c++17 and gnu++17 to their 1z equivalents, until 17 is finalized.
+		cppStd = strings.Replace(String(compiler.Properties.Cpp_std), "17", "1z", 1)
+	}
+
+	if !flags.Clang {
+		// GCC uses an invalid C++14 ABI (emits calls to
+		// __cxa_throw_bad_array_length, which is not a valid C++ RT ABI).
+		// http://b/25022512
+		// The host GCC doesn't support C++14 (and is deprecated, so likely
+		// never will).
+		// Build these modules with C++11.
+		cppStd = config.GccCppStdVersion
+	}
+
+	if compiler.Properties.Gnu_extensions != nil && *compiler.Properties.Gnu_extensions == false {
+		cStd = gnuToCReplacer.Replace(cStd)
+		cppStd = gnuToCReplacer.Replace(cppStd)
+	}
+
+	flags.ConlyFlags = append([]string{"-std=" + cStd}, flags.ConlyFlags...)
+	flags.CppFlags = append([]string{"-std=" + cppStd}, flags.CppFlags...)
+
 	if ctx.useVndk() {
 		flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
 	}
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 796ccfb..471db1d 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -199,20 +199,6 @@
 	return list
 }
 
-func indexList(s string, list []string) int {
-	for i, l := range list {
-		if l == s {
-			return i
-		}
-	}
-
-	return -1
-}
-
-func inList(s string, list []string) bool {
-	return indexList(s, list) != -1
-}
-
 func SanitizerRuntimeLibrary(t Toolchain, sanitizer string) string {
 	arch := t.SanitizerRuntimeLibraryArch()
 	if arch == "" {
@@ -243,3 +229,5 @@
 	}
 	return filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
 }
+
+var inList = android.InList
diff --git a/cc/lto.go b/cc/lto.go
index 91b11b5..2ced124 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -86,7 +86,14 @@
 			// https://github.com/android-ndk/ndk/issues/498.
 			flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-emulated-tls")
 		}
-		flags.ArFlags = append(flags.ArFlags, " --plugin ${config.LLVMGoldPlugin}")
+		flags.ArGoldPlugin = true
+
+		// If the module does not have a profile, be conservative and do not inline
+		// or unroll loops during LTO, in order to prevent significant size bloat.
+		if !ctx.isPgoCompile() {
+			flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-inline-threshold=0")
+			flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-unroll-threshold=0")
+		}
 	}
 	return flags
 }
diff --git a/cc/pgo.go b/cc/pgo.go
index 3ce67be..779ef39 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -27,18 +27,25 @@
 	// some functions
 	profileUseOtherFlags = []string{"-Wno-backend-plugin"}
 
-	pgoProfileProjects = []string{
+	globalPgoProfileProjects = []string{
 		"toolchain/pgo-profiles",
 		"vendor/google_data/pgo-profiles",
 	}
 )
 
+const pgoProfileProjectsConfigKey = "PgoProfileProjects"
 const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp"
 const profileSamplingFlag = "-gline-tables-only"
 const profileUseInstrumentFormat = "-fprofile-use=%s"
 const profileUseSamplingFormat = "-fprofile-sample-use=%s"
 
-func recordMissingProfileFile(ctx ModuleContext, missing string) {
+func getPgoProfileProjects(config android.DeviceConfig) []string {
+	return config.OnceStringSlice(pgoProfileProjectsConfigKey, func() []string {
+		return append(globalPgoProfileProjects, config.PgoAdditionalProfileDirs()...)
+	})
+}
+
+func recordMissingProfileFile(ctx BaseModuleContext, missing string) {
 	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFile).Store(missing, true)
 }
 
@@ -56,6 +63,7 @@
 
 	PgoPresent          bool `blueprint:"mutated"`
 	ShouldProfileModule bool `blueprint:"mutated"`
+	PgoCompile          bool `blueprint:"mutated"`
 }
 
 type pgo struct {
@@ -91,9 +99,9 @@
 	return flags
 }
 
-func (props *PgoProperties) getPgoProfileFile(ctx ModuleContext) android.OptionalPath {
-	// Test if the profile_file is present in any of the pgoProfileProjects
-	for _, profileProject := range pgoProfileProjects {
+func (props *PgoProperties) getPgoProfileFile(ctx BaseModuleContext) android.OptionalPath {
+	// Test if the profile_file is present in any of the PGO profile projects
+	for _, profileProject := range getPgoProfileProjects(ctx.DeviceConfig()) {
 		path := android.ExistentPathForSource(ctx, "", profileProject, *props.Pgo.Profile_file)
 		if path.Valid() {
 			return path
@@ -225,6 +233,12 @@
 			}
 		}
 	}
+
+	if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") {
+		if profileFile := pgo.Properties.getPgoProfileFile(ctx); profileFile.Valid() {
+			pgo.Properties.PgoCompile = true
+		}
+	}
 }
 
 func (pgo *pgo) deps(ctx BaseModuleContext, deps Deps) Deps {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index ac6cb77..c47c319 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -38,7 +38,6 @@
 		"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blacklist.txt"}
 	cfiLdflags = []string{"-flto", "-fsanitize-cfi-cross-dso", "-fsanitize=cfi",
 		"-Wl,-plugin-opt,O1"}
-	cfiArflags         = []string{"--plugin ${config.ClangBin}/../lib64/LLVMgold.so"}
 	cfiExportsMapPath  = "build/soong/cc/config/cfi_exports.map"
 	cfiExportsMap      android.Path
 	cfiStaticLibsMutex sync.Mutex
@@ -407,7 +406,7 @@
 			// See b/72706604 or https://github.com/android-ndk/ndk/issues/498.
 			flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-emulated-tls")
 		}
-		flags.ArFlags = append(flags.ArFlags, cfiArflags...)
+		flags.ArGoldPlugin = true
 		if Bool(sanitize.Properties.Sanitize.Diag.Cfi) {
 			diagSanitizers = append(diagSanitizers, "cfi")
 		}
diff --git a/cc/stl.go b/cc/stl.go
index c5757cd..2da6471 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -176,8 +176,7 @@
 		ndkSrcRoot := android.PathForSource(ctx, "prebuilts/ndk/current/sources/cxx-stl/system/include")
 		flags.CFlags = append(flags.CFlags, "-isystem "+ndkSrcRoot.String())
 	case "ndk_libc++_shared", "ndk_libc++_static":
-		// TODO(danalbert): This really shouldn't be here...
-		flags.CppFlags = append(flags.CppFlags, "-std=c++11")
+		// Nothing.
 	case "":
 		// None or error.
 		if !ctx.toolchain().Bionic() {
diff --git a/cc/util.go b/cc/util.go
index 5131b09..92a32bc 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -84,6 +84,7 @@
 		systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
 
 		groupStaticLibs: in.GroupStaticLibs,
+		arGoldPlugin:    in.ArGoldPlugin,
 	}
 }
 
diff --git a/cc/vndk.go b/cc/vndk.go
index d417bea..a0d338b 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -178,7 +178,7 @@
 
 // gather list of vndk-core, vndk-sp, and ll-ndk libs
 func vndkMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok {
+	if m, ok := mctx.Module().(*Module); ok && m.Enabled() {
 		if lib, ok := m.linker.(*llndkStubDecorator); ok {
 			vndkLibrariesLock.Lock()
 			defer vndkLibrariesLock.Unlock()
diff --git a/cmd/pom2mk/pom2mk.go b/cmd/pom2mk/pom2mk.go
index 721e250..63bcbb6 100644
--- a/cmd/pom2mk/pom2mk.go
+++ b/cmd/pom2mk/pom2mk.go
@@ -98,8 +98,7 @@
 	ArtifactId string `xml:"artifactId"`
 	Version    string `xml:"version"`
 	Type       string `xml:"type"`
-
-	Scope string `xml:"scope"`
+	Scope      string `xml:"scope"`
 }
 
 func (d Dependency) MkName() string {
@@ -140,17 +139,19 @@
 }
 
 func (p Pom) MkJarDeps() []string {
-	return p.MkDeps("jar")
+	return p.MkDeps("jar", "compile")
 }
 
 func (p Pom) MkAarDeps() []string {
-	return p.MkDeps("aar")
+	return p.MkDeps("aar", "compile")
 }
 
-func (p Pom) MkDeps(typeExt string) []string {
+// MkDeps obtains dependencies filtered by type and scope. The results of this
+// method are formatted as Make targets, e.g. run through MavenToMk rules.
+func (p Pom) MkDeps(typeExt string, scope string) []string {
 	var ret []string
 	for _, d := range p.Dependencies {
-		if d.Type != typeExt {
+		if d.Type != typeExt || d.Scope != scope {
 			continue
 		}
 		name := rewriteNames.MavenToMk(d.GroupId, d.ArtifactId)
@@ -164,13 +165,22 @@
 	return sdkVersion
 }
 
-func (p *Pom) FixDepTypes(modules map[string]*Pom) {
+func (p *Pom) FixDeps(modules map[string]*Pom) {
 	for _, d := range p.Dependencies {
-		if d.Type != "" {
-			continue
+		if d.Type == "" {
+			if depPom, ok := modules[d.MkName()]; ok {
+				// We've seen the POM for this dependency, use its packaging
+				// as the dependency type rather than Maven spec default.
+				d.Type = depPom.Packaging
+			} else {
+				// Dependency type was not specified and we don't have the POM
+				// for this artifact, use the default from Maven spec.
+				d.Type = "jar"
+			}
 		}
-		if depPom, ok := modules[d.MkName()]; ok {
-			d.Type = depPom.Packaging
+		if d.Scope == "" {
+			// Scope was not specified, use the default from Maven spec.
+			d.Scope = "compile"
 		}
 	}
 }
@@ -360,7 +370,7 @@
 	}
 
 	for _, pom := range poms {
-		pom.FixDepTypes(modules)
+		pom.FixDeps(modules)
 	}
 
 	fmt.Println("# Automatically generated with:")
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 3b41c90..0af1886 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -136,14 +136,11 @@
 
 	tempDir, err := ioutil.TempDir(sandboxesRoot, "sbox")
 
-	// Rewrite output file paths to be relative to output root
-	// This facilitates matching them up against the corresponding paths in the temporary directory in case they're absolute
 	for i, filePath := range outputsVarEntries {
-		relativePath, err := filepath.Rel(outputRoot, filePath)
-		if err != nil {
-			return err
+		if !strings.HasPrefix(filePath, "__SBOX_OUT_DIR__/") {
+			return fmt.Errorf("output files must start with `__SBOX_OUT_DIR__/`")
 		}
-		outputsVarEntries[i] = relativePath
+		outputsVarEntries[i] = strings.TrimPrefix(filePath, "__SBOX_OUT_DIR__/")
 	}
 
 	allOutputs = append([]string(nil), outputsVarEntries...)
diff --git a/cmd/symbol_inject/elf.go b/cmd/symbol_inject/elf.go
index 1741a5b..12253f6 100644
--- a/cmd/symbol_inject/elf.go
+++ b/cmd/symbol_inject/elf.go
@@ -56,7 +56,7 @@
 	case elf.ET_REL:
 		// "In relocatable files, st_value holds a section offset for a defined symbol.
 		// That is, st_value is an offset from the beginning of the section that st_shndx identifies."
-		return file.Sections[symbol.Section].Addr + symbol.Value, nil
+		return section.Offset + symbol.Value, nil
 	case elf.ET_EXEC, elf.ET_DYN:
 		// "In executable and shared object files, st_value holds a virtual address. To make these
 		// files’ symbols more useful for the dynamic linker, the section offset (file interpretation)
diff --git a/genrule/filegroup.go b/genrule/filegroup.go
index 8f28638..9f4aa10 100644
--- a/genrule/filegroup.go
+++ b/genrule/filegroup.go
@@ -62,6 +62,7 @@
 
 func (fg *fileGroup) DepsMutator(ctx android.BottomUpMutatorContext) {
 	android.ExtractSourcesDeps(ctx, fg.properties.Srcs)
+	android.ExtractSourcesDeps(ctx, fg.properties.Exclude_srcs)
 }
 
 func (fg *fileGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 4f3ba93..e423ebc 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -112,9 +112,10 @@
 type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) generateTask
 
 type generateTask struct {
-	in  android.Paths
-	out android.WritablePaths
-	cmd string
+	in          android.Paths
+	out         android.WritablePaths
+	sandboxOuts []string
+	cmd         string
 }
 
 func (g *Module) GeneratedSourceFiles() android.Paths {
@@ -320,7 +321,7 @@
 		Inputs:          task.in,
 		Implicits:       g.deps,
 		Args: map[string]string{
-			"allouts": strings.Join(task.out.Strings(), " "),
+			"allouts": strings.Join(task.sandboxOuts, " "),
 		},
 	}
 	if Bool(g.properties.Depfile) {
@@ -346,23 +347,31 @@
 	return module
 }
 
+// replace "out" with "__SBOX_OUT_DIR__/<the value of ${out}>"
+func pathToSandboxOut(path android.Path, genDir android.Path) string {
+	relOut, err := filepath.Rel(genDir.String(), path.String())
+	if err != nil {
+		panic(fmt.Sprintf("Could not make ${out} relative: %v", err))
+	}
+	return filepath.Join("__SBOX_OUT_DIR__", relOut)
+
+}
+
 func NewGenSrcs() *Module {
 	properties := &genSrcsProperties{}
 
 	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) generateTask {
 		commands := []string{}
 		outFiles := android.WritablePaths{}
-		genPath := android.PathForModuleGen(ctx).String()
+		genDir := android.PathForModuleGen(ctx)
+		sandboxOuts := []string{}
 		for _, in := range srcFiles {
 			outFile := android.GenPathWithExt(ctx, "", in, String(properties.Output_extension))
 			outFiles = append(outFiles, outFile)
 
-			// replace "out" with "__SBOX_OUT_DIR__/<the value of ${out}>"
-			relOut, err := filepath.Rel(genPath, outFile.String())
-			if err != nil {
-				panic(fmt.Sprintf("Could not make ${out} relative: %v", err))
-			}
-			sandboxOutfile := filepath.Join("__SBOX_OUT_DIR__", relOut)
+			sandboxOutfile := pathToSandboxOut(outFile, genDir)
+			sandboxOuts = append(sandboxOuts, sandboxOutfile)
+
 			command, err := android.Expand(rawCommand, func(name string) (string, error) {
 				switch name {
 				case "in":
@@ -384,9 +393,10 @@
 		fullCommand := strings.Join(commands, " && ")
 
 		return generateTask{
-			in:  srcFiles,
-			out: outFiles,
-			cmd: fullCommand,
+			in:          srcFiles,
+			out:         outFiles,
+			sandboxOuts: sandboxOuts,
+			cmd:         fullCommand,
 		}
 	}
 
@@ -409,13 +419,17 @@
 
 	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) generateTask {
 		outs := make(android.WritablePaths, len(properties.Out))
+		sandboxOuts := make([]string, len(properties.Out))
+		genDir := android.PathForModuleGen(ctx)
 		for i, out := range properties.Out {
 			outs[i] = android.PathForModuleGen(ctx, out)
+			sandboxOuts[i] = pathToSandboxOut(outs[i], genDir)
 		}
 		return generateTask{
-			in:  srcFiles,
-			out: outs,
-			cmd: rawCommand,
+			in:          srcFiles,
+			out:         outs,
+			sandboxOuts: sandboxOuts,
+			cmd:         rawCommand,
 		}
 	}
 
diff --git a/java/builder.go b/java/builder.go
index 2fd4ac0..46de4f7 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -35,9 +35,9 @@
 	// Compiling java is not conducive to proper dependency tracking.  The path-matches-class-name
 	// requirement leads to unpredictable generated source file names, and a single .java file
 	// will get compiled into multiple .class files if it contains inner classes.  To work around
-	// this, all java rules write into separate directories and then a post-processing step lists
-	// the files in the the directory into a list file that later rules depend on (and sometimes
-	// read from directly using @<listfile>)
+	// this, all java rules write into separate directories and then are combined into a .jar file
+	// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
+	// .srcjar files are unzipped into a temporary directory when compiled with javac.
 	javac = pctx.AndroidGomaStaticRule("javac",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
@@ -61,19 +61,23 @@
 
 	kotlinc = pctx.AndroidGomaStaticRule("kotlinc",
 		blueprint.RuleParams{
-			// TODO(ccross): kotlinc doesn't support @ file for arguments, which will limit the
-			// maximum number of input files, especially on darwin.
-			Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-				`${config.KotlincCmd} $classpath $kotlincFlags ` +
-				`-jvm-target $kotlinJvmTarget -d $outDir $in && ` +
+			Command: `rm -rf "$outDir" "$srcJarDir" && mkdir -p "$outDir" "$srcJarDir" && ` +
+				`${config.ExtractSrcJarsCmd} $srcJarDir $srcJarDir/list $srcJars && ` +
+				`${config.GenKotlinBuildFileCmd} $classpath $outDir $out.rsp $srcJarDir/list > $outDir/kotlinc-build.xml &&` +
+				`${config.KotlincCmd} $kotlincFlags ` +
+				`-jvm-target $kotlinJvmTarget -Xbuild-file=$outDir/kotlinc-build.xml && ` +
 				`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
 			CommandDeps: []string{
 				"${config.KotlincCmd}",
 				"${config.KotlinCompilerJar}",
+				"${config.GenKotlinBuildFileCmd}",
 				"${config.SoongZipCmd}",
+				"${config.ExtractSrcJarsCmd}",
 			},
+			Rspfile:        "$out.rsp",
+			RspfileContent: `$in`,
 		},
-		"kotlincFlags", "classpath", "outDir", "kotlinJvmTarget")
+		"kotlincFlags", "classpath", "srcJars", "srcJarDir", "outDir", "kotlinJvmTarget")
 
 	errorprone = pctx.AndroidStaticRule("errorprone",
 		blueprint.RuleParams{
@@ -123,8 +127,10 @@
 
 	jar = pctx.AndroidStaticRule("jar",
 		blueprint.RuleParams{
-			Command:     `${config.SoongZipCmd} -jar -o $out $jarArgs`,
-			CommandDeps: []string{"${config.SoongZipCmd}"},
+			Command:        `${config.SoongZipCmd} -jar -o $out @$out.rsp`,
+			CommandDeps:    []string{"${config.SoongZipCmd}"},
+			Rspfile:        "$out.rsp",
+			RspfileContent: "$jarArgs",
 		},
 		"jarArgs")
 
@@ -169,13 +175,11 @@
 	srcFiles, srcJars android.Paths,
 	flags javaBuilderFlags) {
 
-	classDir := android.PathForModuleOut(ctx, "kotlinc", "classes")
-
 	inputs := append(android.Paths(nil), srcFiles...)
-	inputs = append(inputs, srcJars...)
 
 	var deps android.Paths
 	deps = append(deps, flags.kotlincClasspath...)
+	deps = append(deps, srcJars...)
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        kotlinc,
@@ -186,7 +190,9 @@
 		Args: map[string]string{
 			"classpath":    flags.kotlincClasspath.FormJavaClassPath("-classpath"),
 			"kotlincFlags": flags.kotlincFlags,
-			"outDir":       classDir.String(),
+			"srcJars":      strings.Join(srcJars.Strings(), " "),
+			"outDir":       android.PathForModuleOut(ctx, "kotlinc", "classes").String(),
+			"srcJarDir":    android.PathForModuleOut(ctx, "kotlinc", "srcJars").String(),
 			// http://b/69160377 kotlinc only supports -jvm-target 1.6 and 1.8
 			"kotlinJvmTarget": "1.8",
 		},
diff --git a/java/config/config.go b/java/config/config.go
index 75176c9..b5e574e 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -83,6 +83,8 @@
 	pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime")
 
 	pctx.SourcePathVariable("ExtractSrcJarsCmd", "build/soong/scripts/extract-srcjars.sh")
+	pctx.SourcePathVariable("GenKotlinBuildFileCmd", "build/soong/scripts/gen-kotlin-build-file.sh")
+
 	pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
 	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
 	pctx.HostBinToolVariable("MergeZipsCmd", "merge_zips")
diff --git a/java/java.go b/java/java.go
index 955b720..65bd438 100644
--- a/java/java.go
+++ b/java/java.go
@@ -466,6 +466,7 @@
 	ctx.AddDependency(ctx.Module(), libTag, j.properties.Annotation_processors...)
 
 	android.ExtractSourcesDeps(ctx, j.properties.Srcs)
+	android.ExtractSourcesDeps(ctx, j.properties.Exclude_srcs)
 	android.ExtractSourcesDeps(ctx, j.properties.Java_resources)
 	android.ExtractSourceDeps(ctx, j.properties.Manifest)
 
@@ -757,6 +758,16 @@
 
 	jarName := ctx.ModuleName() + ".jar"
 
+	javaSrcFiles := srcFiles.FilterByExt(".java")
+	var uniqueSrcFiles android.Paths
+	set := make(map[string]bool)
+	for _, v := range javaSrcFiles {
+		if _, found := set[v.String()]; !found {
+			set[v.String()] = true
+			uniqueSrcFiles = append(uniqueSrcFiles, v)
+		}
+	}
+
 	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
@@ -767,11 +778,15 @@
 			flags.kotlincFlags += " -no-jdk"
 		}
 
+		var kotlinSrcFiles android.Paths
+		kotlinSrcFiles = append(kotlinSrcFiles, uniqueSrcFiles...)
+		kotlinSrcFiles = append(kotlinSrcFiles, srcFiles.FilterByExt(".kt")...)
+
 		flags.kotlincClasspath = append(flags.kotlincClasspath, deps.kotlinStdlib...)
 		flags.kotlincClasspath = append(flags.kotlincClasspath, deps.classpath...)
 
 		kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
-		TransformKotlinToClasses(ctx, kotlinJar, srcFiles, srcJars, flags)
+		TransformKotlinToClasses(ctx, kotlinJar, kotlinSrcFiles, srcJars, flags)
 		if ctx.Failed() {
 			return
 		}
@@ -783,16 +798,6 @@
 		jars = append(jars, deps.kotlinStdlib...)
 	}
 
-	javaSrcFiles := srcFiles.FilterByExt(".java")
-	var uniqueSrcFiles android.Paths
-	set := make(map[string]bool)
-	for _, v := range javaSrcFiles {
-		if _, found := set[v.String()]; !found {
-			set[v.String()] = true
-			uniqueSrcFiles = append(uniqueSrcFiles, v)
-		}
-	}
-
 	// Store the list of .java files that was passed to javac
 	j.compiledJavaSrcs = uniqueSrcFiles
 	j.compiledSrcJars = srcJars
@@ -1247,15 +1252,6 @@
 	return module
 }
 
-func inList(s string, l []string) bool {
-	for _, e := range l {
-		if e == s {
-			return true
-		}
-	}
-	return false
-}
-
 //
 // Defaults
 //
@@ -1290,3 +1286,4 @@
 
 var Bool = proptools.Bool
 var String = proptools.String
+var inList = android.InList
diff --git a/java/java_test.go b/java/java_test.go
index 60d9a40..19c5f21 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -131,16 +131,19 @@
 	}
 
 	mockFS := map[string][]byte{
-		"Android.bp":  []byte(bp),
-		"a.java":      nil,
-		"b.java":      nil,
-		"c.java":      nil,
-		"b.kt":        nil,
-		"a.jar":       nil,
-		"b.jar":       nil,
-		"java-res/a":  nil,
-		"java-res/b":  nil,
-		"java-res2/a": nil,
+		"Android.bp":     []byte(bp),
+		"a.java":         nil,
+		"b.java":         nil,
+		"c.java":         nil,
+		"b.kt":           nil,
+		"a.jar":          nil,
+		"b.jar":          nil,
+		"java-res/a":     nil,
+		"java-res/b":     nil,
+		"java-res2/a":    nil,
+		"java-fg/a.java": nil,
+		"java-fg/b.java": nil,
+		"java-fg/c.java": nil,
 
 		"prebuilts/sdk/14/android.jar":                nil,
 		"prebuilts/sdk/14/framework.aidl":             nil,
@@ -907,6 +910,32 @@
 	}
 }
 
+func TestExcludeFileGroupInSrcs(t *testing.T) {
+	ctx := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java", ":foo-srcs"],
+			exclude_srcs: ["a.java", ":foo-excludes"],
+		}
+
+		filegroup {
+			name: "foo-srcs",
+			srcs: ["java-fg/a.java", "java-fg/b.java", "java-fg/c.java"],
+		}
+
+		filegroup {
+			name: "foo-excludes",
+			srcs: ["java-fg/a.java", "java-fg/b.java"],
+		}
+	`)
+
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+
+	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "java-fg/c.java" {
+		t.Errorf(`foo inputs %v != ["java-fg/c.java"]`, javac.Inputs)
+	}
+}
+
 func fail(t *testing.T, errs []error) {
 	t.Helper()
 	if len(errs) > 0 {
diff --git a/python/python.go b/python/python.go
index 9a3b1bb..ed879eb 100644
--- a/python/python.go
+++ b/python/python.go
@@ -266,11 +266,13 @@
 	android.ExtractSourcesDeps(ctx, p.properties.Data)
 	// deps from "srcs".
 	android.ExtractSourcesDeps(ctx, p.properties.Srcs)
+	android.ExtractSourcesDeps(ctx, p.properties.Exclude_srcs)
 
 	switch p.properties.Actual_version {
 	case pyVersion2:
 		// deps from "version.py2.srcs" property.
 		android.ExtractSourcesDeps(ctx, p.properties.Version.Py2.Srcs)
+		android.ExtractSourcesDeps(ctx, p.properties.Version.Py2.Exclude_srcs)
 
 		ctx.AddVariationDependencies(nil, pythonLibTag,
 			uniqueLibs(ctx, p.properties.Libs, "version.py2.libs",
@@ -286,6 +288,7 @@
 	case pyVersion3:
 		// deps from "version.py3.srcs" property.
 		android.ExtractSourcesDeps(ctx, p.properties.Version.Py3.Srcs)
+		android.ExtractSourcesDeps(ctx, p.properties.Version.Py3.Exclude_srcs)
 
 		ctx.AddVariationDependencies(nil, pythonLibTag,
 			uniqueLibs(ctx, p.properties.Libs, "version.py3.libs",
diff --git a/scripts/gen-kotlin-build-file.sh b/scripts/gen-kotlin-build-file.sh
new file mode 100755
index 0000000..f077a0c
--- /dev/null
+++ b/scripts/gen-kotlin-build-file.sh
@@ -0,0 +1,60 @@
+#!/bin/bash -e
+
+# Copyright 2018 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.
+
+# Generates kotlinc module xml file to standard output based on rsp files
+
+if [ -z "$1" ]; then
+  echo "usage: $0 <classpath> <outDir> <rspFiles>..." >&2
+  exit 1
+fi
+
+# Classpath variable has a tendency to be prefixed by "-classpath", remove it.
+if [[ $1 == "-classpath" ]]; then
+  shift
+fi;
+
+classpath=$1
+out_dir=$2
+shift 2
+
+# Path in the build file are relative to the build file, we need to make them absolute.
+prefix=`pwd`
+
+# Print preamble
+echo "<modules><module name=\"name\" type=\"java-production\" outputDir=\"${out_dir}\">"
+
+# Print classpath entries
+for file in $(echo $classpath | tr ":" "\n"); do
+  echo "  <classpath path=\"${prefix}/${file}\"/>"
+done
+
+# For each rsp file, print source entries
+while (( "$#" )); do
+  for file in $(cat $1); do
+    if [[ $file == *.java ]]; then
+      echo "  <javaSourceRoots path=\"${prefix}/${file}\"/>"
+    elif [[ $file == *.kt ]]; then
+      echo "  <sources path=\"${prefix}/${file}\"/>"
+    else
+      echo "Unknown source file type ${file}"
+      exit 1
+    fi
+  done
+
+  shift
+done
+
+echo "</module></modules>"
diff --git a/ui/build/config.go b/ui/build/config.go
index 363121f..27ed8e9 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -15,12 +15,14 @@
 package build
 
 import (
+	"io/ioutil"
 	"log"
 	"os"
 	"path/filepath"
 	"runtime"
 	"strconv"
 	"strings"
+	"time"
 
 	"android/soong/shared"
 )
@@ -181,6 +183,20 @@
 	ret.environ.Set("ANDROID_JAVA9_HOME", java9Home)
 	ret.environ.Set("PATH", strings.Join(newPath, string(filepath.ListSeparator)))
 
+	outDir := ret.OutDir()
+	buildDateTimeFile := filepath.Join(outDir, "build_date.txt")
+	var content string
+	if buildDateTime, ok := ret.environ.Get("BUILD_DATETIME"); ok && buildDateTime != "" {
+		content = buildDateTime
+	} else {
+		content = strconv.FormatInt(time.Now().Unix(), 10)
+	}
+	err := ioutil.WriteFile(buildDateTimeFile, []byte(content), 0777)
+	if err != nil {
+		ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
+	}
+	ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile)
+
 	return Config{ret}
 }
 
diff --git a/zip/Android.bp b/zip/Android.bp
index 3bb4f25..259e010 100644
--- a/zip/Android.bp
+++ b/zip/Android.bp
@@ -26,5 +26,8 @@
         "zip.go",
         "rate_limit.go",
     ],
+    testSrcs: [
+      "zip_test.go",
+    ],
 }
 
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index c0418f7..60017aa 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -120,30 +120,12 @@
 }
 
 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'")
-	writeIfChanged = flag.Bool("write_if_changed", false, "only update resultant .zip if it has changed")
+	rootPrefix, relativeRoot *string
 
 	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()
@@ -151,7 +133,42 @@
 }
 
 func main() {
-	flag.Parse()
+	var expandedArgs []string
+	for _, arg := range os.Args {
+		if strings.HasPrefix(arg, "@") {
+			bytes, err := ioutil.ReadFile(strings.TrimPrefix(arg, "@"))
+			if err != nil {
+				fmt.Fprintln(os.Stderr, err.Error())
+				os.Exit(1)
+			}
+			respArgs := zip.ReadRespFile(bytes)
+			expandedArgs = append(expandedArgs, respArgs...)
+		} else {
+			expandedArgs = append(expandedArgs, arg)
+		}
+	}
+
+	flags := flag.NewFlagSet("flags", flag.ExitOnError)
+
+	out := flags.String("o", "", "file to write zip file to")
+	manifest := flags.String("m", "", "input jar manifest file name")
+	directories := flags.Bool("d", false, "include directories in zip")
+	rootPrefix = flags.String("P", "", "path prefix within the zip at which to place files")
+	relativeRoot = flags.String("C", "", "path to use as relative root of files in following -f, -l, or -D arguments")
+	parallelJobs := flags.Int("j", runtime.NumCPU(), "number of parallel threads to use")
+	compLevel := flags.Int("L", 5, "deflate compression level (0-9)")
+	emulateJar := flags.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
+	writeIfChanged := flags.Bool("write_if_changed", false, "only update resultant .zip if it has changed")
+
+	cpuProfile := flags.String("cpuprofile", "", "write cpu profile to file")
+	traceFile := flags.String("trace", "", "write trace to file")
+
+	flags.Var(&listFiles{}, "l", "file containing list of .class files")
+	flags.Var(&dir{}, "D", "directory to include in zip")
+	flags.Var(&file{}, "f", "file to include in zip")
+	flags.Var(&nonDeflatedFiles, "s", "file path to be stored within the zip without compression")
+
+	flags.Parse(expandedArgs[1:])
 
 	err := zip.Run(zip.ZipArgs{
 		FileArgs:                 fArgs,
diff --git a/zip/zip.go b/zip/zip.go
index c878a0c..b7e3764 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -31,6 +31,7 @@
 	"strings"
 	"sync"
 	"time"
+	"unicode"
 
 	"github.com/google/blueprint/pathtools"
 
@@ -132,6 +133,49 @@
 	WriteIfChanged           bool
 }
 
+const NOQUOTE = '\x00'
+
+func ReadRespFile(bytes []byte) []string {
+	var args []string
+	var arg []rune
+
+	isEscaping := false
+	quotingStart := NOQUOTE
+	for _, c := range string(bytes) {
+		switch {
+		case isEscaping:
+			if quotingStart == '"' {
+				if !(c == '"' || c == '\\') {
+					// '\"' or '\\' will be escaped under double quoting.
+					arg = append(arg, '\\')
+				}
+			}
+			arg = append(arg, c)
+			isEscaping = false
+		case c == '\\' && quotingStart != '\'':
+			isEscaping = true
+		case quotingStart == NOQUOTE && (c == '\'' || c == '"'):
+			quotingStart = c
+		case quotingStart != NOQUOTE && c == quotingStart:
+			quotingStart = NOQUOTE
+		case quotingStart == NOQUOTE && unicode.IsSpace(c):
+			// Current character is a space outside quotes
+			if len(arg) != 0 {
+				args = append(args, string(arg))
+			}
+			arg = arg[:0]
+		default:
+			arg = append(arg, c)
+		}
+	}
+
+	if len(arg) != 0 {
+		args = append(args, string(arg))
+	}
+
+	return args
+}
+
 func Run(args ZipArgs) (err error) {
 	if args.CpuProfileFilePath != "" {
 		f, err := os.Create(args.CpuProfileFilePath)
diff --git a/zip/zip_test.go b/zip/zip_test.go
new file mode 100644
index 0000000..03e7958
--- /dev/null
+++ b/zip/zip_test.go
@@ -0,0 +1,87 @@
+// Copyright 2018 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 zip
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestReadRespFile(t *testing.T) {
+	testCases := []struct {
+		name, in string
+		out      []string
+	}{
+		{
+			name: "single quoting test case 1",
+			in:   `./cmd '"'-C`,
+			out:  []string{"./cmd", `"-C`},
+		},
+		{
+			name: "single quoting test case 2",
+			in:   `./cmd '-C`,
+			out:  []string{"./cmd", `-C`},
+		},
+		{
+			name: "single quoting test case 3",
+			in:   `./cmd '\"'-C`,
+			out:  []string{"./cmd", `\"-C`},
+		},
+		{
+			name: "single quoting test case 4",
+			in:   `./cmd '\\'-C`,
+			out:  []string{"./cmd", `\\-C`},
+		},
+		{
+			name: "none quoting test case 1",
+			in:   `./cmd \'-C`,
+			out:  []string{"./cmd", `'-C`},
+		},
+		{
+			name: "none quoting test case 2",
+			in:   `./cmd \\-C`,
+			out:  []string{"./cmd", `\-C`},
+		},
+		{
+			name: "none quoting test case 3",
+			in:   `./cmd \"-C`,
+			out:  []string{"./cmd", `"-C`},
+		},
+		{
+			name: "double quoting test case 1",
+			in:   `./cmd "'"-C`,
+			out:  []string{"./cmd", `'-C`},
+		},
+		{
+			name: "double quoting test case 2",
+			in:   `./cmd "\\"-C`,
+			out:  []string{"./cmd", `\-C`},
+		},
+		{
+			name: "double quoting test case 3",
+			in:   `./cmd "\""-C`,
+			out:  []string{"./cmd", `"-C`},
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got := ReadRespFile([]byte(testCase.in))
+			if !reflect.DeepEqual(got, testCase.out) {
+				t.Errorf("expected %q got %q", testCase.out, got)
+			}
+		})
+	}
+}