mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-06-16 09:06:33 +00:00
Improved variable resolution and error handling
This commit is contained in:
parent
1441b685ee
commit
a886a106f5
@ -2,9 +2,12 @@
|
|||||||
import { VariableContribution, VariableRegistry, Variable } from '@theia/variable-resolver/lib/browser';
|
import { VariableContribution, VariableRegistry, Variable } from '@theia/variable-resolver/lib/browser';
|
||||||
import { injectable, inject } from 'inversify';
|
import { injectable, inject } from 'inversify';
|
||||||
import URI from '@theia/core/lib/common/uri';
|
import URI from '@theia/core/lib/common/uri';
|
||||||
|
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||||
|
import { ApplicationShell, Navigatable } from '@theia/core/lib/browser';
|
||||||
|
import { FileStat, FileSystem } from '@theia/filesystem/lib/common';
|
||||||
|
import { WorkspaceVariableContribution } from '@theia/workspace/lib/browser/workspace-variable-contribution';
|
||||||
import { BoardsServiceClientImpl } from 'arduino-ide-extension/lib/browser/boards/boards-service-client-impl';
|
import { BoardsServiceClientImpl } from 'arduino-ide-extension/lib/browser/boards/boards-service-client-impl';
|
||||||
import { BoardsService, ToolLocations } from 'arduino-ide-extension/lib/common/protocol/boards-service';
|
import { BoardsService, ToolLocations } from 'arduino-ide-extension/lib/common/protocol/boards-service';
|
||||||
import { WorkspaceVariableContribution } from '@theia/workspace/lib/browser/workspace-variable-contribution';
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ArduinoVariableResolver implements VariableContribution {
|
export class ArduinoVariableResolver implements VariableContribution {
|
||||||
@ -18,64 +21,134 @@ export class ArduinoVariableResolver implements VariableContribution {
|
|||||||
@inject(WorkspaceVariableContribution)
|
@inject(WorkspaceVariableContribution)
|
||||||
protected readonly workspaceVars: WorkspaceVariableContribution;
|
protected readonly workspaceVars: WorkspaceVariableContribution;
|
||||||
|
|
||||||
|
@inject(ApplicationShell)
|
||||||
|
protected readonly applicationShell: ApplicationShell;
|
||||||
|
|
||||||
|
@inject(FileSystem)
|
||||||
|
protected readonly fileSystem: FileSystem;
|
||||||
|
|
||||||
|
@inject(MessageService)
|
||||||
|
protected readonly messageService: MessageService
|
||||||
|
|
||||||
registerVariables(variables: VariableRegistry): void {
|
registerVariables(variables: VariableRegistry): void {
|
||||||
variables.registerVariable(<Variable>{
|
variables.registerVariable(<Variable>{
|
||||||
name: `boardTools`,
|
name: 'boardTools',
|
||||||
description: "Provides paths and access to board specific tooling",
|
description: 'Provides paths and access to board specific tooling',
|
||||||
resolve: this.resolveBoardTools.bind(this),
|
resolve: this.resolveBoardTools.bind(this),
|
||||||
});
|
});
|
||||||
variables.registerVariable(<Variable>{
|
variables.registerVariable(<Variable>{
|
||||||
name: "board",
|
name: 'board',
|
||||||
description: "Provides details about the currently selected board",
|
description: 'Provides details about the currently selected board',
|
||||||
resolve: this.resolveBoard.bind(this),
|
resolve: this.resolveBoard.bind(this),
|
||||||
});
|
});
|
||||||
|
|
||||||
variables.registerVariable({
|
variables.registerVariable({
|
||||||
name: "sketchBinary",
|
name: 'sketchBinary',
|
||||||
description: "Path to the sketch's binary file",
|
description: 'Path to the sketch\'s binary file',
|
||||||
resolve: this.resolveSketchBinary.bind(this)
|
resolve: this.resolveSketchBinary.bind(this)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this function is a total hack. Instead of botching around with URI's it should ask something on the backend
|
protected resolveSketchBinary(context?: URI, argument?: string, configurationSection?: string): Promise<Object | undefined> {
|
||||||
// that properly udnerstands the filesystem.
|
if (argument) {
|
||||||
protected async resolveSketchBinary(context?: URI, argument?: string, configurationSection?: string): Promise<Object> {
|
return this.resolveBinaryWithHint(argument);
|
||||||
let sketchPath = argument || this.workspaceVars.getResourceUri()!.path.toString();
|
}
|
||||||
return sketchPath.substring(0, sketchPath.length - 3) + "arduino.samd.arduino_zero_edbg.elf";
|
const resourceUri = this.workspaceVars.getResourceUri();
|
||||||
|
if (resourceUri) {
|
||||||
|
return this.resolveBinaryWithHint(resourceUri.toString());
|
||||||
|
}
|
||||||
|
for (const tabBar of this.applicationShell.mainAreaTabBars) {
|
||||||
|
if (tabBar.currentTitle && Navigatable.is(tabBar.currentTitle.owner)) {
|
||||||
|
const uri = tabBar.currentTitle.owner.getResourceUri();
|
||||||
|
if (uri) {
|
||||||
|
return this.resolveBinaryWithHint(uri.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.messageService.error('No sketch available. Please open a sketch to start debugging.');
|
||||||
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async resolveBoard(context?: URI, argument?: string, configurationSection?: string): Promise<Object> {
|
private async resolveBinaryWithHint(hint: string): Promise<string | undefined> {
|
||||||
const { boardsConfig } = this.boardsServiceClient;
|
const fileStat = await this.fileSystem.getFileStat(hint);
|
||||||
if (!boardsConfig || !boardsConfig.selectedBoard) {
|
if (!fileStat) {
|
||||||
throw new Error('No boards selected. Please select a board.');
|
this.messageService.error('Cannot find sketch binary: ' + hint);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!fileStat.isDirectory && fileStat.uri.endsWith('.elf')) {
|
||||||
|
return fileStat.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!argument || argument === "fqbn") {
|
let parent: FileStat | undefined;
|
||||||
|
let prefix: string | undefined;
|
||||||
|
let suffix: string;
|
||||||
|
if (fileStat.isDirectory) {
|
||||||
|
parent = fileStat;
|
||||||
|
} else {
|
||||||
|
const uri = new URI(fileStat.uri);
|
||||||
|
parent = await this.fileSystem.getFileStat(uri.parent.toString());
|
||||||
|
prefix = uri.path.name;
|
||||||
|
}
|
||||||
|
const { boardsConfig } = this.boardsServiceClient;
|
||||||
|
if (boardsConfig && boardsConfig.selectedBoard && boardsConfig.selectedBoard.fqbn) {
|
||||||
|
suffix = boardsConfig.selectedBoard.fqbn.replace(/:/g, '.') + '.elf';
|
||||||
|
} else {
|
||||||
|
suffix = '.elf';
|
||||||
|
}
|
||||||
|
if (parent && parent.children) {
|
||||||
|
let bin: FileStat | undefined;
|
||||||
|
if (prefix) {
|
||||||
|
bin = parent.children.find(c => c.uri.startsWith(prefix!) && c.uri.endsWith(suffix));
|
||||||
|
}
|
||||||
|
if (!bin) {
|
||||||
|
bin = parent.children.find(c => c.uri.endsWith(suffix));
|
||||||
|
}
|
||||||
|
if (!bin && suffix.length > 4) {
|
||||||
|
bin = parent.children.find(c => c.uri.endsWith('.elf'));
|
||||||
|
}
|
||||||
|
if (bin) {
|
||||||
|
return bin.uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.messageService.error('Cannot find sketch binary: ' + hint);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async resolveBoard(context?: URI, argument?: string, configurationSection?: string): Promise<string | undefined> {
|
||||||
|
const { boardsConfig } = this.boardsServiceClient;
|
||||||
|
if (!boardsConfig || !boardsConfig.selectedBoard) {
|
||||||
|
this.messageService.error('No boards selected. Please select a board.');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!argument || argument === 'fqbn') {
|
||||||
return boardsConfig.selectedBoard.fqbn!;
|
return boardsConfig.selectedBoard.fqbn!;
|
||||||
}
|
}
|
||||||
if (argument === "name") {
|
if (argument === 'name') {
|
||||||
return boardsConfig.selectedBoard.name;
|
return boardsConfig.selectedBoard.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
const details = await this.boardsService.detail({id: boardsConfig.selectedBoard.fqbn!});
|
const details = await this.boardsService.detail({ id: boardsConfig.selectedBoard.fqbn! });
|
||||||
if (!details.item) {
|
if (!details.item) {
|
||||||
throw new Error("Cannot get board details");
|
this.messageService.error('Details of the selected boards are not available.');
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
if (argument === "openocd-debug-file") {
|
if (argument === 'openocd-debug-file') {
|
||||||
return details.item.locations!.debugScript;
|
return details.item.locations!.debugScript;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boardsConfig.selectedBoard.fqbn!;
|
return boardsConfig.selectedBoard.fqbn!;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async resolveBoardTools(context?: URI, argument?: string, configurationSection?: string): Promise<Object> {
|
protected async resolveBoardTools(context?: URI, argument?: string, configurationSection?: string): Promise<string | undefined> {
|
||||||
const { boardsConfig } = this.boardsServiceClient;
|
const { boardsConfig } = this.boardsServiceClient;
|
||||||
if (!boardsConfig || !boardsConfig.selectedBoard) {
|
if (!boardsConfig || !boardsConfig.selectedBoard) {
|
||||||
throw new Error('No boards selected. Please select a board.');
|
this.messageService.error('No boards selected. Please select a board.');
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
const details = await this.boardsService.detail({id: boardsConfig.selectedBoard.fqbn!});
|
const details = await this.boardsService.detail({ id: boardsConfig.selectedBoard.fqbn! });
|
||||||
if (!details.item) {
|
if (!details.item) {
|
||||||
throw new Error("Cannot get board details")
|
this.messageService.error('Details of the selected boards are not available.');
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let toolLocations: { [name: string]: ToolLocations } = {};
|
let toolLocations: { [name: string]: ToolLocations } = {};
|
||||||
@ -84,17 +157,37 @@ export class ArduinoVariableResolver implements VariableContribution {
|
|||||||
})
|
})
|
||||||
|
|
||||||
switch (argument) {
|
switch (argument) {
|
||||||
case "openocd":
|
case 'openocd': {
|
||||||
return toolLocations["openocd"].main;
|
const openocd = toolLocations['openocd'];
|
||||||
case "openocd-scripts":
|
if (openocd) {
|
||||||
return toolLocations["openocd"].scripts;
|
return openocd.main;
|
||||||
case "objdump":
|
}
|
||||||
return toolLocations["arm-none-eabi-gcc"].objdump;
|
this.messageService.error('Unable to find debugging tool: openocd');
|
||||||
case "gdb":
|
return undefined;
|
||||||
return toolLocations["arm-none-eabi-gcc"].gdb;
|
}
|
||||||
|
case 'openocd-scripts': {
|
||||||
|
const openocd = toolLocations['openocd'];
|
||||||
|
return openocd ? openocd.scripts : undefined;
|
||||||
|
}
|
||||||
|
case 'objdump': {
|
||||||
|
const gcc = Object.keys(toolLocations).find(key => key.endsWith('gcc'));
|
||||||
|
if (gcc) {
|
||||||
|
return toolLocations[gcc].objdump;
|
||||||
|
}
|
||||||
|
this.messageService.error('Unable to find debugging tool: objdump');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
case 'gdb': {
|
||||||
|
const gcc = Object.keys(toolLocations).find(key => key.endsWith('gcc'));
|
||||||
|
if (gcc) {
|
||||||
|
return toolLocations[gcc].gdb;
|
||||||
|
}
|
||||||
|
this.messageService.error('Unable to find debugging tool: gdb');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return boardsConfig.selectedBoard.name;
|
return boardsConfig.selectedBoard.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,59 +1,59 @@
|
|||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { DebugAdapterContribution, DebugAdapterExecutable, DebugAdapterSessionFactory } from '@theia/debug/lib/common/debug-model';
|
import { DebugAdapterContribution, DebugAdapterExecutable, DebugAdapterSessionFactory } from '@theia/debug/lib/common/debug-model';
|
||||||
import { DebugConfiguration } from "@theia/debug/lib/common/debug-configuration";
|
import { DebugConfiguration } from '@theia/debug/lib/common/debug-configuration';
|
||||||
import { MaybePromise } from "@theia/core/lib/common/types";
|
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||||
import { IJSONSchema, IJSONSchemaSnippet } from "@theia/core/lib/common/json-schema";
|
import { IJSONSchema, IJSONSchemaSnippet } from '@theia/core/lib/common/json-schema';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ArduinoDebugAdapterContribution implements DebugAdapterContribution {
|
export class ArduinoDebugAdapterContribution implements DebugAdapterContribution {
|
||||||
|
|
||||||
type = "arduino";
|
type = 'arduino';
|
||||||
|
|
||||||
label = "Arduino";
|
label = 'Arduino';
|
||||||
|
|
||||||
languages = ["c", "cpp", "ino"];
|
languages = ['c', 'cpp', 'ino'];
|
||||||
|
|
||||||
debugAdapterSessionFactory?: DebugAdapterSessionFactory;
|
debugAdapterSessionFactory?: DebugAdapterSessionFactory;
|
||||||
|
|
||||||
getSchemaAttributes?(): MaybePromise<IJSONSchema[]> {
|
getSchemaAttributes(): MaybePromise<IJSONSchema[]> {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"required": [
|
'required': [
|
||||||
"program"
|
'program'
|
||||||
],
|
],
|
||||||
"properties": {
|
'properties': {
|
||||||
"sketch": {
|
'sketch': {
|
||||||
"type": "string",
|
'type': 'string',
|
||||||
"description": "path to the sketch root ino file",
|
'description': 'path to the sketch root ino file',
|
||||||
"default": "${file}",
|
'default': '${file}',
|
||||||
},
|
},
|
||||||
"fqbn": {
|
'fqbn': {
|
||||||
"type": "string",
|
'type': 'string',
|
||||||
"description": "Fully-qualified board name to debug on",
|
'description': 'Fully-qualified board name to debug on',
|
||||||
"default": ""
|
'default': ''
|
||||||
},
|
},
|
||||||
"runToMain": {
|
'runToMain': {
|
||||||
"description": "If enabled the debugger will run until the start of the main function.",
|
'description': 'If enabled the debugger will run until the start of the main function.',
|
||||||
"type": "boolean",
|
'type': 'boolean',
|
||||||
"default": false
|
'default': false
|
||||||
},
|
},
|
||||||
"verbose": {
|
'verbose': {
|
||||||
"type": "boolean",
|
'type': 'boolean',
|
||||||
"description": "Produce verbose log output",
|
'description': 'Produce verbose log output',
|
||||||
"default": false
|
'default': false
|
||||||
},
|
},
|
||||||
"debugDebugAdapter": {
|
'debugDebugAdapter': {
|
||||||
"type": "boolean",
|
'type': 'boolean',
|
||||||
"description": "Start the debug adapter in debug mode (with --inspect-brk)",
|
'description': 'Start the debug adapter in debug mode (with --inspect-brk)',
|
||||||
"default": false
|
'default': false
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfigurationSnippets?(): MaybePromise<IJSONSchemaSnippet[]> {
|
getConfigurationSnippets(): MaybePromise<IJSONSchemaSnippet[]> {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,29 +65,29 @@ export class ArduinoDebugAdapterContribution implements DebugAdapterContribution
|
|||||||
args = args.concat([path.join(__dirname, 'debug-adapter', 'main')]);
|
args = args.concat([path.join(__dirname, 'debug-adapter', 'main')]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
command: "node",
|
command: 'node',
|
||||||
args: args,
|
args: args,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provideDebugConfigurations?(workspaceFolderUri?: string): MaybePromise<DebugConfiguration[]> {
|
provideDebugConfigurations(workspaceFolderUri?: string): MaybePromise<DebugConfiguration[]> {
|
||||||
return [
|
return [
|
||||||
<DebugConfiguration>{
|
<DebugConfiguration>{
|
||||||
name: this.label,
|
name: this.label,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
request: "launch",
|
request: 'launch',
|
||||||
sketch: "${file}",
|
sketch: '${file}',
|
||||||
},
|
},
|
||||||
<DebugConfiguration>{
|
<DebugConfiguration>{
|
||||||
name: this.label + " (explicit)",
|
name: this.label + ' (explicit)',
|
||||||
type: this.type,
|
type: this.type,
|
||||||
request: "launch",
|
request: 'launch',
|
||||||
|
|
||||||
program: "${sketchBinary}",
|
program: '${sketchBinary}',
|
||||||
objdump: "${boardTools:objdump}",
|
objdump: '${boardTools:objdump}',
|
||||||
gdb: "${boardTools:gdb}",
|
gdb: '${boardTools:gdb}',
|
||||||
gdbServer: "${boardTools:openocd}",
|
gdbServer: '${boardTools:openocd}',
|
||||||
gdbServerArguments: ["-s", "${boardTools:openocd-scripts}", "--file", "${board:openocd-debug-file}"],
|
gdbServerArguments: ['-s', '${boardTools:openocd-scripts}', '--file', '${board:openocd-debug-file}'],
|
||||||
|
|
||||||
runToMain: false,
|
runToMain: false,
|
||||||
verbose: false,
|
verbose: false,
|
||||||
@ -95,23 +95,23 @@ export class ArduinoDebugAdapterContribution implements DebugAdapterContribution
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolveDebugConfiguration?(config: DebugConfiguration, workspaceFolderUri?: string): Promise<DebugConfiguration> {
|
async resolveDebugConfiguration(config: DebugConfiguration, workspaceFolderUri?: string): Promise<DebugConfiguration> {
|
||||||
// if program is present we expect to have an explicit config here
|
// if program is present we expect to have an explicit config here
|
||||||
if (!!config.program) {
|
if (!!config.program) {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sketchBinary = "${sketchBinary}"
|
let sketchBinary = '${sketchBinary}'
|
||||||
if (config.sketch !== "${file}") {
|
if (typeof config.sketch === 'string' && config.sketch.indexOf('${') < 0) {
|
||||||
sketchBinary = "${sketchBinary:" + config.sketch + "}";
|
sketchBinary = '${sketchBinary:' + config.sketch + '}';
|
||||||
}
|
}
|
||||||
const res: ActualDebugConfig = {
|
const res: ActualDebugConfig = {
|
||||||
...config,
|
...config,
|
||||||
|
|
||||||
objdump: "${boardTools:objdump}",
|
objdump: '${boardTools:objdump}',
|
||||||
gdb: "${boardTools:gdb}",
|
gdb: '${boardTools:gdb}',
|
||||||
gdbServer: "${boardTools:openocd}",
|
gdbServer: '${boardTools:openocd}',
|
||||||
gdbServerArguments: ["-s", "${boardTools:openocd-scripts}", "--file", "${board:openocd-debug-file}"],
|
gdbServerArguments: ['-s', '${boardTools:openocd-scripts}', '--file', '${board:openocd-debug-file}'],
|
||||||
program: sketchBinary
|
program: sketchBinary
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
@ -48,7 +48,14 @@ export abstract class AbstractServer extends EventEmitter {
|
|||||||
try {
|
try {
|
||||||
this.timer = setTimeout(() => this.onSpawnError(new Error('Timeout waiting for gdb server to start')), TIMEOUT);
|
this.timer = setTimeout(() => this.onSpawnError(new Error('Timeout waiting for gdb server to start')), TIMEOUT);
|
||||||
|
|
||||||
const command = args.gdbServer || 'gdb-server';
|
const command = args.gdbServer;
|
||||||
|
if (!command) {
|
||||||
|
throw new Error('Missing parameter: gdbServer');
|
||||||
|
}
|
||||||
|
const varRegexp = /\$\{.*\}/;
|
||||||
|
if (varRegexp.test(command)) {
|
||||||
|
throw new Error(`Unresolved variable: ${command}`)
|
||||||
|
}
|
||||||
const serverArguments = await this.resolveServerArguments(args);
|
const serverArguments = await this.resolveServerArguments(args);
|
||||||
this.process = spawn(command, serverArguments, {
|
this.process = spawn(command, serverArguments, {
|
||||||
cwd: dirname(command),
|
cwd: dirname(command),
|
||||||
@ -110,7 +117,7 @@ export abstract class AbstractServer extends EventEmitter {
|
|||||||
|
|
||||||
protected onData(chunk: string | Buffer, buffer: string, event: string) {
|
protected onData(chunk: string | Buffer, buffer: string, event: string) {
|
||||||
buffer += typeof chunk === 'string' ? chunk
|
buffer += typeof chunk === 'string' ? chunk
|
||||||
: chunk.toString('utf8');
|
: chunk.toString('utf8');
|
||||||
|
|
||||||
const end = buffer.lastIndexOf('\n');
|
const end = buffer.lastIndexOf('\n');
|
||||||
if (end !== -1) {
|
if (end !== -1) {
|
||||||
|
@ -71,7 +71,8 @@ export class CmsisDebugSession extends GDBDebugSession {
|
|||||||
await this.runSession(args);
|
await this.runSession(args);
|
||||||
this.sendResponse(response);
|
this.sendResponse(response);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.sendErrorResponse(response, 1, err.message);
|
const message = `Failed to launch the debugger:\n${err.message}`;
|
||||||
|
this.sendErrorResponse(response, 1, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +81,8 @@ export class CmsisDebugSession extends GDBDebugSession {
|
|||||||
await this.runSession(args);
|
await this.runSession(args);
|
||||||
this.sendResponse(response);
|
this.sendResponse(response);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.sendErrorResponse(response, 1, err.message);
|
const message = `Failed to attach the debugger:\n${err.message}`;
|
||||||
|
this.sendErrorResponse(response, 1, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,6 +304,14 @@ export class CmsisDebugSession extends GDBDebugSession {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected spawn(args: CmsisRequestArguments): Promise<void> {
|
||||||
|
const varRegexp = /\$\{.*\}/;
|
||||||
|
if (args.gdb && varRegexp.test(args.gdb)) {
|
||||||
|
throw new Error(`Unresolved variable: ${args.gdb}`)
|
||||||
|
}
|
||||||
|
return super.spawn(args);
|
||||||
|
}
|
||||||
|
|
||||||
private async getGlobalVariables(frameHandle: number): Promise<DebugProtocol.Variable[]> {
|
private async getGlobalVariables(frameHandle: number): Promise<DebugProtocol.Variable[]> {
|
||||||
const frame = this.frameHandles.get(frameHandle);
|
const frame = this.frameHandles.get(frameHandle);
|
||||||
const symbolInfo = this.symbolTable.getGlobalVariables();
|
const symbolInfo = this.symbolTable.getGlobalVariables();
|
||||||
@ -393,7 +403,7 @@ export class CmsisDebugSession extends GDBDebugSession {
|
|||||||
codeOrMessage: number | DebugProtocol.Message, format?: string,
|
codeOrMessage: number | DebugProtocol.Message, format?: string,
|
||||||
variables?: any, dest?: ErrorDestination): void {
|
variables?: any, dest?: ErrorDestination): void {
|
||||||
if (!!format && (dest === undefined || dest === ErrorDestination.User)) {
|
if (!!format && (dest === undefined || dest === ErrorDestination.User)) {
|
||||||
format = format.replace('\n', '<br>');
|
format = format.replace(/\n/g, '<br>');
|
||||||
}
|
}
|
||||||
super.sendErrorResponse(response, codeOrMessage, format, variables, dest);
|
super.sendErrorResponse(response, codeOrMessage, format, variables, dest);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { spawnSync } from 'child_process';
|
import { spawnSync } from 'child_process';
|
||||||
import { platform, EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import { dirname, normalize, basename } from 'path';
|
import { dirname, normalize, basename } from 'path';
|
||||||
|
|
||||||
export enum SymbolType {
|
export enum SymbolType {
|
||||||
@ -53,7 +53,6 @@ export interface SymbolInformation {
|
|||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_OBJDUMP = platform() !== 'win32' ? 'arm-none-eabi-objdump' : 'arm-none-eabi-objdump.exe';
|
|
||||||
const SYMBOL_REGEX = /^([0-9a-f]{8})\s([lg\ !])([w\ ])([C\ ])([W\ ])([I\ ])([dD\ ])([FfO\ ])\s([^\s]+)\s([0-9a-f]+)\s(.*)\r?$/;
|
const SYMBOL_REGEX = /^([0-9a-f]{8})\s([lg\ !])([w\ ])([C\ ])([W\ ])([I\ ])([dD\ ])([FfO\ ])\s([^\s]+)\s([0-9a-f]+)\s(.*)\r?$/;
|
||||||
|
|
||||||
const TYPE_MAP: { [id: string]: SymbolType } = {
|
const TYPE_MAP: { [id: string]: SymbolType } = {
|
||||||
@ -74,7 +73,14 @@ export class SymbolTable {
|
|||||||
|
|
||||||
private symbols: SymbolInformation[] = [];
|
private symbols: SymbolInformation[] = [];
|
||||||
|
|
||||||
constructor(private program: string, private objdump: string = DEFAULT_OBJDUMP) {
|
constructor(private program: string, private objdump?: string) {
|
||||||
|
const varRegexp = /\$\{.*\}/;
|
||||||
|
if (varRegexp.test(program)) {
|
||||||
|
throw new Error(`Unresolved variable: ${program}`);
|
||||||
|
}
|
||||||
|
if (objdump && varRegexp.test(objdump)) {
|
||||||
|
throw new Error(`Unresolved variable: ${objdump}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadSymbols(): Promise<void> {
|
public async loadSymbols(): Promise<void> {
|
||||||
@ -128,6 +134,9 @@ export class SymbolTable {
|
|||||||
|
|
||||||
private execute(): Promise<string> {
|
private execute(): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!this.objdump) {
|
||||||
|
return reject(new Error('Missing parameter: objdump'));
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const { stdout, stderr } = spawnSync(this.objdump, [
|
const { stdout, stderr } = spawnSync(this.objdump, [
|
||||||
'--syms',
|
'--syms',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user