blob: fa4257e071ff0b24bc783b6075cfd08104ea8a43 [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 util
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"v.io/jiri/project"
"v.io/jiri/tool"
"v.io/x/lib/envvar"
)
const (
jiriProfileEnv = "JIRI_PROFILE"
javaEnv = "JAVA_HOME"
)
// WARNING: this function is in the proces of being removed and replaced
// by the profiles based configuration (see jiri/profiles). Use
// profiles.ConfigHelper instead of this for new code.
//
// JiriLegacyEnvironment returns the environment variables setting for the project.
// The util package captures the original state of the relevant environment
// variables when the tool is initialized and every invocation of this function
// updates this original state according to the jiri tool configuration.
//
// By default, the Go and VDL workspaces are added to the GOPATH and VDLPATH
// environment variables respectively. In addition, the JIRI_PROFILE
// environment variable can be used to activate an environment variable setting
// for various development profiles of the project (e.g. arm, android, java, or
// nacl). Unlike the default setting, the setting enabled by the JIRI_PROFILE
// environment variable can override existing environment.
func JiriLegacyEnvironment(ctx *tool.Context) (*envvar.Vars, error) {
env := envvar.VarsFromOS()
root, err := project.JiriRoot()
if err != nil {
return nil, err
}
config, err := LoadConfig(ctx)
if err != nil {
return nil, err
}
env.Set("CGO_ENABLED", "1")
if err := setGoPath(ctx, env, root, config); err != nil {
return nil, err
}
if err := setVdlPath(ctx, env, root, config); err != nil {
return nil, err
}
if profile := os.Getenv(jiriProfileEnv); profile != "" {
fmt.Fprintf(ctx.Stdout(), `NOTE: Enabling environment variable setting for %q.
This can override values of existing environment variables.
`, profile)
switch profile {
case "android":
// Cross-compilation for android on linux.
if err := setAndroidEnv(ctx, env, root); err != nil {
return nil, err
}
case "arm":
// Cross-compilation for arm on linux.
if err := setArmEnv(ctx, env, root); err != nil {
return nil, err
}
case "java":
// Building of a Go shared library for Java.
if err := setJavaEnv(ctx, env, root); err != nil {
return nil, err
}
case "nacl":
// Cross-compilation for nacl.
if err := setNaclEnv(ctx, env, root); err != nil {
return nil, err
}
default:
fmt.Fprintf(ctx.Stderr(), "Unknown environment profile %q", profile)
}
}
if err := setSyncbaseEnv(ctx, env, root); err != nil {
return nil, err
}
return env, nil
}
// setJavaEnv sets the environment variables used for building a Go
// shared library that is invoked from Java code. If Java is not
// installed on the host, this function is a no-op.
func setJavaEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
jdkHome := os.Getenv(javaEnv)
if jdkHome == "" {
return nil
}
cflags := env.GetTokens("CGO_CFLAGS", " ")
cflags = append(cflags, filepath.Join("-I"+jdkHome, "include"), filepath.Join("-I"+jdkHome, "include", runtime.GOOS))
env.SetTokens("CGO_CFLAGS", cflags, " ")
return nil
}
// setAndroidEnv sets the environment variables used for android
// cross-compilation.
func setAndroidEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
// Compile using Android Go 1.5 (installed via
// 'jiri profile install android').
androidGoDir := filepath.Join(root, "third_party", "android", "go")
// Set Go-specific environment variables.
env.Set("GOARCH", "arm")
env.Set("GOARM", "7")
env.Set("GOOS", "android")
env.Set("GOROOT", androidGoDir)
// Update PATH.
if _, err := ctx.Run().Stat(androidGoDir); err != nil {
return fmt.Errorf("Couldn't find android Go installation directory %s: did you run \"jiri profile install android\"?", androidGoDir)
}
path := env.GetTokens("PATH", ":")
path = append([]string{filepath.Join(androidGoDir, "bin")}, path...)
env.SetTokens("PATH", path, ":")
return nil
}
// setArmEnv sets the environment variables used for arm cross-compilation.
func setArmEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
// Set Go specific environment variables.
env.Set("GOARCH", "arm")
env.Set("GOARM", "6")
env.Set("GOOS", "linux")
// Add the paths to arm cross-compilation tools to the PATH.
path := env.GetTokens("PATH", ":")
path = append([]string{
filepath.Join(root, "third_party", "cout", "xgcc", "cross_arm"),
filepath.Join(root, "third_party", "repos", "go_arm", "bin"),
}, path...)
env.SetTokens("PATH", path, ":")
return nil
}
// setGoPath adds the paths of Go workspaces to the GOPATH variable.
func setGoPath(ctx *tool.Context, env *envvar.Vars, root string, config *Config) error {
return setPathHelper(ctx, env, "GOPATH", root, config.GoWorkspaces(), "")
}
// setVdlPath adds the paths of VDL workspaces to the VDLPATH variable.
func setVdlPath(ctx *tool.Context, env *envvar.Vars, root string, config *Config) error {
return setPathHelper(ctx, env, "VDLPATH", root, config.VDLWorkspaces(), "src")
}
// setPathHelper is a utility function for setting path environment
// variables for different types of workspaces.
func setPathHelper(ctx *tool.Context, env *envvar.Vars, name, root string, workspaces []string, suffix string) error {
path := env.GetTokens(name, ":")
projects, _, err := project.ReadManifest(ctx)
if err != nil {
return err
}
for _, workspace := range workspaces {
absWorkspace := filepath.Join(root, workspace, suffix)
// Only append an entry to the path if the workspace is rooted
// under a jiri project that exists locally or vice versa.
for _, project := range projects {
// We check if <project.Path> is a prefix of <absWorkspace> to
// account for Go workspaces nested under a single jiri project,
// such as: $JIRI_ROOT/release/projects/chat/go.
//
// We check if <absWorkspace> is a prefix of <project.Path> to
// account for Go workspaces that span multiple jiri projects,
// such as: $JIRI_ROOT/release/go.
if strings.HasPrefix(absWorkspace, project.Path) || strings.HasPrefix(project.Path, absWorkspace) {
if _, err := ctx.Run().Stat(filepath.Join(absWorkspace)); err == nil {
path = append(path, absWorkspace)
break
}
}
}
}
env.SetTokens(name, path, ":")
return nil
}
// TODO(nlacasse): Move setNaclEnv and setSyncbaseEnv out of jiri since they
// are not general-purpose.
// setNaclEnv sets the environment variables used for nacl
// cross-compilation.
func setNaclEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
env.Set("GOARCH", "amd64p32")
env.Set("GOOS", "nacl")
// Add the path to nacl cross-compiler to the PATH.
path := env.GetTokens("PATH", ":")
path = append([]string{
filepath.Join(root, "third_party", "repos", "go_ppapi", "bin"),
}, path...)
env.SetTokens("PATH", path, ":")
return nil
}
// setSyncbaseEnv adds the LevelDB third-party C++ libraries Vanadium
// Go code depends on to the CGO_CFLAGS and CGO_LDFLAGS variables.
func setSyncbaseEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
libs := []string{
"leveldb",
"snappy",
}
// TODO(rogulenko): get these vars from a config file.
goos, goarch := env.Get("GOOS"), env.Get("GOARCH")
if goos == "" {
goos = runtime.GOOS
}
if goarch == "" {
goarch = runtime.GOARCH
}
for _, lib := range libs {
cflags := env.GetTokens("CGO_CFLAGS", " ")
cxxflags := env.GetTokens("CGO_CXXFLAGS", " ")
ldflags := env.GetTokens("CGO_LDFLAGS", " ")
dir, err := ThirdPartyCCodePath(goos, goarch)
if err != nil {
return err
}
dir = filepath.Join(dir, lib)
if _, err := ctx.Run().Stat(dir); err != nil {
if !os.IsNotExist(err) {
return err
}
continue
}
cflags = append(cflags, filepath.Join("-I"+dir, "include"))
cxxflags = append(cxxflags, filepath.Join("-I"+dir, "include"))
ldflags = append(ldflags, filepath.Join("-L"+dir, "lib"))
if runtime.GOARCH == "linux" {
ldflags = append(ldflags, "-Wl,-rpath", filepath.Join(dir, "lib"))
}
env.SetTokens("CGO_CFLAGS", cflags, " ")
env.SetTokens("CGO_CXXFLAGS", cxxflags, " ")
env.SetTokens("CGO_LDFLAGS", ldflags, " ")
}
return nil
}