Skip to content
This repository has been archived by the owner on Oct 1, 2024. It is now read-only.

Commit

Permalink
Populate the list of programmers by parsing programmers.txt for each …
Browse files Browse the repository at this point in the history
…package (#1129)

* Improved handling of programmer selection

- Selected programmer is now saved to and loaded from the arduino.json file
- Arduino.json is monitored for changes, and changing file will update selected programmer & ui
- Programmer selection UI now shows both the friendly name of the programmer, as well as the arduino name
- Minor fix to deviceContexts to fire change events after all states are modified
- Layed groundwork to support querying list of programmers for the current board from arduino toolchain

* Parse the list of programmers from packages

* Tests for parsing programmers

* Show board specific list of programmers when selecting

Populate the selected programmer and it's display name using list of programmers provided by BoardManager.  When selecting programmer, only present the user a list of programmers relevant to the current board.

* Initial set of tests for ProgrammerManager

* add support for cli

* fix hardcoded package name for programmers

* adds programmer.key back to support arduino IDE

* fix handeling of programmer name in ide and cli

Co-authored-by: Adi Azulay <[email protected]>
  • Loading branch information
maddogjt and adiazulay authored Jan 20, 2021
1 parent 8bb1130 commit f80b38f
Show file tree
Hide file tree
Showing 12 changed files with 444 additions and 36 deletions.
30 changes: 29 additions & 1 deletion src/arduino/boardManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import { DeviceContext } from "../deviceContext";
import { ArduinoApp } from "./arduino";
import { IArduinoSettings } from "./arduinoSettings";
import { boardEqual, parseBoardDescriptor } from "./board";
import { BoardConfigResult, IBoard, IPackage, IPlatform } from "./package";
import { BoardConfigResult, IBoard, IPackage, IPlatform, IProgrammer } from "./package";
import { parseProgrammerDescriptor } from "./programmer";
import { VscodeSettings } from "./vscodeSettings";

export class BoardManager {
Expand All @@ -22,6 +23,8 @@ export class BoardManager {

private _platforms: IPlatform[];

private _programmers: Map<string, IProgrammer>;

private _installedPlatforms: IPlatform[];

private _boards: Map<string, IBoard>;
Expand Down Expand Up @@ -69,6 +72,9 @@ export class BoardManager {

// Load all supported board types
this.loadInstalledBoards();
this.loadInstalledProgrammers();
this.updateStatusBar();
this._boardConfigStatusBar.show();

const dc = DeviceContext.getInstance();
dc.onChangeBoard(() => this.onDeviceContextBoardChange());
Expand Down Expand Up @@ -150,6 +156,10 @@ export class BoardManager {
return this._boards;
}

public get installedProgrammers(): Map<string, IProgrammer> {
return this._programmers;
}

public get currentBoard(): IBoard {
return this._currentBoard;
}
Expand Down Expand Up @@ -253,6 +263,7 @@ export class BoardManager {
this._installedPlatforms.push(existingPlatform);
}
this.loadInstalledBoardsFromPlatform(existingPlatform);
this.loadInstalledProgrammersFromPlatform(existingPlatform);
}
}
}
Expand Down Expand Up @@ -470,6 +481,23 @@ export class BoardManager {
}
}

private loadInstalledProgrammers(): void {
this._programmers = new Map<string, IProgrammer>();
this._installedPlatforms.forEach((plat) => {
this.loadInstalledProgrammersFromPlatform(plat);
});
}

private loadInstalledProgrammersFromPlatform(plat: IPlatform) {
if (util.fileExistsSync(path.join(plat.rootBoardPath, "programmers.txt"))) {
const programmersContent = fs.readFileSync(path.join(plat.rootBoardPath, "programmers.txt"), "utf8");
const res = parseProgrammerDescriptor(programmersContent, plat);
res.forEach((prog) => {
this._programmers.set(prog.name, prog);
});
}
}

