Initial support for converting jars to java9 system modules

Adds a java_system_modules module type that (when
EXPERIMENTAL_USE_OPENJDK9 is set to true) converts a list of
java library modules and prebuilt jars into system modules,
and plumbs the system modules through to the javac command
line.

Also exports the location of the system modules to make
variables, as well as the name of the default system module.

Test: TestClasspath in java_test.go, runs automatically as part of the build
Bug: 63986449
Change-Id: I27bd5d2010092422a27b69c91568e49010e02f40
diff --git a/java/java.go b/java/java.go
index 5393b06..c3b7557 100644
--- a/java/java.go
+++ b/java/java.go
@@ -116,6 +116,14 @@
 
 	// List of classes to pass to javac to use as annotation processors
 	Annotation_processor_classes []string
+
+	Openjdk9 struct {
+		// List of source files that should only be used when passing -source 1.9
+		Srcs []string
+
+		// List of javac flags that should only be used when passing -source 1.9
+		Javacflags []string
+	}
 }
 
 type CompilerDeviceProperties struct {
@@ -134,6 +142,9 @@
 
 	// If true, export a copy of the module as a -hostdex module for host testing.
 	Hostdex *bool
+
+	// When targeting 1.9, override the modules to use with --system
+	System_modules *string
 }
 
 // Module contains the properties and members used by all java module types
@@ -185,26 +196,39 @@
 	staticLibTag     = dependencyTag{name: "staticlib"}
 	libTag           = dependencyTag{name: "javalib"}
 	bootClasspathTag = dependencyTag{name: "bootclasspath"}
+	systemModulesTag = dependencyTag{name: "system modules"}
 	frameworkResTag  = dependencyTag{name: "framework-res"}
 )
 
 type sdkDep struct {
 	useModule, useFiles, useDefaultLibs, invalidVersion bool
 
-	module string
-	jar    android.Path
-	aidl   android.Path
+	module        string
+	systemModules string
+
+	jar  android.Path
+	aidl android.Path
+}
+
+func sdkStringToNumber(ctx android.BaseContext, v string) int {
+	switch v {
+	case "", "current", "system_current", "test_current":
+		return 10000
+	default:
+		if i, err := strconv.Atoi(v); err != nil {
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version")
+			return -1
+		} else {
+			return i
+		}
+	}
 }
 
 func decodeSdkDep(ctx android.BaseContext, v string) sdkDep {
-	switch v {
-	case "", "current", "system_current", "test_current":
-		// OK
-	default:
-		if _, err := strconv.Atoi(v); err != nil {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version")
-			return sdkDep{}
-		}
+	i := sdkStringToNumber(ctx, v)
+	if i == -1 {
+		// Invalid sdk version, error handled by sdkStringToNumber.
+		return sdkDep{}
 	}
 
 	toFile := func(v string) sdkDep {
@@ -240,8 +264,9 @@
 
 	toModule := func(m string) sdkDep {
 		return sdkDep{
-			useModule: true,
-			module:    m,
+			useModule:     true,
+			module:        m,
+			systemModules: m + "_system_modules",
 		}
 	}
 
@@ -266,20 +291,31 @@
 }
 
 func (j *Module) deps(ctx android.BottomUpMutatorContext) {
-	if !proptools.Bool(j.properties.No_standard_libs) {
-		if ctx.Device() {
+	if ctx.Device() {
+		if !proptools.Bool(j.properties.No_standard_libs) {
 			sdkDep := decodeSdkDep(ctx, j.deviceProperties.Sdk_version)
 			if sdkDep.useDefaultLibs {
 				ctx.AddDependency(ctx.Module(), bootClasspathTag, config.DefaultBootclasspathLibraries...)
+				if ctx.AConfig().TargetOpenJDK9() {
+					ctx.AddDependency(ctx.Module(), systemModulesTag, config.DefaultSystemModules)
+				}
 				if !proptools.Bool(j.properties.No_framework_libs) {
 					ctx.AddDependency(ctx.Module(), libTag, config.DefaultLibraries...)
 				}
-			}
-			if sdkDep.useModule {
+			} else if sdkDep.useModule {
+				if ctx.AConfig().TargetOpenJDK9() {
+					ctx.AddDependency(ctx.Module(), systemModulesTag, sdkDep.systemModules)
+				}
 				ctx.AddDependency(ctx.Module(), bootClasspathTag, sdkDep.module)
 			}
+		} else if j.deviceProperties.System_modules == nil {
+			ctx.PropertyErrorf("no_standard_libs",
+				"system_modules is required to be set when no_standard_libs is true, did you mean no_framework_libs?")
+		} else if *j.deviceProperties.System_modules != "none" && ctx.AConfig().TargetOpenJDK9() {
+			ctx.AddDependency(ctx.Module(), systemModulesTag, *j.deviceProperties.System_modules)
 		}
 	}
+
 	ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...)
 	ctx.AddDependency(ctx.Module(), staticLibTag, j.properties.Static_libs...)
 	ctx.AddDependency(ctx.Module(), libTag, j.properties.Annotation_processors...)
@@ -335,6 +371,7 @@
 	staticJarResources android.Paths
 	aidlIncludeDirs    android.Paths
 	srcFileLists       android.Paths
+	systemModules      android.Path
 	aidlPreprocess     android.OptionalPath
 }
 
@@ -358,6 +395,15 @@
 			switch tag {
 			case android.DefaultsDepTag, android.SourceDepTag:
 				// Nothing to do
+			case systemModulesTag:
+				if deps.systemModules != nil {
+					panic("Found two system module dependencies")
+				}
+				sm := module.(*SystemModules)
+				if sm.outputFile == nil {
+					panic("Missing directory for system module dependency")
+				}
+				deps.systemModules = sm.outputFile
 			default:
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
@@ -397,19 +443,29 @@
 	var flags javaBuilderFlags
 
 	javacFlags := j.properties.Javacflags
-	if ctx.AConfig().Getenv("EXPERIMENTAL_USE_OPENJDK9") == "" {
-		javacFlags = config.StripJavac9Flags(javacFlags)
+	if ctx.AConfig().TargetOpenJDK9() {
+		javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...)
+		j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...)
 	}
 
+	sdk := sdkStringToNumber(ctx, j.deviceProperties.Sdk_version)
 	if j.properties.Java_version != nil {
 		flags.javaVersion = *j.properties.Java_version
+	} else if ctx.Device() && sdk <= 23 {
+		flags.javaVersion = "1.7"
+	} else if ctx.Device() && sdk <= 26 || !ctx.AConfig().TargetOpenJDK9() {
+		flags.javaVersion = "1.8"
 	} else {
-		flags.javaVersion = "${config.DefaultJavaVersion}"
+		flags.javaVersion = "1.9"
 	}
 
 	flags.bootClasspath.AddPaths(deps.bootClasspath)
 	flags.classpath.AddPaths(deps.classpath)
 
+	if deps.systemModules != nil {
+		flags.systemModules = append(flags.systemModules, deps.systemModules)
+	}
+
 	if len(javacFlags) > 0 {
 		ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
 		flags.javacFlags = "$javacFlags"