Merge "Let LoadHooks call CreateModule"
diff --git a/README.md b/README.md
index bc019ae..3d24e75 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,7 @@
 first assignment, and properties statically by the module type.  The supported
 types are:
 * Bool (`true` or `false`)
+* Integers (`int`)
 * Strings (`"string"`)
 * Lists of strings (`["string1", "string2"]`)
 * Maps (`{key1: "value1", key2: ["value2"]}`)
@@ -71,8 +72,9 @@
 ### Operators
 
 Strings, lists of strings, and maps can be appended using the `+` operator.
-Appending a map produces the union of keys in both maps, appending the values
-of any keys that are present in both maps.
+Integers can be summed up using the `+` operator. Appending a map produces the
+union of keys in both maps, appending the values of any keys that are present
+in both maps.
 
 ### Defaults modules
 
diff --git a/cc/config/global.go b/cc/config/global.go
index 7362f2e..de4fa11 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -36,6 +36,7 @@
 
 		// Make paths in deps files relative
 		"-no-canonical-prefixes",
+		"-fno-canonical-system-headers",
 
 		"-DNDEBUG",
 		"-UDEBUG",
@@ -54,7 +55,6 @@
 	deviceGlobalCflags = []string{
 		"-fdiagnostics-color",
 
-		"-fno-canonical-system-headers",
 		"-ffunction-sections",
 		"-funwind-tables",
 		"-fstack-protector-strong",
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 9e1f02f..5d7f617 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -34,9 +34,8 @@
 
 	cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
 		"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blacklist.txt"}
-	// FIXME: revert the __cfi_check flag when clang is updated to r280031.
 	cfiLdflags = []string{"-flto", "-fsanitize-cfi-cross-dso", "-fsanitize=cfi",
-		"-Wl,-plugin-opt,O1 -Wl,-export-dynamic-symbol=__cfi_check"}
+		"-Wl,-plugin-opt,O1"}
 	cfiArflags        = []string{"--plugin ${config.ClangBin}/../lib64/LLVMgold.so"}
 	cfiExportsMapPath = "build/soong/cc/config/cfi_exports.map"
 	cfiExportsMap     android.Path
@@ -364,9 +363,6 @@
 			// __cfi_check needs to be built as Thumb (see the code in linker_cfi.cpp). LLVM is not set up
 			// to do this on a function basis, so force Thumb on the entire module.
 			flags.RequiredInstructionSet = "thumb"
-			// Workaround for b/33678192. CFI jumptables need Thumb2 codegen.  Revert when
-			// Clang is updated past r290384.
-			flags.LdFlags = append(flags.LdFlags, "-march=armv7-a")
 		}
 		sanitizers = append(sanitizers, "cfi")
 
diff --git a/cmd/multiproduct_kati/Android.bp b/cmd/multiproduct_kati/Android.bp
index b264c35..04a5802 100644
--- a/cmd/multiproduct_kati/Android.bp
+++ b/cmd/multiproduct_kati/Android.bp
@@ -18,6 +18,7 @@
         "soong-ui-build",
         "soong-ui-logger",
         "soong-ui-tracer",
+        "soong-zip",
     ],
     srcs: [
         "main.go",
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 1c853d6..e4a05fc 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -30,6 +30,7 @@
 	"android/soong/ui/build"
 	"android/soong/ui/logger"
 	"android/soong/ui/tracer"
+	"android/soong/zip"
 )
 
 // We default to number of cpus / 4, which seems to be the sweet spot for my
@@ -45,7 +46,7 @@
 
 var numJobs = flag.Int("j", detectNumJobs(), "number of parallel kati jobs")
 
-var keep = flag.Bool("keep", false, "keep successful output files")
+var keepArtifacts = flag.Bool("keep", false, "keep archives of artifacts")
 
 var outDir = flag.String("out", "", "path to store output directories (defaults to tmpdir under $OUT when empty)")
 var alternateResultDir = flag.Bool("dist", false, "write select results to $DIST_DIR (or <out>/dist when empty)")
@@ -200,24 +201,19 @@
 		if err := os.MkdirAll(*outDir, 0777); err != nil {
 			log.Fatalf("Failed to create tempdir: %v", err)
 		}
