diff --git a/README.md b/README.md index 8574a9a26..2b95db977 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,14 @@ Please see **[Documentation](https://github.com/gpmgo/docs)** before you ever st # Commands ``` +NAME: + gopm - Go Package Manager + USAGE: gopm [global options] command [command options] [arguments...] VERSION: - 0.6.3.0311 + 0.6.5.0320 COMMANDS: get fetch remote package(s) and dependencies to local repository @@ -30,7 +33,7 @@ COMMANDS: update check and update gopm resources including itself config configurate gopm global settings help, h Shows a list of commands or help for one command - + GLOBAL OPTIONS: --noterm disable color output --version, -v print the version diff --git a/cmd/bin.go b/cmd/bin.go index bc9b0975b..b1fd5c088 100644 --- a/cmd/bin.go +++ b/cmd/bin.go @@ -1,4 +1,4 @@ -// Copyright 2013 gopm authors. +// Copyright 2013-2014 gopm authors. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain @@ -110,8 +110,7 @@ func runBin(ctx *cli.Context) { // Change to repository path. log.Log("Changing work directory to %s", repoPath) - err = os.Chdir(repoPath) - if err != nil { + if err = os.Chdir(repoPath); err != nil { log.Error("bin", "Fail to change work directory:") log.Fatal("", "\t"+err.Error()) } @@ -123,6 +122,7 @@ func runBin(ctx *cli.Context) { os.RemoveAll(path.Join(repoPath, doc.VENDOR)) }() + includes := make([]string, 0, 3) // Check if previous steps were successful. if com.IsFile(doc.GOPM_FILE_NAME) { log.Trace("Loading gopmfile...") @@ -133,6 +133,8 @@ func runBin(ctx *cli.Context) { if err == nil { log.Log("Target name: %s", pkgName) } + + includes = strings.Split(gf.MustValue("res", "include"), "|") } if len(pkgName) == 0 { @@ -157,20 +159,30 @@ func runBin(ctx *cli.Context) { } if com.IsExist(movePath + "/" + binName) { - err = os.Remove(movePath + "/" + binName) - if err != nil { + if err = os.Remove(movePath + "/" + binName); err != nil { log.Warn("Cannot remove binary in work directory:") log.Warn("\t %s", err) } } - err = os.Rename(binName, movePath+"/"+binName) - if err != nil { + if err = os.Rename(binName, movePath+"/"+binName); err != nil { log.Error("bin", "Fail to move binary:") log.Fatal("", "\t"+err.Error()) } os.Chmod(movePath+"/"+binName, os.ModePerm) + if len(includes) > 0 { + log.Log("Copying resources to %s", movePath) + for _, include := range includes { + if com.IsDir(include) { + if err = com.CopyDir(include, filepath.Join(movePath, include)); err != nil { + log.Error("bin", "Fail to copy following resource:") + log.Error("", "\t"+include) + } + } + } + } + log.Log("Changing work directory back to %s", wd) os.Chdir(wd) diff --git a/cmd/cmd.go b/cmd/cmd.go index 8a2d5d24a..279bb84d3 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -83,5 +83,6 @@ func versionSuffix(value string) string { } func isSubpackage(rootPath, targetPath string) bool { - return strings.HasSuffix(workDir, rootPath) || strings.HasPrefix(rootPath, targetPath) + return strings.HasSuffix(strings.Replace(workDir, "\\", "/", -1), rootPath) || + strings.HasPrefix(rootPath, targetPath) } diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go new file mode 100644 index 000000000..70a146b4f --- /dev/null +++ b/cmd/cmd_test.go @@ -0,0 +1,31 @@ +// Copyright 2013-2014 gopm authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package cmd + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func Test_parseTarget(t *testing.T) { + Convey("Target is empty", t, func() { + So(parseTarget(""), ShouldEqual, ".") + }) + + Convey("Target is not empty", t, func() { + So(parseTarget("github.com/gpmgo/gopm"), ShouldEqual, "github.com/gpmgo/gopm") + }) +} diff --git a/cmd/gen.go b/cmd/gen.go index 1c78171e0..5e067b7a8 100644 --- a/cmd/gen.go +++ b/cmd/gen.go @@ -1,4 +1,4 @@ -// Copyright 2013 gopm authors. +// Copyright 2013-2014 gopm authors. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain @@ -58,12 +58,11 @@ func runGen(ctx *cli.Context) { targetPath := parseTarget(gf.MustValue("target", "path")) // Get and set dependencies. - imports := doc.GetAllImports([]string{workDir}, targetPath, ctx.Bool("example")) - log.Log("%v", imports) + imports := doc.GetAllImports([]string{workDir}, targetPath, ctx.Bool("example"), false) for _, p := range imports { p = doc.GetProjectPath(p) // Skip subpackage(s) of current project. - if strings.HasSuffix(workDir, p) || strings.HasPrefix(p, targetPath) { + if isSubpackage(p, targetPath) { continue } diff --git a/cmd/get.go b/cmd/get.go index a6f616b86..d747502e6 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -106,8 +106,11 @@ func runGet(ctx *cli.Context) { switch len(ctx.Args()) { case 0: getByGopmfile(ctx) - default: + case 1: getByPath(ctx) + default: + log.Error("get", "too many arguments") + log.Help("Try 'gopm help get' to get more information") } } @@ -120,10 +123,11 @@ func getByGopmfile(ctx *cli.Context) { targetPath := parseTarget(gf.MustValue("target", "path")) // Get dependencies. - imports := doc.GetAllImports([]string{workDir}, targetPath, ctx.Bool("example")) + imports := doc.GetAllImports([]string{workDir}, targetPath, ctx.Bool("example"), false) nodes := make([]*doc.Node, 0, len(imports)) for _, p := range imports { + // TODO: DOING TEST CASES!!! p = doc.GetProjectPath(p) // Skip subpackage(s) of current project. if isSubpackage(p, targetPath) { @@ -312,12 +316,12 @@ func downloadPackage(ctx *cli.Context, nod *doc.Node) (*doc.Node, []string) { vcs := getVcsName(gopathDir) if ctx.Bool("update") && ctx.Bool("gopath") && len(vcs) > 0 { err = updateByVcs(vcs, gopathDir) - imports = doc.GetAllImports([]string{gopathDir}, nod.RootPath, false) + imports = doc.GetAllImports([]string{gopathDir}, nod.RootPath, false, false) } else { // If package has revision and exist, then just check dependencies. if nod.IsGetDepsOnly { return nod, doc.GetAllImports([]string{path.Join(installRepoPath, nod.RootPath) + versionSuffix(nod.Value)}, - nod.RootPath, ctx.Bool("example")) + nod.RootPath, ctx.Bool("example"), false) } nod.Revision = doc.LocalNodes.MustValue(nod.RootPath, "value") imports, err = doc.PureDownload(nod, installRepoPath, ctx) //CmdGet.Flags) diff --git a/cmd/gopath.go b/cmd/gopath.go index 8fe40064e..1c5c321cb 100644 --- a/cmd/gopath.go +++ b/cmd/gopath.go @@ -1,3 +1,17 @@ +// Copyright 2013-2014 gopm authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + package cmd import ( @@ -35,7 +49,7 @@ func getGopmPkgs(dirPath string, isTest bool) (pkgs map[string]*doc.Pkg, err err } } - imports := doc.GetAllImports([]string{dirPath}, ".", false) + imports := doc.GetAllImports([]string{dirPath}, ".", false, false) pkgs = make(map[string]*doc.Pkg) for _, name := range imports { if name == "C" { @@ -257,7 +271,7 @@ func genNewGoPath(ctx *cli.Context, isTest bool) { continue } - if !isExistP && ((len(pkg.Value) > 0 || ctx.Bool("remote")) || + if !isExistP && (len(pkg.Value) > 0 || ctx.Bool("remote") || !com.IsDir(filepath.Join(installGopath, pkg.ImportPath))) { log.Log("Linking %s", name+suf) err = autoLink(oldPath, newPath) diff --git a/cmd/install.go b/cmd/install.go index f83aff7bb..7d8af31a0 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -1,4 +1,4 @@ -// Copyright 2013 gopm authors. +// Copyright 2013-2014 gopm authors. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain @@ -83,8 +83,7 @@ func runInstall(ctx *cli.Context) { var installRepos []string if ctx.Bool("pkg") { curPath, _ := filepath.Abs(".") - installRepos = doc.GetAllImports([]string{curPath}, - ".", ctx.Bool("example")) + installRepos = doc.GetAllImports([]string{curPath}, ".", ctx.Bool("example"), false) } else { if len(target) == 0 { target = pkgName diff --git a/doc/conf.go b/doc/conf.go index 610da82d9..590afafd6 100644 --- a/doc/conf.go +++ b/doc/conf.go @@ -59,7 +59,7 @@ func init() { } } Cfg, err = goconfig.LoadConfigFile(cfgPath) - if _, err = os.Create(cfgPath); err != nil { + if err != nil { log.Error("", "Fail to load gopm config file") log.Fatal("", err.Error()) } diff --git a/doc/utils.go b/doc/utils.go index 558ab2ac9..7ff1fc8a1 100644 --- a/doc/utils.go +++ b/doc/utils.go @@ -1,4 +1,4 @@ -// Copyright 2013 gopm authors. +// Copyright 2013-2014 gopm authors. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain @@ -15,11 +15,16 @@ package doc import ( + "bytes" "go/build" + "io" + "io/ioutil" "os" "path" + "path/filepath" "regexp" "strings" + "time" "github.com/Unknwon/com" @@ -29,26 +34,105 @@ import ( const VENDOR = ".vendor" // GetDirsInfo returns os.FileInfo of all sub-directories in root path. -func GetDirsInfo(rootPath string) []os.FileInfo { +func GetDirsInfo(rootPath string) ([]os.FileInfo, error) { + if !com.IsDir(rootPath) { + log.Warn("Directory %s does not exist", rootPath) + return []os.FileInfo{}, nil + } + rootDir, err := os.Open(rootPath) if err != nil { - log.Error("", "Fail to open directory") - log.Fatal("", err.Error()) + return nil, err } defer rootDir.Close() dirs, err := rootDir.Readdir(0) if err != nil { - log.Error("", "Fail to read directory") - log.Fatal("", err.Error()) + return nil, err } - return dirs + return dirs, nil +} + +// A Source describles a Source code file. +type Source struct { + SrcName string + SrcData []byte +} + +func (s *Source) Name() string { return s.SrcName } +func (s *Source) Size() int64 { return int64(len(s.SrcData)) } +func (s *Source) Mode() os.FileMode { return 0 } +func (s *Source) ModTime() time.Time { return time.Time{} } +func (s *Source) IsDir() bool { return false } +func (s *Source) Sys() interface{} { return nil } +func (s *Source) Data() []byte { return s.SrcData } + +type Context struct { + build.Context + importPath string + srcFiles map[string]*Source +} + +func (ctx *Context) readDir(dir string) ([]os.FileInfo, error) { + fis := make([]os.FileInfo, 0, len(ctx.srcFiles)) + for _, src := range ctx.srcFiles { + fis = append(fis, src) + } + return fis, nil +} + +func (ctx *Context) openFile(path string) (r io.ReadCloser, err error) { + if src, ok := ctx.srcFiles[filepath.Base(path)]; ok { + return ioutil.NopCloser(bytes.NewReader(src.Data())), nil + } + return nil, os.ErrNotExist } // GetImports returns package denpendencies. -func GetImports(absPath, importPath string, example bool) []string { - pkg, err := build.ImportDir(absPath, build.AllowBinary) +func GetImports(absPath, importPath string, example, test bool) []string { + fis, err := GetDirsInfo(absPath) + if err != nil { + log.Error("", "Fail to get directory's information") + log.Fatal("", err.Error()) + } + absPath += "/" + + ctx := new(Context) + ctx.importPath = importPath + ctx.srcFiles = make(map[string]*Source) + ctx.Context = build.Default + ctx.JoinPath = path.Join + ctx.IsAbsPath = path.IsAbs + ctx.ReadDir = ctx.readDir + ctx.OpenFile = ctx.openFile + + // TODO: Load too much, need to make sure which is imported which are not. + dirs := make([]string, 0, 10) + for _, fi := range fis { + if strings.Contains(fi.Name(), VENDOR) { + continue + } + + if fi.IsDir() { + dirs = append(dirs, absPath+fi.Name()) + continue + } else if !test && strings.HasSuffix(fi.Name(), "_test.go") { + continue + } else if !strings.HasSuffix(fi.Name(), ".go") || strings.HasPrefix(fi.Name(), ".") || + strings.HasPrefix(fi.Name(), "_") { + continue + } + src := &Source{SrcName: fi.Name()} + src.SrcData, err = ioutil.ReadFile(absPath + fi.Name()) + if err != nil { + log.Error("", "Fail to read file") + log.Fatal("", err.Error()) + } + ctx.srcFiles[fi.Name()] = src + } + + pkg, err := ctx.ImportDir(absPath, build.AllowBinary) if err != nil { if _, ok := err.(*build.NoGoError); !ok { log.Error("", "Fail to get imports") @@ -56,9 +140,6 @@ func GetImports(absPath, importPath string, example bool) []string { } } - fis := GetDirsInfo(absPath) - absPath += "/" - imports := make([]string, 0, len(pkg.Imports)) for _, p := range pkg.Imports { if !IsGoRepoPath(p) && !strings.HasPrefix(p, importPath) { @@ -66,31 +147,25 @@ func GetImports(absPath, importPath string, example bool) []string { } } - // TODO: Load too much - dirs := make([]string, 0, len(imports)) - for _, fi := range fis { - if fi.IsDir() && !strings.Contains(fi.Name(), VENDOR) { - dirs = append(dirs, absPath+fi.Name()) - } - } - if len(dirs) > 0 { - imports = append(imports, GetAllImports(dirs, importPath, example)...) + imports = append(imports, GetAllImports(dirs, importPath, example, test)...) } return imports } +// isVcsPath returns true if the directory was created by VCS. func isVcsPath(dirPath string) bool { return strings.Contains(dirPath, "/.git") || strings.Contains(dirPath, "/.hg") || strings.Contains(dirPath, "/.svn") } -func GetAllImports(dirs []string, importPath string, example bool) (imports []string) { +// GetAllImports returns all imports in given directory and all sub-directories. +func GetAllImports(dirs []string, importPath string, example, test bool) (imports []string) { for _, d := range dirs { if !isVcsPath(d) && !(!example && strings.Contains(d, "example")) { - imports = append(imports, GetImports(d, importPath, example)...) + imports = append(imports, GetImports(d, importPath, example, test)...) } } return imports @@ -116,7 +191,11 @@ func CheckIsExistWithVCS(path string) bool { } // Check if only has VCS folder. - dirs := GetDirsInfo(path) + dirs, err := GetDirsInfo(path) + if err != nil { + log.Error("", "Fail to get directory's information") + log.Fatal("", err.Error()) + } if len(dirs) > 1 { return true diff --git a/doc/utils_test.go b/doc/utils_test.go new file mode 100644 index 000000000..568cc6d0f --- /dev/null +++ b/doc/utils_test.go @@ -0,0 +1,71 @@ +// Copyright 2013-2014 gopm authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package doc + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +var VcsTestPairs = map[string]bool{ + "/.hg": true, + "/.git": true, + "/.svn": true, + "/.vendor": false, +} + +func Test_isVcsPath(t *testing.T) { + Convey("Test if the path is belonging to VCS", t, func() { + for name, expect := range VcsTestPairs { + So(isVcsPath(name), ShouldEqual, expect) + } + }) +} + +func TestGetDirsInfo(t *testing.T) { + Convey("Get directory's information that exist", t, func() { + dis, err := GetDirsInfo(".") + So(err, ShouldBeNil) + So(len(dis), ShouldEqual, 13) + }) + + Convey("Get directory's information does not exist", t, func() { + dis, err := GetDirsInfo("./404") + So(err, ShouldBeNil) + So(len(dis), ShouldEqual, 0) + }) +} + +var GoStdTestPairs = map[string]bool{ + "net/http": true, + "fmt": true, + "github.com/gpmgo/gopm": false, + "github.com/Unknwon/com": false, +} + +func TestIsGoRepoPath(t *testing.T) { + Convey("Test if the path is belonging to Go STD", t, func() { + for name, expect := range GoStdTestPairs { + So(IsGoRepoPath(name), ShouldEqual, expect) + } + }) +} + +func TestGetImports(t *testing.T) { + Convey("Get package that are imported", t, func() { + So(len(GetImports(".", "github.com/gpmgo/gopm/docs", false)), ShouldEqual, 4) + }) +} diff --git a/doc/vcs.go b/doc/vcs.go index c3afc090c..224ae7507 100644 --- a/doc/vcs.go +++ b/doc/vcs.go @@ -1,4 +1,4 @@ -// Copyright 2013 gopm authors. +// Copyright 2013-2014 gopm authors. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain @@ -245,7 +245,11 @@ metaScan: } func getImports(rootPath string, match map[string]string, cmdFlags map[string]bool, nod *Node) (imports []string) { - dirs := GetDirsInfo(rootPath) + dirs, err := GetDirsInfo(rootPath) + if err != nil { + log.Error("", "Fail to get directory's information") + log.Fatal("", err.Error()) + } for _, d := range dirs { if d.IsDir() && !(!cmdFlags["-e"] && strings.Contains(d.Name(), "example")) { diff --git a/gopm.go b/gopm.go index 0695a5ad9..072f2299b 100644 --- a/gopm.go +++ b/gopm.go @@ -29,9 +29,10 @@ import ( // Test that go1.1 tag above is included in builds. main.go refers to this definition. const go11tag = true -const APP_VER = "0.6.3.0312" +const APP_VER = "0.6.5.0320" -// //cmd.CmdSearch, +// cmd.CmdTest, +// cmd.CmdSearch, // cmdClean, // cmdDoc, // cmdEnv, @@ -39,7 +40,6 @@ const APP_VER = "0.6.3.0312" // cmdList, // cmdTool, // cmdVet, -// } func init() { runtime.GOMAXPROCS(runtime.NumCPU()) @@ -59,7 +59,6 @@ func main() { cmd.CmdInstall, cmd.CmdUpdate, cmd.CmdConfig, - //cmd.CmdTest, } app.Flags = append(app.Flags, []cli.Flag{ cli.BoolFlag{"noterm", "disable color output"},