diff --git a/.travis.yml b/.travis.yml index 530d6ee..530a051 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,19 +47,25 @@ script: #- revel package my/testapp prod # Ensure the new-app flow works (plus the other commands). - - revel new --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v - - revel test --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v - - revel clean --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v - - revel build --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v -t build/testapp2 - - revel build --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v -t build/testapp2 -m prod - - revel package --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v - - revel package --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v -m prod + - revel new --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@$REVEL_BRANCH" -a my/testapp2 --package revelframework.com -v + - revel test --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@$REVEL_BRANCH" -a my/testapp2 -v + - revel clean --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@$REVEL_BRANCH" -a my/testapp2 -v + - revel build --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@$REVEL_BRANCH" -a my/testapp2 -v -t build/testapp2 + - revel build --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@$REVEL_BRANCH" -a my/testapp2 -v -t build/testapp2 -m prod + - revel package --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@$REVEL_BRANCH" -a my/testapp2 -v + - revel package --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@$REVEL_BRANCH" -a my/testapp2 -v -m prod + - export INITIALWD=$PWD # Check build works with no-vendor flag - cd $GOPATH - export GO111MODULE=auto - - revel new -a my/testapp2 --no-vendor - - revel test -a my/testapp2 + - revel new -a my/testapp2 --no-vendor -v + - revel test -a my/testapp2 -v + + # Check non verbose build, outside of GO path + - cd $INITIALWD + - revel new --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@$REVEL_BRANCH" -a my/testapp3 --package revelframework.com + - revel test --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@$REVEL_BRANCH" -a my/testapp3 matrix: allow_failures: diff --git a/harness/app.go b/harness/app.go index 5538bd3..60b8613 100644 --- a/harness/app.go +++ b/harness/app.go @@ -106,7 +106,7 @@ func (cmd AppCmd) Kill() { if cmd.Cmd != nil && (cmd.ProcessState == nil || !cmd.ProcessState.Exited()) { // Windows appears to send the kill to all threads, shutting down the // server before this can, this check will ensure the process is still running - if _, err := os.FindProcess(int(cmd.Process.Pid));err!=nil { + if _, err := os.FindProcess(int(cmd.Process.Pid)); err != nil { // Server has already exited utils.Logger.Info("Server not running revel server pid", "pid", cmd.Process.Pid) return @@ -143,9 +143,9 @@ func (cmd AppCmd) Kill() { } if err != nil { - utils.Logger.Error( - "Revel app failed to kill process.", - "processid", cmd.Process.Pid,"error",err, + utils.Logger.Info( + "Revel app already exited.", + "processid", cmd.Process.Pid, "error", err, "killerror", cmd.Process.Kill()) return } diff --git a/harness/harness.go b/harness/harness.go index fd6397c..cefc5d6 100644 --- a/harness/harness.go +++ b/harness/harness.go @@ -16,6 +16,7 @@ package harness import ( "crypto/tls" "fmt" + "time" "go/build" "io" "net" @@ -56,6 +57,8 @@ type Harness struct { paths *model.RevelContainer // The Revel container config *model.CommandConfig // The configuration runMode string // The runmode the harness is running in + isError bool // True if harness is in error state + ranOnce bool // True app compiled once } func (h *Harness) renderError(iw http.ResponseWriter, ir *http.Request, err error) { @@ -202,6 +205,21 @@ func NewHarness(c *model.CommandConfig, paths *model.RevelContainer, runMode str // Refresh method rebuilds the Revel application and run it on the given port. // called by the watcher func (h *Harness) Refresh() (err *utils.SourceError) { + t := time.Now(); + fmt.Println("Changed detected, recompiling") + err = h.refresh() + if err!=nil && !h.ranOnce && h.useProxy { + addr := fmt.Sprintf("%s:%d", h.paths.HTTPAddr, h.paths.HTTPPort) + + fmt.Printf("\nError compiling code, to view error details see proxy running on http://%s\n\n",addr) + } + + h.ranOnce = true + fmt.Printf("\nTime to recompile %s\n",time.Now().Sub(t).String()) + return +} + +func (h *Harness) refresh() (err *utils.SourceError) { // Allow only one thread to rebuild the process // If multiple requests to rebuild are queued only the last one is executed on // So before a build is started we wait for a second to determine if @@ -281,7 +299,8 @@ func (h *Harness) Run() { paths = append(paths, h.paths.CodePaths...) h.watcher = watcher.NewWatcher(h.paths, false) h.watcher.Listen(h, paths...) - h.watcher.Notify() + go h.Refresh() + // h.watcher.Notify() if h.useProxy { go func() { @@ -292,6 +311,7 @@ func (h *Harness) Run() { addr := fmt.Sprintf("%s:%d", h.paths.HTTPAddr, h.paths.HTTPPort) utils.Logger.Infof("Proxy server is listening on %s", addr) + var err error if h.paths.HTTPSsl { err = http.ListenAndServeTLS( @@ -308,13 +328,15 @@ func (h *Harness) Run() { }() } - // Kill the app on signal. + + // Make a new channel to listen for the interrupt event ch := make(chan os.Signal) signal.Notify(ch, os.Interrupt, os.Kill) - <-ch + // Kill the app and exit if h.app != nil { h.app.Kill() } + <-ch os.Exit(1) } diff --git a/model/command_config.go b/model/command_config.go index 08f0b90..717a061 100644 --- a/model/command_config.go +++ b/model/command_config.go @@ -174,8 +174,10 @@ func (c *CommandConfig) initAppFolder() (err error) { // Use app folder to read the go.mod if it exists and extract the package information goModFile := filepath.Join(appFolder, "go.mod") + utils.Logger.Info("Checking gomod, extracting from file", "path", goModFile,"exists", utils.Exists(goModFile)) if utils.Exists(goModFile) { c.Vendored = true + utils.Logger.Info("Found go mod, extracting from file", "path", goModFile) file, err := ioutil.ReadFile(goModFile) if err != nil { return err @@ -220,7 +222,7 @@ func (c *CommandConfig) initAppFolder() (err error) { c.AppPath = appFolder } - utils.Logger.Info("Set application path", "path", c.AppPath) + utils.Logger.Info("Set application path", "path", c.AppPath, "vendored",c.Vendored, "importpath",c.ImportPath) return nil } @@ -233,6 +235,7 @@ func (c *CommandConfig) InitPackageResolver() { c.PackageResolver = func(pkgName string) error { utils.Logger.Info("Request for package ", "package", pkgName, "use vendor", c.Vendored) var getCmd *exec.Cmd + print("Downloading related packages ...") if c.Vendored { getCmd = exec.Command(c.GoCmd, "mod", "tidy") } else { @@ -246,6 +249,7 @@ func (c *CommandConfig) InitPackageResolver() { if err != nil { utils.Logger.Error("Failed to import package", "error", err, "gopath", build.Default.GOPATH, "GO-ROOT", build.Default.GOROOT, "output", string(output)) } + println(" completed.") return nil } diff --git a/model/source_info.go b/model/source_info.go index 836f004..8da1b26 100644 --- a/model/source_info.go +++ b/model/source_info.go @@ -57,7 +57,6 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered // Look through the embedded types to see if the current type is among them. for _, embeddedType := range spec.EmbeddedTypes { - // If so, add this type's simple name to the nodeQueue, and its spec to // the filtered list. if typeSimpleName == embeddedType.String() { @@ -111,6 +110,7 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered // ControllerSpecs returns the all the controllers that embeds // `revel.Controller` func (s *SourceInfo) ControllerSpecs() []*TypeInfo { + utils.Logger.Info("Scanning controller specifications for types ","typePath",RevelImportPath + ".Controller", "speclen",len(s.controllerSpecs)) if s.controllerSpecs == nil { s.controllerSpecs = s.TypesThatEmbed(RevelImportPath + ".Controller", "controllers") } diff --git a/parser2/source_info_processor.go b/parser2/source_info_processor.go index e0e4141..40d3f85 100644 --- a/parser2/source_info_processor.go +++ b/parser2/source_info_processor.go @@ -8,6 +8,7 @@ import ( "go/token" "strings" "path/filepath" + "github.com/revel/cmd/logger" ) type ( @@ -31,14 +32,21 @@ func (s *SourceInfoProcessor) processPackage(p *packages.Package) (sourceInfo *m strings.Contains(p.PkgPath, "/tests/") methodMap = map[string][]*model.MethodSpec{} ) + localImportMap := map[string]string{} + log := s.sourceProcessor.log.New("package", p.PkgPath) + log.Info("Processing package") for _, tree := range p.Syntax { for _, decl := range tree.Decls { + s.sourceProcessor.packageMap[p.PkgPath] = filepath.Dir(p.Fset.Position(decl.Pos()).Filename) - //println("*** checking", p.Fset.Position(decl.Pos()).Filename) + if !s.addImport(decl, p, localImportMap, log) { + continue + } spec, found := s.getStructTypeDecl(decl, p.Fset) + //log.Info("Checking file","filename", p.Fset.Position(decl.Pos()).Filename,"found",found) if found { if isController || isTest { - controllerSpec := s.getControllerSpec(spec, p) + controllerSpec := s.getControllerSpec(spec, p, localImportMap) sourceInfo.StructSpecs = append(sourceInfo.StructSpecs, controllerSpec) } } else { @@ -56,7 +64,7 @@ func (s *SourceInfoProcessor) processPackage(p *packages.Package) (sourceInfo *m // return one result if m, receiver := s.getControllerFunc(funcDecl, p); m != nil { methodMap[receiver] = append(methodMap[receiver], m) - s.sourceProcessor.log.Info("Added method map to ", "receiver", receiver, "method", m.Name) + log.Info("Added method map to ", "receiver", receiver, "method", m.Name) } } // Check for validation @@ -271,7 +279,7 @@ func (s *SourceInfoProcessor) getControllerFunc(funcDecl *ast.FuncDecl, p *packa } return } -func (s *SourceInfoProcessor) getControllerSpec(spec *ast.TypeSpec, p *packages.Package) (controllerSpec *model.TypeInfo) { +func (s *SourceInfoProcessor) getControllerSpec(spec *ast.TypeSpec, p *packages.Package, localImportMap map[string]string) (controllerSpec *model.TypeInfo) { structType := spec.Type.(*ast.StructType) // At this point we know it's a type declaration for a struct. @@ -282,6 +290,7 @@ func (s *SourceInfoProcessor) getControllerSpec(spec *ast.TypeSpec, p *packages. ImportPath: p.PkgPath, PackageName: p.Name, } + log := s.sourceProcessor.log.New("file", p.Fset.Position(spec.Pos()).Filename, "position", p.Fset.Position(spec.Pos()).Line) for _, field := range structType.Fields.List { // If field.Names is set, it's not an embedded type. if field.Names != nil { @@ -329,9 +338,12 @@ func (s *SourceInfoProcessor) getControllerSpec(spec *ast.TypeSpec, p *packages. importPath = p.PkgPath } else { var ok bool - if importPath, ok = s.sourceProcessor.importMap[pkgName]; !ok { - s.sourceProcessor.log.Error("Error: Failed to find import path for ", "package", pkgName, "type", typeName, "map", s.sourceProcessor.importMap) - continue + if importPath, ok = localImportMap[pkgName]; !ok { + log.Debug("Debug: Unusual, failed to find package locally ", "package", pkgName, "type", typeName, "map", s.sourceProcessor.importMap, "usedin", ) + if importPath, ok = s.sourceProcessor.importMap[pkgName]; !ok { + log.Error("Error: Failed to find import path for ", "package", pkgName, "type", typeName, "map", s.sourceProcessor.importMap, "usedin", ) + continue + } } } @@ -376,4 +388,37 @@ func (s *SourceInfoProcessor) getFuncName(funcDecl *ast.FuncDecl) string { prefix += "." } return prefix + funcDecl.Name.Name +} +func (s *SourceInfoProcessor) addImport(decl ast.Decl, p *packages.Package, localImportMap map[string]string, log logger.MultiLogger) (shouldContinue bool) { + shouldContinue = true + genDecl, ok := decl.(*ast.GenDecl) + if !ok { + return + } + + if genDecl.Tok == token.IMPORT { + shouldContinue = false + for _, spec := range genDecl.Specs { + importSpec := spec.(*ast.ImportSpec) + //fmt.Printf("*** import specification %#v\n", importSpec) + var pkgAlias string + if importSpec.Name != nil { + pkgAlias = importSpec.Name.Name + if pkgAlias == "_" { + continue + } + } + quotedPath := importSpec.Path.Value // e.g. "\"sample/app/models\"" + fullPath := quotedPath[1 : len(quotedPath) - 1] // Remove the quotes + if pkgAlias == "" { + pkgAlias = fullPath + if index := strings.LastIndex(pkgAlias, "/"); index > 0 { + pkgAlias = pkgAlias[index + 1:] + } + } + localImportMap[pkgAlias] = fullPath + } + + } + return } \ No newline at end of file diff --git a/parser2/source_processor.go b/parser2/source_processor.go index d7499ee..00ef0f4 100644 --- a/parser2/source_processor.go +++ b/parser2/source_processor.go @@ -1,15 +1,18 @@ package parser2 import ( - "go/ast" - "go/token" "github.com/revel/cmd/model" "golang.org/x/tools/go/packages" "github.com/revel/cmd/utils" - "errors" - + "go/parser" "strings" "github.com/revel/cmd/logger" + "os" + "path/filepath" + "go/ast" + "go/token" + "go/scanner" + ) type ( @@ -33,10 +36,6 @@ func ProcessSource(revelContainer *model.RevelContainer) (sourceInfo *model.Sour processor.log.Infof("From parsers : Structures:%d InitImports:%d ValidationKeys:%d %v", len(sourceInfo.StructSpecs), len(sourceInfo.InitImportPaths), len(sourceInfo.ValidationKeys), sourceInfo.PackageMap) } - if false { - compileError = errors.New("Incompleted") - utils.Logger.Panic("Not implemented") - } return } @@ -45,16 +44,20 @@ func NewSourceProcessor(revelContainer *model.RevelContainer) *SourceProcessor { s.sourceInfoProcessor = NewSourceInfoProcessor(s) return s } + func (s *SourceProcessor) parse() (compileError error) { + print("Parsing packages, (may require download if not cached)...") if compileError = s.addPackages(); compileError != nil { return } + println(" Completed") if compileError = s.addImportMap(); compileError != nil { return } if compileError = s.addSourceInfo(); compileError != nil { return } + s.sourceInfo.PackageMap = map[string]string{} getImportFromMap := func(packagePath string) string { for path := range s.packageMap { @@ -73,11 +76,15 @@ func (s *SourceProcessor) parse() (compileError error) { return } + +// Using the packages.Load function load all the packages and type specifications (forces compile). +// this sets the SourceProcessor.packageList []*packages.Package func (s *SourceProcessor) addPackages() (err error) { - allPackages := []string{s.revelContainer.ImportPath + "/...", model.RevelImportPath} + allPackages := []string{model.RevelImportPath + "/..."} for _, module := range s.revelContainer.ModulePathMap { allPackages = append(allPackages, module.ImportPath + "/...") // +"/app/controllers/...") } + s.log.Info("Reading packages", "packageList", allPackages) //allPackages = []string{s.revelContainer.ImportPath + "/..."} //+"/app/controllers/..."} config := &packages.Config{ @@ -102,9 +109,115 @@ func (s *SourceProcessor) addPackages() (err error) { Dir:s.revelContainer.AppPath, } s.packageList, err = packages.Load(config, allPackages...) - s.log.Info("Loaded packages ", "len results", len(s.packageList), "error", err) + s.log.Info("Loaded modules ", "len results", len(s.packageList), "error", err) + + + // Now process the files in the aap source folder s.revelContainer.ImportPath + "/...", + err = utils.Walk(s.revelContainer.AppPath, s.processPath) + s.log.Info("Loaded apps and modules ", "len results", len(s.packageList), "error", err) return } + +// This callback is used to build the packages for the "app" package. This allows us to +// parse the source files without doing a full compile on them +// This callback only processes folders, so any files passed to this will return a nil +func (s *SourceProcessor) processPath(path string, info os.FileInfo, err error) error { + if err != nil { + s.log.Error("Error scanning app source:", "error", err) + return nil + } + + // Ignore files and folders not marked tmp (since those are generated) + if !info.IsDir() || info.Name() == "tmp" { + return nil + } + + // Real work for processing the folder + pkgImportPath := s.revelContainer.ImportPath + appPath := s.revelContainer.BasePath + if appPath != path { + pkgImportPath = s.revelContainer.ImportPath + "/" + filepath.ToSlash(path[len(appPath)+1:]) + } + // Parse files within the path. + var pkgMap map[string]*ast.Package + fset := token.NewFileSet() + pkgMap, err = parser.ParseDir( + fset, + path, + func(f os.FileInfo) bool { + return !f.IsDir() && !strings.HasPrefix(f.Name(), ".") && strings.HasSuffix(f.Name(), ".go") + }, + 0) + + if err != nil { + if errList, ok := err.(scanner.ErrorList); ok { + var pos = errList[0].Pos + newError := &utils.SourceError{ + SourceType: ".go source", + Title: "Go Compilation Error", + Path: pos.Filename, + Description: errList[0].Msg, + Line: pos.Line, + Column: pos.Column, + SourceLines: utils.MustReadLines(pos.Filename), + } + + errorLink := s.revelContainer.Config.StringDefault("error.link", "") + if errorLink != "" { + newError.SetLink(errorLink) + } + return newError + } + + // This is exception, err already checked above. Here just a print + ast.Print(nil, err) + s.log.Fatal("Failed to parse dir", "error", err) + } + // Skip "main" packages. + delete(pkgMap, "main") + + // Ignore packages that end with _test + // These cannot be included in source code that is not generated specifically as a test + for i := range pkgMap { + if len(i) > 6 { + if string(i[len(i)-5:]) == "_test" { + delete(pkgMap, i) + } + } + } + + // If there is no code in this directory, skip it. + if len(pkgMap) == 0 { + return nil + } + + // There should be only one package in this directory. + if len(pkgMap) > 1 { + for i := range pkgMap { + println("Found package ", i) + } + utils.Logger.Fatal("Most unexpected! Multiple packages in a single directory:", "packages", pkgMap) + } + + // At this point there is only one package in the pkgs map, + p := &packages.Package{} + p.PkgPath = pkgImportPath + p.Fset = fset + for _, pkg := range pkgMap { + p.Name = pkg.Name + s.log.Info("Found package","pkg.Name", pkg.Name,"p.Name", p.PkgPath) + for filename,astFile := range pkg.Files { + p.Syntax = append(p.Syntax,astFile) + p.GoFiles = append(p.GoFiles,filename) + } + } + s.packageList = append(s.packageList, p) + + return nil +} + +// This function is used to populate a map so that we can lookup controller embedded types in order to determine +// if a Struct inherits from from revel.Controller func (s *SourceProcessor) addImportMap() (err error) { s.importMap = map[string]string{} s.packageMap = map[string]string{} @@ -113,41 +226,11 @@ func (s *SourceProcessor) addImportMap() (err error) { if len(p.Errors) > 0 { // Generate a compile error for _, e := range p.Errors { - if !strings.Contains(e.Msg, "fsnotify") { - err = utils.NewCompileError("", "", e) - } + s.log.Info("While reading packages encountered import error ignoring ", "PkgPath", p.PkgPath, "error", e) } } for _, tree := range p.Syntax { - for _, decl := range tree.Decls { - genDecl, ok := decl.(*ast.GenDecl) - if !ok { - continue - } - - if genDecl.Tok == token.IMPORT { - for _, spec := range genDecl.Specs { - importSpec := spec.(*ast.ImportSpec) - //fmt.Printf("*** import specification %#v\n", importSpec) - var pkgAlias string - if importSpec.Name != nil { - pkgAlias = importSpec.Name.Name - if pkgAlias == "_" { - continue - } - } - quotedPath := importSpec.Path.Value // e.g. "\"sample/app/models\"" - fullPath := quotedPath[1 : len(quotedPath) - 1] // Remove the quotes - if pkgAlias == "" { - pkgAlias = fullPath - if index := strings.LastIndex(pkgAlias, "/"); index > 0 { - pkgAlias = pkgAlias[index + 1:] - } - } - s.importMap[pkgAlias] = fullPath - } - } - } + s.importMap[tree.Name.Name] = p.PkgPath } } return @@ -165,53 +248,3 @@ func (s *SourceProcessor) addSourceInfo() (err error) { } return } - -// Add imports to the map from the source dir -//func addImports(imports map[string]string, decl ast.Decl, srcDir string) { -// genDecl, ok := decl.(*ast.GenDecl) -// if !ok { -// return -// } -// -// if genDecl.Tok != token.IMPORT { -// return -// } -// -// for _, spec := range genDecl.Specs { -// importSpec := spec.(*ast.ImportSpec) -// var pkgAlias string -// if importSpec.Name != nil { -// pkgAlias = importSpec.Name.Name -// if pkgAlias == "_" { -// continue -// } -// } -// quotedPath := importSpec.Path.Value // e.g. "\"sample/app/models\"" -// fullPath := quotedPath[1 : len(quotedPath)-1] // Remove the quotes -// -// // If the package was not aliased (common case), we have to import it -// // to see what the package name is. -// // TODO: Can improve performance here a lot: -// // 1. Do not import everything over and over again. Keep a cache. -// // 2. Exempt the standard library; their directories always match the package name. -// // 3. Can use build.FindOnly and then use parser.ParseDir with mode PackageClauseOnly -// if pkgAlias == "" { -// -// utils.Logger.Debug("Reading from build", "path", fullPath, "srcPath", srcDir, "gopath", build.Default.GOPATH) -// pkg, err := build.Import(fullPath, srcDir, 0) -// if err != nil { -// // We expect this to happen for apps using reverse routing (since we -// // have not yet generated the routes). Don't log that. -// if !strings.HasSuffix(fullPath, "/app/routes") { -// utils.Logger.Warn("Could not find import:", "path", fullPath, "srcPath", srcDir, "error", err) -// } -// continue -// } else { -// utils.Logger.Debug("Found package in dir", "dir", pkg.Dir, "name", pkg.ImportPath) -// } -// pkgAlias = pkg.Name -// } -// -// imports[pkgAlias] = fullPath -// } -//} \ No newline at end of file diff --git a/revel/new.go b/revel/new.go index 147d08a..21fe5b2 100644 --- a/revel/new.go +++ b/revel/new.go @@ -72,7 +72,7 @@ func newApp(c *model.CommandConfig) (err error) { // Check for an existing folder so we don't clobber it _, err = build.Import(c.ImportPath, "", build.FindOnly) if err == nil || !utils.Empty(c.AppPath) { - return utils.NewBuildError("Abort: Import path already exists.", "path", c.ImportPath) + return utils.NewBuildError("Abort: Import path already exists.", "path", c.ImportPath, "apppath", c.AppPath) } // checking and setting skeleton @@ -112,6 +112,10 @@ func newApp(c *model.CommandConfig) (err error) { fmt.Fprintln(os.Stdout, "Your application has been created in:\n ", c.AppPath) // Check to see if it should be run right off if c.New.Run { + // Need to prep the run command + c.Run.ImportPath = c.ImportPath + updateRunConfig(c,nil) + c.UpdateImportPath() runApp(c) } else { fmt.Fprintln(os.Stdout, "\nYou can run it with:\n revel run -a ", c.ImportPath) @@ -203,13 +207,13 @@ func setApplicationPath(c *model.CommandConfig) (err error) { // revel/revel#1014 validate relative path, we cannot use built-in functions // since Go import path is valid relative path too. // so check basic part of the path, which is "." - if filepath.IsAbs(c.ImportPath) || strings.HasPrefix(c.ImportPath, ".") { - utils.Logger.Fatalf("Abort: '%s' looks like a directory. Please provide a Go import path instead.", - c.ImportPath) - } // If we are running a vendored version of Revel we do not need to check for it. if !c.Vendored { + if filepath.IsAbs(c.ImportPath) || strings.HasPrefix(c.ImportPath, ".") { + utils.Logger.Fatalf("Abort: '%s' looks like a directory. Please provide a Go import path instead.", + c.ImportPath) + } _, err = build.Import(model.RevelImportPath, "", build.FindOnly) if err != nil { //// Go get the revel project diff --git a/revel/run.go b/revel/run.go index c996ae2..93f22e6 100644 --- a/revel/run.go +++ b/revel/run.go @@ -11,9 +11,7 @@ import ( "github.com/revel/cmd/harness" "github.com/revel/cmd/model" "github.com/revel/cmd/utils" - "go/build" "os" - "path/filepath" ) var cmdRun = &Command{ @@ -116,10 +114,7 @@ func updateRunConfig(c *model.CommandConfig, args []string) bool { // Returns true if this is an absolute path or a relative gopath func runIsImportPath(pathToCheck string) bool { - if _, err := build.Import(pathToCheck, "", build.FindOnly); err == nil { - return true - } - return filepath.IsAbs(pathToCheck) + return utils.DirExists(pathToCheck) } // Called to run the app diff --git a/revel/version_test.go b/revel/version_test.go index 4762721..ad13685 100644 --- a/revel/version_test.go +++ b/revel/version_test.go @@ -34,8 +34,8 @@ func TestVersion(t *testing.T) { a.Nil(main.Commands[model.VERSION].RunWith(c), "Failed to run version-test") }) if !t.Failed() { - if err := os.RemoveAll(gopath); err != nil { - a.Fail("Failed to remove test path") + if err := os.RemoveAll(gopath); err != nil && err!=os.ErrNotExist { + a.Fail("Failed to remove test path",err.Error()) } } } diff --git a/utils/file.go b/utils/file.go index ab915bd..92a2cfd 100644 --- a/utils/file.go +++ b/utils/file.go @@ -203,7 +203,8 @@ func Walk(root string, walkFn filepath.WalkFunc) error { return fsWalk(root, root, walkFn) } -// Walk the tree using the function +// Walk the path tree using the function +// Every file found will call the function func fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) error { fsWalkFunc := func(path string, info os.FileInfo, err error) error { if err != nil { @@ -308,9 +309,13 @@ func Exists(filename string) bool { // empty returns true if the given directory is empty. // the directory must exist. func Empty(dirname string) bool { + if !DirExists(dirname) { + return true + } dir, err := os.Open(dirname) if err != nil { Logger.Infof("error opening directory: %s", err) + return false } defer func() { _ = dir.Close() @@ -322,8 +327,8 @@ func Empty(dirname string) bool { // Find the full source dir for the import path, uses the build.Default.GOPATH to search for the directory func FindSrcPaths(appPath string, packageList []string, packageResolver func(pkgName string) error) (sourcePathsmap map[string]string, err error) { sourcePathsmap, missingList, err := findSrcPaths(appPath, packageList) - if err != nil && packageResolver != nil || len(missingList)>0 { - Logger.Info("Failed to find package, attempting to call resolver for missing packages","missing packages",missingList) + if err != nil && packageResolver != nil || len(missingList) > 0 { + Logger.Info("Failed to find package, attempting to call resolver for missing packages", "missing packages", missingList) for _, item := range missingList { if err = packageResolver(item); err != nil { return @@ -352,25 +357,25 @@ func findSrcPaths(appPath string, packagesList []string) (sourcePathsmap map[str Dir:appPath, } sourcePathsmap = map[string]string{} - Logger.Infof("Environment path %s root %s config env %s", os.Getenv("GOPATH"), os.Getenv("GOROOT"),config.Env) + Logger.Infof("Environment path %s root %s config env %s", os.Getenv("GOPATH"), os.Getenv("GOROOT"), config.Env) pkgs, err := packages.Load(config, packagesList...) - Logger.Infof("Environment path %s root %s config env %s", os.Getenv("GOPATH"), os.Getenv("GOROOT"),config.Env) + Logger.Infof("Environment path %s root %s config env %s", os.Getenv("GOPATH"), os.Getenv("GOROOT"), config.Env) Logger.Info("Loaded packages ", "len results", len(pkgs), "error", err, "basedir", appPath) for _, packageName := range packagesList { - found := false + found := false log := Logger.New("seeking", packageName) for _, pck := range pkgs { log.Info("Found package", "package", pck.ID) if pck.ID == packageName { if pck.Errors != nil && len(pck.Errors) > 0 { - log.Info("Error ", "count", len(pck.Errors), "App Import Path", pck.ID, "errors", pck.Errors) - continue + log.Error("Error ", "count", len(pck.Errors), "App Import Path", pck.ID, "filesystem path", pck.PkgPath, "errors", pck.Errors) + // continue } //a,_ := pck.MarshalJSON() log.Info("Found ", "count", len(pck.GoFiles), "App Import Path", pck.ID, "apppath", appPath) - if len(pck.GoFiles)>0 { + if len(pck.GoFiles) > 0 { sourcePathsmap[packageName] = filepath.Dir(pck.GoFiles[0]) found = true } diff --git a/watcher/watcher.go b/watcher/watcher.go index b055e7e..be7ab0a 100644 --- a/watcher/watcher.go +++ b/watcher/watcher.go @@ -33,7 +33,7 @@ type DiscerningListener interface { // Watcher allows listeners to register to be notified of changes under a given // directory. type Watcher struct { - // Parallel arrays of watcher/listener pairs. + // Parallel arrays of watcher/listener pairs. watchers []*fsnotify.Watcher listeners []Listener forceRefresh bool @@ -42,8 +42,8 @@ type Watcher struct { lastError int notifyMutex sync.Mutex paths *model.RevelContainer - refreshTimer *time.Timer // The timer to countdown the next refresh - timerMutex *sync.Mutex // A mutex to prevent concurrent updates + refreshTimer *time.Timer // The timer to countdown the next refresh + timerMutex *sync.Mutex // A mutex to prevent concurrent updates refreshChannel chan *utils.SourceError refreshChannelCount int refreshTimerMS time.Duration // The number of milliseconds between refreshing builds @@ -52,10 +52,10 @@ type Watcher struct { // Creates a new watched based on the container func NewWatcher(paths *model.RevelContainer, eagerRefresh bool) *Watcher { return &Watcher{ - forceRefresh: true, + forceRefresh: false, lastError: -1, paths: paths, - refreshTimerMS: time.Duration(paths.Config.IntDefault("watch.rebuild.delay", 10)), + refreshTimerMS: time.Duration(paths.Config.IntDefault("watch.rebuild.delay", 1000)), eagerRefresh: eagerRefresh || paths.DevMode && paths.Config.BoolDefault("watch", true) && @@ -85,7 +85,7 @@ func (w *Watcher) Listen(listener Listener, roots ...string) { for _, p := range roots { // is the directory / file a symlink? f, err := os.Lstat(p) - if err == nil && f.Mode()&os.ModeSymlink == os.ModeSymlink { + if err == nil && f.Mode() & os.ModeSymlink == os.ModeSymlink { var realPath string realPath, err = filepath.EvalSymlinks(p) if err != nil { @@ -200,12 +200,13 @@ func (w *Watcher) Notify() *utils.SourceError { case <-watcher.Errors: continue default: - // No events left to pull + // No events left to pull } break } - utils.Logger.Info("Watcher:Notify refresh state", "Current Index", i, " last error index", w.lastError) + utils.Logger.Info("Watcher:Notify refresh state", "Current Index", i, " last error index", w.lastError, + "force", w.forceRefresh, "refresh", refresh, "lastError", w.lastError == i) if w.forceRefresh || refresh || w.lastError == i { var err *utils.SourceError if w.serial { @@ -285,22 +286,10 @@ func (w *Watcher) rebuildRequired(ev fsnotify.Event, listener Listener) bool { } if dl, ok := listener.(DiscerningListener); ok { - if !dl.WatchFile(ev.Name) || ev.Op&fsnotify.Chmod == fsnotify.Chmod { + if !dl.WatchFile(ev.Name) || ev.Op & fsnotify.Chmod == fsnotify.Chmod { return false } } return true } -/* -var WatchFilter = func(c *Controller, fc []Filter) { - if MainWatcher != nil { - err := MainWatcher.Notify() - if err != nil { - c.Result = c.RenderError(err) - return - } - } - fc[0](c, fc[1:]) -} -*/