-
-		if !*keep {
-			defer func() {
-				if status.Finished() == 0 {
-					os.RemoveAll(*outDir)
-				}
-			}()
-		}
 	}
 	config.Environment().Set("OUT_DIR", *outDir)
 	log.Println("Output directory:", *outDir)
 
+	logsDir := filepath.Join(config.OutDir(), "logs")
+	os.MkdirAll(logsDir, 0777)
+
 	build.SetupOutDir(buildCtx, config)
 	if *alternateResultDir {
-		logsDir := filepath.Join(config.DistDir(), "logs")
-		os.MkdirAll(logsDir, 0777)
-		log.SetOutput(filepath.Join(logsDir, "soong.log"))
-		trace.SetOutput(filepath.Join(logsDir, "build.trace"))
+		distLogsDir := filepath.Join(config.DistDir(), "logs")
+		os.MkdirAll(distLogsDir, 0777)
+		log.SetOutput(filepath.Join(distLogsDir, "soong.log"))
+		trace.SetOutput(filepath.Join(distLogsDir, "build.trace"))
 	} else {
 		log.SetOutput(filepath.Join(config.OutDir(), "soong.log"))
 		trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))
@@ -269,17 +265,14 @@
 			})
 
 			productOutDir := filepath.Join(config.OutDir(), product)
-			productLogDir := productOutDir
-			if *alternateResultDir {
-				productLogDir = filepath.Join(config.DistDir(), product)
-				if err := os.MkdirAll(productLogDir, 0777); err != nil {
-					log.Fatalf("Error creating log directory: %v", err)
-				}
-			}
+			productLogDir := filepath.Join(logsDir, product)
 
 			if err := os.MkdirAll(productOutDir, 0777); err != nil {
 				log.Fatalf("Error creating out directory: %v", err)
 			}
+			if err := os.MkdirAll(productLogDir, 0777); err != nil {
+				log.Fatalf("Error creating log directory: %v", err)
+			}
 
 			stdLog = filepath.Join(productLogDir, "std.log")
 			f, err := os.Create(stdLog)
@@ -324,6 +317,26 @@
 						status.Fail(product.config.TargetProduct(), err, product.logFile)
 					})
 
+					defer func() {
+						if *keepArtifacts {
+							args := zip.ZipArgs{
+								FileArgs: []zip.FileArg{
+									{
+										GlobDir:             product.config.OutDir(),
+										SourcePrefixToStrip: product.config.OutDir(),
+									},
+								},
+								OutputFilePath:   filepath.Join(config.OutDir(), product.config.TargetProduct()+".zip"),
+								NumParallelJobs:  runtime.NumCPU(),
+								CompressionLevel: 5,
+							}
+							if err := zip.Run(args); err != nil {
+								log.Fatalf("Error zipping artifacts: %v", err)
+							}
+						}
+						os.RemoveAll(product.config.OutDir())
+					}()
+
 					buildWhat := 0
 					if !*onlyConfig {
 						buildWhat |= build.BuildSoong
@@ -332,9 +345,6 @@
 						}
 					}
 					build.Build(product.ctx, product.config, buildWhat)
-					if !*keep {
-						os.RemoveAll(product.config.OutDir())
-					}
 					status.Finish(product.config.TargetProduct())
 				}()
 			}
@@ -342,6 +352,20 @@
 	}
 	wg2.Wait()
 
+	if *alternateResultDir {
+		args := zip.ZipArgs{
+			FileArgs: []zip.FileArg{
+				{GlobDir: logsDir, SourcePrefixToStrip: logsDir},
+			},
+			OutputFilePath:   filepath.Join(config.DistDir(), "logs.zip"),
+			NumParallelJobs:  runtime.NumCPU(),
+			CompressionLevel: 5,
+		}
+		if err := zip.Run(args); err != nil {
+			log.Fatalf("Error zipping logs: %v", err)
+		}
+	}
+
 	if count := status.Finished(); count > 0 {
 		log.Fatalln(count, "products failed")
 	}
diff --git a/cmd/pom2mk/pom2mk.go b/cmd/pom2mk/pom2mk.go
index e6144a5..ac29a2a 100644
--- a/cmd/pom2mk/pom2mk.go
+++ b/cmd/pom2mk/pom2mk.go
@@ -18,7 +18,6 @@
 	"encoding/xml"
 	"flag"
 	"fmt"
