mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-28 05:36:35 +00:00
Align language server spawning with arduino-cli
This commit is contained in:
parent
2577451c15
commit
6618816330
@ -1,12 +1,8 @@
|
||||
import * as os from 'os';
|
||||
import * as which from 'which';
|
||||
import * as semver from 'semver';
|
||||
import { spawn } from 'child_process';
|
||||
import { join } from 'path';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { ILogger } from '@theia/core';
|
||||
import { FileUri } from '@theia/core/lib/node/file-uri';
|
||||
import { Config } from '../common/protocol/config-service';
|
||||
import { spawnCommand, getExecPath } from './exec-util';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoCli {
|
||||
@ -20,33 +16,19 @@ export class ArduinoCli {
|
||||
if (this.execPath) {
|
||||
return this.execPath;
|
||||
}
|
||||
const version = /\d+\.\d+\.\d+/;
|
||||
const cli = `arduino-cli${os.platform() === 'win32' ? '.exe' : ''}`;
|
||||
const buildCli = join(__dirname, '..', '..', 'build', cli);
|
||||
const buildVersion = await this.spawn(`"${buildCli}"`, ['version']);
|
||||
const buildShortVersion = (buildVersion.match(version) || [])[0];
|
||||
this.execPath = buildCli;
|
||||
const pathCli = await new Promise<string | undefined>(resolve => which(cli, (error, path) => resolve(error ? undefined : path)));
|
||||
if (!pathCli) {
|
||||
return buildCli;
|
||||
}
|
||||
const pathVersion = await this.spawn(`"${pathCli}"`, ['version']);
|
||||
const pathShortVersion = (pathVersion.match(version) || [])[0];
|
||||
if (semver.gt(pathShortVersion, buildShortVersion)) {
|
||||
this.execPath = pathCli;
|
||||
return pathCli;
|
||||
}
|
||||
return buildCli;
|
||||
const path = await getExecPath('arduino-cli', this.logger, 'version');
|
||||
this.execPath = path;
|
||||
return path;
|
||||
}
|
||||
|
||||
async getVersion(): Promise<string> {
|
||||
const execPath = await this.getExecPath();
|
||||
return this.spawn(`"${execPath}"`, ['version']);
|
||||
return spawnCommand(`"${execPath}"`, ['version'], this.logger);
|
||||
}
|
||||
|
||||
async getDefaultConfig(): Promise<Config> {
|
||||
const execPath = await this.getExecPath();
|
||||
const result = await this.spawn(`"${execPath}"`, ['config', 'dump', '--format', 'json']);
|
||||
const result = await spawnCommand(`"${execPath}"`, ['config', 'dump', '--format', 'json'], this.logger);
|
||||
const { directories } = JSON.parse(result);
|
||||
if (!directories) {
|
||||
throw new Error(`Could not parse config. 'directories' was missing from: ${result}`);
|
||||
@ -64,33 +46,4 @@ export class ArduinoCli {
|
||||
};
|
||||
}
|
||||
|
||||
private spawn(command: string, args?: string[]): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const buffers: Buffer[] = [];
|
||||
const cp = spawn(command, args, { windowsHide: true, shell: true });
|
||||
cp.stdout.on('data', (b: Buffer) => buffers.push(b));
|
||||
cp.on('error', error => {
|
||||
this.logger.error(`Error executing ${command} with args: ${JSON.stringify(args)}.`, error);
|
||||
reject(error);
|
||||
});
|
||||
cp.on('exit', (code, signal) => {
|
||||
if (code === 0) {
|
||||
const result = Buffer.concat(buffers).toString('utf8').trim()
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
if (signal) {
|
||||
this.logger.error(`Unexpected signal '${signal}' when executing ${command} with args: ${JSON.stringify(args)}.`);
|
||||
reject(new Error(`Process exited with signal: ${signal}`));
|
||||
return;
|
||||
}
|
||||
if (code) {
|
||||
this.logger.error(`Unexpected exit code '${code}' when executing ${command} with args: ${JSON.stringify(args)}.`);
|
||||
reject(new Error(`Process exited with exit code: ${code}`));
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
60
arduino-ide-extension/src/node/exec-util.ts
Normal file
60
arduino-ide-extension/src/node/exec-util.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import * as os from 'os';
|
||||
import * as which from 'which';
|
||||
import * as semver from 'semver';
|
||||
import { spawn } from 'child_process';
|
||||
import { join } from 'path';
|
||||
import { ILogger } from '@theia/core';
|
||||
|
||||
export async function getExecPath(commandName: string, logger: ILogger, versionArg?: string, inBinDir?: boolean): Promise<string> {
|
||||
const execName = `${commandName}${os.platform() === 'win32' ? '.exe' : ''}`;
|
||||
const relativePath = ['..', '..', 'build'];
|
||||
if (inBinDir) {
|
||||
relativePath.push('bin');
|
||||
}
|
||||
const buildCommand = join(__dirname, ...relativePath, execName);
|
||||
if (!versionArg) {
|
||||
return buildCommand;
|
||||
}
|
||||
const versionRegexp = /\d+\.\d+\.\d+/;
|
||||
const buildVersion = await spawnCommand(`"${buildCommand}"`, [versionArg], logger);
|
||||
const buildShortVersion = (buildVersion.match(versionRegexp) || [])[0];
|
||||
const pathCommand = await new Promise<string | undefined>(resolve => which(execName, (error, path) => resolve(error ? undefined : path)));
|
||||
if (!pathCommand) {
|
||||
return buildCommand;
|
||||
}
|
||||
const pathVersion = await spawnCommand(`"${pathCommand}"`, [versionArg], logger);
|
||||
const pathShortVersion = (pathVersion.match(versionRegexp) || [])[0];
|
||||
if (semver.gt(pathShortVersion, buildShortVersion)) {
|
||||
return pathCommand;
|
||||
}
|
||||
return buildCommand;
|
||||
}
|
||||
|
||||
export function spawnCommand(command: string, args: string[], logger: ILogger): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const cp = spawn(command, args, { windowsHide: true, shell: true });
|
||||
const buffers: Buffer[] = [];
|
||||
cp.stdout.on('data', (b: Buffer) => buffers.push(b));
|
||||
cp.on('error', error => {
|
||||
logger.error(`Error executing ${command} with args: ${JSON.stringify(args)}.`, error);
|
||||
reject(error);
|
||||
});
|
||||
cp.on('exit', (code, signal) => {
|
||||
if (code === 0) {
|
||||
const result = Buffer.concat(buffers).toString('utf8').trim()
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
if (signal) {
|
||||
logger.error(`Unexpected signal '${signal}' when executing ${command} with args: ${JSON.stringify(args)}.`);
|
||||
reject(new Error(`Process exited with signal: ${signal}`));
|
||||
return;
|
||||
}
|
||||
if (code) {
|
||||
logger.error(`Unexpected exit code '${code}' when executing ${command} with args: ${JSON.stringify(args)}.`);
|
||||
reject(new Error(`Process exited with exit code: ${code}`));
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import * as which from 'which';
|
||||
import * as os from 'os';
|
||||
import { join, delimiter } from 'path';
|
||||
import { injectable } from 'inversify';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { ILogger } from '@theia/core';
|
||||
import { BaseLanguageServerContribution, IConnection, LanguageServerStartOptions } from '@theia/languages/lib/node';
|
||||
import { Board } from '../../common/protocol/boards-service';
|
||||
import { getExecPath } from '../exec-util';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoLanguageServerContribution extends BaseLanguageServerContribution {
|
||||
@ -23,10 +23,13 @@ export class ArduinoLanguageServerContribution extends BaseLanguageServerContrib
|
||||
return this.description.name;
|
||||
}
|
||||
|
||||
@inject(ILogger)
|
||||
protected logger: ILogger;
|
||||
|
||||
async start(clientConnection: IConnection, options: LanguageServerStartOptions): Promise<void> {
|
||||
const languageServer = await this.resolveExecutable('arduino-language-server');
|
||||
const clangd = await this.resolveExecutable('clangd');
|
||||
const cli = await this.resolveExecutable('arduino-cli');
|
||||
const languageServer = await getExecPath('arduino-language-server', this.logger);
|
||||
const clangd = await getExecPath('clangd', this.logger, '--version', os.platform() !== 'win32');
|
||||
const cli = await getExecPath('arduino-cli', this.logger, 'version');
|
||||
// Add '-log' argument to enable logging to files
|
||||
const args: string[] = ['-clangd', clangd, '-cli', cli];
|
||||
if (options.parameters && options.parameters.selectedBoard) {
|
||||
@ -45,21 +48,4 @@ export class ArduinoLanguageServerContribution extends BaseLanguageServerContrib
|
||||
serverConnection.onClose(() => (clientConnection as any).reader.socket.close());
|
||||
}
|
||||
|
||||
protected resolveExecutable(name: string): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const segments = ['..', '..', '..', 'build'];
|
||||
if (name === 'clangd' && os.platform() !== 'win32') {
|
||||
segments.push('bin');
|
||||
}
|
||||
const path = `${process.env.PATH}${delimiter}${join(__dirname, ...segments)}`;
|
||||
const suffix = os.platform() === 'win32' ? '.exe' : '';
|
||||
which(name + suffix, { path }, (err, execPath) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(execPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user