Skip to content

Commit

Permalink
Handle app dedicated settings
Browse files Browse the repository at this point in the history
- Last selected device
- Last selected use_case
- last selected variant
  • Loading branch information
cedelavergne-ledger committed May 17, 2024
1 parent 43048af commit dc6b107
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 26 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@
"type": "boolean",
"default": false,
"markdownDescription": "Allow device operations on Nano X (requires special development device)"
},
"ledgerDevTools.appSettings": {
"type": "object",
"scope": "resource",
"description": "Application dedicated settings."
}
}
}
Expand Down
64 changes: 50 additions & 14 deletions src/appSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { platform } from "node:process";
import * as cp from "child_process";
import { TaskProvider } from "./taskProvider";
import { LedgerDevice, TargetSelector } from "./targetSelector";
import { pushError } from "./extension";
import { pushError, updateSetting, getSetting } from "./extension";
const APP_DETECTION_FILES: string[] = ["Makefile", "ledger_app.toml"];
const C_APP_DETECTION_STRING: string = "include $(BOLOS_SDK)/Makefile.defines";
const C_APP_NAME_MAKEFILE_VAR: string = "APPNAME";
Expand Down Expand Up @@ -92,6 +92,8 @@ export function setBuildUseCase(name: string) {
for (let useCase of selectedApp?.buildUseCases) {
if (useCase.name === name) {
selectedApp.selectedBuildUseCase = useCase;
// Save the selected use case in the app repo settings
updateSetting("selectedUseCase", name, selectedApp.folderUri);
break;
}
}
Expand All @@ -118,6 +120,8 @@ export function setVariant(name: string) {
for (let variant of selectedApp?.variants.values) {
if (variant === name) {
selectedApp.variants.selected = variant;
// Save the selected variant in the app repo settings
updateSetting("selectedVariant", name, selectedApp.folderUri);
break;
}
}
Expand Down Expand Up @@ -251,10 +255,14 @@ export function findAppInFolder(folderUri: vscode.Uri): App | undefined {
// Add the app to the list
if (found) {
if (appLanguage === "C") {
variants = getAppVariants(folderStr.replace(/^file?:\/\//, ''), appName);
variants = getAppVariants(folderStr.replace(/^file?:\/\//, ''), appName, appFolderUri);
}
// Log all found fields
console.log(`Found app '${appName}' in folder '${appFolderName}' with buildDirPath '${buildDirPath}' and language '${appLanguage}'`);

// Retrieve last use case name from settings
let selectedBuildUseCase = getAppUseCases(buildUseCases, folderUri);

app = {
name: appName,
folderName: appFolderName,
Expand All @@ -269,7 +277,7 @@ export function findAppInFolder(folderUri: vscode.Uri): App | undefined {
selectedTestUseCase: testsUseCases ? testsUseCases[0] : undefined,
builtTestDependencies: false,
buildUseCases: buildUseCases,
selectedBuildUseCase: buildUseCases ? buildUseCases[0] : undefined,
selectedBuildUseCase: selectedBuildUseCase,
variants: variants,
};
}
Expand Down Expand Up @@ -444,32 +452,60 @@ function getAppName(appdir: string): string {
return cp.execSync(cleanCmd, optionsExecSync).toString().trim();
}

// Get the app build use cases
function getAppUseCases(buildUseCases: BuildUseCase[] | undefined, folderUri: vscode.Uri): BuildUseCase | undefined {
// Retrieve last selected use case name from settings
let selectedBuildUseCase: BuildUseCase | undefined = undefined;
if (buildUseCases) {
const lastUseCase = getSetting("selectedUseCase", folderUri);
if (lastUseCase) {
selectedBuildUseCase = buildUseCases.find(useCase => useCase.name === lastUseCase);
}
if (selectedBuildUseCase === undefined) {
selectedBuildUseCase = buildUseCases[0];
updateSetting("selectedUseCase", selectedBuildUseCase.name, folderUri);
}
}
return selectedBuildUseCase;
}

// Get the app variants (for C apps)
function getAppVariants(appdir: string, appName: string): VariantList {
function getAppVariants(appdir: string, appName: string, folderUri: vscode.Uri): VariantList {
let optionsExecSync: cp.ExecSyncOptions = { stdio: "pipe", encoding: "utf-8" };
// If platform is windows, set shell to powershell for cp exec.
if (platform === "win32") {
let shell: string = "C:\\windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe";
optionsExecSync.shell = shell;
}

const conf = vscode.workspace.getConfiguration("ledgerDevTools");
let variants: VariantList = {
name: "",
selected: "",
values: [],
};
// Retrieve the last selected variant from settings
variants.selected = getSetting("selectedVariant", folderUri);

const conf = vscode.workspace.getConfiguration("ledgerDevTools", folderUri);
const image = conf.get<string>("dockerImage") || "";

// BOLOS_SDK value doesn't impact the APPNAME
let cleanCmd:string = `docker run --rm -v '${appdir}:/app' ${image} bash -c "BOLOS_SDK=/opt/stax-secure-sdk make listvariants | grep ${C_VARIANT_MAKEFILE_VAR} | cut -d' ' -f2-"`;
let result = cp.execSync(cleanCmd, optionsExecSync).toString().trim().split(" ");
// Variant name is the 2nd word, and the values are following from the 3rd word
let variants: VariantList = {
name: result[0],
selected: result[1],
values: result.slice(1),
};
// Try to find the default variant, using the APPNAME
for (let elt of result) {
if (elt.toLowerCase() === appName.toLowerCase()) {
variants.selected = elt;
variants.name = result[0];
variants.values = result.slice(1);

if (variants.selected === "") {
// Try to guess the default variant, using the APPNAME
const selected = result.find(elt => elt.toLowerCase() === appName.toLowerCase());
if (selected) {
variants.selected = selected;
} else {
// Variant not found: Init with 1st element in the list
variants.selected = result[1];
}
updateSetting("selectedVariant", variants.selected, folderUri);
}

return variants;
Expand Down
36 changes: 36 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
onVariantSelectedEvent,
showVariant,
} from "./appSelector";
import { inspect } from "util";

let outputChannel: vscode.OutputChannel;
const appDetectionFiles = ["Cargo.toml", "ledger_app.toml", "Makefile"];
Expand Down Expand Up @@ -260,3 +261,38 @@ export async function deactivate() {
// DO STUFF
console.log(`Ledger: extension deactivated`);
}


export function updateSetting(key: string, value: string, folderUri: vscode.Uri) {
const conf = vscode.workspace.getConfiguration("ledgerDevTools", folderUri);
const appSettings = conf.get<Record<string, string>>("appSettings");
if (appSettings) {
if (appSettings[key] !== value) {
if (appSettings[key]) {
console.log(`Ledger: appSettings '${key}' found (${appSettings[key].toString()}), updating it with '${value}'`);
} else {
console.log(`Ledger: appSettings no '${key}' key exist yet, adding it with '${value}'`);
}
appSettings[key] = value;
conf.update("appSettings", appSettings, vscode.ConfigurationTarget.WorkspaceFolder);
}
} else {
console.log(`Ledger: no appSettings configuration, creating it with '${value}'`);
conf.update("appSettings", { [key]: value }, vscode.ConfigurationTarget.WorkspaceFolder);
}
}

export function getSetting(key: string, folderUri: vscode.Uri, defaultKey?: string): string {
const conf = vscode.workspace.getConfiguration("ledgerDevTools", folderUri);
const appSettings = conf.get<Record<string, string>>("appSettings");
let value: string = "";
if (appSettings && appSettings[key]) {
value = appSettings[key];
} else if (defaultKey) {
const inspect = conf.inspect<string>(defaultKey);
if (inspect && inspect.defaultValue) {
value = inspect.defaultValue;
}
}
return value;
}
26 changes: 18 additions & 8 deletions src/targetSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { StatusBarManager } from "./statusBar";
import { TaskProvider } from "./taskProvider";
import { TreeDataProvider } from "./treeView";
import { getSelectedApp, setBuildUseCase } from "./appSelector";
import { updateSetting, getSetting } from "./extension";

// Define valid devices
const devices = ["Nano S", "Nano S Plus", "Nano X", "Stax", "Flex"] as const;

const specialAllDevice = "All";
export const specialAllDevice = "All";

type SpecialAllDevice = typeof specialAllDevice;

Expand Down Expand Up @@ -58,9 +59,16 @@ export class TargetSelector {
private prevSelectedApp: string = "";

constructor() {
const conf = vscode.workspace.getConfiguration("ledgerDevTools");
this.updateTargetsInfos();
this.setSelectedTarget(conf.get<string>("defaultDevice", "Nano S"));
const selectedApp = getSelectedApp();
if (selectedApp === undefined) {
return;
}

const dev = getSetting("selectedDevice", selectedApp.folderUri, "defaultDevice");
if (dev){
this.updateTargetsInfos();
this.setSelectedTarget(dev);
}
}

// Type guard function to check if a string is a valid device
Expand All @@ -76,12 +84,13 @@ export class TargetSelector {

this.selectedTarget = target;

// Memorize the selected device in the settings
const conf = vscode.workspace.getConfiguration("ledgerDevTools");
conf.update("defaultDevice", target, vscode.ConfigurationTarget.Global);
// Save the selected device in the app repo settings, if we have not selected "All"
const currentApp = getSelectedApp();
if (currentApp && this.prevSelectedApp === "") {
updateSetting("selectedDevice", target, currentApp?.folderUri);
}

if (!(this.selectedTarget === specialAllDevice)) {
const currentApp = getSelectedApp();
if (currentApp && !currentApp.compatibleDevices.includes(this.selectedTarget as LedgerDevice)) {
// Fallback to compatible device
this.selectedTarget = currentApp.compatibleDevices[0];
Expand Down Expand Up @@ -127,6 +136,7 @@ export class TargetSelector {
public toggleAllTargetSelection() {
if (this.selectedTarget === specialAllDevice) {
this.setSelectedTarget(this.prevSelectedApp);
this.prevSelectedApp = "";
} else {
this.prevSelectedApp = this.selectedTarget;
this.setSelectedTarget(specialAllDevice);
Expand Down
8 changes: 4 additions & 4 deletions src/taskProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as vscode from "vscode";
import * as path from "path";
import * as fs from "fs";
import { platform } from "node:process";
import { TargetSelector } from "./targetSelector";
import { TargetSelector, specialAllDevice } from "./targetSelector";
import { getSelectedApp, App, AppLanguage } from "./appSelector";
import { TreeDataProvider } from "./treeView";

Expand Down Expand Up @@ -605,13 +605,13 @@ export class TaskProvider implements vscode.TaskProvider {
customFunction = defineResult[1];
// If the selected target is all and the task behavior is to be executed for all targets,
// define the exec for all targets of the app
if (this.tgtSelector.getSelectedTarget() === "All" && item.allSelectedBehavior === "executeForEveryTarget") {
if (this.tgtSelector.getSelectedTarget() === specialAllDevice && item.allSelectedBehavior === "executeForEveryTarget") {
exec = "";
this.tgtSelector.getTargetsArray().forEach((target) => {
this.tgtSelector.setSelectedTarget(target);
exec += defineExec(item)[0] + " ; ";
});
this.tgtSelector.setSelectedTarget("All");
this.tgtSelector.setSelectedTarget(specialAllDevice);
customFunction = undefined;
}

Expand Down Expand Up @@ -651,7 +651,7 @@ export class TaskProvider implements vscode.TaskProvider {
}

// If selected target is all and the task behavior is to be disabled when all targets are selected, disable the task
if (this.tgtSelector.getSelectedTarget() === "All") {
if (this.tgtSelector.getSelectedTarget() === specialAllDevice) {
this.taskSpecs.forEach((item) => {
if (item.allSelectedBehavior === "disable" && item.state === "enabled") {
item.state = "disabled";
Expand Down

0 comments on commit dc6b107

Please sign in to comment.