-	"io"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -84,6 +83,8 @@
 
 var extraDeps = make(ExtraDeps)
 
+var useVersion string
+
 type Dependency struct {
 	XMLName xml.Name `xml:"dependency"`
 
@@ -98,6 +99,7 @@
 type Pom struct {
 	XMLName xml.Name `xml:"http://maven.apache.org/POM/4.0.0 project"`
 
+	PomFile      string `xml:"-"`
 	ArtifactFile string `xml:"-"`
 
 	GroupId    string `xml:"groupId"`
@@ -105,7 +107,7 @@
 	Version    string `xml:"version"`
 	Packaging  string `xml:"packaging"`
 
-	Dependencies []Dependency `xml:"dependencies>dependency"`
+	Dependencies []*Dependency `xml:"dependencies>dependency"`
 }
 
 func (p Pom) MkName() string {
@@ -125,6 +127,17 @@
 	return ret
 }
 
+func (p *Pom) FixDepTypes(modules map[string]*Pom) {
+	for _, d := range p.Dependencies {
+		if d.Type != "" {
+			continue
+		}
+		if depPom, ok := modules[d.ArtifactId]; ok {
+			d.Type = depPom.Packaging
+		}
+	}
+}
+
 var mkTemplate = template.Must(template.New("mk").Parse(`
 include $(CLEAR_VARS)
 LOCAL_MODULE := {{.MkName}}
@@ -140,25 +153,30 @@
 include $(BUILD_PREBUILT)
 `))
 
-func convert(filename string, out io.Writer) error {
+func parse(filename string) (*Pom, error) {
 	data, err := ioutil.ReadFile(filename)
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	var pom Pom
 	err = xml.Unmarshal(data, &pom)
 	if err != nil {
-		return err
+		return nil, err
+	}
+
+	if useVersion != "" && pom.Version != useVersion {
+		return nil, nil
 	}
 
 	if pom.Packaging == "" {
 		pom.Packaging = "jar"
 	}
 
+	pom.PomFile = filename
 	pom.ArtifactFile = strings.TrimSuffix(filename, ".pom") + "." + pom.Packaging
 
-	return mkTemplate.Execute(out, pom)
+	return &pom, nil
 }
 
 func main() {
@@ -178,6 +196,9 @@
      Some Android.mk modules have transitive dependencies that must be specified when they are
      depended upon (like android-support-v7-mediarouter requires android-support-v7-appcompat).
      This may be specified multiple times to declare these dependencies.
+  -use-version <version>
+     If the maven directory contains multiple versions of artifacts and their pom files,
+     -use-version can be used to only write makefiles for a specific version of those artifacts.
   <dir>
      The directory to search for *.pom files under.
 
@@ -187,6 +208,7 @@
 
 	flag.Var(&extraDeps, "extra-deps", "Extra dependencies needed when depending on a module")
 	flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names")
+	flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version")
 	flag.Parse()
 
 	if flag.NArg() != 1 {
@@ -240,14 +262,39 @@
 
 	sort.Strings(filenames)
 
+	poms := []*Pom{}
+	modules := make(map[string]*Pom)
+	for _, filename := range filenames {
+		pom, err := parse(filename)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, "Error converting", filename, err)
+			os.Exit(1)
+		}
+
+		if pom != nil {
+			poms = append(poms, pom)
+
+			if old, ok := modules[pom.ArtifactId]; ok {
+				fmt.Fprintln(os.Stderr, "Module", pom.ArtifactId, "defined twice:", old.PomFile, pom.PomFile)
+				os.Exit(1)
+			}
+
+			modules[pom.ArtifactId] = pom
+		}
+	}
+
+	for _, pom := range poms {
+		pom.FixDepTypes(modules)
+	}
+
 	fmt.Println("# Automatically generated with:")
 	fmt.Println("# pom2mk", strings.Join(proptools.ShellEscape(os.Args[1:]), " "))
 	fmt.Println("LOCAL_PATH := $(call my-dir)")
 
-	for _, filename := range filenames {
-		err := convert(filename, os.Stdout)
+	for _, pom := range poms {
+		err := mkTemplate.Execute(os.Stdout, pom)
 		if err != nil {
-			fmt.Fprintln(os.Stderr, "Error converting", filename, err)
+			fmt.Fprintln(os.Stderr, "Error writing", pom.PomFile, pom.MkName(), err)
 			os.Exit(1)
 		}
 	}
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index e3e68c9..4b008eb 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"flag"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -24,7 +25,50 @@
 	"strings"
 )
 
+var (
+	sandboxesRoot string
+	rawCommand    string
+	outputRoot    string
+	keepOutDir    bool
+	depfileOut    string
+)
+
+func init() {
+	flag.StringVar(&sandboxesRoot, "sandbox-path", "",
+		"root of temp directory to put the sandbox into")
+	flag.StringVar(&rawCommand, "c", "",
+		"command to run")
+	flag.StringVar(&outputRoot, "output-root", "",
+		"root of directory to copy outputs into")
+	flag.BoolVar(&keepOutDir, "keep-out-dir", false,
+		"whether to keep the sandbox directory when done")
+
+	flag.StringVar(&depfileOut, "depfile-out", "",
+		"file path of the depfile to generate. This value will replace '__SBOX_DEPFILE__' in the command and will be treated as an output but won't be added to __SBOX_OUT_FILES__")
+}
+
+func usageViolation(violation string) {
+	if violation != "" {
+		fmt.Fprintf(os.Stderr, "Usage error: %s.\n\n", violation)
+	}
+
+	fmt.Fprintf(os.Stderr,
+		"Usage: sbox -c <commandToRun> --sandbox-path <sandboxPath> --output-root <outputRoot> [--depfile-out depFile] <outputFile> [<outputFile>...]\n"+
+			"\n"+
+			"Runs <commandToRun> and moves each <outputFile> out of <sandboxPath>\n"+
+			"and into <outputRoot>\n")
+
+	flag.PrintDefaults()
+
+	os.Exit(1)
+}
+
 func main() {
+	flag.Usage = func() {
+		usageViolation("")
+	}
+	flag.Parse()
+
 	error := run()
 	if error != nil {
 		fmt.Fprintln(os.Stderr, error)
@@ -32,55 +76,9 @@
 	}
 }
 
-var usage = "Usage: sbox -c <commandToRun> --sandbox-path <sandboxPath> --output-root <outputRoot> [--depfile-out depFile] <outputFile> [<outputFile>...]\n" +
-	"\n" +
-	"Runs <commandToRun> and moves each <outputFile> out of <sandboxPath>\n" +
-	"If any file in <outputFiles> is specified by absolute path, then <outputRoot> must be specified as well,\n" +
-	"to enable sbox to compute the relative path within the sandbox of the specified output files"
-
-func usageError(violation string) error {
-	return fmt.Errorf("Usage error: %s.\n\n%s", violation, usage)
-}
-
 func run() error {
-	// the contents of the __SBOX_OUT_FILES__ variable
-	var outputsVarEntries []string
-	// all outputs
-	var allOutputs []string
-
-	args := os.Args[1:]
-
-	var rawCommand string
-	var sandboxesRoot string
-	removeTempDir := true
-	var outputRoot string
-	var depfile string
-
-	for i := 0; i < len(args); i++ {
-		arg := args[i]
-		if arg == "--sandbox-path" {
-			sandboxesRoot = args[i+1]
-			i++
-		} else if arg == "-c" {
-			rawCommand = args[i+1]
-			i++
-		} else if arg == "--output-root" {
-			outputRoot = args[i+1]
-			i++
-		} else if arg == "--keep-out-dir" {
-			removeTempDir = false
-		} else if arg == "--depfile-out" {
-			depfile = args[i+1]
-			i++
-		} else {
-			outputsVarEntries = append(outputsVarEntries, arg)
-		}
-	}
 	if rawCommand == "" {
-		return usageError("-c <commandToRun> is required and must be non-empty")
-	}
-	if len(outputsVarEntries) == 0 {
-		return usageError("at least one output file must be given")
+		usageViolation("-c <commandToRun> is required and must be non-empty")
 	}
 	if sandboxesRoot == "" {
 		// In practice, the value of sandboxesRoot will mostly likely be at a fixed location relative to OUT_DIR,
@@ -88,12 +86,21 @@
 		// the value of sandboxesRoot will most likely be at a fixed location relative to the sbox executable
 		// However, Soong also needs to be able to separately remove the sandbox directory on startup (if it has anything left in it)
 		// and by passing it as a parameter we don't need to duplicate its value
-		return usageError("--sandbox-path <sandboxPath> is required and must be non-empty")
+		usageViolation("--sandbox-path <sandboxPath> is required and must be non-empty")
 	}
 	if len(outputRoot) == 0 {
-		return usageError("--output-root <outputRoot> is required and must be non-empty")
+		usageViolation("--output-root <outputRoot> is required and must be non-empty")
 	}
 
+	// the contents of the __SBOX_OUT_FILES__ variable
+	outputsVarEntries := flag.Args()
+	if len(outputsVarEntries) == 0 {
+		usageViolation("at least one output file must be given")
+	}
+
+	// all outputs
+	var allOutputs []string
+
 	os.MkdirAll(sandboxesRoot, 0777)
 
 	tempDir, err := ioutil.TempDir(sandboxesRoot, "sbox")
@@ -110,8 +117,8 @@
 
 	allOutputs = append([]string(nil), outputsVarEntries...)
 
-	if depfile != "" {
-		sandboxedDepfile, err := filepath.Rel(outputRoot, depfile)
+	if depfileOut != "" {
+		sandboxedDepfile, err := filepath.Rel(outputRoot, depfileOut)
 		if err != nil {
 			return err
 		}
@@ -132,7 +139,7 @@
 	// then at the beginning of the next build, Soong will retry the cleanup
 	defer func() {
 		// in some cases we decline to remove the temp dir, to facilitate debugging
-		if removeTempDir {
+		if !keepOutDir {
 			os.RemoveAll(tempDir)
 		}
 	}()
@@ -190,7 +197,7 @@
 	if len(outputErrors) > 0 {
 		// Keep the temporary output directory around in case a user wants to inspect it for debugging purposes.
 		// Soong will delete it later anyway.
-		removeTempDir = false
+		keepOutDir = true
 		return fmt.Errorf("mismatch between declared and actual outputs in sbox command (%s):\n%v", commandDescription, outputErrors)
 	}
 	// the created files match the declared files; now move them
diff --git a/java/builder.go b/java/builder.go
index 8b6eb9f..45e59a4 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -19,6 +19,8 @@
 // functions.
 
 import (
+	"path/filepath"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -211,12 +213,16 @@
 	})
 }
 
-func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
-	srcFiles, srcJars android.Paths,
-	flags javaBuilderFlags, deps android.Paths) {
+func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int,
+	srcFiles, srcJars android.Paths, flags javaBuilderFlags, deps android.Paths) {
 
-	transformJavaToClasses(ctx, outputFile, srcFiles, srcJars, flags, deps,
-		"javac", "javac", javac)
+	// Compile java sources into .class files
+	desc := "javac"
+	if shardIdx >= 0 {
+		desc += strconv.Itoa(shardIdx)
+	}
+
+	transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc, javac)
 }
 
 func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath,
@@ -226,7 +232,7 @@
 		ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
 	}
 
-	transformJavaToClasses(ctx, outputFile, srcFiles, srcJars, flags, nil,
+	transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil,
 		"errorprone", "errorprone", errorprone)
 }
 
