mirror of
https://github.com/golang/go
synced 2024-11-24 12:10:03 -07:00
cmd/goinstall: remove now that 'go get' works
The changes to builder were not tested. R=golang-dev, rsc CC=golang-dev https://golang.org/cl/5572083
This commit is contained in:
parent
d91ade02e7
commit
1e5b7e706e
@ -40,7 +40,6 @@ CLEANDIRS=\
|
||||
godoc\
|
||||
fix\
|
||||
gofmt\
|
||||
goinstall\
|
||||
gotest\
|
||||
vet\
|
||||
yacc\
|
||||
|
@ -1,19 +0,0 @@
|
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
include ../../Make.inc
|
||||
|
||||
TARG=goinstall
|
||||
GOFILES=\
|
||||
download.go\
|
||||
main.go\
|
||||
make.go\
|
||||
|
||||
include ../../Make.cmd
|
||||
|
||||
test:
|
||||
gotest
|
||||
|
||||
testshort:
|
||||
gotest -test.short
|
@ -1,200 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Goinstall is an experiment in automatic package installation.
|
||||
It installs packages, possibly downloading them from the internet.
|
||||
It maintains a list of public Go packages at
|
||||
http://godashboard.appspot.com/package.
|
||||
|
||||
Usage:
|
||||
goinstall [flags] importpath...
|
||||
goinstall [flags] -a
|
||||
|
||||
Flags and default settings:
|
||||
-a=false install all previously installed packages
|
||||
-clean=false clean the package directory before installing
|
||||
-dashboard=true tally public packages on godashboard.appspot.com
|
||||
-install=true build and install the package and its dependencies
|
||||
-nuke=false remove the target object and clean before installing
|
||||
-u=false update already-downloaded packages
|
||||
-v=false verbose operation
|
||||
|
||||
Goinstall installs each of the packages identified on the command line. It
|
||||
installs a package's prerequisites before trying to install the package
|
||||
itself. Unless -log=false is specified, goinstall logs the import path of each
|
||||
installed package to $GOROOT/goinstall.log for use by goinstall -a.
|
||||
|
||||
If the -a flag is given, goinstall reinstalls all previously installed
|
||||
packages, reading the list from $GOROOT/goinstall.log. After updating to a
|
||||
new Go release, which deletes all package binaries, running
|
||||
|
||||
goinstall -a
|
||||
|
||||
will recompile and reinstall goinstalled packages.
|
||||
|
||||
Another common idiom is to use
|
||||
|
||||
goinstall -a -u
|
||||
|
||||
to update, recompile, and reinstall all goinstalled packages.
|
||||
|
||||
The source code for a package with import path foo/bar is expected
|
||||
to be in the directory $GOROOT/src/pkg/foo/bar/ or $GOPATH/src/foo/bar/.
|
||||
See "The GOPATH Environment Variable" for more about GOPATH.
|
||||
|
||||
By default, goinstall prints output only when it encounters an error.
|
||||
The -v flag causes goinstall to print information about packages
|
||||
being considered and installed.
|
||||
|
||||
Goinstall ignores Makefiles.
|
||||
|
||||
|
||||
Remote Repositories
|
||||
|
||||
If a package import path refers to a remote repository, goinstall will
|
||||
download the code if necessary.
|
||||
|
||||
Goinstall recognizes packages from a few common code hosting sites:
|
||||
|
||||
BitBucket (Git, Mercurial)
|
||||
|
||||
import "bitbucket.org/user/project"
|
||||
import "bitbucket.org/user/project/sub/directory"
|
||||
|
||||
GitHub (Git)
|
||||
|
||||
import "github.com/user/project"
|
||||
import "github.com/user/project/sub/directory"
|
||||
|
||||
Google Code Project Hosting (Git, Mercurial, Subversion)
|
||||
|
||||
import "project.googlecode.com/git"
|
||||
import "project.googlecode.com/git/sub/directory"
|
||||
|
||||
import "project.googlecode.com/hg"
|
||||
import "project.googlecode.com/hg/sub/directory"
|
||||
|
||||
import "project.googlecode.com/svn/trunk"
|
||||
import "project.googlecode.com/svn/trunk/sub/directory"
|
||||
|
||||
Google Code Project Hosting sub-repositories:
|
||||
|
||||
import "code.google.com/p/project.subrepo/sub/directory
|
||||
|
||||
Launchpad (Bazaar)
|
||||
|
||||
import "launchpad.net/project"
|
||||
import "launchpad.net/project/series"
|
||||
import "launchpad.net/project/series/sub/directory"
|
||||
|
||||
import "launchpad.net/~user/project/branch"
|
||||
import "launchpad.net/~user/project/branch/sub/directory"
|
||||
|
||||
If the destination directory (e.g., $GOROOT/src/pkg/bitbucket.org/user/project)
|
||||
already exists and contains an appropriate checkout, goinstall will not
|
||||
attempt to fetch updates. The -u flag changes this behavior,
|
||||
causing goinstall to update all remote packages encountered during
|
||||
the installation.
|
||||
|
||||
When downloading or updating, goinstall looks for a tag with the "go." prefix
|
||||
that corresponds to the local Go version. For Go "release.r58" it looks for a
|
||||
tag named "go.r58". For "weekly.2011-06-03" it looks for "go.weekly.2011-06-03".
|
||||
If the specific "go.X" tag is not found, it chooses the closest earlier version.
|
||||
If an appropriate tag is found, goinstall uses that version of the code.
|
||||
Otherwise it uses the default version selected by the version control
|
||||
system, typically HEAD for git, tip for Mercurial.
|
||||
|
||||
After a successful download and installation of one of these import paths,
|
||||
goinstall reports the installation to godashboard.appspot.com, which
|
||||
increments a count associated with the package and the time of its most
|
||||
recent installation. This mechanism powers the package list at
|
||||
http://godashboard.appspot.com/package, allowing Go programmers to learn about
|
||||
popular packages that might be worth looking at.
|
||||
The -dashboard=false flag disables this reporting.
|
||||
|
||||
For code hosted on other servers, goinstall recognizes the general form
|
||||
|
||||
repository.vcs/path
|
||||
|
||||
as denoting the given repository, with or without the .vcs suffix, using
|
||||
the named version control system, and then the path inside that repository.
|
||||
The supported version control systems are:
|
||||
|
||||
Bazaar .bzr
|
||||
Git .git
|
||||
Mercurial .hg
|
||||
Subversion .svn
|
||||
|
||||
For example,
|
||||
|
||||
import "example.org/user/foo.hg"
|
||||
|
||||
denotes the root directory of the Mercurial repository at example.org/user/foo
|
||||
or foo.hg, and
|
||||
|
||||
import "example.org/repo.git/foo/bar"
|
||||
|
||||
denotes the foo/bar directory of the Git repository at example.com/repo or
|
||||
repo.git.
|
||||
|
||||
When a version control system supports multiple protocols, goinstall tries each
|
||||
in turn.
|
||||
For example, for Git it tries git://, then https://, then http://.
|
||||
|
||||
|
||||
The GOPATH Environment Variable
|
||||
|
||||
GOPATH may be set to a colon-separated list of paths inside which Go code,
|
||||
package objects, and executables may be found.
|
||||
|
||||
Set a GOPATH to use goinstall to build and install your own code and
|
||||
external libraries outside of the Go tree (and to avoid writing Makefiles).
|
||||
|
||||
The top-level directory structure of a GOPATH is prescribed:
|
||||
|
||||
The 'src' directory is for source code. The directory naming inside 'src'
|
||||
determines the package import path or executable name.
|
||||
|
||||
The 'pkg' directory is for package objects. Like the Go tree, package objects
|
||||
are stored inside a directory named after the target operating system and
|
||||
processor architecture ('pkg/$GOOS_$GOARCH').
|
||||
A package whose source is located at '$GOPATH/src/foo/bar' would be imported
|
||||
as 'foo/bar' and installed as '$GOPATH/pkg/$GOOS_$GOARCH/foo/bar.a'.
|
||||
|
||||
The 'bin' directory is for executable files.
|
||||
Goinstall installs program binaries using the name of the source folder.
|
||||
A binary whose source is at 'src/foo/qux' would be built and installed to
|
||||
'$GOPATH/bin/qux'. (Note 'bin/qux', not 'bin/foo/qux' - this is such that
|
||||
you can put the bin directory in your PATH.)
|
||||
|
||||
Here's an example directory layout:
|
||||
|
||||
GOPATH=/home/user/gocode
|
||||
|
||||
/home/user/gocode/
|
||||
src/foo/
|
||||
bar/ (go code in package bar)
|
||||
qux/ (go code in package main)
|
||||
bin/qux (executable file)
|
||||
pkg/linux_amd64/foo/bar.a (object file)
|
||||
|
||||
Run 'goinstall foo/bar' to build and install the package 'foo/bar'
|
||||
(and its dependencies).
|
||||
Goinstall will search each GOPATH (in order) for 'src/foo/bar'.
|
||||
If the directory cannot be found, goinstall will attempt to fetch the
|
||||
source from a remote repository and write it to the 'src' directory of the
|
||||
first GOPATH (or $GOROOT/src/pkg if GOPATH is not set).
|
||||
|
||||
Goinstall recognizes relative and absolute paths (paths beginning with / or .).
|
||||
The following commands would build our example packages:
|
||||
|
||||
goinstall /home/user/gocode/src/foo/bar # build and install foo/bar
|
||||
cd /home/user/gocode/src/foo
|
||||
goinstall ./bar # build and install foo/bar (again)
|
||||
cd qux
|
||||
goinstall . # build and install foo/qux
|
||||
|
||||
*/
|
||||
package documentation
|
@ -1,537 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Download remote packages.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const dashboardURL = "http://godashboard.appspot.com/package"
|
||||
|
||||
// maybeReportToDashboard reports path to dashboard unless
|
||||
// -dashboard=false is on command line. It ignores errors.
|
||||
func maybeReportToDashboard(path string) {
|
||||
// if -dashboard=false was on command line, do nothing
|
||||
if !*reportToDashboard {
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise lob url to dashboard
|
||||
r, _ := http.Post(dashboardURL, "application/x-www-form-urlencoded", strings.NewReader("path="+path))
|
||||
if r != nil && r.Body != nil {
|
||||
r.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// a vcs represents a version control system
|
||||
// like Mercurial, Git, or Subversion.
|
||||
type vcs struct {
|
||||
name string
|
||||
cmd string
|
||||
metadir string
|
||||
checkout string
|
||||
clone string
|
||||
update string
|
||||
updateRevFlag string
|
||||
pull string
|
||||
pullForceFlag string
|
||||
tagList string
|
||||
tagListRe *regexp.Regexp
|
||||
check string
|
||||
protocols []string
|
||||
suffix string
|
||||
}
|
||||
|
||||
func (v *vcs) String() string {
|
||||
return v.name
|
||||
}
|
||||
|
||||
var vcsMap = map[string]*vcs{
|
||||
"hg": {
|
||||
name: "Mercurial",
|
||||
cmd: "hg",
|
||||
metadir: ".hg",
|
||||
checkout: "checkout",
|
||||
clone: "clone",
|
||||
update: "update",
|
||||
pull: "pull",
|
||||
tagList: "tags",
|
||||
tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"),
|
||||
check: "identify",
|
||||
protocols: []string{"https", "http"},
|
||||
suffix: ".hg",
|
||||
},
|
||||
|
||||
"git": {
|
||||
name: "Git",
|
||||
cmd: "git",
|
||||
metadir: ".git",
|
||||
checkout: "checkout",
|
||||
clone: "clone",
|
||||
update: "pull",
|
||||
pull: "fetch",
|
||||
tagList: "tag",
|
||||
tagListRe: regexp.MustCompile("([^\n]+)\n"),
|
||||
check: "ls-remote",
|
||||
protocols: []string{"git", "https", "http"},
|
||||
suffix: ".git",
|
||||
},
|
||||
|
||||
"svn": {
|
||||
name: "Subversion",
|
||||
cmd: "svn",
|
||||
metadir: ".svn",
|
||||
checkout: "checkout",
|
||||
clone: "checkout",
|
||||
update: "update",
|
||||
check: "info",
|
||||
protocols: []string{"https", "http", "svn"},
|
||||
suffix: ".svn",
|
||||
},
|
||||
|
||||
"bzr": {
|
||||
name: "Bazaar",
|
||||
cmd: "bzr",
|
||||
metadir: ".bzr",
|
||||
checkout: "update",
|
||||
clone: "branch",
|
||||
update: "update",
|
||||
updateRevFlag: "-r",
|
||||
pull: "pull",
|
||||
pullForceFlag: "--overwrite",
|
||||
tagList: "tags",
|
||||
tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"),
|
||||
check: "info",
|
||||
protocols: []string{"https", "http", "bzr"},
|
||||
suffix: ".bzr",
|
||||
},
|
||||
}
|
||||
|
||||
type RemoteRepo interface {
|
||||
// IsCheckedOut returns whether this repository is checked
|
||||
// out inside the given srcDir (eg, $GOPATH/src).
|
||||
IsCheckedOut(srcDir string) bool
|
||||
|
||||
// Repo returns the information about this repository: its url,
|
||||
// the part of the import path that forms the repository root,
|
||||
// and the version control system it uses. It may discover this
|
||||
// information by using the supplied client to make HTTP requests.
|
||||
Repo(*http.Client) (url, root string, vcs *vcs, err error)
|
||||
}
|
||||
|
||||
type host struct {
|
||||
pattern *regexp.Regexp
|
||||
repo func(repo string) (RemoteRepo, error)
|
||||
}
|
||||
|
||||
var knownHosts = []host{
|
||||
{
|
||||
regexp.MustCompile(`^code\.google\.com/p/([a-z0-9\-]+(\.[a-z0-9\-]+)?)(/[a-z0-9A-Z_.\-/]+)?$`),
|
||||
matchGoogleRepo,
|
||||
},
|
||||
{
|
||||
regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]+)?$`),
|
||||
matchGithubRepo,
|
||||
},
|
||||
{
|
||||
regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]+)?$`),
|
||||
matchBitbucketRepo,
|
||||
},
|
||||
{
|
||||
regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`),
|
||||
matchLaunchpadRepo,
|
||||
},
|
||||
}
|
||||
|
||||
// baseRepo is the base implementation of RemoteRepo.
|
||||
type baseRepo struct {
|
||||
url, root string
|
||||
vcs *vcs
|
||||
}
|
||||
|
||||
func (r *baseRepo) Repo(*http.Client) (url, root string, vcs *vcs, err error) {
|
||||
return r.url, r.root, r.vcs, nil
|
||||
}
|
||||
|
||||
// IsCheckedOut reports whether the repo root inside srcDir contains a
|
||||
// repository metadir. It updates the baseRepo's vcs field if necessary.
|
||||
func (r *baseRepo) IsCheckedOut(srcDir string) bool {
|
||||
pkgPath := filepath.Join(srcDir, r.root)
|
||||
if r.vcs == nil {
|
||||
for _, vcs := range vcsMap {
|
||||
if isDir(filepath.Join(pkgPath, vcs.metadir)) {
|
||||
r.vcs = vcs
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return isDir(filepath.Join(pkgPath, r.vcs.metadir))
|
||||
}
|
||||
|
||||
// matchGithubRepo handles matches for github.com repositories.
|
||||
func matchGithubRepo(root string) (RemoteRepo, error) {
|
||||
if strings.HasSuffix(root, ".git") {
|
||||
return nil, errors.New("path must not include .git suffix")
|
||||
}
|
||||
return &baseRepo{"http://" + root + ".git", root, vcsMap["git"]}, nil
|
||||
}
|
||||
|
||||
// matchLaunchpadRepo handles matches for launchpad.net repositories.
|
||||
func matchLaunchpadRepo(root string) (RemoteRepo, error) {
|
||||
return &baseRepo{"https://" + root, root, vcsMap["bzr"]}, nil
|
||||
}
|
||||
|
||||
// matchGoogleRepo matches repos like "code.google.com/p/repo.subrepo/path".
|
||||
func matchGoogleRepo(id string) (RemoteRepo, error) {
|
||||
root := "code.google.com/p/" + id
|
||||
return &googleRepo{baseRepo{"https://" + root, root, nil}}, nil
|
||||
}
|
||||
|
||||
// googleRepo implements a RemoteRepo that discovers a Google Code
|
||||
// repository's VCS type by scraping the code.google.com source checkout page.
|
||||
type googleRepo struct{ baseRepo }
|
||||
|
||||
var googleRepoRe = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)
|
||||
|
||||
func (r *googleRepo) Repo(client *http.Client) (url, root string, vcs *vcs, err error) {
|
||||
if r.vcs != nil {
|
||||
return r.url, r.root, r.vcs, nil
|
||||
}
|
||||
|
||||
// Use the code.google.com source checkout page to find the VCS type.
|
||||
const prefix = "code.google.com/p/"
|
||||
p := strings.SplitN(r.root[len(prefix):], ".", 2)
|
||||
u := fmt.Sprintf("https://%s%s/source/checkout", prefix, p[0])
|
||||
if len(p) == 2 {
|
||||
u += fmt.Sprintf("?repo=%s", p[1])
|
||||
}
|
||||
resp, err := client.Get(u)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return "", "", nil, fmt.Errorf("fetching %s: %v", u, resp.Status)
|
||||
}
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", "", nil, fmt.Errorf("fetching %s: %v", u, err)
|
||||
}
|
||||
|
||||
// Scrape result for vcs details.
|
||||
if m := googleRepoRe.FindSubmatch(b); len(m) == 2 {
|
||||
s := string(m[1])
|
||||
if v := vcsMap[s]; v != nil {
|
||||
if s == "svn" {
|
||||
// Subversion still uses the old-style URL.
|
||||
r.url = fmt.Sprintf("http://%s.googlecode.com/svn", p[0])
|
||||
}
|
||||
r.vcs = v
|
||||
return r.url, r.root, r.vcs, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", "", nil, errors.New("could not detect googlecode vcs")
|
||||
}
|
||||
|
||||
// matchBitbucketRepo handles matches for all bitbucket.org repositories.
|
||||
func matchBitbucketRepo(root string) (RemoteRepo, error) {
|
||||
if strings.HasSuffix(root, ".git") {
|
||||
return nil, errors.New("path must not include .git suffix")
|
||||
}
|
||||
return &bitbucketRepo{baseRepo{root: root}}, nil
|
||||
}
|
||||
|
||||
// bitbucketRepo implements a RemoteRepo that uses the BitBucket API to
|
||||
// discover the repository's VCS type.
|
||||
type bitbucketRepo struct{ baseRepo }
|
||||
|
||||
func (r *bitbucketRepo) Repo(client *http.Client) (url, root string, vcs *vcs, err error) {
|
||||
if r.vcs != nil && r.url != "" {
|
||||
return r.url, r.root, r.vcs, nil
|
||||
}
|
||||
|
||||
// Use the BitBucket API to find which kind of repository this is.
|
||||
const apiUrl = "https://api.bitbucket.org/1.0/repositories/"
|
||||
resp, err := client.Get(apiUrl + strings.SplitN(r.root, "/", 2)[1])
|
||||
if err != nil {
|
||||
return "", "", nil, fmt.Errorf("BitBucket API: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return "", "", nil, fmt.Errorf("BitBucket API: %v", resp.Status)
|
||||
}
|
||||
var response struct {
|
||||
Vcs string `json:"scm"`
|
||||
}
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
if err != nil {
|
||||
return "", "", nil, fmt.Errorf("BitBucket API: %v", err)
|
||||
}
|
||||
switch response.Vcs {
|
||||
case "git":
|
||||
r.url = "http://" + r.root + ".git"
|
||||
case "hg":
|
||||
r.url = "http://" + r.root
|
||||
default:
|
||||
return "", "", nil, errors.New("unsupported bitbucket vcs: " + response.Vcs)
|
||||
}
|
||||
if r.vcs = vcsMap[response.Vcs]; r.vcs == nil {
|
||||
panic("vcs is nil when it should not be")
|
||||
}
|
||||
return r.url, r.root, r.vcs, nil
|
||||
}
|
||||
|
||||
// findPublicRepo checks whether importPath is a well-formed path for one of
|
||||
// the supported code hosting sites and, if so, returns a RemoteRepo.
|
||||
func findPublicRepo(importPath string) (RemoteRepo, error) {
|
||||
for _, host := range knownHosts {
|
||||
if hm := host.pattern.FindStringSubmatch(importPath); hm != nil {
|
||||
return host.repo(hm[1])
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// findAnyRepo matches import paths with a repo suffix (.git, etc).
|
||||
func findAnyRepo(importPath string) RemoteRepo {
|
||||
for _, v := range vcsMap {
|
||||
i := strings.Index(importPath+"/", v.suffix+"/")
|
||||
if i < 0 {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(importPath[:i], "/") {
|
||||
continue // don't match vcs suffix in the host name
|
||||
}
|
||||
return &anyRepo{
|
||||
baseRepo{
|
||||
root: importPath[:i] + v.suffix,
|
||||
vcs: v,
|
||||
},
|
||||
importPath[:i],
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// anyRepo implements an discoverable remote repo with a suffix (.git, etc).
|
||||
type anyRepo struct {
|
||||
baseRepo
|
||||
rootWithoutSuffix string
|
||||
}
|
||||
|
||||
func (r *anyRepo) Repo(*http.Client) (url, root string, vcs *vcs, err error) {
|
||||
if r.url != "" {
|
||||
return r.url, r.root, r.vcs, nil
|
||||
}
|
||||
url, err = r.vcs.findURL(r.rootWithoutSuffix)
|
||||
if url == "" && err == nil {
|
||||
err = fmt.Errorf("couldn't find %s repository", r.vcs.name)
|
||||
}
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
r.url = url
|
||||
return r.url, r.root, r.vcs, nil
|
||||
}
|
||||
|
||||
// findURL finds the URL for a given repo root by trying each combination of
|
||||
// protocol and suffix in series.
|
||||
func (v *vcs) findURL(root string) (string, error) {
|
||||
for _, proto := range v.protocols {
|
||||
for _, suffix := range []string{"", v.suffix} {
|
||||
url := proto + "://" + root + suffix
|
||||
out, err := exec.Command(v.cmd, v.check, url).CombinedOutput()
|
||||
if err == nil {
|
||||
printf("find %s: found %s\n", root, url)
|
||||
return url, nil
|
||||
}
|
||||
printf("findURL(%s): %s %s %s: %v\n%s\n", root, v.cmd, v.check, url, err, out)
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
var oldGoogleRepo = regexp.MustCompile(`^([a-z0-9\-]+)\.googlecode\.com/(svn|git|hg)(/[a-z0-9A-Z_.\-/]+)?$`)
|
||||
|
||||
type errOldGoogleRepo struct {
|
||||
fixedPath string
|
||||
}
|
||||
|
||||
func (e *errOldGoogleRepo) Error() string {
|
||||
return fmt.Sprintf("unsupported import path; should be %q", e.fixedPath)
|
||||
}
|
||||
|
||||
// download checks out or updates the specified package from the remote server.
|
||||
func download(importPath, srcDir string) (public bool, err error) {
|
||||
if strings.Contains(importPath, "..") {
|
||||
err = errors.New("invalid path (contains ..)")
|
||||
return
|
||||
}
|
||||
|
||||
if m := oldGoogleRepo.FindStringSubmatch(importPath); m != nil {
|
||||
fixedPath := "code.google.com/p/" + m[1] + m[3]
|
||||
err = &errOldGoogleRepo{fixedPath}
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := findPublicRepo(importPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if repo != nil {
|
||||
public = true
|
||||
} else {
|
||||
repo = findAnyRepo(importPath)
|
||||
}
|
||||
if repo == nil {
|
||||
err = errors.New("cannot download: " + importPath)
|
||||
return
|
||||
}
|
||||
err = checkoutRepo(srcDir, repo)
|
||||
return
|
||||
}
|
||||
|
||||
// checkoutRepo checks out repo into srcDir (if it's not checked out already)
|
||||
// and, if the -u flag is set, updates the repository.
|
||||
func checkoutRepo(srcDir string, repo RemoteRepo) error {
|
||||
if !repo.IsCheckedOut(srcDir) {
|
||||
// do checkout
|
||||
url, root, vcs, err := repo.Repo(http.DefaultClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repoPath := filepath.Join(srcDir, root)
|
||||
parent, _ := filepath.Split(repoPath)
|
||||
if err = os.MkdirAll(parent, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = run(string(filepath.Separator), nil, vcs.cmd, vcs.clone, url, repoPath); err != nil {
|
||||
return err
|
||||
}
|
||||
return vcs.updateRepo(repoPath)
|
||||
}
|
||||
if *update {
|
||||
// do update
|
||||
_, root, vcs, err := repo.Repo(http.DefaultClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repoPath := filepath.Join(srcDir, root)
|
||||
// Retrieve new revisions from the remote branch, if the VCS
|
||||
// supports this operation independently (e.g. svn doesn't)
|
||||
if vcs.pull != "" {
|
||||
if vcs.pullForceFlag != "" {
|
||||
if err = run(repoPath, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err = run(repoPath, nil, vcs.cmd, vcs.pull); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Update to release or latest revision
|
||||
return vcs.updateRepo(repoPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateRepo gets a list of tags in the repository and
|
||||
// checks out the tag closest to the current runtime.Version.
|
||||
// If no matching tag is found, it just updates to tip.
|
||||
func (v *vcs) updateRepo(repoPath string) error {
|
||||
if v.tagList == "" || v.tagListRe == nil {
|
||||
// TODO(adg): fix for svn
|
||||
return run(repoPath, nil, v.cmd, v.update)
|
||||
}
|
||||
|
||||
// Get tag list.
|
||||
stderr := new(bytes.Buffer)
|
||||
cmd := exec.Command(v.cmd, v.tagList)
|
||||
cmd.Dir = repoPath
|
||||
cmd.Stderr = stderr
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return &RunError{strings.Join(cmd.Args, " "), repoPath, out, err}
|
||||
}
|
||||
var tags []string
|
||||
for _, m := range v.tagListRe.FindAllStringSubmatch(string(out), -1) {
|
||||
tags = append(tags, m[1])
|
||||
}
|
||||
|
||||
// Only use the tag component of runtime.Version.
|
||||
ver := strings.Split(runtime.Version(), " ")[0]
|
||||
|
||||
// Select tag.
|
||||
if tag := selectTag(ver, tags); tag != "" {
|
||||
printf("selecting revision %q\n", tag)
|
||||
return run(repoPath, nil, v.cmd, v.checkout, v.updateRevFlag+tag)
|
||||
}
|
||||
|
||||
// No matching tag found, make default selection.
|
||||
printf("selecting tip\n")
|
||||
return run(repoPath, nil, v.cmd, v.update)
|
||||
}
|
||||
|
||||
// selectTag returns the closest matching tag for a given version.
|
||||
// Closest means the latest one that is not after the current release.
|
||||
// Version "release.rN" matches tags of the form "go.rN" (N being a decimal).
|
||||
// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
|
||||
func selectTag(goVersion string, tags []string) (match string) {
|
||||
const rPrefix = "release.r"
|
||||
if strings.HasPrefix(goVersion, rPrefix) {
|
||||
p := "go.r"
|
||||
v, err := strconv.ParseFloat(goVersion[len(rPrefix):], 64)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
var matchf float64
|
||||
for _, t := range tags {
|
||||
if !strings.HasPrefix(t, p) {
|
||||
continue
|
||||
}
|
||||
tf, err := strconv.ParseFloat(t[len(p):], 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if matchf < tf && tf <= v {
|
||||
match, matchf = t, tf
|
||||
}
|
||||
}
|
||||
}
|
||||
const wPrefix = "weekly."
|
||||
if strings.HasPrefix(goVersion, wPrefix) {
|
||||
p := "go.weekly."
|
||||
v := goVersion[len(wPrefix):]
|
||||
for _, t := range tags {
|
||||
if !strings.HasPrefix(t, p) {
|
||||
continue
|
||||
}
|
||||
if match < t && t[len(p):] <= v {
|
||||
match = t
|
||||
}
|
||||
}
|
||||
}
|
||||
return match
|
||||
}
|
||||
|
||||
func isDir(dir string) bool {
|
||||
fi, err := os.Stat(dir)
|
||||
return err == nil && fi.IsDir()
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var FindPublicRepoTests = []struct {
|
||||
pkg string
|
||||
vcs, root, url string
|
||||
transport *testTransport
|
||||
}{
|
||||
{
|
||||
"code.google.com/p/repo/path/foo",
|
||||
"hg",
|
||||
"code.google.com/p/repo",
|
||||
"https://code.google.com/p/repo",
|
||||
&testTransport{
|
||||
"https://code.google.com/p/repo/source/checkout",
|
||||
`<tt id="checkoutcmd">hg clone https://...`,
|
||||
},
|
||||
},
|
||||
{
|
||||
"code.google.com/p/repo/path/foo",
|
||||
"svn",
|
||||
"code.google.com/p/repo",
|
||||
"http://repo.googlecode.com/svn",
|
||||
&testTransport{
|
||||
"https://code.google.com/p/repo/source/checkout",
|
||||
`<tt id="checkoutcmd">svn checkout https://...`,
|
||||
},
|
||||
},
|
||||
{
|
||||
"code.google.com/p/repo/path/foo",
|
||||
"git",
|
||||
"code.google.com/p/repo",
|
||||
"https://code.google.com/p/repo",
|
||||
&testTransport{
|
||||
"https://code.google.com/p/repo/source/checkout",
|
||||
`<tt id="checkoutcmd">git clone https://...`,
|
||||
},
|
||||
},
|
||||
{
|
||||
"code.google.com/p/repo.sub/path",
|
||||
"hg",
|
||||
"code.google.com/p/repo.sub",
|
||||
"https://code.google.com/p/repo.sub",
|
||||
&testTransport{
|
||||
"https://code.google.com/p/repo/source/checkout?repo=sub",
|
||||
`<tt id="checkoutcmd">hg clone https://...`,
|
||||
},
|
||||
},
|
||||
{
|
||||
"bitbucket.org/user/repo/path/foo",
|
||||
"hg",
|
||||
"bitbucket.org/user/repo",
|
||||
"http://bitbucket.org/user/repo",
|
||||
&testTransport{
|
||||
"https://api.bitbucket.org/1.0/repositories/user/repo",
|
||||
`{"scm": "hg"}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
"bitbucket.org/user/repo/path/foo",
|
||||
"git",
|
||||
"bitbucket.org/user/repo",
|
||||
"http://bitbucket.org/user/repo.git",
|
||||
&testTransport{
|
||||
"https://api.bitbucket.org/1.0/repositories/user/repo",
|
||||
`{"scm": "git"}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
"github.com/user/repo/path/foo",
|
||||
"git",
|
||||
"github.com/user/repo",
|
||||
"http://github.com/user/repo.git",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"launchpad.net/project/series/path",
|
||||
"bzr",
|
||||
"launchpad.net/project/series",
|
||||
"https://launchpad.net/project/series",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"launchpad.net/~user/project/branch/path",
|
||||
"bzr",
|
||||
"launchpad.net/~user/project/branch",
|
||||
"https://launchpad.net/~user/project/branch",
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
func TestFindPublicRepo(t *testing.T) {
|
||||
for _, test := range FindPublicRepoTests {
|
||||
client := http.DefaultClient
|
||||
if test.transport != nil {
|
||||
client = &http.Client{Transport: test.transport}
|
||||
}
|
||||
repo, err := findPublicRepo(test.pkg)
|
||||
if err != nil {
|
||||
t.Errorf("findPublicRepo(%s): error: %v", test.pkg, err)
|
||||
continue
|
||||
}
|
||||
if repo == nil {
|
||||
t.Errorf("%s: got nil match", test.pkg)
|
||||
continue
|
||||
}
|
||||
url, root, vcs, err := repo.Repo(client)
|
||||
if err != nil {
|
||||
t.Errorf("%s: repo.Repo error: %v", test.pkg, err)
|
||||
continue
|
||||
}
|
||||
if v := vcsMap[test.vcs]; vcs != v {
|
||||
t.Errorf("%s: got vcs=%v, want %v", test.pkg, vcs, v)
|
||||
}
|
||||
if root != test.root {
|
||||
t.Errorf("%s: got root=%v, want %v", test.pkg, root, test.root)
|
||||
}
|
||||
if url != test.url {
|
||||
t.Errorf("%s: got url=%v, want %v", test.pkg, url, test.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testTransport struct {
|
||||
expectURL string
|
||||
responseBody string
|
||||
}
|
||||
|
||||
func (t *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if g, e := req.URL.String(), t.expectURL; g != e {
|
||||
return nil, errors.New("want " + e)
|
||||
}
|
||||
body := ioutil.NopCloser(bytes.NewBufferString(t.responseBody))
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: body,
|
||||
}, nil
|
||||
}
|
@ -1,400 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath" // use for file system paths
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "usage: goinstall [flags] importpath...")
|
||||
fmt.Fprintln(os.Stderr, " goinstall [flags] -a")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
const logfile = "goinstall.log"
|
||||
|
||||
var (
|
||||
fset = token.NewFileSet()
|
||||
argv0 = os.Args[0]
|
||||
parents = make(map[string]string)
|
||||
visit = make(map[string]status)
|
||||
installedPkgs = make(map[string]map[string]bool)
|
||||
schemeRe = regexp.MustCompile(`^[a-z]+://`)
|
||||
|
||||
allpkg = flag.Bool("a", false, "install all previously installed packages")
|
||||
reportToDashboard = flag.Bool("dashboard", true, "report public packages at "+dashboardURL)
|
||||
update = flag.Bool("u", false, "update already-downloaded packages")
|
||||
doGofix = flag.Bool("fix", false, "gofix each package before building it")
|
||||
doInstall = flag.Bool("install", true, "build and install")
|
||||
clean = flag.Bool("clean", false, "clean the package directory before installing")
|
||||
nuke = flag.Bool("nuke", false, "clean the package directory and target before installing")
|
||||
useMake = flag.Bool("make", true, "use make to build and install (obsolete, always true)")
|
||||
verbose = flag.Bool("v", false, "verbose")
|
||||
)
|
||||
|
||||
type status int // status for visited map
|
||||
const (
|
||||
unvisited status = iota
|
||||
visiting
|
||||
done
|
||||
)
|
||||
|
||||
type PackageError struct {
|
||||
pkg string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *PackageError) Error() string {
|
||||
return fmt.Sprintf("%s: %v", e.pkg, e.err)
|
||||
}
|
||||
|
||||
type DownloadError struct {
|
||||
pkg string
|
||||
goroot bool
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *DownloadError) Error() string {
|
||||
s := fmt.Sprintf("%s: download failed: %v", e.pkg, e.err)
|
||||
if e.goroot && os.Getenv("GOPATH") == "" {
|
||||
s += " ($GOPATH is not set)"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type DependencyError PackageError
|
||||
|
||||
func (e *DependencyError) Error() string {
|
||||
return fmt.Sprintf("%s: depends on failing packages:\n\t%v", e.pkg, e.err)
|
||||
}
|
||||
|
||||
type BuildError PackageError
|
||||
|
||||
func (e *BuildError) Error() string {
|
||||
return fmt.Sprintf("%s: build failed: %v", e.pkg, e.err)
|
||||
}
|
||||
|
||||
type RunError struct {
|
||||
cmd, dir string
|
||||
out []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *RunError) Error() string {
|
||||
return fmt.Sprintf("%v\ncd %q && %q\n%s", e.err, e.dir, e.cmd, e.out)
|
||||
}
|
||||
|
||||
func logf(format string, args ...interface{}) {
|
||||
format = "%s: " + format
|
||||
args = append([]interface{}{argv0}, args...)
|
||||
fmt.Fprintf(os.Stderr, format, args...)
|
||||
}
|
||||
|
||||
func printf(format string, args ...interface{}) {
|
||||
if *verbose {
|
||||
logf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
if runtime.GOROOT() == "" {
|
||||
fmt.Fprintf(os.Stderr, "%s: no $GOROOT\n", argv0)
|
||||
os.Exit(1)
|
||||
}
|
||||
readPackageList()
|
||||
|
||||
// special case - "unsafe" is already installed
|
||||
visit["unsafe"] = done
|
||||
|
||||
args := flag.Args()
|
||||
if *allpkg {
|
||||
if len(args) != 0 {
|
||||
usage() // -a and package list both provided
|
||||
}
|
||||
// install all packages that were ever installed
|
||||
n := 0
|
||||
for _, pkgs := range installedPkgs {
|
||||
for pkg := range pkgs {
|
||||
args = append(args, pkg)
|
||||
n++
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
logf("no installed packages\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
if len(args) == 0 {
|
||||
usage()
|
||||
}
|
||||
errs := false
|
||||
for _, path := range args {
|
||||
if err := install(path, ""); err != nil {
|
||||
errs = true
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
}
|
||||
if errs {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// printDeps prints the dependency path that leads to pkg.
|
||||
func printDeps(pkg string) {
|
||||
if pkg == "" {
|
||||
return
|
||||
}
|
||||
if visit[pkg] != done {
|
||||
printDeps(parents[pkg])
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\t%s ->\n", pkg)
|
||||
}
|
||||
|
||||
// readPackageList reads the list of installed packages from the
|
||||
// goinstall.log files in GOROOT and the GOPATHs and initializes
|
||||
// the installedPkgs variable.
|
||||
func readPackageList() {
|
||||
for _, t := range build.Path {
|
||||
installedPkgs[t.Path] = make(map[string]bool)
|
||||
name := filepath.Join(t.Path, logfile)
|
||||
pkglistdata, err := ioutil.ReadFile(name)
|
||||
if err != nil {
|
||||
printf("%s\n", err)
|
||||
continue
|
||||
}
|
||||
pkglist := strings.Fields(string(pkglistdata))
|
||||
for _, pkg := range pkglist {
|
||||
installedPkgs[t.Path][pkg] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// logPackage logs the named package as installed in the goinstall.log file
|
||||
// in the given tree if the package is not already in that file.
|
||||
func logPackage(pkg string, tree *build.Tree) (logged bool) {
|
||||
if installedPkgs[tree.Path][pkg] {
|
||||
return false
|
||||
}
|
||||
name := filepath.Join(tree.Path, logfile)
|
||||
fout, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
printf("package log: %s\n", err)
|
||||
return false
|
||||
}
|
||||
fmt.Fprintf(fout, "%s\n", pkg)
|
||||
fout.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
// install installs the package named by path, which is needed by parent.
|
||||
func install(pkg, parent string) error {
|
||||
// Basic validation of import path string.
|
||||
if s := schemeRe.FindString(pkg); s != "" {
|
||||
return fmt.Errorf("%q used in import path, try %q\n", s, pkg[len(s):])
|
||||
}
|
||||
if strings.HasSuffix(pkg, "/") {
|
||||
return fmt.Errorf("%q should not have trailing '/'\n", pkg)
|
||||
}
|
||||
|
||||
// Make sure we're not already trying to install pkg.
|
||||
switch visit[pkg] {
|
||||
case done:
|
||||
return nil
|
||||
case visiting:
|
||||
fmt.Fprintf(os.Stderr, "%s: package dependency cycle\n", argv0)
|
||||
printDeps(parent)
|
||||
fmt.Fprintf(os.Stderr, "\t%s\n", pkg)
|
||||
os.Exit(2)
|
||||
}
|
||||
parents[pkg] = parent
|
||||
visit[pkg] = visiting
|
||||
defer func() {
|
||||
visit[pkg] = done
|
||||
}()
|
||||
|
||||
// Check whether package is local or remote.
|
||||
// If remote, download or update it.
|
||||
tree, pkg, err := build.FindTree(pkg)
|
||||
// Don't build the standard library.
|
||||
if err == nil && tree.Goroot && isStandardPath(pkg) {
|
||||
if parent == "" {
|
||||
return &PackageError{pkg, errors.New("cannot goinstall the standard library")}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Download remote packages if not found or forced with -u flag.
|
||||
remote, public := isRemote(pkg), false
|
||||
if remote {
|
||||
if err == build.ErrNotFound || (err == nil && *update) {
|
||||
// Download remote package.
|
||||
printf("%s: download\n", pkg)
|
||||
public, err = download(pkg, tree.SrcDir())
|
||||
if err != nil {
|
||||
// only suggest -fix if the bad import was not on the command line
|
||||
if e, ok := err.(*errOldGoogleRepo); ok && parent != "" {
|
||||
err = fmt.Errorf("%v\nRun goinstall with -fix to gofix the code.", e)
|
||||
}
|
||||
return &DownloadError{pkg, tree.Goroot, err}
|
||||
}
|
||||
} else {
|
||||
// Test if this is a public repository
|
||||
// (for reporting to dashboard).
|
||||
repo, e := findPublicRepo(pkg)
|
||||
public = repo != nil
|
||||
err = e
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return &PackageError{pkg, err}
|
||||
}
|
||||
|
||||
// Install the package and its dependencies.
|
||||
if err := installPackage(pkg, parent, tree, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if remote {
|
||||
// mark package as installed in goinstall.log
|
||||
logged := logPackage(pkg, tree)
|
||||
|
||||
// report installation to the dashboard if this is the first
|
||||
// install from a public repository.
|
||||
if logged && public {
|
||||
maybeReportToDashboard(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// installPackage installs the specified package and its dependencies.
|
||||
func installPackage(pkg, parent string, tree *build.Tree, retry bool) (installErr error) {
|
||||
printf("%s: install\n", pkg)
|
||||
|
||||
// Read package information.
|
||||
dir := filepath.Join(tree.SrcDir(), filepath.FromSlash(pkg))
|
||||
dirInfo, err := build.ScanDir(dir)
|
||||
if err != nil {
|
||||
return &PackageError{pkg, err}
|
||||
}
|
||||
|
||||
// We reserve package main to identify commands.
|
||||
if parent != "" && dirInfo.Package == "main" {
|
||||
return &PackageError{pkg, fmt.Errorf("found only package main in %s; cannot import", dir)}
|
||||
}
|
||||
|
||||
// Run gofix if we fail to build and -fix is set.
|
||||
defer func() {
|
||||
if retry || installErr == nil || !*doGofix {
|
||||
return
|
||||
}
|
||||
if e, ok := (installErr).(*DependencyError); ok {
|
||||
// If this package failed to build due to a
|
||||
// DependencyError, only attempt to gofix it if its
|
||||
// dependency failed for some reason other than a
|
||||
// DependencyError or BuildError.
|
||||
// (If a dep or one of its deps doesn't build there's
|
||||
// no way that gofixing this package can help.)
|
||||
switch e.err.(type) {
|
||||
case *DependencyError:
|
||||
return
|
||||
case *BuildError:
|
||||
return
|
||||
}
|
||||
}
|
||||
gofix(pkg, dir, dirInfo)
|
||||
installErr = installPackage(pkg, parent, tree, true) // retry
|
||||
}()
|
||||
|
||||
// Install prerequisites.
|
||||
for _, p := range dirInfo.Imports {
|
||||
if p == "C" {
|
||||
continue
|
||||
}
|
||||
if err := install(p, pkg); err != nil {
|
||||
return &DependencyError{pkg, err}
|
||||
}
|
||||
}
|
||||
|
||||
// Install this package.
|
||||
err = domake(dir, pkg, tree, dirInfo.IsCommand())
|
||||
if err != nil {
|
||||
return &BuildError{pkg, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// gofix runs gofix against the GoFiles and CgoFiles of dirInfo in dir.
|
||||
func gofix(pkg, dir string, dirInfo *build.DirInfo) {
|
||||
printf("%s: gofix\n", pkg)
|
||||
files := append([]string{}, dirInfo.GoFiles...)
|
||||
files = append(files, dirInfo.CgoFiles...)
|
||||
for i, file := range files {
|
||||
files[i] = filepath.Join(dir, file)
|
||||
}
|
||||
cmd := exec.Command("gofix", files...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
logf("%s: gofix: %v", pkg, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Is this a standard package path? strings container/list etc.
|
||||
// Assume that if the first element has a dot, it's a domain name
|
||||
// and is not the standard package path.
|
||||
func isStandardPath(s string) bool {
|
||||
dot := strings.Index(s, ".")
|
||||
slash := strings.Index(s, "/")
|
||||
return dot < 0 || 0 < slash && slash < dot
|
||||
}
|
||||
|
||||
// run runs the command cmd in directory dir with standard input stdin.
|
||||
// If verbose is set and the command fails it prints the output to stderr.
|
||||
func run(dir string, stdin []byte, arg ...string) error {
|
||||
cmd := exec.Command(arg[0], arg[1:]...)
|
||||
cmd.Stdin = bytes.NewBuffer(stdin)
|
||||
cmd.Dir = dir
|
||||
printf("cd %s && %s %s\n", dir, cmd.Path, strings.Join(arg[1:], " "))
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
if *verbose {
|
||||
fmt.Fprintf(os.Stderr, "%v\n%s\n", err, out)
|
||||
}
|
||||
return &RunError{strings.Join(arg, " "), dir, out, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isRemote returns true if the first part of the package name looks like a
|
||||
// hostname - i.e. contains at least one '.' and the last part is at least 2
|
||||
// characters.
|
||||
func isRemote(pkg string) bool {
|
||||
parts := strings.SplitN(pkg, "/", 2)
|
||||
if len(parts) != 2 {
|
||||
return false
|
||||
}
|
||||
parts = strings.Split(parts[0], ".")
|
||||
if len(parts) < 2 || len(parts[len(parts)-1]) < 2 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
@ -1,183 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Run "make install" to build package.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"go/build"
|
||||
"path" // use for import paths
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// domake builds the package in dir.
|
||||
// domake generates a standard Makefile and passes it
|
||||
// to make on standard input.
|
||||
func domake(dir, pkg string, tree *build.Tree, isCmd bool) (err error) {
|
||||
makefile, err := makeMakefile(dir, pkg, tree, isCmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := []string{"bash", "gomake", "-f-"}
|
||||
if *nuke {
|
||||
cmd = append(cmd, "nuke")
|
||||
} else if *clean {
|
||||
cmd = append(cmd, "clean")
|
||||
}
|
||||
if *doInstall {
|
||||
cmd = append(cmd, "install")
|
||||
}
|
||||
if len(cmd) <= 3 { // nothing to do
|
||||
return nil
|
||||
}
|
||||
return run(dir, makefile, cmd...)
|
||||
}
|
||||
|
||||
// makeMakefile computes the standard Makefile for the directory dir
|
||||
// installing as package pkg. It includes all *.go files in the directory
|
||||
// except those in package main and those ending in _test.go.
|
||||
func makeMakefile(dir, pkg string, tree *build.Tree, isCmd bool) ([]byte, error) {
|
||||
if !safeName(pkg) {
|
||||
return nil, errors.New("unsafe name: " + pkg)
|
||||
}
|
||||
targ := pkg
|
||||
targDir := tree.PkgDir()
|
||||
if isCmd {
|
||||
// use the last part of the package name for targ
|
||||
_, targ = path.Split(pkg)
|
||||
targDir = tree.BinDir()
|
||||
}
|
||||
dirInfo, err := build.ScanDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cgoFiles := dirInfo.CgoFiles
|
||||
isCgo := make(map[string]bool, len(cgoFiles))
|
||||
for _, file := range cgoFiles {
|
||||
if !safeName(file) {
|
||||
return nil, errors.New("bad name: " + file)
|
||||
}
|
||||
isCgo[file] = true
|
||||
}
|
||||
|
||||
goFiles := make([]string, 0, len(dirInfo.GoFiles))
|
||||
for _, file := range dirInfo.GoFiles {
|
||||
if !safeName(file) {
|
||||
return nil, errors.New("unsafe name: " + file)
|
||||
}
|
||||
if !isCgo[file] {
|
||||
goFiles = append(goFiles, file)
|
||||
}
|
||||
}
|
||||
|
||||
oFiles := make([]string, 0, len(dirInfo.CFiles)+len(dirInfo.SFiles))
|
||||
cgoOFiles := make([]string, 0, len(dirInfo.CFiles))
|
||||
for _, file := range dirInfo.CFiles {
|
||||
if !safeName(file) {
|
||||
return nil, errors.New("unsafe name: " + file)
|
||||
}
|
||||
// When cgo is in use, C files are compiled with gcc,
|
||||
// otherwise they're compiled with gc.
|
||||
if len(cgoFiles) > 0 {
|
||||
cgoOFiles = append(cgoOFiles, file[:len(file)-2]+".o")
|
||||
} else {
|
||||
oFiles = append(oFiles, file[:len(file)-2]+".$O")
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range dirInfo.SFiles {
|
||||
if !safeName(file) {
|
||||
return nil, errors.New("unsafe name: " + file)
|
||||
}
|
||||
oFiles = append(oFiles, file[:len(file)-2]+".$O")
|
||||
}
|
||||
|
||||
var imports []string
|
||||
for _, t := range build.Path {
|
||||
imports = append(imports, t.PkgDir())
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
md := makedata{targ, targDir, "pkg", goFiles, oFiles, cgoFiles, cgoOFiles, imports}
|
||||
if isCmd {
|
||||
md.Type = "cmd"
|
||||
}
|
||||
if err := makefileTemplate.Execute(&buf, &md); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var safeBytes = []byte("+-~./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz")
|
||||
|
||||
func safeName(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
if strings.Contains(s, "..") {
|
||||
return false
|
||||
}
|
||||
if s[0] == '~' {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(s); i++ {
|
||||
if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// makedata is the data type for the makefileTemplate.
|
||||
type makedata struct {
|
||||
Targ string // build target
|
||||
TargDir string // build target directory
|
||||
Type string // build type: "pkg" or "cmd"
|
||||
GoFiles []string // list of non-cgo .go files
|
||||
OFiles []string // list of .$O files
|
||||
CgoFiles []string // list of cgo .go files
|
||||
CgoOFiles []string // list of cgo .o files, without extension
|
||||
Imports []string // gc/ld import paths
|
||||
}
|
||||
|
||||
var makefileTemplate = template.Must(template.New("Makefile").Parse(`
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG={{.Targ}}
|
||||
TARGDIR={{.TargDir}}
|
||||
|
||||
{{with .GoFiles}}
|
||||
GOFILES=\
|
||||
{{range .}} {{.}}\
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
{{with .OFiles}}
|
||||
OFILES=\
|
||||
{{range .}} {{.}}\
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
{{with .CgoFiles}}
|
||||
CGOFILES=\
|
||||
{{range .}} {{.}}\
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
{{with .CgoOFiles}}
|
||||
CGO_OFILES=\
|
||||
{{range .}} {{.}}\
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
GCIMPORTS={{range .Imports}}-I "{{.}}" {{end}}
|
||||
LDIMPORTS={{range .Imports}}-L "{{.}}" {{end}}
|
||||
|
||||
include $(GOROOT)/src/Make.{{.Type}}
|
||||
`))
|
@ -1,73 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
var selectTagTestTags = []string{
|
||||
"go.r58",
|
||||
"go.r58.1",
|
||||
"go.r59",
|
||||
"go.r59.1",
|
||||
"go.r61",
|
||||
"go.r61.1",
|
||||
"go.weekly.2010-01-02",
|
||||
"go.weekly.2011-10-12",
|
||||
"go.weekly.2011-10-12.1",
|
||||
"go.weekly.2011-10-14",
|
||||
"go.weekly.2011-11-01",
|
||||
// these should be ignored:
|
||||
"release.r59",
|
||||
"release.r59.1",
|
||||
"release",
|
||||
"weekly.2011-10-12",
|
||||
"weekly.2011-10-12.1",
|
||||
"weekly",
|
||||
"foo",
|
||||
"bar",
|
||||
"go.f00",
|
||||
"go!r60",
|
||||
"go.1999-01-01",
|
||||
}
|
||||
|
||||
var selectTagTests = []struct {
|
||||
version string
|
||||
selected string
|
||||
}{
|
||||
{"release.r57", ""},
|
||||
{"release.r58.2", "go.r58.1"},
|
||||
{"release.r59", "go.r59"},
|
||||
{"release.r59.1", "go.r59.1"},
|
||||
{"release.r60", "go.r59.1"},
|
||||
{"release.r60.1", "go.r59.1"},
|
||||
{"release.r61", "go.r61"},
|
||||
{"release.r66", "go.r61.1"},
|
||||
{"weekly.2010-01-01", ""},
|
||||
{"weekly.2010-01-02", "go.weekly.2010-01-02"},
|
||||
{"weekly.2010-01-02.1", "go.weekly.2010-01-02"},
|
||||
{"weekly.2010-01-03", "go.weekly.2010-01-02"},
|
||||
{"weekly.2011-10-12", "go.weekly.2011-10-12"},
|
||||
{"weekly.2011-10-12.1", "go.weekly.2011-10-12.1"},
|
||||
{"weekly.2011-10-13", "go.weekly.2011-10-12.1"},
|
||||
{"weekly.2011-10-14", "go.weekly.2011-10-14"},
|
||||
{"weekly.2011-10-14.1", "go.weekly.2011-10-14"},
|
||||
{"weekly.2011-11-01", "go.weekly.2011-11-01"},
|
||||
{"weekly.2014-01-01", "go.weekly.2011-11-01"},
|
||||
{"weekly.3000-01-01", "go.weekly.2011-11-01"},
|
||||
// faulty versions:
|
||||
{"release.f00", ""},
|
||||
{"weekly.1999-01-01", ""},
|
||||
{"junk", ""},
|
||||
{"", ""},
|
||||
}
|
||||
|
||||
func TestSelectTag(t *testing.T) {
|
||||
for _, c := range selectTagTests {
|
||||
selected := selectTag(c.version, selectTagTestTags)
|
||||
if selected != c.selected {
|
||||
t.Errorf("selectTag(%q) = %q, want %q", c.version, selected, c.selected)
|
||||
}
|
||||
}
|
||||
}
|
@ -158,7 +158,6 @@ DIRS=\
|
||||
../cmd/godoc\
|
||||
../cmd/fix\
|
||||
../cmd/gofmt\
|
||||
../cmd/goinstall\
|
||||
../cmd/gotest\
|
||||
../cmd/vet\
|
||||
../cmd/yacc\
|
||||
|
Loading…
Reference in New Issue
Block a user