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/Blueprints b/Blueprints
index 8a2d85f..31b4fa8 100644
--- a/Blueprints
+++ b/Blueprints
@@ -21,6 +21,7 @@
         "soong-config",
         "soong-env",
         "soong-genrule",
+        "soong-java",
     ],
     srcs: [
         "cmd/soong_build/main.go",
@@ -138,6 +139,30 @@
         "genrule/genrule.go",
     ],
 }
+
+bootstrap_go_binary {
+    name: "soong_jar",
+    srcs: [
+        "cmd/soong_jar/soong_jar.go",
+    ],
+}
+
+bootstrap_go_package {
+    name: "soong-java",
+    pkgPath: "android/soong/java",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "soong-common",
+        "soong-config",
+    ],
+    srcs: [
+        "java/builder.go",
+        "java/java.go",
+        "java/resources.go",
+    ],
+}
+
 //
 // androidmk Android.mk to Blueprints translator
 //
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index 4f2fca1..9de0197 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -18,6 +18,7 @@
 	"LOCAL_ARM_MODE_HACK":   "instruction_set",
 	"LOCAL_SDK_VERSION":     "sdk_version",
 	"LOCAL_NDK_STL_VARIANT": "stl",
+	"LOCAL_JAR_MANIFEST":    "manifest",
 }
 
 var listProperties = map[string]string{
@@ -38,6 +39,12 @@
 	"LOCAL_MODULE_TAGS":             "tags",
 	"LOCAL_LDLIBS":                  "host_ldlibs",
 	"LOCAL_CLANG_CFLAGS":            "clang_cflags",
+
+	"LOCAL_JAVA_RESOURCE_DIRS":    "resource_dirs",
+	"LOCAL_JAVACFLAGS":            "javacflags",
+	"LOCAL_DX_FLAGS":              "dxflags",
+	"LOCAL_JAVA_LIBRARIES":        "java_libs",
+	"LOCAL_STATIC_JAVA_LIBRARIES": "java_static_libs",
 }
 
 var boolProperties = map[string]string{
@@ -49,6 +56,8 @@
 	"LOCAL_NO_CRT":                  "nocrt",
 	"LOCAL_ALLOW_UNDEFINED_SYMBOLS": "allow_undefined_symbols",
 	"LOCAL_RTTI_FLAG":               "rtti",
+
+	"LOCAL_NO_STANDARD_LIBRARIES": "no_standard_libraries",
 }
 
 var deleteProperties = map[string]struct{}{
@@ -103,7 +112,13 @@
 	"BUILD_HOST_EXECUTABLE":     "cc_binary_host",
 	"BUILD_NATIVE_TEST":         "cc_test",
 	"BUILD_HOST_NATIVE_TEST":    "cc_test_host",
-	"BUILD_PREBUILT":            "prebuilt",
+
+	"BUILD_JAVA_LIBRARY":             "java_library",
+	"BUILD_STATIC_JAVA_LIBRARY":      "java_library_static",
+	"BUILD_HOST_JAVA_LIBRARY":        "java_library_host",
+	"BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik",
+
+	"BUILD_PREBUILT": "prebuilt",
 }
 
 var soongModuleTypes = map[string]bool{}
diff --git a/build.ninja.in b/build.ninja.in
index 4fb6325..cdc72fc 100644
--- a/build.ninja.in
+++ b/build.ninja.in
@@ -53,7 +53,7 @@
 # Variant:
 # Type:    bootstrap_go_binary
 # Factory: github.com/google/blueprint/bootstrap.newGoBinaryModule
-# Defined: build/soong/Blueprints:145:1
+# Defined: build/soong/Blueprints:170:1
 
 build .bootstrap/androidmk/obj/androidmk.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/androidmk/cmd/androidmk/android.go $
@@ -79,7 +79,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
-# Defined: build/soong/Blueprints:158:1
+# Defined: build/soong/Blueprints:183:1
 
 build .bootstrap/androidmk-parser/pkg/android/soong/androidmk/parser.a: $
         g.bootstrap.gc $
@@ -310,7 +310,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
-# Defined: build/soong/Blueprints:104:1
+# Defined: build/soong/Blueprints:105:1
 
 build .bootstrap/soong-cc/pkg/android/soong/cc.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/cc/builder.go $
@@ -342,7 +342,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
-# Defined: build/soong/Blueprints:71:1
+# Defined: build/soong/Blueprints:72:1
 
 build .bootstrap/soong-common/pkg/android/soong/common.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/common/arch.go $
