From 17459d14e6d607f545a6eaad6a82eb64275e0b22 Mon Sep 17 00:00:00 2001 From: NotZippy Date: Wed, 19 Sep 2018 09:45:59 -0700 Subject: [PATCH] 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. --- harness/build.go | 89 +++++++++++++++++++++++++++++--------------- model/source_info.go | 20 ++++++---- parser/reflect.go | 18 ++++++--- revel/run.go | 1 + revel/test.go | 1 + utils/file.go | 2 +- 6 files changed, 86 insertions(+), 45 deletions(-) diff --git a/harness/build.go b/harness/build.go index 7e91324..dc23e1e 100644 --- a/harness/build.go +++ b/harness/build.go @@ -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. // 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(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). cleanSource(paths, "tmp", "routes") @@ -56,12 +56,20 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer, buildFlags ...st // Generate two source files. templateArgs := map[string]interface{}{ + "ImportPath": paths.ImportPath, "Controllers": controllers, "ValidationKeys": sourceInfo.ValidationKeys, "ImportPaths": calcImportAliases(sourceInfo), "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, filepath.Join("tmp", "run"), "run.go", RevelRunTemplate, templateArgs) genSource(paths, "routes", "routes.go", RevelRoutesTemplate, templateArgs) // Read build config. @@ -126,14 +134,14 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer, buildFlags ...st } gotten := make(map[string]struct{}) - contains := func (s []string, e string) bool { - for _, a := range s { - if a == e { - return true - } + contains := func(s []string, e string) bool { + for _, a := range s { + if a == e { + return true } - return false } + return false + } for { 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 flags := []string{} - if len(c.BuildFlags)==0 { + if len(c.BuildFlags) == 0 { flags = []string{ "build", "-i", @@ -152,24 +160,23 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer, buildFlags ...st "-tags", buildTags, "-o", binName} } else { - if !contains(c.BuildFlags,"build") { + if !contains(c.BuildFlags, "build") { flags = []string{"build"} } - flags = append(flags,c.BuildFlags...) + flags = append(flags, c.BuildFlags...) if !contains(flags, "-ldflags") { - flags = append(flags,"-ldflags", versionLinkerFlags) + flags = append(flags, "-ldflags", versionLinkerFlags) } if !contains(flags, "-tags") { - flags = append(flags,"-tags", buildTags) + flags = append(flags, "-tags", buildTags) } if !contains(flags, "-o") { - flags = append(flags,"-o", binName) + flags = append(flags, "-o", binName) } } - // Add in build flags - flags = append(flags, buildFlags...) + flags = append(flags, c.BuildFlags...) // This is Go main path gopath := c.GoPath @@ -236,7 +243,7 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer, buildFlags ...st getOutput, err := getCmd.CombinedOutput() if err != nil { 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) } } @@ -329,7 +336,6 @@ func cleanDir(paths *model.RevelContainer, dir string) { // genSource renders the given template to produce source code, which it writes // to the given directory and file. 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) if err != nil { @@ -469,14 +475,13 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error { } // RevelMainTemplate template for app/tmp/main.go -const RevelMainTemplate = `// GENERATED CODE - DO NOT EDIT -// This file is the main file for Revel. +const RevelRunTemplate = `// GENERATED CODE - DO NOT EDIT +// This file is the run 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 +package run import ( - "flag" "reflect" "github.com/revel/revel"{{range $k, $v := $.ImportPaths}} {{$v}} "{{$k}}"{{end}} @@ -484,18 +489,18 @@ import ( ) 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... _ = reflect.Invalid ) -func main() { - flag.Parse() - revel.Init(*runMode, *importPath, *srcPath) +// Register and run the application +func Run(port int) { + Register() + revel.Run(port) +} + +// Register all the controllers +func Register() { revel.AppLog.Info("Running revel server") {{range $i, $c := .Controllers}} revel.RegisterController((*{{index $.ImportPaths .ImportPath}}.{{.StructName}})(nil), @@ -522,8 +527,32 @@ func main() { testing.TestSuites = []interface{}{ {{range .TestSuites}} (*{{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) } ` diff --git a/model/source_info.go b/model/source_info.go index 8b47043..f4c9a4a 100644 --- a/model/source_info.go +++ b/model/source_info.go @@ -5,8 +5,8 @@ package model import ( "github.com/revel/cmd/utils" "path/filepath" - "unicode" "strings" + "unicode" ) type SourceInfo struct { @@ -41,9 +41,9 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered processed []string ) for len(nodeQueue) > 0 { - controllerSimpleName := nodeQueue[0] + typeSimpleName := nodeQueue[0] nodeQueue = nodeQueue[1:] - processed = append(processed, controllerSimpleName) + processed = append(processed, typeSimpleName) // Look through all known structs. 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 // the filtered list. - if controllerSimpleName == embeddedType.String() { + if typeSimpleName == embeddedType.String() { nodeQueue = append(nodeQueue, spec.String()) filtered = append(filtered, spec) break @@ -66,6 +66,7 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered } } } + // Strip out any specifications that contain a lower case for exit := false; !exit; exit = true { for i, filteredItem := range filtered { @@ -84,25 +85,28 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered for _, spec := range s.StructSpecs { if spec.PackageName == packageFilter { found := false + unfoundNames := "" for _, filteredItem := range filtered { if filteredItem.StructName == spec.StructName { found = true break + } else { + unfoundNames += filteredItem.StructName + "," } } // Report non controller structures in controller folder. if !found && !strings.HasPrefix(spec.StructName, "Test") { - utils.Logger.Warn("Type found in package: " + packageFilter + - ", but did not embed from: " + filepath.Base(targetType), - "name", spec.StructName, "path", spec.ImportPath) + utils.Logger.Warn("Type found in package: "+packageFilter+ + ", but did not embed from: "+filepath.Base(targetType), + "name", spec.StructName, "importpath", spec.ImportPath, "foundstructures", unfoundNames) } } } return } -// ControllerSpecs returns the all the contollers that embeds +// ControllerSpecs returns the all the controllers that embeds // `revel.Controller` func (s *SourceInfo) ControllerSpecs() []*TypeInfo { if s.controllerSpecs == nil { diff --git a/parser/reflect.go b/parser/reflect.go index c4a3a13..e143e2c 100644 --- a/parser/reflect.go +++ b/parser/reflect.go @@ -94,12 +94,8 @@ func ProcessSource(paths *model.RevelContainer) (*model.SourceInfo, *utils.Error // Skip "main" packages. 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 + // These cannot be included in source code that is not generated specifically as a test for i := range pkgs { if len(i) > 6 { 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. if len(pkgs) > 1 { 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) } + var pkg *ast.Package for _, v := range pkgs { 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 }) } diff --git a/revel/run.go b/revel/run.go index 2a3d239..4c4cb5f 100644 --- a/revel/run.go +++ b/revel/run.go @@ -109,6 +109,7 @@ func runApp(c *model.CommandConfig) { if c.Run.Mode == "" { c.Run.Mode = "dev" } + c.ImportPath = c.Run.ImportPath revel_path := model.NewRevelPaths(c.Run.Mode, c.Run.ImportPath, "", model.DoNothingRevelCallback) if c.Run.Port != "" { diff --git a/revel/test.go b/revel/test.go index d9b0256..1b1f022 100644 --- a/revel/test.go +++ b/revel/test.go @@ -78,6 +78,7 @@ func testApp(c *model.CommandConfig) { if c.Test.Mode != "" { mode = c.Test.Mode } + c.ImportPath = c.Test.ImportPath // Find and parse app.conf revel_path := model.NewRevelPaths(mode, c.Test.ImportPath, "", model.DoNothingRevelCallback) diff --git a/utils/file.go b/utils/file.go index e9c6fc6..c134f43 100644 --- a/utils/file.go +++ b/utils/file.go @@ -71,7 +71,7 @@ func MustGenerateTemplate(filename, templateSource string, args map[string]inter sourceCode := b.String() filePath := filepath.Dir(filename) if !DirExists(filePath) { - err = os.Mkdir(filePath, 0777) + err = os.MkdirAll(filePath, 0777) if err != nil && !os.IsExist(err) { Logger.Fatal("Failed to make directory","dir", filePath, "error", err) }