Add optional_subdirs variable support

When building a subset of a source tree, some of the subdirectories
mentioned in the subdirs variable may be missing.  Add support for an
optional_subdirs variable, which won't error out if the requested
directory is missing.
diff --git a/context.go b/context.go
index c8bc1f9..83ff6ae 100644
--- a/context.go
+++ b/context.go
@@ -458,6 +458,7 @@
 
 	scope = parser.NewScope(scope)
 	scope.Remove("subdirs")
+	scope.Remove("optional_subdirs")
 	scope.Remove("build")
 	file, errs = parser.ParseAndEval(filename, r, scope)
 	if len(errs) > 0 {
@@ -482,6 +483,11 @@
 		errs = append(errs, err)
 	}
 
+	optionalSubdirs, optionalSubdirsPos, err := getLocalStringListFromScope(scope, "optional_subdirs")
+	if err != nil {
+		errs = append(errs, err)
+	}
+
 	build, buildPos, err := getLocalStringListFromScope(scope, "build")
 	if err != nil {
 		errs = append(errs, err)
@@ -489,11 +495,24 @@
 
 	subBlueprintsName, _, err := getStringFromScope(scope, "subname")
 
-	blueprints, deps, newErrs := c.findSubdirBlueprints(filepath.Dir(filename), subdirs, build,
-		subBlueprintsName, subdirsPos, buildPos)
-	if len(newErrs) > 0 {
-		errs = append(errs, newErrs...)
-	}
+	var blueprints []string
+
+	newBlueprints, newDeps, newErrs := c.findBuildBlueprints(filepath.Dir(filename), build, buildPos)
+	blueprints = append(blueprints, newBlueprints...)
+	deps = append(deps, newDeps...)
+	errs = append(errs, newErrs...)
+
+	newBlueprints, newDeps, newErrs = c.findSubdirBlueprints(filepath.Dir(filename), subdirs, subdirsPos,
+		subBlueprintsName, false)
+	blueprints = append(blueprints, newBlueprints...)
+	deps = append(deps, newDeps...)
+	errs = append(errs, newErrs...)
+
+	newBlueprints, newDeps, newErrs = c.findSubdirBlueprints(filepath.Dir(filename), optionalSubdirs,
+		optionalSubdirsPos, subBlueprintsName, true)
+	blueprints = append(blueprints, newBlueprints...)
+	deps = append(deps, newDeps...)
+	errs = append(errs, newErrs...)
 
 	subBlueprintsAndScope := make([]stringAndScope, len(blueprints))
 	for i, b := range blueprints {
@@ -703,8 +722,55 @@
 	}
 }
 
-func (c *Context) findSubdirBlueprints(dir string, subdirs, build []string, subBlueprintsName string,
-	subdirsPos, buildPos scanner.Position) (blueprints, deps []string, errs []error) {
+func (c *Context) findBuildBlueprints(dir string, build []string,
+	buildPos scanner.Position) (blueprints, deps []string, errs []error) {
+
+	for _, file := range build {
+		globPattern := filepath.Join(dir, file)
+		matches, matchedDirs, err := pathtools.Glob(globPattern)
+		if err != nil {
+			errs = append(errs, &Error{
+				Err: fmt.Errorf("%q: %s", globPattern, err.Error()),
+				Pos: buildPos,
+			})
+			continue
+		}
+
+		if len(matches) == 0 {
+			errs = append(errs, &Error{
+				Err: fmt.Errorf("%q: not found", globPattern),
+				Pos: buildPos,
+			})
+		}
+
+		// Depend on all searched directories so we pick up future changes.
+		deps = append(deps, matchedDirs...)
+
+		for _, foundBlueprints := range matches {
+			fileInfo, err := os.Stat(foundBlueprints)
+			if os.IsNotExist(err) {
+				errs = append(errs, &Error{
+					Err: fmt.Errorf("%q not found", foundBlueprints),
+				})
+				continue
+			}
+
+			if fileInfo.IsDir() {
+				errs = append(errs, &Error{
+					Err: fmt.Errorf("%q is a directory", foundBlueprints),
+				})
+				continue
+			}
+
+			blueprints = append(blueprints, foundBlueprints)
+		}
+	}
+
+	return blueprints, deps, errs
+}
+
+func (c *Context) findSubdirBlueprints(dir string, subdirs []string, subdirsPos scanner.Position,
+	subBlueprintsName string, optional bool) (blueprints, deps []string, errs []error) {
 
 	for _, subdir := range subdirs {
 		globPattern := filepath.Join(dir, subdir)
@@ -717,7 +783,7 @@
 			continue
 		}
 
-		if len(matches) == 0 {
+		if len(matches) == 0 && !optional {
 			errs = append(errs, &Error{
 				Err: fmt.Errorf("%q: not found", globPattern),
 				Pos: subdirsPos,
@@ -763,47 +829,6 @@
 		}
 	}
 
-	for _, file := range build {
-		globPattern := filepath.Join(dir, file)
-		matches, matchedDirs, err := pathtools.Glob(globPattern)
-		if err != nil {
-			errs = append(errs, &Error{
-				Err: fmt.Errorf("%q: %s", globPattern, err.Error()),
-				Pos: buildPos,
-			})
-			continue
-		}
-
-		if len(matches) == 0 {
-			errs = append(errs, &Error{
-				Err: fmt.Errorf("%q: not found", globPattern),
-				Pos: buildPos,
-			})
-		}
-
-		// Depend on all searched directories so we pick up future changes.
-		deps = append(deps, matchedDirs...)
-
-		for _, foundBlueprints := range matches {
-			fileInfo, err := os.Stat(foundBlueprints)
-			if os.IsNotExist(err) {
-				errs = append(errs, &Error{
-					Err: fmt.Errorf("%q not found", foundBlueprints),
-				})
-				continue
-			}
-
-			if fileInfo.IsDir() {
-				errs = append(errs, &Error{
-					Err: fmt.Errorf("%q is a directory", foundBlueprints),
-				})
-				continue
-			}
-
-			blueprints = append(blueprints, foundBlueprints)
-		}
-	}
-
 	return blueprints, deps, errs
 }