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 }