// genSource renders the given template to produce source code, which it writes // to the given directory and file. func genSource(dir, filename, templateSource string, args map[string]interface{}) { sourceCode := revel.ExecuteTemplate( template.Must(template.New("").Parse(templateSource)), args) // Create a fresh dir. tmpPath := path.Join(revel.AppPath, dir) err := os.RemoveAll(tmpPath) if err != nil { revel.ERROR.Println("Failed to remove dir:", err) } err = os.Mkdir(tmpPath, 0777) if err != nil { revel.ERROR.Fatalf("Failed to make tmp directory: %v", err) } // Create the file file, err := os.Create(path.Join(tmpPath, filename)) defer file.Close() if err != nil { revel.ERROR.Fatalf("Failed to create file: %v", err) } _, err = file.WriteString(sourceCode) if err != nil { revel.ERROR.Fatalf("Failed to write to file: %v", err) } }
// Build the app: // 1. Generate the the main.go file. // 2. Run the appropriate "go build" command. // Requires that revel.Init has been called previously. // Returns the path to the built binary, and an error if there was a problem building it. func Build() (app *App, compileError *revel.Error) { sourceInfo, compileError := ProcessSource(revel.CodePaths) if compileError != nil { return nil, compileError } // Add the db.import to the import paths. if dbImportPath, found := revel.Config.String("db.import"); found { sourceInfo.InitImportPaths = append(sourceInfo.InitImportPaths, dbImportPath) } tmpl := template.Must(template.New("").Parse(REGISTER_CONTROLLERS)) registerControllerSource := revel.ExecuteTemplate(tmpl, map[string]interface{}{ "Controllers": sourceInfo.ControllerSpecs, "ValidationKeys": sourceInfo.ValidationKeys, "ImportPaths": calcImportAliases(sourceInfo), "TestSuites": sourceInfo.TestSuites, }) // Create a fresh temp dir. tmpPath := path.Join(revel.AppPath, "tmp") err := os.RemoveAll(tmpPath) if err != nil { revel.ERROR.Println("Failed to remove tmp dir:", err) } err = os.Mkdir(tmpPath, 0777) if err != nil { revel.ERROR.Fatalf("Failed to make tmp directory: %v", err) } // Create the main.go file controllersFile, err := os.Create(path.Join(tmpPath, "main.go")) defer controllersFile.Close() if err != nil { revel.ERROR.Fatalf("Failed to create main.go: %v", err) } _, err = controllersFile.WriteString(registerControllerSource) if err != nil { revel.ERROR.Fatalf("Failed to write to main.go: %v", err) } // Read build config. buildTags := revel.Config.StringDefault("build.tags", "") // Build the user program (all code under app). // It relies on the user having "go" installed. goPath, err := exec.LookPath("go") if err != nil { revel.ERROR.Fatalf("Go executable not found in PATH.") } ctx := build.Default pkg, err := ctx.Import(revel.ImportPath, "", build.FindOnly) if err != nil { revel.ERROR.Fatalln("Failure importing", revel.ImportPath) } binName := path.Join(pkg.BinDir, path.Base(revel.BasePath)) if runtime.GOOS == "windows" { binName += ".exe" } gotten := make(map[string]struct{}) for { buildCmd := exec.Command(goPath, "build", "-tags", buildTags, "-o", binName, path.Join(revel.ImportPath, "app", "tmp")) revel.TRACE.Println("Exec:", buildCmd.Args) output, err := buildCmd.CombinedOutput() // If the build succeeded, we're done. if err == nil { return NewApp(binName), nil } revel.ERROR.Println(string(output)) // See if it was an import error that we can go get. matches := importErrorPattern.FindStringSubmatch(string(output)) if matches == nil { return nil, newCompileError(output) } // Ensure we haven't already tried to go get it. pkgName := matches[1] if _, alreadyTried := gotten[pkgName]; alreadyTried { return nil, newCompileError(output) } gotten[pkgName] = struct{}{} // Execute "go get <pkg>" getCmd := exec.Command(goPath, "get", pkgName) revel.TRACE.Println("Exec:", getCmd.Args) getOutput, err := getCmd.CombinedOutput() if err != nil { revel.ERROR.Println(string(getOutput)) return nil, newCompileError(output) } // Success getting the import, attempt to build again. } revel.ERROR.Fatalf("Not reachable") return nil, nil }