Support java libraries, binaries, and prebuilts

Add support for compiling java libraries (.jar files with
or without .dex), java binaries (.jar files with a wrapper
script to run them), and java prebuilts (for the SDK .jars)

Change-Id: Id624da64c92cf20c6d9577c6bb06e5b212af0d1b
diff --git a/java/builder.go b/java/builder.go
new file mode 100644
index 0000000..2f9d701
--- /dev/null
+++ b/java/builder.go
@@ -0,0 +1,189 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+// This file generates the final rules for compiling all Java.  All properties related to
+// compiling should have been translated into javaBuilderFlags or another argument to the Transform*
+// functions.
+
+import (
+	"path/filepath"
+	"strings"
+
+	"android/soong/common"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/bootstrap"
+)
+
+var (
+	pctx = blueprint.NewPackageContext("android/soong/java")
+
+	// 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>)
+	cc = pctx.StaticRule("javac",
+		blueprint.RuleParams{
+			Command: `$javacCmd -encoding UTF-8 $javacFlags $bootClasspath $classpath ` +
+				`-extdirs "" -d $outDir @$out.rsp || ( rm -rf $outDir; exit 41 ) && ` +
+				`find $outDir -name "*.class" > $out`,
+			Rspfile:        "$out.rsp",
+			RspfileContent: "$in",
+			Description:    "javac $outDir",
+		},
+		"javacCmd", "javacFlags", "bootClasspath", "classpath", "outDir")
+
+	jar = pctx.StaticRule("jar",
+		blueprint.RuleParams{
+			Command:     `$jarCmd -o $out $jarArgs`,
+			Description: "jar $out",
+		},
+		"jarCmd", "jarArgs")
+
+	dx = pctx.StaticRule("dx",
+		blueprint.RuleParams{
+			Command:     "$dxCmd --dex --output=$out $dxFlags $in",
+			Description: "dex $out",
+		},
+		"outDir", "dxFlags")
+)
+
+func init() {
+	pctx.StaticVariable("commonJdkFlags", "-source 1.7 -target 1.7 -Xmaxerrs 9999999")
+	pctx.StaticVariable("javacCmd", "javac -J-Xmx1024M $commonJdkFlags")
+	pctx.StaticVariable("jarCmd", filepath.Join(bootstrap.BinDir, "soong_jar"))
+	pctx.VariableFunc("dxCmd", func(c interface{}) (string, error) {
+		return c.(Config).HostBinTool("dx")
+	})
+}
+
+type javaBuilderFlags struct {
+	javacFlags    string
+	dxFlags       string
+	bootClasspath string
+	classpath     string
+}
+
+type jarSpec struct {
+	fileList, dir string
+}
+
+func (j jarSpec) soongJarArgs() string {
+	return "-C " + j.dir + " -l " + j.fileList
+}
+
+func TransformJavaToClasses(ctx common.AndroidModuleContext, srcFiles []string,
+	flags javaBuilderFlags, deps []string) jarSpec {
+
+	classDir := filepath.Join(common.ModuleOutDir(ctx), "classes")
+	classFileList := filepath.Join(classDir, "classes.list")
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      cc,
+		Outputs:   []string{classFileList},
+		Inputs:    srcFiles,
+		Implicits: deps,
+		Args: map[string]string{
+			"javacFlags":    flags.javacFlags,
+			"bootClasspath": flags.bootClasspath,
+			"classpath":     flags.classpath,
+			"outDir":        classDir,
+		},
+	})
+
+	return jarSpec{classFileList, classDir}
+}
+
+func TransformClassesToJar(ctx common.AndroidModuleContext, classes []jarSpec,
+	manifest string) string {
+
+	outputFile := filepath.Join(common.ModuleOutDir(ctx), "classes-full-debug.jar")
+
+	deps := []string{}
+	jarArgs := []string{}
+
+	for _, j := range classes {
+		deps = append(deps, j.fileList)
+		jarArgs = append(jarArgs, j.soongJarArgs())
+	}
+
+	if manifest != "" {
+		deps = append(deps, manifest)
+		jarArgs = append(jarArgs, "-m "+manifest)
+	}
+
+	deps = append(deps, "$jarCmd")
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      jar,
+		Outputs:   []string{outputFile},
+		Implicits: deps,
+		Args: map[string]string{
+			"jarArgs": strings.Join(jarArgs, " "),
+		},
+	})
+
+	return outputFile
+}
+
+func TransformClassesJarToDex(ctx common.AndroidModuleContext, classesJar string,
+	flags javaBuilderFlags) string {
+
+	outputFile := filepath.Join(common.ModuleOutDir(ctx), "classes.dex")
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      dx,
+		Outputs:   []string{outputFile},
+		Inputs:    []string{classesJar},
+		Implicits: []string{"$dxCmd"},
+		Args: map[string]string{
+			"dxFlags": flags.dxFlags,
+		},
+	})
+
+	return outputFile
+}
+
+func TransformDexToJavaLib(ctx common.AndroidModuleContext, resources []jarSpec,
+	dexFile string) string {
+
+	outputFile := filepath.Join(common.ModuleOutDir(ctx), "javalib.jar")
+	var deps []string
+	var jarArgs []string
+
+	for _, j := range resources {
+		deps = append(deps, j.fileList)
+		jarArgs = append(jarArgs, j.soongJarArgs())
+	}
+
+	dexDir, _ := filepath.Split(dexFile)
+	jarArgs = append(jarArgs, "-C "+dexDir+" -f "+dexFile)
+
+	deps = append(deps, "$jarCmd", dexFile)
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      jar,
+		Outputs:   []string{outputFile},
+		Implicits: deps,
+		Args: map[string]string{
+			"jarArgs": strings.Join(jarArgs, " "),
+		},
+	})
+
+	return outputFile
+}