Initial support of the default paths from the CLI.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta 2019-08-13 09:09:29 +02:00 committed by jbicker
parent 59553bf81f
commit f9641a3d76
10 changed files with 161 additions and 67 deletions

12
.vscode/tasks.json vendored
View File

@ -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"
]
}
]

View File

@ -30,7 +30,7 @@
"theia": {
"frontend": {
"config": {
"applicationName": "Arduino-PoC",
"applicationName": "Arduino Editor",
"defaultTheme": "arduino-theme",
"preferences": {
"editor.autoSave": "on"

View File

@ -33,7 +33,7 @@
"target": "electron",
"frontend": {
"config": {
"applicationName": "Arduino-PoC",
"applicationName": "Arduino Editor",
"defaultTheme": "arduino-theme",
"preferences": {
"editor.autoSave": "on"

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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<string> {
const build = join(__dirname, '..', '..', 'build');
return new Promise<string>((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<ArduinoCli.Config> {
const command = await this.getExecPath();
return new Promise<ArduinoCli.Config>((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<ArduinoCli.Config> = {};
const raw = stdout.trim();
for (const line of raw.split(/\r?\n/) || []) {
// TODO: Named capture groups are avail from ES2018.
// const pair = line.match(/(?<key>[^:]+):(?<value>[^,]+),?/);
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;
}
}

View File

@ -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<string>((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.`);

View File

@ -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<Client | undefined> {
return this.clientRequestQueue.add(() => new Promise<Client | undefined>(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();

View File

@ -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 <christian.weichel@typefox.io>",
@ -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": [