@@ -370,7 +370,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
-# Defined: build/soong/Blueprints:91:1
+# Defined: build/soong/Blueprints:92:1
 
 build .bootstrap/soong-config/pkg/android/soong/config.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/config/config.go | $
@@ -393,7 +393,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
-# Defined: build/soong/Blueprints:41:1
+# Defined: build/soong/Blueprints:42:1
 
 build .bootstrap/soong-env/pkg/android/soong/env.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/env/env.go | ${g.bootstrap.gcCmd}
@@ -405,7 +405,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
-# Defined: build/soong/Blueprints:128:1
+# Defined: build/soong/Blueprints:129:1
 
 build .bootstrap/soong-genrule/pkg/android/soong/genrule.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/genrule/genrule.go | $
@@ -429,7 +429,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
-# Defined: build/soong/Blueprints:60:1
+# Defined: build/soong/Blueprints:61:1
 
 build .bootstrap/soong-glob/pkg/android/soong/glob.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/glob/glob.go | ${g.bootstrap.gcCmd} $
@@ -439,6 +439,32 @@
 default .bootstrap/soong-glob/pkg/android/soong/glob.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Module:  soong-java
+# Variant:
+# Type:    bootstrap_go_package
+# Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
+# Defined: build/soong/Blueprints:150:1
+
+build .bootstrap/soong-java/pkg/android/soong/java.a: g.bootstrap.gc $
+        ${g.bootstrap.srcDir}/build/soong/java/builder.go $
+        ${g.bootstrap.srcDir}/build/soong/java/java.go $
+        ${g.bootstrap.srcDir}/build/soong/java/resources.go | $
+        ${g.bootstrap.gcCmd} $
+        .bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
+        .bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
+        .bootstrap/blueprint/pkg/github.com/google/blueprint.a $
+        .bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
+        .bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
+        .bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
+        .bootstrap/soong-env/pkg/android/soong/env.a $
+        .bootstrap/soong-glob/pkg/android/soong/glob.a $
+        .bootstrap/soong-common/pkg/android/soong/common.a $
+        .bootstrap/soong-config/pkg/android/soong/config.a
+    incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-proptools/pkg -I .bootstrap/blueprint/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-deptools/pkg -I .bootstrap/blueprint-bootstrap/pkg -I .bootstrap/soong-env/pkg -I .bootstrap/soong-glob/pkg -I .bootstrap/soong-common/pkg -I .bootstrap/soong-config/pkg
+    pkgPath = android/soong/java
+default .bootstrap/soong-java/pkg/android/soong/java.a
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  soong_build
 # Variant:
 # Type:    bootstrap_go_binary
@@ -460,14 +486,15 @@
         .bootstrap/soong-config/pkg/android/soong/config.a $
         .bootstrap/soong-genrule/pkg/android/soong/genrule.a $
         .bootstrap/soong-cc/pkg/android/soong/cc.a $
-        .bootstrap/soong-art/pkg/android/soong/art.a
-    incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-proptools/pkg -I .bootstrap/blueprint/pkg -I .bootstrap/blueprint-deptools/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-bootstrap/pkg -I .bootstrap/soong-env/pkg -I .bootstrap/soong-glob/pkg -I .bootstrap/soong-common/pkg -I .bootstrap/soong-config/pkg -I .bootstrap/soong-genrule/pkg -I .bootstrap/soong-cc/pkg -I .bootstrap/soong-art/pkg
+        .bootstrap/soong-art/pkg/android/soong/art.a $
+        .bootstrap/soong-java/pkg/android/soong/java.a
+    incFlags = -I .bootstrap/blueprint-parser/pkg -I .bootstrap/blueprint-proptools/pkg -I .bootstrap/blueprint/pkg -I .bootstrap/blueprint-deptools/pkg -I .bootstrap/blueprint-pathtools/pkg -I .bootstrap/blueprint-bootstrap/pkg -I .bootstrap/soong-env/pkg -I .bootstrap/soong-glob/pkg -I .bootstrap/soong-common/pkg -I .bootstrap/soong-config/pkg -I .bootstrap/soong-genrule/pkg -I .bootstrap/soong-cc/pkg -I .bootstrap/soong-art/pkg -I .bootstrap/soong-java/pkg
     pkgPath = soong_build
 default .bootstrap/soong_build/obj/soong_build.a
 
 build .bootstrap/soong_build/obj/a.out: g.bootstrap.link $
         .bootstrap/soong_build/obj/soong_build.a | ${g.bootstrap.linkCmd}
