Left: | ||
Right: |
OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2012 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 // This is a tool for packaging binary releases. | |
6 // It currently supports FreeBSD, Linux, and OS X. | |
r
2012/02/24 08:44:35
s/currently //
adg
2012/02/25 04:47:01
Done.
| |
7 package main | |
8 | |
9 import ( | |
10 "bytes" | |
11 "encoding/base64" | |
12 "errors" | |
13 "flag" | |
14 "fmt" | |
15 "io" | |
16 "io/ioutil" | |
17 "log" | |
18 "mime/multipart" | |
19 "net/http" | |
20 "os" | |
21 "os/exec" | |
22 "path/filepath" | |
23 "strings" | |
24 ) | |
25 | |
26 var ( | |
27 tag = flag.String("tag", "weekly", "mercurial tag to check out") | |
28 repo = flag.String("repo", "https://code.google.com/p/go", "repo URL ") | |
29 user = flag.String("user", "builder@golang.org", "Google Code upload username") | |
30 password = flag.String("password", "", "Google Code upload password") | |
r
2012/02/24 08:44:35
don't do this. clear text, on the command line, vi
adg
2012/02/25 04:47:01
Now I'm using the .gobuildkey file in the same way
| |
31 ) | |
32 | |
33 const ( | |
34 packageMaker = "/Applications/Utilities/PackageMaker.app/Contents/MacOS/ PackageMaker" | |
35 uploadURL = "https://go.googlecode.com/files" | |
36 ) | |
37 | |
38 var cleanFiles = []string{ | |
39 ".hg", | |
40 ".hgtags", | |
41 ".hgignore", | |
42 "VERSION.cache", | |
43 } | |
44 | |
45 func main() { | |
46 flag.Usage = func() { | |
47 fmt.Fprintf(os.Stderr, "usage: %s [flags] targets...\n", os.Args [0]) | |
48 flag.PrintDefaults() | |
49 os.Exit(2) | |
50 } | |
51 flag.Parse() | |
52 if flag.NArg() == 0 { | |
53 flag.Usage() | |
54 } | |
55 for _, targ := range flag.Args() { | |
56 p := strings.SplitN(targ, "-", 2) | |
57 if len(p) != 2 { | |
58 log.Println("Ignoring unrecognized target:", targ) | |
59 continue | |
60 } | |
61 b := Build{OS: p[0], Arch: p[1]} | |
62 if err := b.Do(); err != nil { | |
63 log.Printf("%s: %v", targ, err) | |
64 } | |
65 } | |
66 } | |
67 | |
68 type Build struct { | |
69 OS, Arch string | |
r
2012/02/24 08:44:35
put on two lines
adg
2012/02/25 04:47:01
Done.
| |
70 root string | |
71 } | |
72 | |
73 func (b *Build) Do() error { | |
74 work, err := ioutil.TempDir("", "bindist") | |
75 if err != nil { | |
76 return err | |
77 } | |
78 defer os.RemoveAll(work) | |
79 b.root = filepath.Join(work, "go") | |
80 | |
81 // Clone Go distribution and update to tag. | |
82 _, err = b.run(work, "hg", "clone", "-q", *repo, b.root) | |
83 if err != nil { | |
84 return err | |
85 } | |
86 _, err = b.run(b.root, "hg", "update", *tag) | |
87 if err != nil { | |
88 return err | |
89 } | |
90 | |
91 // Build. | |
92 _, err = b.run(filepath.Join(work, "go/src"), "bash", "make.bash") | |
93 if err != nil { | |
94 return err | |
95 } | |
96 | |
97 // Get version string. | |
98 version, err := b.run("", filepath.Join(b.root, "bin/go"), "version") | |
99 if err != nil { | |
100 return err | |
101 } | |
102 v := bytes.SplitN(version, []byte(" "), 4) | |
103 version = bytes.Join(v[2:], []byte(" ")) | |
104 | |
105 // Write VERSION file. | |
106 err = ioutil.WriteFile(filepath.Join(b.root, "VERSION"), version, 0644) | |
107 if err != nil { | |
108 return err | |
109 } | |
110 | |
111 // Clean goroot. | |
112 for _, name := range cleanFiles { | |
r
2012/02/24 08:44:35
scary. instead, i'd test it's clean and stop proce
adg
2012/02/24 23:01:56
This is to remove the (many megabytes of) mercuria
| |
113 err = os.RemoveAll(filepath.Join(b.root, name)) | |
114 if err != nil { | |
115 return err | |
116 } | |
117 } | |
118 | |
119 // Create packages. | |
120 targ := fmt.Sprintf("go.%s.%s-%s", v[2], b.OS, b.Arch) | |
121 switch b.OS { | |
122 case "linux", "freebsd": | |
123 // build tarball | |
124 targ += ".tar.gz" | |
125 _, err = b.run("", "tar", "czf", targ, "-C", work, "go") | |
126 case "darwin": | |
127 // arrange work so it's laid out as the dest filesystem | |
128 etc := filepath.Join(b.root, "misc/dist/darwin/etc") | |
129 _, err = b.run(work, "cp", "-r", etc, ".") | |
130 if err != nil { | |
131 return err | |
132 } | |
133 localDir := filepath.Join(work, "usr/local") | |
134 err = os.MkdirAll(localDir, 0744) | |
135 if err != nil { | |
136 return err | |
137 } | |
138 _, err = b.run(work, "mv", "go", localDir) | |
139 if err != nil { | |
140 return err | |
141 } | |
142 // build package | |
143 pm := packageMaker | |
144 if !exists(pm) { | |
145 pm = "/Developer" + pm | |
146 if !exists(pm) { | |
147 return errors.New("couldn't find PackageMaker") | |
148 } | |
149 } | |
150 targ += ".pkg" | |
151 scripts := filepath.Join(work, "usr/local/go/misc/dist/darwin/sc ripts") | |
152 _, err = b.run("", pm, "-v", | |
153 "-r", work, | |
154 "-o", targ, | |
155 "--scripts", scripts, | |
156 "--id", "com.googlecode.go", | |
157 "--title", "Go", | |
158 "--version", "1.0", | |
159 "--target", "10.5") | |
r
2012/02/24 08:44:35
is that true?
adg
2012/02/25 04:47:01
Yes, it's the minimum.
| |
160 } | |
161 if err == nil && *password != "" { | |
162 err = b.upload(string(v[2]), targ) | |
163 } | |
164 return err | |
165 } | |
166 | |
167 func (b *Build) run(dir, name string, args ...string) ([]byte, error) { | |
168 buf := new(bytes.Buffer) | |
169 cmd := exec.Command(name, args...) | |
170 cmd.Stdout = buf | |
171 cmd.Stderr = buf | |
172 cmd.Dir = dir | |
173 cmd.Env = b.env() | |
174 if err := cmd.Run(); err != nil { | |
175 fmt.Fprintf(os.Stderr, "%s", buf.Bytes()) | |
176 return nil, fmt.Errorf("%s %s: %v", name, strings.Join(args, " " ), err) | |
177 } | |
178 return buf.Bytes(), nil | |
179 } | |
180 | |
181 var cleanEnv = []string{ | |
182 "GOARCH", | |
183 "GOBIN", | |
184 "GOHOSTARCH", | |
185 "GOHOSTOS", | |
186 "GOOS", | |
187 "GOROOT", | |
188 "GOROOT_FINAL", | |
189 } | |
190 | |
191 func (b *Build) env() []string { | |
192 env := os.Environ() | |
193 for i := 0; i < len(env); i++ { | |
194 for _, c := range cleanEnv { | |
195 if strings.HasPrefix(env[i], c+"=") { | |
196 env = append(env[:i], env[i+1:]...) | |
197 } | |
198 } | |
199 } | |
200 env = append(env, | |
201 "GOARCH="+b.Arch, | |
202 "GOHOSTARCH="+b.Arch, | |
203 "GOHOSTOS="+b.OS, | |
204 "GOOS="+b.OS, | |
205 "GOROOT="+b.root, | |
206 "GOROOT_FINAL=/usr/local/go", | |
207 ) | |
208 return env | |
209 } | |
210 | |
211 func (b *Build) upload(version string, filename string) error { | |
212 // Prepare upload metadata. | |
213 labels := []string{"Arch-" + b.Arch} | |
214 os_, arch := b.OS, b.Arch | |
215 switch b.Arch { | |
216 case "386": | |
217 arch = "32-bit" | |
218 case "amd64": | |
219 arch = "64-bit" | |
220 } | |
221 switch b.OS { | |
222 case "linux": | |
223 os_ = "Linux" | |
224 labels = append(labels, "Type-Archive", "OpSys-Linux") | |
225 case "freebsd": | |
226 os_ = "FreeBSD" | |
227 labels = append(labels, "Type-Archive", "OpSys-FreeBSD") | |
228 case "darwin": | |
229 os_ = "Mac OS X" | |
230 labels = append(labels, "Type-Installer", "OpSys-OSX") | |
231 } | |
232 summary := fmt.Sprintf("Go %s %s (%s)", version, os_, arch) | |
233 | |
234 // Open file to upload. | |
235 f, err := os.Open(filename) | |
236 if err != nil { | |
237 return err | |
238 } | |
239 defer f.Close() | |
240 | |
241 // Prepare multipart payload. | |
242 body := new(bytes.Buffer) | |
243 w := multipart.NewWriter(body) | |
244 if err := w.WriteField("summary", summary); err != nil { | |
245 return err | |
246 } | |
247 for _, l := range labels { | |
248 if err := w.WriteField("label", l); err != nil { | |
249 return err | |
250 } | |
251 } | |
252 fw, err := w.CreateFormFile("filename", filename) | |
253 if err != nil { | |
254 return err | |
255 } | |
256 if _, err = io.Copy(fw, f); err != nil { | |
257 return err | |
258 } | |
259 if err := w.Close(); err != nil { | |
260 return err | |
261 } | |
262 | |
263 // Send the file to Google Code. | |
264 req, err := http.NewRequest("POST", uploadURL, body) | |
265 if err != nil { | |
266 return err | |
267 } | |
268 token := fmt.Sprintf("%s:%s", *user, *password) | |
269 token = base64.StdEncoding.EncodeToString([]byte(token)) | |
270 req.Header.Set("Authorization", "Basic "+token) | |
271 req.Header.Set("Content-type", w.FormDataContentType()) | |
272 | |
273 resp, err := http.DefaultTransport.RoundTrip(req) | |
274 if err != nil { | |
275 return err | |
276 } | |
277 if resp.StatusCode/100 != 2 { | |
278 fmt.Fprintln(os.Stderr, "upload failed") | |
279 defer resp.Body.Close() | |
280 io.Copy(os.Stderr, resp.Body) | |
281 return fmt.Errorf("upload: %s", resp.Status) | |
282 } | |
283 return nil | |
284 } | |
285 | |
286 func exists(path string) bool { | |
287 _, err := os.Stat(path) | |
288 return err == nil | |
289 } | |
OLD | NEW |