@@ -275,7 +281,7 @@
 // suffix will be appended to various intermediate files and directories to avoid collisions when
 // this function is called twice in the same module directory.
 func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
-	srcFiles, srcJars android.Paths,
+	shardIdx int, srcFiles, srcJars android.Paths,
 	flags javaBuilderFlags, deps android.Paths,
 	intermediatesDir, desc string, rule blueprint.Rule) {
 
@@ -298,6 +304,15 @@
 
 	deps = append(deps, flags.classpath...)
 
+	srcJarDir := "srcjars"
+	outDir := "classes"
+	annoDir := "anno"
+	if shardIdx >= 0 {
+		shardDir := "shard" + strconv.Itoa(shardIdx)
+		srcJarDir = filepath.Join(shardDir, srcJarDir)
+		outDir = filepath.Join(shardDir, outDir)
+		annoDir = filepath.Join(shardDir, annoDir)
+	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
 		Description: desc,
@@ -309,9 +324,9 @@
 			"bootClasspath": bootClasspath,
 			"classpath":     flags.classpath.FormJavaClassPath("-classpath"),
 			"srcJars":       strings.Join(srcJars.Strings(), " "),
-			"srcJarDir":     android.PathForModuleOut(ctx, intermediatesDir, "srcjars").String(),
-			"outDir":        android.PathForModuleOut(ctx, intermediatesDir, "classes").String(),
-			"annoDir":       android.PathForModuleOut(ctx, intermediatesDir, "anno").String(),
+			"srcJarDir":     android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(),
+			"outDir":        android.PathForModuleOut(ctx, intermediatesDir, outDir).String(),
+			"annoDir":       android.PathForModuleOut(ctx, intermediatesDir, annoDir).String(),
 			"javaVersion":   flags.javaVersion,
 		},
 	})
