arduino-ide/arduino-ide-extension/src/node/core-service-impl.ts
Alberto Iannaccone 7f8b227c39
[ATL-1531] Integrate arduino-cli 0.19.1 (#506)
* integrate cli 0.19.0

* Update CLI version used to fix crash on lib/core install/uninstall

* Update CLI version

* Update CLI version

* update cli version

Co-authored-by: Silvano Cerza <silvanocerza@gmail.com>
2021-09-30 09:02:09 +01:00

238 lines
7.8 KiB
TypeScript

import { FileUri } from '@theia/core/lib/node/file-uri';
import { inject, injectable } from 'inversify';
import { relative } from 'path';
import * as jspb from 'google-protobuf';
import { BoolValue } from 'google-protobuf/google/protobuf/wrappers_pb';
import { ClientReadableStream } from '@grpc/grpc-js';
import { CompilerWarnings, CoreService } from '../common/protocol/core-service';
import {
CompileRequest,
CompileResponse,
} from './cli-protocol/cc/arduino/cli/commands/v1/compile_pb';
import { CoreClientAware } from './core-client-provider';
import {
BurnBootloaderRequest,
BurnBootloaderResponse,
UploadRequest,
UploadResponse,
UploadUsingProgrammerRequest,
UploadUsingProgrammerResponse,
} from './cli-protocol/cc/arduino/cli/commands/v1/upload_pb';
import { ResponseService } from '../common/protocol/response-service';
import { NotificationServiceServer } from '../common/protocol';
import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
import { firstToUpperCase, firstToLowerCase } from '../common/utils';
import { Port } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb';
@injectable()
export class CoreServiceImpl extends CoreClientAware implements CoreService {
@inject(ResponseService)
protected readonly responseService: ResponseService;
@inject(NotificationServiceServer)
protected readonly notificationService: NotificationServiceServer;
async compile(
options: CoreService.Compile.Options & {
exportBinaries?: boolean;
compilerWarnings?: CompilerWarnings;
}
): Promise<void> {
const { sketchUri, fqbn, compilerWarnings } = options;
const sketchPath = FileUri.fsPath(sketchUri);
await this.coreClientProvider.initialized;
const coreClient = await this.coreClient();
const { client, instance } = coreClient;
const compileReq = new CompileRequest();
compileReq.setInstance(instance);
compileReq.setSketchPath(sketchPath);
if (fqbn) {
compileReq.setFqbn(fqbn);
}
if (compilerWarnings) {
compileReq.setWarnings(compilerWarnings.toLowerCase());
}
compileReq.setOptimizeForDebug(options.optimizeForDebug);
compileReq.setPreprocess(false);
compileReq.setVerbose(options.verbose);
compileReq.setQuiet(false);
if (typeof options.exportBinaries === 'boolean') {
const exportBinaries = new BoolValue();
exportBinaries.setValue(options.exportBinaries);
compileReq.setExportBinaries(exportBinaries);
}
this.mergeSourceOverrides(compileReq, options);
const result = client.compile(compileReq);
try {
await new Promise<void>((resolve, reject) => {
result.on('data', (cr: CompileResponse) => {
this.responseService.appendToOutput({
chunk: Buffer.from(cr.getOutStream_asU8()).toString(),
});
this.responseService.appendToOutput({
chunk: Buffer.from(cr.getErrStream_asU8()).toString(),
});
});
result.on('error', (error) => reject(error));
result.on('end', () => resolve());
});
this.responseService.appendToOutput({
chunk: '\n--------------------------\nCompilation complete.\n',
});
} catch (e) {
this.responseService.appendToOutput({
chunk: `Compilation error: ${e}\n`,
severity: 'error',
});
throw e;
}
}
async upload(options: CoreService.Upload.Options): Promise<void> {
await this.doUpload(
options,
() => new UploadRequest(),
(client, req) => client.upload(req)
);
}
async uploadUsingProgrammer(
options: CoreService.Upload.Options
): Promise<void> {
await this.doUpload(
options,
() => new UploadUsingProgrammerRequest(),
(client, req) => client.uploadUsingProgrammer(req),
'upload using programmer'
);
}
protected async doUpload(
options: CoreService.Upload.Options,
requestProvider: () => UploadRequest | UploadUsingProgrammerRequest,
// tslint:disable-next-line:max-line-length
responseHandler: (
client: ArduinoCoreServiceClient,
req: UploadRequest | UploadUsingProgrammerRequest
) => ClientReadableStream<UploadResponse | UploadUsingProgrammerResponse>,
task = 'upload'
): Promise<void> {
await this.compile(Object.assign(options, { exportBinaries: false }));
const { sketchUri, fqbn, port, programmer } = options;
const sketchPath = FileUri.fsPath(sketchUri);
await this.coreClientProvider.initialized;
const coreClient = await this.coreClient();
const { client, instance } = coreClient;
const req = requestProvider();
req.setInstance(instance);
req.setSketchPath(sketchPath);
if (fqbn) {
req.setFqbn(fqbn);
}
const p = new Port();
if (port) {
p.setAddress(port.address);
p.setLabel(port.label || '');
p.setProtocol(port.protocol);
}
req.setPort(p);
if (programmer) {
req.setProgrammer(programmer.id);
}
req.setVerbose(options.verbose);
req.setVerify(options.verify);
const result = responseHandler(client, req);
try {
await new Promise<void>((resolve, reject) => {
result.on('data', (resp: UploadResponse) => {
this.responseService.appendToOutput({
chunk: Buffer.from(resp.getOutStream_asU8()).toString(),
});
this.responseService.appendToOutput({
chunk: Buffer.from(resp.getErrStream_asU8()).toString(),
});
});
result.on('error', (error) => reject(error));
result.on('end', () => resolve());
});
this.responseService.appendToOutput({
chunk:
'\n--------------------------\n' +
firstToLowerCase(task) +
' complete.\n',
});
} catch (e) {
this.responseService.appendToOutput({
chunk: `${firstToUpperCase(task)} error: ${e}\n`,
severity: 'error',
});
throw e;
}
}
async burnBootloader(options: CoreService.Bootloader.Options): Promise<void> {
await this.coreClientProvider.initialized;
const coreClient = await this.coreClient();
const { client, instance } = coreClient;
const { fqbn, port, programmer } = options;
const burnReq = new BurnBootloaderRequest();
burnReq.setInstance(instance);
if (fqbn) {
burnReq.setFqbn(fqbn);
}
const p = new Port();
if (port) {
p.setAddress(port.address);
p.setLabel(port.label || '');
p.setProtocol(port.protocol);
}
burnReq.setPort(p);
if (programmer) {
burnReq.setProgrammer(programmer.id);
}
burnReq.setVerify(options.verify);
burnReq.setVerbose(options.verbose);
const result = client.burnBootloader(burnReq);
try {
await new Promise<void>((resolve, reject) => {
result.on('data', (resp: BurnBootloaderResponse) => {
this.responseService.appendToOutput({
chunk: Buffer.from(resp.getOutStream_asU8()).toString(),
});
this.responseService.appendToOutput({
chunk: Buffer.from(resp.getErrStream_asU8()).toString(),
});
});
result.on('error', (error) => reject(error));
result.on('end', () => resolve());
});
} catch (e) {
this.responseService.appendToOutput({
chunk: `Error while burning the bootloader: ${e}\n`,
severity: 'error',
});
throw e;
}
}
private mergeSourceOverrides(
req: { getSourceOverrideMap(): jspb.Map<string, string> },
options: CoreService.Compile.Options
): void {
const sketchPath = FileUri.fsPath(options.sketchUri);
for (const uri of Object.keys(options.sourceOverride)) {
const content = options.sourceOverride[uri];
if (content) {
const relativePath = relative(sketchPath, FileUri.fsPath(uri));
req.getSourceOverrideMap().set(relativePath, content);
}
}
}
}