blob: 9cc20b416fe07aea61548ff75579c5f6f9d72a60 [file] [log] [blame]
// 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/x/devtools/tooldata"
"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,
},
}
var osPackages = map[xspec]map[xspec][]string{
xspec{"amd64", "darwin"}: {},
xspec{"amd64", "linux"}: {
xspec{"arm", "linux"}: []string{
"automake", "bison", "bzip2", "curl", "flex", "g++",
"gawk", "libexpat1-dev", "gettext", "gperf",
"libncurses5-dev", "libtool", "subversion", "texinfo",
},
},
}
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 (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
return osPackages[xspec{runtime.GOARCH, runtime.GOOS}][xspec{target.Arch(), target.OS()}], nil
}
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 {
return err
}
return s.Rename(filepath.Join(tmpDir, "go"), go14Dir).
Chdir(goSrcDir).
Env(env.ToMap()).Last(makeBin, "--no-clean")
}
return profilesutil.AtomicAction(jirix, installGo14Fn, go14Dir, "Build and install Go 1.4")
}
// installGo15Plus installs any version of go past 1.5 at the specified git and go
// revision.
func (m *Manager) installGo15Plus(jirix *jiri.X, goBootstrapDir, goInstDir jiri.RelPath, version string, env *envvar.Vars) error {
// First install bootstrap Go 1.4 for the host.
if err := installGo14(jirix, goBootstrapDir.Abs(jirix), envvar.VarsFromOS()); err != nil {
return err
}
installGo15Fn := func() error {
// Clone go1.5 into a tmp directory and then rename it to the
// final destination.
s := jirix.NewSeq()
tmpDir, err := s.TempDir("", "")
if err != nil {
return err
}
defer jirix.NewSeq().RemoveAll(tmpDir)
goSrcDir := filepath.Join(goInstDir.Abs(jirix), "src")
// Git clone the code and get into the right directory.
if err := s.Pushd(tmpDir).
Call(func() error { return gitutil.New(jirix.NewSeq()).Clone(go15GitRemote, tmpDir) }, "").
Call(func() error { return gitutil.New(jirix.NewSeq()).Reset(m.spec.gitRevision) }, "").
Popd().
RemoveAll(goInstDir.Abs(jirix)).
Rename(tmpDir, goInstDir.Abs(jirix)).
Chmod(goInstDir.Abs(jirix), profilesutil.DefaultDirPerm).
Done(); err != nil {
return err
}
// Apply patches, if any and build.
s.Pushd(goSrcDir)
for _, patchFile := range m.spec.patchFiles {
s.Run("git", "apply", patchFile)
}
makeBin := filepath.Join(goSrcDir, "make.bash")
env.Set("GOROOT_BOOTSTRAP", goBootstrapDir.Abs(jirix))
if err := s.Env(env.ToMap()).Last(makeBin); err != nil {
s.RemoveAll(filepath.Join(goInstDir.Abs(jirix), "bin")).Done()
return err
}
return nil
}
if err := profilesutil.AtomicAction(jirix, installGo15Fn, goInstDir.Abs(jirix), "Build and install Go "+version+" @ "+m.spec.gitRevision); err != nil {
return err
}
env.Set("GOROOT_BOOTSTRAP", goBootstrapDir.Symbolic())
return nil
}
func linux_to_linux(jirix *jiri.X, m *Manager, root jiri.RelPath, target profiles.Target, action profiles.Action) (bindir string, env []string, e error) {
targetABI := ""
switch target.Arch() {
case "arm":
targetABI = "arm-unknown-linux-gnueabi"
default:
return "", nil, fmt.Errorf("Arch %q is not yet supported for crosstools xgcc", target.Arch())
}
xgccOutDir := filepath.Join(m.root.Abs(jirix), "profiles", "cout", "xgcc")
xtoolInstDir := filepath.Join(xgccOutDir, "crosstools-ng-"+targetABI)
xgccInstDir := filepath.Join(xgccOutDir, targetABI)
xgccLinkInstDir := filepath.Join(xgccOutDir, "links-"+targetABI)
if action == profiles.Uninstall {
s := jirix.NewSeq()
s.Last("chmod", "-R", "+w", xgccInstDir)
for _, dir := range []string{xtoolInstDir, xgccInstDir, xgccLinkInstDir} {
if err := s.RemoveAll(dir).Done(); err != nil {
return "", nil, err
}
}
return "", nil, nil
}
// Build and install crosstool-ng.
installNgFn := func() error {
xgccSrcDir := jiri.NewRelPath("third_party", "csrc", "crosstool-ng-1.20.0").Abs(jirix)
return jirix.NewSeq().
Pushd(xgccSrcDir).
Run("autoreconf", "--install", "--force", "--verbose").
Run("./configure", fmt.Sprintf("--prefix=%v", xtoolInstDir)).
Run("make", fmt.Sprintf("-j%d", runtime.NumCPU())).
Run("make", "install").
Last("make", "distclean")
}
if err := profilesutil.AtomicAction(jirix, installNgFn, xtoolInstDir, "Build and install crosstool-ng"); err != nil {
return "", nil, err
}
// Build arm/linux gcc tools.
installXgccFn := func() error {
s := jirix.NewSeq()
tmpDir, err := s.TempDir("", "")
if err != nil {
return fmt.Errorf("TempDir() failed: %v", err)
}
defer collect.Error(func() error { return jirix.NewSeq().RemoveAll(tmpDir).Done() }, &e)
bin := filepath.Join(xtoolInstDir, "bin", "ct-ng")
if err := s.Chdir(tmpDir).Last(bin, targetABI); err != nil {
return err
}
dataPath, err := tooldata.DataDirPath(jirix, "")
if err != nil {
return err
}
configFile := filepath.Join(dataPath, "crosstool-ng-1.20.0.config")
config, err := ioutil.ReadFile(configFile)
if err != nil {
return fmt.Errorf("ReadFile(%v) failed: %v", configFile, err)
}
old, new := "/usr/local/vanadium", filepath.Join(m.root.Abs(jirix), "profiles", "cout")
newConfig := strings.Replace(string(config), old, new, -1)
newConfigFile := filepath.Join(tmpDir, ".config")
if err := s.WriteFile(newConfigFile, []byte(newConfig), profilesutil.DefaultFilePerm).Done(); err != nil {
return fmt.Errorf("WriteFile(%v) failed: %v", newConfigFile, err)
}
dirinfo, err := s.Run(bin, "oldconfig").
Run(bin, "build").
Stat(xgccInstDir)
if err != nil {
return err
}
// crosstool-ng build creates the tool output directory with no write
// permissions. Change it so that AtomicAction can create the
// "action completed" file.
return s.Chmod(xgccInstDir, dirinfo.Mode()|0755).Done()
}
if err := profilesutil.AtomicAction(jirix, installXgccFn, xgccInstDir, "Build arm/linux gcc tools"); err != nil {
return "", nil, err
}
linkBinDir := filepath.Join(xgccLinkInstDir, "bin")
// Create arm/linux gcc symlinks.
installLinksFn := func() error {
s := jirix.NewSeq()
err := s.MkdirAll(linkBinDir, profilesutil.DefaultDirPerm).
Chdir(xgccLinkInstDir).Done()
if err != nil {
return err
}
binDir := filepath.Join(xgccInstDir, "bin")
fileInfoList, err := ioutil.ReadDir(binDir)
if err != nil {
return fmt.Errorf("ReadDir(%v) failed: %v", binDir, err)
}
for _, fileInfo := range fileInfoList {
prefix := targetABI + "-"
if strings.HasPrefix(fileInfo.Name(), prefix) {
src := filepath.Join(binDir, fileInfo.Name())
dst := filepath.Join(linkBinDir, strings.TrimPrefix(fileInfo.Name(), prefix))
if err := s.Symlink(src, dst).Done(); err != nil {
return err
}
}
}
return nil
}
if err := profilesutil.AtomicAction(jirix, installLinksFn, xgccLinkInstDir, "Create gcc symlinks"); err != nil {
return "", nil, err
}
vars := []string{
"CC_FOR_TARGET=" + filepath.Join(linkBinDir, "gcc"),
"CXX_FOR_TARGET=" + filepath.Join(linkBinDir, "g++"),
}
return linkBinDir, vars, nil
}
func darwin_to_linux(jirix *jiri.X, m *Manager, root jiri.RelPath, target profiles.Target, action profiles.Action) (bindir string, env []string, e error) {
if target.Arch() == "arm" {
return useLLVM(jirix, m, root, target, action)
}
return "", nil, fmt.Errorf("cross compilation from darwin to %s linux is not yet supported.", target.Arch())
}
func darwin_to_ios(jirix *jiri.X, m *Manager, root jiri.RelPath, target profiles.Target, action profiles.Action) (bindir string, env []string, e error) {
if action == profiles.Uninstall {
return "", nil, nil
}
// As of Go 1.6, unable to generate PIC code for 32-bit arm.
// As of Go 1.5.x, the linker fails for darwin/32-bit
// As of Go 1.5.1 the linker fails for darwin/32-bit, and is unable to generate PIC code for 32-bit arm
if target.Arch() == "386" && (target.Version() == "1.5" || strings.HasPrefix(target.Version(), "1.5.")) {
return "", nil, fmt.Errorf("32-bit iOS simulator is not supported by go yet with c-archive. " +
"See https://github.com/golang/go/issues/12683")
}
if target.Arch() == "arm" {
return "", nil, fmt.Errorf("32-bit ARM is not supported by go yet as it does not generate PIC code. " +
"See https://github.com/golang/go/issues/12681")
}
vars := []string{
"CGO_ENABLED=1",
"CC_FOR_TARGET=" + filepath.Join(jirix.Root, "release", "swift", "clang", "clangwrap.sh"),
"CXX_FOR_TARGET=" + filepath.Join(jirix.Root, "release", "swift", "clang", "clangwrap++.sh"),
"GOOS=darwin",
"GOHOSTARCH=amd64",
"GOHOSTOS=darwin",
// We need to explicitly pass the ios build tag to the make.bash script for go so that it won't compile
// crypto code that's meant for the mac. It'll work on the device because those files have a build tag
// of !arm64, but the simulator will fail because those specific APIs don't exist on iOS.
"GO_FLAGS=-tags ios",
}
// 32-bit arm is always armv7 in Apple-land
if target.Arch() == "arm" {
vars = append(vars, "GOARM=7")
} else if target.Arch() == "arm64" {
vars = append(vars, "GOARM=arm64")
}
// Add patch for text-relocation errors on arm64
// Submitted to golang, currently marked for 1.7: https://go-review.googlesource.com/#/c/19206/
patchPath := filepath.Join(jirix.Root, "release", "go", "src", "v.io", "x", "devtools", "jiri-profile-v23", "go_profile", "macho_linker.patch")
m.spec.patchFiles = append(m.spec.patchFiles, patchPath)
return "", vars, nil
}
func to_android(jirix *jiri.X, m *Manager, root jiri.RelPath, target profiles.Target, action profiles.Action) (bindir string, env []string, e error) {
if action == profiles.Uninstall {
return "", nil, nil
}
ev := envvar.VarsFromSlice(target.CommandLineEnv().Vars)
jiri.ExpandEnv(jirix, ev)
ndk := ev.Get("ANDROID_NDK_DIR")
if len(ndk) == 0 {
return "", nil, fmt.Errorf("ANDROID_NDK_DIR not specified in the command line environment")
}
ndkBin := filepath.Join(ndk, "bin")
var abi string
switch target.Arch() {
case "amd64":
abi = "x86_64-linux-android"
case "arm":
abi = "arm-linux-androideabi"
default:
return "", nil, fmt.Errorf("could not locate android abi for target arch %s", target.Arch())
}
var (
cc = filepath.Join(ndkBin, fmt.Sprintf("%s-gcc", abi))
cxx = filepath.Join(ndkBin, fmt.Sprintf("%s-g++", abi))
vars = []string{
"CC_FOR_TARGET=" + cc,
"CXX_FOR_TARGET=" + cxx,
"CLANG=" + cc,
"CLANG++=" + cxx,
}
)
return ndkBin, vars, nil
}
func to_fnl(jirix *jiri.X, m *Manager, root jiri.RelPath, target profiles.Target, action profiles.Action) (bindir string, env []string, e error) {
if action == profiles.Uninstall {
return "", nil, nil
}
fnlRoot := os.Getenv("FNL_JIRI_ROOT")
if len(fnlRoot) == 0 {
return "", nil, fmt.Errorf("FNL_JIRI_ROOT not specified in the command line environment")
}
muslBin := filepath.Join(fnlRoot, "out/root/tools/x86_64-fuchsia-linux-musl/bin")
// This cross compiles by building a go compiler with HOST=386 rather than amd64 because
// the go compiler build process doesn't support building two different versions when
// host and target are the same.
// TODO(bprosnitz) Determine whether fnl is linux or a separate os and make the build process cleaner.
vars := []string{
"CC_FOR_TARGET=" + filepath.Join(muslBin, "x86_64-fuchsia-linux-musl-gcc"),
"CXX_FOR_TARGET=" + filepath.Join(muslBin, "x86_64-fuchsia-linux-musl-g++"),
"GOHOSTARCH=386",
"GOHOSTOS=linux",
"GOARCH=amd64",
"GOOS=linux",
// Under $JIRI_ROOT, packages for both standard amd64 linux machines and fnl
// will be placed under "release/go/pkg/linux_amd64" yet be incompatible.
// This sets a suffix "linux_amd64_musl" for fnl target packages.
// Note that this isn't a problem for the fnl go cross compiler itself
// because it is cross compiling from 386 to amd64 and the package
// directories will not conflict.
// GO_FLAGS is an environment variable used by go's compiler build scripts
// to inject flags into the build commands.
"GO_FLAGS=-installsuffix=musl",
}
return "", vars, nil
}
// abs2symbolic is the inverse of jiri.RelPath.Abs
func abs2symbolic(jirix *jiri.X, path string) string {
if !strings.HasPrefix(path, jirix.Root) {
return path
}
rel, err := filepath.Rel(jirix.Root, path)
if err != nil {
return rel
}
return jiri.NewRelPath(rel).Symbolic()
}