blob: 59eeb64bf48d83e3f0c5ddf21ad05ecce26ea392 [file] [log] [blame]
Russ Cox3e4e4ec2010-03-04 17:04:50 -08001// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Experimental Go package installer; see doc.go.
6
7package main
8
9import (
10 "bytes"
11 "exec"
12 "flag"
13 "fmt"
14 "io"
15 "os"
16 "path"
Russ Coxda392d92010-08-18 10:08:49 -040017 "runtime"
Russ Cox3e4e4ec2010-03-04 17:04:50 -080018 "strings"
19)
20
21func usage() {
22 fmt.Fprint(os.Stderr, "usage: goinstall importpath...\n")
23 flag.PrintDefaults()
24 os.Exit(2)
25}
26
27var (
28 argv0 = os.Args[0]
29 errors = false
30 gobin = os.Getenv("GOBIN")
31 parents = make(map[string]string)
Russ Coxda392d92010-08-18 10:08:49 -040032 root = runtime.GOROOT()
Russ Cox3e4e4ec2010-03-04 17:04:50 -080033 visit = make(map[string]status)
34
35 reportToDashboard = flag.Bool("dashboard", true, "report public packages at "+dashboardURL)
36 update = flag.Bool("u", false, "update already-downloaded packages")
37 verbose = flag.Bool("v", false, "verbose")
38)
39
40type status int // status for visited map
41const (
42 unvisited status = iota
43 visiting
44 done
45)
46
47func main() {
48 flag.Usage = usage
49 flag.Parse()
50 if root == "" {
51 fmt.Fprintf(os.Stderr, "%s: no $GOROOT\n", argv0)
52 os.Exit(1)
53 }
Russ Cox3e4e4ec2010-03-04 17:04:50 -080054 if gobin == "" {
Russ Coxaafe474e2010-08-24 20:00:33 -040055 gobin = root + "/bin"
Russ Cox3e4e4ec2010-03-04 17:04:50 -080056 }
Russ Coxaafe474e2010-08-24 20:00:33 -040057 root += "/src/pkg/"
Russ Cox3e4e4ec2010-03-04 17:04:50 -080058
59 // special case - "unsafe" is already installed
60 visit["unsafe"] = done
61
62 // install command line arguments
63 args := flag.Args()
64 if len(args) == 0 {
65 usage()
66 }
67 for _, path := range args {
68 install(path, "")
69 }
70 if errors {
71 os.Exit(1)
72 }
73}
74
75// printDeps prints the dependency path that leads to pkg.
76func printDeps(pkg string) {
77 if pkg == "" {
78 return
79 }
80 if visit[pkg] != done {
81 printDeps(parents[pkg])
82 }
83 fmt.Fprintf(os.Stderr, "\t%s ->\n", pkg)
84}
85
86// install installs the package named by path, which is needed by parent.
87func install(pkg, parent string) {
88 // Make sure we're not already trying to install pkg.
Russ Coxc7122a32010-03-30 10:51:11 -070089 switch visit[pkg] {
Russ Cox3e4e4ec2010-03-04 17:04:50 -080090 case done:
91 return
92 case visiting:
93 fmt.Fprintf(os.Stderr, "%s: package dependency cycle\n", argv0)
94 printDeps(parent)
95 fmt.Fprintf(os.Stderr, "\t%s\n", pkg)
96 os.Exit(2)
97 }
98 visit[pkg] = visiting
99 parents[pkg] = parent
100 if *verbose {
101 fmt.Println(pkg)
102 }
103
104 // Check whether package is local or remote.
105 // If remote, download or update it.
106 var dir string
107 local := false
108 if isLocalPath(pkg) {
109 dir = pkg
110 local = true
111 } else if isStandardPath(pkg) {
112 dir = path.Join(root, pkg)
113 local = true
114 } else {
115 var err os.Error
116 dir, err = download(pkg)
117 if err != nil {
118 fmt.Fprintf(os.Stderr, "%s: %s: %s\n", argv0, pkg, err)
119 errors = true
120 visit[pkg] = done
121 return
122 }
123 }
124
125 // Install prerequisites.
Roger Peppe3ce29382010-06-21 11:01:20 -0700126 files, m, pkgname, err := goFiles(dir, parent == "")
Russ Cox3e4e4ec2010-03-04 17:04:50 -0800127 if err != nil {
128 fmt.Fprintf(os.Stderr, "%s: %s: %s\n", argv0, pkg, err)
129 errors = true
130 visit[pkg] = done
131 return
132 }
133 if len(files) == 0 {
134 fmt.Fprintf(os.Stderr, "%s: %s: package has no files\n", argv0, pkg)
135 errors = true
136 visit[pkg] = done
137 return
138 }
139 for p := range m {
140 install(p, pkg)
141 }
Roger Peppe3ce29382010-06-21 11:01:20 -0700142 if pkgname == "main" {
143 if !errors {
144 fmt.Fprintf(os.Stderr, "%s: %s's dependencies are installed.\n", argv0, pkg)
145 }
146 errors = true
147 visit[pkg] = done
148 return
149 }
Russ Cox3e4e4ec2010-03-04 17:04:50 -0800150
151 // Install this package.
152 if !errors {
153 if err := domake(dir, pkg, local); err != nil {
154 fmt.Fprintf(os.Stderr, "%s: installing %s: %s\n", argv0, pkg, err)
155 errors = true
156 }
157 }
158
159 visit[pkg] = done
160}
161
162// Is this a local path? /foo ./foo ../foo . ..
163func isLocalPath(s string) bool {
164 return strings.HasPrefix(s, "/") || strings.HasPrefix(s, "./") || strings.HasPrefix(s, "../") || s == "." || s == ".."
165}
166
167// Is this a standard package path? strings container/vector etc.
168// Assume that if the first element has a dot, it's a domain name
169// and is not the standard package path.
170func isStandardPath(s string) bool {
171 dot := strings.Index(s, ".")
172 slash := strings.Index(s, "/")
173 return dot < 0 || 0 < slash && slash < dot
174}
175
176// run runs the command cmd in directory dir with standard input stdin.
177// If the command fails, run prints the command and output on standard error
178// in addition to returning a non-nil os.Error.
179func run(dir string, stdin []byte, cmd ...string) os.Error {
180 return genRun(dir, stdin, cmd, false)
181}
182
183// quietRun is like run but prints nothing on failure unless -v is used.
184func quietRun(dir string, stdin []byte, cmd ...string) os.Error {
185 return genRun(dir, stdin, cmd, true)
186}
187
Gustavo Niemeyerae330322010-06-30 23:33:49 -0700188// genRun implements run and quietRun.
Russ Cox3e4e4ec2010-03-04 17:04:50 -0800189func genRun(dir string, stdin []byte, cmd []string, quiet bool) os.Error {
190 bin, err := exec.LookPath(cmd[0])
191 if err != nil {
Andrey Mirtchovski456642a2010-03-23 18:13:16 -0700192 // report binary as well as the error
193 return os.NewError(cmd[0] + ": " + err.String())
Russ Cox3e4e4ec2010-03-04 17:04:50 -0800194 }
195 p, err := exec.Run(bin, cmd, os.Environ(), dir, exec.Pipe, exec.Pipe, exec.MergeWithStdout)
196 if *verbose {
197 fmt.Fprintf(os.Stderr, "%s: %s; %s %s\n", argv0, dir, bin, strings.Join(cmd[1:], " "))
198 }
199 if err != nil {
200 return err
201 }
202 go func() {
203 p.Stdin.Write(stdin)
204 p.Stdin.Close()
205 }()
206 var buf bytes.Buffer
207 io.Copy(&buf, p.Stdout)
208 io.Copy(&buf, p.Stdout)
209 w, err := p.Wait(0)
210 p.Close()
Alex Brainman12576f92010-08-04 17:18:57 -0700211 if err != nil {
212 return err
213 }
Russ Cox3e4e4ec2010-03-04 17:04:50 -0800214 if !w.Exited() || w.ExitStatus() != 0 {
215 if !quiet || *verbose {
216 if dir != "" {
217 dir = "cd " + dir + "; "
218 }
219 fmt.Fprintf(os.Stderr, "%s: === %s%s\n", argv0, dir, strings.Join(cmd, " "))
220 os.Stderr.Write(buf.Bytes())
221 fmt.Fprintf(os.Stderr, "--- %s\n", w)
222 }
223 return os.ErrorString("running " + cmd[0] + ": " + w.String())
224 }
225 return nil
226}