<commit_before>// Copyright 2015 The Vanadium 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 go_profile

import (
	"crypto/sha256"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"v.io/jiri"
	"v.io/jiri/collect"
	"v.io/jiri/gitutil"
	"v.io/jiri/profiles"
	"v.io/jiri/profiles/profilesmanager"
	"v.io/jiri/profiles/profilesutil"
	"v.io/jiri/project"
	"v.io/x/lib/envvar"
)

const (
	go15GitRemote = "https://github.com/golang/go.git"
)

var (
	goSysrootFlag        = ""
	goSysrootDirs        = ""
	defaultGoSysrootDirs = "/lib:/usr/lib:/usr/include"
)

// Supported cross compilation toolchains.
type xspec struct{ arch, os string }
type xbuilder func(*jiri.X, *Manager, jiri.RelPath, profiles.Target, profiles.Action) (bindir string, env []string, e error)

var xcompilers = map[xspec]map[xspec]xbuilder{
	xspec{"amd64", "darwin"}: {
		xspec{"amd64", "linux"}:   darwin_to_linux,
		xspec{"arm", "linux"}:     darwin_to_linux,
		xspec{"arm", "android"}:   to_android,
		xspec{"amd64", "android"}: to_android,
		xspec{"arm", "ios"}:       darwin_to_ios,
		xspec{"arm64", "ios"}:     darwin_to_ios,
		xspec{"386", "ios"}:       darwin_to_ios, // iOS simulator
		xspec{"amd64", "ios"}:     darwin_to_ios, // iOS simulator
	},
	xspec{"amd64", "linux"}: {
		xspec{"amd64", "fnl"}:     to_fnl,
		xspec{"arm", "linux"}:     linux_to_linux,
		xspec{"arm", "android"}:   to_android,
		xspec{"amd64", "android"}: to_android,
	},
}

type versionSpec struct {
	gitRevision string
	patchFiles  []string
}

// goRelease enables installation of specific release versions of the Go toolchain.
type goRelease struct {
	file   string
	sha256 string
}

func newGoRelease(version string) *goRelease {
	arch := runtime.GOARCH
	if arch == "arm" {
		arch = "armv6l"
	}
	file := fmt.Sprintf("go%s.%s-%s.tar.gz", version, runtime.GOOS, arch)
	// From: https://golang.org/dl/
	shamap := map[string]string{
		"go1.6.darwin-amd64.tar.gz":  "8b686ace24c0166738fd9f6003503f9d55ce03b7f24c963b043ba7bb56f43000",
		"go1.6.freebsd-386.tar.gz":   "67f0278e0650b303156adbfe012317b9ce75396e3a28cbc0a8210284bb07ab85",
		"go1.6.freebsd-amd64.tar.gz": "3763015cdc7971e10f90fb5bec80d885e9956f836277dcb35a2166ffbd7af9b5",
		"go1.6.linux-386.tar.gz":     "7a240a0f45e559d47ea07319d9faf838225eb9e18174f56a76ccaf9860dbb9b1",
		"go1.6.linux-amd64.tar.gz":   "5470eac05d273c74ff8bac7bef5bad0b5abbd1c4052efbdbc8db45332e836b0b",
		"go1.6.linux-armv6l.tar.gz":  "c6c1859acd3727f23f900bde855b5fd0f74d36b1d10f6dd7beddebfb57513d0b",
		"go1.6.windows-386.zip":      "ac41a46f44d0ea5b83ad7e6a55ee1d58c6a01b7ab7342e243f232510342f16f0",
		"go1.6.windows-amd64.zip":    "1be06afa469666d636a00928755c4bcd6403a01f5761946b2b13b8a664f86bac",
	}
	sha256, ok := shamap[file]
	if !ok {
		return nil
	}
	return &goRelease{file, sha256}
}