-    libDirFlags = -L .bootstrap/blueprint-parser/pkg -L .bootstrap/blueprint-proptools/pkg -L .bootstrap/blueprint/pkg -L .bootstrap/blueprint-deptools/pkg -L .bootstrap/blueprint-pathtools/pkg -L .bootstrap/blueprint-bootstrap/pkg -L .bootstrap/soong-env/pkg -L .bootstrap/soong-glob/pkg -L .bootstrap/soong-common/pkg -L .bootstrap/soong-config/pkg -L .bootstrap/soong-genrule/pkg -L .bootstrap/soong-cc/pkg -L .bootstrap/soong-art/pkg
+    libDirFlags = -L .bootstrap/blueprint-parser/pkg -L .bootstrap/blueprint-proptools/pkg -L .bootstrap/blueprint/pkg -L .bootstrap/blueprint-deptools/pkg -L .bootstrap/blueprint-pathtools/pkg -L .bootstrap/blueprint-bootstrap/pkg -L .bootstrap/soong-env/pkg -L .bootstrap/soong-glob/pkg -L .bootstrap/soong-common/pkg -L .bootstrap/soong-config/pkg -L .bootstrap/soong-genrule/pkg -L .bootstrap/soong-cc/pkg -L .bootstrap/soong-art/pkg -L .bootstrap/soong-java/pkg
 default .bootstrap/soong_build/obj/a.out
 
 build .bootstrap/bin/soong_build: g.bootstrap.cp $
@@ -479,7 +506,7 @@
 # Variant:
 # Type:    bootstrap_go_binary
 # Factory: github.com/google/blueprint/bootstrap.newGoBinaryModule
-# Defined: build/soong/Blueprints:31:1
+# Defined: build/soong/Blueprints:32:1
 
 build .bootstrap/soong_env/obj/soong_env.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/cmd/soong_env/soong_env.go | $
@@ -501,7 +528,7 @@
 # Variant:
 # Type:    bootstrap_go_binary
 # Factory: github.com/google/blueprint/bootstrap.newGoBinaryModule
-# Defined: build/soong/Blueprints:50:1
+# Defined: build/soong/Blueprints:51:1
 
 build .bootstrap/soong_glob/obj/soong_glob.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/cmd/soong_glob/soong_glob.go | $
@@ -522,6 +549,25 @@
 default .bootstrap/bin/soong_glob
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Module:  soong_jar
+# Variant:
+# Type:    bootstrap_go_binary
+# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModule
+# Defined: build/soong/Blueprints:143:1
+
+build .bootstrap/soong_jar/obj/soong_jar.a: g.bootstrap.gc $
+        ${g.bootstrap.srcDir}/build/soong/cmd/soong_jar/soong_jar.go | $
+        ${g.bootstrap.gcCmd}
+    pkgPath = soong_jar
+default .bootstrap/soong_jar/obj/soong_jar.a
+
+build .bootstrap/soong_jar/obj/a.out: g.bootstrap.link $
+        .bootstrap/soong_jar/obj/soong_jar.a | ${g.bootstrap.linkCmd}
+default .bootstrap/soong_jar/obj/a.out
+build .bootstrap/bin/soong_jar: g.bootstrap.cp .bootstrap/soong_jar/obj/a.out
+default .bootstrap/bin/soong_jar
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Singleton: bootstrap
 # Factory:   github.com/google/blueprint/bootstrap.newSingleton
 
@@ -540,7 +586,7 @@
         ${g.bootstrap.srcDir}/Blueprints | .bootstrap/bin/androidmk $
         .bootstrap/bin/bpfmt .bootstrap/bin/bpmodify .bootstrap/bin/minibp $
         .bootstrap/bin/soong_build .bootstrap/bin/soong_env $
-        .bootstrap/bin/soong_glob
+        .bootstrap/bin/soong_glob .bootstrap/bin/soong_jar
 default .bootstrap/main.ninja.in
 build .bootstrap/notAFile: phony
 default .bootstrap/notAFile
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 23bcb56..01740d1 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -28,6 +28,7 @@
 	"android/soong/common"
 	"android/soong/config"
 	"android/soong/genrule"
