diff options
Diffstat (limited to 'webapp/src/app/pages/build')
7 files changed, 427 insertions, 0 deletions
diff --git a/webapp/src/app/pages/build/build.component.html b/webapp/src/app/pages/build/build.component.html new file mode 100644 index 0000000..a1ef62d --- /dev/null +++ b/webapp/src/app/pages/build/build.component.html @@ -0,0 +1,63 @@ +<div class="row"> + <div class="col-12"> + <nb-card-body> + <nb-actions size="medium"> + <nb-action> + <xds-project-select-dropdown></xds-project-select-dropdown> + </nb-action> + <nb-action> + <xds-sdk-select-dropdown></xds-sdk-select-dropdown> + </nb-action> + </nb-actions> + </nb-card-body> + </div> + <div class="col-md-12 col-lg-12 col-xxxl-6"> + <nb-card size="xlarge"> + <nb-tabset fullWidth> + + <nb-tab tabTitle="Build"> + + <div class="row" style="margin-top:1em;"> + <!-- FIXME SEB + <button class="btn pull-right " (click)="reset() "> + <span class="fa fa-eraser fa-size-x2"></span> + </button> + --> + <div class="col-md-12 text-center "> + <textarea rows="20" class="textarea-scroll" #scrollOutput>{{ cmdOutput }}</textarea> + </div> + </div> + + <nb-card-body> + <nb-actions size="medium" fullWidth> + <nb-action (click)="clean()"> + <i class="fa fa-eraser"></i> + <span>Clean</span> + </nb-action> + <nb-action (click)="preBuild()"> + <i class="nb-list"></i> + <span>Pre-Build</span> + </nb-action> + <nb-action (click)="build()"> + <i class="fa fa-wrench"></i> + <span>Build</span> + </nb-action> + <nb-action (click)="populate()"> + <i class="fa fa-send"></i> + <span>Populate</span> + </nb-action> + </nb-actions> + </nb-card-body> + + </nb-tab> + + <nb-tab tabTitle="Deploy"> + <span> Content deploy...</span> + </nb-tab> + <nb-tab tabTitle="Debug"> + <span> Content debug...</span> + </nb-tab> + </nb-tabset> + </nb-card> + </div> +</div> diff --git a/webapp/src/app/pages/build/build.component.scss b/webapp/src/app/pages/build/build.component.scss new file mode 100644 index 0000000..b256f66 --- /dev/null +++ b/webapp/src/app/pages/build/build.component.scss @@ -0,0 +1,37 @@ +@import '../../@theme/styles/themes'; +@import '~bootstrap/scss/mixins/breakpoints'; +@import '~@nebular/theme/styles/global/bootstrap/breakpoints'; +@include nb-install-component() { + nb-tabset { + height: 100%; + display: flex; + flex-direction: column; + } + nb-tab { + padding: nb-theme(padding); + } + /deep/ ngx-tab1, + /deep/ ngx-tab2 { + display: block; + padding: nb-theme(padding); + } + @include media-breakpoint-down(xs) { + nb-tabset /deep/ul { + font-size: 1rem; + padding: 0 0.25rem; + } + } +} + +nb-action { + i { + font-size: 2rem; + margin-right: 0.5rem; + } +} + +.textarea-scroll { + border-color: lightgray; + width: 97%; + overflow-y: scroll; +} diff --git a/webapp/src/app/pages/build/build.component.spec.ts b/webapp/src/app/pages/build/build.component.spec.ts new file mode 100644 index 0000000..016192c --- /dev/null +++ b/webapp/src/app/pages/build/build.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BuildComponent } from './build.component'; + +describe('BuildComponent', () => { + let component: BuildComponent; + let fixture: ComponentFixture<BuildComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ BuildComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BuildComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/webapp/src/app/pages/build/build.component.ts b/webapp/src/app/pages/build/build.component.ts new file mode 100644 index 0000000..5adb9bc --- /dev/null +++ b/webapp/src/app/pages/build/build.component.ts @@ -0,0 +1,198 @@ +import { Component, ViewEncapsulation, AfterViewChecked, ElementRef, ViewChild, OnInit, Input } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms'; +import { CookieService } from 'ngx-cookie'; + +import 'rxjs/add/operator/scan'; +import 'rxjs/add/operator/startWith'; + +import { XDSAgentService, ICmdOutput } from '../../@core-xds/services/xdsagent.service'; +import { ProjectService, IProject } from '../../@core-xds/services/project.service'; +import { AlertService, IAlert } from '../../@core-xds/services/alert.service'; +import { SdkService } from '../../@core-xds/services/sdk.service'; + +@Component({ + selector: 'xds-panel-build', + templateUrl: './build.component.html', + styleUrls: ['./build.component.scss'], + encapsulation: ViewEncapsulation.None, +}) + +export class BuildComponent implements OnInit, AfterViewChecked { + @ViewChild('scrollOutput') private scrollContainer: ElementRef; + + // FIXME workaround of https://github.com/angular/angular-cli/issues/2034 + // should be removed with angular 5 + // @Input() curProject: IProject; + @Input() curProject = <IProject>null; + + public buildForm: FormGroup; + public subpathCtrl = new FormControl('', Validators.required); + public debugEnable = false; + public buildIsCollapsed = false; + public cmdOutput: string; + public cmdInfo: string; + + private startTime: Map<string, number> = new Map<string, number>(); + + constructor( + private prjSvr: ProjectService, + private xdsSvr: XDSAgentService, + private fb: FormBuilder, + private alertSvr: AlertService, + private sdkSvr: SdkService, + private cookie: CookieService, + ) { + this.cmdOutput = ''; + this.cmdInfo = ''; // TODO: to be remove (only for debug) + this.buildForm = fb.group({ + subpath: this.subpathCtrl, + cmdClean: ['', Validators.nullValidator], + cmdPrebuild: ['', Validators.nullValidator], + cmdBuild: ['', Validators.nullValidator], + cmdPopulate: ['', Validators.nullValidator], + cmdArgs: ['', Validators.nullValidator], + envVars: ['', Validators.nullValidator], + }); + } + + ngOnInit() { + // Set default settings + // TODO save & restore values from cookies + this.buildForm.patchValue({ + subpath: '', + cmdClean: 'rm -rf build', + cmdPrebuild: 'mkdir -p build && cd build && cmake ..', + cmdBuild: 'cd build && make', + cmdPopulate: 'cd build && make remote-target-populate', + cmdArgs: '', + envVars: '', + }); + + // Command output data tunneling + this.xdsSvr.CmdOutput$.subscribe(data => { + this.cmdOutput += data.stdout; + this.cmdOutput += data.stderr; + }); + + // Command exit + this.xdsSvr.CmdExit$.subscribe(exit => { + if (this.startTime.has(exit.cmdID)) { + this.cmdInfo = 'Last command duration: ' + this._computeTime(this.startTime.get(exit.cmdID)); + this.startTime.delete(exit.cmdID); + } + + if (exit && exit.code !== 0) { + this.cmdOutput += '--- Command exited with code ' + exit.code + ' ---\n\n'; + } + }); + + this._scrollToBottom(); + + // only use for debug + this.debugEnable = (this.cookie.get('debug_build') === '1'); + } + + ngAfterViewChecked() { + this._scrollToBottom(); + } + + reset() { + this.cmdOutput = ''; + } + + clean() { + this._exec( + this.buildForm.value.cmdClean, + this.buildForm.value.subpath, + [], + this.buildForm.value.envVars); + } + + preBuild() { + this._exec( + this.buildForm.value.cmdPrebuild, + this.buildForm.value.subpath, + [], + this.buildForm.value.envVars); + } + + build() { + this._exec( + this.buildForm.value.cmdBuild, + this.buildForm.value.subpath, + [], + this.buildForm.value.envVars + ); + } + + populate() { + this._exec( + this.buildForm.value.cmdPopulate, + this.buildForm.value.subpath, + [], // args + this.buildForm.value.envVars + ); + } + + execCmd() { + this._exec( + this.buildForm.value.cmdArgs, + this.buildForm.value.subpath, + [], + this.buildForm.value.envVars + ); + } + + private _exec(cmd: string, dir: string, args: string[], env: string) { + if (!this.curProject) { + this.alertSvr.warning('No active project', true); + } + + // const prjID = this.curProject.id; + const prjID = this.prjSvr.getCurrent().id; + + this.cmdOutput += this._outputHeader(); + + const sdkid = this.sdkSvr.getCurrentId(); + + // Detect key=value in env string to build array of string + const envArr = []; + env.split(';').forEach(v => envArr.push(v.trim())); + + const t0 = performance.now(); + this.cmdInfo = 'Start build of ' + prjID + ' at ' + t0; + + this.xdsSvr.exec(prjID, dir, cmd, sdkid, args, envArr) + .subscribe(res => { + this.startTime.set(String(res.cmdID), t0); + }, + err => { + this.cmdInfo = 'Last command duration: ' + this._computeTime(t0); + this.alertSvr.error('ERROR: ' + err); + }); + } + + private _scrollToBottom(): void { + try { + this.scrollContainer.nativeElement.scrollTop = this.scrollContainer.nativeElement.scrollHeight; + } catch (err) { } + } + + private _computeTime(t0: number, t1?: number): string { + const enlap = Math.round((t1 || performance.now()) - t0); + if (enlap < 1000.0) { + return enlap.toFixed(2) + ' ms'; + } else { + return (enlap / 1000.0).toFixed(3) + ' seconds'; + } + } + + private _outputHeader(): string { + return '--- ' + new Date().toString() + ' ---\n'; + } + + private _outputFooter(): string { + return '\n'; + } +} diff --git a/webapp/src/app/pages/build/build.module.ts b/webapp/src/app/pages/build/build.module.ts new file mode 100644 index 0000000..ac1dfab --- /dev/null +++ b/webapp/src/app/pages/build/build.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { ThemeModule } from '../../@theme/theme.module'; + +import { BuildComponent } from './build.component'; +import { ProjectSelectDropdownComponent } from './settings/project-select-dropdown.component'; +import { SdkSelectDropdownComponent } from './settings/sdk-select-dropdown.component'; + +@NgModule({ + imports: [ + ThemeModule, + ], + declarations: [ + BuildComponent, + ProjectSelectDropdownComponent, + SdkSelectDropdownComponent, + ], + entryComponents: [ + ], +}) +export class BuildModule { } diff --git a/webapp/src/app/pages/build/settings/project-select-dropdown.component.ts b/webapp/src/app/pages/build/settings/project-select-dropdown.component.ts new file mode 100644 index 0000000..da3580a --- /dev/null +++ b/webapp/src/app/pages/build/settings/project-select-dropdown.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit, Input } from '@angular/core'; + +import { IProject, ProjectService } from '../../../@core-xds/services/project.service'; + +@Component({ + selector: 'xds-project-select-dropdown', + template: ` + <div class="form-group"> + <label>Project</label> + <select class="form-control"> + <option *ngFor="let prj of projects" (click)="select(prj)">{{prj.label}}</option> + </select> + </div> + `, +}) +export class ProjectSelectDropdownComponent implements OnInit { + + projects: IProject[]; + curPrj: IProject; + + constructor(private prjSvr: ProjectService) { } + + ngOnInit() { + this.curPrj = this.prjSvr.getCurrent(); + this.prjSvr.Projects$.subscribe((s) => { + if (s) { + this.projects = s; + if (this.curPrj === null || s.indexOf(this.curPrj) === -1) { + this.prjSvr.setCurrent(this.curPrj = s.length ? s[0] : null); + } + } + }); + } + + select(s) { + this.prjSvr.setCurrent(this.curPrj = s); + } +} + + diff --git a/webapp/src/app/pages/build/settings/sdk-select-dropdown.component.ts b/webapp/src/app/pages/build/settings/sdk-select-dropdown.component.ts new file mode 100644 index 0000000..562386d --- /dev/null +++ b/webapp/src/app/pages/build/settings/sdk-select-dropdown.component.ts @@ -0,0 +1,44 @@ +import { Component, OnInit, Input } from '@angular/core'; + +import { ISdk, SdkService } from '../../../@core-xds/services/sdk.service'; + +@Component({ + selector: 'xds-sdk-select-dropdown', + template: ` + <div class="form-group"> + <label>SDK</label> + <select class="form-control"> + <option *ngFor="let sdk of sdks" (click)="select(sdk)">{{sdk.name}}</option> + </select> + </div> + `, +}) +export class SdkSelectDropdownComponent implements OnInit { + + // FIXME investigate to understand why not working with sdks as input + // <xds-sdk-select-dropdown [sdks]="(sdks$ | async)"></xds-sdk-select-dropdown> + // @Input() sdks: ISdk[]; + sdks: ISdk[]; + + curSdk: ISdk; + + constructor(private sdkSvr: SdkService) { } + + ngOnInit() { + this.curSdk = this.sdkSvr.getCurrent(); + this.sdkSvr.Sdks$.subscribe((s) => { + if (s) { + this.sdks = s; + if (this.curSdk === null || s.indexOf(this.curSdk) === -1) { + this.sdkSvr.setCurrent(this.curSdk = s.length ? s[0] : null); + } + } + }); + } + + select(s) { + this.sdkSvr.setCurrent(this.curSdk = s); + } +} + + |