pulumi/pkg/compiler/compiler_sema.go

97 lines
3.2 KiB
Go
Raw Normal View History

// Copyright 2016 Marapongo, Inc. All rights reserved.
package compiler
import (
"github.com/golang/glog"
"github.com/marapongo/mu/pkg/ast"
"github.com/marapongo/mu/pkg/diag"
"github.com/marapongo/mu/pkg/errors"
"github.com/marapongo/mu/pkg/util"
"github.com/marapongo/mu/pkg/workspace"
)
// buildDocumentSema runs the middle semantic analysis phases of the compiler.
func (c *compiler) buildDocumentSema(w workspace.W, stack *ast.Stack) {
// Perform semantic analysis on all stacks passes to validate, transform, and/or update the AST.
b := NewBinder(c)
c.bindStack(b, w, stack)
if !c.Diag().Success() {
return
}
}
// bindStack performs the two phases of binding plus dependency resolution for the given Stack.
func (c *compiler) bindStack(b Binder, w workspace.W, stack *ast.Stack) {
util.Assert(stack != nil)
// First prepare the AST for binding.
deprefs := b.PrepareStack(stack)
if !c.Diag().Success() {
return
}
// Next, resolve all dependencies discovered during this first pass.
depdocs := make(ast.DependencyDocuments)
for _, ref := range deprefs {
// Only resolve dependencies that are currently unknown. This will exlude built-in types that have already
// been bound to a stack during the first phase of binding. Note that we don't actually parse and perform
// template substitution here; instead, we remember the document and let the binder do this, since it has
// all of the information necessary to create a unique Stack per-PropertyBag used to instantiate it.
if doc := c.resolveDependency(w, stack, ref); doc != nil {
depdocs[ref] = doc
}
}
if !c.Diag().Success() {
return
}
// Complete the binding process.
deps := b.BindStack(stack, depdocs)
if !c.Diag().Success() {
return
}
// Now ensure we bind all dependency stacks too.
for _, dep := range deps {
c.bindStack(b, w, dep)
}
}
// resolveDependency loads up the target dependency from the current workspace using the stack resolution rules.
func (c *compiler) resolveDependency(w workspace.W, stack *ast.Stack, ref ast.Ref) *diag.Document {
glog.V(3).Infof("Loading Stack %v dependency %v", stack.Name, ref)
// First, see if we've already loaded this dependency (anywhere in any Stacks). If yes, reuse it.
// TODO: check for version mismatches.
if doc, exists := c.deps[ref]; exists {
return doc
}
// There are many places a dependency could come from. Consult the workspace for a list of those paths. It will
// return a number of them, in preferred order, and we simply probe each one until we find something.
dep := ref.MustParse()
for _, loc := range w.DepCandidates(dep) {
// Try to read this location as a document.
isMufile := workspace.IsMufile(loc, c.Diag())
glog.V(5).Infof("Probing for dependency %v at %v: %v", dep, loc, isMufile)
if isMufile {
doc, err := diag.ReadDocument(loc)
if err != nil {
c.Diag().Errorf(errors.ErrorCouldNotReadMufile.AtFile(loc), err)
return nil
}
// Memoize this in the compiler's cache and return it.
c.deps[ref] = doc
return doc
}
}
// If we got to this spot, we could not find the dependency. Issue an error and bail out.
c.Diag().Errorf(errors.ErrorStackTypeNotFound.At(stack), ref)
return nil
}