Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 1 | package impl |
| 2 | |
| 3 | import ( |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 4 | "bytes" |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 5 | "errors" |
| 6 | "io" |
| 7 | "io/ioutil" |
| 8 | "os" |
| 9 | "os/exec" |
| 10 | "path/filepath" |
| 11 | |
| 12 | "veyron2/ipc" |
| 13 | "veyron2/services/mgmt/binary" |
| 14 | "veyron2/services/mgmt/build" |
| 15 | "veyron2/vlog" |
| 16 | ) |
| 17 | |
| 18 | var ( |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 19 | errBuildFailed = errors.New("build failed") |
| 20 | errInternalError = errors.New("internal error") |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 21 | ) |
| 22 | |
| 23 | // invoker holds the state of a build server invocation. |
| 24 | type invoker struct { |
| 25 | // gobin is the path to the Go compiler binary. |
| 26 | gobin string |
| 27 | } |
| 28 | |
| 29 | // NewInvoker is the invoker factory. |
| 30 | func NewInvoker(gobin string) *invoker { |
| 31 | return &invoker{ |
| 32 | gobin: gobin, |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | // BUILD INTERFACE IMPLEMENTATION |
| 37 | |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 38 | // TODO(jsimsa): Add support for building for a specific profile |
| 39 | // specified as a suffix the Build(). |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 40 | func (i *invoker) Build(_ ipc.ServerContext, stream build.BuildServiceBuildStream) ([]byte, error) { |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 41 | vlog.VI(1).Infof("Build() called.") |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 42 | dir, prefix := "", "" |
| 43 | dirPerm, filePerm := os.FileMode(0700), os.FileMode(0600) |
| 44 | root, err := ioutil.TempDir(dir, prefix) |
| 45 | if err != nil { |
| 46 | vlog.Errorf("TempDir(%v, %v) failed: %v", dir, prefix, err) |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 47 | return nil, errInternalError |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 48 | } |
| 49 | defer os.RemoveAll(root) |
| 50 | srcDir := filepath.Join(root, "go", "src") |
| 51 | if err := os.MkdirAll(srcDir, dirPerm); err != nil { |
| 52 | vlog.Errorf("MkdirAll(%v, %v) failed: %v", srcDir, dirPerm, err) |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 53 | return nil, errInternalError |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 54 | } |
| 55 | for { |
| 56 | srcFile, err := stream.Recv() |
| 57 | if err != nil && err != io.EOF { |
| 58 | vlog.Errorf("Recv() failed: %v", err) |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 59 | return nil, errInternalError |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 60 | } |
| 61 | if err == io.EOF { |
| 62 | break |
| 63 | } |
| 64 | filePath := filepath.Join(srcDir, filepath.FromSlash(srcFile.Name)) |
| 65 | dir := filepath.Dir(filePath) |
| 66 | if err := os.MkdirAll(dir, dirPerm); err != nil { |
| 67 | vlog.Errorf("MkdirAll(%v, %v) failed: %v", dir, dirPerm, err) |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 68 | return nil, errInternalError |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 69 | } |
| 70 | if err := ioutil.WriteFile(filePath, srcFile.Contents, filePerm); err != nil { |
| 71 | vlog.Errorf("WriteFile(%v, %v) failed: %v", filePath, filePerm, err) |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 72 | return nil, errInternalError |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 73 | } |
| 74 | } |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 75 | cmd := exec.Command(i.gobin, "install", "-v", "...") |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 76 | cmd.Env = append(cmd.Env, "GOPATH="+filepath.Dir(srcDir)) |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 77 | var output bytes.Buffer |
| 78 | cmd.Stdout = &output |
| 79 | cmd.Stderr = &output |
| 80 | if err := cmd.Run(); err != nil { |
| 81 | vlog.Errorf("Run() failed: %v", err) |
| 82 | if output.Len() != 0 { |
| 83 | vlog.Errorf("%v", output.String()) |
| 84 | } |
| 85 | return output.Bytes(), errBuildFailed |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 86 | } |
Jiri Simsa | 2f8bc27 | 2014-07-16 12:29:15 -0700 | [diff] [blame] | 87 | binDir := filepath.Join(root, "go", "bin") |
| 88 | files, err := ioutil.ReadDir(binDir) |
| 89 | if err != nil { |
| 90 | vlog.Errorf("ReadDir(%v) failed: %v", binDir, err) |
| 91 | return nil, errInternalError |
| 92 | } |
| 93 | // TODO(jsimsa): Analyze the binary files for non-standard shared |
| 94 | // library dependencies. |
| 95 | for _, file := range files { |
| 96 | binPath := filepath.Join(root, "go", "bin", file.Name()) |
| 97 | bytes, err := ioutil.ReadFile(binPath) |
| 98 | if err != nil { |
| 99 | vlog.Errorf("ReadFile(%v) failed: %v", binPath, err) |
| 100 | return nil, errInternalError |
| 101 | } |
| 102 | result := build.File{ |
| 103 | Name: "bin/" + file.Name(), |
| 104 | Contents: bytes, |
| 105 | } |
| 106 | if err := stream.Send(result); err != nil { |
| 107 | vlog.Errorf("Send() failed: %v", err) |
| 108 | return nil, errInternalError |
| 109 | } |
| 110 | } |
| 111 | return output.Bytes(), nil |
Jiri Simsa | 2e7dd71 | 2014-07-11 16:19:47 -0700 | [diff] [blame] | 112 | } |
| 113 | |
| 114 | func (i *invoker) Describe(_ ipc.ServerContext, name string) (binary.Description, error) { |
| 115 | // TODO(jsimsa): Implement. |
| 116 | return binary.Description{}, nil |
| 117 | } |