github.com/Xenoex/gopm@v0.6.5/cmd/get.go (about)

     1  // Copyright 2013-2014 gopm authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  package cmd
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path"
    21  	"strings"
    22  
    23  	"github.com/Unknwon/com"
    24  	"github.com/Unknwon/goconfig"
    25  	"github.com/codegangsta/cli"
    26  
    27  	"github.com/gpmgo/gopm/doc"
    28  	"github.com/gpmgo/gopm/log"
    29  )
    30  
    31  var (
    32  	installRepoPath string // The path of gopm local repository.
    33  	installGopath   string // The first path in the GOPATH.
    34  	//review:in the code it is not used , instead ctx.bool("gopath") is used
    35  	isHasGopath bool // Indicates whether system has GOPATH.
    36  
    37  	downloadCache map[string]bool // Saves packages that have been downloaded.
    38  	downloadCount int
    39  	failConut     int
    40  )
    41  
    42  var CmdGet = cli.Command{
    43  	Name:  "get",
    44  	Usage: "fetch remote package(s) and dependencies to local repository",
    45  	Description: `Command get fetches a package, and any pakcage that it depents on. 
    46  If the package has a gopmfile, the fetch process will be driven by that.
    47  
    48  gopm get
    49  gopm get <import path>@[<tag|commit|branch>:<value>]
    50  gopm get <package name>@[<tag|commit|branch>:<value>]
    51  
    52  Can specify one or more: gopm get beego@tag:v0.9.0 github.com/beego/bee
    53  
    54  If no version specified and package exists in GOPATH,
    55  it will be skipped unless user enabled '--remote, -r' option 
    56  then all the packages go into gopm local repository.`,
    57  	Action: runGet,
    58  	Flags: []cli.Flag{
    59  		cli.BoolFlag{"gopath, g", "download all pakcages to GOPATH"},
    60  		cli.BoolFlag{"update, u", "update pakcage(s) and dependencies if any"},
    61  		cli.BoolFlag{"example, e", "download dependencies for example folder"},
    62  		cli.BoolFlag{"remote, r", "download all pakcages to gopm local repository"},
    63  		cli.BoolFlag{"verbose, v", "show process details"},
    64  		cli.BoolFlag{"local,l", "download all packages to local gopath"},
    65  	},
    66  }
    67  
    68  func init() {
    69  	downloadCache = make(map[string]bool)
    70  }
    71  
    72  func runGet(ctx *cli.Context) {
    73  	setup(ctx)
    74  	// Check conflicts.
    75  	if ctx.Bool("gopath") && ctx.Bool("remote") ||
    76  		ctx.Bool("local") && ctx.Bool("remote") ||
    77  		ctx.Bool("gopath") && ctx.Bool("local") {
    78  		e := " "
    79  		if ctx.Bool("gopath") {
    80  			e += "--gopth,-g "
    81  		}
    82  		if ctx.Bool("remote") {
    83  			e += "--remote,-r "
    84  		}
    85  		if ctx.Bool("local") {
    86  			e += "--local,-l "
    87  		}
    88  		log.Error("get", "Command options have conflicts")
    89  		log.Error("", "Following options are not supposed to use at same time:")
    90  		log.Error("", "\t"+e)
    91  		log.Help("Try 'gopm help get' to get more information")
    92  	}
    93  
    94  	if !ctx.Bool("remote") {
    95  		// Get GOPATH.
    96  		installGopath = com.GetGOPATHs()[0]
    97  		if com.IsDir(installGopath) {
    98  			isHasGopath = true
    99  			log.Log("Indicated GOPATH: %s", installGopath)
   100  			installGopath += "/src"
   101  		} else {
   102  			if ctx.Bool("gopath") {
   103  				log.Error("get", "Invalid GOPATH path")
   104  				log.Error("", "GOPATH does not exist or is not a directory:")
   105  				log.Error("", "\t"+installGopath)
   106  				log.Help("Try 'go help gopath' to get more information")
   107  			} else {
   108  				// It's OK that no GOPATH setting
   109  				// when user does not specify to use.
   110  				log.Warn("No GOPATH setting available")
   111  			}
   112  		}
   113  		// if flag local set use localPath as GOPATH
   114  		if ctx.Bool("local") {
   115  			if !com.IsExist(".gopmfile") {
   116  				runGen(ctx)
   117  			}
   118  			gf, err := goconfig.LoadConfigFile(".gopmfile")
   119  			if err != nil {
   120  				log.Fatal("get", err.Error())
   121  			} else {
   122  				installGopath = gf.MustValue("project", "localPath")
   123  				if installGopath == "" {
   124  					os.Remove(".gopmfile")
   125  					log.Fatal("get", "unexpected localPath or no localPath exists")
   126  				}
   127  				installGopath += "/src"
   128  			}
   129  		}
   130  	}
   131  
   132  	// The gopm local repository.
   133  	installRepoPath = path.Join(doc.HomeDir, "repos")
   134  	log.Log("Local repository path: %s", installRepoPath)
   135  
   136  	// Check number of arguments to decide which function to call.
   137  	switch len(ctx.Args()) {
   138  	case 0:
   139  		getByGopmfile(ctx)
   140  	case 1:
   141  		getByPath(ctx)
   142  	default:
   143  		log.Error("get", "too many arguments")
   144  		log.Help("Try 'gopm help get' to get more information")
   145  	}
   146  }
   147  
   148  func getByGopmfile(ctx *cli.Context) {
   149  	// Check if gopmfile exists and generate one if not.
   150  	if !com.IsFile(".gopmfile") {
   151  		runGen(ctx)
   152  	}
   153  	gf := doc.NewGopmfile(".")
   154  
   155  	targetPath := parseTarget(gf.MustValue("target", "path"))
   156  	// Get dependencies.
   157  	imports := doc.GetAllImports([]string{workDir}, targetPath, ctx.Bool("example"), false)
   158  
   159  	nodes := make([]*doc.Node, 0, len(imports))
   160  	for _, p := range imports {
   161  		// TODO: DOING TEST CASES!!!
   162  		p = doc.GetProjectPath(p)
   163  		// Skip subpackage(s) of current project.
   164  		if isSubpackage(p, targetPath) {
   165  			continue
   166  		}
   167  		node := doc.NewNode(p, p, doc.BRANCH, "", true)
   168  
   169  		// Check if user specified the version.
   170  		if v, err := gf.GetValue("deps", p); err == nil && len(v) > 0 {
   171  			node.Type, node.Value = validPath(v)
   172  		}
   173  		nodes = append(nodes, node)
   174  	}
   175  
   176  	downloadPackages(ctx, nodes)
   177  	//save vcs infromation in the .gopm/data
   178  	doc.SaveLocalNodes()
   179  
   180  	log.Log("%d package(s) downloaded, %d failed", downloadCount, failConut)
   181  }
   182  
   183  func getByPath(ctx *cli.Context) {
   184  	nodes := make([]*doc.Node, 0, len(ctx.Args()))
   185  	for _, info := range ctx.Args() {
   186  		pkgPath := info
   187  		node := doc.NewNode(pkgPath, pkgPath, doc.BRANCH, "", true)
   188  
   189  		if i := strings.Index(info, "@"); i > -1 {
   190  			pkgPath = info[:i]
   191  			tp, ver := validPath(info[i+1:])
   192  			node = doc.NewNode(pkgPath, pkgPath, tp, ver, true)
   193  		}
   194  
   195  		// Check package name.
   196  		if !strings.Contains(pkgPath, "/") {
   197  			pkgPath = doc.GetPkgFullPath(pkgPath)
   198  		}
   199  
   200  		node.ImportPath = pkgPath
   201  		node.DownloadURL = pkgPath
   202  		nodes = append(nodes, node)
   203  	}
   204  
   205  	downloadPackages(ctx, nodes)
   206  	doc.SaveLocalNodes()
   207  
   208  	log.Log("%d package(s) downloaded, %d failed", downloadCount, failConut)
   209  }
   210  
   211  func copyToGopath(srcPath, destPath string) {
   212  	importPath := strings.TrimPrefix(destPath, installGopath+"/")
   213  	if len(getVcsName(destPath)) > 0 {
   214  		log.Warn("Package in GOPATH has version control: %s", importPath)
   215  		return
   216  	}
   217  
   218  	os.RemoveAll(destPath)
   219  	err := com.CopyDir(srcPath, destPath)
   220  	if err != nil {
   221  		log.Error("download", "Fail to copy to GOPATH:")
   222  		log.Fatal("", "\t"+err.Error())
   223  	}
   224  
   225  	log.Log("Package copied to GOPATH: %s", importPath)
   226  }
   227  
   228  // downloadPackages downloads packages with certain commit,
   229  // if the commit is empty string, then it downloads all dependencies,
   230  // otherwise, it only downloada package with specific commit only.
   231  func downloadPackages(ctx *cli.Context, nodes []*doc.Node) {
   232  	// Check all packages, they may be raw packages path.
   233  	for _, n := range nodes {
   234  		// Check if local reference
   235  		if n.Type == doc.LOCAL {
   236  			continue
   237  		}
   238  		// Check if it is a valid remote path or C.
   239  		if n.ImportPath == "C" {
   240  			continue
   241  		} else if !doc.IsValidRemotePath(n.ImportPath) {
   242  			// Invalid import path.
   243  			log.Error("download", "Skipped invalid package: "+fmt.Sprintf("%s@%s:%s",
   244  				n.ImportPath, n.Type, doc.CheckNodeValue(n.Value)))
   245  			failConut++
   246  			continue
   247  		}
   248  
   249  		// Valid import path.
   250  		gopathDir := path.Join(installGopath, n.ImportPath)
   251  		//RootPath is projectpath with certainn number set as VCS TYPE
   252  		n.RootPath = doc.GetProjectPath(n.ImportPath)
   253  		// installPath is the local  gopm repository path with certain VCS value
   254  		installPath := path.Join(installRepoPath, n.RootPath) + versionSuffix(n.Value)
   255  
   256  		if isSubpackage(n.RootPath, ".") {
   257  			continue
   258  		}
   259  
   260  		// Indicates whether need to download package again.
   261  		if n.IsFixed() && com.IsExist(installPath) {
   262  			n.IsGetDepsOnly = true
   263  		}
   264  
   265  		if !ctx.Bool("update") {
   266  			// Check if package has been downloaded.
   267  			if (len(n.Value) == 0 && !ctx.Bool("remote") && com.IsExist(gopathDir)) ||
   268  				com.IsExist(installPath) {
   269  				log.Trace("Skipped installed package: %s@%s:%s",
   270  					n.ImportPath, n.Type, doc.CheckNodeValue(n.Value))
   271  
   272  				// Only copy when no version control.
   273  				// if local set copy to local gopath
   274  				if (ctx.Bool("gopath") || ctx.Bool("local")) && (com.IsExist(installPath) ||
   275  					len(getVcsName(gopathDir)) == 0) {
   276  					copyToGopath(installPath, gopathDir)
   277  				}
   278  				continue
   279  			} else {
   280  				doc.LocalNodes.SetValue(n.RootPath, "value", "")
   281  			}
   282  		}
   283  
   284  		if downloadCache[n.RootPath] {
   285  			log.Trace("Skipped downloaded package: %s@%s:%s",
   286  				n.ImportPath, n.Type, doc.CheckNodeValue(n.Value))
   287  			continue
   288  		}
   289  
   290  		// Download package.
   291  		nod, imports := downloadPackage(ctx, n)
   292  		if len(imports) > 0 {
   293  			var gf *goconfig.ConfigFile
   294  
   295  			// Check if has gopmfile.
   296  			if com.IsFile(installPath + "/" + doc.GOPM_FILE_NAME) {
   297  				log.Log("Found gopmfile: %s@%s:%s",
   298  					n.ImportPath, n.Type, doc.CheckNodeValue(n.Value))
   299  				gf = doc.NewGopmfile(installPath)
   300  			}
   301  
   302  			// Need to download dependencies.
   303  			// Generate temporary nodes.
   304  			nodes := make([]*doc.Node, len(imports))
   305  			for i := range nodes {
   306  				nodes[i] = doc.NewNode(imports[i], imports[i], doc.BRANCH, "", true)
   307  
   308  				if gf == nil {
   309  					continue
   310  				}
   311  
   312  				// Check if user specified the version.
   313  				if v, err := gf.GetValue("deps", imports[i]); err == nil && len(v) > 0 {
   314  					nodes[i].Type, nodes[i].Value = validPath(v)
   315  				}
   316  			}
   317  			downloadPackages(ctx, nodes)
   318  		}
   319  
   320  		// Only save package information with specific commit.
   321  		if nod == nil {
   322  			continue
   323  		}
   324  
   325  		// Save record in local nodes.
   326  		log.Success("SUCC", "GET", fmt.Sprintf("%s@%s:%s",
   327  			n.ImportPath, n.Type, doc.CheckNodeValue(n.Value)))
   328  		downloadCount++
   329  
   330  		// Only save non-commit node.
   331  		if len(nod.Value) == 0 && len(nod.Revision) > 0 {
   332  			doc.LocalNodes.SetValue(nod.RootPath, "value", nod.Revision)
   333  		}
   334  
   335  		//if update set downloadPackage will use VSC tools to download the package
   336  		//else just use puredownload to gopm repos and copy to gopath
   337  		if (ctx.Bool("gopath") || ctx.Bool("local")) && com.IsExist(installPath) && !ctx.Bool("update") &&
   338  			len(getVcsName(path.Join(installGopath, nod.RootPath))) == 0 {
   339  			copyToGopath(installPath, gopathDir)
   340  		}
   341  	}
   342  }
   343  
   344  // downloadPackage downloads package either use version control tools or not.
   345  func downloadPackage(ctx *cli.Context, nod *doc.Node) (*doc.Node, []string) {
   346  	log.Message("Downloading", fmt.Sprintf("package: %s@%s:%s",
   347  		nod.ImportPath, nod.Type, doc.CheckNodeValue(nod.Value)))
   348  	// Mark as donwloaded.
   349  	downloadCache[nod.RootPath] = true
   350  
   351  	// Check if only need to use VCS tools.
   352  	var imports []string
   353  	var err error
   354  	gopathDir := path.Join(installGopath, nod.RootPath)
   355  	vcs := getVcsName(gopathDir)
   356  	//if update set and gopath set and VCS tools set,
   357  	//use VCS tools  to download the package
   358  	if ctx.Bool("update") && (ctx.Bool("gopath") || ctx.Bool("local")) && len(vcs) > 0 {
   359  		err = updateByVcs(vcs, gopathDir)
   360  		imports = doc.GetAllImports([]string{gopathDir}, nod.RootPath, false, false)
   361  	} else {
   362  		// If package has revision and exist, then just check dependencies.
   363  		if nod.IsGetDepsOnly {
   364  			return nod, doc.GetAllImports([]string{path.Join(installRepoPath, nod.RootPath) + versionSuffix(nod.Value)},
   365  				nod.RootPath, ctx.Bool("example"), false)
   366  		}
   367  		nod.Revision = doc.LocalNodes.MustValue(nod.RootPath, "value")
   368  		imports, err = doc.PureDownload(nod, installRepoPath, ctx) //CmdGet.Flags)
   369  	}
   370  
   371  	if err != nil {
   372  		log.Error("get", "Fail to download pakage: "+nod.ImportPath)
   373  		log.Error("", "\t"+err.Error())
   374  		failConut++
   375  		os.RemoveAll(installRepoPath + "/" + nod.RootPath)
   376  		return nil, nil
   377  	}
   378  	return nod, imports
   379  }
   380  
   381  //check whether dirPath has .git .hg .svn else return ""
   382  func getVcsName(dirPath string) string {
   383  	switch {
   384  	case com.IsExist(path.Join(dirPath, ".git")):
   385  		return "git"
   386  	case com.IsExist(path.Join(dirPath, ".hg")):
   387  		return "hg"
   388  	case com.IsExist(path.Join(dirPath, ".svn")):
   389  		return "svn"
   390  	}
   391  	return ""
   392  }
   393  
   394  //if vcs has been detected ,  use corresponding command to update dirPath
   395  func updateByVcs(vcs, dirPath string) error {
   396  	err := os.Chdir(dirPath)
   397  	if err != nil {
   398  		log.Error("Update by VCS", "Fail to change work directory:")
   399  		log.Fatal("", "\t"+err.Error())
   400  	}
   401  	defer os.Chdir(workDir)
   402  
   403  	switch vcs {
   404  	case "git":
   405  		branch, _, err := com.ExecCmd("git", "rev-parse", "--abbrev-ref", "HEAD")
   406  		if err != nil {
   407  			log.Error("", "Error occurs when 'git rev-parse --abbrev-ref HEAD'")
   408  			log.Error("", "\t"+err.Error())
   409  		}
   410  
   411  		_, _, err = com.ExecCmd("git", "pull", "origin", branch)
   412  		if err != nil {
   413  			log.Error("", "Error occurs when 'git pull origin "+branch+"'")
   414  			log.Error("", "\t"+err.Error())
   415  		}
   416  	case "hg":
   417  		_, stderr, err := com.ExecCmd("hg", "pull")
   418  		if err != nil {
   419  			log.Error("", "Error occurs when 'hg pull'")
   420  			log.Error("", "\t"+err.Error())
   421  		}
   422  		if len(stderr) > 0 {
   423  			log.Error("", "Error: "+stderr)
   424  		}
   425  
   426  		_, stderr, err = com.ExecCmd("hg", "up")
   427  		if err != nil {
   428  			log.Error("", "Error occurs when 'hg up'")
   429  			log.Error("", "\t"+err.Error())
   430  		}
   431  		if len(stderr) > 0 {
   432  			log.Error("", "Error: "+stderr)
   433  		}
   434  	case "svn":
   435  		_, stderr, err := com.ExecCmd("svn", "update")
   436  		if err != nil {
   437  			log.Error("", "Error occurs when 'svn update'")
   438  			log.Error("", "\t"+err.Error())
   439  		}
   440  		if len(stderr) > 0 {
   441  			log.Error("", "Error: "+stderr)
   442  		}
   443  	}
   444  	return nil
   445  }