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