Skip to content

Commit

Permalink
Split main file
Browse files Browse the repository at this point in the history
Added code to split the generated main file into two separate files. This allows other code to launch the web application inline.
  • Loading branch information
notzippy committed Sep 19, 2018
1 parent 4d7a290 commit 17459d1
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 45 deletions.
89 changes: 59 additions & 30 deletions harness/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand All @@ -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.
Expand Down Expand Up @@ -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)
Expand All @@ -144,32 +152,31 @@ 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",
"-ldflags", versionLinkerFlags,
"-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
Expand Down Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -469,33 +475,32 @@ 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}}
"github.com/revel/revel/testing"
)
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),
Expand All @@ -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
import (
"flag"
"{{.ImportPath}}/app/tmp/run"
"github.com/revel/revel"
)
revel.Run(*port)
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)
}
`

Expand Down
20 changes: 12 additions & 8 deletions model/source_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ package model
import (
"github.com/revel/cmd/utils"
"path/filepath"
"unicode"
"strings"
"unicode"
)

type SourceInfo struct {
Expand Down Expand Up @@ -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 {
Expand All @@ -58,14 +58,15 @@ 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
}
}
}
}

// Strip out any specifications that contain a lower case
for exit := false; !exit; exit = true {
for i, filteredItem := range filtered {
Expand All @@ -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 {
Expand Down
18 changes: 12 additions & 6 deletions parser/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand All @@ -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 {
Expand All @@ -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
})
}
Expand Down
1 change: 1 addition & 0 deletions revel/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 != "" {
Expand Down
1 change: 1 addition & 0 deletions revel/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion utils/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down

0 comments on commit 17459d1

Please sign in to comment.