Delay dependency errors to ninja time for unbundled builds

Unbundled builds may use a subset of the tree, which can bring in unused
modules but not their dependencies.  Delay handling of dependency errors
for unbundled builds to ninja time, which will prevent errors if only
modules with satisified dependencies are built.

Change-Id: Ib93bae93fcfa0b55df500a30d8e35231ffb0987c
diff --git a/cc/cc.go b/cc/cc.go
index 6315cbf..312e42b 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -740,7 +740,7 @@
 				return
 			}
 		})
-		if !found {
+		if !found && !inList(n, ctx.GetMissingDependencies()) {
 			ctx.ModuleErrorf("unsatisified dependency on %q", n)
 		}
 	}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 254f922..1bb8fcd 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -44,5 +44,7 @@
 	// Temporary hack
 	//ctx.SetIgnoreUnknownModuleTypes(true)
 
+	ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
+
 	bootstrap.Main(ctx, configuration, configuration.ConfigFileName, configuration.ProductVariablesFileName)
 }
diff --git a/common/config.go b/common/config.go
index 971c4c1..74e0660 100644
--- a/common/config.go
+++ b/common/config.go
@@ -22,8 +22,12 @@
 	"runtime"
 	"strings"
 	"sync"
+
+	"github.com/google/blueprint/proptools"
 )
 
+var Bool = proptools.Bool
+
 // The configuration file name
 const configFileName = "soong.config"
 const productVariablesFileName = "soong.variables"
@@ -287,3 +291,7 @@
 func (c *config) DefaultAppCertificate(ctx PathContext) SourcePath {
 	return c.DefaultAppCertificateDir(ctx).Join(ctx, "testkey")
 }
+
+func (c *config) AllowMissingDependencies() bool {
+	return Bool(c.ProductVariables.Unbundled_build)
+}
diff --git a/common/defs.go b/common/defs.go
index 9e185e4..1250068 100644
--- a/common/defs.go
+++ b/common/defs.go
@@ -59,6 +59,13 @@
 			Description: "symlink $out",
 		},
 		"fromPath")
+
+	ErrorRule = pctx.StaticRule("Error",
+		blueprint.RuleParams{
+			Command:     `echo "$error" && false`,
+			Description: "error building $out",
+		},
+		"error")
 )
 
 func init() {
diff --git a/common/module.go b/common/module.go
index e03b006..a3de5f6 100644
--- a/common/module.go
+++ b/common/module.go
@@ -15,7 +15,9 @@
 package common
 
 import (
+	"fmt"
 	"path/filepath"
+	"strings"
 
 	"android/soong"
 	"android/soong/glob"
@@ -363,6 +365,7 @@
 		androidBaseContextImpl: a.androidBaseContextFactory(ctx),
 		installDeps:            a.computeInstallDeps(ctx),
 		installFiles:           a.installFiles,
+		missingDeps:            ctx.GetMissingDependencies(),
 	}
 
 	if !a.Enabled() {
@@ -397,9 +400,28 @@
 	installDeps     Paths
 	installFiles    Paths
 	checkbuildFiles Paths
+	missingDeps     []string
+}
+
+func (a *androidModuleContext) ninjaError(outputs []string, err error) {
+	a.ModuleContext.Build(pctx, blueprint.BuildParams{
+		Rule:     ErrorRule,
+		Outputs:  outputs,
+		Optional: true,
+		Args: map[string]string{
+			"error": err.Error(),
+		},
+	})
+	return
 }
 
 func (a *androidModuleContext) Build(pctx blueprint.PackageContext, params blueprint.BuildParams) {
+	if a.missingDeps != nil {
+		a.ninjaError(params.Outputs, fmt.Errorf("module %s missing dependencies: %s\n",
+			a.ModuleName(), strings.Join(a.missingDeps, ", ")))
+		return
+	}
+
 	params.Optional = true
 	a.ModuleContext.Build(pctx, params)
 }
@@ -425,9 +447,19 @@
 		bparams.Implicits = append(bparams.Implicits, params.Implicit.String())
 	}
 
+	if a.missingDeps != nil {
+		a.ninjaError(bparams.Outputs, fmt.Errorf("module %s missing dependencies: %s\n",
+			a.ModuleName(), strings.Join(a.missingDeps, ", ")))
+		return
+	}
+
 	a.ModuleContext.Build(pctx, bparams)
 }
 
+func (a *androidModuleContext) GetMissingDependencies() []string {
+	return a.missingDeps
+}
+
 func (a *androidBaseContextImpl) Arch() Arch {
 	return a.arch
 }