func (g *goRelease) install(jirix *jiri.X, dir string) error {
	s := jirix.NewSeq()
	tmpDir, err := s.TempDir("", "")
	if err != nil {
		return err
	}
	defer jirix.NewSeq().RemoveAll(tmpDir)
	local := filepath.Join(tmpDir, g.file)
	remote := "https://storage.googleapis.com/golang/" + g.file
	if err := profilesutil.Fetch(jirix, local, remote); err != nil {
		return err
	}
	// Verify the checksum of the downloaded file
	csum := sha256.New()
	file, err := os.Open(local)
	if err != nil {
		return err
	}
	defer file.Close()
	if _, err := io.Copy(csum, file); err != nil {
		return fmt.Errorf("failed to checksum %v: %v", file, err)
	}
	file.Close()
	if got, want := hex.EncodeToString(csum.Sum(nil)), g.sha256; got != want {
		return fmt.Errorf("checksum mismatch in download of %q. Got %v, want %v", remote, got, want)
	}
	if strings.HasSuffix(local, ".zip") {
		if err := profilesutil.Unzip(jirix, local, tmpDir); err != nil {
			return err
		}
	} else {
		s = s.Run("tar", "-C", tmpDir, "-xzf", local)
	}
	if err := s.Remove(local).
		MkdirAll(filepath.Dir(dir), profilesutil.DefaultDirPerm).
		Rename(filepath.Join(tmpDir, "go"), dir).
		Done(); err != nil {
		return err
	}
	return nil
}

func Register(installer, profile string) {
	m := &Manager{
		profileInstaller: installer,
		profileName:      profile,
		qualifiedName:    profiles.QualifiedProfileName(installer, profile),
		versionInfo: profiles.NewVersionInfo(profile, map[string]interface{}{
			"1.5": &versionSpec{
				"cc6554f750ccaf63bcdcc478b2a60d71ca76d342", nil},
			"1.5.1": &versionSpec{
				"f2e4c8b5fb3660d793b2c545ef207153db0a34b1", nil},
			// 1.5.1 at a specific git revision, create a new version anytime
			// a new profile is checked in.
			"1.5.1.1:2738c5e0": &versionSpec{
				"492a62e945555bbf94a6f9dd6d430f712738c5e0", nil},
			// 1.5.2 with x86_64 android support
			"1.5.2.1:56093743": &versionSpec{
				"560937434d5f2857bb69e0a6881a38201a197a8d", nil},
			"1.6": &versionSpec{
				"e805bf39458915365924228dc53969ce04e32813", nil},
		}, "1.6"),
	}
	profilesmanager.Register(m)
}

type Manager struct {
	profileInstaller, profileName, qualifiedName string
	root, goRoot                                 jiri.RelPath
	versionInfo                                  *profiles.VersionInfo
	spec                                         versionSpec
}

func (m Manager) Name() string {
	return m.profileName
}

func (m Manager) Installer() string {
	return m.profileInstaller
}

func (m Manager) String() string {
	return fmt.Sprintf("%s[%s]", m.qualifiedName, m.versionInfo.Default())
}

func (m Manager) Info() string {
	return `
The go profile manages installations of the go compiler and in particular configures
them for cross compilation with cgo. A separate build of each cross-compilation
environment is maintained to simplify use of cgo albeit at the cost of some disk space.`
}

func (m Manager) VersionInfo() *profiles.VersionInfo {
	return m.versionInfo
}

func (m *Manager) AddFlags(flags *flag.FlagSet, action profiles.Action) {
	flags.StringVar(&goSysrootFlag, m.profileName+".sysroot-image", "", "sysroot image for cross compiling to the currently specified target")
	flags.StringVar(&goSysrootDirs, m.profileName+".sysroot-image-dirs-to-use", defaultGoSysrootDirs, "a colon separated list of directories to use from the sysroot image")
}

func (m *Manager) initForTarget(jirix *jiri.X, root jiri.RelPath, target *profiles.Target) error {
	m.root = root
	m.goRoot = root.Join("go")
	if err := m.versionInfo.Lookup(target.Version(), &m.spec); err != nil {
		return err
	}
	if jirix.Verbose() {
		fmt.Fprintf(jirix.Stdout(), "Go Profiles: %s\n", m.goRoot)
	}
	return nil
}

