diff --git a/README.md b/README.md index a01df5ac7..5501fbeba 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,4 @@ gpm - Go Package Manager ![GPMGo_Logo](https://raw.github.com/GPMGo/gpm-site/master/static/img/gpmgo2.png?raw=true) -gpm(Go Package Manager) is a Go package manage tool for search, install, update and share packages in Go. +gpm(Go Package Manager) is a Go package manage tool for search, install, update, save and share your packages in Go. diff --git a/README_ZH.md b/README_ZH.md index aa6b0bc2a..5fc2d8f16 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -3,4 +3,4 @@ gpm - Go 项目管理工具 ![GPMGo_Logo](https://raw.github.com/GPMGo/gpm-site/master/static/img/gpmgo2.png?raw=true) -gpm(Go 项目管理工具) 是一款涵盖搜索、安装、更新以及分享 Go 项目的管理工具。 \ No newline at end of file +gpm(Go 项目管理工具) 是一款涵盖搜索、安装、更新、保存以及分享 Go 项目的管理工具。 \ No newline at end of file diff --git a/conf/gpm.toml b/conf/gpm.toml index bd0cee6f2..7757b2316 100644 --- a/conf/gpm.toml +++ b/conf/gpm.toml @@ -1,5 +1,5 @@ # This is a configuration file for gpm with toml format. title = "gpm(Go Package Manager)" -version = "v0.0.1 Build 0517" +version = "v0.0.2 Build 0519" user_language = "en-US" \ No newline at end of file diff --git a/doc/github.go b/doc/github.go index f25916cb9..a0e063ec2 100644 --- a/doc/github.go +++ b/doc/github.go @@ -5,17 +5,17 @@ package doc import ( - "archive/zip" + /*"archive/zip" "bytes" "fmt" - "io" + "io"*/ "net/http" - "os" - "path" + /*"os" + "path"*/ "regexp" - "strings" + //"strings" - "github.com/GPMGo/gpm/utils" + //"github.com/GPMGo/gpm/utils" ) var ( @@ -28,8 +28,8 @@ func SetGithubCredentials(id, secret string) { githubCred = "client_id=" + id + "&client_secret=" + secret } -func GetGithubDoc(client *http.Client, match map[string]string, commit string) (*Package, error) { - SetGithubCredentials("1862bcb265171f37f36c", "308d71ab53ccd858416cfceaed52d5d5b7d53c5f") +func GetGithubDoc(client *http.Client, match map[string]string, commit string) (*Package, []string, error) { + /*SetGithubCredentials("1862bcb265171f37f36c", "308d71ab53ccd858416cfceaed52d5d5b7d53c5f") match["cred"] = githubCred var refs []*struct { @@ -130,14 +130,14 @@ func GetGithubDoc(client *http.Client, match map[string]string, commit string) ( data: fbytes, }) }*/ - } + /* } - pkg := &Package{ - ImportPath: importPath, - AbsPath: installPath, - Commit: commit, - Dirs: dirs, - } + pkg := &Package{ + ImportPath: importPath, + AbsPath: installPath, + Commit: commit, + Dirs: dirs, + }*/ - return pkg, nil + return nil, nil, nil } diff --git a/doc/walker.go b/doc/walker.go deleted file mode 100644 index b52327cd5..000000000 --- a/doc/walker.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2011 Gary Burd -// Copyright 2013 Unknown -// -// 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 ( - "bytes" - "errors" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/token" - "io" - "io/ioutil" - "os" - "path" - "regexp" - "runtime" - "strings" - "time" - - "github.com/GPMGo/gpm/models" -) - -type sliceWriter struct{ p *[]byte } - -func (w sliceWriter) Write(p []byte) (int, error) { - *w.p = append(*w.p, p...) - return len(p), nil -} - -func (w *walker) readDir(dir string) ([]os.FileInfo, error) { - if dir != w.pinfo.Path { - panic("unexpected") - } - fis := make([]os.FileInfo, 0, len(w.srcs)) - for _, src := range w.srcs { - fis = append(fis, src) - } - return fis, nil -} - -func (w *walker) openFile(path string) (io.ReadCloser, error) { - if strings.HasPrefix(path, w.pinfo.Path+"/") { - if src, ok := w.srcs[path[len(w.pinfo.Path)+1:]]; ok { - return ioutil.NopCloser(bytes.NewReader(src.data)), nil - } - } - panic("unexpected") -} - -func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) { - pkg := imports[path] - if pkg == nil { - // Guess the package name without importing it. Start with the last - // element of the path. - name := path[strings.LastIndex(path, "/")+1:] - - // Trim commonly used prefixes and suffixes containing illegal name - // runes. - name = strings.TrimSuffix(name, ".go") - name = strings.TrimSuffix(name, "-go") - name = strings.TrimPrefix(name, "go.") - name = strings.TrimPrefix(name, "go-") - name = strings.TrimPrefix(name, "biogo.") - - // It's also common for the last element of the path to contain an - // extra "go" prefix, but not always. TODO: examine unresolved ids to - // detect when trimming the "go" prefix is appropriate. - - pkg = ast.NewObj(ast.Pkg, name) - pkg.Data = ast.NewScope(nil) - imports[path] = pkg - } - return pkg, nil -} - -var buildPicPattern = regexp.MustCompile(`\[+!+\[+([a-zA-Z ]*)+\]+\(+[a-zA-z]+://[^\s]*`) - -// build generates data from source files. -/*func (w *walker) build(srcs []*source) (*models.PkgInfo, error) { - // Set created time. - w.pinfo.Created = time.Now().UTC() - - // Add source files to walker, I skipped references here. - w.srcs = make(map[string]*source) - for _, src := range srcs { - w.srcs[src.name] = src - } - - w.fset = token.NewFileSet() - - // Find the package and associated files. - ctxt := build.Context{ - GOOS: runtime.GOOS, - GOARCH: runtime.GOARCH, - CgoEnabled: true, - JoinPath: path.Join, - IsAbsPath: path.IsAbs, - SplitPathList: func(list string) []string { return strings.Split(list, ":") }, - IsDir: func(path string) bool { panic("unexpected") }, - HasSubdir: func(root, dir string) (rel string, ok bool) { panic("unexpected") }, - ReadDir: func(dir string) (fi []os.FileInfo, err error) { return w.readDir(dir) }, - OpenFile: func(path string) (r io.ReadCloser, err error) { return w.openFile(path) }, - Compiler: "gc", - } - - bpkg, err := ctxt.ImportDir(w.pinfo.Path, 0) - // Continue if there are no Go source files; we still want the directory info. - _, nogo := err.(*build.NoGoError) - if err != nil { - if nogo { - err = nil - } else { - return w.pinfo, errors.New("doc.walker.build(): " + err.Error()) - } - } - - // Parse the Go files - - files := make(map[string]*ast.File) - for _, name := range append(bpkg.GoFiles, bpkg.CgoFiles...) { - file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments) - if err != nil { - //beego.Error("doc.walker.build():", err) - continue - } - files[name] = file - } - - w.pinfo.Imports = bpkg.Imports - fmt.Println(w.pinfo) - // beego.Info("doc.walker.build(", pdoc.ImportPath, "), Goroutine #", runtime.NumGoroutine()) - return w.pinfo, err -} -*/ diff --git a/gpm.go b/gpm.go index a9d86e4b9..d2d3cce9b 100644 --- a/gpm.go +++ b/gpm.go @@ -23,9 +23,8 @@ import ( ) var ( - config tomlConfig - appPath string // Application path. - isWindows bool // Indicates if current system is windows. + config tomlConfig + appPath string // Application path. ) type tomlConfig struct { @@ -93,7 +92,6 @@ func getAppPath() bool { } if runtime.GOOS == "windows" { - isWindows = true // Replace all '\' to '/'. appPath = strings.Replace(filepath.Dir(appPath), "\\", "/", -1) + "/" } diff --git a/i18n/en-US/usage_install.txt b/i18n/en-US/usage_install.txt index d3a80ebac..78bf628e7 100644 --- a/i18n/en-US/usage_install.txt +++ b/i18n/en-US/usage_install.txt @@ -18,6 +18,7 @@ The list flags accept a space-separated list of strings. To embed spaces in an element in the list, surround it with either single or double quotes. For more about specifying packages, see 'go help packages'. -For more about hash, see 'gpm help hash'. +For more about bundle, see 'gpm help bundle'. +For more about snapshot, see 'gpm help snapshot'. See also: gpm build. diff --git a/install.go b/install.go index 07e23fa9a..a6e60bdf3 100644 --- a/install.go +++ b/install.go @@ -21,7 +21,7 @@ var ( ) var cmdInstall = &Command{ - UsageLine: "install [install flags] ", + UsageLine: "install [install flags] ", } func init() { @@ -34,88 +34,145 @@ func init() { } } -func runInstall(cmd *Command, args []string) { - // Check if has flags. - num := 0 +// printPrompt prints prompt information to users to +// let them know what's going on. +func printPrompt(flag string) { + switch flag { + case "-p": + fmt.Printf("You enabled pure download.\n") + case "-d": + fmt.Printf("You enabled download without installing.\n") + } +} + +// checkFlags checks if the flag exists with correct format. +func checkFlags(args []string) int { + num := 0 // Number of valid flags, use to cut out. for i, f := range args { - if strings.Index(f, "-") > -1 { - // Deal with flags. - if _, ok := cmdInstall.Flags[f]; ok { - cmdInstall.Flags[f] = true - printPrompt(f) - } else { - fmt.Printf("Unknown flag: %s.\n", f) - return - } - num = i + 1 + // Check flag prefix '-'. + if !strings.HasPrefix(f, "-") { + // Not a flag, finish check process. + break } + + // Check if it a valid flag. + /* Here we use ok pattern to check it because + this way can avoid same flag appears multiple times.*/ + if _, ok := cmdInstall.Flags[f]; ok { + cmdInstall.Flags[f] = true + printPrompt(f) + } else { + fmt.Printf("Unknown flag: %s.\n", f) + return -1 + } + num = i + 1 + } + + return num +} + +// checkVCSTool checks if users have installed version control tools. +func checkVCSTool() { + // git. + if _, err := exec.LookPath("git"); err == nil { + isHasGit = true + } + // hg. + if _, err := exec.LookPath("hg"); err == nil { + isHasHg = true + } + // svn. +} + +func runInstall(cmd *Command, args []string) { + // Check flags. + num := checkFlags(args) + if num == -1 { + return } - // Cut out flag. args = args[num:] // Check length of arguments. if len(args) < 1 { - fmt.Printf("Please list at least one package.\n") + fmt.Printf("Please list at least one package/bundle/snapshot.\n") return } // Check version control tools. - _, err := exec.LookPath("git") - if err == nil { - isHasGit = true - } - _, err = exec.LookPath("hg") - if err == nil { - isHasHg = true - } + checkVCSTool() - // Install package(s). - for _, p := range args { - // Check if it is a hash string. - // TODO + // Download packages. + commits := make([]string, len(args)) + downloadPackages(args, commits) - // Check if it is vaild remote path. - if !utils.IsValidRemotePath(p) { - fmt.Printf("Invalid remote path: %s.\n", p) - } else { - downloadPackage(p, "") - } - } + // Install packages all together. + fmt.Println("Well done.") } -func printPrompt(flag string) { - switch flag { - case "-p": - fmt.Println("You enabled pure download.") - case "-d": - fmt.Println("You enabled download without installing.") +// downloadPackages downloads packages with certain commit, +// if the commit is empty string, then it downloads all dependencies, +// otherwise, it only downloada package with specific commit only. +func downloadPackages(pkgs, commits []string) { + // Check all packages, they may be bundles, snapshots or raw packages path. + for i, p := range pkgs { + // Check if it is a bundle or snapshot. + switch { + case p[0] == 'B': + // TODO: api.GetBundleInfo() + case p[0] == 'S': + // TODO: api.GetSnapshotInfo() + case utils.IsValidRemotePath(p) && !downloadCache[p]: + // Download package. + pkg, imports := downloadPackage(p, commits[i]) + if imports != nil { + // Need to download dependencies. + tags := make([]string, len(imports)) + downloadPackages(imports, tags) + continue + } + + // Only save package information with specific commit. + if pkg != nil { + // Save record in local database. + fmt.Printf("Saved information: %s:%s.\n", pkg.ImportPath, pkg.Commit) + } + default: + // Invalid import path. + fmt.Printf("Skipped invalid import path: %s.\n", p) + } } } // downloadPackage download package either use version control tools or not. -func downloadPackage(path, commit string) { +func downloadPackage(path, commit string) (pkg *doc.Package, imports []string) { // Check if use version control tools. switch { case !cmdInstall.Flags["-p"] && ((path[0] == 'g' && isHasGit) || (path[0] == 'c' && isHasHg)): // github.com, code.google.com + fmt.Printf("Installing package(%s) through 'go get'.\n", path) args := checkGoGetFlags() args = append(args, path) - fmt.Printf("Installing package: %s.\n", path) executeGoCommand(args) + return nil, nil default: // Pure download. if !cmdInstall.Flags["-p"] { - fmt.Printf("No version control tool available, pure download enabled!\n") + cmdInstall.Flags["-p"] = true + fmt.Printf("No version control tool is available, pure download enabled!\n") } fmt.Printf("Downloading package: %s.\n", path) - pkg, err := pureDownload(path, commit) + // Mark as donwloaded. + downloadCache[path] = true + + var err error + pkg, imports, err = pureDownload(path, commit) if err != nil { fmt.Printf("Fail to download package(%s) with error: %s.\n", path, err) + return nil, nil } else { fmt.Println(pkg) - fmt.Printf("Checking imports(%s).\n", path) - - fmt.Printf("Installing package: %s.\n", path) + fmt.Printf("Downloaded package: %s.\n", path) + return pkg, imports } } } @@ -137,7 +194,7 @@ func checkGoGetFlags() (args []string) { type service struct { pattern *regexp.Regexp prefix string - get func(*http.Client, map[string]string, string) (*doc.Package, error) + get func(*http.Client, map[string]string, string) (*doc.Package, []string, error) } // services is the list of source code control services handled by gopkgdoc. @@ -148,8 +205,8 @@ var services = []*service{ //{launchpadPattern, "launchpad.net/", getLaunchpadDoc}, } -// pureDownload downloads package without control control. -func pureDownload(path, commit string) (pinfo *doc.Package, err error) { +// pureDownload downloads package without version control. +func pureDownload(path, commit string) (pinfo *doc.Package, imports []string, err error) { for _, s := range services { if s.get == nil || !strings.HasPrefix(path, s.prefix) { continue @@ -157,7 +214,8 @@ func pureDownload(path, commit string) (pinfo *doc.Package, err error) { m := s.pattern.FindStringSubmatch(path) if m == nil { if s.prefix != "" { - return nil, doc.NotFoundError{"Import path prefix matches known service, but regexp does not."} + return nil, nil, + doc.NotFoundError{"Import path prefix matches known service, but regexp does not."} } continue } @@ -169,5 +227,5 @@ func pureDownload(path, commit string) (pinfo *doc.Package, err error) { } return s.get(doc.HttpClient, match, commit) } - return nil, doc.ErrNoMatch + return nil, nil, doc.ErrNoMatch } diff --git a/utils/utils.go b/utils/utils.go index be7056e9e..4f3471343 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -369,7 +369,7 @@ func IsValidRemotePath(importPath string) bool { return true } -// GetGOPATHs return all GOPATH in system. +// GetGOPATH return all GOPATH in system. func GetGOPATH() []string { gopath := os.Getenv("GOPATH") var paths []string