Ver código fonte

Add soong_build primary builder

Initial build logic for building android with soong.  It can build
a variety of C and C++ files for arm/arm64 and host.

Change-Id: I10eb37c2c2a50be6af1bb5fd568c0962b9476bf0
Colin Cross 9 anos atrás
pai
commit
3f40fa460d

+ 112 - 0
Blueprints

@@ -0,0 +1,112 @@
+//
+// WARNING: Modifying this file will NOT automatically regenerate build.ninja.in!
+//
+// Before modifying this file make sure minibp is up to date:
+//    1) "repo sync build/soong" to make sure you have the latest build.ninja.in
+//    2) build minibp, which builds automicatically through the normal build steps. For example:
+//
+// After modifying this file regenerate build.ninja.in and build your changes:
+//    1) In your build directory, execute "../bootstrap.bash -r" to regenerate build.ninja.in
+//    2) Build again
+//
+
+bootstrap_go_binary(
+    name = "soong_build",
+    deps = [
+        "blueprint",
+        "blueprint-bootstrap",
+        "soong-cc",
+        "soong-common",
+        "soong-config",
+    ],
+    srcs = [
+        "cmd/soong_build/main.go",
+    ],
+    primaryBuilder = true,
+)
+
+bootstrap_go_binary(
+    name = "soong_glob",
+    deps = [
+        "soong-glob",
+    ],
+    srcs = [
+        "cmd/soong_glob/soong_glob.go",
+    ],
+)
+
+bootstrap_go_package(
+    name = "soong-glob",
+    pkgPath = "android/soong/glob",
+    deps = [
+        "blueprint-deptools",
+    ],
+    srcs = [
+        "glob/glob.go",
+    ],
+)
+
+bootstrap_go_package(
+    name = "soong-common",
+    pkgPath = "android/soong/common",
+    deps = [
+        "blueprint",
+        "blueprint-bootstrap",
+        "soong-glob"
+    ],
+    srcs = [
+        "common/arch.go",
+        "common/defs.go",
+        "common/glob.go",
+        "common/module.go",
+        "common/paths.go",
+    ],
+)
+
+bootstrap_go_package(
+    name = "soong-config",
+    pkgPath = "android/soong/config",
+    deps = [
+        "blueprint",
+        "blueprint-bootstrap",
+        "soong-common",
+    ],
+    srcs = [
+        "config/config.go",
+    ],
+)
+
+bootstrap_go_package(
+    name = "soong-cc",
+    pkgPath = "android/soong/cc",
+    deps = [
+        "blueprint",
+        "blueprint-pathtools",
+        "soong-common",
+        "soong-config",
+    ],
+    srcs = [
+        "cc/builder.go",
+        "cc/cc.go",
+        "cc/clang.go",
+        "cc/toolchain.go",
+        "cc/util.go",
+
+        "cc/arm_device.go",
+        "cc/arm64_device.go",
+
+        "cc/x86_linux_host.go",
+    ],
+)
+
+toolchain_library(
+    name = "libatomic",
+)
+
+toolchain_library(
+    name = "libgcc",
+)
+
+toolchain_library(
+    name = "libgcov",
+)

+ 115 - 0
androidmk/cmd/androidmk/android.go

@@ -0,0 +1,115 @@
+package main
+
+import (
+	"android/soong/androidmk/parser"
+)
+
+const (
+	clear_vars                = "__android_mk_clear_vars"
+	build_shared_library      = "cc_library_shared"
+	build_static_library      = "cc_library_static"
+	build_host_static_library = "cc_library_host_static"
+	build_host_shared_library = "cc_library_host_shared"
+	build_executable          = "cc_binary"
+	build_host_executable     = "cc_binary_host"
+	build_native_test         = "cc_test"
+	build_prebuilt            = "prebuilt"
+)
+
+var stringProperties = map[string]string{
+	"LOCAL_MODULE":       "name",
+	"LOCAL_MODULE_STEM":  "stem",
+	"LOCAL_MODULE_CLASS": "class",
+	"LOCAL_CXX_STL":      "cxx_stl",
+	"LOCAL_STRIP_MODULE": "strip",
+	"LOCAL_MULTILIB":     "compile_multilib",
+}
+
+var listProperties = map[string]string{
+	"LOCAL_SRC_FILES":               "srcs",
+	"LOCAL_SHARED_LIBRARIES":        "shared_libs",
+	"LOCAL_STATIC_LIBRARIES":        "static_libs",
+	"LOCAL_WHOLE_STATIC_LIBRARIES":  "whole_static_libs",
+	"LOCAL_SYSTEM_SHARED_LIBRARIES": "system_shared_libs",
+	"LOCAL_C_INCLUDES":              "include_dirs",
+	"LOCAL_EXPORT_C_INCLUDE_DIRS":   "export_include_dirs",
+	"LOCAL_ASFLAGS":                 "asflags",
+	"LOCAL_CLANG_ASFLAGS":           "clang_asflags",
+	"LOCAL_CFLAGS":                  "cflags",
+	"LOCAL_CONLYFLAGS":              "conlyflags",
+	"LOCAL_CPPFLAGS":                "cppflags",
+	"LOCAL_LDFLAGS":                 "ldflags",
+	"LOCAL_REQUIRED_MODULES":        "required",
+	"LOCAL_MODULE_TAGS":             "tags",
+	"LOCAL_LDLIBS":                  "host_ldlibs",
+	"LOCAL_CLANG_CFLAGS":            "clang_cflags",
+}
+
+var boolProperties = map[string]string{
+	"LOCAL_IS_HOST_MODULE":          "host",
+	"LOCAL_CLANG":                   "clang",
+	"LOCAL_FORCE_STATIC_EXECUTABLE": "static",
+	"LOCAL_ADDRESS_SANITIZER":       "asan",
+	"LOCAL_NATIVE_COVERAGE":         "native_coverage",
+	"LOCAL_NO_CRT":                  "nocrt",
+	"LOCAL_ALLOW_UNDEFINED_SYMBOLS": "allow_undefined_symbols",
+	"LOCAL_RTTI_FLAG":               "rtti",
+}
+
+var propertySuffixes = []struct {
+	suffix string
+	class  string
+}{
+	{"arm", "arch"},
+	{"arm64", "arch"},
+	{"mips", "arch"},
+	{"mips64", "arch"},
+	{"x86", "arch"},
+	{"x86_64", "arch"},
+	{"32", "multilib"},
+	{"64", "multilib"},
+}
+
+var propertySuffixTranslations = map[string]string{
+	"32": "lib32",
+	"64": "lib64",
+}
+
+var conditionalTranslations = map[string]struct {
+	class  string
+	suffix string
+}{
+	"($(HOST_OS),darwin)":   {"host_os", "darwin"},
+	"($(HOST_OS), darwin)":  {"host_os", "darwin"},
+	"($(HOST_OS),windows)":  {"host_os", "windows"},
+	"($(HOST_OS), windows)": {"host_os", "windows"},
+}
+
+func mydir(args []string) string {
+	return "."
+}
+
+func androidScope() parser.Scope {
+	globalScope := parser.NewScope(nil)
+	globalScope.Set("CLEAR_VARS", clear_vars)
+	globalScope.Set("BUILD_HOST_EXECUTABLE", build_host_executable)
+	globalScope.Set("BUILD_SHARED_LIBRARY", build_shared_library)
+	globalScope.Set("BUILD_STATIC_LIBRARY", build_static_library)
+	globalScope.Set("BUILD_HOST_STATIC_LIBRARY", build_host_static_library)
+	globalScope.Set("BUILD_HOST_SHARED_LIBRARY", build_host_shared_library)
+	globalScope.Set("BUILD_NATIVE_TEST", build_native_test)
+	globalScope.Set("BUILD_EXECUTABLE", build_executable)
+	globalScope.Set("BUILD_PREBUILT", build_prebuilt)
+	globalScope.SetFunc("my-dir", mydir)
+
+	globalScope.Set("lib32", "lib32")
+	globalScope.Set("lib64", "lib64")
+	globalScope.Set("arm", "arm")
+	globalScope.Set("arm64", "arm64")
+	globalScope.Set("mips", "mips")
+	globalScope.Set("mips64", "mips64")
+	globalScope.Set("x86", "x86")
+	globalScope.Set("x86_64", "x86_64")
+
+	return globalScope
+}

+ 438 - 0
androidmk/cmd/androidmk/androidmk.go

@@ -0,0 +1,438 @@
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+	"text/scanner"
+
+	mkparser "android/soong/androidmk/parser"
+
+	bpparser "blueprint/parser"
+)
+
+// TODO: non-expanded variables with expressions
+
+type bpFile struct {
+	comments          []bpparser.Comment
+	defs              []bpparser.Definition
+	localAssignments  map[string]*bpparser.Property
+	globalAssignments map[string]*bpparser.Value
+	scope             mkparser.Scope
+	module            *bpparser.Module
+
+	pos            scanner.Position
+	prevLine, line int
+}
+
+func (f *bpFile) errorf(thing mkparser.MakeThing, s string, args ...interface{}) {
+	orig := thing.Dump()
+	s = fmt.Sprintf(s, args...)
+	f.comments = append(f.comments, bpparser.Comment{
+		Comment: fmt.Sprintf("// ANDROIDMK TRANSLATION ERROR: %s", s),
+		Pos:     f.pos,
+	})
+	lines := strings.Split(orig, "\n")
+	for _, l := range lines {
+		f.incPos()
+		f.comments = append(f.comments, bpparser.Comment{
+			Comment: "// " + l,
+			Pos:     f.pos,
+		})
+	}
+}
+
+func (f *bpFile) setPos(pos, endPos scanner.Position) {
+	f.pos = pos
+
+	f.line++
+	if f.pos.Line > f.prevLine+1 {
+		f.line++
+	}
+
+	f.pos.Line = f.line
+	f.prevLine = endPos.Line
+}
+
+func (f *bpFile) incPos() {
+	f.pos.Line++
+	f.line++
+	f.prevLine++
+}
+
+type conditional struct {
+	cond string
+	eq   bool
+}
+
+func main() {
+	b, err := ioutil.ReadFile(os.Args[1])
+	if err != nil {
+		fmt.Println(err.Error())
+		return
+	}
+
+	p := mkparser.NewParser(os.Args[1], bytes.NewBuffer(b))
+
+	things, errs := p.Parse()
+	if len(errs) > 0 {
+		for _, err := range errs {
+			fmt.Println("ERROR: ", err)
+		}
+		return
+	}
+
+	file := &bpFile{
+		scope:             androidScope(),
+		localAssignments:  make(map[string]*bpparser.Property),
+		globalAssignments: make(map[string]*bpparser.Value),
+	}
+
+	var conds []*conditional
+	var cond *conditional
+
+	for _, t := range things {
+		file.setPos(t.Pos(), t.EndPos())
+
+		if comment, ok := t.AsComment(); ok {
+			file.comments = append(file.comments, bpparser.Comment{
+				Pos:     file.pos,
+				Comment: "//" + comment.Comment,
+			})
+		} else if assignment, ok := t.AsAssignment(); ok {
+			handleAssignment(file, assignment, cond)
+		} else if directive, ok := t.AsDirective(); ok {
+			switch directive.Name {
+			case "include":
+				val := directive.Args.Value(file.scope)
+				switch val {
+				case build_shared_library, build_static_library,
+					build_executable, build_host_executable,
+					build_prebuilt, build_host_static_library,
+					build_host_shared_library, build_native_test:
+
+					handleModuleConditionals(file, directive, cond)
+					makeModule(file, val)
+				case clear_vars:
+					resetModule(file)
+				default:
+					file.errorf(directive, "unsupported include")
+					continue
+				}
+			case "ifeq", "ifneq":
+				args := directive.Args.Dump()
+				eq := directive.Name == "ifeq"
+				switch args {
+				case "($(HOST_OS),windows)", "($(HOST_OS), windows)",
+					"($(HOST_OS),darwin)", "($(HOST_OS), darwin)":
+					newCond := conditional{args, eq}
+					conds = append(conds, &newCond)
+					if cond == nil {
+						cond = &newCond
+					} else {
+						file.errorf(directive, "unsupported nested conditional")
+					}
+				default:
+					file.errorf(directive, "unsupported conditional")
+					conds = append(conds, nil)
+					continue
+				}
+			case "else":
+				if len(conds) == 0 {
+					file.errorf(directive, "missing if before else")
+					continue
+				} else if conds[len(conds)-1] == nil {
+					file.errorf(directive, "else from unsupported contitional")
+					continue
+				}
+				cond.eq = !cond.eq
+			case "endif":
+				if len(conds) == 0 {
+					file.errorf(directive, "missing if before endif")
+					continue
+				} else if conds[len(conds)-1] == nil {
+					file.errorf(directive, "endif from unsupported contitional")
+					conds = conds[:len(conds)-1]
+				} else {
+					if cond == conds[len(conds)-1] {
+						cond = nil
+					}
+					conds = conds[:len(conds)-1]
+				}
+			default:
+				file.errorf(directive, "unsupported directive")
+				continue
+			}
+		}
+	}
+
+	out, err := bpparser.Print(&bpparser.File{
+		Defs:     file.defs,
+		Comments: file.comments,
+	})
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+
+	fmt.Print(string(out))
+}
+
+func handleAssignment(file *bpFile, assignment mkparser.Assignment, c *conditional) {
+	if !assignment.Name.Const() {
+		file.errorf(assignment, "unsupported non-const variable name")
+		return
+	}
+
+	if assignment.Target != nil {
+		file.errorf(assignment, "unsupported target assignment")
+		return
+	}
+
+	name := assignment.Name.Value(nil)
+	suffix := ""
+	class := ""
+
+	if strings.HasPrefix(name, "LOCAL_") {
+		for _, v := range propertySuffixes {
+			s, c := v.suffix, v.class
+			if strings.HasSuffix(name, "_"+s) {
+				name = strings.TrimSuffix(name, "_"+s)
+				suffix = s
+				if s, ok := propertySuffixTranslations[s]; ok {
+					suffix = s
+				}
+				class = c
+				break
+			}
+		}
+
+		if c != nil {
+			if class != "" {
+				file.errorf(assignment, "suffix assignment inside conditional, skipping conditional")
+			} else {
+				if v, ok := conditionalTranslations[c.cond]; ok {
+					class = v.class
+					suffix = v.suffix
+					if !c.eq {
+						suffix = "not_" + suffix
+					}
+				} else {
+					panic("unknown conditional")
+				}
+			}
+		}
+	} else {
+		if c != nil {
+			eq := "eq"
+			if !c.eq {
+				eq = "neq"
+			}
+			file.errorf(assignment, "conditional %s %s on global assignment", eq, c.cond)
+		}
+	}
+
+	var err error
+	if prop, ok := stringProperties[name]; ok {
+		err = setVariable(file, assignment.Value, assignment.Type == "+=", prop, bpparser.String, true, class, suffix)
+	} else if prop, ok := listProperties[name]; ok {
+		err = setVariable(file, assignment.Value, assignment.Type == "+=", prop, bpparser.List, true, class, suffix)
+	} else if prop, ok := boolProperties[name]; ok {
+		err = setVariable(file, assignment.Value, assignment.Type == "+=", prop, bpparser.Bool, true, class, suffix)
+	} else {
+		if name == "LOCAL_PATH" {
+			// Nothing to do, except maybe avoid the "./" in paths?
+		} else if strings.HasPrefix(name, "LOCAL_") {
+			//setVariable(file, assignment, name, bpparser.String, true)
+			switch name {
+			case "LOCAL_ADDITIONAL_DEPENDENCIES":
+				// TODO: check for only .mk files?
+			default:
+				file.errorf(assignment, "unsupported assignment to %s", name)
+				return
+			}
+		} else {
+			err = setVariable(file, assignment.Value, assignment.Type == "+=", name, bpparser.List, false, class, suffix)
+		}
+	}
+	if err != nil {
+		file.errorf(assignment, err.Error())
+	}
+}
+
+func handleModuleConditionals(file *bpFile, directive mkparser.Directive, c *conditional) {
+	if c == nil {
+		return
+	}
+
+	if v, ok := conditionalTranslations[c.cond]; ok {
+		class := v.class
+		suffix := v.suffix
+		disabledSuffix := v.suffix
+		if !c.eq {
+			suffix = "not_" + suffix
+		} else {
+			disabledSuffix = "not_" + disabledSuffix
+		}
+
+		// Hoist all properties inside the condtional up to the top level
+		file.module.Properties = file.localAssignments[class+"___"+suffix].Value.MapValue
+		file.module.Properties = append(file.module.Properties, file.localAssignments[class])
+		file.localAssignments[class+"___"+suffix].Value.MapValue = nil
+		for i := range file.localAssignments[class].Value.MapValue {
+			if file.localAssignments[class].Value.MapValue[i].Name.Name == suffix {
+				file.localAssignments[class].Value.MapValue =
+					append(file.localAssignments[class].Value.MapValue[:i],
+						file.localAssignments[class].Value.MapValue[i+1:]...)
+			}
+		}
+
+		// Create a fake assignment with enabled = false
+		err := setVariable(file, mkparser.SimpleMakeString("true", file.pos), false,
+			"disabled", bpparser.Bool, true, class, disabledSuffix)
+		if err != nil {
+			file.errorf(directive, err.Error())
+		}
+	} else {
+		panic("unknown conditional")
+	}
+}
+
+func makeModule(file *bpFile, t string) {
+	file.module.Type = bpparser.Ident{
+		Name: t,
+		Pos:  file.module.LbracePos,
+	}
+	file.module.RbracePos = file.pos
+	file.defs = append(file.defs, file.module)
+}
+
+func resetModule(file *bpFile) {
+	file.module = &bpparser.Module{}
+	file.module.LbracePos = file.pos
+	file.localAssignments = make(map[string]*bpparser.Property)
+}
+
+func setVariable(file *bpFile, val *mkparser.MakeString, plusequals bool, name string,
+	typ bpparser.ValueType, local bool, class string, suffix string) error {
+
+	pos := file.pos
+
+	var oldValue *bpparser.Value
+	if local {
+		var oldProp *bpparser.Property
+		if class != "" {
+			oldProp = file.localAssignments[name+"___"+class+"___"+suffix]
+		} else {
+			oldProp = file.localAssignments[name]
+		}
+		if oldProp != nil {
+			oldValue = &oldProp.Value
+		}
+	} else {
+		oldValue = file.globalAssignments[name]
+	}
+
+	var exp *bpparser.Value
+	var err error
+	switch typ {
+	case bpparser.List:
+		exp, err = makeToListExpression(val)
+	case bpparser.String:
+		exp, err = makeToStringExpression(val)
+	case bpparser.Bool:
+		exp, err = makeToBoolExpression(val)
+	default:
+		panic("unknown type")
+	}
+
+	if err != nil {
+		return err
+	}
+
+	if local {
+		if oldValue != nil && plusequals {
+			val, err := addValues(oldValue, exp)
+			if err != nil {
+				return fmt.Errorf("unsupported addition: %s", err.Error())
+			}
+			val.Expression.Pos = pos
+			*oldValue = *val
+		} else if class == "" {
+			prop := &bpparser.Property{
+				Name:  bpparser.Ident{Name: name, Pos: pos},
+				Pos:   pos,
+				Value: *exp,
+			}
+			file.localAssignments[name] = prop
+			file.module.Properties = append(file.module.Properties, prop)
+		} else {
+			classProp := file.localAssignments[class]
+			if classProp == nil {
+				classProp = &bpparser.Property{
+					Name: bpparser.Ident{Name: class, Pos: pos},
+					Pos:  pos,
+					Value: bpparser.Value{
+						Type:     bpparser.Map,
+						MapValue: []*bpparser.Property{},
+					},
+				}
+				file.localAssignments[class] = classProp
+				file.module.Properties = append(file.module.Properties, classProp)
+			}
+
+			suffixProp := file.localAssignments[class+"___"+suffix]
+			if suffixProp == nil {
+				suffixProp = &bpparser.Property{
+					Name: bpparser.Ident{Name: suffix, Pos: pos},
+					Pos:  pos,
+					Value: bpparser.Value{
+						Type:     bpparser.Map,
+						MapValue: []*bpparser.Property{},
+					},
+				}
+				file.localAssignments[class+"___"+suffix] = suffixProp
+				classProp.Value.MapValue = append(classProp.Value.MapValue, suffixProp)
+			}
+
+			prop := &bpparser.Property{
+				Name:  bpparser.Ident{Name: name, Pos: pos},
+				Pos:   pos,
+				Value: *exp,
+			}
+			file.localAssignments[class+"___"+suffix+"___"+name] = prop
+			suffixProp.Value.MapValue = append(suffixProp.Value.MapValue, prop)
+		}
+	} else {
+		if oldValue != nil && plusequals {
+			a := &bpparser.Assignment{
+				Name: bpparser.Ident{
+					Name: name,
+					Pos:  pos,
+				},
+				Value:     *exp,
+				OrigValue: *exp,
+				Pos:       pos,
+				Assigner:  "+=",
+			}
+			file.defs = append(file.defs, a)
+		} else {
+			a := &bpparser.Assignment{
+				Name: bpparser.Ident{
+					Name: name,
+					Pos:  pos,
+				},
+				Value:     *exp,
+				OrigValue: *exp,
+				Pos:       pos,
+				Assigner:  "=",
+			}
+			file.globalAssignments[name] = &a.Value
+			file.defs = append(file.defs, a)
+		}
+	}
+
+	return nil
+}

+ 192 - 0
androidmk/cmd/androidmk/values.go

