Add a mu verify command
This adds a `mu verify` command that simply runs the verification pass against a MuPackage and its MuIL. This is handy for compiler authors to verify that the right stuff is getting emitted.
This commit is contained in:
parent
a1c94275e0
commit
15ba2949dc
3 changed files with 123 additions and 17 deletions
|
@ -41,6 +41,7 @@ func NewMuCmd() *cobra.Command {
|
|||
cmd.AddCommand(newEvalCmd())
|
||||
cmd.AddCommand(newGetCmd())
|
||||
cmd.AddCommand(newPlanCmd())
|
||||
cmd.AddCommand(newVerifyCmd())
|
||||
cmd.AddCommand(newVersionCmd())
|
||||
|
||||
return cmd
|
||||
|
|
63
cmd/verify.go
Normal file
63
cmd/verify.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2016 Marapongo, Inc. All rights reserved.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/marapongo/mu/pkg/compiler"
|
||||
"github.com/marapongo/mu/pkg/util/cmdutil"
|
||||
"github.com/marapongo/mu/pkg/util/contract"
|
||||
)
|
||||
|
||||
func newVerifyCmd() *cobra.Command {
|
||||
var cmd = &cobra.Command{
|
||||
Use: "verify [package]",
|
||||
Short: "Check that a MuPackage and its MuIL are correct",
|
||||
Long: "Check that a MuPackage and its MuIL are correct, reporting any errors.\n" +
|
||||
"\n" +
|
||||
"A MuPackage contains intermediate language (MuIL) that encodes symbols,\n" +
|
||||
"definitions, and executable code. This MuIL must obey a set of specific rules\n" +
|
||||
"for it to be valid. Otherwise, evaluating it will fail.\n" +
|
||||
"\n" +
|
||||
"The verify command thoroughly checks the MuIL against these rules, and issues\n" +
|
||||
"errors anywhere it doesn't obey them. This is generally useful for tools developers\n" +
|
||||
"and can ensure that MuPackages do not fail at runtime, when such invariants are checked.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// In the case of an argument, load that specific package and new up a compiler based on its base path.
|
||||
// Otherwise, use the default workspace and package logic (which consults the current working directory).
|
||||
var success bool
|
||||
if len(args) == 0 {
|
||||
comp, err := compiler.Newwd(nil)
|
||||
if err != nil {
|
||||
contract.Failf("fatal: %v", err)
|
||||
}
|
||||
success = comp.Verify()
|
||||
} else {
|
||||
fn := args[0]
|
||||
if pkg := cmdutil.ReadPackageFromArg(fn); pkg != nil {
|
||||
var comp compiler.Compiler
|
||||
var err error
|
||||
if fn == "-" {
|
||||
comp, err = compiler.Newwd(nil)
|
||||
} else {
|
||||
comp, err = compiler.New(filepath.Dir(fn), nil)
|
||||
}
|
||||
if err != nil {
|
||||
contract.Failf("fatal: %v", err)
|
||||
}
|
||||
success = comp.VerifyPackage(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
if !success {
|
||||
fmt.Printf("fatal: MuPackage verification failed\n")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
|
@ -34,6 +34,12 @@ type Compiler interface {
|
|||
CompilePath(path string) graph.Graph
|
||||
// CompilePackage compiles a given package object into its associated graph form.
|
||||
CompilePackage(pkg *pack.Package) graph.Graph
|
||||
// Verify detects a package from the workspace and validates its MuIL contents.
|
||||
Verify() bool
|
||||
// VerifyPath verifies a package given its path, validating that its MuIL contents are correct.
|
||||
VerifyPath(path string) bool
|
||||
// VerifyPackage verifies a given package object, validating that its MuIL contents are correct.
|
||||
VerifyPackage(pkg *pack.Package) bool
|
||||
}
|
||||
|
||||
// compiler is the canonical implementation of the Mu compiler.
|
||||
|
@ -100,30 +106,18 @@ func (c *compiler) Workspace() workspace.W { return c.w }
|
|||
|
||||
// Compile attempts to detect the package from the current working directory and, provided that succeeds, compiles it.
|
||||
func (c *compiler) Compile() graph.Graph {
|
||||
path, err := c.w.DetectPackage()
|
||||
if err != nil {
|
||||
c.Diag().Errorf(errors.ErrorIO, err)
|
||||
return nil
|
||||
} else if path == "" {
|
||||
c.Diag().Errorf(errors.ErrorMissingMufile, c.ctx.Path)
|
||||
return nil
|
||||
} else {
|
||||
if path := c.detectPackage(); path != "" {
|
||||
return c.CompilePath(path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CompilePath loads a package at the given path and compiles it into a graph.
|
||||
func (c *compiler) CompilePath(path string) graph.Graph {
|
||||
doc, err := diag.ReadDocument(path)
|
||||
if err != nil {
|
||||
c.Diag().Errorf(errors.ErrorCouldNotReadMufile.AtFile(path), err)
|
||||
return nil
|
||||
if pkg := c.readPackage(path); pkg != nil {
|
||||
return c.CompilePackage(pkg)
|
||||
}
|
||||
pkg := c.reader.ReadPackage(doc)
|
||||
if pkg == nil {
|
||||
return nil
|
||||
}
|
||||
return c.CompilePackage(pkg)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CompilePackage compiles the given package into a graph.
|
||||
|
@ -170,3 +164,51 @@ func (c *compiler) CompilePackage(pkg *pack.Package) graph.Graph {
|
|||
// Finally ask the graph generator to return what it has seen in graph form.
|
||||
return gg.Graph()
|
||||
}
|
||||
|
||||
// Verify detects a package from the workspace and validates its MuIL contents.
|
||||
func (c *compiler) Verify() bool {
|
||||
if path := c.detectPackage(); path != "" {
|
||||
return c.VerifyPath(path)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// VerifyPath verifies a package given its path, validating that its MuIL contents are correct.
|
||||
func (c *compiler) VerifyPath(path string) bool {
|
||||
if pkg := c.readPackage(path); pkg != nil {
|
||||
return c.VerifyPackage(pkg)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// VerifyPackage verifies a given package object, validating that its MuIL contents are correct.
|
||||
func (c *compiler) VerifyPackage(pkg *pack.Package) bool {
|
||||
// To verify a package, simply run the binder aspects of it.
|
||||
b := binder.New(c.w, c.ctx, c.reader)
|
||||
b.BindPackage(pkg)
|
||||
return c.Diag().Success()
|
||||
}
|
||||
|
||||
// detectPackage detects a package in the current workspace.
|
||||
func (c *compiler) detectPackage() string {
|
||||
path, err := c.w.DetectPackage()
|
||||
if err != nil {
|
||||
c.Diag().Errorf(errors.ErrorIO, err)
|
||||
return ""
|
||||
}
|
||||
if path == "" {
|
||||
c.Diag().Errorf(errors.ErrorMissingMufile, c.ctx.Path)
|
||||
return ""
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// readPackage loads a package from the given path, issuing errors if anything bad happens.
|
||||
func (c *compiler) readPackage(path string) *pack.Package {
|
||||
doc, err := diag.ReadDocument(path)
|
||||
if err != nil {
|
||||
c.Diag().Errorf(errors.ErrorCouldNotReadMufile.AtFile(path), err)
|
||||
return nil
|
||||
}
|
||||
return c.reader.ReadPackage(doc)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue