~ubuntu-branches/ubuntu/vivid/golang/vivid

« back to all changes in this revision

Viewing changes to src/cmd/goinstall/download.go

  • Committer: Bazaar Package Importer
  • Author(s): Ondřej Surý
  • Date: 2011-08-03 17:04:59 UTC
  • mfrom: (14.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20110803170459-wzd99m3567y80ila
Tags: 1:59-1
* Imported Upstream version 59
* Refresh patches to a new release
* Fix FTBFS on ARM (Closes: #634270)
* Update version.bash to work with Debian packaging and not hg
  repository

Show diffs side-by-side

added added

removed removed

Lines of Context:
7
7
package main
8
8
 
9
9
import (
 
10
        "exec"
 
11
        "fmt"
10
12
        "http"
11
13
        "os"
12
14
        "path/filepath"
31
33
        }
32
34
}
33
35
 
34
 
var vcsPatterns = map[string]*regexp.Regexp{
35
 
        "googlecode": regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/(svn|hg))(/[a-z0-9A-Z_.\-/]*)?$`),
36
 
        "github":     regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`),
37
 
        "bitbucket":  regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`),
38
 
        "launchpad":  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_.\-/]+)?$`),
39
 
}
40
 
 
41
 
// isRemote returns true if the provided package path
42
 
// matches one of the supported remote repositories.
43
 
func isRemote(pkg string) bool {
44
 
        for _, r := range vcsPatterns {
45
 
                if r.MatchString(pkg) {
46
 
                        return true
47
 
                }
48
 
        }
49
 
        return false
50
 
}
51
 
 
52
 
// download checks out or updates pkg from the remote server.
53
 
func download(pkg, srcDir string) os.Error {
54
 
        if strings.Contains(pkg, "..") {
55
 
                return os.ErrorString("invalid path (contains ..)")
56
 
        }
57
 
        if m := vcsPatterns["bitbucket"].FindStringSubmatch(pkg); m != nil {
58
 
                if err := vcsCheckout(&hg, srcDir, m[1], "http://"+m[1], m[1]); err != nil {
59
 
                        return err
60
 
                }
61
 
                return nil
62
 
        }
63
 
        if m := vcsPatterns["googlecode"].FindStringSubmatch(pkg); m != nil {
64
 
                var v *vcs
65
 
                switch m[2] {
66
 
                case "hg":
67
 
                        v = &hg
68
 
                case "svn":
69
 
                        v = &svn
70
 
                default:
71
 
                        // regexp only allows hg, svn to get through
72
 
                        panic("missing case in download: " + pkg)
73
 
                }
74
 
                if err := vcsCheckout(v, srcDir, m[1], "https://"+m[1], m[1]); err != nil {
75
 
                        return err
76
 
                }
77
 
                return nil
78
 
        }
79
 
        if m := vcsPatterns["github"].FindStringSubmatch(pkg); m != nil {
80
 
                if strings.HasSuffix(m[1], ".git") {
81
 
                        return os.ErrorString("repository " + pkg + " should not have .git suffix")
82
 
                }
83
 
                if err := vcsCheckout(&git, srcDir, m[1], "http://"+m[1]+".git", m[1]); err != nil {
84
 
                        return err
85
 
                }
86
 
                return nil
87
 
        }
88
 
        if m := vcsPatterns["launchpad"].FindStringSubmatch(pkg); m != nil {
89
 
                // Either lp.net/<project>[/<series>[/<path>]]
90
 
                //       or lp.net/~<user or team>/<project>/<branch>[/<path>]
91
 
                if err := vcsCheckout(&bzr, srcDir, m[1], "https://"+m[1], m[1]); err != nil {
92
 
                        return err
93
 
                }
94
 
                return nil
95
 
        }
96
 
        return os.ErrorString("unknown repository: " + pkg)
97
 
}
98
 
 
99
36
// a vcs represents a version control system
100
37
// like Mercurial, Git, or Subversion.
101
38
type vcs struct {
 
39
        name              string
102
40
        cmd               string
103
41
        metadir           string
104
42
        checkout          string
110
48
        log               string
111
49
        logLimitFlag      string
112
50
        logReleaseFlag    string
 
51
        check             string
 
52
        protocols         []string
 
53
        suffix            string
 
54
        defaultHosts      []host
 
55
}
 
56
 
 
57
type host struct {
 
58
        pattern  *regexp.Regexp
 
59
        protocol string
 
60
        suffix   string
113
61
}
114
62
 
115
63
var hg = vcs{
 
64
        name:              "Mercurial",
116
65
        cmd:               "hg",
117
66
        metadir:           ".hg",
118
67
        checkout:          "checkout",
123
72
        log:               "log",
124
73
        logLimitFlag:      "-l1",
125
74
        logReleaseFlag:    "-rrelease",
 
75
        check:             "identify",
 
76
        protocols:         []string{"https", "http"},
 
77
        suffix:            ".hg",
 
78
        defaultHosts: []host{
 
79
                {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/hg)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
 
80
                {regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ""},
 
81
        },
126
82
}
127
83
 
128
84
var git = vcs{
 
85
        name:              "Git",
129
86
        cmd:               "git",
130
87
        metadir:           ".git",
131
88
        checkout:          "checkout",
136
93
        log:               "show-ref",
137
94
        logLimitFlag:      "",
138
95
        logReleaseFlag:    "release",
 
96
        check:             "ls-remote",
 
97
        protocols:         []string{"git", "https", "http"},
 
98
        suffix:            ".git",
 
99
        defaultHosts: []host{
 
100
                {regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ".git"},
 
101
        },
139
102
}
140
103
 
141
104
var svn = vcs{
 
105
        name:              "Subversion",
142
106
        cmd:               "svn",
143
107
        metadir:           ".svn",
144
108
        checkout:          "checkout",
148
112
        log:               "log",
149
113
        logLimitFlag:      "-l1",
150
114
        logReleaseFlag:    "release",
 
115
        check:             "info",
 
116
        protocols:         []string{"https", "http", "svn"},
 
117
        suffix:            ".svn",
 
118
        defaultHosts: []host{
 
119
                {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/svn)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
 
120
        },
151
121
}
152
122
 
