You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

282 lines
7.0 KiB

// Copyright (c) 2013 GPMGo Members. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package main
import (
12 years ago
"encoding/json"
"fmt"
"net/http"
12 years ago
"os"
"os/exec"
"regexp"
"strings"
"github.com/GPMGo/gpm/doc"
"github.com/GPMGo/gpm/utils"
)
var (
isHasGit, isHasHg bool
12 years ago
downloadCache map[string]bool // Saves packages that have downloaded.
installGOPATH string // The GOPATH that packages are downloaded to.
)
var cmdInstall = &Command{
UsageLine: "install [install flags] <packages|bundles|snapshots>",
}
func init() {
12 years ago
downloadCache = make(map[string]bool)
cmdInstall.Run = runInstall
cmdInstall.Flags = map[string]bool{
"-p": false,
"-d": false,
"-u": false, // Flag for 'go get'.
"-e": false,
12 years ago
"-s": false,
}
}
// 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")
case "-e":
12 years ago
fmt.Printf("You enabled download dependencies in example.\n")
12 years ago
case "-s":
fmt.Printf("You enabled download from sources.\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 {
// 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
}
args = args[num:]
// Check length of arguments.
if len(args) < 1 {
fmt.Printf("Please list at least one package/bundle/snapshot.\n")
return
}
// Check version control tools.
checkVCSTool()
installGOPATH = utils.GetBestMatchGOPATH(appPath)
fmt.Printf("Packages will be downloaded to GOPATH(%s).\n", installGOPATH)
// Download packages.
commits := make([]string, len(args))
downloadPackages(args, commits)
if !cmdInstall.Flags["-d"] && cmdInstall.Flags["-p"] {
// Install packages all together.
var cmdArgs []string
cmdArgs = append(cmdArgs, "install")
cmdArgs = append(cmdArgs, "<blank>")
for k := range downloadCache {
fmt.Printf("Installing package: %s.\n", k)
cmdArgs[1] = k
executeGoCommand(cmdArgs)
}
12 years ago
// Save local nodes to file.
fw, err := os.Create(appPath + "data/nodes.json")
if err != nil {
fmt.Println(err)
return
}
defer fw.Close()
fbytes, _ := json.MarshalIndent(&localNodes, "", "\t")
fw.Write(fbytes)
}
fmt.Println("Well done.")
}
// 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):
if !downloadCache[p] {
// Download package.
12 years ago
node, imports := downloadPackage(p, commits[i])
if len(imports) > 0 {
// Need to download dependencies.
tags := make([]string, len(imports))
downloadPackages(imports, tags)
}
// Only save package information with specific commit.
12 years ago
if node != nil {
// Save record in local nodes.
saveNode(node)
//fmt.Printf("Saved information: %s:%s.\n", pkg.ImportPath, pkg.Commit)
}
} else {
fmt.Printf("Skipped downloaded package: %s.\n", p)
}
default:
// Invalid import path.
fmt.Printf("Skipped invalid import path: %s.\n", p)
}
}
}
12 years ago
// saveNode saves node into local nodes.
func saveNode(n *doc.Node) {
// Check if this node exists.
for _, v := range localNodes {
if n.ImportPath == v.ImportPath {
v = n
return
}
}
// Add new node.
localNodes = append(localNodes, n)
}
// downloadPackage download package either use version control tools or not.
12 years ago
func downloadPackage(path, commit string) (node *doc.Node, 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)
executeGoCommand(args)
return nil, nil
default: // Pure download.
if !cmdInstall.Flags["-p"] {
cmdInstall.Flags["-p"] = true
fmt.Printf("No version control tool is available, pure download enabled!\n")
}
fmt.Printf("Downloading package: %s.\n", path)
// Mark as donwloaded.
downloadCache[path] = true
var err error
12 years ago
node, imports, err = pureDownload(path, commit)
if err != nil {
fmt.Printf("Fail to download package(%s) with error: %s.\n", path, err)
return nil, nil
}
12 years ago
return node, imports
}
}
func checkGoGetFlags() (args []string) {
args = append(args, "get")
switch {
case cmdInstall.Flags["-d"]:
args = append(args, "-d")
fallthrough
case cmdInstall.Flags["-u"]:
args = append(args, "-u")
}
return args
}
// service represents a source code control service.
type service struct {
pattern *regexp.Regexp
prefix string
12 years ago
get func(*http.Client, map[string]string, string, string, map[string]bool) (*doc.Node, []string, error)
}
// services is the list of source code control services handled by gopkgdoc.
var services = []*service{
{doc.GithubPattern, "github.com/", doc.GetGithubDoc},
{doc.GooglePattern, "code.google.com/", doc.GetGoogleDoc},
{doc.BitbucketPattern, "bitbucket.org/", doc.GetBitbucketDoc},
{doc.LaunchpadPattern, "launchpad.net/", doc.GetLaunchpadDoc},
}
// pureDownload downloads package without version control.
12 years ago
func pureDownload(path, commit string) (*doc.Node, []string, error) {
for _, s := range services {
if s.get == nil || !strings.HasPrefix(path, s.prefix) {
continue
}
m := s.pattern.FindStringSubmatch(path)
if m == nil {
if s.prefix != "" {
return nil, nil,
doc.NotFoundError{"Import path prefix matches known service, but regexp does not."}
}
continue
}
match := map[string]string{"importPath": path}
for i, n := range s.pattern.SubexpNames() {
if n != "" {
match[n] = m[i]
}
}
return s.get(doc.HttpClient, match, installGOPATH, commit, cmdInstall.Flags)
}
return nil, nil, doc.ErrNoMatch
}