From f9641a3d76d46d1d9a52809bf22e58aea38bc869 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Tue, 13 Aug 2019 09:09:29 +0200 Subject: [PATCH] Initial support of the default paths from the CLI. Signed-off-by: Akos Kitta --- .vscode/tasks.json | 12 +-- arduino-ide-browser/package.json | 2 +- arduino-ide-electron/package.json | 2 +- .../browser/arduino-frontend-contribution.tsx | 19 ----- .../src/node/arduino-backend-module.ts | 11 +++ .../src/node/arduino-cli-contribution.ts | 27 +++++++ arduino-ide-extension/src/node/arduino-cli.ts | 79 +++++++++++++++++++ .../src/node/arduino-daemon.ts | 30 +++---- .../src/node/core-client-provider-impl.ts | 40 +++++----- package.json | 6 +- 10 files changed, 161 insertions(+), 67 deletions(-) create mode 100644 arduino-ide-extension/src/node/arduino-cli-contribution.ts create mode 100644 arduino-ide-extension/src/node/arduino-cli.ts diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9ce4065b..9f66b9c2 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,7 @@ "version": "2.0.0", "tasks": [ { - "label": "Arduino-PoC - Start Browser Example", + "label": "Arduino Editor - Start Browser Example", "type": "shell", "command": "yarn --cwd ./arduino-ide-browser start", "group": "build", @@ -15,7 +15,7 @@ } }, { - "label": "Arduino-PoC - Watch Theia Extension", + "label": "Arduino Editor - Watch Theia Extension", "type": "shell", "command": "yarn --cwd ./arduino-ide-extension watch", "group": "build", @@ -26,7 +26,7 @@ } }, { - "label": "Arduino-PoC - Watch Browser Example", + "label": "Arduino Editor - Watch Browser Example", "type": "shell", "command": "yarn --cwd ./arduino-ide-browser watch", "group": "build", @@ -37,11 +37,11 @@ } }, { - "label": "Arduino-PoC - Watch All", + "label": "Arduino Editor - Watch All", "type": "shell", "dependsOn": [ - "Arduino-PoC - Watch Theia Extension", - "Arduino-PoC - Watch Browser Example" + "Arduino Editor - Watch Theia Extension", + "Arduino Editor - Watch Browser Example" ] } ] diff --git a/arduino-ide-browser/package.json b/arduino-ide-browser/package.json index afa5811d..04d45922 100644 --- a/arduino-ide-browser/package.json +++ b/arduino-ide-browser/package.json @@ -30,7 +30,7 @@ "theia": { "frontend": { "config": { - "applicationName": "Arduino-PoC", + "applicationName": "Arduino Editor", "defaultTheme": "arduino-theme", "preferences": { "editor.autoSave": "on" diff --git a/arduino-ide-electron/package.json b/arduino-ide-electron/package.json index f3392091..ed1b2639 100644 --- a/arduino-ide-electron/package.json +++ b/arduino-ide-electron/package.json @@ -33,7 +33,7 @@ "target": "electron", "frontend": { "config": { - "applicationName": "Arduino-PoC", + "applicationName": "Arduino Editor", "defaultTheme": "arduino-theme", "preferences": { "editor.autoSave": "on" diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index e62c192e..0872fc39 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -543,25 +543,6 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C return undefined; } - // private async onNoBoardsInstalled() { - // const action = await this.messageService.info("You have no boards installed. Use the boards manager to install one.", "Open Boards Manager"); - // if (!action) { - // return; - // } - - // this.boardsListWidgetFrontendContribution.openView({ reveal: true }); - // } - - // private async onUnknownBoard() { - // const action = await this.messageService.warn("There's a board connected for which you need to install software." + - // " If this were not a PoC we would offer you the right package now.", "Open Boards Manager"); - // if (!action) { - // return; - // } - - // this.boardsListWidgetFrontendContribution.openView({ reveal: true }); - // } - private isArduinoToolbar(maybeToolbarWidget: any): boolean { if (maybeToolbarWidget instanceof ArduinoToolbar) { return true; diff --git a/arduino-ide-extension/src/node/arduino-backend-module.ts b/arduino-ide-extension/src/node/arduino-backend-module.ts index 3d50f866..d6226a09 100644 --- a/arduino-ide-extension/src/node/arduino-backend-module.ts +++ b/arduino-ide-extension/src/node/arduino-backend-module.ts @@ -22,8 +22,19 @@ import { SketchesService, SketchesServicePath } from '../common/protocol/sketche import { MonitorServiceImpl } from './monitor/monitor-service-impl'; import { MonitorService, MonitorServicePath, MonitorServiceClient } from '../common/protocol/monitor-service'; import { MonitorClientProvider } from './monitor/monitor-client-provider'; +import { ArduinoCli } from './arduino-cli'; +import { ArduinoCliContribution } from './arduino-cli-contribution'; +import { CliContribution } from '@theia/core/lib/node'; export default new ContainerModule((bind, unbind, isBound, rebind) => { + // Theia backend CLI contribution. + bind(ArduinoCliContribution).toSelf().inSingletonScope(); + bind(CliContribution).toService(ArduinoCliContribution); + + // Provides the path of the Ardunio CLI. + bind(ArduinoCli).toSelf().inSingletonScope(); + + // Shared daemonn bind(ArduinoDaemon).toSelf().inSingletonScope(); bind(BackendApplicationContribution).toService(ArduinoDaemon); diff --git a/arduino-ide-extension/src/node/arduino-cli-contribution.ts b/arduino-ide-extension/src/node/arduino-cli-contribution.ts new file mode 100644 index 00000000..148b92b5 --- /dev/null +++ b/arduino-ide-extension/src/node/arduino-cli-contribution.ts @@ -0,0 +1,27 @@ +import { injectable } from 'inversify'; +import { Argv, Arguments } from 'yargs'; +import { CliContribution } from '@theia/core/lib/node'; + +@injectable() +export class ArduinoCliContribution implements CliContribution { + + protected _debugCli = false + + configure(conf: Argv): void { + conf.option('debug-cli', { + description: 'Can be specified if the CLI daemon process was started externally.', + type: 'boolean', + default: false, + nargs: 1 + }); + } + + setArguments(args: Arguments): void { + this._debugCli = args['debug-cli']; + } + + get debugCli(): boolean { + return this._debugCli; + } + +} diff --git a/arduino-ide-extension/src/node/arduino-cli.ts b/arduino-ide-extension/src/node/arduino-cli.ts new file mode 100644 index 00000000..66514f92 --- /dev/null +++ b/arduino-ide-extension/src/node/arduino-cli.ts @@ -0,0 +1,79 @@ +import * as os from 'os'; +import * as which from 'which'; +import * as cp from 'child_process'; +import { join, delimiter } from 'path'; +import { injectable } from 'inversify'; + +@injectable() +export class ArduinoCli { + + async getExecPath(): Promise { + const build = join(__dirname, '..', '..', 'build'); + return new Promise((resolve, reject) => { + which(`arduino-cli${os.platform() === 'win32' ? '.exe' : ''}`, { path: `${process.env.PATH}${delimiter}${build}` }, (err, path) => { + if (err) { + reject(err); + return; + } + resolve(path); + }); + }); + } + + async getDefaultConfig(): Promise { + const command = await this.getExecPath(); + return new Promise((resolve, reject) => { + cp.execFile( + command, + ['config', 'dump', '--format', 'json'], + { encoding: 'utf8' }, + (error, stdout, stderr) => { + + if (error) { + throw error; + } + + if (stderr) { + throw new Error(stderr); + } + + // const { sketchbook_path: sketchDirPath, arduino_data: dataDirPath } = JSON.parse(raw); + + // https://github.com/arduino/arduino-cli/issues/342 + // XXX: this is a hack. The CLI provides a non-valid JSON. + const config: Partial = {}; + const raw = stdout.trim(); + for (const line of raw.split(/\r?\n/) || []) { + // TODO: Named capture groups are avail from ES2018. + // const pair = line.match(/(?[^:]+):(?[^,]+),?/); + const pair = line.split(':').map(entry => entry.trim()); + if (pair[0] === 'sketchbook_path') { + config.sketchDirPath = pair[1]; + } else if (pair[0] === 'arduino_data') { + config.dataDirPath = pair[1]; + } + } + + if (!config.dataDirPath) { + reject(new Error(`Could not parse config. 'arduino_data' was missing from: ${stdout}`)); + return; + } + + if (!config.sketchDirPath) { + reject(new Error(`Could not parse config. 'sketchbook_path' was missing from: ${stdout}`)); + return; + } + + resolve({ sketchDirPath: config.sketchDirPath, dataDirPath: config.dataDirPath }); + }); + }); + } + +} + +export namespace ArduinoCli { + export interface Config { + sketchDirPath: string; + dataDirPath: string; + } +} diff --git a/arduino-ide-extension/src/node/arduino-daemon.ts b/arduino-ide-extension/src/node/arduino-daemon.ts index 767a3452..9141eca6 100644 --- a/arduino-ide-extension/src/node/arduino-daemon.ts +++ b/arduino-ide-extension/src/node/arduino-daemon.ts @@ -1,6 +1,4 @@ -import * as which from 'which'; -import * as os from 'os'; -import { join, delimiter } from 'path'; +import { join } from 'path'; import { exec, spawn, SpawnOptions } from 'child_process'; import { inject, injectable, named } from 'inversify'; import { ILogger } from '@theia/core/lib/common/logger'; @@ -9,17 +7,22 @@ import { Deferred } from '@theia/core/lib/common/promise-util'; import { environment } from '@theia/application-package/lib/environment'; import { DaemonLog } from './daemon-log'; import { ToolOutputServiceServer } from '../common/protocol/tool-output-service'; +import { ArduinoCliContribution } from './arduino-cli-contribution'; +import { ArduinoCli } from './arduino-cli'; @injectable() export class ArduinoDaemon implements BackendApplicationContribution { - // Set this to `true` if you want to connect to a CLI running in debug mode. - static DEBUG_CLI = false; - @inject(ILogger) @named('daemon') protected readonly logger: ILogger + @inject(ArduinoCli) + protected readonly cli: ArduinoCli; + + @inject(ArduinoCliContribution) + protected readonly cliContribution: ArduinoCliContribution; + @inject(ToolOutputServiceServer) protected readonly toolOutputService: ToolOutputServiceServer; @@ -27,17 +30,8 @@ export class ArduinoDaemon implements BackendApplicationContribution { async onStart() { try { - if (!ArduinoDaemon.DEBUG_CLI) { - const build = join(__dirname, '..', '..', 'build'); - const executable = await new Promise((resolve, reject) => { - which(`arduino-cli${os.platform() === 'win32' ? '.exe' : ''}`, { path: `${process.env.PATH}${delimiter}${build}` }, (err, path) => { - if (err) { - reject(err); - return; - } - resolve(path); - }); - }); + if (!this.cliContribution.debugCli) { + const executable = await this.cli.getExecPath(); this.logger.info(`>>> Starting 'arduino-cli' daemon... [${executable}]`); const daemon = exec(`${executable} --debug daemon`, (err, stdout, stderr) => { if (err || stderr) { @@ -74,7 +68,7 @@ export class ArduinoDaemon implements BackendApplicationContribution { await new Promise(resolve => setTimeout(resolve, 2000)); this.isReady.resolve(); - if (!ArduinoDaemon.DEBUG_CLI) { + if (!this.cliContribution.debugCli) { this.logger.info(`<<< The 'arduino-cli' daemon is up an running.`); } else { this.logger.info(`Assuming the 'arduino-cli' already runs in debug mode.`); diff --git a/arduino-ide-extension/src/node/core-client-provider-impl.ts b/arduino-ide-extension/src/node/core-client-provider-impl.ts index f259259b..a796c9af 100644 --- a/arduino-ide-extension/src/node/core-client-provider-impl.ts +++ b/arduino-ide-extension/src/node/core-client-provider-impl.ts @@ -1,5 +1,11 @@ -import { inject, injectable } from 'inversify'; +import * as fs from 'fs'; import * as grpc from '@grpc/grpc-js'; +import * as PQueue from 'p-queue'; +import { inject, injectable } from 'inversify'; +import URI from '@theia/core/lib/common/uri'; +import { FileSystem } from '@theia/filesystem/lib/common'; +import { WorkspaceServiceExt } from '../browser/workspace-service-ext'; +import { ToolOutputServiceServer } from '../common/protocol/tool-output-service'; import { ArduinoCoreClient } from './cli-protocol/commands/commands_grpc_pb'; import { InitResp, @@ -10,16 +16,9 @@ import { UpdateLibrariesIndexReq, UpdateLibrariesIndexResp } from './cli-protocol/commands/commands_pb'; -import { WorkspaceServiceExt } from '../browser/workspace-service-ext'; -import { FileSystem } from '@theia/filesystem/lib/common'; -import URI from '@theia/core/lib/common/uri'; -import { CoreClientProvider, Client } from './core-client-provider'; -import * as PQueue from 'p-queue'; -import { ToolOutputServiceServer } from '../common/protocol/tool-output-service'; +import { ArduinoCli } from './arduino-cli'; import { Instance } from './cli-protocol/commands/common_pb'; -import * as fs from 'fs-extra'; -import * as path from 'path'; -import * as os from 'os'; +import { CoreClientProvider, Client } from './core-client-provider'; @injectable() export class CoreClientProviderImpl implements CoreClientProvider { @@ -36,6 +35,9 @@ export class CoreClientProviderImpl implements CoreClientProvider { @inject(ToolOutputServiceServer) protected readonly toolOutputService: ToolOutputServiceServer; + @inject(ArduinoCli) + protected readonly cli: ArduinoCli; + async getClient(workspaceRootOrResourceUri?: string): Promise { return this.clientRequestQueue.add(() => new Promise(async resolve => { const roots = await this.workspaceServiceExt.roots(); @@ -76,19 +78,19 @@ export class CoreClientProviderImpl implements CoreClientProvider { throw new Error(`Could not resolve filesystem path of URI: ${rootUri}.`); } - const defaultDownloadsDirPath = path.resolve(os.homedir(), 'Arduino-PoC', 'downloads'); - if (!fs.existsSync(defaultDownloadsDirPath)) { - fs.mkdirpSync(defaultDownloadsDirPath); + const { dataDirPath, sketchDirPath } = await this.cli.getDefaultConfig(); + + if (!fs.existsSync(dataDirPath)) { + throw new Error(`Data dir path does not exist: ${dataDirPath}.`); } - const defaultDataDirPath = path.resolve(os.homedir(), 'Arduino-PoC', 'data') - if (!fs.existsSync(defaultDataDirPath)) { - fs.mkdirpSync(defaultDataDirPath); + if (!fs.existsSync(sketchDirPath)) { + throw new Error(`Sketch dir path does not exist: ${sketchDirPath}.`); } - config.setSketchbookdir(rootPath); - config.setDatadir(defaultDataDirPath); - config.setDownloadsdir(defaultDownloadsDirPath); + config.setSketchbookdir(sketchDirPath); + config.setDatadir(dataDirPath); + config.setDownloadsdir(dataDirPath); config.setBoardmanageradditionalurlsList(['https://downloads.arduino.cc/packages/package_index.json']); const initReq = new InitReq(); diff --git a/package.json b/package.json index 86a71718..8c83c919 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "arduino-poc", + "name": "arduino-editor", "version": "0.0.1", - "description": "A PoC demonstrating an Arduino IDE built using Eclipse Theia", + "description": "Arduino IDE built using Eclipse Theia", "main": "index.js", "repository": "https://github.com/bcmi-labs/arduino-editor.git", "author": "Christian Weichel ", @@ -14,7 +14,7 @@ "prepare": "lerna run prepare", "rebuild:browser": "theia rebuild:browser", "rebuild:electron": "theia rebuild:electron", - "start": "cd arduino-ide-browser && yarn start", + "start": "yarn --cwd ./arduino-ide-browser start", "watch": "lerna run watch --parallel" }, "workspaces": [