├── .gitignore ├── Godeps ├── Godeps.json └── Readme ├── Makefile ├── README.md ├── cp.go ├── gonative.go ├── platform.go └── unpack.go /.gitignore: -------------------------------------------------------------------------------- 1 | Godeps/_workspace 2 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/inconshreveable/gonative", 3 | "GoVersion": "go1.5.2", 4 | "GodepVersion": "v74", 5 | "Packages": [ 6 | "github.com/inconshreveable/gonative" 7 | ], 8 | "Deps": [ 9 | { 10 | "ImportPath": "github.com/codegangsta/cli", 11 | "Comment": "v1.11.1", 12 | "Rev": "c31a7975863e7810c92e2e288a9ab074f9a88f29" 13 | }, 14 | { 15 | "ImportPath": "github.com/go-stack/stack", 16 | "Comment": "v1.5.2", 17 | "Rev": "100eb0c0a9c5b306ca2fb4f165df21d80ada4b82" 18 | }, 19 | { 20 | "ImportPath": "github.com/inconshreveable/axiom", 21 | "Rev": "082a33c88ebd58a4027c566ceade6ba430a38b8f" 22 | }, 23 | { 24 | "ImportPath": "github.com/inconshreveable/log15", 25 | "Comment": "v2.3-77-gb410a38", 26 | "Rev": "b410a384e5471a0f33ce79ce0a68e16133ee91ba" 27 | }, 28 | { 29 | "ImportPath": "github.com/inconshreveable/log15/term", 30 | "Comment": "v2.3-77-gb410a38", 31 | "Rev": "b410a384e5471a0f33ce79ce0a68e16133ee91ba" 32 | }, 33 | { 34 | "ImportPath": "github.com/inconshreveable/mousetrap", 35 | "Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" 36 | }, 37 | { 38 | "ImportPath": "github.com/kardianos/osext", 39 | "Rev": "29ae4ffbc9a6fe9fb2bc5029050ce6996ea1d3bc" 40 | }, 41 | { 42 | "ImportPath": "github.com/kr/binarydist", 43 | "Rev": "9955b0ab8708602d411341e55fffd7e0700f86bd" 44 | }, 45 | { 46 | "ImportPath": "github.com/mattn/go-colorable", 47 | "Rev": "3dac7b4f76f6e17fb39b768b89e3783d16e237fe" 48 | }, 49 | { 50 | "ImportPath": "github.com/mattn/go-isatty", 51 | "Rev": "56b76bdf51f7708750eac80fa38b952bb9f32639" 52 | }, 53 | { 54 | "ImportPath": "golang.org/x/sys/unix", 55 | "Rev": "833a04a10549a95dc34458c195cbad61bbb6cb4d" 56 | }, 57 | { 58 | "ImportPath": "gopkg.in/inconshreveable/go-update.v0", 59 | "Rev": "d8b0b1d421aa1cbf392c05869f8abbc669bb7066" 60 | }, 61 | { 62 | "ImportPath": "gopkg.in/inconshreveable/go-update.v0/check", 63 | "Rev": "d8b0b1d421aa1cbf392c05869f8abbc669bb7066" 64 | }, 65 | { 66 | "ImportPath": "gopkg.in/inconshreveable/go-update.v0/download", 67 | "Rev": "d8b0b1d421aa1cbf392c05869f8abbc669bb7066" 68 | }, 69 | { 70 | "ImportPath": "gopkg.in/yaml.v1", 71 | "Rev": "9f9df34309c04878acc86042b16630b0f696e1de" 72 | } 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PKG=github.com/inconshreveable/gonative 2 | WORKSPACE=$(shell pwd)/Godeps/_workspace 3 | CANONICAL=$(WORKSPACE)/src/$(PKG) 4 | GODEP=$(WORKSPACE)/bin/godep 5 | export GOPATH:=$(WORKSPACE) 6 | 7 | BUILD=build 8 | 9 | default: workspace $(CANONICAL) $(GODEP) fmt 10 | $(GODEP) go $(BUILD) -v $(PKG) 11 | 12 | $(GODEP): 13 | go get github.com/tools/godep 14 | 15 | workspace: $(GODEP) 16 | $(GODEP) restore 17 | 18 | $(CANONICAL): 19 | mkdir -p $(WORKSPACE)/src/github.com/inconshreveable 20 | ln -s $(shell pwd) $(WORKSPACE)/src/$(PKG) 21 | 22 | fmt: 23 | go fmt ./... 24 | 25 | clean: 26 | go clean -i -r $(PKG) 27 | 28 | test: 29 | go test -cover 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gonative 2 | 3 | Cross compiled Go binaries are not suitable for production applications 4 | because code in the standard library relies on Cgo for DNS resolution 5 | with the native resolver, access to system certificate roots, and parts of os/user. 6 | 7 | gonative is a simple tool which creates a build of Go that can cross compile 8 | to all platforms while still using the Cgo-enabled versions of the stdlib 9 | packages. It does this by downloading the binary distributions for each 10 | platform and copying their libraries into the proper places. It sets 11 | the correct mod time so they don't get rebuilt. It also copies 12 | some auto-generated runtime files into the build as well. gonative does 13 | not modify any Go that you have installed and builds a new installaion of 14 | Go in a separate directory (the current directory by default). 15 | 16 | Once you have a toolchain for cross-compilation, you can use tools like 17 | [gox](https://github.com/mitchellh/gox) to cross-compile native builds easily. 18 | 19 | gonative will not help you if your own packages rely on Cgo 20 | 21 | ### Installation 22 | 23 | git clone https://github.com/inconshreveable/gonative 24 | cd gonative 25 | make 26 | 27 | Alternatively, you can install gonative via `go get` but the dependencies are not 28 | locked down. 29 | 30 | go get github.com/inconshreveable/gonative 31 | 32 | ### Running 33 | The 'build' command will build a toolchain in a directory called 'go' in your working directory. 34 | 35 | gonative build 36 | 37 | To build a particular version of Go (default is 1.4): 38 | 39 | gonative build -version=1.3.3 40 | 41 | For options and help: 42 | 43 | gonative build -h 44 | 45 | ### How it works 46 | 47 | gonative downloads the go source code and compiles it for your host platform. 48 | It then bootstraps the toolchain for all target platforms (but does not compile the standard library). 49 | Then, it fetches the official binary distributions for all target platforms and copies 50 | each pkg/OS\_ARCH directory into the toolchain so that you will link with natively-compiled versions 51 | of the standard library. It walks all of the copied standard library and sets their modtimes so that 52 | they won't get rebuilt. It also copies some necessary auto-generated runtime source 53 | files for each platform (z\*\_) into the source directory to make it all work. 54 | 55 | ### Example with gox: 56 | 57 | Here's an example of how to cross-compile a project: 58 | 59 | $ go get github.com/mitchellh/gox 60 | $ go get github.com/inconshreveable/gonative 61 | $ cd /your/project 62 | $ gonative build 63 | $ PATH=$PWD/go/bin/:$PATH gox 64 | 65 | This isn't the most optimal way of doing things though. You only ever need one gonative-built 66 | Go toolchain. And with the proper GOPATH set up, you don't need to be 67 | in your project's working directory. I use it mostly like this: 68 | 69 | #### One time only setup: 70 | 71 | $ go get github.com/mitchellh/gox 72 | $ go get github.com/inconshreveable/gonative 73 | $ mkdir -p /usr/local/gonative 74 | $ cd /usr/local/gonative 75 | $ gonative build 76 | 77 | #### Building a project: 78 | 79 | $ PATH=/usr/local/gonative/go/bin/:$PATH gox github.com/your-name/application-name 80 | 81 | ### Open Issues 82 | 83 | - gonative is untested on Windows 84 | 85 | ### Caveats 86 | - no linux/arm support because there are no official builds of linux/arm 87 | - linux\_386 binaries that use native libs depend on 32-bit libc/libpthread/elf loader. some 64-bit linux distributions might not have those installed by default 88 | -------------------------------------------------------------------------------- /cp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015 Caleb Spare 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | // Package cp offers simple file and directory copying for Go. 24 | package main 25 | 26 | import ( 27 | "errors" 28 | "io" 29 | "os" 30 | "path/filepath" 31 | "strings" 32 | ) 33 | 34 | var errCopyFileWithDir = errors.New("dir argument to CopyFile") 35 | 36 | // CopyFile copies the file with path src to dst. The new file must not exist. 37 | // It is created with the same permissions as src. 38 | func CopyFile(dst, src string) error { 39 | rf, err := os.Open(src) 40 | if err != nil { 41 | return err 42 | } 43 | defer rf.Close() 44 | rstat, err := rf.Stat() 45 | if err != nil { 46 | return err 47 | } 48 | if rstat.IsDir() { 49 | return errCopyFileWithDir 50 | } 51 | 52 | wf, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, rstat.Mode()) 53 | if err != nil { 54 | return err 55 | } 56 | if _, err := io.Copy(wf, rf); err != nil { 57 | wf.Close() 58 | return err 59 | } 60 | return wf.Close() 61 | } 62 | 63 | // CopyAll copies the file or (recursively) the directory at src to dst. 64 | // Permissions are preserved. dst must not already exist. 65 | func CopyAll(dst, src string) error { 66 | Log.Info("copy recursive", "dst", dst, "src", src) 67 | return filepath.Walk(src, makeWalkFn(dst, src)) 68 | } 69 | 70 | func makeWalkFn(dst, src string) filepath.WalkFunc { 71 | return func(path string, info os.FileInfo, err error) error { 72 | if err != nil { 73 | return err 74 | } 75 | dstPath := filepath.Join(dst, strings.TrimPrefix(path, src)) 76 | if info.IsDir() { 77 | err := os.Mkdir(dstPath, info.Mode()) 78 | if err == nil || os.IsExist(err) { 79 | return nil 80 | } 81 | return err 82 | } 83 | return CopyFile(dstPath, path) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /gonative.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "runtime" 9 | "strings" 10 | "sync" 11 | "time" 12 | 13 | "github.com/codegangsta/cli" 14 | "github.com/inconshreveable/axiom" 15 | log "github.com/inconshreveable/log15" 16 | ) 17 | 18 | var Log = log.Root() 19 | 20 | const usage = `build Go installations with native stdlib packages 21 | 22 | DESCRIPTION: 23 | Cross compiled Go binaries are not suitable for production applications 24 | because code in the standard library relies on Cgo for DNS resolution 25 | with the native resolver, access to system certificate roots, and parts of os/user. 26 | 27 | gonative is a simple tool which creates a build of Go that can cross compile 28 | to all platforms while still using the Cgo-enabled versions of the stdlib 29 | packages. It does this by downloading the binary distributions for each 30 | platform and copying their libraries into the proper places. It sets 31 | the correct access time so they don't get rebuilt. It also copies 32 | some auto-generated runtime files into the build as well. gonative does 33 | not modify any Go that you have installed and builds Go again in a separate 34 | directory (the current directory by default).` 35 | const equinoxAppId = "ap_VQ_K1O_27-tPsncKE3E2GszIPm" 36 | const updatePublicKey = `-----BEGIN PUBLIC KEY----- 37 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvMwGMSLLi3bfq6UZesVR 38 | H+/EnPyVqbVTJs3zCiFSnLrXMkOMuXfmf7mC23q1cPaGOIFTfmhcx5/vkda10NJ1 39 | owTAJKXVctC6TUei42vIiBSPsdhzyinNtCdkEkBT2f6Ac58OQV1dUBW/b0fQRQZN 40 | 9tEwW7PK1QnR++bmVu2XzoGEw17XZdeDoXftDBgYAzOWDqapZpHETPobL5oQHeQN 41 | CVdCaNbNo52/HL6XKyDGCNudVqiKgIoExPzcOL6KKfvMla1Y4mrrArbuNBlE3qxW 42 | CwmnjtWg+J7vb9rKfZvuVPXPD/RoruZUmHBc1f31KB/QFvn/zXSqeyBcsd6ywCfo 43 | KwIDAQAB 44 | -----END PUBLIC KEY-----` 45 | 46 | type Options struct { 47 | Version string 48 | SrcPath string 49 | TargetPath string 50 | Platforms []Platform 51 | } 52 | 53 | func main() { 54 | app := cli.NewApp() 55 | app.Name = "gonative" 56 | app.Author = "inconshreveable" 57 | app.Email = "alan@inconshreveable.com" 58 | app.Usage = usage 59 | app.HideHelp = true 60 | app.HideVersion = true 61 | app.Version = "0.2.0" 62 | app.Commands = []cli.Command{ 63 | cli.Command{ 64 | Name: "build", 65 | Usage: "build a go installation with native stdlib packages", 66 | Flags: []cli.Flag{ 67 | cli.StringFlag{"version", "1.5.2", "version of Go to build", "", nil}, 68 | cli.StringFlag{"src", "", "path to go source, empty string means to fetch from internet", "", nil}, 69 | cli.StringFlag{"target", "go", "target directory in which to build Go", "", nil}, 70 | cli.StringFlag{"platforms", "", "space separated list of platforms to build, default is 'darwin_amd64 freebsd_amd64 linux_386 linux_amd64 windows_386 windows_amd64'", "", nil}, 71 | }, 72 | Action: buildCmd, 73 | }, 74 | } 75 | axiom.WrapApp(app, axiom.NewLogged()) 76 | app.Commands = append(app.Commands, []cli.Command{ 77 | axiom.VersionCommand(), 78 | axiom.NewUpdater(equinoxAppId, updatePublicKey).Command(), 79 | }...) 80 | app.Run(os.Args) 81 | } 82 | 83 | func buildCmd(c *cli.Context) { 84 | exit := func(err error) { 85 | if err == nil { 86 | os.Exit(0) 87 | } else { 88 | log.Crit("command failed", "err", err) 89 | os.Exit(1) 90 | } 91 | } 92 | 93 | opts := &Options{ 94 | Version: c.String("version"), 95 | SrcPath: c.String("src"), 96 | TargetPath: c.String("target"), 97 | } 98 | 99 | platforms := c.String("platforms") 100 | if platforms == "" { 101 | opts.Platforms = defaultPlatforms 102 | } else { 103 | opts.Platforms = make([]Platform, 0) 104 | for _, pString := range strings.Split(platforms, " ") { 105 | parts := strings.Split(pString, "_") 106 | if len(parts) != 2 { 107 | exit(fmt.Errorf("Invalid platform string: %v", pString)) 108 | } 109 | opts.Platforms = append(opts.Platforms, Platform{parts[0], parts[1]}) 110 | } 111 | } 112 | 113 | exit(Build(opts)) 114 | } 115 | 116 | func Build(opts *Options) error { 117 | // normalize paths 118 | targetPath, err := filepath.Abs(opts.TargetPath) 119 | if err != nil { 120 | return err 121 | } 122 | 123 | src := opts.SrcPath 124 | if src == "" { 125 | src = "(from internet)" 126 | } 127 | Log.Info("building go", "version", opts.Version, "src", src, "target", targetPath, "platforms", opts.Platforms) 128 | 129 | // tells the platform goroutines that the target path is ready 130 | targetReady := make(chan struct{}) 131 | 132 | // platform gorouintes can report an error here 133 | errors := make(chan error, len(opts.Platforms)) 134 | 135 | // need to wait for each platform to finish 136 | var wg sync.WaitGroup 137 | wg.Add(len(opts.Platforms)) 138 | 139 | // run all platform fetch/copies in parallel 140 | for _, p := range opts.Platforms { 141 | go getPlatform(p, targetPath, opts.Version, targetReady, errors, &wg) 142 | } 143 | 144 | // if no source path specified, fetch source from the internet 145 | if opts.SrcPath == "" { 146 | srcPath, err := srcPlatform.Download(opts.Version) 147 | if err != nil { 148 | return err 149 | } 150 | defer os.RemoveAll(srcPath) 151 | opts.SrcPath = filepath.Join(srcPath, "go") 152 | } 153 | 154 | // copy the source to the target directory 155 | err = CopyAll(targetPath, opts.SrcPath) 156 | if err != nil { 157 | return err 158 | } 159 | 160 | // build Go for the host platform 161 | err = makeDotBash(targetPath) 162 | Log.Debug("make.bash", "err", err) 163 | if err != nil { 164 | return err 165 | } 166 | 167 | // bootstrap compilers for all target platforms 168 | Log.Info("boostraping go compilers") 169 | for _, p := range opts.Platforms { 170 | err = distBootstrap(targetPath, p) 171 | Log.Debug("bootstrap compiler", "plat", p, "err", err) 172 | if err != nil { 173 | return err 174 | } 175 | } 176 | 177 | // tell the platform goroutines that the target dir is ready 178 | close(targetReady) 179 | 180 | // wait for all platforms to finish 181 | wg.Wait() 182 | 183 | // return error if a platform failed 184 | select { 185 | case err := <-errors: 186 | return err 187 | default: 188 | Log.Info("successfuly built Go", "path", targetPath) 189 | return nil 190 | } 191 | } 192 | 193 | func getPlatform(p Platform, targetPath, version string, targetReady chan struct{}, errors chan error, wg *sync.WaitGroup) { 194 | lg := Log.New("plat", p) 195 | defer wg.Done() 196 | 197 | // download the binary distribution 198 | path, err := p.Download(version) 199 | if err != nil { 200 | errors <- err 201 | return 202 | } 203 | defer os.RemoveAll(path) 204 | 205 | // wait for target directory to be ready 206 | <-targetReady 207 | 208 | // copy over the packages 209 | targetPkgPath := filepath.Join(targetPath, "pkg", p.String()) 210 | srcPkgPath := filepath.Join(path, "go", "pkg", p.String()) 211 | err = CopyAll(targetPkgPath, srcPkgPath) 212 | if err != nil { 213 | errors <- err 214 | return 215 | } 216 | 217 | // copy over the auto-generated z_ files 218 | srcZPath := filepath.Join(path, "go", "src", "runtime", "z*_"+p.String()) 219 | targetZPath := filepath.Join(targetPath, "src", "runtime") 220 | if version < "1.4" { 221 | srcZPath = filepath.Join(path, "go", "src", "pkg", "runtime", "z*_"+p.String()) 222 | targetZPath = filepath.Join(targetPath, "src", "pkg", "runtime") 223 | } 224 | lg.Debug("copy zfile", "dst", targetZPath, "src", srcZPath, "err", err) 225 | CopyFile(targetZPath, srcZPath) 226 | 227 | // change the mod times 228 | now := time.Now() 229 | err = filepath.Walk(targetPkgPath, func(path string, info os.FileInfo, err error) error { 230 | os.Chtimes(path, now, now) 231 | return nil 232 | }) 233 | lg.Debug("set modtimes", "err", err) 234 | if err != nil { 235 | errors <- err 236 | return 237 | } 238 | } 239 | 240 | // runs make.[bash|bat] in the source directory to build all of the compilers 241 | // and standard library 242 | func makeDotBash(goRoot string) (err error) { 243 | scriptName := "make.bash" 244 | if runtime.GOOS == "windows" { 245 | scriptName = "make.bat" 246 | } 247 | 248 | scriptPath, err := filepath.Abs(filepath.Join(goRoot, "src", scriptName)) 249 | if err != nil { 250 | return 251 | } 252 | scriptDir := filepath.Dir(scriptPath) 253 | 254 | cmd := exec.Cmd{ 255 | Path: scriptPath, 256 | Args: []string{scriptPath}, 257 | Env: os.Environ(), 258 | Dir: scriptDir, 259 | Stdout: os.Stdout, 260 | Stderr: os.Stderr, 261 | } 262 | 263 | return cmd.Run() 264 | } 265 | 266 | // runs dist bootrap to build the compilers for a target platform 267 | func distBootstrap(goRoot string, p Platform) (err error) { 268 | // the dist tool gets put in the pkg/tool/{host_platform} directory after we've built 269 | // the compilers/stdlib for the host platform 270 | hostPlatform := Platform{runtime.GOOS, runtime.GOARCH} 271 | scriptPath, err := filepath.Abs(filepath.Join(goRoot, "pkg", "tool", hostPlatform.String(), "dist")) 272 | if err != nil { 273 | return 274 | } 275 | 276 | // but we want to run it from the src directory 277 | scriptDir, err := filepath.Abs(filepath.Join(goRoot, "src")) 278 | if err != nil { 279 | return 280 | } 281 | 282 | bootstrapCmd := exec.Cmd{ 283 | Path: scriptPath, 284 | Args: []string{scriptPath, "bootstrap", "-v"}, 285 | Env: append(os.Environ(), 286 | "GOOS="+p.OS, 287 | "GOARCH="+p.Arch, 288 | "GOROOT="+goRoot), 289 | Dir: scriptDir, 290 | Stdout: os.Stdout, 291 | Stderr: os.Stderr, 292 | } 293 | 294 | return bootstrapCmd.Run() 295 | } 296 | -------------------------------------------------------------------------------- /platform.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/hex" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "net/http" 10 | "os" 11 | "strings" 12 | 13 | "github.com/inconshreveable/log15" 14 | ) 15 | 16 | var srcPlatform = Platform{"", ""} 17 | 18 | var defaultPlatforms = []Platform{ 19 | Platform{"linux", "386"}, 20 | Platform{"linux", "amd64"}, 21 | Platform{"freebsd", "amd64"}, 22 | Platform{"darwin", "amd64"}, 23 | Platform{"windows", "386"}, 24 | Platform{"windows", "amd64"}, 25 | } 26 | 27 | const ( 28 | oldDistURL = "https://go.googlecode.com/files/go%s.%s.tar.gz" 29 | distURL = "https://storage.googleapis.com/golang/go%s.%s.tar.gz" 30 | lastOldDistVersion = "1.2.1" 31 | lastOldDarwinVersion = "1.4.2" 32 | ) 33 | 34 | type Platform struct { 35 | OS string 36 | Arch string 37 | } 38 | 39 | func (p *Platform) String() string { 40 | if p.OS == "" && p.Arch == "" { 41 | return "src" 42 | } 43 | return p.OS + "_" + p.Arch 44 | } 45 | 46 | func (p *Platform) Download(version string) (path string, err error) { 47 | url := p.distURL(version) 48 | lg := Log.New("plat", p.String(), "url", url) 49 | lg.Info("start download") 50 | resp, err := http.Get(url) 51 | if err != nil { 52 | return 53 | } 54 | defer resp.Body.Close() 55 | 56 | if resp.StatusCode != 200 { 57 | return "", fmt.Errorf("Bad response for download (%s): %v", url, resp.StatusCode) 58 | } 59 | 60 | archive, err := download(lg, resp.Body, p.String(), checksums[url]) 61 | if err != nil { 62 | return "", err 63 | } 64 | defer os.Remove(archive.Name()) 65 | defer archive.Close() 66 | if _, err := archive.Seek(0, os.SEEK_SET); err != nil { 67 | return "", err 68 | } 69 | 70 | path, err = ioutil.TempDir(".", p.String()+"-") 71 | if err != nil { 72 | return 73 | } 74 | var unpackFn func(string, *os.File) error 75 | switch { 76 | case strings.HasSuffix(url, ".zip"): 77 | unpackFn = unpackZip 78 | case strings.HasSuffix(url, ".tar.gz"): 79 | unpackFn = unpackTarGz 80 | default: 81 | return "", fmt.Errorf("Unknown archive type for URL: %v", url) 82 | } 83 | 84 | if err := unpackFn(path, archive); err != nil { 85 | lg.Error("unpack error", "err", err) 86 | return "", err 87 | } 88 | 89 | lg.Info("download complete") 90 | return path, nil 91 | } 92 | 93 | func (p *Platform) distURL(version string) string { 94 | template := distURL 95 | if version <= lastOldDistVersion { 96 | template = oldDistURL 97 | } 98 | 99 | distString := p.OS + "-" + p.Arch 100 | // special cases 101 | switch { 102 | case p.OS == "darwin" && version <= lastOldDarwinVersion: 103 | distString += "-osx10.8" 104 | case p.OS == "" && p.Arch == "": 105 | distString = "src" 106 | } 107 | 108 | s := fmt.Sprintf(template, version, distString) 109 | if p.OS == "windows" { 110 | s = strings.Replace(s, ".tar.gz", ".zip", 1) 111 | } 112 | return s 113 | } 114 | 115 | func download(lg log15.Logger, rd io.Reader, name string, checksum string) (*os.File, error) { 116 | f, err := ioutil.TempFile(".", name+"-") 117 | if err != nil { 118 | return nil, err 119 | } 120 | defer func() { 121 | if err != nil { 122 | f.Close() 123 | os.Remove(f.Name()) 124 | } 125 | }() 126 | sha := sha1.New() 127 | wr := io.MultiWriter(f, sha) 128 | if _, err := io.Copy(wr, rd); err != nil { 129 | return nil, err 130 | } 131 | if checksum == "" { 132 | lg.Warn("no checksum for URL") 133 | } else if actual := hex.EncodeToString(sha.Sum(nil)); actual != checksum { 134 | lg.Error("checksum mismatch", "expected", checksum, "got", actual) 135 | return nil, fmt.Errorf("checksum mismatch: %v/%v", actual, checksum) 136 | } 137 | return f, nil 138 | } 139 | 140 | var checksums = map[string]string{ 141 | "https://storage.googleapis.com/golang/go1.5.2.src.tar.gz": "c7d78ba4df574b5f9a9bb5d17505f40c4d89b81c", 142 | "https://storage.googleapis.com/golang/go1.5.2.darwin-amd64.tar.gz": "4f30332a56e9c8a36daeeff667bab3608e4dffd2", 143 | "https://storage.googleapis.com/golang/go1.5.2.darwin-amd64.pkg": "102b4e946b7bb40f0e8aa508e41340a696ead752", 144 | "https://storage.googleapis.com/golang/go1.5.2.freebsd-amd64.tar.gz": "34bbe347a95908ca440e4bf584a200522bba1985", 145 | "https://storage.googleapis.com/golang/go1.5.2.linux-386.tar.gz": "49ff1c2510eaba80423e55a633901464b28437ef", 146 | "https://storage.googleapis.com/golang/go1.5.2.linux-amd64.tar.gz": "cae87ed095e8d94a81871281d35da7829bd1234e", 147 | "https://storage.googleapis.com/golang/go1.5.2.windows-386.zip": "a9b265268a4632ad6f7ca8769e6a34eb1522f784", 148 | "https://storage.googleapis.com/golang/go1.5.2.windows-386.msi": "31bf4feb763385cc6e87a4c2aac9d8c711e3b378", 149 | "https://storage.googleapis.com/golang/go1.5.2.windows-amd64.zip": "5eb85b0eec36cfef05700935f2420b6104986733", 150 | "https://storage.googleapis.com/golang/go1.5.2.windows-amd64.msi": "101a612d3ce65a51459667340d1991d594f9b8e7", 151 | "https://storage.googleapis.com/golang/go1.5.1.src.tar.gz": "0df564746d105f4180c2b576a1553ebca9d9a124", 152 | "https://storage.googleapis.com/golang/go1.5.1.darwin-amd64.tar.gz": "02451b1f3b2c715edc5587174e35438982663672", 153 | "https://storage.googleapis.com/golang/go1.5.1.darwin-amd64.pkg": "857b77a85ba111af1b0928a73cca52136780a75d", 154 | "https://storage.googleapis.com/golang/go1.5.1.freebsd-amd64.tar.gz": "78ac27b7c009142ed0d86b899f1711bb9811b7e1", 155 | "https://storage.googleapis.com/golang/go1.5.1.linux-386.tar.gz": "6ce7328f84a863f341876658538dfdf10aff86ee", 156 | "https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz": "46eecd290d8803887dec718c691cc243f2175fe0", 157 | "https://storage.googleapis.com/golang/go1.5.1.windows-386.zip": "bb071ec45ef39cd5ed9449b54c5dd083b8233bfa", 158 | "https://storage.googleapis.com/golang/go1.5.1.windows-386.msi": "034065452b7233b2a570d4be1218a97c475cded0", 159 | "https://storage.googleapis.com/golang/go1.5.1.windows-amd64.zip": "7815772347ad3e11a096d927c65bfb15d5b0f490", 160 | "https://storage.googleapis.com/golang/go1.5.1.windows-amd64.msi": "0a439f49b546b82f85adf84a79bbf40de2b3d5ba", 161 | "https://storage.googleapis.com/golang/go1.4.3.src.tar.gz": "486db10dc571a55c8d795365070f66d343458c48", 162 | "https://storage.googleapis.com/golang/go1.4.3.darwin-amd64.tar.gz": "945666c36b42bf859d98775c4f02f807a5bdb6b0", 163 | "https://storage.googleapis.com/golang/go1.4.3.darwin-amd64.pkg": "3d91a21e3217370b80ca26e89a994e8199d583e7", 164 | "https://storage.googleapis.com/golang/go1.4.3.freebsd-amd64.tar.gz": "573217c097f78143ea7c54212445c31944750144", 165 | "https://storage.googleapis.com/golang/go1.4.3.linux-386.tar.gz": "405777725abe566989cdb436d2efeb2667be670f", 166 | "https://storage.googleapis.com/golang/go1.4.3.linux-amd64.tar.gz": "332b64236d30a8805fc8dd8b3a269915b4c507fe", 167 | "https://storage.googleapis.com/golang/go1.4.3.windows-386.zip": "77ec9b61c1e1bf475463c62c36c395ba9d69aa9e", 168 | "https://storage.googleapis.com/golang/go1.4.3.windows-386.msi": "cad793895b258929ee796ef9ea77855626740ecd", 169 | "https://storage.googleapis.com/golang/go1.4.3.windows-amd64.zip": "821a6773adadd7409380addc4791771f2b057fa0", 170 | "https://storage.googleapis.com/golang/go1.4.3.windows-amd64.msi": "5e7c6cb012cbf09242b040b84b78b5e52d980337", 171 | "https://storage.googleapis.com/golang/go1.4.2.src.tar.gz": "460caac03379f746c473814a65223397e9c9a2f6", 172 | "https://storage.googleapis.com/golang/go1.4.2.darwin-386-osx10.6.tar.gz": "fb3e6b30f4e1b1be47bbb98d79dd53da8dec24ec", 173 | "https://storage.googleapis.com/golang/go1.4.2.darwin-386-osx10.8.tar.gz": "65f5610fdb38febd869aeffbd426c83b650bb408", 174 | "https://storage.googleapis.com/golang/go1.4.2.darwin-386-osx10.6.pkg": "3ed569ce33616d5d36f963e5d7cefb55727c8621", 175 | "https://storage.googleapis.com/golang/go1.4.2.darwin-386-osx10.8.pkg": "7f3fb2438fa0212febef13749d8d144934bb1c80", 176 | "https://storage.googleapis.com/golang/go1.4.2.darwin-amd64-osx10.6.tar.gz": "00c3f9a03daff818b2132ac31d57f054925c60e7", 177 | "https://storage.googleapis.com/golang/go1.4.2.darwin-amd64-osx10.8.tar.gz": "58a04b3eb9853c75319d9076df6f3ac8b7430f7f", 178 | "https://storage.googleapis.com/golang/go1.4.2.darwin-amd64-osx10.6.pkg": "3fa5455e211a70c0a920abd53cb3093269c5149c", 179 | "https://storage.googleapis.com/golang/go1.4.2.darwin-amd64-osx10.8.pkg": "8fde619d48864cb1c77ddc2a1aec0b7b20406b38", 180 | "https://storage.googleapis.com/golang/go1.4.2.linux-386.tar.gz": "50557248e89b6e38d395fda93b2f96b2b860a26a", 181 | "https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz": "5020af94b52b65cc9b6f11d50a67e4bae07b0aff", 182 | "https://storage.googleapis.com/golang/go1.4.2.windows-386.zip": "0e074e66a7816561d7947ff5c3514be96f347dc4", 183 | "https://storage.googleapis.com/golang/go1.4.2.windows-386.msi": "e8bd3d87cb52441b2c9aee7c2c5f5ce7ffccc832", 184 | "https://storage.googleapis.com/golang/go1.4.2.windows-amd64.zip": "91b229a3ff0a1ce6e791c832b0b4670bfc5457b5", 185 | "https://storage.googleapis.com/golang/go1.4.2.windows-amd64.msi": "a914f3dad5521a8f658dce3e1575f3b6792975f0", 186 | "https://storage.googleapis.com/golang/go1.4.src.tar.gz": "6a7d9bd90550ae1e164d7803b3e945dc8309252b", 187 | "https://storage.googleapis.com/golang/go1.4.darwin-386-osx10.6.tar.gz": "ee31cd0e26245d0e48f11667e4298e2e7f54f9b6", 188 | "https://storage.googleapis.com/golang/go1.4.darwin-386-osx10.8.tar.gz": "4d2ae2f5c0216c44e432c6044b1e1f0aea99f712", 189 | "https://storage.googleapis.com/golang/go1.4.darwin-386-osx10.6.pkg": "05f2a1ab9d2aaae06c968fbdf1a6a9c28d380ceb", 190 | "https://storage.googleapis.com/golang/go1.4.darwin-386-osx10.8.pkg": "81534c4eec80729b81b8e5f5889dfc2a3ba37131", 191 | "https://storage.googleapis.com/golang/go1.4.darwin-amd64-osx10.6.tar.gz": "09621b9226abe12c2179778b015a33c1787b29d6", 192 | "https://storage.googleapis.com/golang/go1.4.darwin-amd64-osx10.8.tar.gz": "28b2b731f86ada85246969e8ffc77d50542cdcb5", 193 | "https://storage.googleapis.com/golang/go1.4.darwin-amd64-osx10.6.pkg": "29271b54d3ce7108270a9b7b64342950026704bf", 194 | "https://storage.googleapis.com/golang/go1.4.darwin-amd64-osx10.8.pkg": "2043aaf5c1363e483c6042f8685acd70ec9e41f8", 195 | "https://storage.googleapis.com/golang/go1.4.freebsd-386.tar.gz": "36c5cc2ebef4b4404b12f2b5f2dfd23d73ecdbcc", 196 | "https://storage.googleapis.com/golang/go1.4.freebsd-amd64.tar.gz": "9441745b9c61002feedee8f0016c082b56319e44", 197 | "https://storage.googleapis.com/golang/go1.4.linux-386.tar.gz": "cb18d8122bfd3bbba20fa1a19b8f7566dcff795d", 198 | "https://storage.googleapis.com/golang/go1.4.linux-amd64.tar.gz": "cd82abcb0734f82f7cf2d576c9528cebdafac4c6", 199 | "https://storage.googleapis.com/golang/go1.4.windows-386.zip": "f44240a1750dd051476ae78e9ad0502bc5c7661d", 200 | "https://storage.googleapis.com/golang/go1.4.windows-386.msi": "b26151702cba760d6eec94214c457bee01f6d859", 201 | "https://storage.googleapis.com/golang/go1.4.windows-amd64.zip": "44f103d558b293919eb680041625c262dd00eb9a", 202 | "https://storage.googleapis.com/golang/go1.4.windows-amd64.msi": "359124f2bba4c59df1eb81d11e16e388d0a996f9", 203 | "https://storage.googleapis.com/golang/go1.3.3.src.tar.gz": "b54b7deb7b7afe9f5d9a3f5dd830c7dede35393a", 204 | "https://storage.googleapis.com/golang/go1.3.3.darwin-386-osx10.6.tar.gz": "04b3e38549183e984f509c07ad40d8bcd577a702", 205 | "https://storage.googleapis.com/golang/go1.3.3.darwin-386-osx10.8.tar.gz": "88f35d3327a84107aac4f2f24cb0883e5fdbe0e5", 206 | "https://storage.googleapis.com/golang/go1.3.3.darwin-386-osx10.6.pkg": "49756b700670ae4109e555f2e5f9bedbaa3c50da", 207 | "https://storage.googleapis.com/golang/go1.3.3.darwin-386-osx10.8.pkg": "a89b570a326e5f8c9509f40be9fa90e54b3bf7a7", 208 | "https://storage.googleapis.com/golang/go1.3.3.darwin-amd64-osx10.6.tar.gz": "dfe68de684f6e8d9c371d01e6d6a522efe3b8942", 209 | "https://storage.googleapis.com/golang/go1.3.3.darwin-amd64-osx10.8.tar.gz": "be686ec7ba68d588735cc2094ccab8bdd651de9e", 210 | "https://storage.googleapis.com/golang/go1.3.3.darwin-amd64-osx10.6.pkg": "9aec7e9eff11100a6db026d1b423d1250925e4c4", 211 | "https://storage.googleapis.com/golang/go1.3.3.darwin-amd64-osx10.8.pkg": "6435e50059fe7fa0d60f1b15aab7f255a61816ce", 212 | "https://storage.googleapis.com/golang/go1.3.3.freebsd-386.tar.gz": "875a5515dd7d3e5826c7c003bb2450f3129ccbad", 213 | "https://storage.googleapis.com/golang/go1.3.3.freebsd-amd64.tar.gz": "8531ae5e745c887f8dad1a3f00ca873cfcace56e", 214 | "https://storage.googleapis.com/golang/go1.3.3.linux-386.tar.gz": "9eb426d5505de55729e2656c03d85722795dd85e", 215 | "https://storage.googleapis.com/golang/go1.3.3.linux-amd64.tar.gz": "14068fbe349db34b838853a7878621bbd2b24646", 216 | "https://storage.googleapis.com/golang/go1.3.3.windows-386.zip": "ba99083b22e0b22b560bb2d28b9b99b405d01b6b", 217 | "https://storage.googleapis.com/golang/go1.3.3.windows-386.msi": "6017a0e1667a5a41109f527b405bf6e0c83580f5", 218 | "https://storage.googleapis.com/golang/go1.3.3.windows-amd64.zip": "5f0b3b104d3db09edd32ef1d086ba20bafe01ada", 219 | "https://storage.googleapis.com/golang/go1.3.3.windows-amd64.msi": "25112a8c4df93dc4009e65eff00bc4ef76f94e46", 220 | "https://storage.googleapis.com/golang/go1.3.2.src.tar.gz": "67d3a692588c259f9fe9dca5b80109e5b99271df", 221 | "https://storage.googleapis.com/golang/go1.3.2.darwin-386-osx10.6.tar.gz": "d1652f6e0ed3063b7b43d2bc12981d927bc85deb", 222 | "https://storage.googleapis.com/golang/go1.3.2.darwin-386-osx10.8.tar.gz": "d040c85698c749fdbe25e8568c4d71648a5e3a75", 223 | "https://storage.googleapis.com/golang/go1.3.2.darwin-386-osx10.6.pkg": "d20375615cf8e36e3c9a9b6ddeef16eff7a4ea89", 224 | "https://storage.googleapis.com/golang/go1.3.2.darwin-386-osx10.8.pkg": "f11930cfb032d39ab445f342742865c93c60ec14", 225 | "https://storage.googleapis.com/golang/go1.3.2.darwin-amd64-osx10.6.tar.gz": "36ca7e8ac9af12e70b1e01182c7ffc732ff3b876", 226 | "https://storage.googleapis.com/golang/go1.3.2.darwin-amd64-osx10.8.tar.gz": "323bf8088614d58fee2b4d2cb07d837063d7d77e", 227 | "https://storage.googleapis.com/golang/go1.3.2.darwin-amd64-osx10.6.pkg": "e1529241fcef643e5f752c37dc4c86911df91338", 228 | "https://storage.googleapis.com/golang/go1.3.2.darwin-amd64-osx10.8.pkg": "fd8637658fcb133423e794c44029ce3476b48e0c", 229 | "https://storage.googleapis.com/golang/go1.3.2.freebsd-386.tar.gz": "fea3ef264120b5c3b4c50a8929d56f47a8366503", 230 | "https://storage.googleapis.com/golang/go1.3.2.freebsd-amd64.tar.gz": "95b633f45156fbbe79076638f854e76b9cd01301", 231 | "https://storage.googleapis.com/golang/go1.3.2.linux-386.tar.gz": "3cbfd62d401a6ca70779856fa8ad8c4d6c35c8cc", 232 | "https://storage.googleapis.com/golang/go1.3.2.linux-amd64.tar.gz": "0e4b6120eee6d45e2e4374dac4fe7607df4cbe42", 233 | "https://storage.googleapis.com/golang/go1.3.2.windows-386.zip": "86160c478436253f51241ac1905577d337577ce0", 234 | "https://storage.googleapis.com/golang/go1.3.2.windows-386.msi": "589c35f9ad3506c92aa944130f6a950ce9ee558b", 235 | "https://storage.googleapis.com/golang/go1.3.2.windows-amd64.zip": "7f7147484b1bc9e52cf034de816146977d0137f6", 236 | "https://storage.googleapis.com/golang/go1.3.2.windows-amd64.msi": "a697fff05cbd4a4d902f6c33f7c42588bcc474bc", 237 | "https://storage.googleapis.com/golang/go1.3.1.src.tar.gz": "bc296c9c305bacfbd7bff9e1b54f6f66ae421e6e", 238 | "https://storage.googleapis.com/golang/go1.3.1.darwin-386-osx10.6.tar.gz": "84f70a4c83be24cea696654a5b55331ea32f8a3f", 239 | "https://storage.googleapis.com/golang/go1.3.1.darwin-386-osx10.8.tar.gz": "244dfba1f4239b8e2eb9c3abae5ad63fc32c807a", 240 | "https://storage.googleapis.com/golang/go1.3.1.darwin-386-osx10.6.pkg": "16e0df7b90d49c8499f71a551af8b595e2faa961", 241 | "https://storage.googleapis.com/golang/go1.3.1.darwin-386-osx10.8.pkg": "13296cd9a980819bf2304d7d24a38a1b39719c13", 242 | "https://storage.googleapis.com/golang/go1.3.1.darwin-amd64-osx10.6.tar.gz": "40716361d352c4b40252e79048e8bc084c3f3d1b", 243 | "https://storage.googleapis.com/golang/go1.3.1.darwin-amd64-osx10.8.tar.gz": "a7271cbdc25173d0f8da66549258ff65cca4bf06", 244 | "https://storage.googleapis.com/golang/go1.3.1.darwin-amd64-osx10.6.pkg": "49bf5f14d2683fb99161fcb7025af60ec2d3691f", 245 | "https://storage.googleapis.com/golang/go1.3.1.darwin-amd64-osx10.8.pkg": "5d4728e0b3c3fd9fc657cc192c6b9fb3f837823b", 246 | "https://storage.googleapis.com/golang/go1.3.1.freebsd-386.tar.gz": "586debe95542b3b56841f6bd2e5257e301a1ffdc", 247 | "https://storage.googleapis.com/golang/go1.3.1.freebsd-amd64.tar.gz": "99e23fdd33860d837912e8647ed2a4b3d2b09d3c", 248 | "https://storage.googleapis.com/golang/go1.3.1.linux-386.tar.gz": "36f87ce21cdb4cb8920bb706003d8655b4e1fc81", 249 | "https://storage.googleapis.com/golang/go1.3.1.linux-amd64.tar.gz": "3af011cc19b21c7180f2604fd85fbc4ddde97143", 250 | "https://storage.googleapis.com/golang/go1.3.1.windows-386.zip": "64f99e40e79e93a622e73d7d55a5b8340f07747f", 251 | "https://storage.googleapis.com/golang/go1.3.1.windows-386.msi": "df37e307c52fbea02070e23ae0a49cb869d54f33", 252 | "https://storage.googleapis.com/golang/go1.3.1.windows-amd64.zip": "4548785cfa3bc228d18d2d06e39f58f0e4e014f1", 253 | "https://storage.googleapis.com/golang/go1.3.1.windows-amd64.msi": "88c5d9a51a74c2846226a08681fc28cd3469cba0", 254 | "https://storage.googleapis.com/golang/go1.3.src.tar.gz": "9f9dfcbcb4fa126b2b66c0830dc733215f2f056e", 255 | "https://storage.googleapis.com/golang/go1.3.darwin-386-osx10.6.tar.gz": "159d2797bee603a80b829c4404c1fb2ee089cc00", 256 | "https://storage.googleapis.com/golang/go1.3.darwin-386-osx10.8.tar.gz": "bade975462b5610781f6a9fe8ac13031b3fb7aa6", 257 | "https://storage.googleapis.com/golang/go1.3.darwin-386-osx10.6.pkg": "07e7142540558f432a8750eb6cb25d6b06ed80bb", 258 | "https://storage.googleapis.com/golang/go1.3.darwin-386-osx10.8.pkg": "c908ecdb177c8a20abd61272c260b15e513f6e73", 259 | "https://storage.googleapis.com/golang/go1.3.darwin-amd64-osx10.6.tar.gz": "82ffcfb7962ca7114a1ee0a96cac51c53061ea05", 260 | "https://storage.googleapis.com/golang/go1.3.darwin-amd64-osx10.8.tar.gz": "8d768f10cd00e0b152490291d9cd6179a8ccf0a7", 261 | "https://storage.googleapis.com/golang/go1.3.darwin-amd64-osx10.6.pkg": "631d6867d7f4b92b314fd87115e1cefadeeac2ab", 262 | "https://storage.googleapis.com/golang/go1.3.darwin-amd64-osx10.8.pkg": "4e8f2cafa23797211fd13f3fa4893ce3d5f084c4", 263 | "https://storage.googleapis.com/golang/go1.3.freebsd-386.tar.gz": "8afa9574140cdd5fc97883a06a11af766e7f0203", 264 | "https://storage.googleapis.com/golang/go1.3.freebsd-amd64.tar.gz": "71214bafabe2b5f52ee68afce96110031b446f0c", 265 | "https://storage.googleapis.com/golang/go1.3.linux-386.tar.gz": "22db33b0c4e242ed18a77b03a60582f8014fd8a6", 266 | "https://storage.googleapis.com/golang/go1.3.linux-amd64.tar.gz": "b6b154933039987056ac307e20c25fa508a06ba6", 267 | "https://storage.googleapis.com/golang/go1.3.windows-386.zip": "e4e5279ce7d8cafdf210a522a70677d5b9c7589d", 268 | "https://storage.googleapis.com/golang/go1.3.windows-386.msi": "d457a86ce6701bb96608e4c33778b8471c48a764", 269 | "https://storage.googleapis.com/golang/go1.3.windows-amd64.zip": "1e4888e1494aed7f6934acb5c4a1ffb0e9a022b1", 270 | "https://storage.googleapis.com/golang/go1.3.windows-amd64.msi": "e81a0e4f551722c7682f912e0485ad20a287f2ef", 271 | "https://storage.googleapis.com/golang/go1.2.2.src.tar.gz": "3ce0ac4db434fc1546fec074841ff40dc48c1167", 272 | "https://storage.googleapis.com/golang/go1.2.2.darwin-386-osx10.6.tar.gz": "360ec6cbfdec9257de029f918a881b9944718d7c", 273 | "https://storage.googleapis.com/golang/go1.2.2.darwin-386-osx10.8.tar.gz": "4219b464e82e7c23d9dc02c193e7a0a28a09af1a", 274 | "https://storage.googleapis.com/golang/go1.2.2.darwin-386-osx10.6.pkg": "dff27e94c8ff25301cd958b0b1b629e97ea21f03", 275 | "https://storage.googleapis.com/golang/go1.2.2.darwin-386-osx10.8.pkg": "f1fb44aa22cba3e81dc33f88393a54e49eae0d8b", 276 | "https://storage.googleapis.com/golang/go1.2.2.darwin-amd64-osx10.6.tar.gz": "24c182718fd61b2621692dcdfc34937a6b5ee369", 277 | "https://storage.googleapis.com/golang/go1.2.2.darwin-amd64-osx10.8.tar.gz": "19be1eca8fc01b32bb6588a70773b84cdce6bed1", 278 | "https://storage.googleapis.com/golang/go1.2.2.darwin-amd64-osx10.6.pkg": "2d4b49f1105a78e1ea31d7f9ea0b43909cc209be", 279 | "https://storage.googleapis.com/golang/go1.2.2.darwin-amd64-osx10.8.pkg": "5d78f2a3fe82b01fe5dfcb267e703e754274b253", 280 | "https://storage.googleapis.com/golang/go1.2.2.freebsd-386.tar.gz": "d226b8e1c3f75d31fa426df63aa776d7e08cddac", 281 | "https://storage.googleapis.com/golang/go1.2.2.freebsd-amd64.tar.gz": "858744ab8ff9661d42940486af63d451853914a0", 282 | "https://storage.googleapis.com/golang/go1.2.2.linux-386.tar.gz": "d16f892173b0589945d141cefb22adce57e3be9c", 283 | "https://storage.googleapis.com/golang/go1.2.2.linux-amd64.tar.gz": "6bd151ca49c435462c8bf019477a6244b958ebb5", 284 | "https://storage.googleapis.com/golang/go1.2.2.windows-386.zip": "560bb33ec70ab733f31ff15f1a48fe35963983b9", 285 | "https://storage.googleapis.com/golang/go1.2.2.windows-386.msi": "60b91a7bf68596b23978acb109d1ff8668b7d18f", 286 | "https://storage.googleapis.com/golang/go1.2.2.windows-amd64.zip": "9ee22fe6c4d98124d582046aab465ab69eaab048", 287 | "https://storage.googleapis.com/golang/go1.2.2.windows-amd64.msi": "c8f5629bc8d91b161840b4a05a3043c6e5fa310b", 288 | "https://storage.googleapis.com/golang/go1.4rc2.src.tar.gz": "270afd320c0b8e3bfa6f5e3b09e61a3917489494", 289 | "https://storage.googleapis.com/golang/go1.4rc2.darwin-386-osx10.6.tar.gz": "98c80b0c30b5ccb48e02b5ed0d4c5047db82fa4f", 290 | "https://storage.googleapis.com/golang/go1.4rc2.darwin-386-osx10.8.tar.gz": "e6b6f970f4c487c256e35e1aa1bd7c4ff0f74cf3", 291 | "https://storage.googleapis.com/golang/go1.4rc2.darwin-386-osx10.6.pkg": "40edb3f2000a3dba73062545794fa3f709c827ad", 292 | "https://storage.googleapis.com/golang/go1.4rc2.darwin-386-osx10.8.pkg": "d8d445c949dae30e29a95de94eadc8758d48061a", 293 | "https://storage.googleapis.com/golang/go1.4rc2.darwin-amd64-osx10.6.tar.gz": "1a8649c1cd13c13dc7820ae02ee2abac3856dd70", 294 | "https://storage.googleapis.com/golang/go1.4rc2.darwin-amd64-osx10.8.tar.gz": "3769cc4a72cff59f3a3ce3a9d6309d999a749093", 295 | "https://storage.googleapis.com/golang/go1.4rc2.darwin-amd64-osx10.6.pkg": "9e6e6215cb961dd9d479785299f26fdf79d20970", 296 | "https://storage.googleapis.com/golang/go1.4rc2.darwin-amd64-osx10.8.pkg": "ee6aeec11be5ac15b0206c74293f1173bb3c7a14", 297 | "https://storage.googleapis.com/golang/go1.4rc2.freebsd-386.tar.gz": "a9ebddfce542d82fdd9f27747ec5d5465470ef5d", 298 | "https://storage.googleapis.com/golang/go1.4rc2.freebsd-amd64.tar.gz": "3f39c414c006baefaa5ac29f0cd7614e6bb010f3", 299 | "https://storage.googleapis.com/golang/go1.4rc2.linux-386.tar.gz": "04d2c1d744ebb419738f4a2543621eee46223d91", 300 | "https://storage.googleapis.com/golang/go1.4rc2.linux-amd64.tar.gz": "950f74edbee7e55f4ca5760c19f51fe24de8d05f", 301 | "https://storage.googleapis.com/golang/go1.4rc2.windows-386.zip": "8be5274637c7b6d0b1305fc09c5ed5dcf5e58188", 302 | "https://storage.googleapis.com/golang/go1.4rc2.windows-386.msi": "6571af0e97947412f449571dd143a0f3d9827f9f", 303 | "https://storage.googleapis.com/golang/go1.4rc2.windows-amd64.zip": "6c37566760d6ca0482a9840d69578bc45e6029da", 304 | "https://storage.googleapis.com/golang/go1.4rc2.windows-amd64.msi": "6ac7e8d99e58be1ba63be1330e1b4e7e438f14b7", 305 | "https://storage.googleapis.com/golang/go1.4rc1.src.tar.gz": "ff8e7d78e85658251a36e45f944af70f226368ab", 306 | "https://storage.googleapis.com/golang/go1.4rc1.darwin-386-osx10.6.tar.gz": "127565a471073b4872745583a215f9c89a740686", 307 | "https://storage.googleapis.com/golang/go1.4rc1.darwin-386-osx10.8.tar.gz": "f3d610c65a078f59fff46a00b34297d05b36aacd", 308 | "https://storage.googleapis.com/golang/go1.4rc1.darwin-386-osx10.6.pkg": "7496637aec61a824398f142e23faa283ab0caa45", 309 | "https://storage.googleapis.com/golang/go1.4rc1.darwin-386-osx10.8.pkg": "1c0f99bdfc82a8333ada5916b38d6d71387d282b", 310 | "https://storage.googleapis.com/golang/go1.4rc1.darwin-amd64-osx10.6.tar.gz": "ae22ddca700ec2ef1f4933f5e13dbdd5149cc6c3", 311 | "https://storage.googleapis.com/golang/go1.4rc1.darwin-amd64-osx10.8.tar.gz": "957b9d55f696bd078e74969bc1d1b43ca545069a", 312 | "https://storage.googleapis.com/golang/go1.4rc1.darwin-amd64-osx10.6.pkg": "31be1cdb25a9707291cad9900fc020c57587a0be", 313 | "https://storage.googleapis.com/golang/go1.4rc1.darwin-amd64-osx10.8.pkg": "2f663489724a2716da5d1b77121ea171ea7a3a50", 314 | "https://storage.googleapis.com/golang/go1.4rc1.freebsd-386.tar.gz": "481b6b74314a4cafade7366c1d2722e0e0fe401d", 315 | "https://storage.googleapis.com/golang/go1.4rc1.freebsd-amd64.tar.gz": "87e10516627da751842f33a1acb34d5eb07d48a7", 316 | "https://storage.googleapis.com/golang/go1.4rc1.linux-386.tar.gz": "77299d1791a68f7da816bde7d7dfef1cbfff71e3", 317 | "https://storage.googleapis.com/golang/go1.4rc1.linux-amd64.tar.gz": "a5217681e47dd1a276c66ee31ded66e7bf4d41b7", 318 | "https://storage.googleapis.com/golang/go1.4rc1.windows-386.zip": "c50859276251ac464c0a7453a8dc3b84c863cf4e", 319 | "https://storage.googleapis.com/golang/go1.4rc1.windows-386.msi": "d36340f876d0180a81fbb8dc73258cff4650fe67", 320 | "https://storage.googleapis.com/golang/go1.4rc1.windows-amd64.zip": "b8276e8e8f4a2134b9a1533a9b1b6f3fe797a579", 321 | "https://storage.googleapis.com/golang/go1.4rc1.windows-amd64.msi": "4d97adbc6a3f6ae33a89a1cf63ad60e85b0733e3", 322 | "https://storage.googleapis.com/golang/go1.4beta1.src.tar.gz": "f2fece0c9f9cdc6e8a85ab56b7f1ffcb57c3e7cd", 323 | "https://storage.googleapis.com/golang/go1.4beta1.darwin-386-osx10.6.tar.gz": "a360e7c8f1d528901e721d0cc716461f8a636823", 324 | "https://storage.googleapis.com/golang/go1.4beta1.darwin-386-osx10.8.tar.gz": "d863907870e8e79850a7a725b398502afd1163d8", 325 | "https://storage.googleapis.com/golang/go1.4beta1.darwin-386-osx10.6.pkg": "ee4d1f74c35eddbdc49e9fb01e86a971e1bb54a7", 326 | "https://storage.googleapis.com/golang/go1.4beta1.darwin-386-osx10.8.pkg": "c118f624262a1720317105d116651f8fb4b80383", 327 | "https://storage.googleapis.com/golang/go1.4beta1.darwin-amd64-osx10.6.tar.gz": "ad8798fe744bb119f0e8eeacf97be89763c5f12a", 328 | "https://storage.googleapis.com/golang/go1.4beta1.darwin-amd64-osx10.8.tar.gz": "e08df216d9761c970e438295129721ec8374654a", 329 | "https://storage.googleapis.com/golang/go1.4beta1.darwin-amd64-osx10.6.pkg": "831e95cc381cc1afd6c4bfa886e86790f1c96de6", 330 | "https://storage.googleapis.com/golang/go1.4beta1.darwin-amd64-osx10.8.pkg": "dc0b5805ba117654dd95c84ce7872406380de3d5", 331 | "https://storage.googleapis.com/golang/go1.4beta1.freebsd-386.tar.gz": "65045b7a5d2a991a45b1e86ad11252bc84043651", 332 | "https://storage.googleapis.com/golang/go1.4beta1.freebsd-amd64.tar.gz": "42fbd5336437dde85b34d774bfed111fe579db88", 333 | "https://storage.googleapis.com/golang/go1.4beta1.linux-386.tar.gz": "122ea6cae37d9b62c69efa3e21cc228e41006b75", 334 | "https://storage.googleapis.com/golang/go1.4beta1.linux-amd64.tar.gz": "d2712acdaa4469ce2dc57c112a70900667269ca0", 335 | "https://storage.googleapis.com/golang/go1.4beta1.windows-386.zip": "a6d75ca59b70226087104b514389e48d49854ed4", 336 | "https://storage.googleapis.com/golang/go1.4beta1.windows-386.msi": "1f8d11306d733bec975f2d747b26810926348517", 337 | "https://storage.googleapis.com/golang/go1.4beta1.windows-amd64.zip": "386deea0a7c384178aedfe48e4ee2558a8cd43d8", 338 | "https://storage.googleapis.com/golang/go1.4beta1.windows-amd64.msi": "ec3ec78072128d725878404a5ce27bd1c1e7132b", 339 | "https://storage.googleapis.com/golang/go1.3rc2.src.tar.gz": "53a5b75c8bb2399c36ed8fe14f64bd2df34ca4d9", 340 | "https://storage.googleapis.com/golang/go1.3rc2.darwin-386-osx10.6.tar.gz": "600433eccda28b91b2afe566142bce759d154b49", 341 | "https://storage.googleapis.com/golang/go1.3rc2.darwin-386-osx10.8.tar.gz": "36fa30bfdeb8560c5d9ae57f02ec0cdb33613cb5", 342 | "https://storage.googleapis.com/golang/go1.3rc2.darwin-386-osx10.6.pkg": "c41c4e55017d3d835cf66feaaf18eeaeabaa066a", 343 | "https://storage.googleapis.com/golang/go1.3rc2.darwin-386-osx10.8.pkg": "4cbdcccac38eed1ebffbbf1eba594724e5d05a77", 344 | "https://storage.googleapis.com/golang/go1.3rc2.darwin-amd64-osx10.6.tar.gz": "84c25957096d4700f342c10f82f1f720bf646f6e", 345 | "https://storage.googleapis.com/golang/go1.3rc2.darwin-amd64-osx10.8.tar.gz": "1bd241130b5e7a3eb4876fbb17257b16ea9db67d", 346 | "https://storage.googleapis.com/golang/go1.3rc2.darwin-amd64-osx10.6.pkg": "18c8ed409a7ba97a61e00f00982361d7c84f7fdb", 347 | "https://storage.googleapis.com/golang/go1.3rc2.darwin-amd64-osx10.8.pkg": "33d2129c26ea0cb5f147fa5f24395c8ed5c4433f", 348 | "https://storage.googleapis.com/golang/go1.3rc2.freebsd-386.tar.gz": "3e5394e0f4eb99c32510dda48eb4dc1af9717a41", 349 | "https://storage.googleapis.com/golang/go1.3rc2.freebsd-amd64.tar.gz": "bbaba53742cf43d96abca710cf49fe8c0ede6673", 350 | "https://storage.googleapis.com/golang/go1.3rc2.linux-386.tar.gz": "7462cb654712ef6785ccae5b75ed393de6f49da2", 351 | "https://storage.googleapis.com/golang/go1.3rc2.linux-amd64.tar.gz": "3a7d86a3245b4c8bd4dc5b1ff4e0073c2d1b81b5", 352 | "https://storage.googleapis.com/golang/go1.3rc2.windows-386.zip": "f3f7a995baf77742b813723bc823d584466cb26f", 353 | "https://storage.googleapis.com/golang/go1.3rc2.windows-386.msi": "a1138a6f7d22768eac73dfb254a1af8531aaeb1b", 354 | "https://storage.googleapis.com/golang/go1.3rc2.windows-amd64.zip": "607b6ed4830785d166d83029a76e6975b2e99068", 355 | "https://storage.googleapis.com/golang/go1.3rc2.windows-amd64.msi": "dba588d51f9b9353c7bdc271cecea065eea06250", 356 | "https://storage.googleapis.com/golang/go1.3rc1.src.tar.gz": "6a9dac2e65c07627fe51899e0031e298560b0097", 357 | "https://storage.googleapis.com/golang/go1.3rc1.darwin-386-osx10.6.tar.gz": "a15031c21871d9ffb567c7d204653b32f0d84737", 358 | "https://storage.googleapis.com/golang/go1.3rc1.darwin-386-osx10.8.tar.gz": "7ace88dfe731c38e83cee27f23eb2588419cf249", 359 | "https://storage.googleapis.com/golang/go1.3rc1.darwin-386-osx10.6.pkg": "8de0f308c51cec5fcec45fde762967723ef61eb9", 360 | "https://storage.googleapis.com/golang/go1.3rc1.darwin-386-osx10.8.pkg": "498e0840c44258e6b29eb5aa34b2fb3c31e79fdd", 361 | "https://storage.googleapis.com/golang/go1.3rc1.darwin-amd64-osx10.6.tar.gz": "d250f20d84c310aa82053dea16743b223bbf933a", 362 | "https://storage.googleapis.com/golang/go1.3rc1.darwin-amd64-osx10.8.tar.gz": "e3fb91fcfa2dfa97e451de9048ec5788713bc94e", 363 | "https://storage.googleapis.com/golang/go1.3rc1.darwin-amd64-osx10.6.pkg": "02e6537c9a3f0cc80dcf901b40683eeab6d8bebf", 364 | "https://storage.googleapis.com/golang/go1.3rc1.darwin-amd64-osx10.8.pkg": "17c42d6e6b5ca99fcd1e6927a79652d7e630a226", 365 | "https://storage.googleapis.com/golang/go1.3rc1.freebsd-386.tar.gz": "953a95277ef06da98f0b8d7bb9bd02f4846374ff", 366 | "https://storage.googleapis.com/golang/go1.3rc1.freebsd-amd64.tar.gz": "3c0a03ee5a64f6db46298fa3ad26d577ef7b2db5", 367 | "https://storage.googleapis.com/golang/go1.3rc1.linux-386.tar.gz": "07c656173c444e4373a799141c1cb28128a345eb", 368 | "https://storage.googleapis.com/golang/go1.3rc1.linux-amd64.tar.gz": "affaccfd69a694e0aa59466450e4db5260aeb1a3", 369 | "https://storage.googleapis.com/golang/go1.3rc1.windows-386.zip": "d43c973adede9e8f18118a2924d8b825352db50a", 370 | "https://storage.googleapis.com/golang/go1.3rc1.windows-386.msi": "23534cce0db1f8c0cc0cf0f70472df59ac26bbfa", 371 | "https://storage.googleapis.com/golang/go1.3rc1.windows-amd64.zip": "312358b64711fd827f9dfb0cef61383f9eb5057b", 372 | "https://storage.googleapis.com/golang/go1.3rc1.windows-amd64.msi": "d089fbe3c12b8ec8d3e30526b3eb604c9ae84c7d", 373 | } 374 | -------------------------------------------------------------------------------- /unpack.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "archive/tar" 5 | "archive/zip" 6 | "compress/gzip" 7 | "fmt" 8 | "io" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | ) 13 | 14 | func unpackFile(dest string, r io.Reader, fname string, mode os.FileMode) error { 15 | dir, name := filepath.Split(fname) 16 | dirPath := filepath.Join(dest, dir) 17 | filePath := filepath.Join(dirPath, name) 18 | if !strings.HasPrefix(filePath, dest) { 19 | return fmt.Errorf("%q is outside of %s", fname, dest) 20 | } 21 | if err := os.MkdirAll(dirPath, 0755); err != nil { 22 | return err 23 | } 24 | f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) 25 | if err != nil { 26 | return err 27 | } 28 | defer f.Close() 29 | _, err = io.Copy(f, r) 30 | return err 31 | } 32 | 33 | func unpackTarGz(dest string, r *os.File) error { 34 | gr, err := gzip.NewReader(r) 35 | if err != nil { 36 | return err 37 | } 38 | tr := tar.NewReader(gr) 39 | for { 40 | f, err := tr.Next() 41 | if err != nil { 42 | if err == io.EOF { 43 | err = nil 44 | } 45 | return err 46 | } 47 | if f.Typeflag != tar.TypeReg && f.Typeflag != tar.TypeReg { 48 | continue 49 | } 50 | if err := unpackFile(dest, tr, f.Name, os.FileMode(f.Mode)); err != nil { 51 | return err 52 | } 53 | } 54 | } 55 | 56 | func unpackZip(dest string, r *os.File) error { 57 | stat, err := r.Stat() 58 | if err != nil { 59 | return err 60 | } 61 | zr, err := zip.NewReader(r, stat.Size()) 62 | if err != nil { 63 | return err 64 | } 65 | for _, f := range zr.File { 66 | if strings.HasSuffix(f.Name, "/") { 67 | continue 68 | } 69 | fr, err := f.Open() 70 | if err != nil { 71 | return err 72 | } 73 | err = unpackFile(dest, fr, f.Name, f.Mode()) 74 | fr.Close() 75 | if err != nil { 76 | return err 77 | } 78 | } 79 | return nil 80 | } 81 | --------------------------------------------------------------------------------