+	"android/soong/java"
 )
 
 func main() {
@@ -61,6 +62,13 @@
 	ctx.RegisterModuleType("art_cc_library", art.ArtCCLibraryFactory)
 	ctx.RegisterModuleType("art_cc_binary", art.ArtCCBinaryFactory)
 
+	ctx.RegisterModuleType("java_library", java.JavaLibraryFactory)
+	ctx.RegisterModuleType("java_library_static", java.JavaLibraryFactory)
+	ctx.RegisterModuleType("java_library_host", java.JavaLibraryHostFactory)
+	ctx.RegisterModuleType("java_binary", java.JavaBinaryFactory)
+	ctx.RegisterModuleType("java_binary_host", java.JavaBinaryHostFactory)
+	ctx.RegisterModuleType("prebuilt_java_library", java.JavaPrebuiltFactory)
+
 	// Mutators
 	ctx.RegisterEarlyMutator("arch", common.ArchMutator)
 	ctx.RegisterEarlyMutator("link", cc.LinkageMutator)
diff --git a/cmd/soong_jar/soong_jar.go b/cmd/soong_jar/soong_jar.go
new file mode 100644
index 0000000..2314496
--- /dev/null
+++ b/cmd/soong_jar/soong_jar.go
@@ -0,0 +1,220 @@
+// 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 main
+
+import (
+	"archive/zip"
+	"flag"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+type fileArg struct {
+	relativeRoot, file string
+}
+
+type fileArgs []fileArg
+
+func (l *fileArgs) String() string {
+	return `""`
+}
+
+func (l *fileArgs) Set(s string) error {
+	if *relativeRoot == "" {
+		return fmt.Errorf("must pass -C before -f")
+	}
+
+	*l = append(*l, fileArg{*relativeRoot, s})
+	return nil
+}
+
+func (l *fileArgs) Get() interface{} {
+	return l
+}
+
+var (
+	out          = flag.String("o", "", "file to write jar file to")
+	manifest     = flag.String("m", "", "input manifest file name")
+	directories  = flag.Bool("d", false, "include directories in jar")
+	relativeRoot = flag.String("C", "", "path to use as relative root of files in next -f or -l argument")
+	listFiles    fileArgs
+	files        fileArgs
+)
+
+func init() {
+	flag.Var(&listFiles, "l", "file containing list of .class files")
+	flag.Var(&files, "f", "file to include in jar")
+}
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "usage: soong_jar -o jarfile [-m manifest] -C dir [-f|-l file]...\n")
+	flag.PrintDefaults()
+	os.Exit(2)
+}
+
+type zipInfo struct {
+	time        time.Time
+	createdDirs map[string]bool
+	directories bool
+}
+
+func main() {
+	flag.Parse()
+
+	if *out == "" {
+		fmt.Fprintf(os.Stderr, "error: -o is required\n")
+		usage()
+	}
+
+	info := zipInfo{
+		time:        time.Now(),
+		createdDirs: make(map[string]bool),
+		directories: *directories,
+	}
+
+	// TODO: Go's zip implementation doesn't support increasing the compression level yet
+	err := writeZipFile(*out, listFiles, *manifest, info)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err.Error())
+		os.Exit(1)
+	}
+}
+
+func writeZipFile(out string, listFiles fileArgs, manifest string, info zipInfo) error {
+	f, err := os.Create(out)
+	if err != nil {
+		return err
+	}
+
+	defer f.Close()
+	defer func() {
+		if err != nil {
+			os.Remove(out)
+		}
+	}()
+
+	zipFile := zip.NewWriter(f)
+	defer zipFile.Close()
+
+	for _, listFile := range listFiles {
+		err = writeListFile(zipFile, listFile, info)
+		if err != nil {
+			return err
+		}
+	}
+
+	for _, file := range files {
+		err = writeRelFile(zipFile, file.relativeRoot, file.file, info)
+		if err != nil {
+			return err
+		}
+	}
+
+	if manifest != "" {
+		err = writeFile(zipFile, "META-INF/MANIFEST.MF", manifest, info)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func writeListFile(zipFile *zip.Writer, listFile fileArg, info zipInfo) error {
+	list, err := ioutil.ReadFile(listFile.file)
+	if err != nil {
+		return err
+	}
+
+	files := strings.Split(string(list), "\n")
+
+	for _, file := range files {
+		file = strings.TrimSpace(file)
+		if file == "" {
+			continue
+		}
+		err = writeRelFile(zipFile, listFile.relativeRoot, file, info)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func writeRelFile(zipFile *zip.Writer, root, file string, info zipInfo) error {
+	rel, err := filepath.Rel(root, file)
+	if err != nil {
+		return err
+	}
+
+	err = writeFile(zipFile, rel, file, info)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func writeFile(zipFile *zip.Writer, rel, file string, info zipInfo) error {
+	if info.directories {
+		dir, _ := filepath.Split(rel)
+		for dir != "" && !info.createdDirs[dir] {
+			info.createdDirs[dir] = true
+
+			dirHeader := &zip.FileHeader{
+				Name: dir,
+			}
+			dirHeader.SetMode(os.ModeDir)
+			dirHeader.SetModTime(info.time)
+
+			_, err := zipFile.CreateHeader(dirHeader)
+			if err != nil {
+				return err
+			}
+
+			dir, _ = filepath.Split(dir)
+		}
+	}
+
+	fileHeader := &zip.FileHeader{
+		Name:   rel,
+		Method: zip.Deflate,
+	}
+	fileHeader.SetModTime(info.time)
+
+	out, err := zipFile.CreateHeader(fileHeader)
+	if err != nil {
+		return err
+	}
+
+	in, err := os.Open(file)
+	if err != nil {
+		return err
+	}
+	defer in.Close()
+
+	_, err = io.Copy(out, in)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/common/arch.go b/common/arch.go
index 1758c0d..d4c17cf 100644
--- a/common/arch.go
+++ b/common/arch.go
@@ -31,6 +31,10 @@
 	Mips64 = newArch64("Mips64")
 	X86    = newArch32("X86")
 	X86_64 = newArch64("X86_64")
+
+	Common = ArchType{
+		Name: "common",
+	}
 )
 
 /*
@@ -254,6 +258,14 @@
 		HostOrDevice: Host,
 		ArchType:     X86_64,
 	}
+	commonDevice = Arch{
+		HostOrDevice: Device,
+		ArchType:     Common,
+	}
+	commonHost = Arch{
+		HostOrDevice: Host,
+		ArchType:     Common,
+	}
 )
 
 func ArchMutator(mctx blueprint.EarlyMutatorContext) {
@@ -269,11 +281,18 @@
 	arches := []Arch{}
 
 	if module.base().HostSupported() {
-		arches = append(arches, host64Arch)
+		switch module.base().commonProperties.Compile_multilib {
+		case "common":
+			arches = append(arches, commonHost)
+		default:
+			arches = append(arches, host64Arch)
+		}
 	}
 
 	if module.base().DeviceSupported() {
 		switch module.base().commonProperties.Compile_multilib {
+		case "common":
+			arches = append(arches, commonDevice)
 		case "both":
 			arches = append(arches, arm64Arch, armArch)
 		case "first", "64":
@@ -345,6 +364,10 @@
 
 // Rewrite the module's properties structs to contain arch-specific values.
 func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext, arch Arch) {
+	if arch.ArchType == Common {
+		return
+	}
+
 	for i := range a.generalProperties {
 		generalPropsValue := reflect.ValueOf(a.generalProperties[i]).Elem()
 
diff --git a/common/module.go b/common/module.go
index a834384..c008d7d 100644
--- a/common/module.go
+++ b/common/module.go
@@ -107,8 +107,9 @@
 type Multilib string
 
 const (
-	MultilibBoth  Multilib = "both"
-	MultilibFirst Multilib = "first"
+	MultilibBoth   Multilib = "both"
+	MultilibFirst  Multilib = "first"
+	MultilibCommon Multilib = "common"
 )
 
 func InitAndroidModule(m AndroidModule,
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
+}
diff --git a/java/java.go b/java/java.go
new file mode 100644
index 0000000..67bde04
--- /dev/null
+++ b/java/java.go
@@ -0,0 +1,403 @@
+// 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 contains the module types for compiling Java for Android, and converts the properties
+// into the flags and filenames necessary to pass to the compiler.  The final creation of the rules
+// is handled in builder.go
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
+
+	"android/soong/common"
+)
+
+type Config interface {
+	SrcDir() string
+	PrebuiltOS() string
+	HostBinTool(string) (string, error)
+	Getenv(string) string
+}
+
+// TODO:
+// Autogenerated files:
+//  AIDL
+//  Proto
+//  Renderscript
+// Post-jar passes:
+//  Proguard
+//  Emma
+//  Jarjar
+//  Dex
+// Rmtypedefs
+// Jack
+// DroidDoc
+// Findbugs
+
+// javaBase contains the properties and members used by all java module types, and implements
+// the blueprint.Module interface.
+type javaBase struct {
+	common.AndroidModuleBase
+	module JavaModuleType
+
+	properties struct {
+		// srcs: list of source files used to compile the Java module.  May be .java, .logtags, .proto,
+		// or .aidl files.
+		Srcs []string `android:"arch_variant,arch_subtract"`
+
+		// resource_dirs: list of directories containing resources
+		Resource_dirs []string `android:"arch_variant"`
+
+		// no_standard_libraries: don't build against the default libraries (core-libart, core-junit,
+		// ext, and framework for device targets)
+		No_standard_libraries bool
+
+		// javacflags: list of module-specific flags that will be used for javac compiles
+		Javacflags []string `android:"arch_variant"`
+
+		// dxflags: list of module-specific flags that will be used for dex compiles
+		Dxflags []string `android:"arch_variant"`
+
+		// java_libs: list of of java libraries that will be in the classpath
+		Java_libs []string `android:"arch_variant"`
+
+		// java_static_libs: list of java libraries that will be compiled into the resulting jar
+		Java_static_libs []string `android:"arch_variant"`
+
+		// manifest: manifest file to be included in resulting jar
+		Manifest string
+
+		// sdk_version: if not blank, set to the version of the sdk to compile against
+		Sdk_version string
+
+		// Set for device java libraries, and for host versions of device java libraries
+		// built for testing
+		Dex bool `blueprint:"mutated"`
+	}
+
+	// output file suitable for inserting into the classpath of another compile
+	classpathFile string
+
+	// jarSpecs suitable for inserting classes from a static library into another jar
+	classJarSpecs []jarSpec
+
+	// jarSpecs suitable for inserting resources from a static library into another jar
+	resourceJarSpecs []jarSpec
+
+	// installed file for binary dependency
+	installFile string
+}
+
+type JavaModuleType interface {
+	GenerateJavaBuildActions(ctx common.AndroidModuleContext)
+}
+
+type JavaDependency interface {
+	ClasspathFile() string
+	ClassJarSpecs() []jarSpec
+	ResourceJarSpecs() []jarSpec
+}
+
+func NewJavaBase(base *javaBase, module JavaModuleType, hod common.HostOrDeviceSupported,
+	props ...interface{}) (blueprint.Module, []interface{}) {
+
+	base.module = module
+
+	props = append(props, &base.properties)
+
+	return common.InitAndroidArchModule(base, hod, common.MultilibCommon, props...)
+}
+
+func (j *javaBase) BootClasspath(ctx common.AndroidBaseContext) string {
+	if ctx.Device() {
+		if j.properties.Sdk_version == "" {
+			return "core-libart"
+		} else if j.properties.Sdk_version == "current" {
+			// TODO: !TARGET_BUILD_APPS
+			return "android_stubs_current"
+		} else if j.properties.Sdk_version == "system_current" {
+			return "android_system_stubs_current"
+		} else {
+			return "sdk_v" + j.properties.Sdk_version
+		}
+	} else {
+		if j.properties.Dex {
+			return "core-libart"
+		} else {
+			return ""
+		}
+	}
+}
+
+func (j *javaBase) AndroidDynamicDependencies(ctx common.AndroidDynamicDependerModuleContext) []string {
+	var deps []string
+
+	if !j.properties.No_standard_libraries {
+		bootClasspath := j.BootClasspath(ctx)
+		if bootClasspath != "" {
+			deps = append(deps, bootClasspath)
+		}
+	}
+	deps = append(deps, j.properties.Java_libs...)
+	deps = append(deps, j.properties.Java_static_libs...)
+
+	return deps
+}
+
+func (j *javaBase) collectDeps(ctx common.AndroidModuleContext) (classpath []string,
+	bootClasspath string, classJarSpecs, resourceJarSpecs []jarSpec) {
+
+	ctx.VisitDirectDeps(func(module blueprint.Module) {
+		otherName := ctx.OtherModuleName(module)
+		if javaDep, ok := module.(JavaDependency); ok {
+			if inList(otherName, j.properties.Java_libs) {
+				classpath = append(classpath, javaDep.ClasspathFile())
+			} else if inList(otherName, j.properties.Java_static_libs) {
+				classpath = append(classpath, javaDep.ClasspathFile())
+				classJarSpecs = append(classJarSpecs, javaDep.ClassJarSpecs()...)
+				resourceJarSpecs = append(resourceJarSpecs, javaDep.ResourceJarSpecs()...)
+			} else if otherName == j.BootClasspath(ctx) {
+				bootClasspath = javaDep.ClasspathFile()
+			} else {
+				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
+			}
+		} else {
+			ctx.ModuleErrorf("unknown dependency module type for %q", otherName)
+		}
+	})
+
+	return classpath, bootClasspath, classJarSpecs, resourceJarSpecs
+}
+
+func (j *javaBase) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
+	j.module.GenerateJavaBuildActions(ctx)
+}
+
+func (j *javaBase) GenerateJavaBuildActions(ctx common.AndroidModuleContext) {
+	flags := javaBuilderFlags{
+		javacFlags: strings.Join(j.properties.Javacflags, " "),
+	}
+
+	var javacDeps []string
+
+	srcFiles := j.properties.Srcs
+	srcFiles = pathtools.PrefixPaths(srcFiles, common.ModuleSrcDir(ctx))
+	srcFiles = common.ExpandGlobs(ctx, srcFiles)
+
+	classpath, bootClasspath, classJarSpecs, resourceJarSpecs := j.collectDeps(ctx)
+
+	if bootClasspath != "" {
+		flags.bootClasspath = "-bootclasspath " + bootClasspath
+		javacDeps = append(javacDeps, bootClasspath)
+	}
+
+	if len(classpath) > 0 {
+		flags.classpath = "-classpath " + strings.Join(classpath, ":")
+		javacDeps = append(javacDeps, classpath...)
+	}
+
+	// Compile java sources into .class files
+	classes := TransformJavaToClasses(ctx, srcFiles, flags, javacDeps)
+	if ctx.Failed() {
+		return
+	}
+
+	resourceJarSpecs = append(ResourceDirsToJarSpecs(ctx, j.properties.Resource_dirs), resourceJarSpecs...)
+	classJarSpecs = append([]jarSpec{classes}, classJarSpecs...)
+
+	manifest := j.properties.Manifest
+	if manifest != "" {
+		manifest = filepath.Join(common.ModuleSrcDir(ctx), manifest)
+	}
+
+	allJarSpecs := append([]jarSpec(nil), classJarSpecs...)
+	allJarSpecs = append(allJarSpecs, resourceJarSpecs...)
+
+	// Combine classes + resources into classes-full-debug.jar
+	outputFile := TransformClassesToJar(ctx, allJarSpecs, manifest)
+	if ctx.Failed() {
+		return
+	}
+	j.classJarSpecs = classJarSpecs
+	j.resourceJarSpecs = resourceJarSpecs
+	j.classpathFile = outputFile
+
+	if j.properties.Dex {
+		dxFlags := j.properties.Dxflags
+		if false /* emma enabled */ {
+			// If you instrument class files that have local variable debug information in
+			// them emma does not correctly maintain the local variable table.
+			// This will cause an error when you try to convert the class files for Android.
+			// The workaround here is to build different dex file here based on emma switch
+			// then later copy into classes.dex. When emma is on, dx is run with --no-locals
+			// option to remove local variable information
+			dxFlags = append(dxFlags, "--no-locals")
+		}
+
+		if ctx.Config().(Config).Getenv("NO_OPTIMIZE_DX") != "" {
+			dxFlags = append(dxFlags, "--no-optimize")
+		}
+
+		if ctx.Config().(Config).Getenv("GENERATE_DEX_DEBUG") != "" {
+			dxFlags = append(dxFlags,
+				"--debug",
+				"--verbose",
+				"--dump-to="+filepath.Join(common.ModuleOutDir(ctx), "classes.lst"),
+				"--dump-width=1000")
+		}
+
+		flags.dxFlags = strings.Join(dxFlags, " ")
+
+		// Compile classes.jar into classes.dex
+		dexFile := TransformClassesJarToDex(ctx, outputFile, flags)
+		if ctx.Failed() {
+			return
+		}
+
+		// Combine classes.dex + resources into javalib.jar
+		outputFile = TransformDexToJavaLib(ctx, resourceJarSpecs, dexFile)
+	}
+
+	j.installFile = ctx.InstallFileName("framework", ctx.ModuleName()+".jar", outputFile)
+}
+
+var _ JavaDependency = (*JavaLibrary)(nil)
+
+func (j *javaBase) ClasspathFile() string {
+	return j.classpathFile
+}
+
+func (j *javaBase) ClassJarSpecs() []jarSpec {
+	return j.classJarSpecs
+}
+
+func (j *javaBase) ResourceJarSpecs() []jarSpec {
+	return j.resourceJarSpecs
+}
+
+//
+// Java libraries (.jar file)
+//
+
+type JavaLibrary struct {
+	javaBase
+}
+
+func JavaLibraryFactory() (blueprint.Module, []interface{}) {
+	module := &JavaLibrary{}
+
+	module.properties.Dex = true
+
+	return NewJavaBase(&module.javaBase, module, common.HostAndDeviceSupported)
+}
+
+func JavaLibraryHostFactory() (blueprint.Module, []interface{}) {
+	module := &JavaLibrary{}
+
+	return NewJavaBase(&module.javaBase, module, common.HostSupported)
+}
+
+//
+// Java Binaries (.jar file plus wrapper script)
+//
+
+type JavaBinary struct {
+	JavaLibrary
+
+	binaryProperties struct {
+		// wrapper: installable script to execute the resulting jar
+		Wrapper string
+	}
+}
+
+func (j *JavaBinary) GenerateJavaBuildActions(ctx common.AndroidModuleContext) {
+	j.JavaLibrary.GenerateJavaBuildActions(ctx)
+
+	// Depend on the installed jar (j.installFile) so that the wrapper doesn't get executed by
+	// another build rule before the jar has been installed.
+	ctx.InstallFile("bin", filepath.Join(common.ModuleSrcDir(ctx), j.binaryProperties.Wrapper),
+		j.installFile)
+}
+
+func JavaBinaryFactory() (blueprint.Module, []interface{}) {
+	module := &JavaBinary{}
+
+	module.properties.Dex = true
+
+	return NewJavaBase(&module.javaBase, module, common.HostAndDeviceSupported, &module.binaryProperties)
+}
+
+func JavaBinaryHostFactory() (blueprint.Module, []interface{}) {
+	module := &JavaBinary{}
+
+	return NewJavaBase(&module.javaBase, module, common.HostSupported, &module.binaryProperties)
+}
+
+//
+// Java prebuilts
+//
+
+type JavaPrebuilt struct {
+	common.AndroidModuleBase
+
+	properties struct {
+		Srcs []string
+	}
+
+	classpathFile string
+}
+
+func (j *JavaPrebuilt) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
+	if len(j.properties.Srcs) != 1 {
+		ctx.ModuleErrorf("expected exactly one jar in srcs")
+		return
+	}
+	j.classpathFile = filepath.Join(common.ModuleSrcDir(ctx), j.properties.Srcs[0])
+}
+
+var _ JavaDependency = (*JavaPrebuilt)(nil)
+
+func (j *JavaPrebuilt) ClasspathFile() string {
+	return j.classpathFile
+}
+
+func (j *JavaPrebuilt) ClassJarSpecs() []jarSpec {
+	return nil
+}
+
+func (j *JavaPrebuilt) ResourceJarSpecs() []jarSpec {
+	return nil
+}
+
+func JavaPrebuiltFactory() (blueprint.Module, []interface{}) {
+	module := &JavaPrebuilt{}
+
+	return common.InitAndroidArchModule(module, common.HostAndDeviceSupported,
+		common.MultilibCommon, &module.properties)
+}
+
+func inList(s string, l []string) bool {
+	for _, e := range l {
+		if e == s {
+			return true
+		}
+	}
+	return false
+}
diff --git a/java/resources.go b/java/resources.go
new file mode 100644
index 0000000..2fbf54b
--- /dev/null
+++ b/java/resources.go
@@ -0,0 +1,45 @@
+// 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
+
+import (
+	"path/filepath"
+
+	"android/soong/common"
+)
+
+var resourceExcludes = []string{
+	"*.java",
+	"package.html",
+	"overview.html",
+	".*.swp",
+	".DS_Store",
+	"*~",
+}
+
+func ResourceDirsToJarSpecs(ctx common.AndroidModuleContext, dirs []string) []jarSpec {
+	jarSpecs := make([]jarSpec, len(dirs))
+
+	for i, dir := range dirs {
+		fileListFile := filepath.Join(common.ModuleOutDir(ctx), "res", dir, "resources.list")
+		depFile := fileListFile + ".d"
+		dir := filepath.Join(common.ModuleSrcDir(ctx), dir)
+		glob := filepath.Join(dir, "**/*")
+		common.GlobRule(ctx, glob, resourceExcludes, fileListFile, depFile)
+		jarSpecs[i] = jarSpec{fileListFile, dir}
+	}
+
+	return jarSpecs
+}
diff --git a/root.bp b/root.bp
index 00ee1f0..18e734f 100644
--- a/root.bp
+++ b/root.bp
@@ -3,8 +3,11 @@
     "build/blueprint",
     "build/soong",
     "bionic",
+    "dalvik",
     "external/*",
+    "libcore",
     "libnativehelper",
     "prebuilts/ndk",
+    "prebuilts/sdk",
     "system/core/*",
 ]