pulumi/cmd/build.go
joeduffy b408c3ce2a Pass compiler options to template evaluation
In some cases, we want to specialize template generation based on
the options passed to the compiler.  This change flows them through
so that they can be accessed as

        {{if .Options.SomeSetting}}
        ...
        {{end}}
2016-12-09 12:42:28 -08:00

153 lines
4 KiB
Go

// Copyright 2016 Marapongo, Inc. All rights reserved.
package cmd
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/golang/glog"
"github.com/spf13/cobra"
"github.com/marapongo/mu/pkg/compiler"
"github.com/marapongo/mu/pkg/compiler/backends"
"github.com/marapongo/mu/pkg/compiler/backends/clouds"
"github.com/marapongo/mu/pkg/compiler/backends/schedulers"
)
// defaultIn is where the Mu compiler looks for inputs by default.
const defaultInp = "."
// defaultOutput is where the Mu compiler places build artifacts by default.
const defaultOutp = ".mu"
func newBuildCmd() *cobra.Command {
var outp string
var cluster string
var targetArch string
var skipCodegen bool
var cmd = &cobra.Command{
Use: "build [source] -- [args]",
Short: "Compile a Mu Stack",
Run: func(cmd *cobra.Command, args []string) {
flags := cmd.Flags()
ddash := flags.ArgsLenAtDash()
// If there's a --, we need to separate out the command args from the stack args.
var sargs []string
if ddash != -1 {
sargs = args[ddash:]
args = args[0:ddash]
}
// Fetch the input source directory.
inp := defaultInp
if len(args) > 0 {
inp = args[0]
}
abs, err := filepath.Abs(inp)
if err != nil {
glog.Fatal(err)
}
opts := compiler.DefaultOpts(abs)
if skipCodegen {
opts.SkipCodegen = true
}
// Set the cluster and architecture if specified.
opts.Cluster = cluster
setCloudArchOptions(targetArch, opts)
// See if there are any arguments and, if so, accumulate them.
if len(sargs) > 0 {
opts.Args = make(map[string]string)
// TODO[marapongo/mu#7]: This is a very rudimentary parser. We can and should do better.
for i := 0; i < len(sargs); i++ {
sarg := sargs[i]
// Eat - or -- at the start.
if sarg[0] == '-' {
sarg = sarg[1:]
if sarg[0] == '-' {
sarg = sarg[1:]
}
}
// Now find an k=v, and split the k/v part.
if eq := strings.IndexByte(sarg, '='); eq != -1 {
opts.Args[sarg[:eq]] = sarg[eq+1:]
} else {
// No =; if the next arg doesn't start with '-', use it. Else it must be a boolean "true".
if i+1 < len(sargs) && sargs[i+1][0] != '-' {
opts.Args[sarg] = sargs[i+1]
i++
} else {
// TODO(joe): support --no-key style "false"s.
opts.Args[sarg] = "true"
}
}
}
}
// Now new up a compiler and actually perform the build.
mup := compiler.NewCompiler(opts)
mup.Build(abs, outp)
},
}
cmd.PersistentFlags().StringVar(
&outp, "out", defaultOutp,
"The directory in which to place build artifacts")
cmd.PersistentFlags().StringVarP(
&cluster, "cluster", "c", "",
"Generate output for an existing, named cluster")
cmd.PersistentFlags().StringVarP(
&targetArch, "target", "t", "",
"Generate output for the target cloud architecture (format: \"cloud[:scheduler]\")")
cmd.PersistentFlags().BoolVar(
&skipCodegen, "skip-codegen", false,
"Skip code-generation phases of the compiler")
return cmd
}
func setCloudArchOptions(arch string, opts *compiler.Options) {
// If an architecture was specified, parse the pieces and set the options. This isn't required because stacks
// and workspaces can have defaults. This simply overrides or provides one where none exists.
if arch != "" {
// The format is "cloud[:scheduler]"; parse out the pieces.
var cloud string
var scheduler string
if delim := strings.IndexRune(arch, ':'); delim != -1 {
cloud = arch[:delim]
scheduler = arch[delim+1:]
} else {
cloud = arch
}
cloudArch, ok := clouds.Values[cloud]
if !ok {
fmt.Fprintf(os.Stderr, "Unrecognized cloud arch '%v'\n", cloud)
os.Exit(-1)
}
var schedulerArch schedulers.Arch
if scheduler != "" {
schedulerArch, ok = schedulers.Values[scheduler]
if !ok {
fmt.Fprintf(os.Stderr, "Unrecognized cloud scheduler arch '%v'\n", scheduler)
os.Exit(-1)
}
}
opts.Arch = backends.Arch{
Cloud: cloudArch,
Scheduler: schedulerArch,
}
}
}