Merge pull request #97 from danw/eval

Add SingletonContext.Eval to evaluate ninja strings
diff --git a/context.go b/context.go
index dfe4d37..ca3f919 100644
--- a/context.go
+++ b/context.go
@@ -1846,6 +1846,7 @@
 			context: c,
 			config:  config,
 			scope:   scope,
+			globals: liveGlobals,
 		}
 
 		func() {
diff --git a/singleton_ctx.go b/singleton_ctx.go
index a2d6109..e761e77 100644
--- a/singleton_ctx.go
+++ b/singleton_ctx.go
@@ -44,6 +44,11 @@
 	// set at most one time for a single build, later calls are ignored.
 	SetNinjaBuildDir(pctx PackageContext, value string)
 
+	// Eval takes a string with embedded ninja variables, and returns a string
+	// with all of the variables recursively expanded. Any variables references
+	// are expanded in the scope of the PackageContext.
+	Eval(pctx PackageContext, ninjaStr string) (string, error)
+
 	VisitAllModules(visit func(Module))
 	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
 	VisitDepsDepthFirst(module Module, visit func(Module))
@@ -64,6 +69,7 @@
 	context *Context
 	config  interface{}
 	scope   *localScope
+	globals *liveTracker
 
 	ninjaFileDeps []string
 	errs          []error
@@ -149,6 +155,22 @@
 	s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def)
 }
 
+func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) {
+	s.scope.ReparentTo(pctx)
+
+	ninjaStr, err := parseNinjaString(s.scope, str)
+	if err != nil {
+		return "", err
+	}
+
+	err = s.globals.addNinjaStringDeps(ninjaStr)
+	if err != nil {
+		return "", err
+	}
+
+	return ninjaStr.Eval(s.globals.variables)
+}
+
 func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) {
 	s.context.requireNinjaVersion(major, minor, micro)
 }