diff --git a/java/java.go b/java/java.go
index f9a4c04..bb6e556 100644
--- a/java/java.go
+++ b/java/java.go
@@ -117,6 +117,9 @@
 	// List of classes to pass to javac to use as annotation processors
 	Annotation_processor_classes []string
 
+	// The number of Java source entries each Javac instance can process
+	Javac_shard_size *int64
+
 	Openjdk9 struct {
 		// List of source files that should only be used when passing -source 1.9
 		Srcs []string
@@ -355,6 +358,18 @@
 	return false
 }
 
+func shardPaths(paths android.Paths, shardSize int) []android.Paths {
+	ret := make([]android.Paths, 0, (len(paths)+shardSize-1)/shardSize)
+	for len(paths) > shardSize {
+		ret = append(ret, paths[0:shardSize])
+		paths = paths[shardSize:]
+	}
+	if len(paths) > 0 {
+		ret = append(ret, paths)
+	}
+	return ret
+}
+
 func (j *Module) hasSrcExt(ext string) bool {
 	return hasSrcExt(j.properties.Srcs, ext)
 }
@@ -571,7 +586,17 @@
 		}
 	}
 
+	enable_sharding := false
 	if ctx.Device() && !ctx.AConfig().IsEnvFalse("TURBINE_ENABLED") {
+		if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 {
+			enable_sharding = true
+			if len(j.properties.Annotation_processors) != 0 ||
+				len(j.properties.Annotation_processor_classes) != 0 {
+				ctx.PropertyErrorf("javac_shard_size",
+					"%q cannot be set when annotation processors are enabled.",
+					j.properties.Javac_shard_size)
+			}
+		}
 		// If sdk jar is java module, then directly return classesJar as header.jar
 		if j.Name() != "android_stubs_current" && j.Name() != "android_system_stubs_current" &&
 			j.Name() != "android_test_stubs_current" {
@@ -590,18 +615,35 @@
 			// 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, "errorprone", jarName)
-			RunErrorProne(ctx, errorprone, javaSrcFiles, srcJars, flags)
+			RunErrorProne(ctx, errorprone, uniqueSrcFiles, srcJars, flags)
 			extraJarDeps = append(extraJarDeps, errorprone)
 		}
 
-		// Compile java sources into .class files
-		classes := android.PathForModuleOut(ctx, "javac", jarName)
-		TransformJavaToClasses(ctx, classes, javaSrcFiles, srcJars, flags, extraJarDeps)
+		if enable_sharding {
+			flags.classpath.AddPaths([]android.Path{j.headerJarFile})
+			shardSize := int(*(j.properties.Javac_shard_size))
+			var shardSrcs []android.Paths
+			if len(uniqueSrcFiles) > 0 {
+				shardSrcs = shardPaths(uniqueSrcFiles, shardSize)
+				for idx, shardSrc := range shardSrcs {
+					classes := android.PathForModuleOut(ctx, "javac", jarName+strconv.Itoa(idx))
+					TransformJavaToClasses(ctx, classes, idx, shardSrc, nil, flags, extraJarDeps)
+					jars = append(jars, classes)
+				}
+			}
+			if len(srcJars) > 0 {
+				classes := android.PathForModuleOut(ctx, "javac", jarName+strconv.Itoa(len(shardSrcs)))
+				TransformJavaToClasses(ctx, classes, len(shardSrcs), nil, srcJars, flags, extraJarDeps)
+				jars = append(jars, classes)
+			}
+		} else {
+			classes := android.PathForModuleOut(ctx, "javac", jarName)
+			TransformJavaToClasses(ctx, classes, -1, uniqueSrcFiles, srcJars, flags, extraJarDeps)
+			jars = append(jars, classes)
+		}
 		if ctx.Failed() {
 			return
 		}
-
-		jars = append(jars, classes)
 	}
 
 	dirArgs, dirDeps := ResourceDirsToJarArgs(ctx, j.properties.Java_resource_dirs, j.properties.Exclude_java_resource_dirs)
