Split main file

Added code to split the generated main file into two separate files. This allows other code to launch the web application inline.
This commit is contained in:
NotZippy
2018-09-19 09:45:59 -07:00
parent 4d7a290247
commit 17459d14e6
6 changed files with 86 additions and 45 deletions

View File

@@ -36,7 +36,7 @@ func (c ByString) Less(i, j int) bool { return c[i].String() < c[j].String() }
// 2. Run the appropriate "go build" command. // 2. Run the appropriate "go build" command.
// Requires that revel.Init has been called previously. // 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. // Returns the path to the built binary, and an error if there was a problem building it.
func Build(c *model.CommandConfig, paths *model.RevelContainer, buildFlags ...string) (app *App, compileError *utils.Error) { func Build(c *model.CommandConfig, paths *model.RevelContainer) (app *App, compileError *utils.Error) {
// First, clear the generated files (to avoid them messing with ProcessSource). // First, clear the generated files (to avoid them messing with ProcessSource).
cleanSource(paths, "tmp", "routes") cleanSource(paths, "tmp", "routes")
@@ -56,12 +56,20 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer, buildFlags ...st
// Generate two source files. // Generate two source files.
templateArgs := map[string]interface{}{ templateArgs := map[string]interface{}{
"ImportPath": paths.ImportPath,
"Controllers": controllers, "Controllers": controllers,
"ValidationKeys": sourceInfo.ValidationKeys, "ValidationKeys": sourceInfo.ValidationKeys,
"ImportPaths": calcImportAliases(sourceInfo), "ImportPaths": calcImportAliases(sourceInfo),
"TestSuites": sourceInfo.TestSuites(), "TestSuites": sourceInfo.TestSuites(),
} }
// Generate code for the main, run and routes file.
// The run file allows external programs to launch and run the application
// without being the main thread
cleanSource(paths, "tmp", "routes")
genSource(paths, "tmp", "main.go", RevelMainTemplate, templateArgs) genSource(paths, "tmp", "main.go", RevelMainTemplate, templateArgs)
genSource(paths, filepath.Join("tmp", "run"), "run.go", RevelRunTemplate, templateArgs)
genSource(paths, "routes", "routes.go", RevelRoutesTemplate, templateArgs) genSource(paths, "routes", "routes.go", RevelRoutesTemplate, templateArgs)
// Read build config. // Read build config.
@@ -126,14 +134,14 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer, buildFlags ...st
} }
gotten := make(map[string]struct{}) gotten := make(map[string]struct{})
contains := func (s []string, e string) bool { contains := func(s []string, e string) bool {
for _, a := range s { for _, a := range s {
if a == e { if a == e {
return true return true
}
} }
return false
} }
return false
}
for { for {
appVersion := getAppVersion(paths) appVersion := getAppVersion(paths)
@@ -144,7 +152,7 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer, buildFlags ...st
// Append any build flags specified, they will override existing flags // Append any build flags specified, they will override existing flags
flags := []string{} flags := []string{}
if len(c.BuildFlags)==0 { if len(c.BuildFlags) == 0 {
flags = []string{ flags = []string{
"build", "build",
"-i", "-i",
@@ -152,24 +160,23 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer, buildFlags ...st
"-tags", buildTags, "-tags", buildTags,
"-o", binName} "-o", binName}
} else { } else {
if !contains(c.BuildFlags,"build") { if !contains(c.BuildFlags, "build") {
flags = []string{"build"} flags = []string{"build"}
} }
flags = append(flags,c.BuildFlags...) flags = append(flags, c.BuildFlags...)
if !contains(flags, "-ldflags") { if !contains(flags, "-ldflags") {
flags = append(flags,"-ldflags", versionLinkerFlags) flags = append(flags, "-ldflags", versionLinkerFlags)
} }
if !contains(flags, "-tags") { if !contains(flags, "-tags") {
flags = append(flags,"-tags", buildTags) flags = append(flags, "-tags", buildTags)
} }
if !contains(flags, "-o") { if !contains(flags, "-o") {
flags = append(flags,"-o", binName) flags = append(flags, "-o", binName)
} }
} }
// Add in build flags // Add in build flags
flags = append(flags, buildFlags...) flags = append(flags, c.BuildFlags...)
// This is Go main path // This is Go main path
gopath := c.GoPath gopath := c.GoPath
@@ -236,7 +243,7 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer, buildFlags ...st
getOutput, err := getCmd.CombinedOutput() getOutput, err := getCmd.CombinedOutput()
if err != nil { if err != nil {
utils.Logger.Error("Build failed", "message", stOutput) utils.Logger.Error("Build failed", "message", stOutput)
utils.Logger.Error("Failed to fetch the output", string(getOutput)) utils.Logger.Error("Failed to fetch the output", "getOutput", string(getOutput))
return nil, newCompileError(paths, output) return nil, newCompileError(paths, output)
} }
} }
@@ -329,7 +336,6 @@ func cleanDir(paths *model.RevelContainer, dir string) {
// genSource renders the given template to produce source code, which it writes // genSource renders the given template to produce source code, which it writes
// to the given directory and file. // to the given directory and file.
func genSource(paths *model.RevelContainer, dir, filename, templateSource string, args map[string]interface{}) { func genSource(paths *model.RevelContainer, dir, filename, templateSource string, args map[string]interface{}) {
cleanSource(paths, dir)
err := utils.MustGenerateTemplate(filepath.Join(paths.AppPath, dir, filename), templateSource, args) err := utils.MustGenerateTemplate(filepath.Join(paths.AppPath, dir, filename), templateSource, args)
if err != nil { if err != nil {
@@ -469,14 +475,13 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error {
} }
// RevelMainTemplate template for app/tmp/main.go // RevelMainTemplate template for app/tmp/main.go
const RevelMainTemplate = `// GENERATED CODE - DO NOT EDIT const RevelRunTemplate = `// GENERATED CODE - DO NOT EDIT
// This file is the main file for Revel. // This file is the run file for Revel.
// It registers all the controllers and provides details for the Revel server engine to // It registers all the controllers and provides details for the Revel server engine to
// properly inject parameters directly into the action endpoints. // properly inject parameters directly into the action endpoints.
package main package run
import ( import (
"flag"
"reflect" "reflect"
"github.com/revel/revel"{{range $k, $v := $.ImportPaths}} "github.com/revel/revel"{{range $k, $v := $.ImportPaths}}
{{$v}} "{{$k}}"{{end}} {{$v}} "{{$k}}"{{end}}
@@ -484,18 +489,18 @@ import (
) )
var ( var (
runMode *string = flag.String("runMode", "", "Run mode.")
port *int = flag.Int("port", 0, "By default, read from app.conf")
importPath *string = flag.String("importPath", "", "Go Import Path for the app.")
srcPath *string = flag.String("srcPath", "", "Path to the source root.")
// So compiler won't complain if the generated code doesn't reference reflect package... // So compiler won't complain if the generated code doesn't reference reflect package...
_ = reflect.Invalid _ = reflect.Invalid
) )
func main() { // Register and run the application
flag.Parse() func Run(port int) {
revel.Init(*runMode, *importPath, *srcPath) Register()
revel.Run(port)
}
// Register all the controllers
func Register() {
revel.AppLog.Info("Running revel server") revel.AppLog.Info("Running revel server")
{{range $i, $c := .Controllers}} {{range $i, $c := .Controllers}}
revel.RegisterController((*{{index $.ImportPaths .ImportPath}}.{{.StructName}})(nil), revel.RegisterController((*{{index $.ImportPaths .ImportPath}}.{{.StructName}})(nil),
@@ -522,8 +527,32 @@ func main() {
testing.TestSuites = []interface{}{ {{range .TestSuites}} testing.TestSuites = []interface{}{ {{range .TestSuites}}
(*{{index $.ImportPaths .ImportPath}}.{{.StructName}})(nil),{{end}} (*{{index $.ImportPaths .ImportPath}}.{{.StructName}})(nil),{{end}}
} }
}
`
const RevelMainTemplate = `// GENERATED CODE - DO NOT EDIT
// This file is the main file for Revel.
// It registers all the controllers and provides details for the Revel server engine to
// properly inject parameters directly into the action endpoints.
package main
revel.Run(*port) import (
"flag"
"{{.ImportPath}}/app/tmp/run"
"github.com/revel/revel"
)
var (
runMode *string = flag.String("runMode", "", "Run mode.")
port *int = flag.Int("port", 0, "By default, read from app.conf")
importPath *string = flag.String("importPath", "", "Go Import Path for the app.")
srcPath *string = flag.String("srcPath", "", "Path to the source root.")
)
func main() {
flag.Parse()
revel.Init(*runMode, *importPath, *srcPath)
run.Run(*port)
} }
` `

View File

@@ -5,8 +5,8 @@ package model
import ( import (
"github.com/revel/cmd/utils" "github.com/revel/cmd/utils"
"path/filepath" "path/filepath"
"unicode"
"strings" "strings"
"unicode"
) )
type SourceInfo struct { type SourceInfo struct {
@@ -41,9 +41,9 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered
processed []string processed []string
) )
for len(nodeQueue) > 0 { for len(nodeQueue) > 0 {
controllerSimpleName := nodeQueue[0] typeSimpleName := nodeQueue[0]
nodeQueue = nodeQueue[1:] nodeQueue = nodeQueue[1:]
processed = append(processed, controllerSimpleName) processed = append(processed, typeSimpleName)
// Look through all known structs. // Look through all known structs.
for _, spec := range s.StructSpecs { for _, spec := range s.StructSpecs {
@@ -58,7 +58,7 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered
// If so, add this type's simple name to the nodeQueue, and its spec to // If so, add this type's simple name to the nodeQueue, and its spec to
// the filtered list. // the filtered list.
if controllerSimpleName == embeddedType.String() { if typeSimpleName == embeddedType.String() {
nodeQueue = append(nodeQueue, spec.String()) nodeQueue = append(nodeQueue, spec.String())
filtered = append(filtered, spec) filtered = append(filtered, spec)
break break
@@ -66,6 +66,7 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered
} }
} }
} }
// Strip out any specifications that contain a lower case // Strip out any specifications that contain a lower case
for exit := false; !exit; exit = true { for exit := false; !exit; exit = true {
for i, filteredItem := range filtered { for i, filteredItem := range filtered {
@@ -84,25 +85,28 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered
for _, spec := range s.StructSpecs { for _, spec := range s.StructSpecs {
if spec.PackageName == packageFilter { if spec.PackageName == packageFilter {
found := false found := false
unfoundNames := ""
for _, filteredItem := range filtered { for _, filteredItem := range filtered {
if filteredItem.StructName == spec.StructName { if filteredItem.StructName == spec.StructName {
found = true found = true
break break
} else {
unfoundNames += filteredItem.StructName + ","
} }
} }
// Report non controller structures in controller folder. // Report non controller structures in controller folder.
if !found && !strings.HasPrefix(spec.StructName, "Test") { if !found && !strings.HasPrefix(spec.StructName, "Test") {
utils.Logger.Warn("Type found in package: " + packageFilter + utils.Logger.Warn("Type found in package: "+packageFilter+
", but did not embed from: " + filepath.Base(targetType), ", but did not embed from: "+filepath.Base(targetType),
"name", spec.StructName, "path", spec.ImportPath) "name", spec.StructName, "importpath", spec.ImportPath, "foundstructures", unfoundNames)
} }
} }
} }
return return
} }
// ControllerSpecs returns the all the contollers that embeds // ControllerSpecs returns the all the controllers that embeds
// `revel.Controller` // `revel.Controller`
func (s *SourceInfo) ControllerSpecs() []*TypeInfo { func (s *SourceInfo) ControllerSpecs() []*TypeInfo {
if s.controllerSpecs == nil { if s.controllerSpecs == nil {

View File

@@ -94,12 +94,8 @@ func ProcessSource(paths *model.RevelContainer) (*model.SourceInfo, *utils.Error
// Skip "main" packages. // Skip "main" packages.
delete(pkgs, "main") delete(pkgs, "main")
// If there is no code in this directory, skip it.
if len(pkgs) == 0 {
return nil
}
// Ignore packages that end with _test // Ignore packages that end with _test
// These cannot be included in source code that is not generated specifically as a test
for i := range pkgs { for i := range pkgs {
if len(i) > 6 { if len(i) > 6 {
if string(i[len(i)-5:]) == "_test" { if string(i[len(i)-5:]) == "_test" {
@@ -108,6 +104,11 @@ func ProcessSource(paths *model.RevelContainer) (*model.SourceInfo, *utils.Error
} }
} }
// If there is no code in this directory, skip it.
if len(pkgs) == 0 {
return nil
}
// There should be only one package in this directory. // There should be only one package in this directory.
if len(pkgs) > 1 { if len(pkgs) > 1 {
for i := range pkgs { for i := range pkgs {
@@ -116,12 +117,17 @@ func ProcessSource(paths *model.RevelContainer) (*model.SourceInfo, *utils.Error
utils.Logger.Fatal("Most unexpected! Multiple packages in a single directory:", "packages", pkgs) utils.Logger.Fatal("Most unexpected! Multiple packages in a single directory:", "packages", pkgs)
} }
var pkg *ast.Package var pkg *ast.Package
for _, v := range pkgs { for _, v := range pkgs {
pkg = v pkg = v
} }
srcInfo = appendSourceInfo(srcInfo, processPackage(fset, pkgImportPath, path, pkg)) if pkg != nil {
srcInfo = appendSourceInfo(srcInfo, processPackage(fset, pkgImportPath, path, pkg))
} else {
utils.Logger.Info("Ignoring package, because it contained no packages", "path", path)
}
return nil return nil
}) })
} }

View File

@@ -109,6 +109,7 @@ func runApp(c *model.CommandConfig) {
if c.Run.Mode == "" { if c.Run.Mode == "" {
c.Run.Mode = "dev" c.Run.Mode = "dev"
} }
c.ImportPath = c.Run.ImportPath
revel_path := model.NewRevelPaths(c.Run.Mode, c.Run.ImportPath, "", model.DoNothingRevelCallback) revel_path := model.NewRevelPaths(c.Run.Mode, c.Run.ImportPath, "", model.DoNothingRevelCallback)
if c.Run.Port != "" { if c.Run.Port != "" {

View File

@@ -78,6 +78,7 @@ func testApp(c *model.CommandConfig) {
if c.Test.Mode != "" { if c.Test.Mode != "" {
mode = c.Test.Mode mode = c.Test.Mode
} }
c.ImportPath = c.Test.ImportPath
// Find and parse app.conf // Find and parse app.conf
revel_path := model.NewRevelPaths(mode, c.Test.ImportPath, "", model.DoNothingRevelCallback) revel_path := model.NewRevelPaths(mode, c.Test.ImportPath, "", model.DoNothingRevelCallback)

View File

@@ -71,7 +71,7 @@ func MustGenerateTemplate(filename, templateSource string, args map[string]inter
sourceCode := b.String() sourceCode := b.String()
filePath := filepath.Dir(filename) filePath := filepath.Dir(filename)
if !DirExists(filePath) { if !DirExists(filePath) {
err = os.Mkdir(filePath, 0777) err = os.MkdirAll(filePath, 0777)
if err != nil && !os.IsExist(err) { if err != nil && !os.IsExist(err) {
Logger.Fatal("Failed to make directory","dir", filePath, "error", err) Logger.Fatal("Failed to make directory","dir", filePath, "error", err)
} }