@@ -0,0 +1,192 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+
+	mkparser "android/soong/androidmk/parser"
+
+	bpparser "blueprint/parser"
+)
+
+func stringToStringValue(s string) *bpparser.Value {
+	return &bpparser.Value{
+		Type:        bpparser.String,
+		StringValue: s,
+	}
+}
+
+func addValues(val1, val2 *bpparser.Value) (*bpparser.Value, error) {
+	if val1.Type == bpparser.String && val2.Type == bpparser.List {
+		val1 = &bpparser.Value{
+			Type:      bpparser.List,
+			ListValue: []bpparser.Value{*val1},
+		}
+	} else if val2.Type == bpparser.String && val1.Type == bpparser.List {
+		val2 = &bpparser.Value{
+			Type:      bpparser.List,
+			ListValue: []bpparser.Value{*val1},
+		}
+	} else if val1.Type != val2.Type {
+		return nil, fmt.Errorf("cannot add mismatched types")
+	}
+
+	return &bpparser.Value{
+		Type: val1.Type,
+		Expression: &bpparser.Expression{
+			Operator: '+',
+			Args:     [2]bpparser.Value{*val1, *val2},
+		},
+	}, nil
+}
+
+func makeToStringExpression(ms *mkparser.MakeString) (*bpparser.Value, error) {
+	var val *bpparser.Value
+	var err error
+
+	if ms.Strings[0] != "" {
+		val = stringToStringValue(ms.Strings[0])
+	}
+
+	for i, s := range ms.Strings[1:] {
+		name := ms.Variables[i].Name
+		if !name.Const() {
+			return nil, fmt.Errorf("Unsupported non-const variable name %s", name.Dump())
+		}
+		tmp := &bpparser.Value{
+			Type:     bpparser.String,
+			Variable: name.Value(nil),
+		}
+
+		if val != nil {
+			val, err = addValues(val, tmp)
+			if err != nil {
+				return nil, err
+			}
+		} else {
+			val = tmp
+		}
+
+		if s != "" {
+			tmp := stringToStringValue(s)
+			val, err = addValues(val, tmp)
+			if err != nil {
+				return nil, err
+			}
+		}
+	}
+
+	return val, nil
+}
+
+func stringToListValue(s string) *bpparser.Value {
+	list := strings.Fields(s)
+	valList := make([]bpparser.Value, len(list))
+	for i, l := range list {
+		valList[i] = bpparser.Value{
+			Type:        bpparser.String,
+			StringValue: l,
+		}
+	}
+	return &bpparser.Value{
+		Type:      bpparser.List,
+		ListValue: valList,
+	}
+
+}
+
+func makeToListExpression(ms *mkparser.MakeString) (*bpparser.Value, error) {
+	fields := ms.Split(" \t")
+
+	var listOfListValues []*bpparser.Value
+
+	listValue := &bpparser.Value{
+		Type: bpparser.List,
+	}
+
+	for _, f := range fields {
+		if len(f.Variables) == 1 && f.Strings[0] == "" && f.Strings[1] == "" {
+			// Variable by itself, variable is probably a list
+			if !f.Variables[0].Name.Const() {
+				return nil, fmt.Errorf("unsupported non-const variable name")
+			}
+			if len(listValue.ListValue) > 0 {
+				listOfListValues = append(listOfListValues, listValue)
+			}
+			listOfListValues = append(listOfListValues, &bpparser.Value{
+				Type:     bpparser.List,
+				Variable: f.Variables[0].Name.Value(nil),
+			})
+			listValue = &bpparser.Value{
+				Type: bpparser.List,
+			}
+		} else {
+			s, err := makeToStringExpression(f)
+			if err != nil {
+				return nil, err
+			}
+			if s == nil {
+				continue
+			}
+
+			listValue.ListValue = append(listValue.ListValue, *s)
+		}
+	}
+
+	if len(listValue.ListValue) > 0 {
+		listOfListValues = append(listOfListValues, listValue)
+	}
+
+	if len(listOfListValues) == 0 {
+		return listValue, nil
+	}
+
+	val := listOfListValues[0]
+	for _, tmp := range listOfListValues[1:] {
+		var err error
+		val, err = addValues(val, tmp)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return val, nil
+}
+
+func stringToBoolValue(s string) (*bpparser.Value, error) {
+	var b bool
+	s = strings.TrimSpace(s)
+	switch s {
+	case "true":
+		b = true
+	case "false", "":
+		b = false
+	case "-frtti": // HACK for LOCAL_RTTI_VALUE
+		b = true
+	default:
+		return nil, fmt.Errorf("unexpected bool value %s", s)
+	}
+	return &bpparser.Value{
+		Type:      bpparser.Bool,
+		BoolValue: b,
+	}, nil
+}
+
+func makeToBoolExpression(ms *mkparser.MakeString) (*bpparser.Value, error) {
+	if !ms.Const() {
+		if len(ms.Variables) == 1 && ms.Strings[0] == "" && ms.Strings[1] == "" {
+			name := ms.Variables[0].Name
+			if !name.Const() {
+				return nil, fmt.Errorf("unsupported non-const variable name")
+			}
+			return &bpparser.Value{
+				Type:     bpparser.Bool,
+				Variable: name.Value(nil),
+			}, nil
+		} else {
+			return nil, fmt.Errorf("non-const bool expression %s", ms.Dump())
+		}
+	}
+
+	return stringToBoolValue(ms.Value(nil))
+}

+ 170 - 0
androidmk/parser/make_strings.go

@@ -0,0 +1,170 @@
+package parser
+
+import (
+	"strings"
+	"text/scanner"
+	"unicode"
+)
+
+// A MakeString is a string that may contain variable substitutions in it.
+// It can be considered as an alternating list of raw Strings and variable
+// substitutions, where the first and last entries in the list must be raw
+// Strings (possibly empty).  A MakeString that starts with a variable
+// will have an empty first raw string, and a MakeString that ends with a
+// variable will have an empty last raw string.  Two sequential Variables
+// will have an empty raw string between them.
+//
+// The MakeString is stored as two lists, a list of raw Strings and a list
+// of Variables.  The raw string list is always one longer than the variable
+// list.
+type MakeString struct {
+	Pos       scanner.Position
+	Strings   []string
+	Variables []Variable
+}
+
+func SimpleMakeString(s string, pos scanner.Position) *MakeString {
+	return &MakeString{
+		Pos:     pos,
+		Strings: []string{s},
+	}
+}
+
+func (ms *MakeString) appendString(s string) {
+	if len(ms.Strings) == 0 {
+		ms.Strings = []string{s}
+		return
+	} else {
+		ms.Strings[len(ms.Strings)-1] += s
+	}
+}
+
+func (ms *MakeString) appendVariable(v Variable) {
+	if len(ms.Strings) == 0 {
+		ms.Strings = []string{"", ""}
+		ms.Variables = []Variable{v}
+	} else {
+		ms.Strings = append(ms.Strings, "")
+		ms.Variables = append(ms.Variables, v)
+	}
+}
+
+func (ms *MakeString) appendMakeString(other *MakeString) {
+	last := len(ms.Strings) - 1
+	ms.Strings[last] += other.Strings[0]
+	ms.Strings = append(ms.Strings, other.Strings[1:]...)
+	ms.Variables = append(ms.Variables, other.Variables...)
+}
+
+func (ms *MakeString) Value(scope Scope) string {
+	if len(ms.Strings) == 0 {
+		return ""
+	} else {
+		ret := ms.Strings[0]
+		for i := range ms.Strings[1:] {
+			ret += ms.Variables[i].Value(scope)
+			ret += ms.Strings[i+1]
+		}
+		return ret
+	}
+}
+
+func (ms *MakeString) Dump() string {
+	if len(ms.Strings) == 0 {
+		return ""
+	} else {
+		ret := ms.Strings[0]
+		for i := range ms.Strings[1:] {
+			ret += ms.Variables[i].Dump()
+			ret += ms.Strings[i+1]
+		}
+		return ret
+	}
+}
+
+func (ms *MakeString) Const() bool {
+	return len(ms.Strings) <= 1
+}
+
+func (ms *MakeString) Empty() bool {
+	return len(ms.Strings) == 0 || (len(ms.Strings) == 1 && ms.Strings[0] == "")
+}
+
+func (ms *MakeString) Split(sep string) []*MakeString {
+	return ms.SplitN(sep, -1)
+}
+
+func (ms *MakeString) SplitN(sep string, n int) []*MakeString {
+	ret := []*MakeString{}
+
+	curMs := SimpleMakeString("", ms.Pos)
+
+	var i int
+	var s string
+	for i, s = range ms.Strings {
+		if n != 0 {
+			split := splitAnyN(s, sep, n)
+			if n != -1 {
+				if len(split) > n {
+					panic("oops!")
+				} else {
+					n -= len(split)
+				}
+			}
+			curMs.appendString(split[0])
+
+			for _, r := range split[1:] {
+				ret = append(ret, curMs)
+				curMs = SimpleMakeString(r, ms.Pos)
+			}
+		} else {
+			curMs.appendString(s)
+		}
+
+		if i < len(ms.Strings)-1 {
+			curMs.appendVariable(ms.Variables[i])
+		}
+	}
+
+	ret = append(ret, curMs)
+	return ret
+}
+
+func (ms *MakeString) TrimLeftSpaces() {
+	ms.Strings[0] = strings.TrimLeftFunc(ms.Strings[0], unicode.IsSpace)
+}
+
+func (ms *MakeString) TrimRightSpaces() {
+	last := len(ms.Strings) - 1
+	ms.Strings[last] = strings.TrimRightFunc(ms.Strings[last], unicode.IsSpace)
+}
+
+func (ms *MakeString) TrimRightOne() {
+	last := len(ms.Strings) - 1
+	if len(ms.Strings[last]) > 1 {
+		ms.Strings[last] = ms.Strings[last][0 : len(ms.Strings[last])-1]
+	}
+}
+
+func (ms *MakeString) EndsWith(ch rune) bool {
+	s := ms.Strings[len(ms.Strings)-1]
+	return s[len(s)-1] == uint8(ch)
+}
+
+func splitAnyN(s, sep string, n int) []string {
+	ret := []string{}
+	for n == -1 || n > 1 {
+		index := strings.IndexAny(s, sep)
+		if index >= 0 {
+			ret = append(ret, s[0:index])
+			s = s[index+1:]
+			if n > 0 {
+				n--
+			}
+		} else {
+			break
+		}
+	}
+	ret = append(ret, s)
+	return ret
+}

+ 96 - 0
androidmk/parser/make_strings_test.go

@@ -0,0 +1,96 @@
+package parser
+
+import (
+	"strings"
+	"testing"
+)
+
+var splitNTestCases = []struct {
+	in       *MakeString
+	expected []*MakeString
+	sep      string
+	n        int
+}{
+	{
+		in: &MakeString{
+			strings: []string{
+				"a b c",
+				"d e f",
+				" h i j",
+			},
+			variables: []Variable{
+				variable{name: SimpleMakeString("var1")},
+				variable{name: SimpleMakeString("var2")},
+			},
+		},
+		sep: " ",
+		n:   -1,
+		expected: []*MakeString{
+			SimpleMakeString("a"),
+			SimpleMakeString("b"),
+			&MakeString{
+				strings: []string{"c", "d"},
+				variables: []Variable{
+					variable{name: SimpleMakeString("var1")},
+				},
+			},
+			SimpleMakeString("e"),
+			&MakeString{
+				strings: []string{"f", ""},
+				variables: []Variable{
+					variable{name: SimpleMakeString("var2")},
+				},
+			},
+			SimpleMakeString("h"),
+			SimpleMakeString("i"),
+			SimpleMakeString("j"),
+		},
+	},
+	{
+		in: &MakeString{
+			strings: []string{
+				"a b c",
+				"d e f",
+				" h i j",
+			},
+			variables: []Variable{
+				variable{name: SimpleMakeString("var1")},
+				variable{name: SimpleMakeString("var2")},
+			},
+		},
+		sep: " ",
+		n:   3,
+		expected: []*MakeString{
+			SimpleMakeString("a"),
+			SimpleMakeString("b"),
+			&MakeString{
+				strings: []string{"c", "d e f", " h i j"},
+				variables: []Variable{
+					variable{name: SimpleMakeString("var1")},
+					variable{name: SimpleMakeString("var2")},
+				},
+			},
+		},
+	},
+}
+
+func TestMakeStringSplitN(t *testing.T) {
+	for _, test := range splitNTestCases {
+		got := test.in.SplitN(test.sep, test.n)
+		gotString := dumpArray(got)
+		expectedString := dumpArray(test.expected)
+		if gotString != expectedString {
+			t.Errorf("expected:\n%s\ngot:\n%s", expectedString, gotString)
+		}
+	}
+}
+
+func dumpArray(a []*MakeString) string {
+	ret := make([]string, len(a))
+
+	for i, s := range a {
+		ret[i] = s.Dump()
+	}
+
+	return strings.Join(ret, "|||")
+}

+ 142 - 0
androidmk/parser/makething.go

@@ -0,0 +1,142 @@
+package parser
+
+import (
+	"text/scanner"
+)
+
+type MakeThing interface {
+	AsAssignment() (Assignment, bool)
+	AsComment() (Comment, bool)
+	AsDirective() (Directive, bool)
+	AsRule() (Rule, bool)
+	AsVariable() (Variable, bool)
+	Dump() string
+	Pos() scanner.Position
+	EndPos() scanner.Position
+}
+
+type Assignment struct {
+	makeThing
+	Name   *MakeString
+	Value  *MakeString
+	Target *MakeString
+	Type   string
+}
+
+type Comment struct {
+	makeThing
+	Comment string
+}
+
+type Directive struct {
+	makeThing
+	Name string
+	Args *MakeString
+}
+
+type Rule struct {
+	makeThing
+	Target        *MakeString
+	Prerequisites *MakeString
+	Recipe        string
+}
+
+type Variable struct {
+	makeThing
+	Name *MakeString
+}
+
+type makeThing struct {
+	pos    scanner.Position
+	endPos scanner.Position
+}
+
+func (m makeThing) Pos() scanner.Position {
+	return m.pos
+}
+
+func (m makeThing) EndPos() scanner.Position {
+	return m.endPos
+}
+
+func (makeThing) AsAssignment() (a Assignment, ok bool) {
+	return
+}
+
+func (a Assignment) AsAssignment() (Assignment, bool) {
+	return a, true
+}
+
+func (a Assignment) Dump() string {
+	target := ""
+	if a.Target != nil {
+		target = a.Target.Dump() + ": "
+	}
+	return target + a.Name.Dump() + a.Type + a.Value.Dump()
+}
+
+func (makeThing) AsComment() (c Comment, ok bool) {
+	return
+}
+
+func (c Comment) AsComment() (Comment, bool) {
+	return c, true
+}
+
+func (c Comment) Dump() string {
+	return "#" + c.Comment
+}
+
+func (makeThing) AsDirective() (d Directive, ok bool) {
+	return
+}
+
+func (d Directive) AsDirective() (Directive, bool) {
+	return d, true
+}
+
+func (d Directive) Dump() string {
+	return d.Name + " " + d.Args.Dump()
+}
+
+func (makeThing) AsRule() (r Rule, ok bool) {
+	return
+}
+
+func (r Rule) AsRule() (Rule, bool) {
+	return r, true
+}
+
+func (r Rule) Dump() string {
+	recipe := ""
+	if r.Recipe != "" {
+		recipe = "\n" + r.Recipe
+	}
+	return "rule:       " + r.Target.Dump() + ": " + r.Prerequisites.Dump() + recipe
+}
+
+func (makeThing) AsVariable() (v Variable, ok bool) {
+	return
+}
+
+func (v Variable) AsVariable() (Variable, bool) {
+	return v, true
+}
+
+func (v Variable) Dump() string {
+	return "$(" + v.Name.Dump() + ")"
+}
+
+type byPosition []MakeThing
+
+func (s byPosition) Len() int {
+	return len(s)
+}
+
+func (s byPosition) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+
+func (s byPosition) Less(i, j int) bool {
+	return s[i].Pos().Offset < s[j].Pos().Offset
+}

+ 633 - 0
androidmk/parser/parser.go

@@ -0,0 +1,633 @@
+package parser
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"sort"
+	"text/scanner"
+)
+
+var errTooManyErrors = errors.New("too many errors")
+
+const maxErrors = 100
+
+type ParseError struct {
+	Err error
+	Pos scanner.Position
+}
+
+func (e *ParseError) Error() string {
+	return fmt.Sprintf("%s: %s", e.Pos, e.Err)
+}
+
+func (p *parser) Parse() ([]MakeThing, []error) {
+	defer func() {
+		if r := recover(); r != nil {
+			if r == errTooManyErrors {
+				return
+			}
+			panic(r)
+		}
+	}()
+
+	p.parseLines()
+	p.accept(scanner.EOF)
+	p.things = append(p.things, p.comments...)
+	sort.Sort(byPosition(p.things))
+
+	return p.things, p.errors
+}
+
+type parser struct {
+	scanner  scanner.Scanner
+	tok      rune
+	errors   []error
+	comments []MakeThing
+	things   []MakeThing
+}
+
+func NewParser(filename string, r io.Reader) *parser {
+	p := &parser{}
+	p.scanner.Init(r)
+	p.scanner.Error = func(sc *scanner.Scanner, msg string) {
+		p.errorf(msg)
+	}
+	p.scanner.Whitespace = 0
+	p.scanner.IsIdentRune = func(ch rune, i int) bool {
+		return ch > 0 && ch != ':' && ch != '#' && ch != '=' && ch != '+' && ch != '$' &&
+			ch != '\\' && ch != '(' && ch != ')' && ch != '{' && ch != '}' && ch != ';' &&
+			ch != '|' && ch != '?' && ch != '\r' && !isWhitespace(ch)
+	}
+	p.scanner.Mode = scanner.ScanIdents
+	p.scanner.Filename = filename
+	p.next()
+	return p
+}
+
+func (p *parser) errorf(format string, args ...interface{}) {
+	pos := p.scanner.Position
+	if !pos.IsValid() {
+		pos = p.scanner.Pos()
+	}
+	err := &ParseError{
+		Err: fmt.Errorf(format, args...),
+		Pos: pos,
+	}
+	p.errors = append(p.errors, err)
+	if len(p.errors) >= maxErrors {
+		panic(errTooManyErrors)
+	}
+}
+
+func (p *parser) accept(toks ...rune) bool {
+	for _, tok := range toks {
+		if p.tok != tok {
+			p.errorf("expected %s, found %s", scanner.TokenString(tok),
+				scanner.TokenString(p.tok))
+			return false
+		}
+		p.next()
+	}
+	return true
+}
+
+func (p *parser) next() {
+	if p.tok != scanner.EOF {
+		p.tok = p.scanner.Scan()
+		for p.tok == '\r' {
+			p.tok = p.scanner.Scan()
+		}
+	}
+	return
+}
+
+func (p *parser) parseLines() {
+	for {
+		p.ignoreWhitespace()
+
+		if p.parseDirective() {
+			continue
+		}
+
+		ident, _ := p.parseExpression('=', '?', ':', '#', '\n')
+
+		p.ignoreSpaces()
+
+		switch p.tok {
+		case '?':
+			p.accept('?')
+			if p.tok == '=' {
+				p.parseAssignment("?=", nil, ident)
+			} else {
+				p.errorf("expected = after ?")
+			}
+		case '+':
+			p.accept('+')
+			if p.tok == '=' {
+				p.parseAssignment("+=", nil, ident)
+			} else {
+				p.errorf("expected = after +")
+			}
+		case ':':
+			p.accept(':')
+			switch p.tok {
+			case '=':
+				p.parseAssignment(":=", nil, ident)
+			default:
+				p.parseRule(ident)
+			}
+		case '=':
+			p.parseAssignment("=", nil, ident)
+		case '#', '\n', scanner.EOF:
+			ident.TrimRightSpaces()
+			if v, ok := toVariable(ident); ok {
+				p.things = append(p.things, v)
+			} else if !ident.Empty() {
+				p.errorf("expected directive, rule, or assignment after ident " + ident.Dump())
+			}
+			switch p.tok {
+			case scanner.EOF:
+				return
+			case '\n':
+				p.accept('\n')
+			case '#':
+				p.parseComment()
+			}
+		default:
+			p.errorf("expected assignment or rule definition, found %s\n",
+				p.scanner.TokenText())
+			return
+		}
+	}
+}
+
+func (p *parser) parseDirective() bool {
+	if p.tok != scanner.Ident || !isDirective(p.scanner.TokenText()) {
+		return false
+	}
+
+	d := p.scanner.TokenText()
+	pos := p.scanner.Position
+	endPos := pos
+	p.accept(scanner.Ident)
+
+	expression := SimpleMakeString("", pos)
+
+	switch d {
+	case "endif", "endef", "else":
+		// Nothing
+	case "define":
+		expression = p.parseDefine()
+	default:
+		p.ignoreSpaces()
+		expression, endPos = p.parseExpression()
+	}
+
+	p.things = append(p.things, Directive{
+		makeThing: makeThing{
+			pos:    pos,
+			endPos: endPos,
+		},
+		Name: d,
+		Args: expression,
+	})
+	return true
+}
+
+func (p *parser) parseDefine() *MakeString {
+	value := SimpleMakeString("", p.scanner.Position)
+
+loop:
+	for {
+		switch p.tok {
+		case scanner.Ident:
+			if p.scanner.TokenText() == "endef" {
+				p.accept(scanner.Ident)
+				break loop
+			}
+			value.appendString(p.scanner.TokenText())
+			p.accept(scanner.Ident)
+		case '\\':
+			p.parseEscape()
+			switch p.tok {
+			case '\n':
+				value.appendString(" ")
+			case scanner.EOF:
+				p.errorf("expected escaped character, found %s",
+					scanner.TokenString(p.tok))
+				break loop
+			default:
+				value.appendString(`\` + string(p.tok))
+			}
+			p.accept(p.tok)
+		//TODO: handle variables inside defines?  result depends if
+		//define is used in make or rule context
+		//case '$':
+		//	variable := p.parseVariable()
+		//	value.appendVariable(variable)
+		case scanner.EOF:
+			p.errorf("unexpected EOF while looking for endef")
+			break loop
+		default:
+			value.appendString(p.scanner.TokenText())
+			p.accept(p.tok)
+		}
+	}
+
+	return value
+}
+
+func (p *parser) parseEscape() {
+	p.scanner.Mode = 0
+	p.accept('\\')
+	p.scanner.Mode = scanner.ScanIdents
+}
+
+func (p *parser) parseExpression(end ...rune) (*MakeString, scanner.Position) {
+	value := SimpleMakeString("", p.scanner.Position)
+
+	endParen := false
+	for _, r := range end {
+		if r == ')' {
+			endParen = true
+		}
+	}
+	parens := 0
+
+	endPos := p.scanner.Position
+
+loop:
+	for {
+		if endParen && parens > 0 && p.tok == ')' {
+			parens--
+			value.appendString(")")
+			endPos = p.scanner.Position
+			p.accept(')')
+			continue
+		}
+
+		for _, r := range end {
+			if p.tok == r {
+				break loop
+			}
+		}
+
+		switch p.tok {
+		case '\n':
+			break loop
+		case scanner.Ident:
+			value.appendString(p.scanner.TokenText())
+			endPos = p.scanner.Position
+			p.accept(scanner.Ident)
+		case '\\':
+			p.parseEscape()
+			switch p.tok {
+			case '\n':
+				value.appendString(" ")
+			case scanner.EOF:
+				p.errorf("expected escaped character, found %s",
+					scanner.TokenString(p.tok))
+				return value, endPos
+			default:
+				value.appendString(`\` + string(p.tok))
+			}
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		case '#':
+			p.parseComment()
+			break loop
+		case '$':
+			var variable Variable
+			variable, endPos = p.parseVariable()
+			value.appendVariable(variable)
+		case scanner.EOF:
+			break loop
+		case '(':
+			if endParen {
+				parens++
+			}
+			value.appendString("(")
+			endPos = p.scanner.Position
+			p.accept('(')
+		default:
+			value.appendString(p.scanner.TokenText())
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		}
+	}
+
+	if parens > 0 {
+		p.errorf("expected closing paren %s", value.Dump())
+	}
+	return value, endPos
+}
+
+func (p *parser) parseVariable() (Variable, scanner.Position) {
+	pos := p.scanner.Position
+	endPos := pos
+	p.accept('$')
+	var name *MakeString
+	switch p.tok {
+	case '(':
+		return p.parseBracketedVariable('(', ')', pos)
+	case '{':
+		return p.parseBracketedVariable('{', '}', pos)
+	case '$':
+		name = SimpleMakeString("__builtin_dollar", scanner.Position{})
+	case scanner.EOF:
+		p.errorf("expected variable name, found %s",
+			scanner.TokenString(p.tok))
+	default:
+		name, endPos = p.parseExpression(variableNameEndRunes...)
+	}
+
+	return p.nameToVariable(name, pos, endPos), endPos
+}
+
+func (p *parser) parseBracketedVariable(start, end rune, pos scanner.Position) (Variable, scanner.Position) {
+	p.accept(start)
+	name, endPos := p.parseExpression(end)
+	p.accept(end)
+	return p.nameToVariable(name, pos, endPos), endPos
+}
+
+func (p *parser) nameToVariable(name *MakeString, pos, endPos scanner.Position) Variable {
+	return Variable{
+		makeThing: makeThing{
+			pos:    pos,
+			endPos: endPos,
+		},
+		Name: name,
+	}
+}
+
+func (p *parser) parseRule(target *MakeString) {
+	prerequisites, newLine := p.parseRulePrerequisites(target)
+
+	recipe := ""
+	endPos := p.scanner.Position
+loop:
+	for {
+		if newLine {
+			if p.tok == '\t' {
+				endPos = p.scanner.Position
+				p.accept('\t')
+				newLine = false
+				continue loop
+			} else if p.parseDirective() {
+				newLine = false
+				continue
+			} else {
+				break loop
+			}
+		}
+
+		newLine = false
+		switch p.tok {
+		case '\\':
+			p.parseEscape()
+			recipe += string(p.tok)
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		case '\n':
+			newLine = true
+			recipe += "\n"
+			endPos = p.scanner.Position
+			p.accept('\n')
+		case scanner.EOF:
+			break loop
+		default:
+			recipe += p.scanner.TokenText()
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		}
+	}
+
+	if prerequisites != nil {
+		p.things = append(p.things, Rule{
+			makeThing: makeThing{
+				pos:    target.Pos,
+				endPos: endPos,
+			},
+			Target:        target,
+			Prerequisites: prerequisites,
+			Recipe:        recipe,
+		})
+	}
+}
+
+func (p *parser) parseRulePrerequisites(target *MakeString) (*MakeString, bool) {
+	newLine := false
+
+	p.ignoreSpaces()
+
+	prerequisites, _ := p.parseExpression('#', '\n', ';', ':', '=')
+
+	switch p.tok {
+	case '\n':
+		p.accept('\n')
+		newLine = true
+	case '#':
+		p.parseComment()
+		newLine = true
+	case ';':
+		p.accept(';')
+	case ':':
+		p.accept(':')
+		if p.tok == '=' {
+			p.parseAssignment(":=", target, prerequisites)
+			return nil, true
+		} else {
+			more, _ := p.parseExpression('#', '\n', ';')
+			prerequisites.appendMakeString(more)
+		}
+	case '=':
+		p.parseAssignment("=", target, prerequisites)
+		return nil, true
+	default:
+		p.errorf("unexpected token %s after rule prerequisites", scanner.TokenString(p.tok))
+	}
+
+	return prerequisites, newLine
+}
+
+func (p *parser) parseComment() {
+	pos := p.scanner.Position
+	p.accept('#')
+	comment := ""
+	endPos := pos
+loop:
+	for {
+		switch p.tok {
+		case '\\':
+			p.parseEscape()
+			if p.tok == '\n' {
+				comment += "\n"
+			} else {
+				comment += "\\" + p.scanner.TokenText()
+			}
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		case '\n':
+			endPos = p.scanner.Position
+			p.accept('\n')
+			break loop
+		case scanner.EOF:
+			break loop
+		default:
+			comment += p.scanner.TokenText()
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		}
+	}
+
+	p.comments = append(p.comments, Comment{
+		makeThing: makeThing{
+			pos:    pos,
+			endPos: endPos,
+		},
+		Comment: comment,
+	})
+}
+
+func (p *parser) parseAssignment(t string, target *MakeString, ident *MakeString) {
+	// The value of an assignment is everything including and after the first
+	// non-whitespace character after the = until the end of the logical line,
+	// which may included escaped newlines
+	p.accept('=')
+	value, endPos := p.parseExpression()
+	value.TrimLeftSpaces()
+	if ident.EndsWith('+') && t == "=" {
+		ident.TrimRightOne()
+		t = "+="
+	}
+
+	ident.TrimRightSpaces()
+
+	p.things = append(p.things, Assignment{
+		makeThing: makeThing{
+			pos:    ident.Pos,
+			endPos: endPos,
+		},
+		Name:   ident,
+		Value:  value,
+		Target: target,
+		Type:   t,
+	})
+}
+
+type androidMkModule struct {
+	assignments map[string]string
+}
+
+type androidMkFile struct {
+	assignments map[string]string
+	modules     []androidMkModule
+	includes    []string
+}
+
+var directives = [...]string{
+	"define",
+	"else",
+	"endef",
+	"endif",
+	"ifdef",
+	"ifeq",
+	"ifndef",
+	"ifneq",
+	"include",
+	"-include",
+}
+
+var functions = [...]string{
+	"abspath",
+	"addprefix",
+	"addsuffix",
+	"basename",
+	"dir",
+	"notdir",
+	"subst",
+	"suffix",
+	"filter",
+	"filter-out",
+	"findstring",
+	"firstword",
+	"flavor",
+	"join",
+	"lastword",
+	"patsubst",
+	"realpath",
+	"shell",
+	"sort",
+	"strip",
+	"wildcard",
+	"word",
+	"wordlist",
+	"words",
+	"origin",
+	"foreach",
+	"call",
+	"info",
+	"error",
+	"warning",
+	"if",
+	"or",
+	"and",
+	"value",
+	"eval",
+	"file",
+}
+
+func init() {
+	sort.Strings(directives[:])
+	sort.Strings(functions[:])
+}
+
+func isDirective(s string) bool {
+	for _, d := range directives {
+		if s == d {
+			return true
+		} else if s < d {
+			return false
+		}
+	}
+	return false
+}
+
+func isFunctionName(s string) bool {
+	for _, f := range functions {
+		if s == f {
+			return true
+		} else if s < f {
+			return false
+		}
+	}
+	return false
+}
+
+func isWhitespace(ch rune) bool {
+	return ch == ' ' || ch == '\t' || ch == '\n'
+}
+
+func isValidVariableRune(ch rune) bool {
+	return ch != scanner.Ident && ch != ':' && ch != '=' && ch != '#'
+}
+
+var whitespaceRunes = []rune{' ', '\t', '\n'}
+var variableNameEndRunes = append([]rune{':', '=', '#', ')', '}'}, whitespaceRunes...)
+
+func (p *parser) ignoreSpaces() int {
+	skipped := 0
+	for p.tok == ' ' || p.tok == '\t' {
+		p.accept(p.tok)
+		skipped++
+	}
+	return skipped
+}
+
+func (p *parser) ignoreWhitespace() {
+	for isWhitespace(p.tok) {
+		p.accept(p.tok)
+	}
+}

+ 88 - 0
androidmk/parser/scope.go

@@ -0,0 +1,88 @@
+package parser
+
+import "strings"
+
+type Scope interface {
+	Get(name string) string
+	Set(name, value string)
+	Call(name string, args []string) string
+	SetFunc(name string, f func([]string) string)
+}
+
+type scope struct {
+	variables map[string]string
+	functions map[string]func([]string) string
+	parent    Scope
+}
+
+func (s *scope) Get(name string) string {
+	if val, ok := s.variables[name]; ok {
+		return val
+	} else if s.parent != nil {
+		return s.parent.Get(name)
+	} else if val, ok := builtinScope[name]; ok {
+		return val
+	} else {
+		return "<'" + name + "' unset>"
+	}
+}
+
+func (s *scope) Set(name, value string) {
+	s.variables[name] = value
+}
+
+func (s *scope) Call(name string, args []string) string {
+	if f, ok := s.functions[name]; ok {
+		return f(args)
+	}
+
+	return "<func:'" + name + "' unset>"
+}
+
+func (s *scope) SetFunc(name string, f func([]string) string) {
+	s.functions[name] = f
+}
+
+func NewScope(parent Scope) Scope {
+	return &scope{
+		variables: make(map[string]string),
+		functions: make(map[string]func([]string) string),
+		parent:    parent,
+	}
+}
+
+var builtinScope map[string]string
+
+func init() {
+	builtinScope := make(map[string]string)
+	builtinScope["__builtin_dollar"] = "$"
+}
+
+func (v Variable) Value(scope Scope) string {
+	f := v.Name.SplitN(" \t", 2)
+	if len(f) > 1 && f[0].Const() {
+		fname := f[0].Value(nil)
+		if isFunctionName(fname) {
+			args := f[1].Split(",")
+			argVals := make([]string, len(args))
+			for i, a := range args {
+				argVals[i] = a.Value(scope)
+			}
+
+			if fname == "call" {
+				return scope.Call(argVals[0], argVals[1:])
+			} else {
+				return "__builtin_func:" + fname + " " + strings.Join(argVals, " ")
+			}
+		}
+	}
+
+	return scope.Get(v.Name.Value(scope))
+}
+
+func toVariable(ms *MakeString) (Variable, bool) {
+	if len(ms.Variables) == 1 && ms.Strings[0] == "" && ms.Strings[1] == "" {
+		return ms.Variables[0], true
+	}
+	return Variable{}, false
+}

+ 162 - 3
build.ninja.in

@@ -50,6 +50,7 @@ rule g.bootstrap.link
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint
+# Variant:
 # Type:    bootstrap_go_package
 # Factory: blueprint/bootstrap.newGoPackageModule
 # Defined: build/blueprint/Blueprints:1:1
@@ -74,6 +75,7 @@ default .bootstrap/blueprint/pkg/blueprint.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint-bootstrap
+# Variant:
 # Type:    bootstrap_go_package
 # Factory: blueprint/bootstrap.newGoPackageModule
 # Defined: build/blueprint/Blueprints:55:1
@@ -96,6 +98,7 @@ default .bootstrap/blueprint-bootstrap/pkg/blueprint/bootstrap.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint-deptools
+# Variant:
 # Type:    bootstrap_go_package
 # Factory: blueprint/bootstrap.newGoPackageModule
 # Defined: build/blueprint/Blueprints:34:1
@@ -108,6 +111,7 @@ default .bootstrap/blueprint-deptools/pkg/blueprint/deptools.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint-parser
+# Variant:
 # Type:    bootstrap_go_package
 # Factory: blueprint/bootstrap.newGoPackageModule
 # Defined: build/blueprint/Blueprints:23:1
@@ -123,6 +127,7 @@ default .bootstrap/blueprint-parser/pkg/blueprint/parser.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint-pathtools
+# Variant:
 # Type:    bootstrap_go_package
 # Factory: blueprint/bootstrap.newGoPackageModule
 # Defined: build/blueprint/Blueprints:40:1
@@ -136,6 +141,7 @@ default .bootstrap/blueprint-pathtools/pkg/blueprint/pathtools.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  blueprint-proptools
+# Variant:
 # Type:    bootstrap_go_package
 # Factory: blueprint/bootstrap.newGoPackageModule
 # Defined: build/blueprint/Blueprints:49:1
@@ -148,6 +154,7 @@ default .bootstrap/blueprint-proptools/pkg/blueprint/proptools.a
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  bpfmt
+# Variant:
 # Type:    bootstrap_go_binary
 # Factory: blueprint/bootstrap.newGoBinaryModule
 # Defined: build/blueprint/Blueprints:81:1
@@ -170,6 +177,7 @@ default .bootstrap/bin/bpfmt
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  bpmodify
+# Variant:
 # Type:    bootstrap_go_binary
 # Factory: blueprint/bootstrap.newGoBinaryModule
 # Defined: build/blueprint/Blueprints:87:1
@@ -192,6 +200,7 @@ default .bootstrap/bin/bpmodify
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Module:  minibp
+# Variant:
 # Type:    bootstrap_go_binary
 # Factory: blueprint/bootstrap.newGoBinaryModule
 # Defined: build/blueprint/Blueprints:72:1
@@ -217,14 +226,163 @@ default .bootstrap/minibp/obj/a.out
 build .bootstrap/bin/minibp: g.bootstrap.cp .bootstrap/minibp/obj/a.out
 default .bootstrap/bin/minibp
 
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Module:  soong-cc
+# Variant:
+# Type:    bootstrap_go_package
+# Factory: blueprint/bootstrap.newGoPackageModule
+# Defined: build/soong/Blueprints:79:1
+
+build .bootstrap/soong-cc/pkg/android/soong/cc.a: g.bootstrap.gc $
+        ${g.bootstrap.srcDir}/build/soong/cc/builder.go $
+        ${g.bootstrap.srcDir}/build/soong/cc/cc.go $
+        ${g.bootstrap.srcDir}/build/soong/cc/clang.go $
+        ${g.bootstrap.srcDir}/build/soong/cc/toolchain.go $
+        ${g.bootstrap.srcDir}/build/soong/cc/util.go $
+        ${g.bootstrap.srcDir}/build/soong/cc/arm_device.go $
+        ${g.bootstrap.srcDir}/build/soong/cc/arm64_device.go $
+        ${g.bootstrap.srcDir}/build/soong/cc/x86_linux_host.go | $
+        ${g.bootstrap.gcCmd} $
+        .bootstrap/blueprint-parser/pkg/blueprint/parser.a $
+        .bootstrap/blueprint-proptools/pkg/blueprint/proptools.a $
+        .bootstrap/blueprint/pkg/blueprint.a $
+        .bootstrap/blueprint-pathtools/pkg/blueprint/pathtools.a $
+        .bootstrap/blueprint-deptools/pkg/blueprint/deptools.a $
+        .bootstrap/blueprint-bootstrap/pkg/blueprint/bootstrap.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-glob/pkg -I .bootstrap/soong-common/pkg -I .bootstrap/soong-config/pkg
+    pkgPath = android/soong/cc
+default .bootstrap/soong-cc/pkg/android/soong/cc.a
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Module:  soong-common
+# Variant:
+# Type:    bootstrap_go_package
+# Factory: blueprint/bootstrap.newGoPackageModule
+# Defined: build/soong/Blueprints:49:1
+
+build .bootstrap/soong-common/pkg/android/soong/common.a: g.bootstrap.gc $
+        ${g.bootstrap.srcDir}/build/soong/common/arch.go $
+        ${g.bootstrap.srcDir}/build/soong/common/defs.go $
+        ${g.bootstrap.srcDir}/build/soong/common/glob.go $
+        ${g.bootstrap.srcDir}/build/soong/common/module.go $
+        ${g.bootstrap.srcDir}/build/soong/common/paths.go | $
+        ${g.bootstrap.gcCmd} $
+        .bootstrap/blueprint-parser/pkg/blueprint/parser.a $
+        .bootstrap/blueprint-proptools/pkg/blueprint/proptools.a $
+        .bootstrap/blueprint/pkg/blueprint.a $
+        .bootstrap/blueprint-deptools/pkg/blueprint/deptools.a $
+        .bootstrap/blueprint-pathtools/pkg/blueprint/pathtools.a $
+        .bootstrap/blueprint-bootstrap/pkg/blueprint/bootstrap.a $
+        .bootstrap/soong-glob/pkg/android/soong/glob.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-glob/pkg
+    pkgPath = android/soong/common
+default .bootstrap/soong-common/pkg/android/soong/common.a
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Module:  soong-config
+# Variant:
+# Type:    bootstrap_go_package
+# Factory: blueprint/bootstrap.newGoPackageModule
+# Defined: build/soong/Blueprints:66:1
+
+build .bootstrap/soong-config/pkg/android/soong/config.a: g.bootstrap.gc $
+        ${g.bootstrap.srcDir}/build/soong/config/config.go | $
+        ${g.bootstrap.gcCmd} $
+        .bootstrap/blueprint-parser/pkg/blueprint/parser.a $
+        .bootstrap/blueprint-proptools/pkg/blueprint/proptools.a $
+        .bootstrap/blueprint/pkg/blueprint.a $
+        .bootstrap/blueprint-deptools/pkg/blueprint/deptools.a $
+        .bootstrap/blueprint-pathtools/pkg/blueprint/pathtools.a $
+        .bootstrap/blueprint-bootstrap/pkg/blueprint/bootstrap.a $
+        .bootstrap/soong-glob/pkg/android/soong/glob.a $
+        .bootstrap/soong-common/pkg/android/soong/common.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-glob/pkg -I .bootstrap/soong-common/pkg
+    pkgPath = android/soong/config
+default .bootstrap/soong-config/pkg/android/soong/config.a
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Module:  soong-glob
+# Variant:
+# Type:    bootstrap_go_package
+# Factory: blueprint/bootstrap.newGoPackageModule
+# Defined: build/soong/Blueprints:38:1
+
+build .bootstrap/soong-glob/pkg/android/soong/glob.a: g.bootstrap.gc $
+        ${g.bootstrap.srcDir}/build/soong/glob/glob.go | ${g.bootstrap.gcCmd} $
+        .bootstrap/blueprint-deptools/pkg/blueprint/deptools.a
+    incFlags = -I .bootstrap/blueprint-deptools/pkg
+    pkgPath = android/soong/glob
+default .bootstrap/soong-glob/pkg/android/soong/glob.a
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Module:  soong_build
+# Variant:
+# Type:    bootstrap_go_binary
+# Factory: blueprint/bootstrap.newGoBinaryModule
+# Defined: build/soong/Blueprints:13:1
+
+build .bootstrap/soong_build/obj/soong_build.a: g.bootstrap.gc $
+        ${g.bootstrap.srcDir}/build/soong/cmd/soong_build/main.go | $
+        ${g.bootstrap.gcCmd} $
+        .bootstrap/blueprint-parser/pkg/blueprint/parser.a $
+        .bootstrap/blueprint-proptools/pkg/blueprint/proptools.a $
+        .bootstrap/blueprint/pkg/blueprint.a $
+        .bootstrap/blueprint-deptools/pkg/blueprint/deptools.a $
+        .bootstrap/blueprint-pathtools/pkg/blueprint/pathtools.a $
+        .bootstrap/blueprint-bootstrap/pkg/blueprint/bootstrap.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 $
+        .bootstrap/soong-cc/pkg/android/soong/cc.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-glob/pkg -I .bootstrap/soong-common/pkg -I .bootstrap/soong-config/pkg -I .bootstrap/soong-cc/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-glob/pkg -L .bootstrap/soong-common/pkg -L .bootstrap/soong-config/pkg -L .bootstrap/soong-cc/pkg
+default .bootstrap/soong_build/obj/a.out
+
+build .bootstrap/bin/soong_build: g.bootstrap.cp $
+        .bootstrap/soong_build/obj/a.out
+default .bootstrap/bin/soong_build
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Module:  soong_glob
+# Variant:
+# Type:    bootstrap_go_binary
+# Factory: blueprint/bootstrap.newGoBinaryModule
+# Defined: build/soong/Blueprints:28:1
+
+build .bootstrap/soong_glob/obj/soong_glob.a: g.bootstrap.gc $
+        ${g.bootstrap.srcDir}/build/soong/cmd/soong_glob/soong_glob.go | $
+        ${g.bootstrap.gcCmd} $
+        .bootstrap/blueprint-deptools/pkg/blueprint/deptools.a $
+        .bootstrap/soong-glob/pkg/android/soong/glob.a
+    incFlags = -I .bootstrap/blueprint-deptools/pkg -I .bootstrap/soong-glob/pkg
+    pkgPath = soong_glob
+default .bootstrap/soong_glob/obj/soong_glob.a
+
+build .bootstrap/soong_glob/obj/a.out: g.bootstrap.link $
+        .bootstrap/soong_glob/obj/soong_glob.a | ${g.bootstrap.linkCmd}
+    libDirFlags = -L .bootstrap/blueprint-deptools/pkg -L .bootstrap/soong-glob/pkg
+default .bootstrap/soong_glob/obj/a.out
+
+build .bootstrap/bin/soong_glob: g.bootstrap.cp $
+        .bootstrap/soong_glob/obj/a.out
+default .bootstrap/bin/soong_glob
+
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Singleton: bootstrap
 # Factory:   blueprint/bootstrap.newSingleton
 
 rule s.bootstrap.bigbp
-    command = .bootstrap/bin/minibp -p -d .bootstrap/main.ninja.in.d -m ${g.bootstrap.bootstrapManifest} -o ${out} ${in}
+    command = .bootstrap/bin/soong_build  -d .bootstrap/main.ninja.in.d -m ${g.bootstrap.bootstrapManifest} -o ${out} ${in}
     depfile = .bootstrap/main.ninja.in.d
-    description = minibp ${out}
+    description = soong_build ${out}
 
 rule s.bootstrap.minibp
     command = .bootstrap/bin/minibp -c ${checkFile} -m ${g.bootstrap.bootstrapManifest} -d ${out}.d -o ${out} ${in}
@@ -234,7 +392,8 @@ rule s.bootstrap.minibp
 
 build .bootstrap/main.ninja.in: s.bootstrap.bigbp $
         ${g.bootstrap.srcDir}/Blueprints | .bootstrap/bin/bpfmt $
-        .bootstrap/bin/bpmodify .bootstrap/bin/minibp
+        .bootstrap/bin/bpmodify .bootstrap/bin/minibp $
+        .bootstrap/bin/soong_build .bootstrap/bin/soong_glob
 default .bootstrap/main.ninja.in
 build .bootstrap/notAFile: phony
 default .bootstrap/notAFile

+ 137 - 0
cc/arm64_device.go

@@ -0,0 +1,137 @@
+package cc
+
+import (
+	"strings"
+
+	"android/soong/common"
+)
+
+var (
+	arm64Cflags = []string{
+		"-fno-exceptions", // from build/core/combo/select.mk
+		"-Wno-multichar",  // from build/core/combo/select.mk
+		"-fno-strict-aliasing",
+		"-fstack-protector",
+		"-ffunction-sections",
+		"-fdata-sections",
+		"-funwind-tables",
+		"-Wa,--noexecstack",
+		"-Werror=format-security",
+		"-D_FORTIFY_SOURCE=2",
+		"-fno-short-enums",
+		"-no-canonical-prefixes",
+		"-fno-canonical-system-headers",
+		"-include ${SrcDir}/build/core/combo/include/arch/linux-arm64/AndroidConfig.h",
+
+		// Help catch common 32/64-bit errors.
+		"-Werror=pointer-to-int-cast",
+		"-Werror=int-to-pointer-cast",
+
+		"-fno-strict-volatile-bitfields",
+
+		// TARGET_RELEASE_CFLAGS
+		"-DNDEBUG",
+		"-O2 -g",
+		"-Wstrict-aliasing=2",
+		"-fgcse-after-reload",
+		"-frerun-cse-after-loop",
+		"-frename-registers",
+	}
+
+	arm64Ldflags = []string{
+		"-Wl,-z,noexecstack",
+		"-Wl,-z,relro",
+		"-Wl,-z,now",
+		"-Wl,--build-id=md5",
+		"-Wl,--warn-shared-textrel",
+		"-Wl,--fatal-warnings",
+		"-Wl,-maarch64linux",
+		"-Wl,--hash-style=gnu",
+
+		// Disable transitive dependency library symbol resolving.
+		"-Wl,--allow-shlib-undefined",
+	}
+
+	arm64Cppflags = []string{
+		"-fvisibility-inlines-hidden",
+	}
+)
+
+func init() {
+	pctx.StaticVariable("arm64GccVersion", "4.9")
+
+	pctx.StaticVariable("arm64GccRoot",
+		"${SrcDir}/prebuilts/gcc/${HostPrebuiltTag}/aarch64/aarch64-linux-android-${arm64GccVersion}")
+
+	pctx.StaticVariable("arm64GccTriple", "aarch64-linux-android")
+
+	pctx.StaticVariable("arm64Cflags", strings.Join(arm64Cflags, " "))
+	pctx.StaticVariable("arm64Ldflags", strings.Join(arm64Ldflags, " "))
+	pctx.StaticVariable("arm64Cppflags", strings.Join(arm64Cppflags, " "))
+	pctx.StaticVariable("arm64IncludeFlags", strings.Join([]string{
+		"-isystem ${LibcRoot}/arch-arm64/include",
+		"-isystem ${LibcRoot}/include",
+		"-isystem ${LibcRoot}/kernel/uapi",
+		"-isystem ${LibcRoot}/kernel/uapi/asm-arm64",
+		"-isystem ${LibmRoot}/include",
+		"-isystem ${LibmRoot}/include/arm64",
+	}, " "))
+
+	pctx.StaticVariable("arm64ClangCflags", strings.Join(clangFilterUnknownCflags(arm64Cflags), " "))
+	pctx.StaticVariable("arm64ClangLdflags", strings.Join(clangFilterUnknownCflags(arm64Ldflags), " "))
+	pctx.StaticVariable("arm64ClangCppflags", strings.Join(clangFilterUnknownCflags(arm64Cppflags), " "))
+}
+
+type toolchainArm64 struct {
+	toolchain64Bit
+}
+
+var toolchainArm64Singleton = &toolchainArm64{}
+
+func (t *toolchainArm64) GccRoot() string {
+	return "${arm64GccRoot}"
+}
+
+func (t *toolchainArm64) GccTriple() string {
+	return "${arm64GccTriple}"
+}
+
+func (t *toolchainArm64) Cflags() string {
+	return "${arm64Cflags} ${arm64IncludeFlags}"
+}
+
+func (t *toolchainArm64) Cppflags() string {
+	return "${arm64Cppflags}"
+}
+
+func (t *toolchainArm64) Ldflags() string {
+	return "${arm64Ldflags}"
+}
+
+func (t *toolchainArm64) IncludeFlags() string {
+	return "${arm64IncludeFlags}"
+}
+
+func (t *toolchainArm64) ClangTriple() string {
+	return "${arm64GccTriple}"
+}
+
+func (t *toolchainArm64) ClangCflags() string {
+	return "${arm64ClangCflags}"
+}
+
+func (t *toolchainArm64) ClangCppflags() string {
+	return "${arm64ClangCppflags}"
+}
+
+func (t *toolchainArm64) ClangLdflags() string {
+	return "${arm64Ldflags}"
+}
+
+func arm64ToolchainFactory(archVariant string, cpuVariant string) toolchain {
+	return toolchainArm64Singleton
+}
+
+func init() {
+	registerToolchainFactory(common.Device, common.Arm64, arm64ToolchainFactory)
+}

+ 301 - 0
cc/arm_device.go

@@ -0,0 +1,301 @@
+package cc
+
+import (
+	"fmt"
+	"strings"
+
+	"android/soong/common"
+)
+
+var (
+	armCflags = []string{
+		"-fno-exceptions", // from build/core/combo/select.mk
+		"-Wno-multichar",  // from build/core/combo/select.mk
+		"-fno-strict-aliasing",
+		"-fstack-protector",
+		"-ffunction-sections",
+		"-fdata-sections",
+		"-funwind-tables",
+		"-fstack-protector",
+		"-Wa,--noexecstack",
+		"-Werror=format-security",
+		"-D_FORTIFY_SOURCE=2",
+		"-fno-short-enums",
+		"-no-canonical-prefixes",
+		"-fno-canonical-system-headers",
+		"-include ${SrcDir}/build/core/combo/include/arch/linux-arm/AndroidConfig.h",
+
+		"-fno-builtin-sin",
+		"-fno-strict-volatile-bitfields",
+
+		// TARGET_RELEASE_CFLAGS
+		"-DNDEBUG",
+		"-g",
+		"-Wstrict-aliasing=2",
+		"-fgcse-after-reload",
+		"-frerun-cse-after-loop",
+		"-frename-registers",
+	}
+
+	armCppflags = []string{
+		"-fvisibility-inlines-hidden",
+	}
+
+	armLdflags = []string{
+		"-Wl,-z,noexecstack",
+		"-Wl,-z,relro",
+		"-Wl,-z,now",
+		"-Wl,--build-id=md5",
+		"-Wl,--warn-shared-textrel",
+		"-Wl,--fatal-warnings",
+		"-Wl,-icf=safe",
+		"-Wl,--hash-style=gnu",
+
+		// Disable transitive dependency library symbol resolving.
+		"-Wl,--allow-shlib-undefined",
+	}
+
+	armArmCflags = []string{
+		"-O2",
+		"-fomit-frame-pointer",
+		"-fstrict-aliasing",
+		"-funswitch-loops",
+	}
+
+	armThumbCflags = []string{
+		"-mthumb",
+		"-Os",
+		"-fomit-frame-pointer",
+		"-fno-strict-aliasing",
+	}
+
+	armArchVariantCflags = map[string][]string{
+		"armv5te": []string{
+			"-march=armv5te",
+			"-mtune=xscale",
+			"-D__ARM_ARCH_5__",
+			"-D__ARM_ARCH_5T__",
+			"-D__ARM_ARCH_5E__",
+			"-D__ARM_ARCH_5TE__",
+		},
+		"armv7-a": []string{
+			"-march=armv7-a",
+			"-mfloat-abi=softfp",
+			"-mfpu=vfpv3-d16",
+		},
+		"armv7-a-neon": []string{
+			"-mfloat-abi=softfp",
+			"-mfpu=neon",
+		},
+	}
+
+	armArchVariantLdflags = map[string][]string{
+		"armv7-a": []string{
+			"-Wl,--fix-cortex-a8",
+		},
+	}
+
+	armCpuVariantCflags = map[string][]string{
+		"cortex-a7": []string{
+			"-mcpu=cortex-a7",
+		},
+		"cortex-a8": []string{
+			"-mcpu=cortex-a8",
+		},
+		"cortex-a15": []string{
+			"-mcpu=cortex-a15",
+			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// don't advertise.
+			"-D__ARM_FEATURE_LPAE=1",
+		},
+	}
+
+	armClangCpuVariantCflags  = armCpuVariantCflags
+	armClangArchVariantCflags = armArchVariantCflags
+)
+
+func init() {
+	replaceFirst := func(slice []string, from, to string) {
+		if slice[0] != from {
+			panic(fmt.Errorf("Expected %q, found %q", from, to))
+		}
+
+		slice[0] = to
+	}
+
+	replaceFirst(armClangArchVariantCflags["armv5te"], "-march=armv5te", "-march=armv5t")
+	replaceFirst(armClangCpuVariantCflags["cortex-a15"], "-mcpu=cortex-a15", "-march=armv7-a")
+	armClangCpuVariantCflags["krait"] = []string{
+		"-mcpu=krait",
+	}
+
+	pctx.StaticVariable("armGccVersion", "4.9")
+
+	pctx.StaticVariable("armGccRoot",
+		"${SrcDir}/prebuilts/gcc/${HostPrebuiltTag}/arm/arm-linux-androideabi-${armGccVersion}")
+
+	pctx.StaticVariable("armGccTriple", "arm-linux-androideabi")
+
+	pctx.StaticVariable("armCflags", strings.Join(armCflags, " "))
+	pctx.StaticVariable("armLdflags", strings.Join(armLdflags, " "))
+	pctx.StaticVariable("armCppflags", strings.Join(armCppflags, " "))
+	pctx.StaticVariable("armIncludeFlags", strings.Join([]string{
+		"-isystem ${LibcRoot}/arch-arm/include",
+		"-isystem ${LibcRoot}/include",
+		"-isystem ${LibcRoot}/kernel/uapi",
+		"-isystem ${LibcRoot}/kernel/uapi/asm-arm",
+		"-isystem ${LibmRoot}/include",
+		"-isystem ${LibmRoot}/include/arm",
+	}, " "))
+
+	// Extended cflags
+
+	// ARM mode vs. Thumb mode
+	pctx.StaticVariable("armArmCflags", strings.Join(armArmCflags, " "))
+	pctx.StaticVariable("armThumbCflags", strings.Join(armThumbCflags, " "))
+
+	// Architecture variant cflags
+	pctx.StaticVariable("armArmv5TECflags", strings.Join(armArchVariantCflags["armv5te"], " "))
+	pctx.StaticVariable("armArmv7ACflags", strings.Join(armArchVariantCflags["armv7-a"], " "))
+	pctx.StaticVariable("armArmv7ANeonCflags", strings.Join(armArchVariantCflags["armv7-a-neon"], " "))
+
+	// Architecture variant ldflags
+	pctx.StaticVariable("armArmv7ALdflags", strings.Join(armArchVariantLdflags["armv7-a"], " "))
+
+	// Cpu variant cflags
+	pctx.StaticVariable("armCortexA7Cflags", strings.Join(armCpuVariantCflags["cortex-a7"], " "))
+	pctx.StaticVariable("armCortexA8Cflags", strings.Join(armCpuVariantCflags["cortex-a8"], " "))
+	pctx.StaticVariable("armCortexA15Cflags", strings.Join(armCpuVariantCflags["cortex-a15"], " "))
+
+	// Clang cflags
+	pctx.StaticVariable("armClangCflags", strings.Join(clangFilterUnknownCflags(armCflags), " "))
+	pctx.StaticVariable("armClangLdflags", strings.Join(clangFilterUnknownCflags(armLdflags), " "))
+	pctx.StaticVariable("armClangCppflags", strings.Join(clangFilterUnknownCflags(armCppflags), " "))
+
+	// Clang cpu variant cflags
+	pctx.StaticVariable("armClangArmv5TECflags",
+		strings.Join(armClangArchVariantCflags["armv5te"], " "))
+	pctx.StaticVariable("armClangArmv7ACflags",
+		strings.Join(armClangArchVariantCflags["armv7-a"], " "))
+	pctx.StaticVariable("armClangArmv7ANeonCflags",
+		strings.Join(armClangArchVariantCflags["armv7-a-neon"], " "))
+
+	// Clang cpu variant cflags
+	pctx.StaticVariable("armClangCortexA7Cflags",
+		strings.Join(armClangCpuVariantCflags["cortex-a7"], " "))
+	pctx.StaticVariable("armClangCortexA8Cflags",
+		strings.Join(armClangCpuVariantCflags["cortex-a8"], " "))
+	pctx.StaticVariable("armClangCortexA15Cflags",
+		strings.Join(armClangCpuVariantCflags["cortex-a15"], " "))
+	pctx.StaticVariable("armClangKraitCflags",
+		strings.Join(armClangCpuVariantCflags["krait"], " "))
+}
+
+var (
+	armArchVariantCflagsVar = map[string]string{
+		"armv5te":      "${armArmv5TECflags}",
+		"armv7-a":      "${armArmv7ACflags}",
+		"armv7-a-neon": "${armArmv7ANeonCflags}",
+	}
+
+	armArchVariantLdflagsVar = map[string]string{
+		"armv7-a":      "${armArmv7ALdflags}",
+		"armv7-a-neon": "${armArmv7ALdflags}",
+	}
+
+	armCpuVariantCflagsVar = map[string]string{
+		"":           "",
+		"cortex-a7":  "${armCortexA7Cflags}",
+		"cortex-a8":  "${armCortexA8Cflags}",
+		"cortex-a15": "${armCortexA15Cflags}",
+		"krait":      "${armCortexA15Cflags}",
+		"denver":     "${armCortexA15Cflags}",
+	}
+
+	armClangArchVariantCflagsVar = map[string]string{
+		"armv5te":      "${armClangArmv5TECflags}",
+		"armv7-a":      "${armClangArmv7ACflags}",
+		"armv7-a-neon": "${armClangArmv7ANeonCflags}",
+	}
+
+	armClangCpuVariantCflagsVar = map[string]string{
+		"":           "",
+		"cortex-a7":  "${armClangCortexA7Cflags}",
+		"cortex-a8":  "${armClangCortexA8Cflags}",
+		"cortex-a15": "${armClangCortexA15Cflags}",
+		"krait":      "${armClangKraitCflags}",
+		"denver":     "${armClangCortexA15Cflags}",
+	}
+)
+
+type toolchainArm struct {
+	toolchain32Bit
+	cflags, ldflags, clangCflags string
+}
+
+func (t *toolchainArm) GccRoot() string {
+	return "${armGccRoot}"
+}
+
+func (t *toolchainArm) GccTriple() string {
+	return "${armGccTriple}"
+}
+
+func (t *toolchainArm) Cflags() string {
+	return t.cflags
+}
+
+func (t *toolchainArm) Cppflags() string {
+	return "${armCppflags}"
+}
+
+func (t *toolchainArm) Ldflags() string {
+	return t.ldflags
+}
+
+func (t *toolchainArm) IncludeFlags() string {
+	return "${armIncludeFlags}"
+}
+
+func (t *toolchainArm) ClangTriple() string {
+	return "${armGccTriple}"
+}
+
+func (t *toolchainArm) ClangCflags() string {
+	return t.clangCflags
+}
+
+func (t *toolchainArm) ClangCppflags() string {
+	return "${armClangCppflags}"
+}
+
+func (t *toolchainArm) ClangLdflags() string {
+	return t.ldflags
+}
+
+func armToolchainFactory(archVariant string, cpuVariant string) toolchain {
+	return &toolchainArm{
+		cflags: strings.Join([]string{
+			"${armCflags}",
+			"${armIncludeFlags}",
+			"${armThumbCflags}",
+			armArchVariantCflagsVar[archVariant],
+			armCpuVariantCflagsVar[cpuVariant],
+		}, " "),
+		ldflags: strings.Join([]string{
+			"${armLdflags}",
+			armArchVariantLdflagsVar[archVariant],
+		}, " "),
+		clangCflags: strings.Join([]string{
+			"${armClangCflags}",
+			"${armIncludeFlags}",
+			"${armThumbCflags}",
+			armClangArchVariantCflagsVar[archVariant],
+			armClangCpuVariantCflagsVar[cpuVariant],
+		}, " "),
+	}
+}
+
+func init() {
+	registerToolchainFactory(common.Device, common.Arm, armToolchainFactory)
+}

+ 285 - 0
cc/builder.go

@@ -0,0 +1,285 @@
+// 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 cc
+
+// This file generates the final rules for compiling all C/C++.  All properties related to
+// compiling should have been translated into builderFlags or another argument to the Transform*
+// functions.
+
+import (
+	"android/soong/common"
+
+	"blueprint"
+	"blueprint/pathtools"
+	"path/filepath"
+	"strings"
+)
+
+const (
+	sharedLibraryExtension = ".so"
+	staticLibraryExtension = ".a"
+)
+
+var (
+	pctx = blueprint.NewPackageContext("android/soong/cc")
+
+	cc = pctx.StaticRule("cc",
+		blueprint.RuleParams{
+			Depfile:     "${out}.d",
+			Deps:        blueprint.DepsGCC,
+			Command:     "$ccCmd $incFlags -c $cFlags -MD -MF ${out}.d -o $out $in",
+			Description: "cc $out",
+		},
+		"ccCmd", "incFlags", "cFlags")
+
+	ld = pctx.StaticRule("ld",
+		blueprint.RuleParams{
+			Command: "$ldCmd ${ldDirFlags} ${crtBegin} ${in} " +
+				"${libFlags} ${crtEnd} -o ${out} ${ldFlags} ${ldLibs}",
+			Description: "ld $out",
+		},
+		"ldCmd", "ldDirFlags", "crtBegin", "libFlags", "crtEnd", "ldFlags", "ldLibs")
+
+	partialLd = pctx.StaticRule("partialLd",
+		blueprint.RuleParams{
+			Command:     "$ldCmd -r ${in} -o ${out}",
+			Description: "partialLd $out",
+		},
+		"ldCmd")
+
+	ar = pctx.StaticRule("ar",
+		blueprint.RuleParams{
+			Command:     "rm -f ${out} && $arCmd $arFlags $out $in",
+			Description: "ar $out",
+		},
+		"arCmd", "arFlags")
+
+	copyGccLibPath = pctx.StaticVariable("copyGccLibPath", "${SrcDir}/build/soong/copygcclib.sh")
+
+	copyGccLib = pctx.StaticRule("copyGccLib",
+		blueprint.RuleParams{
+			Depfile:     "${out}.d",
+			Deps:        blueprint.DepsGCC,
+			Command:     "$copyGccLibPath $out $ccCmd $cFlags -print-file-name=${libName}",
+			Description: "copy gcc $out",
+		},
+		"ccCmd", "cFlags", "libName")
+)
+
+type builderFlags struct {
+	globalFlags string
+	asFlags     string
+	cFlags      string
+	conlyFlags  string
+	cppFlags    string
+	ldFlags     string
+	ldLibs      string
+	incFlags    string
+	nocrt       bool
+	toolchain   toolchain
+	clang       bool
+}
+
+// Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
+func TransformSourceToObj(ctx common.AndroidModuleContext, subdir string, srcFiles []string,
+	flags builderFlags) (objFiles []string) {
+
+	objFiles = make([]string, len(srcFiles))
+	objDir := common.ModuleObjDir(ctx)
+	if subdir != "" {
+		objDir = filepath.Join(objDir, subdir)
+	}
+
+	cflags := flags.globalFlags + " " + flags.cFlags + " " + flags.conlyFlags
+	cppflags := flags.globalFlags + " " + flags.cFlags + " " + flags.cppFlags
+	asflags := flags.globalFlags + " " + flags.asFlags
+
+	for i, srcFile := range srcFiles {
+		objFile := strings.TrimPrefix(srcFile, common.ModuleSrcDir(ctx))
+		objFile = filepath.Join(objDir, objFile)
+		objFile = pathtools.ReplaceExtension(objFile, "o")
+
+		objFiles[i] = objFile
+
+		var moduleCflags string
+		var ccCmd string
+
+		switch filepath.Ext(srcFile) {
+		case ".S", ".s":
+			ccCmd = "gcc"
+			moduleCflags = asflags
+		case ".c":
+			ccCmd = "gcc"
+			moduleCflags = cflags
+		case ".cpp", ".cc":
+			ccCmd = "g++"
+			moduleCflags = cppflags
+		default:
+			ctx.ModuleErrorf("File %s has unknown extension", srcFile)
+			continue
+		}
+
+		if flags.clang {
+			switch ccCmd {
+			case "gcc":
+				ccCmd = "clang"
+			case "g++":
+				ccCmd = "clang++"
+			default:
+				panic("unrecoginzied ccCmd")
+			}
+
+			ccCmd = "${clangPath}" + ccCmd
+		} else {
+			ccCmd = gccCmd(flags.toolchain, ccCmd)
+		}
+
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      cc,
+			Outputs:   []string{objFile},
+			Inputs:    []string{srcFile},
+			Implicits: []string{ccCmd},
+			Args: map[string]string{
+				"cFlags":   moduleCflags,
+				"incFlags": flags.incFlags,
+				"ccCmd":    ccCmd,
+			},
+		})
+	}
+
+	return objFiles
+}
+
+// Generate a rule for compiling multiple .o files to a static library (.a)
+func TransformObjToStaticLib(ctx common.AndroidModuleContext, objFiles []string,
+	flags builderFlags, outputFile string) {
+
+	arCmd := gccCmd(flags.toolchain, "ar")
+	arFlags := "crsPD"
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      ar,
+		Outputs:   []string{outputFile},
+		Inputs:    objFiles,
+		Implicits: []string{arCmd},
+		Args: map[string]string{
+			"arFlags": arFlags,
+			"arCmd":   arCmd,
+		},
+	})
+}
+
+// Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
+// and shared libraires, to a shared library (.so) or dynamic executable
+func TransformObjToDynamicBinary(ctx common.AndroidModuleContext,
+	objFiles, sharedLibs, staticLibs, wholeStaticLibs []string, crtBegin, crtEnd string,
+	flags builderFlags, outputFile string) {
+
+	var ldCmd string
+	if flags.clang {
+		ldCmd = "${clangPath}clang++"
+	} else {
+		ldCmd = gccCmd(flags.toolchain, "g++")
+	}
+
+	var ldDirs []string
+	var libFlagsList []string
+
+	if len(wholeStaticLibs) > 0 {
+		libFlagsList = append(libFlagsList, "-Wl,--whole-archive ")
+		libFlagsList = append(libFlagsList, wholeStaticLibs...)
+		libFlagsList = append(libFlagsList, "-Wl,--no-whole-archive ")
+	}
+
+	libFlagsList = append(libFlagsList, staticLibs...)
+
+	for _, lib := range sharedLibs {
+		dir, file := filepath.Split(lib)
+		if !strings.HasPrefix(file, "lib") {
+			panic("shared library " + lib + " does not start with lib")
+		}
+		if !strings.HasSuffix(file, sharedLibraryExtension) {
+			panic("shared library " + lib + " does not end with " + sharedLibraryExtension)
+		}
+		libFlagsList = append(libFlagsList,
+			"-l"+strings.TrimSuffix(strings.TrimPrefix(file, "lib"), sharedLibraryExtension))
+		ldDirs = append(ldDirs, dir)
+	}
+
+	deps := []string{ldCmd}
+	deps = append(deps, sharedLibs...)
+	deps = append(deps, staticLibs...)
+	deps = append(deps, wholeStaticLibs...)
+	if crtBegin != "" {
+		deps = append(deps, crtBegin, crtEnd)
+	}
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      ld,
+		Outputs:   []string{outputFile},
+		Inputs:    objFiles,
+		Implicits: deps,
+		Args: map[string]string{
+			"ldCmd":      ldCmd,
+			"ldDirFlags": ldDirsToFlags(ldDirs),
+			"crtBegin":   crtBegin,
+			"libFlags":   strings.Join(libFlagsList, " "),
+			"ldFlags":    flags.ldFlags,
+			"crtEnd":     crtEnd,
+			"ldLibs":     flags.ldLibs,
+		},
+	})
+}
+
+// Generate a rule for compiling multiple .o files to a .o using ld partial linking
+func TransformObjsToObj(ctx common.AndroidModuleContext, objFiles []string,
+	flags builderFlags, outputFile string) {
+
+	ldCmd := gccCmd(flags.toolchain, "ld")
+
+	deps := []string{ldCmd}
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      partialLd,
+		Outputs:   []string{outputFile},
+		Inputs:    objFiles,
+		Implicits: deps,
+		Args: map[string]string{
+			"ldCmd": ldCmd,
+		},
+	})
+}
+
+func CopyGccLib(ctx common.AndroidModuleContext, libName string,
+	flags builderFlags, outputFile string) {
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:    copyGccLib,
+		Outputs: []string{outputFile},
+		Implicits: []string{
+			"$copyGccLibPath",
+			gccCmd(flags.toolchain, "gcc"),
+		},
+		Args: map[string]string{
+			"ccCmd":   gccCmd(flags.toolchain, "gcc"),
+			"cFlags":  flags.globalFlags,
+			"libName": libName,
+		},
+	})
+}
+
+func gccCmd(toolchain toolchain, cmd string) string {
+	return filepath.Join(toolchain.GccRoot(), "bin", toolchain.GccTriple()+"-"+cmd)
+}

+ 1194 - 0
cc/cc.go

@@ -0,0 +1,1194 @@
+//
+// 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 cc
+
+// This file contains the module types for compiling C/C++ 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 (
+	"blueprint"
+	"blueprint/pathtools"
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"android/soong/common"
+)
+
+type Config interface {
+	SrcDir() string
+	PrebuiltOS() string
+}
+
+var (
+	HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", Config.PrebuiltOS)
+	SrcDir          = pctx.VariableConfigMethod("SrcDir", Config.SrcDir)
+
+	LibcRoot = pctx.StaticVariable("LibcRoot", "${SrcDir}/bionic/libc")
+	LibmRoot = pctx.StaticVariable("LibmRoot", "${SrcDir}/bionic/libm")
+)
+
+// Flags used by lots of devices.  Putting them in package static variables will save bytes in
+// build.ninja so they aren't repeated for every file
+var (
+	commonGlobalCflags = []string{
+		"-DANDROID",
+		"-fmessage-length=0",
+		"-W",
+		"-Wall",
+		"-Wno-unused",
+		"-Winit-self",
+		"-Wpointer-arith",
+
+		// COMMON_RELEASE_CFLAGS
+		"-DNDEBUG",
+		"-UDEBUG",
+	}
+
+	deviceGlobalCflags = []string{
+		// TARGET_ERROR_FLAGS
+		"-Werror=return-type",
+		"-Werror=non-virtual-dtor",
+		"-Werror=address",
+		"-Werror=sequence-point",
+	}
+
+	hostGlobalCflags = []string{}
+
+	commonGlobalCppflags = []string{
+		"-Wsign-promo",
+		"-std=gnu++11",
+	}
+)
+
+func init() {
+	pctx.StaticVariable("commonGlobalCflags", strings.Join(commonGlobalCflags, " "))
+	pctx.StaticVariable("deviceGlobalCflags", strings.Join(deviceGlobalCflags, " "))
+	pctx.StaticVariable("hostGlobalCflags", strings.Join(hostGlobalCflags, " "))
+
+	pctx.StaticVariable("commonGlobalCppflags", strings.Join(commonGlobalCppflags, " "))
+
+	pctx.StaticVariable("commonClangGlobalCflags",
+		strings.Join(clangFilterUnknownCflags(commonGlobalCflags), " "))
+	pctx.StaticVariable("deviceClangGlobalCflags",
+		strings.Join(clangFilterUnknownCflags(deviceGlobalCflags), " "))
+	pctx.StaticVariable("hostClangGlobalCflags",
+		strings.Join(clangFilterUnknownCflags(hostGlobalCflags), " "))
+
+	// Everything in this list is a crime against abstraction and dependency tracking.
+	// Do not add anything to this list.
+	pctx.StaticVariable("commonGlobalIncludes", strings.Join([]string{
+		"-isystem ${SrcDir}/system/core/include",
+		"-isystem ${SrcDir}/hardware/libhardware/include",
+		"-isystem ${SrcDir}/hardware/libhardware_legacy/include",
+		"-isystem ${SrcDir}/hardware/ril/include",
+		"-isystem ${SrcDir}/libnativehelper/include",
+		"-isystem ${SrcDir}/frameworks/native/include",
+		"-isystem ${SrcDir}/frameworks/native/opengl/include",
+		"-isystem ${SrcDir}/frameworks/av/include",
+		"-isystem ${SrcDir}/frameworks/base/include",
+	}, " "))
+
+	pctx.StaticVariable("clangPath", "${SrcDir}/prebuilts/clang/${HostPrebuiltTag}/host/3.6/bin/")
+}
+
+// CcProperties describes properties used to compile all C or C++ modules
+type ccProperties struct {
+	// srcs: list of source files used to compile the C/C++ module.  May be .c, .cpp, or .S files.
+	Srcs []string `android:"arch_variant,arch_subtract"`
+
+	// cflags: list of module-specific flags that will be used for C and C++ compiles.
+	Cflags []string `android:"arch_variant"`
+
+	// cppflags: list of module-specific flags that will be used for C++ compiles
+	Cppflags []string `android:"arch_variant"`
+
+	// conlyflags: list of module-specific flags that will be used for C compiles
+	Conlyflags []string `android:"arch_variant"`
+
+	// asflags: list of module-specific flags that will be used for .S compiles
+	Asflags []string `android:"arch_variant"`
+
+	// ldflags: list of module-specific flags that will be used for all link steps
+	Ldflags []string `android:"arch_variant"`
+
+	// include_dirs: list of directories relative to the root of the source tree that will
+	// be added to the include path using -I.
+	// If possible, don't use this.  If adding paths from the current directory use
+	// local_include_dirs, if adding paths from other modules use export_include_dirs in
+	// that module.
+	Include_dirs []string `android:"arch_variant"`
+
+	// local_include_dirs: list of directories relative to the Blueprints file that will
+	// be added to the include path using -I
+	Local_include_dirs []string `android:"arch_variant"`
+
+	// export_include_dirs: list of directories relative to the Blueprints file that will
+	// be added to the include path using -I for any module that links against this module
+	Export_include_dirs []string
+
+	// clang_cflags: list of module-specific flags that will be used for C and C++ compiles when
+	// compiling with clang
+	Clang_cflags []string `android:"arch_variant"`
+
+	// clang_asflags: list of module-specific flags that will be used for .S compiles when
+	// compiling with clang
+	Clang_asflags []string `android:"arch_variant"`
+
+	// system_shared_libs: list of system libraries that will be dynamically linked to
+	// shared library and executable modules.  If unset, generally defaults to libc
+	// and libm.  Set to [] to prevent linking against libc and libm.
+	System_shared_libs []string
+
+	// whole_static_libs: list of modules whose object files should be linked into this module
+	// in their entirety.  For static library modules, all of the .o files from the intermediate
+	// directory of the dependency will be linked into this modules .a file.  For a shared library,
+	// the dependency's .a file will be linked into this module using -Wl,--whole-archive.
+	Whole_static_libs []string `android:"arch_variant"`
+
+	// static_libs: list of modules that should be statically linked into this module.
+	Static_libs []string `android:"arch_variant"`
+
+	// shared_libs: list of modules that should be dynamically linked into this module.
+	Shared_libs []string `android:"arch_variant"`
+
+	// allow_undefined_symbols: allow the module to contain undefined symbols.  By default,
+	// modules cannot contain undefined symbols that are not satisified by their immediate
+	// dependencies.  Set this flag to true to remove --no-undefined from the linker flags.
+	// This flag should only be necessary for compiling low-level libraries like libc.
+	Allow_undefined_symbols bool
+
+	// nocrt: don't link in crt_begin and crt_end.  This flag should only be necessary for
+	// compiling crt or libc.
+	Nocrt bool `android:"arch_variant"`
+
+	// no_default_compiler_flags: don't insert default compiler flags into asflags, cflags,
+	// cppflags, conlyflags, ldflags, or include_dirs
+	No_default_compiler_flags bool
+
+	// clang: compile module with clang instead of gcc
+	Clang bool `android:"arch_variant"`
+
+	// rtti: pass -frtti instead of -fno-rtti
+	Rtti bool
+
+	// host_ldlibs: -l arguments to pass to linker for host-provided shared libraries
+	Host_ldlibs []string `android:"arch_variant"`
+
+	// stl: select the STL library to use.  Possible values are "libc++", "libc++_static",
+	// "stlport", "stlport_static", "ndk", "libstdc++", or "none".  Leave blank to select the
+	// default
+	Stl string
+
+	// Set for combined shared/static libraries to prevent compiling object files a second time
+	SkipCompileObjs bool `blueprint:"mutated"`
+}
+
+type unusedProperties struct {
+	Asan            bool
+	Native_coverage bool
+	Strip           string
+	Tags            []string
+	Required        []string
+}
+
+// Building C/C++ code is handled by objects that satisfy this interface via composition
+type ccModuleType interface {
+	common.AndroidModule
+
+	// Return the cflags that are specific to this _type_ of module
+	moduleTypeCflags(common.AndroidModuleContext, toolchain) []string
+
+	// Return the ldflags that are specific to this _type_ of module
+	moduleTypeLdflags(common.AndroidModuleContext, toolchain) []string
+
+	// Create a ccDeps struct that collects the module dependency info.  Can also
+	// modify ccFlags in order to add dependency include directories, etc.
+	collectDeps(common.AndroidModuleContext, ccFlags) (ccDeps, ccFlags)
+
+	// Compile objects into final module
+	compileModule(common.AndroidModuleContext, ccFlags, ccDeps, []string)
+
+	// Return the output file (.o, .a or .so) for use by other modules
+	outputFile() string
+}
+
+func (c *ccBase) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
+	toolchain := c.findToolchain(ctx)
+	if ctx.Failed() {
+		return
+	}
+
+	flags := c.flags(ctx, toolchain)
+	if ctx.Failed() {
+		return
+	}
+
+	flags = c.addStlFlags(ctx, flags)
+	if ctx.Failed() {
+		return
+	}
+
+	deps, flags := c.ccModuleType().collectDeps(ctx, flags)
+	if ctx.Failed() {
+		return
+	}
+
+	objFiles := c.compileObjs(ctx, flags, deps)
+	if ctx.Failed() {
+		return
+	}
+
+	c.ccModuleType().compileModule(ctx, flags, deps, objFiles)
+	if ctx.Failed() {
+		return
+	}
+}
+
+func (c *ccBase) ccModuleType() ccModuleType {
+	return c.module
+}
+
+var _ common.AndroidDynamicDepender = (*ccBase)(nil)
+
+func (c *ccBase) findToolchain(ctx common.AndroidModuleContext) toolchain {
+	arch := ctx.Arch()
+	factory := toolchainFactories[arch.HostOrDevice][arch.ArchType]
+	if factory == nil {
+		panic(fmt.Sprintf("Toolchain not found for %s arch %q",
+			arch.HostOrDevice.String(), arch.String()))
+	}
+	return factory(arch.ArchVariant, arch.CpuVariant)
+}
+
+type ccDeps struct {
+	staticLibs, sharedLibs, wholeStaticLibs, objFiles, includeDirs []string
+
+	crtBegin, crtEnd string
+}
+
+type ccFlags struct {
+	globalFlags []string
+	asFlags     []string
+	cFlags      []string
+	conlyFlags  []string
+	cppFlags    []string
+	ldFlags     []string
+	ldLibs      []string
+	includeDirs []string
+	nocrt       bool
+	toolchain   toolchain
+	clang       bool
+
+	extraStaticLibs []string
+	extraSharedLibs []string
+}
+
+// ccBase contains the properties and members used by all C/C++ module types
+type ccBase struct {
+	common.AndroidModuleBase
+	module ccModuleType
+
+	properties ccProperties
+	unused     unusedProperties
+
+	installPath string
+}
+
+func (c *ccBase) moduleTypeCflags(ctx common.AndroidModuleContext, toolchain toolchain) []string {
+	return nil
+}
+
+func (c *ccBase) moduleTypeLdflags(ctx common.AndroidModuleContext, toolchain toolchain) []string {
+	return nil
+}
+
+func (c *ccBase) AndroidDynamicDependencies(ctx common.AndroidDynamicDependerModuleContext) []string {
+	ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, c.properties.Whole_static_libs...)
+	ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, c.properties.Static_libs...)
+	ctx.AddVariationDependencies([]blueprint.Variation{{"link", "shared"}}, c.properties.Shared_libs...)
+
+	return nil
+}
+
+// Create a ccFlags struct that collects the compile flags from global values,
+// per-target values, module type values, and per-module Blueprints properties
+func (c *ccBase) flags(ctx common.AndroidModuleContext, toolchain toolchain) ccFlags {
+
+	arch := ctx.Arch()
+
+	flags := ccFlags{
+		cFlags:     c.properties.Cflags,
+		cppFlags:   c.properties.Cppflags,
+		conlyFlags: c.properties.Conlyflags,
+		ldFlags:    c.properties.Ldflags,
+		asFlags:    c.properties.Asflags,
+		nocrt:      c.properties.Nocrt,
+		toolchain:  toolchain,
+		clang:      c.properties.Clang,
+	}
+
+	if arch.HostOrDevice.Host() {
+		// TODO: allow per-module clang disable for host
+		flags.clang = true
+	}
+
+	if flags.clang {
+		flags.cFlags = clangFilterUnknownCflags(flags.cFlags)
+		flags.cFlags = append(flags.cFlags, c.properties.Clang_cflags...)
+		flags.asFlags = append(flags.asFlags, c.properties.Clang_asflags...)
+		flags.cppFlags = clangFilterUnknownCflags(flags.cppFlags)
+		flags.conlyFlags = clangFilterUnknownCflags(flags.conlyFlags)
+		flags.ldFlags = clangFilterUnknownCflags(flags.ldFlags)
+
+		target := "-target " + toolchain.ClangTriple()
+		gccPrefix := "-B" + filepath.Join(toolchain.GccRoot(), toolchain.GccTriple(), "bin")
+
+		flags.cFlags = append(flags.cFlags, target, gccPrefix)
+		flags.asFlags = append(flags.asFlags, target, gccPrefix)
+		flags.ldFlags = append(flags.ldFlags, target, gccPrefix)
+
+		if arch.HostOrDevice.Host() {
+			gccToolchain := "--gcc-toolchain=" + toolchain.GccRoot()
+			sysroot := "--sysroot=" + filepath.Join(toolchain.GccRoot(), "sysroot")
+
+			// TODO: also need more -B, -L flags to make host builds hermetic
+			flags.cFlags = append(flags.cFlags, gccToolchain, sysroot)
+			flags.asFlags = append(flags.asFlags, gccToolchain, sysroot)
+			flags.ldFlags = append(flags.ldFlags, gccToolchain, sysroot)
+		}
+	}
+
+	flags.includeDirs = pathtools.PrefixPaths(c.properties.Include_dirs, ctx.Config().(Config).SrcDir())
+	localIncludeDirs := pathtools.PrefixPaths(c.properties.Local_include_dirs, common.ModuleSrcDir(ctx))
+	flags.includeDirs = append(flags.includeDirs, localIncludeDirs...)
+
+	if !c.properties.No_default_compiler_flags {
+		flags.includeDirs = append(flags.includeDirs, []string{
+			common.ModuleSrcDir(ctx),
+			common.ModuleOutDir(ctx),
+			common.ModuleGenDir(ctx),
+		}...)
+
+		if arch.HostOrDevice.Device() && !c.properties.Allow_undefined_symbols {
+			flags.ldFlags = append(flags.ldFlags, "-Wl,--no-undefined")
+		}
+
+		if flags.clang {
+			flags.globalFlags = []string{
+				"${commonGlobalIncludes}",
+				toolchain.IncludeFlags(),
+				toolchain.ClangCflags(),
+				"${commonClangGlobalCflags}",
+				fmt.Sprintf("${%sClangGlobalCflags}", arch.HostOrDevice),
+			}
+		} else {
+			flags.globalFlags = []string{
+				"${commonGlobalIncludes}",
+				toolchain.IncludeFlags(),
+				toolchain.Cflags(),
+				"${commonGlobalCflags}",
+				fmt.Sprintf("${%sGlobalCflags}", arch.HostOrDevice),
+			}
+		}
+
+		if arch.HostOrDevice.Host() {
+			flags.ldFlags = append(flags.ldFlags, c.properties.Host_ldlibs...)
+		}
+
+		if arch.HostOrDevice.Device() {
+			if c.properties.Rtti {
+				flags.cppFlags = append(flags.cppFlags, "-frtti")
+			} else {
+				flags.cppFlags = append(flags.cppFlags, "-fno-rtti")
+			}
+		}
+
+		flags.asFlags = append(flags.asFlags, "-D__ASSEMBLY__")
+
+		if flags.clang {
+			flags.cppFlags = append(flags.cppFlags, toolchain.ClangCppflags())
+			flags.ldFlags = append(flags.ldFlags, toolchain.ClangLdflags())
+		} else {
+			flags.cppFlags = append(flags.cppFlags, toolchain.Cppflags())
+			flags.ldFlags = append(flags.ldFlags, toolchain.Ldflags())
+		}
+	}
+
+	flags.cFlags = append(flags.cFlags, c.ccModuleType().moduleTypeCflags(ctx, toolchain)...)
+	flags.ldFlags = append(flags.ldFlags, c.ccModuleType().moduleTypeLdflags(ctx, toolchain)...)
+
+	// Optimization to reduce size of build.ninja
+	// Replace the long list of flags for each file with a module-local variable
+	ctx.Variable(pctx, "cflags", strings.Join(flags.cFlags, " "))
+	ctx.Variable(pctx, "cppflags", strings.Join(flags.cppFlags, " "))
+	ctx.Variable(pctx, "asflags", strings.Join(flags.asFlags, " "))
+	flags.cFlags = []string{"$cflags"}
+	flags.cppFlags = []string{"$cppflags"}
+	flags.asFlags = []string{"$asflags"}
+
+	return flags
+}
+
+// Modify ccFlags structs with STL library info
+func (c *ccBase) addStlFlags(ctx common.AndroidModuleContext, flags ccFlags) ccFlags {
+	if !c.properties.No_default_compiler_flags {
+		arch := ctx.Arch()
+		stl := "libc++" // TODO: mingw needs libstdc++
+		if c.properties.Stl != "" {
+			stl = c.properties.Stl
+		}
+
+		stlStatic := false
+		if strings.HasSuffix(stl, "_static") {
+			stlStatic = true
+		}
+
+		switch stl {
+		case "libc++", "libc++_static":
+			flags.cFlags = append(flags.cFlags, "-D_USING_LIBCXX")
+			flags.includeDirs = append(flags.includeDirs, "${SrcDir}/external/libcxx/include")
+			if arch.HostOrDevice.Host() {
+				flags.cppFlags = append(flags.cppFlags, "-nostdinc++")
+				flags.ldFlags = append(flags.ldFlags, "-nodefaultlibs")
+				flags.ldLibs = append(flags.ldLibs, "-lc", "-lm", "-lpthread")
+			}
+			if stlStatic {
+				flags.extraStaticLibs = append(flags.extraStaticLibs, "libc++_static")
+			} else {
+				flags.extraSharedLibs = append(flags.extraSharedLibs, "libc++")
+			}
+		case "stlport", "stlport_static":
+			if arch.HostOrDevice.Device() {
+				flags.includeDirs = append(flags.includeDirs,
+					"${SrcDir}/external/stlport/stlport",
+					"${SrcDir}/bionic/libstdc++/include",
+					"${SrcDir}/bionic")
+				if stlStatic {
+					flags.extraStaticLibs = append(flags.extraStaticLibs, "libstdc++", "libstlport_static")
+				} else {
+					flags.extraSharedLibs = append(flags.extraSharedLibs, "libstdc++", "libstlport")
+				}
+			}
+		case "ndk":
+			panic("TODO")
+		case "libstdc++":
+			// Using bionic's basic libstdc++. Not actually an STL. Only around until the
+			// tree is in good enough shape to not need it.
+			// Host builds will use GNU libstdc++.
+			if arch.HostOrDevice.Device() {
+				flags.includeDirs = append(flags.includeDirs, "${SrcDir}/bionic/libstdc++/include")
+				flags.extraSharedLibs = append(flags.extraSharedLibs, "libstdc++")
+			}
+		case "none":
+			if arch.HostOrDevice.Host() {
+				flags.cppFlags = append(flags.cppFlags, "-nostdinc++")
+				flags.ldFlags = append(flags.ldFlags, "-nodefaultlibs")
+				flags.ldLibs = append(flags.ldLibs, "-lc", "-lm")
+			}
+		default:
+			ctx.ModuleErrorf("stl: %q is not a supported STL", stl)
+		}
+
+	}
+	return flags
+}
+
+// Compile a list of source files into objects a specified subdirectory
+func (c *ccBase) customCompileObjs(ctx common.AndroidModuleContext, flags ccFlags,
+	deps ccDeps, subdir string, srcFiles []string) []string {
+
+	srcFiles = pathtools.PrefixPaths(srcFiles, common.ModuleSrcDir(ctx))
+	srcFiles = common.ExpandGlobs(ctx, srcFiles)
+
+	return TransformSourceToObj(ctx, subdir, srcFiles, ccFlagsToBuilderFlags(flags))
+}
+
+// Compile files listed in c.properties.Srcs into objects
+func (c *ccBase) compileObjs(ctx common.AndroidModuleContext, flags ccFlags,
+	deps ccDeps) []string {
+
+	if c.properties.SkipCompileObjs {
+		return nil
+	}
+
+	return c.customCompileObjs(ctx, flags, deps, "", c.properties.Srcs)
+}
+
+func (c *ccBase) outputFile() string {
+	return ""
+}
+
+func (c *ccBase) collectDepsFromList(ctx common.AndroidModuleContext,
+	names []string) (modules []common.AndroidModule,
+	outputFiles []string, exportedIncludeDirs []string) {
+
+	for _, n := range names {
+		found := false
+		ctx.VisitDirectDeps(func(m blueprint.Module) {
+			otherName := ctx.OtherModuleName(m)
+			if otherName != n {
+				return
+			}
+
+			if a, ok := m.(ccModuleType); ok {
+				if a.Disabled() {
+					// If a cc_library host+device module depends on a library that exists as both
+					// cc_library_shared and cc_library_host_shared, it will end up with two
+					// dependencies with the same name, one of which is marked disabled for each
+					// of host and device.  Ignore the disabled one.
+					return
+				}
+				if a.HostOrDevice() != ctx.Arch().HostOrDevice {
+					ctx.ModuleErrorf("host/device mismatch between %q and %q", ctx.ModuleName(),
+						otherName)
+					return
+				}
+
+				if outputFile := a.outputFile(); outputFile != "" {
+					if found {
+						ctx.ModuleErrorf("multiple modules satisified dependency on %q", otherName)
+						return
+					}
+					outputFiles = append(outputFiles, outputFile)
+					modules = append(modules, a)
+					if i, ok := a.(ccExportedIncludeDirsProducer); ok {
+						exportedIncludeDirs = append(exportedIncludeDirs, i.exportedIncludeDirs()...)
+					}
+					found = true
+				} else {
+					ctx.ModuleErrorf("module %q missing output file", otherName)
+					return
+				}
+			} else {
+				ctx.ModuleErrorf("module %q not an android module", otherName)
+				return
+			}
+		})
+		if !found {
+			ctx.ModuleErrorf("unsatisified dependency on %q", n)
+		}
+	}
+
+	return modules, outputFiles, exportedIncludeDirs
+}
+
+func (c *ccBase) collectDeps(ctx common.AndroidModuleContext, flags ccFlags) (ccDeps, ccFlags) {
+	var deps ccDeps
+	var newIncludeDirs []string
+
+	wholeStaticLibNames := c.properties.Whole_static_libs
+	_, deps.wholeStaticLibs, newIncludeDirs = c.collectDepsFromList(ctx, wholeStaticLibNames)
+
+	deps.includeDirs = append(deps.includeDirs, newIncludeDirs...)
+
+	staticLibNames := c.properties.Static_libs
+	staticLibNames = append(staticLibNames, flags.extraStaticLibs...)
+	_, deps.staticLibs, newIncludeDirs = c.collectDepsFromList(ctx, staticLibNames)
+	deps.includeDirs = append(deps.includeDirs, newIncludeDirs...)
+
+	return deps, flags
+}
+
+// ccDynamic contains the properties and members used by shared libraries and dynamic executables
+type ccDynamic struct {
+	ccBase
+}
+
+const defaultSystemSharedLibraries = "__default__"
+
+func (c *ccDynamic) systemSharedLibs() []string {
+
+	if len(c.properties.System_shared_libs) == 1 &&
+		c.properties.System_shared_libs[0] == defaultSystemSharedLibraries {
+
+		if c.HostOrDevice().Host() {
+			return []string{}
+		} else {
+			return []string{"libc", "libm"}
+		}
+	}
+	return c.properties.System_shared_libs
+}
+
+var (
+	stlSharedLibs     = []string{"libc++", "libstlport", "libstdc++"}
+	stlSharedHostLibs = []string{"libc++"}
+	stlStaticLibs     = []string{"libc++_static", "libstlport_static", "libstdc++"}
+	stlStaticHostLibs = []string{"libc++_static"}
+)
+
+func (c *ccDynamic) AndroidDynamicDependencies(ctx common.AndroidDynamicDependerModuleContext) []string {
+	deps := c.ccBase.AndroidDynamicDependencies(ctx)
+
+	if c.HostOrDevice().Device() {
+		ctx.AddVariationDependencies([]blueprint.Variation{{"link", "shared"}}, c.systemSharedLibs()...)
+		ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}},
+			"libcompiler_rt-extras",
+			//"libgcov",
+			"libatomic",
+			"libgcc")
+
+		if c.properties.Stl != "none" {
+			ctx.AddVariationDependencies([]blueprint.Variation{{"link", "shared"}}, stlSharedLibs...)
+			ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, stlStaticLibs...)
+		}
+	} else {
+		if c.properties.Stl != "none" {
+			ctx.AddVariationDependencies([]blueprint.Variation{{"link", "shared"}}, stlSharedHostLibs...)
+			ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, stlStaticHostLibs...)
+		}
+	}
+
+	return deps
+}
+
+var implicitStaticLibs = []string{"libcompiler_rt-extras" /*"libgcov",*/, "libatomic", "libgcc"}
+
+func (c *ccDynamic) collectDeps(ctx common.AndroidModuleContext, flags ccFlags) (ccDeps, ccFlags) {
+	var newIncludeDirs []string
+
+	deps, flags := c.ccBase.collectDeps(ctx, flags)
+
+	systemSharedLibs := c.systemSharedLibs()
+	sharedLibNames := make([]string, 0, len(c.properties.Shared_libs)+len(systemSharedLibs)+
+		len(flags.extraSharedLibs))
+	sharedLibNames = append(sharedLibNames, c.properties.Shared_libs...)
+	sharedLibNames = append(sharedLibNames, systemSharedLibs...)
+	sharedLibNames = append(sharedLibNames, flags.extraSharedLibs...)
+	_, deps.sharedLibs, newIncludeDirs = c.collectDepsFromList(ctx, sharedLibNames)
+	deps.includeDirs = append(deps.includeDirs, newIncludeDirs...)
+
+	if ctx.Arch().HostOrDevice.Device() {
+		var staticLibs []string
+		staticLibNames := implicitStaticLibs
+		_, staticLibs, newIncludeDirs = c.collectDepsFromList(ctx, staticLibNames)
+		deps.staticLibs = append(deps.staticLibs, staticLibs...)
+		deps.includeDirs = append(deps.includeDirs, newIncludeDirs...)
+	}
+
+	ctx.VisitDirectDeps(func(m blueprint.Module) {
+		if obj, ok := m.(*ccObject); ok {
+			otherName := ctx.OtherModuleName(m)
+			if strings.HasPrefix(otherName, "crtbegin") {
+				if !c.properties.Nocrt {
+					deps.crtBegin = obj.outputFile()
+				}
+			} else if strings.HasPrefix(otherName, "crtend") {
+				if !c.properties.Nocrt {
+					deps.crtEnd = obj.outputFile()
+				}
+			} else {
+				ctx.ModuleErrorf("object module type only support for crtbegin and crtend, found %q",
+					ctx.OtherModuleName(m))
+			}
+		}
+	})
+
+	flags.includeDirs = append(flags.includeDirs, deps.includeDirs...)
+
+	return deps, flags
+}
+
+type ccExportedIncludeDirsProducer interface {
+	exportedIncludeDirs() []string
+}
+
+//
+// Combined static+shared libraries
+//
+
+type ccLibrary struct {
+	ccDynamic
+
+	primary           *ccLibrary
+	primaryObjFiles   []string
+	objFiles          []string
+	exportIncludeDirs []string
+	out               string
+
+	libraryProperties struct {
+		BuildStatic bool `blueprint:"mutated"`
+		BuildShared bool `blueprint:"mutated"`
+		IsShared    bool `blueprint:"mutated"`
+		IsStatic    bool `blueprint:"mutated"`
+
+		Static struct {
+			Srcs   []string `android:"arch_variant"`
+			Cflags []string `android:"arch_variant"`
+		} `android:"arch_variant"`
+		Shared struct {
+			Srcs   []string `android:"arch_variant"`
+			Cflags []string `android:"arch_variant"`
+		} `android:"arch_variant"`
+	}
+}
+
+func NewCCLibrary() (blueprint.Module, []interface{}) {
+	module := &ccLibrary{}
+	module.module = module
+	module.properties.System_shared_libs = []string{defaultSystemSharedLibraries}
+	module.libraryProperties.BuildShared = true
+	module.libraryProperties.BuildStatic = true
+
+	return common.InitAndroidModule(module, common.HostAndDeviceSupported, "both",
+		&module.properties, &module.unused, &module.libraryProperties)
+}
+
+func (c *ccLibrary) AndroidDynamicDependencies(ctx common.AndroidDynamicDependerModuleContext) []string {
+	if c.libraryProperties.IsShared {
+		deps := c.ccDynamic.AndroidDynamicDependencies(ctx)
+		if c.HostOrDevice().Device() {
+			deps = append(deps, "crtbegin_so", "crtend_so")
+		}
+		return deps
+	} else {
+		return c.ccBase.AndroidDynamicDependencies(ctx)
+	}
+}
+
+func (c *ccLibrary) collectDeps(ctx common.AndroidModuleContext, flags ccFlags) (ccDeps, ccFlags) {
+	if c.libraryProperties.IsStatic {
+		deps, flags := c.ccBase.collectDeps(ctx, flags)
+		wholeStaticLibNames := c.properties.Whole_static_libs
+		wholeStaticLibs, _, _ := c.collectDepsFromList(ctx, wholeStaticLibNames)
+
+		for _, m := range wholeStaticLibs {
+			if staticLib, ok := m.(*ccLibrary); ok && staticLib.libraryProperties.IsStatic {
+				deps.objFiles = append(deps.objFiles, staticLib.allObjFiles()...)
+			} else {
+				ctx.ModuleErrorf("module %q not a static library", ctx.OtherModuleName(m))
+			}
+		}
+
+		return deps, flags
+	} else if c.libraryProperties.IsShared {
+		return c.ccDynamic.collectDeps(ctx, flags)
+	} else {
+		panic("Not shared or static")
+	}
+}
+
+func (c *ccLibrary) outputFile() string {
+	return c.out
+}
+
+func (c *ccLibrary) allObjFiles() []string {
+	return c.objFiles
+}
+
+func (c *ccLibrary) exportedIncludeDirs() []string {
+	return c.exportIncludeDirs
+}
+
+func (c *ccLibrary) moduleTypeCflags(ctx common.AndroidModuleContext, toolchain toolchain) []string {
+	return []string{"-fPIC"}
+}
+
+func (c *ccLibrary) moduleTypeLdflags(ctx common.AndroidModuleContext, toolchain toolchain) []string {
+	if c.libraryProperties.IsShared {
+		libName := ctx.ModuleName()
+		// GCC for Android assumes that -shared means -Bsymbolic, use -Wl,-shared instead
+		sharedFlag := "-Wl,-shared"
+		if c.properties.Clang || ctx.Arch().HostOrDevice.Host() {
+			sharedFlag = "-shared"
+		}
+		if ctx.Arch().HostOrDevice.Device() {
+			return []string{
+				"-nostdlib",
+				"-Wl,--gc-sections",
+				sharedFlag,
+				"-Wl,-soname," + libName,
+			}
+		} else {
+			return []string{
+				"-Wl,--gc-sections",
+				sharedFlag,
+				"-Wl,-soname," + libName,
+			}
+		}
+	} else {
+		return nil
+	}
+}
+
+func (c *ccLibrary) compileStaticLibrary(ctx common.AndroidModuleContext,
+	flags ccFlags, deps ccDeps, objFiles []string) {
+
+	staticFlags := flags
+	staticFlags.cFlags = append(staticFlags.cFlags, c.libraryProperties.Static.Cflags...)
+	objFilesStatic := c.customCompileObjs(ctx, staticFlags, deps, common.DeviceStaticLibrary,
+		c.libraryProperties.Static.Srcs)
+
+	objFiles = append(objFiles, objFilesStatic...)
+
+	var includeDirs []string
+
+	wholeStaticLibNames := c.properties.Whole_static_libs
+	wholeStaticLibs, _, newIncludeDirs := c.collectDepsFromList(ctx, wholeStaticLibNames)
+	includeDirs = append(includeDirs, newIncludeDirs...)
+
+	for _, m := range wholeStaticLibs {
+		if staticLib, ok := m.(*ccLibrary); ok && staticLib.libraryProperties.IsStatic {
+			objFiles = append(objFiles, staticLib.allObjFiles()...)
+		} else {
+			ctx.ModuleErrorf("module %q not a static library", ctx.OtherModuleName(m))
+		}
+	}
+
+	staticLibNames := c.properties.Static_libs
+	_, _, newIncludeDirs = c.collectDepsFromList(ctx, staticLibNames)
+	includeDirs = append(includeDirs, newIncludeDirs...)
+
+	ctx.VisitDirectDeps(func(m blueprint.Module) {
+		if obj, ok := m.(*ccObject); ok {
+			otherName := ctx.OtherModuleName(m)
+			if !strings.HasPrefix(otherName, "crtbegin") && !strings.HasPrefix(otherName, "crtend") {
+				objFiles = append(objFiles, obj.outputFile())
+			}
+		}
+	})
+
+	outputFile := filepath.Join(common.ModuleOutDir(ctx), ctx.ModuleName()+staticLibraryExtension)
+
+	TransformObjToStaticLib(ctx, objFiles, ccFlagsToBuilderFlags(flags), outputFile)
+
+	c.objFiles = objFiles
+	c.out = outputFile
+	c.exportIncludeDirs = pathtools.PrefixPaths(c.properties.Export_include_dirs,
+		common.ModuleSrcDir(ctx))
+
+	ctx.CheckbuildFile(outputFile)
+}
+
+func (c *ccLibrary) compileSharedLibrary(ctx common.AndroidModuleContext,
+	flags ccFlags, deps ccDeps, objFiles []string) {
+
+	sharedFlags := flags
+	sharedFlags.cFlags = append(sharedFlags.cFlags, c.libraryProperties.Shared.Cflags...)
+	objFilesShared := c.customCompileObjs(ctx, sharedFlags, deps, common.DeviceSharedLibrary,
+		c.libraryProperties.Shared.Srcs)
+
+	objFiles = append(objFiles, objFilesShared...)
+
+	outputFile := filepath.Join(common.ModuleOutDir(ctx), ctx.ModuleName()+sharedLibraryExtension)
+
+	TransformObjToDynamicBinary(ctx, objFiles, deps.sharedLibs, deps.staticLibs, deps.wholeStaticLibs,
+		deps.crtBegin, deps.crtEnd, ccFlagsToBuilderFlags(flags), outputFile)
+
+	c.out = outputFile
+	c.exportIncludeDirs = pathtools.PrefixPaths(c.properties.Export_include_dirs,
+		common.ModuleSrcDir(ctx))
+
+	installDir := "lib"
+	if flags.toolchain.Is64Bit() {
+		installDir = "lib64"
+	}
+
+	ctx.InstallFile(installDir, outputFile)
+}
+
+func (c *ccLibrary) compileModule(ctx common.AndroidModuleContext,
+	flags ccFlags, deps ccDeps, objFiles []string) {
+
+	// Reuse the object files from the matching static library if it exists
+	if c.primary == c {
+		c.primaryObjFiles = objFiles
+	} else {
+		objFiles = append([]string(nil), c.primary.primaryObjFiles...)
+	}
+
+	if c.libraryProperties.IsStatic {
+		c.compileStaticLibrary(ctx, flags, deps, objFiles)
+	} else {
+		c.compileSharedLibrary(ctx, flags, deps, objFiles)
+	}
+}
+
+//
+// Objects (for crt*.o)
+//
+
+type ccObject struct {
+	ccBase
+	out string
+}
+
+func NewCCObject() (blueprint.Module, []interface{}) {
+	module := &ccObject{}
+	module.module = module
+
+	return common.InitAndroidModule(module, common.DeviceSupported, "both",
+		&module.properties, &module.unused)
+}
+
+func (*ccObject) AndroidDynamicDependencies(ctx common.AndroidDynamicDependerModuleContext) []string {
+	// object files can't have any dynamic dependencies
+	return nil
+}
+
+func (c *ccObject) collectDeps(ctx common.AndroidModuleContext, flags ccFlags) (ccDeps, ccFlags) {
+	deps, flags := c.ccBase.collectDeps(ctx, flags)
+	ctx.VisitDirectDeps(func(m blueprint.Module) {
+		if obj, ok := m.(*ccObject); ok {
+			deps.objFiles = append(deps.objFiles, obj.outputFile())
+		} else {
+			ctx.ModuleErrorf("Unknown module type for dependency %q", ctx.OtherModuleName(m))
+		}
+	})
+
+	return deps, flags
+}
+
+func (c *ccObject) compileModule(ctx common.AndroidModuleContext,
+	flags ccFlags, deps ccDeps, objFiles []string) {
+
+	objFiles = append(objFiles, deps.objFiles...)
+
+	var outputFile string
+	if len(objFiles) == 1 {
+		outputFile = objFiles[0]
+	} else {
+		outputFile = filepath.Join(common.ModuleOutDir(ctx), ctx.ModuleName()+".o")
+		TransformObjsToObj(ctx, objFiles, ccFlagsToBuilderFlags(flags), outputFile)
+	}
+
+	c.out = outputFile
+
+	ctx.CheckbuildFile(outputFile)
+}
+
+func (c *ccObject) outputFile() string {
+	return c.out
+}
+
+//
+// Executables
+//
+
+type ccBinary struct {
+	ccDynamic
+	binaryProperties binaryProperties
+}
+
+type binaryProperties struct {
+	// static_executable: compile executable with -static
+	Static_executable bool
+
+	// stem: set the name of the output
+	Stem string `android:"arch_variant"`
+
+	// prefix_symbols: if set, add an extra objcopy --prefix-symbols= step
+	Prefix_symbols string
+}
+
+func (c *ccBinary) getStem(ctx common.AndroidModuleContext) string {
+	if c.binaryProperties.Stem != "" {
+		return c.binaryProperties.Stem
+	}
+	return ctx.ModuleName()
+}
+
+func (c *ccBinary) AndroidDynamicDependencies(ctx common.AndroidDynamicDependerModuleContext) []string {
+	deps := c.ccDynamic.AndroidDynamicDependencies(ctx)
+	if c.HostOrDevice().Device() {
+		if c.binaryProperties.Static_executable {
+			deps = append(deps, "crtbegin_static", "crtend_android")
+		} else {
+			deps = append(deps, "crtbegin_dynamic", "crtend_android")
+		}
+	}
+	return deps
+}
+
+func NewCCBinary() (blueprint.Module, []interface{}) {
+	module := &ccBinary{}
+	module.module = module
+
+	module.properties.System_shared_libs = []string{defaultSystemSharedLibraries}
+	return common.InitAndroidModule(module, common.HostAndDeviceSupported, "first", &module.properties,
+		&module.unused, &module.binaryProperties)
+}
+
+func (c *ccBinary) moduleTypeCflags(ctx common.AndroidModuleContext, toolchain toolchain) []string {
+	return []string{"-fpie"}
+}
+
+func (c *ccBinary) moduleTypeLdflags(ctx common.AndroidModuleContext, toolchain toolchain) []string {
+	if ctx.Arch().HostOrDevice.Device() {
+		linker := "/system/bin/linker"
+		if toolchain.Is64Bit() {
+			linker = "/system/bin/linker64"
+		}
+
+		return []string{
+			"-nostdlib",
+			"-Bdynamic",
+			fmt.Sprintf("-Wl,-dynamic-linker,%s", linker),
+			"-Wl,--gc-sections",
+			"-Wl,-z,nocopyreloc",
+		}
+	}
+
+	return nil
+}
+
+func (c *ccBinary) compileModule(ctx common.AndroidModuleContext,
+	flags ccFlags, deps ccDeps, objFiles []string) {
+
+	if !c.binaryProperties.Static_executable && inList("libc", c.properties.Static_libs) {
+		ctx.ModuleErrorf("statically linking libc to dynamic executable, please remove libc\n" +
+			"from static libs or set static_executable: true")
+	}
+
+	outputFile := filepath.Join(common.ModuleOutDir(ctx), c.getStem(ctx))
+
+	TransformObjToDynamicBinary(ctx, objFiles, deps.sharedLibs, deps.staticLibs, deps.wholeStaticLibs,
+		deps.crtBegin, deps.crtEnd, ccFlagsToBuilderFlags(flags), outputFile)
+
+	ctx.InstallFile("bin", outputFile)
+}
+
+//
+// Static library
+//
+
+func NewCCLibraryStatic() (blueprint.Module, []interface{}) {
+	module := &ccLibrary{}
+	module.module = module
+	module.libraryProperties.BuildStatic = true
+
+	return common.InitAndroidModule(module, common.HostAndDeviceSupported, "both",
+		&module.properties, &module.unused)
+}
+
+//
+// Shared libraries
+//
+
+func NewCCLibraryShared() (blueprint.Module, []interface{}) {
+	module := &ccLibrary{}
+	module.module = module
+	module.properties.System_shared_libs = []string{defaultSystemSharedLibraries}
+	module.libraryProperties.BuildShared = true
+
+	return common.InitAndroidModule(module, common.HostAndDeviceSupported, "both",
+		&module.properties, &module.unused)
+}
+
+//
+// Host static library
+//
+
+func NewCCLibraryHostStatic() (blueprint.Module, []interface{}) {
+	module := &ccLibrary{}
+	module.module = module
+	module.libraryProperties.BuildStatic = true
+
+	return common.InitAndroidModule(module, common.HostSupported, "both",
+		&module.properties, &module.unused)
+}
+
+//
+// Host Shared libraries
+//
+
+func NewCCLibraryHostShared() (blueprint.Module, []interface{}) {
+	module := &ccLibrary{}
+	module.module = module
+	module.libraryProperties.BuildShared = true
+
+	return common.InitAndroidModule(module, common.HostSupported, "both",
+		&module.properties, &module.unused)
+}
+
+//
+// Host Binaries
+//
+
+func NewCCBinaryHost() (blueprint.Module, []interface{}) {
+	module := &ccBinary{}
+	module.module = module
+
+	return common.InitAndroidModule(module, common.HostSupported, "first",
+		&module.properties, &module.unused)
+}
+
+//
+// Device libraries shipped with gcc
+//
+
+type toolchainLibrary struct {
+	ccLibrary
+}
+
+func (*toolchainLibrary) AndroidDynamicDependencies(ctx common.AndroidDynamicDependerModuleContext) []string {
+	// toolchain libraries can't have any dependencies
+	return nil
+}
+
+func (*toolchainLibrary) collectDeps(ctx common.AndroidModuleContext, flags ccFlags) (ccDeps, ccFlags) {
+	// toolchain libraries can't have any dependencies
+	return ccDeps{}, flags
+}
+
+func NewToolchainLibrary() (blueprint.Module, []interface{}) {
+	module := &toolchainLibrary{}
+	module.module = module
+	module.libraryProperties.BuildStatic = true
+
+	return common.InitAndroidModule(module, common.DeviceSupported, "both")
+}
+
+func (c *toolchainLibrary) compileModule(ctx common.AndroidModuleContext,
+	flags ccFlags, deps ccDeps, objFiles []string) {
+
+	libName := ctx.ModuleName() + staticLibraryExtension
+	outputFile := filepath.Join(common.ModuleOutDir(ctx), libName)
+
+	CopyGccLib(ctx, libName, ccFlagsToBuilderFlags(flags), outputFile)
+
+	c.out = outputFile
+
+	ctx.CheckbuildFile(outputFile)
+}
+
+func LinkageMutator(mctx blueprint.EarlyMutatorContext) {
+	if c, ok := mctx.Module().(*ccLibrary); ok {
+		var modules []blueprint.Module
+		if c.libraryProperties.BuildStatic && c.libraryProperties.BuildShared {
+			modules = mctx.CreateLocalVariations("static", "shared")
+			modules[0].(*ccLibrary).libraryProperties.IsStatic = true
+			modules[1].(*ccLibrary).libraryProperties.IsShared = true
+		} else if c.libraryProperties.BuildStatic {
+			modules = mctx.CreateLocalVariations("static")
+			modules[0].(*ccLibrary).libraryProperties.IsStatic = true
+		} else if c.libraryProperties.BuildShared {
+			modules = mctx.CreateLocalVariations("shared")
+			modules[0].(*ccLibrary).libraryProperties.IsShared = true
+		} else {
+			panic("ccLibrary not static or shared")
+		}
+		primary := modules[0].(*ccLibrary)
+		for _, m := range modules {
+			m.(*ccLibrary).primary = primary
+			if m != primary {
+				m.(*ccLibrary).properties.SkipCompileObjs = true
+			}
+		}
+	} else if _, ok := mctx.Module().(*toolchainLibrary); ok {
+		mctx.CreateLocalVariations("static")
+	}
+}

+ 104 - 0
cc/clang.go

@@ -0,0 +1,104 @@
+package cc
+
+import (
+	"sort"
+	"strings"
+)
+
+// Cflags that should be filtered out when compiling with clang
+var clangUnknownCflags = []string{
+	"-finline-functions",
+	"-finline-limit=64",
+	"-fno-canonical-system-headers",
+	"-fno-tree-sra",
+	"-funswitch-loops",
+	"-Wmaybe-uninitialized",
+	"-Wno-error=maybe-uninitialized",
+	"-Wno-free-nonheap-object",
+	"-Wno-literal-suffix",
+	"-Wno-maybe-uninitialized",
+	"-Wno-old-style-declaration",
+	"-Wno-psabi",
+	"-Wno-unused-but-set-variable",
+	"-Wno-unused-but-set-parameter",
+	"-Wno-unused-local-typedefs",
+
+	// arm + arm64 + mips + mips64
+	"-fgcse-after-reload",
+	"-frerun-cse-after-loop",
+	"-frename-registers",
+	"-fno-strict-volatile-bitfields",
+
+	// arm + arm64
+	"-fno-align-jumps",
+	"-Wa,--noexecstack",
+
+	// arm
+	"-mthumb-interwork",
+	"-fno-builtin-sin",
+	"-fno-caller-saves",
+	"-fno-early-inlining",
+	"-fno-move-loop-invariants",
+	"-fno-partial-inlining",
+	"-fno-tree-copy-prop",
+	"-fno-tree-loop-optimize",
+
+	// mips + mips64
+	"-msynci",
+	"-mno-fused-madd",
+
+	// x86 + x86_64
+	"-finline-limit=300",
+	"-fno-inline-functions-called-once",
+	"-mfpmath=sse",
+	"-mbionic",
+}
+
+func init() {
+	sort.Strings(clangUnknownCflags)
+
+	pctx.StaticVariable("clangExtraCflags", strings.Join([]string{
+		"-D__compiler_offsetof=__builtin_offsetof",
+
+		// Help catch common 32/64-bit errors.
+		"-Werror=int-conversion",
+
+		// Workaround for ccache with clang.
+		// See http://petereisentraut.blogspot.com/2011/05/ccache-and-clang.html.
+		"-Wno-unused-command-line-argument",
+
+		// Disable -Winconsistent-missing-override until we can clean up the existing
+		// codebase for it.
+		"-Wno-inconsistent-missing-override",
+	}, " "))
+
+	pctx.StaticVariable("clangExtraConlyflags", strings.Join([]string{
+		"-std=gnu99",
+	}, " "))
+
+	pctx.StaticVariable("clangExtraTargetCflags", strings.Join([]string{
+		"-nostdlibinc",
+	}, " "))
+}
+
+func clangFilterUnknownCflags(cflags []string) []string {
+	ret := make([]string, 0, len(cflags))
+	for _, f := range cflags {
+		if !inListSorted(f, clangUnknownCflags) {
+			ret = append(ret, f)
+		}
+	}
+
+	return ret
+}
+
+func inListSorted(s string, list []string) bool {
+	for _, l := range list {
+		if s == l {
+			return true
+		} else if s < l {
+			return false
+		}
+	}
+	return false
+}

+ 62 - 0
cc/toolchain.go

@@ -0,0 +1,62 @@
+// 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 cc
+
+import (
+	"android/soong/common"
+)
+
+type toolchainFactory func(archVariant string, cpuVariant string) toolchain
+
+var toolchainFactories = map[common.HostOrDevice]map[common.ArchType]toolchainFactory{
+	common.Host:   make(map[common.ArchType]toolchainFactory),
+	common.Device: make(map[common.ArchType]toolchainFactory),
+}
+
+func registerToolchainFactory(hod common.HostOrDevice, arch common.ArchType,
+	factory toolchainFactory) {
+
+	toolchainFactories[hod][arch] = factory
+}
+
+type toolchain interface {
+	GccRoot() string
+	GccTriple() string
+	Cflags() string
+	Cppflags() string
+	Ldflags() string
+	IncludeFlags() string
+
+	ClangTriple() string
+	ClangCflags() string
+	ClangCppflags() string
+	ClangLdflags() string
+
+	Is64Bit() bool
+}
+
+type toolchain64Bit struct {
+}
+
+func (toolchain64Bit) Is64Bit() bool {
+	return true
+}
+
+type toolchain32Bit struct {
+}
+
+func (toolchain32Bit) Is64Bit() bool {
+	return false
+}

+ 96 - 0
cc/util.go

@@ -0,0 +1,96 @@
+// 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 cc
+
+import (
+	"fmt"
+	"regexp"
+	"strings"
+)
+
+// Efficiently converts a list of include directories to a single string
+// of cflags with -I prepended to each directory.
+func includeDirsToFlags(dirs []string) string {
+	return joinWithPrefix(dirs, "-I")
+}
+
+func ldDirsToFlags(dirs []string) string {
+	return joinWithPrefix(dirs, "-L")
+}
+
+func libNamesToFlags(names []string) string {
+	return joinWithPrefix(names, "-l")
+}
+
+func joinWithPrefix(strs []string, prefix string) string {
+	if len(strs) == 0 {
+		return ""
+	}
+
+	if len(strs) == 1 {
+		return prefix + strs[0]
+	}
+
+	n := len(" ") * (len(strs) - 1)
+	for _, s := range strs {
+		n += len(prefix) + len(s)
+	}
+
+	ret := make([]byte, 0, n)
+	for i, s := range strs {
+		if i != 0 {
+			ret = append(ret, ' ')
+		}
+		ret = append(ret, prefix...)
+		ret = append(ret, s...)
+	}
+	return string(ret)
+}
+
+func inList(s string, list []string) bool {
+	for _, l := range list {
+		if l == s {
+			return true
+		}
+	}
+
+	return false
+}
+
+var libNameRegexp = regexp.MustCompile(`^lib(.*)$`)
+
+func moduleToLibName(module string) (string, error) {
+	matches := libNameRegexp.FindStringSubmatch(module)
+	if matches == nil {
+		return "", fmt.Errorf("Library module name %s does not start with lib", module)
+	}
+	return matches[1], nil
+}
+
+func ccFlagsToBuilderFlags(in ccFlags) builderFlags {
+	return builderFlags{
+		globalFlags: strings.Join(in.globalFlags, " "),
+		asFlags:     strings.Join(in.asFlags, " "),
+		cFlags:      strings.Join(in.cFlags, " "),
+		conlyFlags:  strings.Join(in.conlyFlags, " "),
+		cppFlags:    strings.Join(in.cppFlags, " "),
+		ldFlags:     strings.Join(in.ldFlags, " "),
+		ldLibs:      strings.Join(in.ldLibs, " "),
+		incFlags:    includeDirsToFlags(in.includeDirs),
+		nocrt:       in.nocrt,
+		toolchain:   in.toolchain,
+		clang:       in.clang,
+	}
+}

+ 231 - 0
cc/x86_linux_host.go

@@ -0,0 +1,231 @@
+package cc
+
+import (
+	"runtime"
+	"strings"
+
+	"android/soong/common"
+)
+
+var (
+	linuxCflags = []string{
+		"-fno-exceptions", // from build/core/combo/select.mk
+		"-Wno-multichar",  // from build/core/combo/select.mk
+
+		"-Wa,--noexecstack",
+
+		"-fPIC",
+		"-no-canonical-prefixes",
+		"-include ${SrcDir}/build/core/combo/include/arch/linux-x86/AndroidConfig.h",
+
+		// Disable new longjmp in glibc 2.11 and later. See bug 2967937. Same for 2.15?
+		"-U_FORTIFY_SOURCE",
+		"-D_FORTIFY_SOURCE=0",
+
+		// Workaround differences in inttypes.h between host and target.
+		//See bug 12708004.
+		"-D__STDC_FORMAT_MACROS",
+		"-D__STDC_CONSTANT_MACROS",
+
+		// HOST_RELEASE_CFLAGS
+		"-O2", // from build/core/combo/select.mk
+		"-g",  // from build/core/combo/select.mk
+		"-fno-strict-aliasing", // from build/core/combo/select.mk
+	}
+
+	linuxLdflags = []string{
+		"-Wl,-z,noexecstack",
+		"-Wl,-z,relro",
+		"-Wl,-z,now",
+		"-Wl,--build-id=md5",
+		"-Wl,--warn-shared-textrel",
+		"-Wl,--fatal-warnings",
+		"-Wl,-icf=safe",
+		"-Wl,--hash-style=gnu",
+
+		// Disable transitive dependency library symbol resolving.
+		"-Wl,--allow-shlib-undefined",
+	}
+
+	// Extended cflags
+	linuxX86Cflags = []string{
+		"-msse3",
+		"-mfpmath=sse",
+		"-m32",
+		"-march=prescott",
+	}
+
+	linuxX8664Cflags = []string{
+		"-m64",
+	}
+
+	linuxX86Ldflags = []string{
+		"-m32",
+	}
+
+	linuxX8664Ldflags = []string{
+		"-m64",
+	}
+
+	linuxClangCflags = append([]string{
+		"--gcc-toolchain=${linuxGccRoot}",
+		"--sysroot=${linuxGccRoot}/sysroot",
+	}, clangFilterUnknownCflags(linuxCflags)...)
+
+	linuxClangLdflags = append([]string{
+		"--gcc-toolchain=${linuxGccRoot}",
+		"--sysroot=${linuxGccRoot}/sysroot",
+	}, clangFilterUnknownCflags(linuxLdflags)...)
+
+	linuxX86ClangLdflags = append([]string{
+		"-B${linuxGccRoot}/lib/gcc/${linuxGccTriple}/${linuxGccVersion}/32",
+		"-L${linuxGccRoot}/lib/gcc/${linuxGccTriple}/${linuxGccVersion}/32",
+		"-L${linuxGccRoot}/${linuxGccTriple}/lib32",
+	}, clangFilterUnknownCflags(linuxX86Ldflags)...)
+
+	linuxX8664ClangLdflags = append([]string{
+		"-B${linuxGccRoot}/lib/gcc/${linuxGccTriple}/${linuxGccVersion}",
+		"-L${linuxGccRoot}/lib/gcc/${linuxGccTriple}/${linuxGccVersion}",
+		"-L${linuxGccRoot}/${linuxGccTriple}/lib64",
+	}, clangFilterUnknownCflags(linuxX8664Ldflags)...)
+
+	linuxClangCppflags = []string{
+		"-isystem ${linuxGccRoot}/${linuxGccTriple}/include/c++/${linuxGccVersion}",
+		"-isystem ${linuxGccRoot}/${linuxGccTriple}/include/c++/${linuxGccVersion}/backward",
+	}
+
+	linuxX86ClangCppflags = []string{
+		"-isystem ${linuxGccRoot}/${linuxGccTriple}/include/c++/${linuxGccVersion}/${linuxGccTriple}/32",
+	}
+
+	linuxX8664ClangCppflags = []string{
+		"-isystem ${linuxGccRoot}/${linuxGccTriple}/include/c++/${linuxGccVersion}/${linuxGccTriple}",
+	}
+)
+
+func init() {
+	pctx.StaticVariable("linuxGccVersion", "4.8")
+
+	pctx.StaticVariable("linuxGccRoot",
+		"${SrcDir}/prebuilts/gcc/${HostPrebuiltTag}/host/x86_64-linux-glibc2.15-${linuxGccVersion}")
+
+	pctx.StaticVariable("linuxGccTriple", "x86_64-linux")
+
+	pctx.StaticVariable("linuxCflags", strings.Join(linuxCflags, " "))
+	pctx.StaticVariable("linuxLdflags", strings.Join(linuxLdflags, " "))
+
+	pctx.StaticVariable("linuxClangCflags", strings.Join(linuxClangCflags, " "))
+	pctx.StaticVariable("linuxClangLdflags", strings.Join(linuxClangLdflags, " "))
+	pctx.StaticVariable("linuxClangCppflags", strings.Join(linuxClangCppflags, " "))
+
+	// Extended cflags
+	pctx.StaticVariable("linuxX86Cflags", strings.Join(linuxX86Cflags, " "))
+	pctx.StaticVariable("linuxX8664Cflags", strings.Join(linuxX8664Cflags, " "))
+	pctx.StaticVariable("linuxX86Ldflags", strings.Join(linuxX86Ldflags, " "))
+	pctx.StaticVariable("linuxX8664Ldflags", strings.Join(linuxX8664Ldflags, " "))
+
+	pctx.StaticVariable("linuxX86ClangCflags",
+		strings.Join(clangFilterUnknownCflags(linuxX86Cflags), " "))
+	pctx.StaticVariable("linuxX8664ClangCflags",
+		strings.Join(clangFilterUnknownCflags(linuxX8664Cflags), " "))
+	pctx.StaticVariable("linuxX86ClangLdflags", strings.Join(linuxX86ClangLdflags, " "))
+	pctx.StaticVariable("linuxX8664ClangLdflags", strings.Join(linuxX8664ClangLdflags, " "))
+	pctx.StaticVariable("linuxX86ClangCppflags", strings.Join(linuxX86ClangCppflags, " "))
+	pctx.StaticVariable("linuxX8664ClangCppflags", strings.Join(linuxX8664ClangCppflags, " "))
+}
+
+type toolchainLinux struct {
+	cFlags, ldFlags string
+}
+
+type toolchainLinuxX86 struct {
+	toolchain32Bit
+	toolchainLinux
+}
+
+type toolchainLinuxX8664 struct {
+	toolchain64Bit
+	toolchainLinux
+}
+
+func (t *toolchainLinux) GccRoot() string {
+	return "${linuxGccRoot}"
+}
+
+func (t *toolchainLinux) GccTriple() string {
+	return "${linuxGccTriple}"
+}
+
+func (t *toolchainLinuxX86) Cflags() string {
+	return "${linuxCflags} ${linuxX86Cflags}"
+}
+
+func (t *toolchainLinuxX8664) Cflags() string {
+	return "${linuxCflags} ${linuxX8664Cflags}"
+}
+
+func (t *toolchainLinux) Cppflags() string {
+	return ""
+}
+
+func (t *toolchainLinuxX86) Ldflags() string {
+	return "${linuxLdflags} ${linuxX86Ldflags}"
+}
+
+func (t *toolchainLinuxX8664) Ldflags() string {
+	return "${linuxLdflags} ${linuxX8664Ldflags}"
+}
+
+func (t *toolchainLinux) IncludeFlags() string {
+	return ""
+}
+
+func (t *toolchainLinuxX86) ClangTriple() string {
+	return "i686-linux-gnu"
+}
+
+func (t *toolchainLinuxX86) ClangCflags() string {
+	return "${linuxClangCflags} ${linuxX86ClangCflags}"
+}
+
+func (t *toolchainLinuxX86) ClangCppflags() string {
+	return "${linuxClangCppflags} ${linuxX86ClangCppflags}"
+}
+
+func (t *toolchainLinuxX8664) ClangTriple() string {
+	return "x86_64-linux-gnu"
+}
+
+func (t *toolchainLinuxX8664) ClangCflags() string {
+	return "${linuxClangCflags} ${linuxX8664ClangCflags}"
+}
+
+func (t *toolchainLinuxX8664) ClangCppflags() string {
+	return "${linuxClangCppflags} ${linuxX8664ClangCppflags}"
+}
+
+func (t *toolchainLinuxX86) ClangLdflags() string {
+	return "${linuxClangLdflags} ${linuxX86ClangLdflags}"
+}
+
+func (t *toolchainLinuxX8664) ClangLdflags() string {
+	return "${linuxClangLdflags} ${linuxX8664ClangLdflags}"
+}
+
+var toolchainLinuxX86Singleton toolchain = &toolchainLinuxX86{}
+var toolchainLinuxX8664Singleton toolchain = &toolchainLinuxX8664{}
+
+func linuxX86ToolchainFactory(archVariant string, cpuVariant string) toolchain {
+	return toolchainLinuxX86Singleton
+}
+
+func linuxX8664ToolchainFactory(archVariant string, cpuVariant string) toolchain {
+	return toolchainLinuxX8664Singleton
+}
+
+func init() {
+	if runtime.GOOS == "linux" {
+		registerToolchainFactory(common.Host, common.X86, linuxX86ToolchainFactory)
+		registerToolchainFactory(common.Host, common.X86_64, linuxX8664ToolchainFactory)
+	}
+}

+ 68 - 0
cmd/soong_build/main.go

@@ -0,0 +1,68 @@
+// 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 (
+	"flag"
+	"fmt"
+	"os"
+	"path/filepath"
+
+	"blueprint"
+	"blueprint/bootstrap"
+
+	"android/soong/cc"
+	"android/soong/common"
+	"android/soong/config"
+)
+
+func main() {
+	flag.Parse()
+
+	// The top-level Blueprints file is passed as the first argument.
+	srcDir := filepath.Dir(flag.Arg(0))
+
+	ctx := blueprint.NewContext()
+
+	// Module types
+	ctx.RegisterModuleType("cc_library_static", cc.NewCCLibraryStatic)
+	ctx.RegisterModuleType("cc_library_shared", cc.NewCCLibraryShared)
+	ctx.RegisterModuleType("cc_library", cc.NewCCLibrary)
+	ctx.RegisterModuleType("cc_object", cc.NewCCObject)
+	ctx.RegisterModuleType("cc_binary", cc.NewCCBinary)
+
+	ctx.RegisterModuleType("toolchain_library", cc.NewToolchainLibrary)
+
+	ctx.RegisterModuleType("cc_library_host_static", cc.NewCCLibraryHostStatic)
+	ctx.RegisterModuleType("cc_library_host_shared", cc.NewCCLibraryHostShared)
+	ctx.RegisterModuleType("cc_binary_host", cc.NewCCBinaryHost)
+
+	// Mutators
+	ctx.RegisterEarlyMutator("arch", common.ArchMutator)
+	ctx.RegisterEarlyMutator("link", cc.LinkageMutator)
+
+	// Singletons
+
+	configuration, err := config.New(srcDir)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "%s", err)
+		os.Exit(1)
+	}
+
+	// Temporary hack
+	//ctx.SetIgnoreUnknownModuleTypes(true)
+
+	bootstrap.Main(ctx, configuration, config.ConfigFileName)
+}

+ 56 - 0
cmd/soong_glob/soong_glob.go

@@ -0,0 +1,56 @@
+// 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.
+
+// soong_glob is the command line tool that checks if the list of files matching a glob has
+// changed, and only updates the output file list if it has changed.  It is used to optimize
+// out build.ninja regenerations when non-matching files are added.  See
+// android/soong/common/glob.go for a longer description.
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+
+	"android/soong/glob"
+)
+
+var (
+	out = flag.String("o", "", "file to write list of files that match glob")
+)
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "usage: soong_glob -o out glob\n")
+	flag.PrintDefaults()
+	os.Exit(2)
+}
+
+func main() {
+	flag.Parse()
+
+	if *out == "" {
+		fmt.Fprintln(os.Stderr, "error: -o is required\n")
+		usage()
+	}
+
+	if flag.NArg() != 1 {
+		usage()
+	}
+
+	_, err := glob.GlobWithDepFile(flag.Arg(0), *out, *out+".d")
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
+		os.Exit(1)
+	}
+}

+ 545 - 0
common/arch.go

@@ -0,0 +1,545 @@
+// 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 common
+
+import (
+	"blueprint"
+	"blueprint/proptools"
+	"fmt"
+	"reflect"
+	"runtime"
+	"strings"
+)
+
+var (
+	Arm    = newArch32("Arm")
+	Arm64  = newArch64("Arm64")
+	Mips   = newArch32("Mips")
+	Mips64 = newArch64("Mips64")
+	X86    = newArch32("X86")
+	X86_64 = newArch64("X86_64")
+)
+
+/*
+Example blueprints file containing all variant property groups, with comment listing what type
+of variants get properties in that group:
+
+module {
+    arch: {
+        arm: {
+            // Host or device variants with arm architecture
+        },
+        arm64: {
+            // Host or device variants with arm64 architecture
+        },
+        mips: {
+            // Host or device variants with mips architecture
+        },
+        mips64: {
+            // Host or device variants with mips64 architecture
+        },
+        x86: {
+            // Host or device variants with x86 architecture
+        },
+        x86_64: {
+            // Host or device variants with x86_64 architecture
+        },
+    },
+    multilib: {
+        lib32: {
+            // Host or device variants for 32-bit architectures
+        },
+        lib64: {
+            // Host or device variants for 64-bit architectures
+        },
+    },
+    target: {
+        android: {
+            // Device variants
+        },
+        host: {
+            // Host variants
+        },
+        linux: {
+            // Linux host variants
+        },
+        darwin: {
+            // Darwin host variants
+        },
+        windows: {
+            // Windows host variants
+        },
+        not_windows: {
+            // Non-windows host variants
+        },
+    },
+}
+*/
+type archProperties struct {
+	Arch struct {
+		Arm    interface{}
+		Arm64  interface{}
+		Mips   interface{}
+		Mips64 interface{}
+		X86    interface{}
+		X86_64 interface{}
+	}
+	Multilib struct {
+		Lib32 interface{}
+		Lib64 interface{}
+	}
+	Target struct {
+		Host        interface{}
+		Android     interface{}
+		Linux       interface{}
+		Darwin      interface{}
+		Windows     interface{}
+		Not_windows interface{}
+	}
+}
+
+// An Arch indicates a single CPU architecture.
+type Arch struct {
+	HostOrDevice HostOrDevice
+	ArchType     ArchType
+	ArchVariant  string
+	CpuVariant   string
+}
+
+func (a Arch) String() string {
+	s := a.HostOrDevice.String() + "_" + a.ArchType.String()
+	if a.ArchVariant != "" {
+		s += "_" + a.ArchVariant
+	}
+	if a.CpuVariant != "" {
+		s += "_" + a.CpuVariant
+	}
+	return s
+}
+
+type ArchType struct {
+	Name          string
+	Field         string
+	Multilib      string
+	MultilibField string
+}
+
+func newArch32(field string) ArchType {
+	return ArchType{
+		Name:          strings.ToLower(field),
+		Field:         field,
+		Multilib:      "lib32",
+		MultilibField: "Lib32",
+	}
+}
+
+func newArch64(field string) ArchType {
+	return ArchType{
+		Name:          strings.ToLower(field),
+		Field:         field,
+		Multilib:      "lib64",
+		MultilibField: "Lib64",
+	}
+}
+
+func (a ArchType) String() string {
+	return a.Name
+}
+
+type HostOrDeviceSupported int
+
+const (
+	_ HostOrDeviceSupported = iota
+	HostSupported
+	DeviceSupported
+	HostAndDeviceSupported
+)
+
+type HostOrDevice int
+
+const (
+	_ HostOrDevice = iota
+	Host
+	Device
+)
+
+func (hod HostOrDevice) String() string {
+	switch hod {
+	case Device:
+		return "device"
+	case Host:
+		return "host"
+	default:
+		panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
+	}
+}
+
+func (hod HostOrDevice) FieldLower() string {
+	switch hod {
+	case Device:
+		return "android"
+	case Host:
+		return "host"
+	default:
+		panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
+	}
+}
+
+func (hod HostOrDevice) Field() string {
+	switch hod {
+	case Device:
+		return "Android"
+	case Host:
+		return "Host"
+	default:
+		panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
+	}
+}
+
+func (hod HostOrDevice) Host() bool {
+	if hod == 0 {
+		panic("HostOrDevice unset")
+	}
+	return hod == Host
+}
+
+func (hod HostOrDevice) Device() bool {
+	if hod == 0 {
+		panic("HostOrDevice unset")
+	}
+	return hod == Device
+}
+
+var hostOrDeviceName = map[HostOrDevice]string{
+	Device: "device",
+	Host:   "host",
+}
+
+var (
+	armArch = Arch{
+		HostOrDevice: Device,
+		ArchType:     Arm,
+		ArchVariant:  "armv7-a-neon",
+		CpuVariant:   "cortex-a15",
+	}
+	arm64Arch = Arch{
+		HostOrDevice: Device,
+		ArchType:     Arm64,
+		ArchVariant:  "armv8-a",
+		CpuVariant:   "denver",
+	}
+	hostArch = Arch{
+		HostOrDevice: Host,
+		ArchType:     X86,
+	}
+	host64Arch = Arch{
+		HostOrDevice: Host,
+		ArchType:     X86_64,
+	}
+)
+
+func ArchMutator(mctx blueprint.EarlyMutatorContext) {
+	var module AndroidModule
+	var ok bool
+	if module, ok = mctx.Module().(AndroidModule); !ok {
+		return
+	}
+
+	// TODO: this is all hardcoded for arm64 primary, arm secondary for now
+	// Replace with a configuration file written by lunch or bootstrap
+
+	arches := []Arch{}
+
+	if module.base().HostSupported() {
+		arches = append(arches, host64Arch)
+	}
+
+	if module.base().DeviceSupported() {
+		switch module.base().commonProperties.Compile_multilib {
+		case "both":
+			arches = append(arches, arm64Arch, armArch)
+		case "first", "64":
+			arches = append(arches, arm64Arch)
+		case "32":
+			arches = append(arches, armArch)
+		default:
+			mctx.ModuleErrorf(`compile_multilib must be "both", "first", "32", or "64", found %q`,
+				module.base().commonProperties.Compile_multilib)
+		}
+	}
+
+	archNames := []string{}
+	for _, arch := range arches {
+		archNames = append(archNames, arch.String())
+	}
+
+	modules := mctx.CreateVariations(archNames...)
+
+	for i, m := range modules {
+		m.(AndroidModule).base().SetArch(arches[i])
+		m.(AndroidModule).base().setArchProperties(mctx, arches[i])
+	}
+}
+
+func InitArchModule(m AndroidModule, defaultMultilib string,
+	propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+	base := m.base()
+
+	base.commonProperties.Compile_multilib = defaultMultilib
+
+	base.generalProperties = append(base.generalProperties,
+		&base.commonProperties)
+	base.generalProperties = append(base.generalProperties,
+		propertyStructs...)
+
+	for _, properties := range base.generalProperties {
+		propertiesValue := reflect.ValueOf(properties)
+		if propertiesValue.Kind() != reflect.Ptr {
+			panic("properties must be a pointer to a struct")
+		}
+
+		propertiesValue = propertiesValue.Elem()
+		if propertiesValue.Kind() != reflect.Struct {
+			panic("properties must be a pointer to a struct")
+		}
+
+		archProperties := &archProperties{}
+		forEachInterface(reflect.ValueOf(archProperties), func(v reflect.Value) {
+			newValue := proptools.CloneProperties(propertiesValue)
+			proptools.ZeroProperties(newValue.Elem())
+			v.Set(newValue)
+		})
+
+		base.archProperties = append(base.archProperties, archProperties)
+	}
+
+	var allProperties []interface{}
+	allProperties = append(allProperties, base.generalProperties...)
+	for _, asp := range base.archProperties {
+		allProperties = append(allProperties, asp)
+	}
+
+	return m, allProperties
+}
+
+// Rewrite the module's properties structs to contain arch-specific values.
+func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext, arch Arch) {
+	for i := range a.generalProperties {
+		generalPropsValue := reflect.ValueOf(a.generalProperties[i]).Elem()
+
+		// Handle arch-specific properties in the form:
+		// arch {
+		//     arm64 {
+		//         key: value,
+		//     },
+		// },
+		t := arch.ArchType
+		extendProperties(ctx, "arch", t.Name, generalPropsValue,
+			reflect.ValueOf(a.archProperties[i].Arch).FieldByName(t.Field).Elem().Elem())
+
+		// Handle multilib-specific properties in the form:
+		// multilib {
+		//     lib32 {
+		//         key: value,
+		//     },
+		// },
+		extendProperties(ctx, "multilib", t.Multilib, generalPropsValue,
+			reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(t.MultilibField).Elem().Elem())
+
+		// Handle host-or-device-specific properties in the form:
+		// target {
+		//     host {
+		//         key: value,
+		//     },
+		// },
+		hod := arch.HostOrDevice
+		extendProperties(ctx, "target", hod.FieldLower(), generalPropsValue,
+			reflect.ValueOf(a.archProperties[i].Target).FieldByName(hod.Field()).Elem().Elem())
+
+		// Handle host target properties in the form:
+		// target {
+		//     linux {
+		//         key: value,
+		//     },
+		//     not_windows {
+		//         key: value,
+		//     },
+		// },
+		var osList = []struct {
+			goos  string
+			field string
+		}{
+			{"darwin", "Darwin"},
+			{"linux", "Linux"},
+			{"windows", "Windows"},
+		}
+
+		if hod.Host() {
+			for _, v := range osList {
+				if v.goos == runtime.GOOS {
+					extendProperties(ctx, "target", v.goos, generalPropsValue,
+						reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem())
+				}
+			}
+			extendProperties(ctx, "target", "not_windows", generalPropsValue,
+				reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem())
+		}
+
+		if ctx.Failed() {
+			return
+		}
+	}
+}
+
+func forEachInterface(v reflect.Value, f func(reflect.Value)) {
+	switch v.Kind() {
+	case reflect.Interface:
+		f(v)
+	case reflect.Struct:
+		for i := 0; i < v.NumField(); i++ {
+			forEachInterface(v.Field(i), f)
+		}
+	case reflect.Ptr:
+		forEachInterface(v.Elem(), f)
+	default:
+		panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
+	}
+}
+
+// TODO: move this to proptools
+func extendProperties(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
+	dstValue, srcValue reflect.Value) {
+	extendPropertiesRecursive(ctx, variationType, variationName, dstValue, srcValue, "")
+}
+
+func extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
+	dstValue, srcValue reflect.Value, recursePrefix string) {
+
+	typ := dstValue.Type()
+	if srcValue.Type() != typ {
+		panic(fmt.Errorf("can't extend mismatching types (%s <- %s)",
+			dstValue.Kind(), srcValue.Kind()))
+	}
+
+	for i := 0; i < srcValue.NumField(); i++ {
+		field := typ.Field(i)
+		if field.PkgPath != "" {
+			// The field is not exported so just skip it.
+			continue
+		}
+
+		srcFieldValue := srcValue.Field(i)
+		dstFieldValue := dstValue.Field(i)
+
+		localPropertyName := proptools.PropertyNameForField(field.Name)
+		propertyName := fmt.Sprintf("%s.%s.%s%s", variationType, variationName,
+			recursePrefix, localPropertyName)
+		propertyPresentInVariation := ctx.ContainsProperty(propertyName)
+
+		if !propertyPresentInVariation {
+			continue
+		}
+
+		tag := field.Tag.Get("android")
+		tags := map[string]bool{}
+		for _, entry := range strings.Split(tag, ",") {
+			if entry != "" {
+				tags[entry] = true
+			}
+		}
+
+		if !tags["arch_variant"] {
+			ctx.PropertyErrorf(propertyName, "property %q can't be specific to a build variant",
+				recursePrefix+proptools.PropertyNameForField(field.Name))
+			continue
+		}
+
+		switch srcFieldValue.Kind() {
+		case reflect.Bool:
+			// Replace the original value.
+			dstFieldValue.Set(srcFieldValue)
+		case reflect.String:
+			// Append the extension string.
+			dstFieldValue.SetString(dstFieldValue.String() +
+				srcFieldValue.String())
+		case reflect.Struct:
+			// Recursively extend the struct's fields.
+			newRecursePrefix := fmt.Sprintf("%s%s.", recursePrefix, strings.ToLower(field.Name))
+			extendPropertiesRecursive(ctx, variationType, variationName,
+				dstFieldValue, srcFieldValue,
+				newRecursePrefix)
+		case reflect.Slice:
+			val, err := archCombineSlices(dstFieldValue, srcFieldValue, tags["arch_subtract"])
+			if err != nil {
+				ctx.PropertyErrorf(propertyName, err.Error())
+				continue
+			}
+			dstFieldValue.Set(val)
+		case reflect.Ptr, reflect.Interface:
+			// Recursively extend the pointed-to struct's fields.
+			if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
+				panic(fmt.Errorf("can't extend field %q: nilitude mismatch"))
+			}
+			if dstFieldValue.Type() != srcFieldValue.Type() {
+				panic(fmt.Errorf("can't extend field %q: type mismatch"))
+			}
+			if !dstFieldValue.IsNil() {
+				newRecursePrefix := fmt.Sprintf("%s.%s", recursePrefix, field.Name)
+				extendPropertiesRecursive(ctx, variationType, variationName,
+					dstFieldValue.Elem(), srcFieldValue.Elem(),
+					newRecursePrefix)
+			}
+		default:
+			panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
+				field.Name, srcFieldValue.Kind()))
+		}
+	}
+}
+
+func archCombineSlices(general, arch reflect.Value, canSubtract bool) (reflect.Value, error) {
+	if !canSubtract {
+		// Append the extension slice.
+		return reflect.AppendSlice(general, arch), nil
+	}
+
+	// Support -val in arch list to subtract a value from original list
+	l := general.Interface().([]string)
+	for archIndex := 0; archIndex < arch.Len(); archIndex++ {
+		archString := arch.Index(archIndex).String()
+		if strings.HasPrefix(archString, "-") {
+			generalIndex := findStringInSlice(archString[1:], l)
+			if generalIndex == -1 {
+				return reflect.Value{},
+					fmt.Errorf("can't find %q to subtract from general properties", archString[1:])
+			}
+			l = append(l[:generalIndex], l[generalIndex+1:]...)
+		} else {
+			l = append(l, archString)
+		}
+	}
+
+	return reflect.ValueOf(l), nil
+}
+
+func findStringInSlice(str string, slice []string) int {
+	for i, s := range slice {
+		if s == str {
+			return i
+		}
+	}
+
+	return -1
+}

+ 61 - 0
common/defs.go

@@ -0,0 +1,61 @@
+// 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 common
+
+import (
+	"blueprint"
+)
+
+var (
+	pctx = blueprint.NewPackageContext("android/soong/common")
+
+	cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks",
+		Config.CpPreserveSymlinksFlags)
+
+	// A phony rule that is not the built-in Ninja phony rule.  The built-in
+	// phony rule has special behavior that is sometimes not desired.  See the
+	// Ninja docs for more details.
+	Phony = pctx.StaticRule("Phony",
+		blueprint.RuleParams{
+			Command:     "# phony $out",
+			Description: "phony $out",
+		})
+
+	// GeneratedFile is a rule for indicating that a given file was generated
+	// while running soong.  This allows the file to be cleaned up if it ever
+	// stops being generated by soong.
+	GeneratedFile = pctx.StaticRule("GeneratedFile",
+		blueprint.RuleParams{
+			Command:     "# generated $out",
+			Description: "generated $out",
+			Generator:   true,
+		})
+
+	// A copy rule.
+	Cp = pctx.StaticRule("Cp",
+		blueprint.RuleParams{
+			Command:     "cp $cpPreserveSymlinks $cpFlags $in $out",
+			Description: "cp $out",
+		},
+		"cpFlags")
+
+	// A symlink rule.
+	Symlink = pctx.StaticRule("Symlink",
+		blueprint.RuleParams{
+			Command:     "ln -f -s $fromPath $out",
+			Description: "symlink $out",
+		},
+		"fromPath")
+)

+ 133 - 0
common/glob.go

@@ -0,0 +1,133 @@
+// 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 common
+
+import (
+	"fmt"
+	"path/filepath"
+
+	"blueprint"
+	"blueprint/bootstrap"
+
+	"android/soong/glob"
+)
+
+// This file supports globbing source files in Blueprints files.
+//
+// The build.ninja file needs to be regenerated any time a file matching the glob is added
+// or removed.  The naive solution is to have the build.ninja file depend on all the
+// traversed directories, but this will cause the regeneration step to run every time a
+// non-matching file is added to a traversed directory, including backup files created by
+// editors.
+//
+// The solution implemented here optimizes out regenerations when the directory modifications
+// don't match the glob by having the build.ninja file depend on an intermedate file that
+// is only updated when a file matching the glob is added or removed.  The intermediate file
+// depends on the traversed directories via a depfile.  The depfile is used to avoid build
+// errors if a directory is deleted - a direct dependency on the deleted directory would result
+// in a build failure with a "missing and no known rule to make it" error.
+
+var (
+	globCmd = filepath.Join(bootstrap.BinDir, "soong_glob")
+
+	// globRule rule traverses directories to produce a list of files that match $glob
+	// and writes it to $out if it has changed, and writes the directories to $out.d
+	globRule = pctx.StaticRule("globRule",
+		blueprint.RuleParams{
+			Command:     fmt.Sprintf(`%s -o $out "$glob"`, globCmd),
+			Description: "glob $glob",
+
+			Restat:    true,
+			Generator: true,
+			Deps:      blueprint.DepsGCC,
+			Depfile:   "$out.d",
+		},
+		"glob")
+)
+
+func hasGlob(in []string) bool {
+	for _, s := range in {
+		if glob.IsGlob(s) {
+			return true
+		}
+	}
+
+	return false
+}
+
+func ExpandGlobs(ctx AndroidModuleContext, in []string) []string {
+	if !hasGlob(in) {
+		return in
+	}
+
+	out := make([]string, 0, len(in))
+	for _, s := range in {
+		if glob.IsGlob(s) {
+			out = append(out, Glob(ctx, s)...)
+		} else {
+			out = append(out, s)
+		}
+	}
+
+	return out
+}
+
+func Glob(ctx AndroidModuleContext, globPattern string) []string {
+	fileListFile := filepath.Join(ModuleOutDir(ctx), "glob", globToString(globPattern))
+	depFile := fileListFile + ".d"
+
+	// Get a globbed file list, and write out fileListFile and depFile
+	files, err := glob.GlobWithDepFile(globPattern, fileListFile, depFile)
+	if err != nil {
+		ctx.ModuleErrorf("glob: %s", err.Error())
+		return []string{globPattern}
+	}
+
+	// Create a rule to rebuild fileListFile if a directory in depFile changes.  fileListFile
+	// will only be rewritten if it has changed, preventing unnecesary build.ninja regenerations.
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      globRule,
+		Outputs:   []string{fileListFile},
+		Implicits: []string{globCmd},
+		Args: map[string]string{
+			"glob": globPattern,
+		},
+	})
+
+	// Phony rule so the cleanup phase doesn't delete the depFile
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:    blueprint.Phony,
+		Outputs: []string{depFile},
+	})
+
+	// Make build.ninja depend on the fileListFile
+	ctx.AddNinjaFileDeps(fileListFile)
+
+	return files
+}
+
+func globToString(glob string) string {
+	ret := ""
+	for _, c := range glob {
+		if c >= 'a' && c <= 'z' ||
+			c >= 'A' && c <= 'Z' ||
+			c >= '0' && c <= '9' ||
+			c == '_' || c == '-' || c == '/' {
+			ret += string(c)
+		}
+	}
+
+	return ret
+}

+ 327 - 0
common/module.go

@@ -0,0 +1,327 @@
+// 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 common
+
+import (
+	"blueprint"
+	"path/filepath"
+)
+
+var (
+	DeviceSharedLibrary = "shared_library"
+	DeviceStaticLibrary = "static_library"
+	DeviceExecutable    = "executable"
+	HostSharedLibrary   = "host_shared_library"
+	HostStaticLibrary   = "host_static_library"
+	HostExecutable      = "host_executable"
+)
+
+type AndroidModuleContext interface {
+	blueprint.ModuleContext
+
+	Arch() Arch
+	InstallFile(installPath, srcPath string)
+	CheckbuildFile(srcPath string)
+}
+
+type AndroidModule interface {
+	blueprint.Module
+
+	GenerateAndroidBuildActions(AndroidModuleContext)
+
+	base() *AndroidModuleBase
+	Disabled() bool
+	HostOrDevice() HostOrDevice
+}
+
+type AndroidDynamicDepender interface {
+	AndroidDynamicDependencies(ctx AndroidDynamicDependerModuleContext) []string
+}
+
+type AndroidDynamicDependerModuleContext interface {
+	blueprint.DynamicDependerModuleContext
+}
+
+type commonProperties struct {
+	Name         string
+	Deps         []string
+	ResourceDirs []string
+
+	// disabled: don't emit any build rules for this module
+	Disabled bool `android:"arch_variant"`
+
+	// multilib: control whether this module compiles for 32-bit, 64-bit, or both.  Possible values
+	// are "32" (compile for 32-bit only), "64" (compile for 64-bit only), "both" (compile for both
+	// architectures), or "first" (compile for 64-bit on a 64-bit platform, and 32-bit on a 32-bit
+	// platform
+	Compile_multilib string
+
+	// Set by ArchMutator
+	CompileArch Arch `blueprint:"mutated"`
+
+	// Set by InitAndroidModule
+	HostOrDeviceSupported HostOrDeviceSupported `blueprint:"mutated"`
+}
+
+type hostAndDeviceProperties struct {
+	Host_supported   bool
+	Device_supported bool
+}
+
+func InitAndroidModule(m AndroidModule, hod HostOrDeviceSupported, defaultMultilib string,
+	propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+	base := m.base()
+	base.module = m
+	base.commonProperties.HostOrDeviceSupported = hod
+
+	if hod == HostAndDeviceSupported {
+		// Default to module to device supported, host not supported, can override in module
+		// properties
+		base.hostAndDeviceProperties.Device_supported = true
+		propertyStructs = append(propertyStructs, &base.hostAndDeviceProperties)
+	}
+
+	return InitArchModule(m, defaultMultilib, propertyStructs...)
+}
+
+// A AndroidModuleBase object contains the properties that are common to all Android
+// modules.  It should be included as an anonymous field in every module
+// struct definition.  InitAndroidModule should then be called from the module's
+// factory function, and the return values from InitAndroidModule should be
+// returned from the factory function.
+//
+// The AndroidModuleBase type is responsible for implementing the
+// GenerateBuildActions method to support the blueprint.Module interface. This
+// method will then call the module's GenerateAndroidBuildActions method once
+// for each build variant that is to be built. GenerateAndroidBuildActions is
+// passed a AndroidModuleContext rather than the usual blueprint.ModuleContext.
+// AndroidModuleContext exposes extra functionality specific to the Android build
+// system including details about the particular build variant that is to be
+// generated.
+//
+// For example:
+//
+//     import (
+//         "android/soong/common"
+//         "blueprint"
+//     )
+//
+//     type myModule struct {
+//         common.AndroidModuleBase
+//         properties struct {
+//             MyProperty string
+//         }
+//     }
+//
+//     func NewMyModule() (blueprint.Module, []interface{}) {
+//         m := &myModule{}
+//         return common.InitAndroidModule(m, &m.properties)
+//     }
+//
+//     func (m *myModule) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
+//         // Get the CPU architecture for the current build variant.
+//         variantArch := ctx.Arch()
+//
+//         // ...
+//     }
+type AndroidModuleBase struct {
+	// Putting the curiously recurring thing pointing to the thing that contains
+	// the thing pattern to good use.
+	module AndroidModule
+
+	commonProperties        commonProperties
+	hostAndDeviceProperties hostAndDeviceProperties
+	generalProperties       []interface{}
+	archProperties          []*archProperties
+
+	noAddressSanitizer bool
+	installFiles       []string
+	checkbuildFiles    []string
+}
+
+func (a *AndroidModuleBase) base() *AndroidModuleBase {
+	return a
+}
+
+func (a *AndroidModuleBase) SetArch(arch Arch) {
+	a.commonProperties.CompileArch = arch
+}
+
+func (a *AndroidModuleBase) HostOrDevice() HostOrDevice {
+	return a.commonProperties.CompileArch.HostOrDevice
+}
+
+func (a *AndroidModuleBase) HostSupported() bool {
+	return a.commonProperties.HostOrDeviceSupported == HostSupported ||
+		a.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+			a.hostAndDeviceProperties.Host_supported
+}
+
+func (a *AndroidModuleBase) DeviceSupported() bool {
+	return a.commonProperties.HostOrDeviceSupported == DeviceSupported ||
+		a.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+			a.hostAndDeviceProperties.Device_supported
+}
+
+func (a *AndroidModuleBase) Disabled() bool {
+	return a.commonProperties.Disabled
+}
+
+func (a *AndroidModuleBase) computeInstallDeps(
+	ctx blueprint.ModuleContext) []string {
+
+	result := []string{}
+	ctx.VisitDepsDepthFirstIf(isFileInstaller,
+		func(m blueprint.Module) {
+			fileInstaller := m.(fileInstaller)
+			files := fileInstaller.filesToInstall()
+			result = append(result, files...)
+		})
+
+	return result
+}
+
+func (a *AndroidModuleBase) filesToInstall() []string {
+	return a.installFiles
+}
+
+func (p *AndroidModuleBase) NoAddressSanitizer() bool {
+	return p.noAddressSanitizer
+}
+
+func (p *AndroidModuleBase) resourceDirs() []string {
+	return p.commonProperties.ResourceDirs
+}
+
+func (a *AndroidModuleBase) generateModuleTarget(ctx blueprint.ModuleContext) {
+	if a != ctx.FinalModule().(AndroidModule).base() {
+		return
+	}
+
+	allInstalledFiles := []string{}
+	ctx.VisitAllModuleVariants(func(module blueprint.Module) {
+		if androidModule, ok := module.(AndroidModule); ok {
+			files := androidModule.base().installFiles
+			allInstalledFiles = append(allInstalledFiles, files...)
+		}
+	})
+
+	if len(allInstalledFiles) > 0 {
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:    blueprint.Phony,
+			Outputs: []string{ctx.ModuleName()},
+			Inputs:  allInstalledFiles,
+		})
+	}
+}
+
+func (a *AndroidModuleBase) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
+	actx := &androidDynamicDependerContext{
+		DynamicDependerModuleContext: ctx,
+		module: a,
+	}
+
+	if dynamic, ok := a.module.(AndroidDynamicDepender); ok {
+		return dynamic.AndroidDynamicDependencies(actx)
+	}
+
+	return nil
+}
+
+func (a *AndroidModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	androidCtx := &androidModuleContext{
+		ModuleContext: ctx,
+		installDeps:   a.computeInstallDeps(ctx),
+		installFiles:  a.installFiles,
+		arch:          a.commonProperties.CompileArch,
+	}
+
+	if a.commonProperties.Disabled {
+		return
+	}
+
+	a.module.GenerateAndroidBuildActions(androidCtx)
+	if ctx.Failed() {
+		return
+	}
+
+	a.generateModuleTarget(ctx)
+	if ctx.Failed() {
+		return
+	}
+}
+
+type androidModuleContext struct {
+	blueprint.ModuleContext
+	arch            Arch
+	installDeps     []string
+	installFiles    []string
+	checkbuildFiles []string
+}
+
+func (a *androidModuleContext) Build(pctx *blueprint.PackageContext, params blueprint.BuildParams) {
+	params.Optional = true
+	a.ModuleContext.Build(pctx, params)
+}
+
+func (a *androidModuleContext) Arch() Arch {
+	return a.arch
+}
+
+func (a *androidModuleContext) InstallFile(installPath, srcPath string) {
+	var fullInstallPath string
+	if a.arch.HostOrDevice.Device() {
+		// TODO: replace unset with a device name once we have device targeting
+		fullInstallPath = filepath.Join("out/target/product/unset/system", installPath,
+			filepath.Base(srcPath))
+	} else {
+		// TODO: replace unset with a host name
+		fullInstallPath = filepath.Join("out/host/unset/", installPath, filepath.Base(srcPath))
+	}
+
+	a.ModuleContext.Build(pctx, blueprint.BuildParams{
+		Rule:      Cp,
+		Outputs:   []string{fullInstallPath},
+		Inputs:    []string{srcPath},
+		OrderOnly: a.installDeps,
+	})
+
+	a.installFiles = append(a.installFiles, fullInstallPath)
+	a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+}
+
+func (a *androidModuleContext) CheckbuildFile(srcPath string) {
+	a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+}
+
+type androidDynamicDependerContext struct {
+	blueprint.DynamicDependerModuleContext
+	module *AndroidModuleBase
+}
+
+type fileInstaller interface {
+	filesToInstall() []string
+}
+
+func isFileInstaller(m blueprint.Module) bool {
+	_, ok := m.(fileInstaller)
+	return ok
+}
+
+func isAndroidModule(m blueprint.Module) bool {
+	_, ok := m.(AndroidModule)
+	return ok
+}

+ 83 - 0
common/paths.go

@@ -0,0 +1,83 @@
+// 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 common
+
+import (
+	"path/filepath"
+
+	"blueprint"
+)
+
+type Config interface {
+	CpPreserveSymlinksFlags() string
+	SrcDir() string
+}
+
+// ModuleOutDir returns the path to the module-specific output directory.
+func ModuleOutDir(ctx AndroidModuleContext) string {
+	return filepath.Join(".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
+}
+
+// ModuleSrcDir returns the path of the directory that all source file paths are
+// specified relative to.
+func ModuleSrcDir(ctx blueprint.ModuleContext) string {
+	config := ctx.Config().(Config)
+	return filepath.Join(config.SrcDir(), ctx.ModuleDir())
+}
+
+// ModuleBinDir returns the path to the module- and architecture-specific binary
+// output directory.
+func ModuleBinDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "bin")
+}
+
+// ModuleLibDir returns the path to the module- and architecture-specific
+// library output directory.
+func ModuleLibDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "lib")
+}
+
+// ModuleGenDir returns the module directory for generated files
+// path.
+func ModuleGenDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "gen")
+}
+
+// ModuleObjDir returns the module- and architecture-specific object directory
+// path.
+func ModuleObjDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "obj")
+}
+
+// ModuleGoPackageDir returns the module-specific package root directory path.
+// This directory is where the final package .a files are output and where
+// dependent modules search for this package via -I arguments.
+func ModuleGoPackageDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "pkg")
+}
+
+// ModuleIncludeDir returns the module-specific public include directory path.
+func ModuleIncludeDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "include")
+}
+
+// ModuleProtoDir returns the module-specific public proto include directory path.
+func ModuleProtoDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "proto")
+}
+
+func ModuleJSCompiledDir(ctx AndroidModuleContext) string {
+	return filepath.Join(ModuleOutDir(ctx), "js")
+}

+ 152 - 0
config/config.go

@@ -0,0 +1,152 @@
+// 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 config
+
+import (
+	"android/soong/common"
+	"encoding/json"
+	"fmt"
+	"os"
+	"runtime"
+)
+
+var _ common.Config = (*Config)(nil)
+
+// The configuration file name
+const ConfigFileName = "soong.config"
+
+// A FileConfigurableOptions contains options which can be configured by the
+// config file. These will be included in the config struct.
+type FileConfigurableOptions struct {
+}
+
+func NewFileConfigurableOptions() FileConfigurableOptions {
+	f := FileConfigurableOptions{}
+	return f
+}
+
+// A Config object represents the entire build configuration for Blue.
+type Config struct {
+	FileConfigurableOptions
+
+	srcDir string // the path of the root source directory
+}
+
+// loads configuration options from a JSON file in the cwd.
+func loadFromConfigFile(config *Config) error {
+	// Make a proxy config
+	var configProxy FileConfigurableOptions
+
+	// Try to open the file
+	configFileReader, err := os.Open(ConfigFileName)
+	defer configFileReader.Close()
+	if os.IsNotExist(err) {
+		// Need to create a file, so that blueprint & ninja don't get in
+		// a dependency tracking loop.
+		// Make a file-configurable-options with defaults, write it out using
+		// a json writer.
+		configProxy = NewFileConfigurableOptions()
+		err = saveToConfigFile(configProxy)
+		if err != nil {
+			return err
+		}
+	} else {
+		// Make a decoder for it
+		jsonDecoder := json.NewDecoder(configFileReader)
+		err = jsonDecoder.Decode(&configProxy)
+		if err != nil {
+			return fmt.Errorf("config file: %s did not parse correctly: "+err.Error(), ConfigFileName)
+		}
+	}
+
+	// Copy the configurable options out of the config_proxy into the config,
+	// and we're done!
+	config.FileConfigurableOptions = configProxy
+
+	// No error
+	return nil
+}
+
+func saveToConfigFile(config FileConfigurableOptions) error {
+	data, err := json.MarshalIndent(&config, "", "    ")
+	if err != nil {
+		return fmt.Errorf("cannot marshal config data: %s", err.Error())
+	}
+
+	configFileWriter, err := os.Create(ConfigFileName)
+	if err != nil {
+		return fmt.Errorf("cannot create empty config file %s: %s\n", ConfigFileName, err.Error())
+	}
+	defer configFileWriter.Close()
+
+	_, err = configFileWriter.Write(data)
+	if err != nil {
+		return fmt.Errorf("default config file: %s could not be written: %s", ConfigFileName, err.Error())
+	}
+
+	return nil
+}
+
+// New creates a new Config object.  The srcDir argument specifies the path to
+// the root source directory. It also loads the config file, if found.
+func New(srcDir string) (*Config, error) {
+	// Make a config with default options
+	config := &Config{srcDir: srcDir}
+
+	// Load any configurable options from the configuration file
+	err := loadFromConfigFile(config)
+	if err != nil {
+		return nil, err
+	}
+
+	return config, nil
+}
+
+func (c *Config) SrcDir() string {
+	return c.srcDir
+}
+
+// HostGoOS returns the OS of the system that the Go toolchain is being run on.
+func (c *Config) HostGoOS() string {
+	return runtime.GOOS
+}
+
+// PrebuiltOS returns the name of the host OS used in prebuilts directories
+func (c *Config) PrebuiltOS() string {
+	switch runtime.GOOS {
+	case "linux":
+		return "linux-x86"
+	case "darwin":
+		return "darwin-x86"
+	default:
+		panic("Unknown GOOS")
+	}
+}
+
+// GoRoot returns the path to the root directory of the Go toolchain.
+func (c *Config) GoRoot() string {
+	return fmt.Sprintf("%s/prebuilts/go/%s", c.srcDir, c.PrebuiltOS())
+}
+
+func (c *Config) CpPreserveSymlinksFlags() string {
+	switch c.HostGoOS() {
+	case "darwin":
+		return "-R"
+	case "linux":
+		return "-d"
+	default:
+		return ""
+	}
+}

+ 7 - 0
copygcclib.sh

@@ -0,0 +1,7 @@
+#!/bin/bash -e
+
+OUT=$1
+shift
+LIBPATH=$($@)
+cp -f $LIBPATH $OUT
+echo "$OUT: $LIBPATH" > ${OUT}.d

+ 56 - 0
doc.go

@@ -0,0 +1,56 @@
+// 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.
+
+// Soong is a builder for Android that uses Blueprint to parse Blueprints
+// files and Ninja to do the dependency tracking and subprocess management.
+// Soong itself is responsible for converting the modules read by Blueprint
+// into build rules, which will be written to a build.ninja file by Blueprint.
+//
+// Android build concepts:
+//
+// Device
+// A device is a piece of hardware that will be running Android.  It may specify
+// global settings like architecture, filesystem configuration, initialization
+// scripts, and device drivers.  A device may support all variants of a single
+// piece of hardware, or multiple devices may be used for different variants.
+// A build is never targeted directly at a device, it is always targeted at a
+// "product".
+//
+// Product
+// A product is a configuration of a device, often for a specific market or
+// use case.  It is sometimes referred to as a "SKU".  A product defines
+// global settings like supported languages, supported use cases, preinstalled
+// modules, and user-visible behavior choices.  A product selects one and only
+// one device.
+//
+// Module
+// A module is a definition of something to be built.  It may be a C library or
+// binary, a java library, an Android app, etc.  A module may be built for multiple
+// targets, even in a single build, for example host and device, or 32-bit device
+// and 64-bit device.
+//
+// Installed module
+// An installed module is one that has been requested by the selected product,
+// or a dependency of an installed module.
+//
+// Target architecture
+// The target architecture is the preferred architecture supported by the selected
+// device.  It is most commonly 32-bit arm, but may also be 64-bit arm, 32-bit or
+// 64-bit x86, or mips.
+//
+// Secondary architecture
+// The secondary architecture specifies the architecture to compile a second copy
+// of some modules for devices that support multiple architectures, for example
+// 64-bit devices that also support 32-bit binaries.
+package soong

+ 129 - 0
glob/glob.go

@@ -0,0 +1,129 @@
+// 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 glob
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"blueprint/deptools"
+)
+
+func IsGlob(glob string) bool {
+	return strings.IndexAny(glob, "*?[") >= 0
+}
+
+// GlobWithDepFile finds all files that match glob.  It compares the list of files
+// against the contents of fileListFile, and rewrites fileListFile if it has changed.  It also
+// writes all of the the directories it traversed as a depenencies on fileListFile to depFile.
+//
+// The format of glob is either path/*.ext for a single directory glob, or path/**/*.ext
+// for a recursive glob.
+//
+// Returns a list of file paths, and an error.
+func GlobWithDepFile(glob, fileListFile, depFile string) (files []string, err error) {
+	globPattern := filepath.Base(glob)
+	globDir := filepath.Dir(glob)
+	recursive := false
+
+	if filepath.Base(globDir) == "**" {
+		recursive = true
+		globDir = filepath.Dir(globDir)
+	}
+
+	var dirs []string
+
+	err = filepath.Walk(globDir,
+		func(path string, info os.FileInfo, err error) error {
+			if err != nil {
+				return err
+			}
+
+			if info.Mode().IsDir() {
+				dirs = append(dirs, path)
+				if !recursive && path != globDir {
+					return filepath.SkipDir
+				}
+			} else if info.Mode().IsRegular() {
+				match, err := filepath.Match(globPattern, info.Name())
+				if err != nil {
+					return err
+				}
+				if match {
+					files = append(files, path)
+				}
+			}
+
+			return nil
+		})
+
+	fileList := strings.Join(files, "\n")
+
+	writeFileIfChanged(fileListFile, []byte(fileList), 0666)
+	deptools.WriteDepFile(depFile, fileListFile, dirs)
+
+	return
+}
+
+func writeFileIfChanged(filename string, data []byte, perm os.FileMode) error {
+	var isChanged bool
+
+	dir := filepath.Dir(filename)
+	err := os.MkdirAll(dir, 0777)
+	if err != nil {
+		return err
+	}
+
+	info, err := os.Stat(filename)
+	if err != nil {
+		if os.IsNotExist(err) {
+			// The file does not exist yet.
+			isChanged = true
+		} else {
+			return err
+		}
+	} else {
+		if info.Size() != int64(len(data)) {
+			isChanged = true
+		} else {
+			oldData, err := ioutil.ReadFile(filename)
+			if err != nil {
+				return err
+			}
+
+			if len(oldData) != len(data) {
+				isChanged = true
+			} else {
+				for i := range data {
+					if oldData[i] != data[i] {
+						isChanged = true
+						break
+					}
+				}
+			}
+		}
+	}
+
+	if isChanged {
+		err = ioutil.WriteFile(filename, data, perm)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 3 - 0
root.bp

@@ -1,4 +1,7 @@
 subdirs = [
     "build/blueprint",
     "build/soong",
+    "bionic/*",
+    "external/*",
+    "system/core/*",
 ]