153
123
var bzr = vcs{
 
124
        name:              "Bazaar",
154
125
        cmd:               "bzr",
155
126
        metadir:           ".bzr",
156
127
        checkout:          "update",
162
133
        log:               "log",
163
134
        logLimitFlag:      "-l1",
164
135
        logReleaseFlag:    "-rrelease",
 
136
        check:             "info",
 
137
        protocols:         []string{"https", "http", "bzr"},
 
138
        suffix:            ".bzr",
 
139
        defaultHosts: []host{
 
140
                {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_.\-/]+)?$`), "https", ""},
 
141
        },
 
142
}
 
143
 
 
144
var vcsList = []*vcs{&git, &hg, &bzr, &svn}
 
145
 
 
146
type vcsMatch struct {
 
147
        *vcs
 
148
        prefix, repo string
 
149
}
 
150
 
 
151
// findHostedRepo checks whether pkg is located at one of
 
152
// the supported code hosting sites and, if so, returns a match.
 
153
func findHostedRepo(pkg string) (*vcsMatch, os.Error) {
 
154
        for _, v := range vcsList {
 
155
                for _, host := range v.defaultHosts {
 
156
                        if hm := host.pattern.FindStringSubmatch(pkg); hm != nil {
 
157
                                if host.suffix != "" && strings.HasSuffix(hm[1], host.suffix) {
 
158
                                        return nil, os.NewError("repository " + pkg + " should not have " + v.suffix + " suffix")
 
159
                                }
 
160
                                repo := host.protocol + "://" + hm[1] + host.suffix
 
161
                                return &vcsMatch{v, hm[1], repo}, nil
 
162
                        }
 
163
                }
 
164
        }
 
165
        return nil, nil
 
166
}
 
167
 
 
168
// findAnyRepo looks for a vcs suffix in pkg (.git, etc) and returns a match.
 
169
func findAnyRepo(pkg string) (*vcsMatch, os.Error) {
 
170
        for _, v := range vcsList {
 
171
                i := strings.Index(pkg+"/", v.suffix+"/")
 
172
                if i < 0 {
 
173
                        continue
 
174
                }
 
175
                if !strings.Contains(pkg[:i], "/") {
 
176
                        continue // don't match vcs suffix in the host name
 
177
                }
 
178
                if m := v.find(pkg[:i]); m != nil {
 
179
                        return m, nil
 
180
                }
 
181
                return nil, fmt.Errorf("couldn't find %s repository", v.name)
 
182
        }
 
183
        return nil, nil
 
184
}
 
185
 
 
186
func (v *vcs) find(pkg string) *vcsMatch {
 
187
        for _, proto := range v.protocols {
 
188
                for _, suffix := range []string{"", v.suffix} {
 
189
                        repo := proto + "://" + pkg + suffix
 
190
                        out, err := exec.Command(v.cmd, v.check, repo).CombinedOutput()
 
191
                        if err == nil {
 
192
                                printf("find %s: found %s\n", pkg, repo)
 
193
                                return &vcsMatch{v, pkg + v.suffix, repo}
 
194
                        }
 
195
                        printf("find %s: %s %s %s: %v\n%s\n", pkg, v.cmd, v.check, repo, err, out)
 
196
                }
 
197
        }
 
198
        return nil
 
199
}
 
200
 
 
201
// isRemote returns true if the first part of the package name looks like a
 
202
// hostname - i.e. contains at least one '.' and the last part is at least 2
 
203
// characters.
 
204
func isRemote(pkg string) bool {
 
205
        parts := strings.SplitN(pkg, "/", 2)
 
206
        if len(parts) != 2 {
 
207
                return false
 
208
        }
 
209
        parts = strings.Split(parts[0], ".")
 
210
        if len(parts) < 2 || len(parts[len(parts)-1]) < 2 {
 
211
                return false
 
212
        }
 
213
        return true
 
214
}
 
215
 
 
216
// download checks out or updates pkg from the remote server.
 
217
func download(pkg, srcDir string) (dashReport bool, err os.Error) {
 
218
        if strings.Contains(pkg, "..") {
 
219
                err = os.NewError("invalid path (contains ..)")
 
220
                return
 
221
        }
 
222
        m, err := findHostedRepo(pkg)
 
223
        if err != nil {
 
224
                return
 
225
        }
 
226
        if m != nil {
 
227
                dashReport = true // only report public code hosting sites
 
228
        } else {
 
229
                m, err = findAnyRepo(pkg)
 
230
                if err != nil {
 
231
                        return
 
232
                }
 
233
        }
 
234
        if m == nil {
 
235
                err = os.NewError("cannot download: " + pkg)
 
236
                return
 
237
        }
 
238
        installed, err := m.checkoutRepo(srcDir, m.prefix, m.repo)
 
239
        if err != nil {
 
240
                return
 
241
        }
 
242
        if !installed {
 
243
                dashReport = false
 
244
        }
 
245
        return
165
246
}
166
247
 
167
248
// Try to detect if a "release" tag exists.  If it does, update
180
261
        return nil
181
262
}
182
263
 
183
 
// vcsCheckout checks out repo into dst using vcs.
 
264
// checkoutRepo checks out repo into dst using vcs.
184
265
// It tries to check out (or update, if the dst already
185
266
// exists and -u was specified on the command line)
186
267
// the repository at tag/branch "release".  If there is no
187
268
// such tag or branch, it falls back to the repository tip.
188
 
func vcsCheckout(vcs *vcs, srcDir, pkgprefix, repo, dashpath string) os.Error {
 
269
func (vcs *vcs) checkoutRepo(srcDir, pkgprefix, repo string) (installed bool, err os.Error) {
189
270
        dst := filepath.Join(srcDir, filepath.FromSlash(pkgprefix))
190
271
        dir, err := os.Stat(filepath.Join(dst, vcs.metadir))
191
272
        if err == nil && !dir.IsDirectory() {
192
 
                return os.ErrorString("not a directory: " + dst)
 
273
                err = os.NewError("not a directory: " + dst)
 
274
                return
193
275
        }
194
276
        if err != nil {
195
277
                parent, _ := filepath.Split(dst)
196
 
                if err := os.MkdirAll(parent, 0777); err != nil {
197
 
                        return err
198
 
                }
199
 
                if err := run(string(filepath.Separator), nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
200
 
                        return err
201
 
                }
202
 
                if err := vcs.updateRepo(dst); err != nil {
203
 
                        return err
204
 
                }
205
 
                // success on first installation - report
206
 
                maybeReportToDashboard(dashpath)
 
278
                if err = os.MkdirAll(parent, 0777); err != nil {
 
279
                        return
 
280
                }
 
281
                if err = run(string(filepath.Separator), nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
 
282
                        return
 
283
                }
 
284
                if err = vcs.updateRepo(dst); err != nil {
 
285
                        return
 
286
                }
 
287
                installed = true
207
288
        } else if *update {
208
289
                // Retrieve new revisions from the remote branch, if the VCS
209
290
                // supports this operation independently (e.g. svn doesn't)
210
291
                if vcs.pull != "" {
211
292
                        if vcs.pullForceFlag != "" {
212
 
                                if err := run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
213
 
                                        return err
 
293
                                if err = run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
 
294
                                        return
214
295
                                }
215
 
                        } else if err := run(dst, nil, vcs.cmd, vcs.pull); err != nil {
216
 
                                return err
 
296
                        } else if err = run(dst, nil, vcs.cmd, vcs.pull); err != nil {
 
297
                                return
217
298
                        }
218
299
                }
219
 
 
220
300
                // Update to release or latest revision
221
 
                if err := vcs.updateRepo(dst); err != nil {
222
 
                        return err
 
301
                if err = vcs.updateRepo(dst); err != nil {
 
302
                        return
223
303
                }
224
304
        }
225
 
        return nil
 
305
        return
226
306
}