func newCompileError(paths *model.RevelContainer, output []byte) *utils.SourceError {
	errorMatch := regexp.MustCompile(`(?m)^([^:#]+):(\d+):(\d+:)? (.*)$`).
		FindSubmatch(output)
	if errorMatch == nil {
		errorMatch = regexp.MustCompile(`(?m)^(.*?):(\d+):\s(.*?)$`).FindSubmatch(output)
		if errorMatch == nil {
			utils.Logger.Error("Failed to parse build errors", "error", string(output))
			return &utils.SourceError{
				SourceType:  "Go code",
				Title:       "Go Compilation Error",
				Description: "See console for build error.",
			}
		}
		errorMatch = append(errorMatch, errorMatch[3])
		utils.Logger.Error("Build errors", "errors", string(output))
	}
	findInPaths := func(relFilename string) string {
		gopaths := filepath.SplitList(build.Default.GOPATH)
		for _, gp := range gopaths {
			newPath := filepath.Join(gp, "src", paths.ImportPath, relFilename)
			println(newPath)
			if utils.Exists(newPath) {
				return newPath
			}
		}
		newPath, _ := filepath.Abs(relFilename)
		utils.Logger.Warn("Could not find in GO path", "file", relFilename)
		return newPath
	}
	var (
		relFilename = string(errorMatch[1])  
		absFilename = findInPaths(relFilename)
		line, _ = strconv.Atoi(string(errorMatch[2]))
		description = string(errorMatch[4])
		compileError = &utils.SourceError{
			SourceType:  "Go code",
			Title:       "Go Compilation Error",
			Path:        relFilename,
			Description: description,
			Line:        line,
		}
	)
	errorLink := paths.Config.StringDefault("error.link", "")
	if errorLink != "" {
		compileError.SetLink(errorLink)
	}
	fileStr, err := utils.ReadLines(absFilename)
	if err != nil {
		compileError.MetaError = absFilename + ": " + err.Error()
		utils.Logger.Info("Unable to readlines " + compileError.MetaError, "error", err)
		return compileError
	}
	compileError.SourceLines = fileStr
	return compileError
}
Parse the output of the "go build" command.
Return a detailed Error.
package harness
import (
	"fmt"
	"go/build"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"time"
	"github.com/revel/cmd/model"
	_ "github.com/revel/cmd/parser"
	"github.com/revel/cmd/utils"
	"github.com/revel/cmd/parser2"
	"github.com/revel/cmd/parser"
)
var importErrorPattern = regexp.MustCompile("cannot find package \"([^\"]+)\"")
type ByString []*model.TypeInfo
func (c ByString) Len() int {
	return len(c)
}
func (c ByString) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}
func (c ByString) Less(i, j int) bool {
	return c[i].String() < c[j].String()
}
func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err error) {
	cleanSource(paths, "tmp", "routes")
	var sourceInfo *model.SourceInfo
	if c.HistoricBuildMode {
		sourceInfo, err = parser.ProcessSource(paths)
	} else {
		sourceInfo, err = parser2.ProcessSource(paths)
	}
	if err != nil {
		return
	}
	if dbImportPath, found := paths.Config.String("db.import"); found {
		sourceInfo.InitImportPaths = append(sourceInfo.InitImportPaths, strings.Split(dbImportPath, ",")...)
	}
	controllers := sourceInfo.ControllerSpecs()
	sort.Stable(ByString(controllers))
	templateArgs := map[string]interface{}{
		"ImportPath":     paths.ImportPath,
		"Controllers":    controllers,
		"ValidationKeys": sourceInfo.ValidationKeys,
		"ImportPaths":    calcImportAliases(sourceInfo),
		"TestSuites":     sourceInfo.TestSuites(),
	}
	cleanSource(paths, "tmp", "routes")
	if err = genSource(paths, "tmp", "main.go", RevelMainTemplate, templateArgs); err != nil {
		return
	}
	if err = genSource(paths, filepath.Join("tmp", "run"), "run.go", RevelRunTemplate, templateArgs); err != nil {
		return
	}
	if err = genSource(paths, "routes", "routes.go", RevelRoutesTemplate, templateArgs); err != nil {
		return
	}
	buildTags := paths.Config.StringDefault("build.tags", "")
	goPath, err := exec.LookPath("go")
	if err != nil {
		utils.Logger.Fatal("Go executable not found in PATH.")
	}
	binName := filepath.Join("target", "app", paths.ImportPath, filepath.Base(paths.BasePath))
	goos := runtime.GOOS
	if goosEnv := os.Getenv("GOOS"); goosEnv != "" {
		goos = goosEnv
	}
	if goos == "windows" {
		binName += ".exe"
	}
	gotten := make(map[string]struct{})
	contains := func(s []string, e string) bool {
		for _, a := range s {
			if a == e {
				return true
			}
		}
		return false
	}
	if len(c.GoModFlags) > 0 {
		for _, gomod := range c.GoModFlags {
			goModCmd := exec.Command(goPath, append([]string{"mod"}, strings.Split(gomod, " ")...)...)
			utils.CmdInit(goModCmd, !c.Vendored, c.AppPath)
			output, err := goModCmd.CombinedOutput()
			utils.Logger.Info("Gomod applied ", "output", string(output))
			if err != nil {
				utils.Logger.Error("Gomod Failed continuing ", "error", err, "output", string(output))
			}
		}
	}
	for {
		appVersion := getAppVersion(paths)
		if appVersion == "" {
			appVersion = "noVersionProvided"
		}
		buildTime := time.Now().UTC().Format(time.RFC3339)
		versionLinkerFlags := fmt.Sprintf("-X '%s/app.AppVersion=%s' -X '%s/app.BuildTime=%s'",
			paths.ImportPath, appVersion, paths.ImportPath, buildTime)
		flags := []string{}
		if len(c.BuildFlags) == 0 {
			flags = []string{
				"build",
				"-ldflags", versionLinkerFlags,
				"-tags", buildTags,
				"-o", binName}
		} else {
			if !contains(c.BuildFlags, "build") {
				flags = []string{"build"}
			}
			if !contains(flags, "-ldflags") {
				ldflags := "-ldflags= " + versionLinkerFlags
				for i := range c.BuildFlags {
					ldflags += " -X '" + c.BuildFlags[i] + "'"
				}
				flags = append(flags, ldflags)
			}
			if !contains(flags, "-tags") && buildTags != "" {
				flags = append(flags, "-tags", buildTags)
			}
			if !contains(flags, "-o") {
				flags = append(flags, "-o", binName)
			}
		}
		flags = append(flags, path.Join(paths.ImportPath, "app", "tmp"))
		buildCmd := exec.Command(goPath, flags...)
		if !c.Vendored {
			gopath := c.GoPath
			for _, o := range paths.ModulePathMap {
				gopath += string(filepath.ListSeparator) + o.Path
			}
			buildCmd.Env = append(os.Environ(),
				"GOPATH=" + gopath,
			)
		}
		utils.CmdInit(buildCmd, !c.Vendored, c.AppPath)
		utils.Logger.Info("Exec:", "args", buildCmd.Args, "working dir", buildCmd.Dir)
		output, err := buildCmd.CombinedOutput()
		if err == nil {
			utils.Logger.Info("Build successful continuing")
			return NewApp(binName, paths, sourceInfo.PackageMap), nil
		}
		stOutput := string(output)
		utils.Logger.Infof("Got error on build of app %s", stOutput)
		matches := importErrorPattern.FindAllStringSubmatch(stOutput, -1)
		utils.Logger.Info("Build failed checking for missing imports", "message", stOutput, "missing_imports", len(matches))
		if matches == nil {
			utils.Logger.Info("Build failed no missing imports", "message", stOutput)
			return nil, newCompileError(paths, output)
		}
		utils.Logger.Warn("Detected missing packages, importing them", "packages", len(matches))
		for _, match := range matches {
			pkgName := match[1]
			utils.Logger.Info("Trying to import ", "package", pkgName)
			if _, alreadyTried := gotten[pkgName]; alreadyTried {
				utils.Logger.Error("Failed to import ", "package", pkgName)
				return nil, newCompileError(paths, output)
			}
			gotten[pkgName] = struct{}{}
			if err := c.PackageResolver(pkgName); err != nil {
				utils.Logger.Error("Unable to resolve package", "package", pkgName, "error", err)
				return nil, newCompileError(paths, []byte(err.Error()))
			}
		}
	}
	utils.Logger.Fatal("Not reachable")
	return nil, nil
}
func getAppVersion(paths *model.RevelContainer) string {
	if version := os.Getenv("APP_VERSION"); version != "" {
		return version
	}
	if gitPath, err := exec.LookPath("git"); err == nil {
		gitDir := filepath.Join(paths.BasePath, ".git")
		info, err := os.Stat(gitDir)
		if (err != nil && os.IsNotExist(err)) || !info.IsDir() {
			return ""
		}
		gitCmd := exec.Command(gitPath, "--git-dir=" + gitDir, "--work-tree=" + paths.BasePath, "describe", "--always", "--dirty")
		utils.Logger.Info("Exec:", "args", gitCmd.Args)
		output, err := gitCmd.Output()
		if err != nil {
			utils.Logger.Error("Cannot determine git repository version:", "error", err)
			return ""
		}
		return "git-" + strings.TrimSpace(string(output))
	}
	return ""
}
func cleanSource(paths *model.RevelContainer, dirs ...string) {
	for _, dir := range dirs {
		cleanDir(paths, dir)
	}
}
func cleanDir(paths *model.RevelContainer, dir string) {
	utils.Logger.Info("Cleaning dir ", "dir", dir)
	tmpPath := filepath.Join(paths.AppPath, dir)
	f, err := os.Open(tmpPath)
	if err != nil {
		if !os.IsNotExist(err) {
			utils.Logger.Error("Failed to clean dir:", "error", err)
		}
	} else {
		defer func() {
			_ = f.Close()
		}()
		infos, err := f.Readdir(0)
		if err != nil {
			if !os.IsNotExist(err) {
				utils.Logger.Fatal("Failed to clean dir:", "error", err)
			}
		} else {
			for _, info := range infos {
				pathName := filepath.Join(tmpPath, info.Name())
				if info.IsDir() {
					err := os.RemoveAll(pathName)
					if err != nil {
						utils.Logger.Fatal("Failed to remove dir:", "error", err)
					}
				} else {
					err := os.Remove(pathName)
					if err != nil {
						utils.Logger.Fatal("Failed to remove file:", "error", err)
					}
				}
			}
		}
	}
}
func genSource(paths *model.RevelContainer, dir, filename, templateSource string, args map[string]interface{}) error {
	return utils.GenerateTemplate(filepath.Join(paths.AppPath, dir, filename), templateSource, args)
}
func calcImportAliases(src *model.SourceInfo) map[string]string {
	aliases := make(map[string]string)
	typeArrays := [][]*model.TypeInfo{src.ControllerSpecs(), src.TestSuites()}
	for _, specs := range typeArrays {
		for _, spec := range specs {
			addAlias(aliases, spec.ImportPath, spec.PackageName)
			for _, methSpec := range spec.MethodSpecs {
				for _, methArg := range methSpec.Args {
					if methArg.ImportPath == "" {
						continue
					}
					addAlias(aliases, methArg.ImportPath, methArg.TypeExpr.PkgName)
				}
			}
		}
	}
	for _, importPath := range src.InitImportPaths {
		if _, ok := aliases[importPath]; !ok {
			aliases[importPath] = "_"
		}
	}
	return aliases
}
func addAlias(aliases map[string]string, importPath, pkgName string) {
	alias, ok := aliases[importPath]
	if ok {
		return
	}
	alias = makePackageAlias(aliases, pkgName)
	aliases[importPath] = alias
}
func makePackageAlias(aliases map[string]string, pkgName string) string {
	i := 0
	alias := pkgName
	for containsValue(aliases, alias) || alias == "revel" {
		alias = fmt.Sprintf("%s%d", pkgName, i)
		i++
	}
	return alias
}
func containsValue(m map[string]string, val string) bool {
	for _, v := range m {
		if v == val {
			return true
		}
	}
	return false
}