func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
	if err := m.initForTarget(jirix, root, &target); err != nil {
		return err
	}

	jirix.NewSeq().RemoveAll(m.goRoot.Join("go-bootstrap").Abs(jirix))
	cgo := true
	if target.CrossCompiling() {
		// We may need to install an additional cross compilation toolchain
		// for cgo to work.
		if builder := xcompilers[xspec{runtime.GOARCH, runtime.GOOS}][xspec{target.Arch(), target.OS()}]; builder != nil {
			_, vars, err := builder(jirix, m, root, target, profiles.Install)
			if err != nil {
				return err
			}
			target.Env.Vars = vars
		} else if runtime.GOARCH == "amd64" && runtime.GOOS == "linux" && target.Arch() == "386" && target.OS() == "linux" {
			// CGO for 386-linux works on amd64-linux host without cross-compilation.
			cgo = true
		} else {
			// CGO is not supported.
			cgo = false
		}
	}

	// Set GOARCH, GOOS to the values specified in the target, if not set.
	target.Env.Vars = envvar.MergeSlices([]string{
		"GOARCH=" + target.Arch(),
		"GOOS=" + target.OS(),
	}, target.Env.Vars)
	if cgo {
		target.Env.Vars = append(target.Env.Vars, "CGO_ENABLED=1")
	}

	// If a release version of the go toolchain can be used, use that.
	// This allows multiple target architectures to share a single
	// toolchain installation, saving time and space.
	//
	// A release version cannot be used if:
	// (1) Don't know the URL for obtaining it (newGoRelease returns nil), OR
	// (2) Any patches need to be applied to the toolchain code (m.spec.patchFiles), OR
	// (3) Any custom build flags (set via GO_FLAGS environment variable)
	var goInstDir jiri.RelPath
	env := envvar.VarsFromSlice(target.Env.Vars)
	if release := newGoRelease(target.Version()); release != nil &&
		len(m.spec.patchFiles) == 0 &&
		!env.Contains("GO_FLAGS") {
		// Not using a bootstrapped Go, delete any references from env.
		env.Delete("GOROOT_BOOTSTRAP")
		goInstDir = m.goRoot.Join("shared").Join(target.Version())
		fn := func() error { return release.install(jirix, goInstDir.Abs(jirix)) }
		if err := profilesutil.AtomicAction(jirix, fn, goInstDir.Abs(jirix), "Install a release version of the Go toolchain"); err != nil {
			return err
		}
		// CC_FOR_TARGET and CXX_FOR_TARGET might have been set by the
		// cross-compiler setup above. Those variables only take effect
		// when building a Go toolchain.  Instead set CC and CXX.
		if k := "CC_FOR_TARGET"; env.Contains(k) {
			env.Set("CC", abs2symbolic(jirix, env.Get(k)))
			env.Delete(k)
		}
		if k := "CXX_FOR_TARGET"; env.Contains(k) {
			env.Set("CXX", abs2symbolic(jirix, env.Get(k)))
			env.Delete(k)
		}
		if jirix.Verbose() {
			fmt.Fprintf(jirix.Stdout(), "Using Go toolchain in %v for target %v", goInstDir, target)
		}
	} else {
		if jirix.Verbose() {
			fmt.Fprintf(jirix.Stdout(), "Compiling go toolchain at revision %v", m.spec.gitRevision)
		}
		// Compile our own Go toolchain
		targetDir := m.goRoot.Join(target.TargetSpecificDirname())
		goInstDir = targetDir.Join(m.spec.gitRevision)
		goBootstrapDir := targetDir.Join("go-bootstrap")
		if jirix.Verbose() {
			fmt.Fprintf(jirix.Stdout(), "Go Target+Revision %s\n", goInstDir.Abs(jirix))
		}
		if err := m.installGo15Plus(jirix, goBootstrapDir, goInstDir, target.Version(), env); err != nil {
			return err
		}
	}
	env.Set("GOROOT", goInstDir.Symbolic())
	target.Env.Vars = env.ToSlice()
	target.InstallationDir = string(goInstDir)

	pdb.InstallProfile(m.profileInstaller, m.profileName, string(m.goRoot))
	return pdb.AddProfileTarget(m.profileInstaller, m.profileName, target)
}