diff --git a/java/java_test.go b/java/java_test.go
index b819447..82eff1e 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -22,6 +22,7 @@
 	"os"
 	"path/filepath"
 	"reflect"
+	"strconv"
 	"strings"
 	"testing"
 )
@@ -669,6 +670,71 @@
 	}
 }
 
+func TestTurbine(t *testing.T) {
+	ctx := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+
+		java_library {
+			name: "bar",
+                        srcs: ["b.java"],
+			static_libs: ["foo"],
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["c.java"],
+			libs: ["bar"],
+			sdk_version: "14",
+		}
+		`)
+
+	fooTurbine := ctx.ModuleForTests("foo", "android_common").Rule("turbine")
+	barTurbine := ctx.ModuleForTests("bar", "android_common").Rule("turbine")
+	barJavac := ctx.ModuleForTests("bar", "android_common").Rule("javac")
+	barTurbineCombined := ctx.ModuleForTests("bar", "android_common").Description("for turbine")
+	bazJavac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
+
+	if len(fooTurbine.Inputs) != 1 || fooTurbine.Inputs[0].String() != "a.java" {
+		t.Errorf(`foo inputs %v != ["a.java"]`, fooTurbine.Inputs)
+	}
+
+	fooHeaderJar := filepath.Join(buildDir, ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
+	if !strings.Contains(barTurbine.Args["classpath"], fooHeaderJar) {
+		t.Errorf("bar turbine classpath %v does not contain %q", barTurbine.Args["classpath"], fooHeaderJar)
+	}
+	if !strings.Contains(barJavac.Args["classpath"], fooHeaderJar) {
+		t.Errorf("bar javac classpath %v does not contain %q", barJavac.Args["classpath"], fooHeaderJar)
+	}
+	if len(barTurbineCombined.Inputs) != 2 || barTurbineCombined.Inputs[1].String() != fooHeaderJar {
+		t.Errorf("bar turbine combineJar inputs %v does not contain %q", barTurbineCombined.Inputs, fooHeaderJar)
+	}
+	if !strings.Contains(bazJavac.Args["classpath"], "prebuilts/sdk/14/android.jar") {
+		t.Errorf("baz javac classpath %v does not contain %q", bazJavac.Args["classpath"],
+			"prebuilts/sdk/14/android.jar")
+	}
+}
+
+func TestSharding(t *testing.T) {
+	ctx := testJava(t, `
+		java_library {
+			name: "bar",
+			srcs: ["a.java","b.java","c.java"],
+			javac_shard_size: 1
+		}
+		`)
+
+	barHeaderJar := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+	for i := 0; i < 3; i++ {
+		barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i))
+		if !strings.Contains(barJavac.Args["classpath"], barHeaderJar) {
+			t.Errorf("bar javac classpath %v does not contain %q", barJavac.Args["classpath"], barHeaderJar)
+		}
+	}
+}
+
 func fail(t *testing.T, errs []error) {
 	if len(errs) > 0 {
 		for _, err := range errs {
diff --git a/scripts/copygcclib.sh b/scripts/copygcclib.sh
index 93c52cc..4bd6f9b 100755
--- a/scripts/copygcclib.sh
+++ b/scripts/copygcclib.sh
@@ -2,6 +2,6 @@
 
 OUT=$1
 shift
-LIBPATH=$($@)
+LIBPATH=$($@ | sed -e "s|^$PWD/||")
 cp -f $LIBPATH $OUT
 echo "$OUT: $LIBPATH" > ${OUT}.d