Migration to AGL gerrit (update go import)
[src/xds/xds-agent.git] / webapp / src / app / pages / build / build.component.ts
1 /**
2 * @license
3 * Copyright (C) 2017-2018 "IoT.bzh"
4 * Author Sebastien Douheret <sebastien@iot.bzh>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 import { Component, ViewEncapsulation, AfterViewChecked, ElementRef, ViewChild, OnInit, Input } from '@angular/core';
20 import { Observable } from 'rxjs/Observable';
21 import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms';
22 import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
23
24 import 'rxjs/add/operator/scan';
25 import 'rxjs/add/operator/startWith';
26
27 import { BuildSettingsModalComponent } from './build-settings-modal/build-settings-modal.component';
28
29 import { XDSAgentService, ICmdOutput } from '../../@core-xds/services/xdsagent.service';
30 import { ProjectService, IProject } from '../../@core-xds/services/project.service';
31 import { AlertService, IAlert } from '../../@core-xds/services/alert.service';
32 import { SdkService } from '../../@core-xds/services/sdk.service';
33
34 @Component({
35   selector: 'xds-panel-build',
36   templateUrl: './build.component.html',
37   styleUrls: ['./build.component.scss'],
38   encapsulation: ViewEncapsulation.None,
39 })
40
41 export class BuildComponent implements OnInit, AfterViewChecked {
42   @ViewChild('scrollOutput') private scrollContainer: ElementRef;
43
44   // FIXME workaround of https://github.com/angular/angular-cli/issues/2034
45   // should be removed with angular 5
46   //  @Input() curProject: IProject;
47   @Input() curProject = <IProject>null;
48
49   public buildIsCollapsed = false;
50   public cmdOutput: string;
51   public cmdInfo: string;
52   public curPrj: IProject;
53
54   private startTime: Map<string, number> = new Map<string, number>();
55
56   constructor(
57     private prjSvr: ProjectService,
58     private xdsSvr: XDSAgentService,
59     private alertSvr: AlertService,
60     private sdkSvr: SdkService,
61     private modalService: NgbModal,
62   ) {
63     this.cmdOutput = '';
64     this.cmdInfo = '';       // TODO: to be remove (only for debug)
65   }
66
67   ngOnInit() {
68     // Retreive current project
69     this.prjSvr.curProject$.subscribe(p => this.curPrj = p);
70
71     // Command output data tunneling
72     this.xdsSvr.CmdOutput$.subscribe(data => {
73       this.cmdOutput += data.stdout;
74       this.cmdOutput += data.stderr;
75     });
76
77     // Command exit
78     this.xdsSvr.CmdExit$.subscribe(exit => {
79       if (this.startTime.has(exit.cmdID)) {
80         this.cmdInfo = 'Last command duration: ' + this._computeTime(this.startTime.get(exit.cmdID));
81         this.startTime.delete(exit.cmdID);
82       }
83
84       if (exit && exit.code !== 0) {
85         this.cmdOutput += '--- Command exited with code ' + exit.code + ' ---\n\n';
86       }
87     });
88
89     this._scrollToBottom();
90   }
91
92   ngAfterViewChecked() {
93     this._scrollToBottom();
94   }
95
96   resetOutput() {
97     this.cmdOutput = '';
98   }
99
100   isSetupValid(): boolean {
101     return (typeof this.curPrj !== 'undefined');
102   }
103
104   settingsShow() {
105     if (!this.isSetupValid()) {
106       return this.alertSvr.warning('Please select first a valid project.', true);
107     }
108     const modal = this.modalService.open(
109       BuildSettingsModalComponent,
110       { size: 'lg', container: 'nb-layout' },
111     );
112   }
113
114   execCmd(cmdName: string) {
115     if (!this.isSetupValid()) {
116       return this.alertSvr.warning('Please select first a valid project.', true);
117     }
118
119     if (!this.curPrj.uiSettings) {
120       return this.alertSvr.warning('Invalid setting structure', true);
121     }
122
123     let cmd = '';
124     switch (cmdName) {
125       case 'clean':
126         cmd = this.curPrj.uiSettings.cmdClean;
127         break;
128       case 'prebuild':
129         cmd = this.curPrj.uiSettings.cmdPrebuild;
130         break;
131       case 'build':
132         cmd = this.curPrj.uiSettings.cmdBuild;
133         break;
134       case 'populate':
135         cmd = this.curPrj.uiSettings.cmdPopulate;
136         break;
137       case 'exec':
138         if (this.curPrj.uiSettings.cmdArgs instanceof Array) {
139           cmd = this.curPrj.uiSettings.cmdArgs.join(' ');
140         } else {
141           cmd = this.curPrj.uiSettings.cmdArgs;
142         }
143         break;
144       default:
145         return this.alertSvr.warning('Unknown command name ' + cmdName);
146     }
147
148     const prjID = this.curPrj.id;
149     const dir = this.curPrj.uiSettings.subpath;
150     const args: string[] = [];
151     const sdkid = this.sdkSvr.getCurrentId();
152
153     let env = '';
154     if (this.curPrj.uiSettings.envVars instanceof Array) {
155       env = this.curPrj.uiSettings.envVars.join(' ');
156     } else {
157       env = this.curPrj.uiSettings.envVars;
158     }
159
160     this.cmdOutput += this._outputHeader();
161
162     // Detect key=value in env string to build array of string
163     const envArr = [];
164     env.split(';').forEach(v => envArr.push(v.trim()));
165
166     const t0 = performance.now();
167     this.cmdInfo = 'Start build of ' + prjID + ' at ' + t0;
168
169     this.xdsSvr.exec(prjID, dir, cmd, sdkid, args, envArr)
170       .subscribe(res => {
171         this.startTime.set(String(res.cmdID), t0);
172       },
173       err => {
174         this.cmdInfo = 'Last command duration: ' + this._computeTime(t0);
175         this.alertSvr.error('ERROR: ' + err);
176       });
177   }
178
179   private _scrollToBottom(): void {
180     try {
181       this.scrollContainer.nativeElement.scrollTop = this.scrollContainer.nativeElement.scrollHeight;
182     } catch (err) { }
183   }
184
185   private _computeTime(t0: number, t1?: number): string {
186     const enlap = Math.round((t1 || performance.now()) - t0);
187     if (enlap < 1000.0) {
188       return enlap.toFixed(2) + ' ms';
189     } else {
190       return (enlap / 1000.0).toFixed(3) + ' seconds';
191     }
192   }
193
194   private _outputHeader(): string {
195     return '--- ' + new Date().toString() + ' ---\n';
196   }
197
198   private _outputFooter(): string {
199     return '\n';
200   }
201 }