Merge "Fix soong_ui with empty arguments"
diff --git a/Android.bp b/Android.bp
index ae31a60..32b89d1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -211,6 +211,7 @@
"java/java.go",
"java/proto.go",
"java/resources.go",
+ "java/system_modules.go",
],
testSrcs: [
"java/java_test.go",
@@ -228,6 +229,7 @@
srcs: [
"java/config/config.go",
"java/config/error_prone.go",
+ "java/config/kotlin.go",
"java/config/makevars.go",
],
}
diff --git a/android/config.go b/android/config.go
index 5abda26..dca998f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -88,6 +88,9 @@
captureBuild bool // true for tests, saves build parameters for each module
ignoreEnvironment bool // true for tests, returns empty from all Getenv calls
+ useOpenJDK9 bool // Use OpenJDK9, but possibly target 1.8
+ targetOpenJDK9 bool // Use OpenJDK9 and target 1.9
+
OncePer
}
@@ -183,6 +186,10 @@
config: config,
}
+ if err := config.fromEnv(); err != nil {
+ panic(err)
+ }
+
return Config{config}
}
@@ -273,9 +280,31 @@
config.Targets = targets
config.BuildOsVariant = targets[Host][0].String()
+ if err := config.fromEnv(); err != nil {
+ return Config{}, err
+ }
+
return Config{config}, nil
}
+func (c *config) fromEnv() error {
+ switch c.Getenv("EXPERIMENTAL_USE_OPENJDK9") {
+ case "":
+ // Use OpenJDK8
+ case "1.8":
+ // Use OpenJDK9, but target 1.8
+ c.useOpenJDK9 = true
+ case "true":
+ // Use OpenJDK9 and target 1.9
+ c.useOpenJDK9 = true
+ c.targetOpenJDK9 = true
+ default:
+ return fmt.Errorf(`Invalid value for EXPERIMENTAL_USE_OPENJDK9, should be "", "1.8", or "true"`)
+ }
+
+ return nil
+}
+
func (c *config) RemoveAbandonedFiles() bool {
return false
}
@@ -518,6 +547,16 @@
return Bool(c.ProductVariables.UseGoma)
}
+// Returns true if OpenJDK9 prebuilts are being used
+func (c *config) UseOpenJDK9() bool {
+ return c.useOpenJDK9
+}
+
+// Returns true if -source 1.9 -target 1.9 is being passed to javac
+func (c *config) TargetOpenJDK9() bool {
+ return c.targetOpenJDK9
+}
+
func (c *config) ClangTidy() bool {
return Bool(c.ProductVariables.ClangTidy)
}
diff --git a/android/paths.go b/android/paths.go
index 69a7b0d..09f760a 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -303,6 +303,39 @@
return list[:k]
}
+// HasExt returns true of any of the paths have extension ext, otherwise false
+func (p Paths) HasExt(ext string) bool {
+ for _, path := range p {
+ if path.Ext() == ext {
+ return true
+ }
+ }
+
+ return false
+}
+
+// FilterByExt returns the subset of the paths that have extension ext
+func (p Paths) FilterByExt(ext string) Paths {
+ ret := make(Paths, 0, len(p))
+ for _, path := range p {
+ if path.Ext() == ext {
+ ret = append(ret, path)
+ }
+ }
+ return ret
+}
+
+// FilterOutByExt returns the subset of the paths that do not have extension ext
+func (p Paths) FilterOutByExt(ext string) Paths {
+ ret := make(Paths, 0, len(p))
+ for _, path := range p {
+ if path.Ext() != ext {
+ ret = append(ret, path)
+ }
+ }
+ return ret
+}
+
// WritablePaths is a slice of WritablePaths, used for multiple outputs.
type WritablePaths []WritablePath
diff --git a/android/variable.go b/android/variable.go
index 9dd9d25..16a6b11 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -92,8 +92,8 @@
}
Pdk struct {
- Enabled *bool
- }
+ Enabled *bool `android:"arch_variant"`
+ } `android:"arch_variant"`
Uml struct {
Cppflags []string
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 8a26171..0619b5c 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -16,6 +16,8 @@
import (
"context"
+ "flag"
+ "fmt"
"os"
"path/filepath"
"strconv"
@@ -45,7 +47,10 @@
log := logger.New(os.Stderr)
defer log.Cleanup()
- if len(os.Args) < 2 || !inList("--make-mode", os.Args) {
+ if len(os.Args) < 2 || !(inList("--make-mode", os.Args) ||
+ os.Args[1] == "--dumpvars-mode" ||
+ os.Args[1] == "--dumpvar-mode") {
+
log.Fatalln("The `soong` native UI is not yet available.")
}
@@ -66,7 +71,12 @@
Tracer: trace,
StdioInterface: build.StdioImpl{},
}}
- config := build.NewConfig(buildCtx, os.Args[1:]...)
+ var config build.Config
+ if os.Args[1] == "--dumpvars-mode" || os.Args[1] == "--dumpvar-mode" {
+ config = build.NewConfig(buildCtx)
+ } else {
+ config = build.NewConfig(buildCtx, os.Args[1:]...)
+ }
log.SetVerbose(config.IsVerbose())
build.SetupOutDir(buildCtx, config)
@@ -99,5 +109,129 @@
defer f.Shutdown()
build.FindSources(buildCtx, config, f)
- build.Build(buildCtx, config, build.BuildAll)
+ if os.Args[1] == "--dumpvar-mode" {
+ dumpVar(buildCtx, config, os.Args[2:])
+ } else if os.Args[1] == "--dumpvars-mode" {
+ dumpVars(buildCtx, config, os.Args[2:])
+ } else {
+ build.Build(buildCtx, config, build.BuildAll)
+ }
+}
+
+func dumpVar(ctx build.Context, config build.Config, args []string) {
+ flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
+ flags.Usage = func() {
+ fmt.Fprintf(os.Stderr, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
+ fmt.Fprintln(os.Stderr, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
+ fmt.Fprintln(os.Stderr, "")
+
+ fmt.Fprintln(os.Stderr, "'report_config' is a special case that prints the human-readable config banner")
+ fmt.Fprintln(os.Stderr, "from the beginning of the build.")
+ fmt.Fprintln(os.Stderr, "")
+ flags.PrintDefaults()
+ }
+ abs := flags.Bool("abs", false, "Print the absolute path of the value")
+ flags.Parse(args)
+
+ if flags.NArg() != 1 {
+ flags.Usage()
+ os.Exit(1)
+ }
+
+ varName := flags.Arg(0)
+ if varName == "report_config" {
+ varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
+ if err != nil {
+ ctx.Fatal(err)
+ }
+
+ fmt.Println(build.Banner(varData))
+ } else {
+ varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
+ if err != nil {
+ ctx.Fatal(err)
+ }
+
+ if *abs {
+ var res []string
+ for _, path := range strings.Fields(varData[varName]) {
+ if abs, err := filepath.Abs(path); err == nil {
+ res = append(res, abs)
+ } else {
+ ctx.Fatalln("Failed to get absolute path of", path, err)
+ }
+ }
+ fmt.Println(strings.Join(res, " "))
+ } else {
+ fmt.Println(varData[varName])
+ }
+ }
+}
+
+func dumpVars(ctx build.Context, config build.Config, args []string) {
+ flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
+ flags.Usage = func() {
+ fmt.Fprintf(os.Stderr, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
+ fmt.Fprintln(os.Stderr, "In dumpvars mode, dump the values of one or more legacy make variables, in")
+ fmt.Fprintln(os.Stderr, "shell syntax. The resulting output may be sourced directly into a shell to")
+ fmt.Fprintln(os.Stderr, "set corresponding shell variables.")
+ fmt.Fprintln(os.Stderr, "")
+
+ fmt.Fprintln(os.Stderr, "'report_config' is a special case that dumps a variable containing the")
+ fmt.Fprintln(os.Stderr, "human-readable config banner from the beginning of the build.")
+ fmt.Fprintln(os.Stderr, "")
+ flags.PrintDefaults()
+ }
+
+ varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
+ absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
+
+ varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
+ absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
+
+ flags.Parse(args)
+
+ if flags.NArg() != 0 {
+ flags.Usage()
+ os.Exit(1)
+ }
+
+ vars := strings.Fields(*varsStr)
+ absVars := strings.Fields(*absVarsStr)
+
+ allVars := append([]string{}, vars...)
+ allVars = append(allVars, absVars...)
+
+ if i := indexList("report_config", allVars); i != -1 {
+ allVars = append(allVars[:i], allVars[i+1:]...)
+ allVars = append(allVars, build.BannerVars...)
+ }
+
+ if len(allVars) == 0 {
+ return
+ }
+
+ varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
+ if err != nil {
+ ctx.Fatal(err)
+ }
+
+ for _, name := range vars {
+ if name == "report_config" {
+ fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
+ } else {
+ fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
+ }
+ }
+ for _, name := range absVars {
+ var res []string
+ for _, path := range strings.Fields(varData[name]) {
+ abs, err := filepath.Abs(path)
+ if err != nil {
+ ctx.Fatalln("Failed to get absolute path of", path, err)
+ }
+ res = append(res, abs)
+ }
+ fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
+ }
}
diff --git a/java/app.go b/java/app.go
index 490a03d..42ae236 100644
--- a/java/app.go
+++ b/java/app.go
@@ -89,7 +89,7 @@
publicResourcesFile, proguardOptionsFile, aaptJavaFileList :=
CreateResourceJavaFiles(ctx, aaptRJavaFlags, aaptDeps)
a.aaptJavaFileList = aaptJavaFileList
- a.ExtraSrcLists = append(a.ExtraSrcLists, aaptJavaFileList)
+ // TODO(ccross): export aapt generated java files as a src jar
if a.appProperties.Export_package_resources {
aaptPackageFlags := append([]string(nil), aaptFlags...)
diff --git a/java/builder.go b/java/builder.go
index 118f239..a03f892 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -40,7 +40,7 @@
blueprint.RuleParams{
Command: `rm -rf "$outDir" "$annoDir" && mkdir -p "$outDir" "$annoDir" && ` +
`${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
- `$javacFlags $bootClasspath $classpath ` +
+ `$javacFlags $sourcepath $bootClasspath $classpath ` +
`-source $javaVersion -target $javaVersion ` +
`-d $outDir -s $annoDir @$out.rsp && ` +
`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
@@ -48,13 +48,29 @@
Rspfile: "$out.rsp",
RspfileContent: "$in",
},
- "javacFlags", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
+ "javacFlags", "sourcepath", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
+
+ 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 $javaVersion -d $outDir $in && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
+ CommandDeps: []string{
+ "${config.KotlincCmd}",
+ "${config.KotlinCompilerJar}",
+ "${config.SoongZipCmd}",
+ },
+ },
+ "kotlincFlags", "classpath", "outDir", "javaVersion")
errorprone = pctx.AndroidStaticRule("errorprone",
blueprint.RuleParams{
Command: `rm -rf "$outDir" "$annoDir" && mkdir -p "$outDir" "$annoDir" && ` +
`${config.ErrorProneCmd} ` +
- `$javacFlags $bootClasspath $classpath ` +
+ `$javacFlags $sourcepath $bootClasspath $classpath ` +
`-source $javaVersion -target $javaVersion ` +
`-d $outDir -s $annoDir @$out.rsp && ` +
`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
@@ -67,7 +83,7 @@
Rspfile: "$out.rsp",
RspfileContent: "$in",
},
- "javacFlags", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
+ "javacFlags", "sourcepath", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
jar = pctx.AndroidStaticRule("jar",
blueprint.RuleParams{
@@ -126,58 +142,86 @@
dxFlags string
bootClasspath classpath
classpath classpath
+ systemModules classpath
desugarFlags string
aidlFlags string
javaVersion string
+ kotlincFlags string
+ kotlincClasspath classpath
+
protoFlags string
protoOutFlag string
}
-func TransformJavaToClasses(ctx android.ModuleContext, srcFiles, srcFileLists android.Paths,
- flags javaBuilderFlags, deps android.Paths) android.ModuleOutPath {
+func TransformKotlinToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles android.Paths, srcJars classpath,
+ flags javaBuilderFlags) {
- return transformJavaToClasses(ctx, srcFiles, srcFileLists, flags, deps,
- "classes-compiled.jar", "", "javac", javac)
+ classDir := android.PathForModuleOut(ctx, "classes-kt")
+
+ inputs := append(android.Paths(nil), srcFiles...)
+ inputs = append(inputs, srcJars...)
+
+ ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+ Rule: kotlinc,
+ Description: "kotlinc",
+ Output: outputFile,
+ Inputs: inputs,
+ Args: map[string]string{
+ "classpath": flags.kotlincClasspath.JavaClasspath(),
+ "kotlincFlags": flags.kotlincFlags,
+ "outDir": classDir.String(),
+ "javaVersion": flags.javaVersion,
+ },
+ })
}
-func RunErrorProne(ctx android.ModuleContext, srcFiles, srcFileLists android.Paths,
- flags javaBuilderFlags) android.Path {
+func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles android.Paths, srcJars classpath,
+ flags javaBuilderFlags, deps android.Paths) {
+
+ transformJavaToClasses(ctx, outputFile, srcFiles, srcJars, flags, deps,
+ "", "javac", javac)
+}
+
+func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles android.Paths, srcJars classpath,
+ flags javaBuilderFlags) {
if config.ErrorProneJar == "" {
ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
- return nil
}
- return transformJavaToClasses(ctx, srcFiles, srcFileLists, flags, nil,
- "classes-errorprone.list", "-errorprone", "errorprone", errorprone)
+ transformJavaToClasses(ctx, outputFile, srcFiles, srcJars, flags, nil,
+ "-errorprone", "errorprone", errorprone)
}
// transformJavaToClasses takes source files and converts them to a jar containing .class files.
-// srcFiles is a list of paths to sources, srcFileLists is a list of paths to files that contain
-// paths to sources. There is no dependency on the sources passed through srcFileLists, those
-// must be added through the deps argument, which contains a list of paths that should be added
-// as implicit dependencies. flags contains various command line flags to be passed to the
-// compiler.
+// srcFiles is a list of paths to sources, srcJars is a list of paths to jar files that contain
+// sources. flags contains various command line flags to be passed to the compiler.
//
// This method may be used for different compilers, including javac and Error Prone. The rule
// argument specifies which command line to use and desc sets the description of the rule that will
// be printed at build time. The stem argument provides the file name of the output jar, and
// 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, srcFiles, srcFileLists android.Paths,
- flags javaBuilderFlags, deps android.Paths, stem, suffix, desc string,
- rule blueprint.Rule) android.ModuleOutPath {
+func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles android.Paths, srcJars classpath,
+ flags javaBuilderFlags, deps android.Paths,
+ intermediatesSuffix, desc string, rule blueprint.Rule) {
- outputFile := android.PathForModuleOut(ctx, stem)
+ deps = append(deps, srcJars...)
- javacFlags := flags.javacFlags
- if len(srcFileLists) > 0 {
- javacFlags += " " + android.JoinWithPrefix(srcFileLists.Strings(), "@")
+ var bootClasspath string
+ if flags.javaVersion == "1.9" {
+ deps = append(deps, flags.systemModules...)
+ bootClasspath = flags.systemModules.JavaSystemModules(ctx.Device())
+ } else {
+ deps = append(deps, flags.bootClasspath...)
+ bootClasspath = flags.bootClasspath.JavaBootClasspath(ctx.Device())
}
- deps = append(deps, srcFileLists...)
- deps = append(deps, flags.bootClasspath...)
deps = append(deps, flags.classpath...)
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
@@ -187,22 +231,19 @@
Inputs: srcFiles,
Implicits: deps,
Args: map[string]string{
- "javacFlags": javacFlags,
- "bootClasspath": flags.bootClasspath.JavaBootClasspath(ctx.Device()),
+ "javacFlags": flags.javacFlags,
+ "bootClasspath": bootClasspath,
+ "sourcepath": srcJars.JavaSourcepath(),
"classpath": flags.classpath.JavaClasspath(),
- "outDir": android.PathForModuleOut(ctx, "classes"+suffix).String(),
- "annoDir": android.PathForModuleOut(ctx, "anno"+suffix).String(),
+ "outDir": android.PathForModuleOut(ctx, "classes"+intermediatesSuffix).String(),
+ "annoDir": android.PathForModuleOut(ctx, "anno"+intermediatesSuffix).String(),
"javaVersion": flags.javaVersion,
},
})
-
- return outputFile
}
-func TransformResourcesToJar(ctx android.ModuleContext, jarArgs []string,
- deps android.Paths) android.Path {
-
- outputFile := android.PathForModuleOut(ctx, "res.jar")
+func TransformResourcesToJar(ctx android.ModuleContext, outputFile android.WritablePath,
+ jarArgs []string, deps android.Paths) {
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: jar,
@@ -213,18 +254,10 @@
"jarArgs": strings.Join(jarArgs, " "),
},
})
-
- return outputFile
}
-func TransformJarsToJar(ctx android.ModuleContext, stem string, jars android.Paths,
- manifest android.OptionalPath, stripDirs bool) android.Path {
-
- outputFile := android.PathForModuleOut(ctx, stem)
-
- if len(jars) == 1 && !manifest.Valid() {
- return jars[0]
- }
+func TransformJarsToJar(ctx android.ModuleContext, outputFile android.WritablePath,
+ jars android.Paths, manifest android.OptionalPath, stripDirs bool) {
var deps android.Paths
@@ -248,18 +281,15 @@
"jarArgs": strings.Join(jarArgs, " "),
},
})
-
- return outputFile
}
-func TransformDesugar(ctx android.ModuleContext, classesJar android.Path,
- flags javaBuilderFlags) android.Path {
+func TransformDesugar(ctx android.ModuleContext, outputFile android.WritablePath,
+ classesJar android.Path, flags javaBuilderFlags) {
- outputFile := android.PathForModuleOut(ctx, "classes-desugar.jar")
dumpDir := android.PathForModuleOut(ctx, "desugar_dumped_classes")
javaFlags := ""
- if ctx.AConfig().Getenv("EXPERIMENTAL_USE_OPENJDK9") != "" {
+ if ctx.AConfig().UseOpenJDK9() {
javaFlags = "--add-opens java.base/java.lang.invoke=ALL-UNNAMED"
}
@@ -284,17 +314,14 @@
"desugarFlags": flags.desugarFlags,
},
})
-
- return outputFile
}
// Converts a classes.jar file to classes*.dex, then combines the dex files with any resources
// in the classes.jar file into a dex jar.
-func TransformClassesJarToDexJar(ctx android.ModuleContext, stem string, classesJar android.Path,
- flags javaBuilderFlags) android.Path {
+func TransformClassesJarToDexJar(ctx android.ModuleContext, outputFile android.WritablePath,
+ classesJar android.Path, flags javaBuilderFlags) {
outDir := android.PathForModuleOut(ctx, "dex")
- outputFile := android.PathForModuleOut(ctx, stem)
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: dx,
@@ -306,12 +333,10 @@
"outDir": outDir.String(),
},
})
-
- return outputFile
}
-func TransformJarJar(ctx android.ModuleContext, classesJar android.Path, rulesFile android.Path) android.ModuleOutPath {
- outputFile := android.PathForModuleOut(ctx, "classes-jarjar.jar")
+func TransformJarJar(ctx android.ModuleContext, outputFile android.WritablePath,
+ classesJar android.Path, rulesFile android.Path) {
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: jarjar,
Description: "jarjar",
@@ -322,12 +347,20 @@
"rulesFile": rulesFile.String(),
},
})
-
- return outputFile
}
type classpath []android.Path
+// Returns a -sourcepath argument in the form javac expects. If the list is empty returns
+// -sourcepath "" to ensure javac does not fall back to searching the classpath for sources.
+func (x *classpath) JavaSourcepath() string {
+ if len(*x) > 0 {
+ return "-sourcepath " + strings.Join(x.Strings(), ":")
+ } else {
+ return `-sourcepath ""`
+ }
+}
+
// Returns a -classpath argument in the form java or javac expects
func (x *classpath) JavaClasspath() string {
if len(*x) > 0 {
@@ -359,6 +392,21 @@
}
}
+// Returns a --system argument in the form javac expects with -source 1.9. If forceEmpty is true,
+// returns --system=none if the list is empty to ensure javac does not fall back to the default
+// system modules.
+func (x *classpath) JavaSystemModules(forceEmpty bool) string {
+ if len(*x) > 1 {
+ panic("more than one system module")
+ } else if len(*x) == 1 {
+ return "--system=" + strings.TrimSuffix((*x)[0].String(), "lib/modules")
+ } else if forceEmpty {
+ return "--system=none"
+ } else {
+ return ""
+ }
+}
+
func (x *classpath) DesugarBootClasspath() []string {
if x == nil || *x == nil {
return nil
diff --git a/java/config/config.go b/java/config/config.go
index 70b8fe5..7d1fa29 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -27,6 +27,7 @@
pctx = android.NewPackageContext("android/soong/java/config")
DefaultBootclasspathLibraries = []string{"core-oj", "core-libart"}
+ DefaultSystemModules = "core-system-modules"
DefaultLibraries = []string{"ext", "framework", "okhttp"}
)
@@ -47,9 +48,10 @@
// If a different javac is used the flag will be ignored and extra bridges will be inserted.
// The flag is implemented by https://android-review.googlesource.com/c/486427
`-XDskipDuplicateBridges=true`,
- }, " "))
- pctx.StaticVariable("DefaultJavaVersion", "1.8")
+ // b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9
+ `-XDstringConcat=inline`,
+ }, " "))
pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
@@ -57,7 +59,7 @@
if override := config.(android.Config).Getenv("OVERRIDE_ANDROID_JAVA_HOME"); override != "" {
return override, nil
}
- if jdk9 := config.(android.Config).Getenv("EXPERIMENTAL_USE_OPENJDK9"); jdk9 != "" {
+ if config.(android.Config).UseOpenJDK9() {
return "prebuilts/jdk/jdk9/${hostPrebuiltTag}", nil
}
return "prebuilts/jdk/jdk8/${hostPrebuiltTag}", nil
@@ -71,6 +73,7 @@
pctx.SourcePathVariable("JavadocCmd", "${JavaToolchain}/javadoc")
pctx.SourcePathVariable("JlinkCmd", "${JavaToolchain}/jlink")
pctx.SourcePathVariable("JmodCmd", "${JavaToolchain}/jmod")
+ pctx.SourcePathVariable("JrtFsJar", "${JavaHome}/lib/jrt-fs.jar")
pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
pctx.StaticVariable("SoongZipCmd", filepath.Join("${bootstrap.ToolDir}", "soong_zip"))
@@ -86,17 +89,3 @@
return "", nil
})
}
-
-func StripJavac9Flags(flags []string) []string {
- var ret []string
- for _, f := range flags {
- switch {
- case strings.HasPrefix(f, "-J--add-modules="):
- // drop
- default:
- ret = append(ret, f)
- }
- }
-
- return ret
-}
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
new file mode 100644
index 0000000..35f9e9d
--- /dev/null
+++ b/java/config/kotlin.go
@@ -0,0 +1,25 @@
+// Copyright 2017 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 config
+
+var (
+ KotlinStdlibJar = "external/kotlinc/lib/kotlin-stdlib.jar"
+)
+
+func init() {
+ pctx.SourcePathVariable("KotlincCmd", "external/kotlinc/bin/kotlinc")
+ pctx.SourcePathVariable("KotlinCompilerJar", "external/kotlinc/lib/kotlin-compiler.jar")
+ pctx.SourcePathVariable("KotlinStdlibJar", KotlinStdlibJar)
+}
diff --git a/java/config/makevars.go b/java/config/makevars.go
index 937d597..1453a07 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -27,8 +27,13 @@
func makeVarsProvider(ctx android.MakeVarsContext) {
ctx.Strict("TARGET_DEFAULT_JAVA_LIBRARIES", strings.Join(DefaultLibraries, " "))
ctx.Strict("TARGET_DEFAULT_BOOTCLASSPATH_LIBRARIES", strings.Join(DefaultBootclasspathLibraries, " "))
+ ctx.Strict("DEFAULT_SYSTEM_MODULES", DefaultSystemModules)
- ctx.Strict("DEFAULT_JAVA_LANGUAGE_VERSION", "${DefaultJavaVersion}")
+ if ctx.Config().TargetOpenJDK9() {
+ ctx.Strict("DEFAULT_JAVA_LANGUAGE_VERSION", "1.9")
+ } else {
+ ctx.Strict("DEFAULT_JAVA_LANGUAGE_VERSION", "1.8")
+ }
ctx.Strict("ANDROID_JAVA_HOME", "${JavaHome}")
ctx.Strict("ANDROID_JAVA_TOOLCHAIN", "${JavaToolchain}")
@@ -47,7 +52,7 @@
ctx.Strict("HOST_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
}
- if ctx.Config().IsEnvTrue("EXPERIMENTAL_USE_OPENJDK9") {
+ if ctx.Config().UseOpenJDK9() {
ctx.Strict("JLINK", "${JlinkCmd}")
ctx.Strict("JMOD", "${JmodCmd}")
}
diff --git a/java/gen.go b/java/gen.go
index e55be91..e12a71c 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -85,7 +85,7 @@
}
func (j *Module) genSources(ctx android.ModuleContext, srcFiles android.Paths,
- flags javaBuilderFlags) (android.Paths, android.Paths) {
+ flags javaBuilderFlags) (android.Paths, classpath) {
var protoFiles android.Paths
outSrcFiles := make(android.Paths, 0, len(srcFiles))
@@ -106,16 +106,17 @@
}
}
- var outSrcFileLists android.Paths
+ var outSrcJars classpath
if len(protoFiles) > 0 {
- protoFileList := genProto(ctx, protoFiles,
+ protoSrcJar := android.PathForModuleGen(ctx, "proto.src.jar")
+ genProto(ctx, protoSrcJar, protoFiles,
flags.protoFlags, flags.protoOutFlag, "")
- outSrcFileLists = append(outSrcFileLists, protoFileList)
+ outSrcJars = append(outSrcJars, protoSrcJar)
}
- return outSrcFiles, outSrcFileLists
+ return outSrcFiles, outSrcJars
}
func LogtagsSingleton() blueprint.Singleton {
diff --git a/java/java.go b/java/java.go
index 5393b06..3382cf2 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
@@ -158,9 +169,9 @@
logtagsSrcs android.Paths
- // filelists of extra source files that should be included in the javac command line,
+ // jars containing source files that should be included in the javac command line,
// for example R.java generated by aapt for android apps
- ExtraSrcLists android.Paths
+ ExtraSrcJars android.Paths
// installed file for binary dependency
installFile android.Path
@@ -185,26 +196,40 @@
staticLibTag = dependencyTag{name: "staticlib"}
libTag = dependencyTag{name: "javalib"}
bootClasspathTag = dependencyTag{name: "bootclasspath"}
+ systemModulesTag = dependencyTag{name: "system modules"}
frameworkResTag = dependencyTag{name: "framework-res"}
+ kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"}
)
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 +265,9 @@
toModule := func(m string) sdkDep {
return sdkDep{
- useModule: true,
- module: m,
+ useModule: true,
+ module: m,
+ systemModules: m + "_system_modules",
}
}
@@ -266,20 +292,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...)
@@ -290,6 +327,12 @@
if j.hasSrcExt(".proto") {
protoDeps(ctx, &j.protoProperties)
}
+
+ if j.hasSrcExt(".kt") {
+ // TODO(ccross): move this to a mutator pass that can tell if generated sources contain
+ // Kotlin files
+ ctx.AddDependency(ctx.Module(), kotlinStdlibTag, "kotlin-stdlib")
+ }
}
func hasSrcExt(srcs []string, ext string) bool {
@@ -334,8 +377,10 @@
staticJars android.Paths
staticJarResources android.Paths
aidlIncludeDirs android.Paths
- srcFileLists android.Paths
+ srcJars android.Paths
+ systemModules android.Path
aidlPreprocess android.OptionalPath
+ kotlinStdlib android.Paths
}
func (j *Module) collectDeps(ctx android.ModuleContext) deps {
@@ -358,6 +403,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)
}
@@ -376,8 +430,10 @@
if ctx.ModuleName() == "framework" {
// framework.jar has a one-off dependency on the R.java and Manifest.java files
// generated by framework-res.apk
- deps.srcFileLists = append(deps.srcFileLists, module.(*AndroidApp).aaptJavaFileList)
+ // TODO(ccross): aapt java files should go in a src jar
}
+ case kotlinStdlibTag:
+ deps.kotlinStdlib = dep.ClasspathFiles()
default:
panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
}
@@ -397,19 +453,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"
@@ -427,17 +493,42 @@
flags = protoFlags(ctx, &j.protoProperties, flags)
}
- var srcFileLists android.Paths
+ var srcJars classpath
+ srcFiles, srcJars = j.genSources(ctx, srcFiles, flags)
- srcFiles, srcFileLists = j.genSources(ctx, srcFiles, flags)
+ srcJars = append(srcJars, deps.srcJars...)
- srcFileLists = append(srcFileLists, deps.srcFileLists...)
-
- srcFileLists = append(srcFileLists, j.ExtraSrcLists...)
+ srcJars = append(srcJars, j.ExtraSrcJars...)
var jars android.Paths
- if len(srcFiles) > 0 {
+ 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
+ // won't emit any classes for them.
+
+ flags.kotlincFlags = "-no-stdlib"
+ if ctx.Device() {
+ flags.kotlincFlags += " -no-jdk"
+ }
+
+ flags.kotlincClasspath = append(flags.kotlincClasspath, deps.kotlinStdlib...)
+ flags.kotlincClasspath = append(flags.kotlincClasspath, deps.classpath...)
+
+ kotlinJar := android.PathForModuleOut(ctx, "classes-kt.jar")
+ TransformKotlinToClasses(ctx, kotlinJar, srcFiles, srcJars, flags)
+ if ctx.Failed() {
+ return
+ }
+
+ // Make javac rule depend on the kotlinc rule
+ flags.classpath = append(flags.classpath, kotlinJar)
+ // Jar kotlin classes into the final jar after javac
+ jars = append(jars, kotlinJar)
+ jars = append(jars, deps.kotlinStdlib...)
+ }
+
+ if javaSrcFiles := srcFiles.FilterByExt(".java"); len(javaSrcFiles) > 0 {
var extraJarDeps android.Paths
if ctx.AConfig().IsEnvTrue("RUN_ERROR_PRONE") {
// If error-prone is enabled, add an additional rule to compile the java files into
@@ -445,12 +536,14 @@
// 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 := RunErrorProne(ctx, srcFiles, srcFileLists, flags)
+ errorprone := android.PathForModuleOut(ctx, "classes-errorprone.list")
+ RunErrorProne(ctx, errorprone, javaSrcFiles, srcJars, flags)
extraJarDeps = append(extraJarDeps, errorprone)
}
// Compile java sources into .class files
- classes := TransformJavaToClasses(ctx, srcFiles, srcFileLists, flags, extraJarDeps)
+ classes := android.PathForModuleOut(ctx, "classes-compiled.jar")
+ TransformJavaToClasses(ctx, classes, javaSrcFiles, srcJars, flags, extraJarDeps)
if ctx.Failed() {
return
}
@@ -477,7 +570,8 @@
}
if len(resArgs) > 0 {
- resourceJar := TransformResourcesToJar(ctx, resArgs, resDeps)
+ resourceJar := android.PathForModuleOut(ctx, "res.jar")
+ TransformResourcesToJar(ctx, resourceJar, resArgs, resDeps)
if ctx.Failed() {
return
}
@@ -492,12 +586,23 @@
// Combine the classes built from sources, any manifests, and any static libraries into
// classes.jar. If there is only one input jar this step will be skipped.
- outputFile := TransformJarsToJar(ctx, "classes.jar", jars, manifest, false)
+ var outputFile android.Path
+
+ if len(jars) == 1 && !manifest.Valid() {
+ // Optimization: skip the combine step if there is nothing to do
+ outputFile = jars[0]
+ } else {
+ combinedJar := android.PathForModuleOut(ctx, "classes.jar")
+ TransformJarsToJar(ctx, combinedJar, jars, manifest, false)
+ outputFile = combinedJar
+ }
if j.properties.Jarjar_rules != nil {
jarjar_rules := android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
// Transform classes.jar into classes-jarjar.jar
- outputFile = TransformJarJar(ctx, outputFile, jarjar_rules)
+ jarjarFile := android.PathForModuleOut(ctx, "classes-jarjar.jar")
+ TransformJarJar(ctx, jarjarFile, outputFile, jarjar_rules)
+ outputFile = jarjarFile
if ctx.Failed() {
return
}
@@ -553,13 +658,17 @@
flags.desugarFlags = strings.Join(desugarFlags, " ")
- desugarJar := TransformDesugar(ctx, outputFile, flags)
+ desugarJar := android.PathForModuleOut(ctx, "classes-desugar.jar")
+ TransformDesugar(ctx, desugarJar, outputFile, flags)
+ outputFile = desugarJar
if ctx.Failed() {
return
}
// Compile classes.jar into classes.dex and then javalib.jar
- outputFile = TransformClassesJarToDexJar(ctx, "javalib.jar", desugarJar, flags)
+ javalibJar := android.PathForModuleOut(ctx, "javalib.jar")
+ TransformClassesJarToDexJar(ctx, javalibJar, desugarJar, flags)
+ outputFile = javalibJar
if ctx.Failed() {
return
}
@@ -734,7 +843,9 @@
func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.classpathFiles = android.PathsForModuleSrc(ctx, j.properties.Jars)
- j.combinedClasspathFile = TransformJarsToJar(ctx, "classes.jar", j.classpathFiles, android.OptionalPath{}, false)
+ outputFile := android.PathForModuleOut(ctx, "classes.jar")
+ TransformJarsToJar(ctx, outputFile, j.classpathFiles, android.OptionalPath{}, false)
+ j.combinedClasspathFile = outputFile
}
var _ Dependency = (*Import)(nil)
diff --git a/java/java_test.go b/java/java_test.go
index a86973d..d64688f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -50,9 +50,12 @@
os.Exit(run())
}
-
func testJava(t *testing.T, bp string) *android.TestContext {
- config := android.TestArchConfig(buildDir, nil)
+ return testJavaWithEnv(t, bp, nil)
+}
+
+func testJavaWithEnv(t *testing.T, bp string, env map[string]string) *android.TestContext {
+ config := android.TestArchConfig(buildDir, env)
ctx := android.NewTestArchContext()
ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
@@ -60,6 +63,7 @@
ctx.RegisterModuleType("java_library_host", android.ModuleFactoryAdaptor(LibraryHostFactory))
ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(ImportFactory))
ctx.RegisterModuleType("java_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
+ ctx.RegisterModuleType("java_system_modules", android.ModuleFactoryAdaptor(SystemModulesFactory))
ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(genrule.FileGroupFactory))
ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(genrule.GenRuleFactory))
ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
@@ -76,6 +80,7 @@
"android_stubs_current",
"android_system_stubs_current",
"android_test_stubs_current",
+ "kotlin-stdlib",
}
for _, extra := range extraModules {
@@ -84,15 +89,34 @@
name: "%s",
srcs: ["a.java"],
no_standard_libs: true,
+ system_modules: "core-system-modules",
}
`, extra)
}
+ if config.TargetOpenJDK9() {
+ systemModules := []string{
+ "core-system-modules",
+ "android_stubs_current_system_modules",
+ "android_system_stubs_current_system_modules",
+ "android_test_stubs_current_system_modules",
+ }
+
+ for _, extra := range systemModules {
+ bp += fmt.Sprintf(`
+ java_system_modules {
+ name: "%s",
+ }
+ `, extra)
+ }
+ }
+
ctx.MockFileSystem(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,
"res/a": nil,
@@ -190,17 +214,20 @@
host android.OsClass
properties string
bootclasspath []string
+ system string
classpath []string
}{
{
name: "default",
bootclasspath: []string{"core-oj", "core-libart"},
+ system: "core-system-modules",
classpath: []string{"ext", "framework", "okhttp"},
},
{
name: "blank sdk version",
properties: `sdk_version: "",`,
bootclasspath: []string{"core-oj", "core-libart"},
+ system: "core-system-modules",
classpath: []string{"ext", "framework", "okhttp"},
},
{
@@ -208,6 +235,7 @@
name: "sdk v14",
properties: `sdk_version: "14",`,
bootclasspath: []string{`""`},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
classpath: []string{"prebuilts/sdk/14/android.jar"},
},
{
@@ -215,6 +243,7 @@
name: "current",
properties: `sdk_version: "current",`,
bootclasspath: []string{"android_stubs_current"},
+ system: "android_stubs_current_system_modules",
classpath: []string{},
},
{
@@ -222,6 +251,7 @@
name: "system_current",
properties: `sdk_version: "system_current",`,
bootclasspath: []string{"android_system_stubs_current"},
+ system: "android_system_stubs_current_system_modules",
classpath: []string{},
},
{
@@ -229,12 +259,22 @@
name: "test_current",
properties: `sdk_version: "test_current",`,
bootclasspath: []string{"android_test_stubs_current"},
+ system: "android_test_stubs_current_system_modules",
classpath: []string{},
},
{
name: "nostdlib",
- properties: `no_standard_libs: true`,
+ properties: `no_standard_libs: true, system_modules: "none"`,
+ system: "none",
+ bootclasspath: []string{`""`},
+ classpath: []string{},
+ },
+ {
+
+ name: "nostdlib system_modules",
+ properties: `no_standard_libs: true, system_modules: "core-system-modules"`,
+ system: "core-system-modules",
bootclasspath: []string{`""`},
classpath: []string{},
},
@@ -263,7 +303,7 @@
{
name: "host supported nostdlib",
host: android.Host,
- properties: `host_supported: true, no_standard_libs: true`,
+ properties: `host_supported: true, no_standard_libs: true, system_modules: "none"`,
classpath: []string{},
},
}
@@ -275,12 +315,17 @@
if testcase.moduleType != "" {
moduleType = testcase.moduleType
}
- ctx := testJava(t, moduleType+` {
+
+ bp := moduleType + ` {
name: "foo",
srcs: ["a.java"],
- `+testcase.properties+`
+ ` + testcase.properties + `
+ }`
+
+ variant := "android_common"
+ if testcase.host == android.Host {
+ variant = android.BuildOs.String() + "_common"
}
- `)
convertModulesToPaths := func(cp []string) []string {
ret := make([]string, len(cp))
@@ -293,33 +338,63 @@
bootclasspath := convertModulesToPaths(testcase.bootclasspath)
classpath := convertModulesToPaths(testcase.classpath)
- variant := "android_common"
- if testcase.host == android.Host {
- variant = android.BuildOs.String() + "_common"
- }
- javac := ctx.ModuleForTests("foo", variant).Rule("javac")
-
- got := strings.TrimPrefix(javac.Args["bootClasspath"], "-bootclasspath ")
bc := strings.Join(bootclasspath, ":")
- if got != bc {
- t.Errorf("bootclasspath expected %q != got %q", bc, got)
+ if bc != "" {
+ bc = "-bootclasspath " + bc
}
- got = strings.TrimPrefix(javac.Args["classpath"], "-classpath ")
c := strings.Join(classpath, ":")
- if got != c {
- t.Errorf("classpath expected %q != got %q", c, got)
+ if c != "" {
+ c = "-classpath " + c
+ }
+ system := ""
+ if testcase.system == "none" {
+ system = "--system=none"
+ } else if testcase.system != "" {
+ system = "--system=" + filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system") + "/"
}
- var deps []string
- if len(bootclasspath) > 0 && bootclasspath[0] != `""` {
- deps = append(deps, bootclasspath...)
- }
- deps = append(deps, classpath...)
+ t.Run("1.8", func(t *testing.T) {
+ // Test default javac 1.8
+ ctx := testJava(t, bp)
- if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
- t.Errorf("implicits expected %q != got %q", deps, javac.Implicits.Strings())
- }
+ javac := ctx.ModuleForTests("foo", variant).Rule("javac")
+
+ got := javac.Args["bootClasspath"]
+ if got != bc {
+ t.Errorf("bootclasspath expected %q != got %q", bc, got)
+ }
+
+ got = javac.Args["classpath"]
+ if got != c {
+ t.Errorf("classpath expected %q != got %q", c, got)
+ }
+
+ var deps []string
+ if len(bootclasspath) > 0 && bootclasspath[0] != `""` {
+ deps = append(deps, bootclasspath...)
+ }
+ deps = append(deps, classpath...)
+
+ if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
+ t.Errorf("implicits expected %q != got %q", deps, javac.Implicits.Strings())
+ }
+ })
+
+ // Test again with javac 1.9
+ t.Run("1.9", func(t *testing.T) {
+ ctx := testJavaWithEnv(t, bp, map[string]string{"EXPERIMENTAL_USE_OPENJDK9": "true"})
+
+ javac := ctx.ModuleForTests("foo", variant).Rule("javac")
+ got := javac.Args["bootClasspath"]
+ expected := system
+ if testcase.system == "bootclasspath" {
+ expected = bc
+ }
+ if got != expected {
+ t.Errorf("bootclasspath expected %q != got %q", expected, got)
+ }
+ })
})
}
@@ -540,6 +615,38 @@
}
}
+func TestKotlin(t *testing.T) {
+ ctx := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java", "b.kt"],
+ }
+ `)
+
+ 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")
+
+ if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" ||
+ kotlinc.Inputs[1].String() != "b.kt" {
+ t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs)
+ }
+
+ if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
+ t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
+ }
+
+ if !strings.Contains(javac.Args["classpath"], kotlinc.Output.String()) {
+ t.Errorf("foo classpath %v does not contain %q",
+ javac.Args["classpath"], kotlinc.Output.String())
+ }
+
+ if !inList(kotlinc.Output.String(), jar.Inputs.Strings()) {
+ t.Errorf("foo jar inputs %v does not contain %q",
+ jar.Inputs.Strings(), kotlinc.Output.String())
+ }
+}
+
func fail(t *testing.T, errs []error) {
if len(errs) > 0 {
for _, err := range errs {
diff --git a/java/proto.go b/java/proto.go
index dd8cabd..fc259a5 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -30,20 +30,21 @@
blueprint.RuleParams{
Command: `rm -rf $outDir && mkdir -p $outDir && ` +
`$protocCmd $protoOut=$protoOutFlags:$outDir $protoFlags $in && ` +
- `find $outDir -name "*.java" > $out`,
- CommandDeps: []string{"$protocCmd"},
+ `${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
+ CommandDeps: []string{
+ "$protocCmd",
+ "${config.SoongZipCmd}",
+ },
}, "protoFlags", "protoOut", "protoOutFlags", "outDir")
)
-func genProto(ctx android.ModuleContext, protoFiles android.Paths,
- protoFlags string, protoOut, protoOutFlags string) android.WritablePath {
-
- protoFileList := android.PathForModuleGen(ctx, "proto.filelist")
+func genProto(ctx android.ModuleContext, outputSrcJar android.WritablePath,
+ protoFiles android.Paths, protoFlags string, protoOut, protoOutFlags string) {
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: proto,
Description: "protoc " + protoFiles[0].Rel(),
- Output: protoFileList,
+ Output: outputSrcJar,
Inputs: protoFiles,
Args: map[string]string{
"outDir": android.ProtoDir(ctx).String(),
@@ -52,8 +53,6 @@
"protoFlags": protoFlags,
},
})
-
- return protoFileList
}
func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
diff --git a/java/system_modules.go b/java/system_modules.go
new file mode 100644
index 0000000..ddfc5cf
--- /dev/null
+++ b/java/system_modules.go
@@ -0,0 +1,144 @@
+// Copyright 2017 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 java
+
+import (
+ "fmt"
+ "io"
+ "strings"
+
+ "github.com/google/blueprint"
+
+ "android/soong/android"
+)
+
+// OpenJDK 9 introduces the concept of "system modules", which replace the bootclasspath. This
+// file will produce the rules necessary to convert each unique set of bootclasspath jars into
+// system modules in a runtime image using the jmod and jlink tools.
+
+func init() {
+ android.RegisterModuleType("java_system_modules", SystemModulesFactory)
+
+ pctx.SourcePathVariable("moduleInfoJavaPath", "build/soong/scripts/jars-to-module-info-java.sh")
+}
+
+var (
+ jarsTosystemModules = pctx.AndroidStaticRule("jarsTosystemModules", blueprint.RuleParams{
+ Command: `rm -rf ${outDir} ${workDir} && mkdir -p ${workDir}/jmod && ` +
+ `${moduleInfoJavaPath} ${moduleName} $in > ${workDir}/module-info.java && ` +
+ `${config.JavacCmd} --system=none --patch-module=java.base=${classpath} ${workDir}/module-info.java && ` +
+ `${config.SoongZipCmd} -jar -o ${workDir}/classes.jar -C ${workDir} -f ${workDir}/module-info.class && ` +
+ `${config.MergeZipsCmd} -j ${workDir}/module.jar ${workDir}/classes.jar $in && ` +
+ `${config.JmodCmd} create --module-version 9 --target-platform android ` +
+ ` --class-path ${workDir}/module.jar ${workDir}/jmod/${moduleName}.jmod && ` +
+ `${config.JlinkCmd} --module-path ${workDir}/jmod --add-modules ${moduleName} --output ${outDir} && ` +
+ `cp ${config.JrtFsJar} ${outDir}/lib/`,
+ CommandDeps: []string{
+ "${moduleInfoJavaPath}",
+ "${config.JavacCmd}",
+ "${config.SoongZipCmd}",
+ "${config.MergeZipsCmd}",
+ "${config.JmodCmd}",
+ "${config.JlinkCmd}",
+ "${config.JrtFsJar}",
+ },
+ },
+ "moduleName", "classpath", "outDir", "workDir")
+)
+
+func TransformJarsToSystemModules(ctx android.ModuleContext, moduleName string, jars android.Paths) android.WritablePath {
+ outDir := android.PathForModuleOut(ctx, "system")
+ workDir := android.PathForModuleOut(ctx, "modules")
+ outputFile := android.PathForModuleOut(ctx, "system/lib/modules")
+ outputs := android.WritablePaths{
+ outputFile,
+ android.PathForModuleOut(ctx, "system/lib/jrt-fs.jar"),
+ android.PathForModuleOut(ctx, "system/release"),
+ }
+
+ ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+ Rule: jarsTosystemModules,
+ Description: "system modules",
+ Outputs: outputs,
+ Inputs: jars,
+ Args: map[string]string{
+ "moduleName": moduleName,
+ "classpath": strings.Join(jars.Strings(), ":"),
+ "workDir": workDir.String(),
+ "outDir": outDir.String(),
+ },
+ })
+
+ return outputFile
+}
+
+func SystemModulesFactory() android.Module {
+ module := &SystemModules{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+ return module
+}
+
+type SystemModules struct {
+ android.ModuleBase
+
+ properties SystemModulesProperties
+
+ outputFile android.Path
+}
+
+type SystemModulesProperties struct {
+ // List of java library modules that should be included in the system modules
+ Libs []string
+
+ // List of prebuilt jars that should be included in the system modules
+ Jars []string
+
+ // Sdk version that should be included in the system modules
+ Sdk_version *string
+}
+
+func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ var jars android.Paths
+
+ ctx.VisitDirectDeps(func(module blueprint.Module) {
+ if ctx.OtherModuleDependencyTag(module) == libTag {
+ dep, _ := module.(Dependency)
+ jars = append(jars, dep.ClasspathFiles()...)
+ }
+ })
+
+ jars = append(jars, android.PathsForModuleSrc(ctx, system.properties.Jars)...)
+
+ if ctx.AConfig().TargetOpenJDK9() {
+ system.outputFile = TransformJarsToSystemModules(ctx, "java.base", jars)
+ }
+}
+
+func (system *SystemModules) DepsMutator(ctx android.BottomUpMutatorContext) {
+ ctx.AddDependency(ctx.Module(), libTag, system.properties.Libs...)
+}
+
+func (system *SystemModules) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+ if system.outputFile != nil {
+ makevar := "SOONG_SYSTEM_MODULES_" + name
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, makevar, ":=", system.outputFile.String())
+ fmt.Fprintln(w, ".KATI_READONLY", ":=", makevar)
+ }
+ },
+ }
+}
diff --git a/scripts/jars-to-module-info-java.sh b/scripts/jars-to-module-info-java.sh
new file mode 100755
index 0000000..44be549
--- /dev/null
+++ b/scripts/jars-to-module-info-java.sh
@@ -0,0 +1,20 @@
+#!/bin/bash -e
+
+# Extracts the Java package names of all classes in the .jar files and writes a module-info.java
+# file to stdout that exports all of those packages.
+
+if [ -z "$1" ]; then
+ echo "usage: $0 <module name> <jar1> [<jar2> ...]" >&2
+ exit 1
+fi
+
+module_name=$1
+shift
+
+echo "module ${module_name} {"
+for j in "$@"; do zipinfo -1 $j ; done \
+ | grep -E '/[^/]*\.class$' \
+ | sed 's|\(.*\)/[^/]*\.class$| exports \1;|g' \
+ | sed 's|/|.|g' \
+ | sort -u
+echo "}"
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index e6c3f56..fb20d63 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -15,6 +15,7 @@
package build
import (
+ "bytes"
"fmt"
"strings"
)
@@ -78,6 +79,49 @@
return ret, nil
}
+// Variables to print out in the top banner
+var BannerVars = []string{
+ "PLATFORM_VERSION_CODENAME",
+ "PLATFORM_VERSION",
+ "TARGET_PRODUCT",
+ "TARGET_BUILD_VARIANT",
+ "TARGET_BUILD_TYPE",
+ "TARGET_BUILD_APPS",
+ "TARGET_ARCH",
+ "TARGET_ARCH_VARIANT",
+ "TARGET_CPU_VARIANT",
+ "TARGET_2ND_ARCH",
+ "TARGET_2ND_ARCH_VARIANT",
+ "TARGET_2ND_CPU_VARIANT",
+ "HOST_ARCH",
+ "HOST_2ND_ARCH",
+ "HOST_OS",
+ "HOST_OS_EXTRA",
+ "HOST_CROSS_OS",
+ "HOST_CROSS_ARCH",
+ "HOST_CROSS_2ND_ARCH",
+ "HOST_BUILD_TYPE",
+ "BUILD_ID",
+ "OUT_DIR",
+ "AUX_OS_VARIANT_LIST",
+ "TARGET_BUILD_PDK",
+ "PDK_FUSION_PLATFORM_ZIP",
+}
+
+func Banner(make_vars map[string]string) string {
+ b := &bytes.Buffer{}
+
+ fmt.Fprintln(b, "============================================")
+ for _, name := range BannerVars {
+ if make_vars[name] != "" {
+ fmt.Fprintf(b, "%s=%s\n", name, make_vars[name])
+ }
+ }
+ fmt.Fprint(b, "============================================")
+
+ return b.String()
+}
+
func runMakeProductConfig(ctx Context, config Config) {
// Variables to export into the environment of Kati/Ninja
exportEnvVars := []string{
@@ -99,35 +143,6 @@
"CCACHE_CPP2",
}
- // Variables to print out in the top banner
- bannerVars := []string{
- "PLATFORM_VERSION_CODENAME",
- "PLATFORM_VERSION",
- "TARGET_PRODUCT",
- "TARGET_BUILD_VARIANT",
- "TARGET_BUILD_TYPE",
- "TARGET_BUILD_APPS",
- "TARGET_ARCH",
- "TARGET_ARCH_VARIANT",
- "TARGET_CPU_VARIANT",
- "TARGET_2ND_ARCH",
- "TARGET_2ND_ARCH_VARIANT",
- "TARGET_2ND_CPU_VARIANT",
- "HOST_ARCH",
- "HOST_2ND_ARCH",
- "HOST_OS",
- "HOST_OS_EXTRA",
- "HOST_CROSS_OS",
- "HOST_CROSS_ARCH",
- "HOST_CROSS_2ND_ARCH",
- "HOST_BUILD_TYPE",
- "BUILD_ID",
- "OUT_DIR",
- "AUX_OS_VARIANT_LIST",
- "TARGET_BUILD_PDK",
- "PDK_FUSION_PLATFORM_ZIP",
- }
-
allVars := append(append([]string{
// Used to execute Kati and Ninja
"NINJA_GOALS",
@@ -135,7 +150,7 @@
// To find target/product/<DEVICE>
"TARGET_DEVICE",
- }, exportEnvVars...), bannerVars...)
+ }, exportEnvVars...), BannerVars...)
make_vars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true)
if err != nil {
@@ -143,13 +158,7 @@
}
// Print the banner like make does
- fmt.Fprintln(ctx.Stdout(), "============================================")
- for _, name := range bannerVars {
- if make_vars[name] != "" {
- fmt.Fprintf(ctx.Stdout(), "%s=%s\n", name, make_vars[name])
- }
- }
- fmt.Fprintln(ctx.Stdout(), "============================================")
+ fmt.Fprintln(ctx.Stdout(), Banner(make_vars))
// Populate the environment
env := config.Environment()