Skip to content

Commit

Permalink
Add variant selection (fix issue #14)
Browse files Browse the repository at this point in the history
  • Loading branch information
cedelavergne-ledger committed May 16, 2024
1 parent e4b387d commit 1d1667c
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 4 deletions.
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
"when": "view == mainView && viewItem == functionalTests && ledgerDevTools.showSelectTestUseCase",
"group": "inline"
},
{
"command": "selectVariant",
"when": "view == mainView && viewItem == selectVariant",
"group": "inline"
},
{
"command": "buildUseCase",
"when": "view == mainView && viewItem == buildUseCase",
Expand Down Expand Up @@ -98,6 +103,13 @@
"category": "Ledger",
"tooltip": "Select the device you want to build your app for."
},
{
"command": "selectVariant",
"title": "Select variant",
"category": "Ledger",
"tooltip": "Select the app variant you want to build.",
"icon": "$(variable)"
},
{
"command": "buildUseCase",
"title": "Select build use case",
Expand Down
79 changes: 79 additions & 0 deletions src/appSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { pushError } 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";
const C_VARIANT_MAKEFILE_VAR: string = "VARIANTS";
const PYTEST_DETECTION_FILE: string = "conftest.py";

type AppType = "manifest" | "legacyManifest" | "makefile";
Expand All @@ -33,6 +34,11 @@ export interface BuildUseCase {
name: string;
options: string;
}
export interface VariantList {
name: string;
selected: string;
values: string[];
}

export interface App {
name: string;
Expand All @@ -54,6 +60,8 @@ export interface App {
// If the app manifest has build use cases (optional) section they are parsed here
buildUseCases?: BuildUseCase[];
selectedBuildUseCase?: BuildUseCase;
// Supported variants
variants?: VariantList;
}

let appList: App[] = [];
Expand All @@ -68,6 +76,9 @@ export const onTestUseCaseSelected: vscode.Event<void> = testUseCaseSelected.eve
let useCaseSelectedEmitter: vscode.EventEmitter<string> = new vscode.EventEmitter<string>();
export const onUseCaseSelectedEvent: vscode.Event<string> = useCaseSelectedEmitter.event;

let variantSelectedEmitter: vscode.EventEmitter<string> = new vscode.EventEmitter<string>();
export const onVariantSelectedEvent: vscode.Event<string> = variantSelectedEmitter.event;

export function getSelectedBuidUseCase(): string {
if (selectedApp && selectedApp.selectedBuildUseCase) {
console.log("LEDGER: getSelectedBuidUseCase: " + selectedApp.selectedBuildUseCase.name);
Expand Down Expand Up @@ -102,6 +113,37 @@ export async function showBuildUseCase() {
return result;
}

export function setVariant(name: string) {
if (selectedApp && selectedApp?.variants) {
for (let variant of selectedApp?.variants.values) {
if (variant === name) {
selectedApp.variants.selected = variant;
break;
}
}
}
}

export function showVariant() {
if (selectedApp) {
if (selectedApp.variants) {
const items: string[] = [];
for (let variant of selectedApp.variants.values) {
items.push(variant);
}
const result = vscode.window.showQuickPick(items, {
placeHolder: "Please select a variant",
onDidSelectItem: (item) => {
setVariant(item.toString());
variantSelectedEmitter.fire(item.toString());
},
});
return result;
}
return "";
}
}

function detectAppType(appFolder: vscode.Uri): [AppType?, string?] {
const searchPatterns = APP_DETECTION_FILES.map((file) => path.join(appFolder.fsPath, `**/${file}`).replace(/\\/g, "/"));
const makefileOrToml = fg.sync(searchPatterns, { onlyFiles: true, deep: 2 });
Expand Down Expand Up @@ -146,6 +188,7 @@ export function findAppInFolder(folderUri: vscode.Uri): App | undefined {
let compatibleDevices: LedgerDevice[] = ["Nano S", "Nano S Plus", "Nano X", "Stax", "Flex"];
let testsUseCases = undefined;
let buildUseCases = undefined;
let variants = undefined;
let buildDirPath = "./";

let found = true;
Expand Down Expand Up @@ -194,6 +237,9 @@ export function findAppInFolder(folderUri: vscode.Uri): App | undefined {
found = false;
break;
}
if (appLanguage === "C") {
variants = getAppVariants(folderStr.replace(/^file?:\/\//, ''), appName);
}
} catch (error) {
let err = new Error();
if (!(error instanceof Error)) {
Expand Down Expand Up @@ -224,6 +270,7 @@ export function findAppInFolder(folderUri: vscode.Uri): App | undefined {
builtTestDependencies: false,
buildUseCases: buildUseCases,
selectedBuildUseCase: buildUseCases ? buildUseCases[0] : undefined,
variants: variants,
};
}

Expand Down Expand Up @@ -397,6 +444,38 @@ function getAppName(appdir: string): string {
return cp.execSync(cleanCmd, optionsExecSync).toString().trim();
}

// Get the app variants (for C apps)
function getAppVariants(appdir: string, appName: string): 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");
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;
}
}

return variants;
}


// Type guard function to check if a string is a valid app language
function isValidLanguage(value: string): AppLanguage {
if (!validLanguages.includes(value as AppLanguage)) {
Expand Down
17 changes: 17 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
onUseCaseSelectedEvent,
getAndBuildAppTestsDependencies,
getSelectedBuidUseCase,
onVariantSelectedEvent,
showVariant,
} from "./appSelector";

let outputChannel: vscode.OutputChannel;
Expand Down Expand Up @@ -71,6 +73,15 @@ export function activate(context: vscode.ExtensionContext) {
})
);

// Event listener for variant selection.
// This event is fired when the user selects a build variant
context.subscriptions.push(
onVariantSelectedEvent((data) => {
taskProvider.generateTasks();
treeProvider.updateDynamicLabels();
})
);

// Event listener for useCase selection.
// This event is fired when the user selects a build useCase
context.subscriptions.push(
Expand Down Expand Up @@ -106,6 +117,12 @@ export function activate(context: vscode.ExtensionContext) {
})
);

context.subscriptions.push(
vscode.commands.registerCommand("selectVariant", () => {
showVariant();
})
);

context.subscriptions.push(
vscode.commands.registerCommand("buildUseCase", () => {
showBuildUseCase();
Expand Down
17 changes: 13 additions & 4 deletions src/taskProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,19 @@ export class TaskProvider implements vscode.TaskProvider {

private cBuildExec(): string {
let buildOpt: string = "";
if (this.currentApp && this.currentApp.selectedBuildUseCase?.options) {
buildOpt = this.currentApp.selectedBuildUseCase?.options;
if (this.currentApp) {
if (this.currentApp.selectedBuildUseCase?.options) {
// Add build option of the selected the useCase
buildOpt = this.currentApp.selectedBuildUseCase?.options;
}

// Add build option for the selected variant
if (this.currentApp.variants && this.currentApp.variants.selected) {
buildOpt += " " + this.currentApp.variants.name + "=" + this.currentApp.variants.selected;
}
}


const exec = `docker exec -it ${
this.containerName
} bash -c 'export BOLOS_SDK=$(echo ${this.tgtSelector.getSelectedSDK()}) && make -C ${this.buildDir} -j ${buildOpt}'`;
Expand All @@ -316,8 +325,8 @@ export class TaskProvider implements vscode.TaskProvider {
}

// Add build option for the selected variant
if (this.currentApp.variant && this.currentApp.variant.selected) {
buildOpt += " " + this.currentApp.variant.name + "=" + this.currentApp.variant.selected;
if (this.currentApp.variants && this.currentApp.variants.selected) {
buildOpt += " " + this.currentApp.variants.name + "=" + this.currentApp.variants.selected;
}
}

Expand Down
24 changes: 24 additions & 0 deletions src/treeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export class TreeDataProvider implements vscode.TreeDataProvider<TreeItem> {
let selectAppItem = this.data.find((item) => item.label && item.label.toString().startsWith("Select app"));
let functionalTestsItem = this.data.find((item) => item.label && item.label.toString().startsWith("Functional"));
let buidUseCaseItem = this.data.find((item) => item.label && item.label.toString().startsWith("Build"));
let selectVariantItem = this.data.find((item) => item.label && item.label.toString().startsWith("Select variant"));

if (selectAppItem) {
selectAppItem.label = `Select app [${currentApp.folderName}]`;
Expand All @@ -204,6 +205,13 @@ export class TreeDataProvider implements vscode.TreeDataProvider<TreeItem> {
buidUseCaseItem.label = `Build`;
}
}
if (selectVariantItem) {
if (currentApp.variants?.values && currentApp.variants?.values.length > 1 && currentApp.variants?.selected) {
selectVariantItem.label = `Select variant [${currentApp.variants?.selected}]`;
} else {
selectVariantItem.label = `Select variant`;
}
}
} else {
// Remove all tree items. The welcome view will be displayed instead.
this.data = [];
Expand All @@ -215,6 +223,7 @@ export class TreeDataProvider implements vscode.TreeDataProvider<TreeItem> {
// Check select app and select target items don't already exist
const selectAppItem = this.data.find((item) => item.label && item.label.toString().startsWith("Select app"));
const selectTargetItem = this.data.find((item) => item.label && item.label.toString().startsWith("Select target"));
const selectVariantItem = this.data.find((item) => item.label && item.label.toString().startsWith("Select variant"));

if (!selectAppItem) {
let selectApp = new TreeItem("Select app");
Expand Down Expand Up @@ -242,6 +251,21 @@ export class TreeDataProvider implements vscode.TreeDataProvider<TreeItem> {
console.log("Ledger: Adding selectTarget to tree");
this.data.push(selectTarget);
}

const currentApp = getSelectedApp();
if (!selectVariantItem && currentApp && currentApp.variants && currentApp.variants.values.length > 1) {
let selectVariant = new TreeItem("Select variant");
selectVariant.contextValue = "selectVariant";
selectVariant.setDefault();
selectVariant.tooltip = "Select the variant to build";
selectVariant.command = {
command: "selectVariant",
title: "Select target",
arguments: [],
};
console.log("Ledger: Adding selectVariant to tree");
this.data.push(selectVariant);
}
}
}

Expand Down

0 comments on commit 1d1667c

Please sign in to comment.