func (m *Manager) Uninstall(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
	if err := m.initForTarget(jirix, root, &target); err != nil {
		return err
	}

	if target.CrossCompiling() {
		// We may need to install an additional cross compilation toolchain
		// for cgo to work.
		def := profiles.DefaultTarget()
		if builder := xcompilers[xspec{def.Arch(), def.OS()}][xspec{target.Arch(), target.OS()}]; builder != nil {
			if _, _, err := builder(jirix, m, root, target, profiles.Uninstall); err != nil {
				return err
			}
		}
	}
	s := jirix.NewSeq()
	if err := s.RemoveAll(m.goRoot.Join(target.TargetSpecificDirname()).Abs(jirix)).Done(); err != nil {
		return err
	}
	if pdb.RemoveProfileTarget(m.profileInstaller, m.profileName, target) {
		// If there are no more targets then remove the entire go directory,
		// including the bootstrap one.
		return s.RemoveAll(m.goRoot.Abs(jirix)).Done()
	}
	return nil
}

// installGo14 installs Go 1.4 at a given location, using the provided
// environment during compilation.
func installGo14(jirix *jiri.X, go14Dir string, env *envvar.Vars) error {
	installGo14Fn := func() error {
		s := jirix.NewSeq()
		tmpDir, err := s.TempDir("", "")
		if err != nil {
			return err
		}
		defer jirix.NewSeq().RemoveAll(tmpDir)

		name := "go1.4.2.src.tar.gz"
		remote, local := "https://storage.googleapis.com/golang/"+name, filepath.Join(tmpDir, name)
		parentDir := filepath.Dir(go14Dir)
		goSrcDir := filepath.Join(go14Dir, "src")
		makeBin := filepath.Join(goSrcDir, "make.bash")

		if err := s.Run("curl", "-Lo", local, remote).
			Run("tar", "-C", tmpDir, "-xzf", local).
			Remove(local).
			RemoveAll(go14Dir).
			MkdirAll(parentDir, profilesutil.DefaultDirPerm).Done(); err != nil {<commit_msg>jiri-profile-v23: Use Go 1.4.3 and not 1.4.2 for bootstrapping.<commit_after>// Copyright 2015 The Vanadium 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 go_profile

import (
	"crypto/sha256"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"v.io/jiri"
	"v.io/jiri/collect"
	"v.io/jiri/gitutil"
	"v.io/jiri/profiles"
	"v.io/jiri/profiles/profilesmanager"
	"v.io/jiri/profiles/profilesutil"
	"v.io/jiri/project"
	"v.io/x/lib/envvar"
)

const (
	go15GitRemote = "https://github.com/golang/go.git"
)

var (
	goSysrootFlag        = ""
	goSysrootDirs        = ""
	defaultGoSysrootDirs = "/lib:/usr/lib:/usr/include"
)

// Supported cross compilation toolchains.
type xspec struct{ arch, os string }
type xbuilder func(*jiri.X, *Manager, jiri.RelPath, profiles.Target, profiles.Action) (bindir string, env []string, e error)

var xcompilers = map[xspec]map[xspec]xbuilder{
	xspec{"amd64", "darwin"}: {
		xspec{"amd64", "linux"}:   darwin_to_linux,
		xspec{"arm", "linux"}:     darwin_to_linux,
		xspec{"arm", "android"}:   to_android,
		xspec{"amd64", "android"}: to_android,
		xspec{"arm", "ios"}:       darwin_to_ios,
		xspec{"arm64", "ios"}:     darwin_to_ios,
		xspec{"386", "ios"}:       darwin_to_ios, // iOS simulator
		xspec{"amd64", "ios"}:     darwin_to_ios, // iOS simulator
	},
	xspec{"amd64", "linux"}: {
		xspec{"amd64", "fnl"}:     to_fnl,
		xspec{"arm", "linux"}:     linux_to_linux,
		xspec{"arm", "android"}:   to_android,
		xspec{"amd64", "android"}: to_android,
	},
}

type versionSpec struct {
	gitRevision string
	patchFiles  []string
}

// goRelease enables installation of specific release versions of the Go toolchain.
type goRelease struct {
	file   string
	sha256 string
}

func newGoRelease(version string) *goRelease {
	arch := runtime.GOARCH
	if arch == "arm" {
		arch = "armv6l"
	}
	file := fmt.Sprintf("go%s.%s-%s.tar.gz", version, runtime.GOOS, arch)
	// From: https://golang.org/dl/
	shamap := map[string]string{
		"go1.6.darwin-amd64.tar.gz":  "8b686ace24c0166738fd9f6003503f9d55ce03b7f24c963b043ba7bb56f43000",
		"go1.6.freebsd-386.tar.gz":   "67f0278e0650b303156adbfe012317b9ce75396e3a28cbc0a8210284bb07ab85",
		"go1.6.freebsd-amd64.tar.gz": "3763015cdc7971e10f90fb5bec80d885e9956f836277dcb35a2166ffbd7af9b5",
		"go1.6.linux-386.tar.gz":     "7a240a0f45e559d47ea07319d9faf838225eb9e18174f56a76ccaf9860dbb9b1",
		"go1.6.linux-amd64.tar.gz":   "5470eac05d273c74ff8bac7bef5bad0b5abbd1c4052efbdbc8db45332e836b0b",
		"go1.6.linux-armv6l.tar.gz":  "c6c1859acd3727f23f900bde855b5fd0f74d36b1d10f6dd7beddebfb57513d0b",
		"go1.6.windows-386.zip":      "ac41a46f44d0ea5b83ad7e6a55ee1d58c6a01b7ab7342e243f232510342f16f0",
		"go1.6.windows-amd64.zip":    "1be06afa469666d636a00928755c4bcd6403a01f5761946b2b13b8a664f86bac",
	}
	sha256, ok := shamap[file]
	if !ok {
		return nil
	}
	return &goRelease{file, sha256}
}

func (g *goRelease) install(jirix *jiri.X, dir string) error {
	s := jirix.NewSeq()
	tmpDir, err := s.TempDir("", "")
	if err != nil {
		return err
	}
	defer jirix.NewSeq().RemoveAll(tmpDir)
	local := filepath.Join(tmpDir, g.file)
	remote := "https://storage.googleapis.com/golang/" + g.file
	if err := profilesutil.Fetch(jirix, local, remote); err != nil {
		return err
	}
	// Verify the checksum of the downloaded file
	csum := sha256.New()
	file, err := os.Open(local)
	if err != nil {
		return err
	}
	defer file.Close()
	if _, err := io.Copy(csum, file); err != nil {
		return fmt.Errorf("failed to checksum %v: %v", file, err)
	}
	file.Close()
	if got, want := hex.EncodeToString(csum.Sum(nil)), g.sha256; got != want {
		return fmt.Errorf("checksum mismatch in download of %q. Got %v, want %v", remote, got, want)
	}
	if strings.HasSuffix(local, ".zip") {
		if err := profilesutil.Unzip(jirix, local, tmpDir); err != nil {
			return err
		}
	} else {
		s = s.Run("tar", "-C", tmpDir, "-xzf", local)
	}
	if err := s.Remove(local).
		MkdirAll(filepath.Dir(dir), profilesutil.DefaultDirPerm).
		Rename(filepath.Join(tmpDir, "go"), dir).
		Done(); err != nil {
		return err
	}
	return nil
}

func Register(installer, profile string) {
	m := &Manager{
		profileInstaller: installer,
		profileName:      profile,
		qualifiedName:    profiles.QualifiedProfileName(installer, profile),
		versionInfo: profiles.NewVersionInfo(profile, map[string]interface{}{
			"1.5": &versionSpec{
				"cc6554f750ccaf63bcdcc478b2a60d71ca76d342", nil},
			"1.5.1": &versionSpec{
				"f2e4c8b5fb3660d793b2c545ef207153db0a34b1", nil},
			// 1.5.1 at a specific git revision, create a new version anytime
			// a new profile is checked in.
			"1.5.1.1:2738c5e0": &versionSpec{
				"492a62e945555bbf94a6f9dd6d430f712738c5e0", nil},
			// 1.5.2 with x86_64 android support
			"1.5.2.1:56093743": &versionSpec{
				"560937434d5f2857bb69e0a6881a38201a197a8d", nil},
			"1.6": &versionSpec{
				"e805bf39458915365924228dc53969ce04e32813", nil},
		}, "1.6"),
	}
	profilesmanager.Register(m)
}

type Manager struct {
	profileInstaller, profileName, qualifiedName string
	root, goRoot                                 jiri.RelPath
	versionInfo                                  *profiles.VersionInfo
	spec                                         versionSpec
}

func (m Manager) Name() string {
	return m.profileName
}

func (m Manager) Installer() string {
	return m.profileInstaller
}

func (m Manager) String() string {
	return fmt.Sprintf("%s[%s]", m.qualifiedName, m.versionInfo.Default())
}

func (m Manager) Info() string {
	return `
The go profile manages installations of the go compiler and in particular configures
them for cross compilation with cgo. A separate build of each cross-compilation
environment is maintained to simplify use of cgo albeit at the cost of some disk space.`
}

func (m Manager) VersionInfo() *profiles.VersionInfo {
	return m.versionInfo
}

func (m *Manager) AddFlags(flags *flag.FlagSet, action profiles.Action) {
	flags.StringVar(&goSysrootFlag, m.profileName+".sysroot-image", "", "sysroot image for cross compiling to the currently specified target")
	flags.StringVar(&goSysrootDirs, m.profileName+".sysroot-image-dirs-to-use", defaultGoSysrootDirs, "a colon separated list of directories to use from the sysroot image")
}

func (m *Manager) initForTarget(jirix *jiri.X, root jiri.RelPath, target *profiles.Target) error {
	m.root = root
	m.goRoot = root.Join("go")
	if err := m.versionInfo.Lookup(target.Version(), &m.spec); err != nil {
		return err
	}
	if jirix.Verbose() {
		fmt.Fprintf(jirix.Stdout(), "Go Profiles: %s\n", m.goRoot)
	}
	return nil
}

func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
	if err := m.initForTarget(jirix, root, &target); err != nil {
		return err
	}

	jirix.NewSeq().RemoveAll(m.goRoot.Join("go-bootstrap").Abs(jirix))
	cgo := true
	if target.CrossCompiling() {
		// We may need to install an additional cross compilation toolchain
		// for cgo to work.
		if builder := xcompilers[xspec{runtime.GOARCH, runtime.GOOS}][xspec{target.Arch(), target.OS()}]; builder != nil {
			_, vars, err := builder(jirix, m, root, target, profiles.Install)
			if err != nil {
				return err
			}
			target.Env.Vars = vars
		} else if runtime.GOARCH == "amd64" && runtime.GOOS == "linux" && target.Arch() == "386" && target.OS() == "linux" {
			// CGO for 386-linux works on amd64-linux host without cross-compilation.
			cgo = true
		} else {
			// CGO is not supported.
			cgo = false
		}
	}

	// Set GOARCH, GOOS to the values specified in the target, if not set.
	target.Env.Vars = envvar.MergeSlices([]string{
		"GOARCH=" + target.Arch(),
		"GOOS=" + target.OS(),
	}, target.Env.Vars)
	if cgo {
		target.Env.Vars = append(target.Env.Vars, "CGO_ENABLED=1")
	}

	// If a release version of the go toolchain can be used, use that.
	// This allows multiple target architectures to share a single
	// toolchain installation, saving time and space.
	//
	// A release version cannot be used if:
	// (1) Don't know the URL for obtaining it (newGoRelease returns nil), OR
	// (2) Any patches need to be applied to the toolchain code (m.spec.patchFiles), OR
	// (3) Any custom build flags (set via GO_FLAGS environment variable)
	var goInstDir jiri.RelPath
	env := envvar.VarsFromSlice(target.Env.Vars)
	if release := newGoRelease(target.Version()); release != nil &&
		len(m.spec.patchFiles) == 0 &&
		!env.Contains("GO_FLAGS") {
		// Not using a bootstrapped Go, delete any references from env.
		env.Delete("GOROOT_BOOTSTRAP")
		goInstDir = m.goRoot.Join("shared").Join(target.Version())
		fn := func() error { return release.install(jirix, goInstDir.Abs(jirix)) }
		if err := profilesutil.AtomicAction(jirix, fn, goInstDir.Abs(jirix), "Install a release version of the Go toolchain"); err != nil {
			return err
		}
		// CC_FOR_TARGET and CXX_FOR_TARGET might have been set by the
		// cross-compiler setup above. Those variables only take effect
		// when building a Go toolchain.  Instead set CC and CXX.
		if k := "CC_FOR_TARGET"; env.Contains(k) {
			env.Set("CC", abs2symbolic(jirix, env.Get(k)))
			env.Delete(k)
		}
		if k := "CXX_FOR_TARGET"; env.Contains(k) {
			env.Set("CXX", abs2symbolic(jirix, env.Get(k)))
			env.Delete(k)
		}
		if jirix.Verbose() {
			fmt.Fprintf(jirix.Stdout(), "Using Go toolchain in %v for target %v", goInstDir, target)
		}
	} else {
		if jirix.Verbose() {
			fmt.Fprintf(jirix.Stdout(), "Compiling go toolchain at revision %v", m.spec.gitRevision)
		}
		// Compile our own Go toolchain
		targetDir := m.goRoot.Join(target.TargetSpecificDirname())
		goInstDir = targetDir.Join(m.spec.gitRevision)
		goBootstrapDir := targetDir.Join("go-bootstrap")
		if jirix.Verbose() {
			fmt.Fprintf(jirix.Stdout(), "Go Target+Revision %s\n", goInstDir.Abs(jirix))
		}
		if err := m.installGo15Plus(jirix, goBootstrapDir, goInstDir, target.Version(), env); err != nil {
			return err
		}
	}
	env.Set("GOROOT", goInstDir.Symbolic())
	target.Env.Vars = env.ToSlice()
	target.InstallationDir = string(goInstDir)

	pdb.InstallProfile(m.profileInstaller, m.profileName, string(m.goRoot))
	return pdb.AddProfileTarget(m.profileInstaller, m.profileName, target)
}

func (m *Manager) Uninstall(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
	if err := m.initForTarget(jirix, root, &target); err != nil {
		return err
	}

	if target.CrossCompiling() {
		// We may need to install an additional cross compilation toolchain
		// for cgo to work.
		def := profiles.DefaultTarget()
		if builder := xcompilers[xspec{def.Arch(), def.OS()}][xspec{target.Arch(), target.OS()}]; builder != nil {
			if _, _, err := builder(jirix, m, root, target, profiles.Uninstall); err != nil {
				return err
			}
		}
	}
	s := jirix.NewSeq()
	if err := s.RemoveAll(m.goRoot.Join(target.TargetSpecificDirname()).Abs(jirix)).Done(); err != nil {
		return err
	}
	if pdb.RemoveProfileTarget(m.profileInstaller, m.profileName, target) {
		// If there are no more targets then remove the entire go directory,
		// including the bootstrap one.
		return s.RemoveAll(m.goRoot.Abs(jirix)).Done()
	}
	return nil
}

// installGo14 installs Go 1.4 at a given location, using the provided
// environment during compilation.
func installGo14(jirix *jiri.X, go14Dir string, env *envvar.Vars) error {
	installGo14Fn := func() error {
		s := jirix.NewSeq()
		tmpDir, err := s.TempDir("", "")
		if err != nil {
			return err
		}
		defer jirix.NewSeq().RemoveAll(tmpDir)

		name := "go1.4.3.src.tar.gz"
		remote, local := "https://storage.googleapis.com/golang/"+name, filepath.Join(tmpDir, name)
		parentDir := filepath.Dir(go14Dir)
		goSrcDir := filepath.Join(go14Dir, "src")
		makeBin := filepath.Join(goSrcDir, "make.bash")

		if err := s.Run("curl", "-Lo", local, remote).
			Run("tar", "-C", tmpDir, "-xzf", local).
			Remove(local).
			RemoveAll(go14Dir).
			MkdirAll(parentDir, profilesutil.DefaultDirPerm).Done(); err != nil {
{'fraction_non_alphanumeric': 0.14445464407911207, 'fraction_numerical': 0.05098890094300259, 'mean_word_length': 3.9396125309150865, 'pattern_counts': {'":': 26, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6698450', 'n_tokens_mistral': 10041, 'n_tokens_neox': 8991, 'n_words': 2088}