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