blob: 83a93ef69bc8abc4ab656d9fac9b2027cbba60de [file] [log] [blame]
package impl
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/services/mgmt/binary"
"veyron.io/veyron/veyron2/services/mgmt/build"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
)
var (
errBuildFailed = verror.Internalf("build failed")
errInternalError = verror.Internalf("internal error")
)
// builderService implements the Builder server interface.
type builderService struct {
// Path to the binary and the value of the GOROOT environment variable.
gobin, goroot string
}
// NewBuilderService returns a new Build service implementation.
func NewBuilderService(gobin, goroot string) *builderService {
return &builderService{
gobin: gobin,
goroot: goroot,
}
}
// TODO(jsimsa): Add support for building for a specific profile
// specified as a suffix the Build().
//
// TODO(jsimsa): Analyze the binary files for shared library
// dependencies and ship these back.
func (i *builderService) Build(ctx build.BuilderBuildContext, arch build.Architecture, opsys build.OperatingSystem) ([]byte, error) {
vlog.VI(1).Infof("Build(%v, %v) called.", arch, opsys)
dir, prefix := "", ""
dirPerm, filePerm := os.FileMode(0700), os.FileMode(0600)
root, err := ioutil.TempDir(dir, prefix)
if err != nil {
vlog.Errorf("TempDir(%v, %v) failed: %v", dir, prefix, err)
return nil, errInternalError
}
defer os.RemoveAll(root)
if err := os.Chdir(root); err != nil {
vlog.Errorf("Chdir(%v) failed: %v", root, err)
}
srcDir := filepath.Join(root, "go", "src")
if err := os.MkdirAll(srcDir, dirPerm); err != nil {
vlog.Errorf("MkdirAll(%v, %v) failed: %v", srcDir, dirPerm, err)
return nil, errInternalError
}
iterator := ctx.RecvStream()
for iterator.Advance() {
srcFile := iterator.Value()
filePath := filepath.Join(srcDir, filepath.FromSlash(srcFile.Name))
dir := filepath.Dir(filePath)
if err := os.MkdirAll(dir, dirPerm); err != nil {
vlog.Errorf("MkdirAll(%v, %v) failed: %v", dir, dirPerm, err)
return nil, errInternalError
}
if err := ioutil.WriteFile(filePath, srcFile.Contents, filePerm); err != nil {
vlog.Errorf("WriteFile(%v, %v) failed: %v", filePath, filePerm, err)
return nil, errInternalError
}
}
if err := iterator.Err(); err != nil {
vlog.Errorf("Advance() failed: %v", err)
return nil, errInternalError
}
cmd := exec.Command(i.gobin, "install", "-v", "./...")
cmd.Env = append(cmd.Env, "GOARCH="+string(arch))
cmd.Env = append(cmd.Env, "GOOS="+string(opsys))
cmd.Env = append(cmd.Env, "GOPATH="+filepath.Dir(srcDir))
if i.goroot != "" {
cmd.Env = append(cmd.Env, "GOROOT="+i.goroot)
}
var output bytes.Buffer
cmd.Stdout = &output
cmd.Stderr = &output
if err := cmd.Run(); err != nil {
vlog.Errorf("Run() failed: %v", err)
if output.Len() != 0 {
vlog.Errorf("%v", output.String())
}
return output.Bytes(), errBuildFailed
}
binDir := filepath.Join(root, "go", "bin")
if runtime.GOARCH != string(arch) || runtime.GOOS != string(opsys) {
binDir = filepath.Join(binDir, fmt.Sprintf("%v_%v", string(opsys), string(arch)))
}
files, err := ioutil.ReadDir(binDir)
if err != nil && !os.IsNotExist(err) {
vlog.Errorf("ReadDir(%v) failed: %v", binDir, err)
return nil, errInternalError
}
for _, file := range files {
binPath := filepath.Join(binDir, file.Name())
bytes, err := ioutil.ReadFile(binPath)
if err != nil {
vlog.Errorf("ReadFile(%v) failed: %v", binPath, err)
return nil, errInternalError
}
result := build.File{
Name: "bin/" + file.Name(),
Contents: bytes,
}
if err := ctx.SendStream().Send(result); err != nil {
vlog.Errorf("Send() failed: %v", err)
return nil, errInternalError
}
}
return output.Bytes(), nil
}
func (i *builderService) Describe(_ ipc.ServerContext, name string) (binary.Description, error) {
// TODO(jsimsa): Implement.
return binary.Description{}, nil
}