Improved devel/build panel (support PreBuild/Build/Populate)
[src/xds/xds-server.git] / webapp / src / app / devel / build / build.component.ts
1 import { Component, AfterViewChecked, ElementRef, ViewChild, OnInit, Input } from '@angular/core';
2 import { Observable } from 'rxjs';
3 import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms';
4 import { CookieService } from 'ngx-cookie';
5
6 import 'rxjs/add/operator/scan';
7 import 'rxjs/add/operator/startWith';
8
9 import { XDSServerService, ICmdOutput } from "../../services/xdsserver.service";
10 import { ConfigService, IConfig, IProject } from "../../services/config.service";
11 import { AlertService, IAlert } from "../../services/alert.service";
12 import { SdkService } from "../../services/sdk.service";
13
14 @Component({
15     selector: 'panel-build',
16     moduleId: module.id,
17     templateUrl: './build.component.html',
18     styleUrls: ['./build.component.css']
19 })
20
21 export class BuildComponent implements OnInit, AfterViewChecked {
22     @ViewChild('scrollOutput') private scrollContainer: ElementRef;
23
24     @Input() curProject: IProject;
25
26     buildForm: FormGroup;
27     subpathCtrl = new FormControl("", Validators.required);
28     debugEnable: boolean = false;
29
30     public cmdOutput: string;
31     public cmdInfo: string;
32
33     private startTime: Map<string, number> = new Map<string, number>();
34
35     constructor(private configSvr: ConfigService,
36         private xdsSvr: XDSServerService,
37         private fb: FormBuilder,
38         private alertSvr: AlertService,
39         private sdkSvr: SdkService,
40         private cookie: CookieService,
41     ) {
42         this.cmdOutput = "";
43         this.cmdInfo = "";      // TODO: to be remove (only for debug)
44         this.buildForm = fb.group({
45             subpath: this.subpathCtrl,
46             cmdClean: ["", Validators.nullValidator],
47             cmdPrebuild: ["", Validators.nullValidator],
48             cmdBuild: ["", Validators.nullValidator],
49             cmdPopulate: ["", Validators.nullValidator],
50             cmdArgs: ["", Validators.nullValidator],
51             envVars: ["", Validators.nullValidator],
52         });
53     }
54
55     ngOnInit() {
56         // Set default settings
57         // TODO save & restore values from cookies
58         this.buildForm.patchValue({
59             subpath: "",
60             cmdClean: "rm -rf build",
61             cmdPrebuild: "mkdir -p build && cd build && cmake ..",
62             cmdBuild: "cd build && make",
63             cmdPopulate: "cd build && make remote-target-populate",
64             cmdArgs: "",
65             envVars: "",
66         });
67
68         // Command output data tunneling
69         this.xdsSvr.CmdOutput$.subscribe(data => {
70             this.cmdOutput += data.stdout + "\n";
71         });
72
73         // Command exit
74         this.xdsSvr.CmdExit$.subscribe(exit => {
75             if (this.startTime.has(exit.cmdID)) {
76                 this.cmdInfo = 'Last command duration: ' + this._computeTime(this.startTime.get(exit.cmdID));
77                 this.startTime.delete(exit.cmdID);
78             }
79
80             if (exit && exit.code !== 0) {
81                 this.cmdOutput += "--- Command exited with code " + exit.code + " ---\n\n";
82             }
83         });
84
85         this._scrollToBottom();
86
87         // only use for debug
88         this.debugEnable = (this.cookie.get("debug_build") === "1");
89     }
90
91     ngAfterViewChecked() {
92         this._scrollToBottom();
93     }
94
95     reset() {
96         this.cmdOutput = '';
97     }
98
99     clean() {
100         this._exec(
101             this.buildForm.value.cmdClean,
102             this.buildForm.value.subpath,
103             [],
104             this.buildForm.value.envVars);
105     }
106
107     preBuild() {
108         this._exec(
109             this.buildForm.value.cmdPrebuild,
110             this.buildForm.value.subpath,
111             [],
112             this.buildForm.value.envVars);
113     }
114
115     build() {
116         this._exec(
117             this.buildForm.value.cmdBuild,
118             this.buildForm.value.subpath,
119             [],
120             this.buildForm.value.envVars
121         );
122     }
123
124     populate() {
125         this._exec(
126             this.buildForm.value.cmdPopulate,
127             this.buildForm.value.subpath,
128             [], // args
129             this.buildForm.value.envVars
130         );
131     }
132
133     execCmd() {
134         this._exec(
135             this.buildForm.value.cmdArgs,
136             this.buildForm.value.subpath,
137             [],
138             this.buildForm.value.envVars
139         );
140     }
141
142     private _exec(cmd: string, dir: string, args: string[], env: string) {
143         if (!this.curProject) {
144             this.alertSvr.warning('No active project', true);
145         }
146
147         let prjID = this.curProject.id;
148
149         this.cmdOutput += this._outputHeader();
150
151         let sdkid = this.sdkSvr.getCurrentId();
152
153         // Detect key=value in env string to build array of string
154         let envArr = [];
155         env.split(';').forEach(v => envArr.push(v.trim()));
156
157         let t0 = performance.now();
158         this.cmdInfo = 'Start build of ' + prjID + ' at ' + t0;
159
160         this.xdsSvr.exec(prjID, dir, cmd, sdkid, args, envArr)
161             .subscribe(res => {
162                 this.startTime.set(String(res.cmdID), t0);
163             },
164             err => {
165                 this.cmdInfo = 'Last command duration: ' + this._computeTime(t0);
166                 this.alertSvr.error('ERROR: ' + err);
167             });
168     }
169
170     make(args: string) {
171         if (!this.curProject) {
172             this.alertSvr.warning('No active project', true);
173         }
174
175         let prjID = this.curProject.id;
176
177         this.cmdOutput += this._outputHeader();
178
179         let sdkid = this.sdkSvr.getCurrentId();
180
181         let argsArr = args ? args.split(' ') : this.buildForm.value.cmdArgs.split(' ');
182
183         // Detect key=value in env string to build array of string
184         let envArr = [];
185         this.buildForm.value.envVars.split(';').forEach(v => envArr.push(v.trim()));
186
187         let t0 = performance.now();
188         this.cmdInfo = 'Start build of ' + prjID + ' at ' + t0;
189
190         this.xdsSvr.make(prjID, this.buildForm.value.subpath, sdkid, argsArr, envArr)
191             .subscribe(res => {
192                 this.startTime.set(String(res.cmdID), t0);
193             },
194             err => {
195                 this.cmdInfo = 'Last command duration: ' + this._computeTime(t0);
196                 this.alertSvr.error('ERROR: ' + err);
197             });
198     }
199
200     private _scrollToBottom(): void {
201         try {
202             this.scrollContainer.nativeElement.scrollTop = this.scrollContainer.nativeElement.scrollHeight;
203         } catch (err) { }
204     }
205
206     private _computeTime(t0: number, t1?: number): string {
207         let enlap = Math.round((t1 || performance.now()) - t0);
208         if (enlap < 1000.0) {
209             return enlap.toFixed(2) + ' ms';
210         } else {
211             return (enlap / 1000.0).toFixed(3) + ' seconds';
212         }
213     }
214
215     private _outputHeader(): string {
216         return "--- " + new Date().toString() + " ---\n";
217     }
218
219     private _outputFooter(): string {
220         return "\n";
221     }
222 }