private listBoards(): IBoard[] {
const result = [];
this._boards.forEach((b) => {
Expand Down
31 changes: 31 additions & 0 deletions src/arduino/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,34 @@ export interface IBoard {
*/
getPackageName();
}

/**
* Interface for classes that represent an Arduino supported programmer.
*
* @interface
*/
export interface IProgrammer {
/**
* Unique key that represent the programmer in the package:name.
* @property {string}
*/
key: string;

/**
* Programmer name for Arduino compilation such as `avrisp`, `atmel_ice`
* @property {string}
*/
name: string;

/**
* The human readable name displayed in the Arduino programmer selection menu
* @property {string}
*/
displayName: string;

/**
* Reference to the platform that contains this board.
* @prop {IPlatform}
*/
platform: IPlatform;
}
67 changes: 67 additions & 0 deletions src/arduino/programmer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

import { IPlatform, IProgrammer } from "./package";

export function parseProgrammerDescriptor(programmerDescriptor: string, plat: IPlatform): Map<string, IProgrammer> {
const progrmmerLineRegex = /([^\.]+)\.(\S+)=(.+)/;

const result = new Map<string, IProgrammer>();
const lines = programmerDescriptor.split(/[\r|\r\n|\n]/);
const menuMap = new Map<string, string>();

lines.forEach((line) => {
// Ignore comments.
if (line.startsWith("#")) {
return;
}

const match = progrmmerLineRegex.exec(line);
if (match && match.length > 3) {
let programmer = result.get(match[1]);
if (!programmer) {
programmer = new Programmer(match[1], plat);
result.set(programmer.name
, programmer);
}
if (match[2] === "name") {
programmer.displayName = match[3].trim();
}
}
});
return result;
}

export class Programmer implements IProgrammer {
constructor(private _name: string,
private _platform: IPlatform,
private _displayName: string = _name) {
}

public get name(): string {
return this._name;
}

public get platform(): IPlatform {
return this._platform;
}

public get displayName(): string {
return this._displayName;
}

public set displayName(value: string) {
this._displayName = value;
}

/**
* @returns {string} Return programmer key in format packageName:name
*/
public get key() {
return `${this.getPackageName}:${this.name}`;
}

private get getPackageName(): string {
return this.platform.packageName ? this.platform.packageName : this.platform.package.name;
}
}
74 changes: 41 additions & 33 deletions src/arduino/programmerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,15 @@ import * as constants from "../common/constants";
import { DeviceContext } from "../deviceContext";
import { ArduinoApp } from "./arduino";
import { IArduinoSettings } from "./arduinoSettings";
import { IBoard, IProgrammer } from "./package";

export class ProgrammerManager {
private _programmervalue: string;
public static notFoundDisplayValue: string = "<Select Programmer>";

private _programmerStatusBar: vscode.StatusBarItem;
private _programmerValue: string;
private _programmerDisplayName: string;

// Static list of 'available' programmers. This should be repopulated by the currently selected board type.
private _availableProgrammers = {
avrisp: "AVR ISP",
avrispmkii: "AVRISP mkII",
usbtinyisp: "USBtinyISP",
arduinoisp: "ArduinoISP",
usbasp: "USBasp",
parallel: "Parallel Programmer",
arduinoasisp: "Arduino as ISP",
usbGemma: "Arduino Gemma",
buspirate: "BusPirate as ISP",
stk500: "Atmel STK500 development board",
jtag3isp: "Atmel JTAGICE3 (ISP mode)",
jtag3: "Atmel JTAGICE3 (JTAG mode)",
atmel_ice: "Atmel-ICE (AVR)",
};
private _programmerStatusBar: vscode.StatusBarItem;

constructor(private _settings: IArduinoSettings, private _arduinoApp: ArduinoApp) {
this._programmerStatusBar = vscode.window.createStatusBarItem(
Expand All @@ -41,7 +28,11 @@ export class ProgrammerManager {
}

public get currentProgrammer(): string {
return this._programmervalue;
return this._programmerValue;
}

public get currentDisplayName(): string {
return this._programmerDisplayName;
}

/**
Expand All @@ -50,10 +41,10 @@ export class ProgrammerManager {
* List format: programmer_name:friendly_name
*/
public async selectProgrammer() {
const selectionItems = Object.keys(this._availableProgrammers).map(
const selectionItems = this.getAvailableProgrammers(this._arduinoApp.boardManager.currentBoard).map(
(programmer) => ({
label: this.getFriendlyName(programmer),
description: programmer,
label: programmer.displayName,
description: programmer.name,
programmer }));
const chosen = await vscode.window.showQuickPick(selectionItems, {
placeHolder: "Select programmer",
Expand All @@ -62,20 +53,37 @@ export class ProgrammerManager {
return;
}

this.setProgrammerValue(chosen.programmer);
const dc = DeviceContext.getInstance();
dc.programmer = chosen.programmer;
this.setProgrammerValue(chosen.programmer.name);
DeviceContext.getInstance().programmer = this._programmerValue;
}

private setProgrammerValue(programmerName: string | null) {
const programmer = this._arduinoApp.boardManager.installedProgrammers.get(programmerName);
this._programmerValue = this._settings.useArduinoCli ? programmerName : programmer ? programmer.key : programmerName;
this._programmerDisplayName = this._programmerValue
? this.getDisplayName(programmerName)
: ProgrammerManager.notFoundDisplayValue;
this._programmerStatusBar.text = this._programmerDisplayName;
}

private setProgrammerValue(programmer: string | null) {
this._programmervalue = programmer;
this._programmerStatusBar.text = this._programmervalue
? this.getFriendlyName(this._programmervalue)
: "<Select Programmer>";
private getDisplayName(programmerName: string): string {
const programmer = this._arduinoApp.boardManager.installedProgrammers.get(programmerName);
return programmer ? programmer.displayName : programmerName;
}

private getFriendlyName(programmer: string): string {
const friendlyName = this._availableProgrammers[programmer];
return friendlyName ? friendlyName : programmer;
private getAvailableProgrammers(currentBoard: IBoard): IProgrammer[] {
if (!currentBoard || !currentBoard.platform) {
return [];
}

// Filter the list of all programmers to those that share the same platform as the board
const availableProgrammers: IProgrammer[] = [];
for (const programmer of this._arduinoApp.boardManager.installedProgrammers.values()) {
if (programmer.platform === currentBoard.platform) {
availableProgrammers.push(programmer);
}
}

return availableProgrammers;
}
}
14 changes: 14 additions & 0 deletions src/deviceContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable {

private _sketchStatusBar: vscode.StatusBarItem;

private _prebuild: string;

private _programmer: string;

private _suppressSaveContext: boolean = false;

/**
* @constructor
*/
Expand Down Expand Up @@ -264,6 +270,14 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable {
this.saveContext();
}

public get suppressSaveContext() {
return this._suppressSaveContext;
}

public set suppressSaveContext(value: boolean) {
this._suppressSaveContext = value;
}

public get buildPreferences() {
return this._settings.buildPreferences.value;
}
Expand Down
29 changes: 29 additions & 0 deletions test/boardmanager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ArduinoSettings } from "../src/arduino/arduinoSettings";
import { parseBoardDescriptor } from "../src/arduino/board";
import { BoardManager } from "../src/arduino/boardManager";
import { IPlatform } from "../src/arduino/package";
import { parseProgrammerDescriptor } from "../src/arduino/programmer";
import * as util from "../src/common/util";

suite("Arduino: Board Manager.", () => {
Expand Down Expand Up @@ -67,6 +68,14 @@ suite("Arduino: Board Manager.", () => {
"should parse installed boards from custom packages ($sketchbook/hardware directory)");
});

test("should be able to load installed programmers", () => {
assert.equal(boardManager.installedProgrammers.size, 17, `Expected to find programmers for dummy & AVR boards`);
assert.ok(boardManager.installedProgrammers.get("avrispmkii"),
"should parse installed programmers from Arduino IDE built-in packages");
assert.ok(boardManager.installedProgrammers.get("esp8266_dummy"),
"should parse installed programmers from custom packages ($sketchbook/hardware directory)");
});

test("should parse boards.txt correctly", () => {
const arduinoAvrBoard = fs.readFileSync(Path.join(Resources.mockedIDEPackagePath, "arduino/avr/boards.txt"), "utf8");
const platform = {
Expand All @@ -92,6 +101,26 @@ suite("Arduino: Board Manager.", () => {
assert.equal(diecimilaBoard.customConfig, "cpu=atmega328");
});

test("should parse programmers.txt correctly", () => {
const arduinoAvrBoard = fs.readFileSync(Path.join(Resources.mockedIDEPackagePath, "arduino/avr/programmers.txt"), "utf8");
const platform = {
name: "Arduino AVR Boards",
architecture: "avr",
package: {
name: "arduino",
},
};
const programmerDescriptors = parseProgrammerDescriptor(arduinoAvrBoard, <IPlatform> platform);

const avrispmkii = programmerDescriptors.get("avrispmkii");
assert.equal(avrispmkii.name, "avrispmkii");
assert.equal(avrispmkii.displayName, "AVRISP mkII");

const usbGemma = programmerDescriptors.get("usbGemma");
assert.equal(usbGemma.name, "usbGemma");
assert.equal(usbGemma.displayName, "Arduino Gemma");
});

test("should parse platform.txt correctly", () => {
const platformConfig = util.parseConfigFile(Path.join(Resources.mockedSketchbookPath, "hardware/esp8266/esp8266/platform.txt"));
assert.equal(platformConfig.get("name"), "ESP8266 Modules");
Expand Down
1 change: 1 addition & 0 deletions test/devicecontext.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ suite("Arduino: Device Context config", () => {
assert.equal(deviceContext.configuration, "cpu=atmega328");
assert.equal(deviceContext.output, null);
assert.equal(deviceContext.debugger_, null);
assert.equal(deviceContext.programmer, "arduino:jtag3isp");
done();
});
} catch (error) {
Expand Down
Loading

0 comments on commit f80b38f

Please sign in to comment.