1 /* eslint-disable max-len */
7 type OpenDialogOptions,
17 import { type ExecOptions, exec } from "child_process";
18 import { HOME_VAR } from "../settings.mjs";
19 import Settings from "../settings.mjs";
20 import Logger from "../logger.mjs";
21 import { dirname, join } from "path";
22 import { join as joinPosix } from "path/posix";
24 type SupportedToolchainVersion,
25 getSupportedToolchains,
26 } from "../utils/toolchainUtil.mjs";
33 } from "../utils/githubREST.mjs";
39 downloadAndInstallCmake,
40 downloadAndInstallNinja,
41 downloadAndInstallOpenOCD,
42 downloadAndInstallSDK,
43 downloadAndInstallToolchain,
44 downloadAndInstallTools,
45 downloadAndInstallPicotool,
48 } from "../utils/download.mjs";
49 import { compare } from "../utils/semverUtil.mjs";
50 import VersionBundlesLoader, {
52 } from "../utils/versionBundles.mjs";
53 import which from "which";
54 import { homedir } from "os";
55 import { readFile } from "fs/promises";
56 import { pyenvInstallPython, setupPyenv } from "../utils/pyenvUtil.mjs";
57 import { existsSync, readdirSync } from "fs";
62 } from "../utils/examplesUtil.mjs";
63 import { unknownErrorToString } from "../utils/errorHelper.mjs";
65 export const NINJA_AUTO_INSTALL_DISABLED = false;
66 // process.platform === "linux" && process.arch === "arm64";
68 export const openOCDVersion = "0.12.0+dev";
70 interface ImportProjectMessageValue {
72 selectedToolchain: string;
73 selectedPicotool: string;
87 interface SubmitExampleMessageValue extends ImportProjectMessageValue {
92 interface SubmitMessageValue extends ImportProjectMessageValue {
96 // features (libraries)
101 hwwatchdogFeature: boolean;
102 hwclocksFeature: boolean;
103 hwinterpolationFeature: boolean;
104 hwtimerFeature: boolean;
107 uartStdioSupport: boolean;
108 usbStdioSupport: boolean;
110 // pico wireless options
111 picoWireless: number;
113 // code generation options
114 addExamples: boolean;
118 cppExceptions: boolean;
121 interface WebviewMessage {
123 value: object | string | SubmitMessageValue;
134 consoleOverUART = "Console over UART",
135 consoleOverUSB = "Console over USB (disables other USB use)",
143 interp = "HW interpolation",
145 watch = "HW watchdog",
146 clocks = "HW clocks",
149 enum PicoWirelessOption {
151 picoWLed = "Pico W onboard LED",
152 picoWPoll = "Polled lwIP",
153 picoWBackground = "Background lwIP",
157 addExamples = "Add examples from Pico library",
158 runFromRAM = "Run the program from RAM rather than flash",
159 cpp = "Generate C++ code",
160 cppRtti = "Enable C++ RTTI (Uses more memory)",
161 cppExceptions = "Enable C++ exceptions (Uses more memory)",
165 debugProbe = "DebugProbe (CMSIS-DAP) [Default]",
166 swd = "SWD (Pi host)",
169 async function enumToBoard(e: BoardType, sdkPath: string): Promise<string> {
170 const quickPickItems: string[] = [];
174 return "-board pico";
175 case BoardType.picoW:
176 return "-board pico_w";
177 case BoardType.pico2:
178 return "-board pico2";
179 case BoardType.other:
180 readdirSync(`${sdkPath}/src/boards/include/boards`).forEach(file => {
181 quickPickItems.push(file.split(".")[0]);
184 // show quick pick for board type
185 board = await window.showQuickPick(quickPickItems, {
186 placeHolder: "Select Board",
189 return `-board ${board}`;
191 // TODO: maybe just return an empty string
192 throw new Error(`Unknown enum value: ${e as string}`);
196 function enumToParam(
206 case ConsoleOption.consoleOverUART:
208 case ConsoleOption.consoleOverUSB:
223 return "-f watchdog";
226 case PicoWirelessOption.none:
227 return "-f picow_none";
228 case PicoWirelessOption.picoWLed:
229 return "-f picow_led";
230 case PicoWirelessOption.picoWPoll:
231 return "-f picow_poll";
232 case PicoWirelessOption.picoWBackground:
233 return "-f picow_background";
234 case CodeOption.addExamples:
236 case CodeOption.runFromRAM:
240 case CodeOption.cppRtti:
242 case CodeOption.cppExceptions:
244 case Debugger.debugProbe:
249 // TODO: maybe just return an empty string
250 throw new Error(`Unknown enum value: ${e as string}`);
254 interface ImportProjectOptions {
257 toolchainVersion: string;
258 toolchainPath: string;
261 picotoolVersion: string;
262 openOCDVersion: string;
264 ninjaExecutable: string;
265 cmakeExecutable: string;
269 interface NewExampleBasedProjectOptions extends ImportProjectOptions {
272 boardType: BoardType;
275 interface NewProjectOptions extends ImportProjectOptions {
277 boardType: BoardType;
278 consoleOptions: ConsoleOption[];
279 libraries: Array<Library | PicoWirelessOption>;
280 codeOptions: CodeOption[];
283 export function getWebviewOptions(extensionUri: Uri): WebviewOptions {
286 localResourceRoots: [Uri.joinPath(extensionUri, "web")],
290 export function getProjectFolderDialogOptions(
292 forImport: boolean = false
293 ): OpenDialogOptions {
295 canSelectFiles: false,
296 canSelectFolders: true,
297 canSelectMany: false,
300 ? "Select a project folder to import"
301 : "Select a project root to create the new project folder in",
302 defaultUri: projectRoot,
306 export class NewProjectPanel {
307 public static currentPanel: NewProjectPanel | undefined;
309 public static readonly viewType = "newPicoProject";
311 private readonly _panel: WebviewPanel;
312 private readonly _extensionUri: Uri;
313 private readonly _settings: Settings;
314 private readonly _logger: Logger = new Logger("NewProjectPanel");
315 private _disposables: Disposable[] = [];
317 private _projectRoot?: Uri;
318 private _supportedToolchains?: SupportedToolchainVersion[];
319 private _versionBundlesLoader?: VersionBundlesLoader;
320 private _versionBundle: VersionBundle | undefined;
321 private _isProjectImport: boolean;
322 private _examples: Example[] = [];
323 private _isCreateFromExampleOnly: boolean = false;
325 public static createOrShow(
327 isProjectImport: boolean = false,
328 createFromExample: boolean = false,
331 const column = window.activeTextEditor
332 ? window.activeTextEditor.viewColumn
335 if (NewProjectPanel.currentPanel) {
337 NewProjectPanel.currentPanel._isProjectImport === isProjectImport &&
338 (NewProjectPanel.currentPanel._isCreateFromExampleOnly
341 (!createFromExample || !isProjectImport)
343 NewProjectPanel.currentPanel._panel.reveal(column);
344 // update already exiting panel with new project root
346 NewProjectPanel.currentPanel._projectRoot = projectUri;
348 void NewProjectPanel.currentPanel._panel.webview.postMessage({
349 command: "changeLocation",
350 value: projectUri?.fsPath,
354 if (createFromExample) {
356 void NewProjectPanel.currentPanel._panel.webview.postMessage({
357 command: "createFromExample",
359 } else if (!isProjectImport) {
360 void NewProjectPanel.currentPanel._panel.webview.postMessage({
361 command: "notCreateFromExample",
367 // replace with new one
368 NewProjectPanel.currentPanel.dispose();
372 const panel = window.createWebviewPanel(
373 NewProjectPanel.viewType,
374 isProjectImport ? "Import Pico Project" : "New Pico Project",
375 column || ViewColumn.One,
376 getWebviewOptions(extensionUri)
379 const settings = Settings.getInstance();
380 if (settings === undefined) {
381 // TODO: maybe add restart button
382 void window.showErrorMessage(
383 "Failed to load settings. Please restart VSCode."
389 NewProjectPanel.currentPanel = new NewProjectPanel(
399 public static revive(
402 isProjectImport: boolean = false,
403 createFromExample: boolean = false
405 const settings = Settings.getInstance();
406 if (settings === undefined) {
407 // TODO: maybe add restart button
408 void window.showErrorMessage(
409 "Failed to load settings. Please restart VSCode."
415 // TODO: reload if it was import panel maybe in state
416 NewProjectPanel.currentPanel = new NewProjectPanel(
429 isProjectImport: boolean = false,
430 createFromExample: boolean = false,
434 this._extensionUri = extensionUri;
435 this._settings = settings;
436 this._isProjectImport = isProjectImport;
437 // set local property as it's an indicator for initial projectRoot update
438 // later during webview initialization
439 projectUri = projectUri ?? this._settings.getLastProjectRoot();
440 this._projectRoot = projectUri;
441 this._isCreateFromExampleOnly = createFromExample;
443 void this._update(createFromExample);
445 this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
447 // Update the content based on view changes
448 this._panel.onDidChangeViewState(
450 if (this._panel.visible) {
451 await this._update(createFromExample);
458 workspace.onDidChangeConfiguration(
460 await this._updateTheme();
466 // Handle messages from the webview
467 this._panel.webview.onDidReceiveMessage(
468 async (message: WebviewMessage) => {
469 switch (message.command) {
470 case "changeLocation":
472 const newLoc = await window.showOpenDialog(
473 getProjectFolderDialogOptions(
475 this._isProjectImport
479 if (newLoc && newLoc[0]) {
480 // overwrite preview folderUri
481 this._projectRoot = newLoc[0];
482 await this._settings.setLastProjectRoot(newLoc[0]);
485 await this._panel.webview.postMessage({
486 command: "changeLocation",
487 value: newLoc[0].fsPath,
492 case "versionBundleAvailableTest":
494 // test if versionBundle for sdk version is available
495 const versionBundle =
496 await this._versionBundlesLoader?.getModuleVersion(
497 message.value as string
499 // change toolchain version on arm64 linux, as Core-V not available for that
500 const riscvToolchain = versionBundle?.riscvToolchain;
501 // return result in message of command versionBundleAvailableTest
502 await this._panel.webview.postMessage({
503 command: "versionBundleAvailableTest",
505 result: versionBundle !== undefined,
506 toolchainVersion: versionBundle?.toolchain,
507 riscvToolchainVersion: riscvToolchain,
516 void window.showErrorMessage(message.value as string);
518 case "importProject":
521 this._projectRoot === undefined ||
522 this._projectRoot.fsPath === ""
524 void window.showErrorMessage(
525 "No project root selected. Please select a project root."
527 await this._panel.webview.postMessage({
528 command: "submitDenied",
534 const posixProjectRoot = this._projectRoot.fsPath.replaceAll(
538 const projectFolderName = posixProjectRoot.substring(
539 posixProjectRoot.lastIndexOf("/") + 1
541 // a restriction since the folder name is passed as a parameter to the python script for project name
542 // if a project is imported
543 if (projectFolderName.includes(" ")) {
544 void window.showErrorMessage(
545 "Project folder name cannot contain spaces."
547 await this._panel.webview.postMessage({
548 command: "submitDenied",
554 const data = message.value as ImportProjectMessageValue;
556 // check the project to import exists
557 if (!existsSync(this._projectRoot.fsPath)) {
558 void window.showErrorMessage(
559 "The project you are trying to import does not exist."
561 await this._panel.webview.postMessage({
562 command: "submitDenied",
568 // close panel before generating project
571 await window.withProgress(
573 location: ProgressLocation.Notification,
574 title: `Importing project ${projectFolderName} from ${this._projectRoot?.fsPath}, this may take a while...`,
577 this._generateProjectOperation(progress, data, message)
581 case "submitExample":
584 this._projectRoot === undefined ||
585 this._projectRoot.fsPath === ""
587 void window.showErrorMessage(
588 "No project root selected. Please select a project root."
590 await this._panel.webview.postMessage({
591 command: "submitDenied",
597 const data = message.value as SubmitExampleMessageValue;
599 // check if projectRoot/projectName folder already exists
600 if (existsSync(join(this._projectRoot.fsPath, data.example))) {
601 void window.showErrorMessage(
602 "Project already exists. Please select a different project root or example."
604 await this._panel.webview.postMessage({
605 command: "submitDenied",
611 if (this._examples.length === 0) {
612 this._examples = await loadExamples();
615 const example = this._examples.find(
616 example => example.searchKey === data.example
618 if (example === undefined) {
619 await window.showErrorMessage(
620 "Failed to find example. Try reinstalling the extension."
626 // close panel before generating project
629 const result = await setupExample(
631 // required to support backslashes in macOS/Linux folder names
632 process.platform !== "win32"
633 ? this._projectRoot.fsPath
634 : this._projectRoot.fsPath.replaceAll("\\", "/")
638 await window.showErrorMessage("Failed to setup example.");
643 await window.withProgress(
645 location: ProgressLocation.Notification,
646 title: `Generating project based on the ${
647 data.example ?? "undefined"
649 this._projectRoot?.fsPath
650 }, this may take a while...`,
653 this._generateProjectOperation(
664 const data = message.value as SubmitMessageValue;
667 this._projectRoot === undefined ||
668 this._projectRoot.fsPath === ""
670 void window.showErrorMessage(
671 "No project root selected. Please select a project root."
673 await this._panel.webview.postMessage({
674 command: "submitDenied",
680 // check if projectRoot/projectName folder already exists
682 existsSync(join(this._projectRoot.fsPath, data.projectName))
684 void window.showErrorMessage(
685 "Project already exists. Please select a different project name or root."
687 await this._panel.webview.postMessage({
688 command: "submitDenied",
694 // close panel before generating project
697 await window.withProgress(
699 location: ProgressLocation.Notification,
700 title: `Generating project ${
701 data.projectName ?? "undefined"
702 } in ${this._projectRoot?.fsPath}, this may take a while...`,
705 this._generateProjectOperation(progress, data, message)
715 // if project uri is defined on construction then also tell the webview
716 if (projectUri !== undefined) {
718 void this._panel.webview.postMessage({
719 command: "changeLocation",
720 value: projectUri?.fsPath,
725 private async _generateProjectOperation(
726 progress: Progress<{ message?: string; increment?: number }>,
729 | SubmitExampleMessageValue
730 | ImportProjectMessageValue,
731 message: WebviewMessage,
734 const projectPath = this._projectRoot?.fsPath ?? "";
736 // check if message is valid
737 // TODO: add an else block if error handling
739 typeof message.value === "object" &&
740 message.value !== null &&
741 projectPath !== "" &&
742 this._versionBundlesLoader !== undefined
744 const selectedSDK = data.selectedSDK.slice(0);
745 const selectedToolchain = this._supportedToolchains?.find(
746 tc => tc.version === data.selectedToolchain.replaceAll(".", "_")
748 const selectedPicotool = data.selectedPicotool.slice(0);
750 if (!selectedToolchain) {
751 void window.showErrorMessage("Failed to find selected toolchain.");
756 // update to new selected sdk);
757 this._versionBundle = await this._versionBundlesLoader.getModuleVersion(
762 this._versionBundle === undefined &&
763 // if no versionBundle then all version options the could be dependent on it must be custom (=> independent of versionBundle)
764 (data.pythonMode === 0 || data.ninjaMode === 0 || data.cmakeMode === 0)
770 await window.showErrorMessage("Failed to find selected SDK version.");
775 // install python (if necessary)
776 let python3Path: string | undefined;
777 if (process.platform === "darwin" || process.platform === "win32") {
778 switch (data.pythonMode) {
780 const versionBundle = this._versionBundle;
781 await window.withProgress(
783 location: ProgressLocation.Notification,
785 "Download and installing Python. This may take a while...",
789 if (process.platform === "win32") {
790 // ! because data.pythonMode === 0 => versionBundle !== undefined
791 python3Path = await downloadEmbedPython(versionBundle!);
792 } else if (process.platform === "darwin") {
793 const result1 = await setupPyenv();
801 const result = await pyenvInstallPython(
802 versionBundle!.python.version
805 if (result !== null) {
806 python3Path = result;
810 "Automatic Python installation is only supported on Windows and macOS."
813 await window.showErrorMessage(
814 "Automatic Python installation is only supported on Windows and macOS."
825 python3Path = process.platform === "win32" ? "python" : "python3";
828 python3Path = data.pythonPath;
832 if (python3Path === undefined) {
837 await window.showErrorMessage("Failed to find python3 executable.");
842 python3Path = "python3";
845 // install selected sdk and toolchain if necessary
846 // show user feedback as downloads can take a while
847 let installedSuccessfully = false;
848 await window.withProgress(
850 location: ProgressLocation.Notification,
851 title: "Downloading SDK and Toolchain",
857 !(await downloadAndInstallSDK(
860 // python3Path is only possible undefined if downloaded and there is already checked and returned if this happened
861 python3Path!.replace(HOME_VAR, homedir().replaceAll("\\", "/"))
863 !(await downloadAndInstallToolchain(selectedToolchain)) ||
864 !(await downloadAndInstallTools(selectedSDK)) ||
865 !(await downloadAndInstallPicotool(selectedPicotool))
868 `Failed to download and install toolchain and SDK.`
872 message: "Failed - Make sure all requirements are met.",
876 installedSuccessfully = false;
878 installedSuccessfully = true;
879 if (!(await downloadAndInstallOpenOCD(openOCDVersion))) {
880 this._logger.error(`Failed to download and install openocd.`);
883 `Successfully downloaded and installed openocd.`
890 if (!installedSuccessfully) {
895 await window.showErrorMessage(
896 "Failed to download and install SDK and/or toolchain."
902 let ninjaExecutable: string;
903 let cmakeExecutable: string;
904 switch (data.ninjaMode) {
906 if (this._versionBundle !== undefined) {
907 data.ninjaVersion = this._versionBundle.ninja;
909 this._logger.error("Failed to get version bundle for ninja.");
914 await window.showErrorMessage(
915 "Failed to get ninja version for the selected Pico SDK version."
920 // eslint-disable-next-line no-fallthrough
922 installedSuccessfully = false;
923 await window.withProgress(
925 location: ProgressLocation.Notification,
926 title: "Download and install ninja",
930 if (await downloadAndInstallNinja(data.ninjaVersion)) {
932 message: "Successfully downloaded and installed ninja.",
936 installedSuccessfully = true;
938 installedSuccessfully = false;
947 if (!installedSuccessfully) {
952 await window.showErrorMessage(
953 "Failed to download and install ninja. Make sure all requirements are met."
958 ninjaExecutable = joinPosix(
959 buildNinjaPath(data.ninjaVersion),
965 ninjaExecutable = "ninja";
968 // normalize path returned by the os selector to posix path for the settings json
969 // and cross platform compatibility
971 process.platform === "win32"
972 ? joinPosix(...data.ninjaPath.split("\\"))
981 await window.showErrorMessage("Unknown ninja selection.");
982 this._logger.error("Unknown ninja selection.");
987 switch (data.cmakeMode) {
989 if (this._versionBundle !== undefined) {
990 data.cmakeVersion = this._versionBundle.cmake;
992 // eslint-disable-next-line no-fallthrough
994 installedSuccessfully = false;
995 await window.withProgress(
997 location: ProgressLocation.Notification,
998 title: "Download and install cmake",
1001 async progress2 => {
1002 if (await downloadAndInstallCmake(data.cmakeVersion)) {
1004 message: "Successfully downloaded and installed cmake.",
1008 installedSuccessfully = true;
1010 installedSuccessfully = false;
1019 if (!installedSuccessfully) {
1024 await window.showErrorMessage(
1025 "Failed to download and install cmake. Make sure all requirements are met."
1030 const cmakeVersionBasePath = buildCMakePath(data.cmakeVersion);
1032 cmakeExecutable = joinPosix(cmakeVersionBasePath, "bin", "cmake");
1036 cmakeExecutable = "cmake";
1039 // normalize path returned by the os selector to posix path for the settings json
1040 // and cross platform compatibility
1042 process.platform === "win32"
1043 ? // TODO: maybe use path.sep for split
1044 joinPosix(...data.cmakePath.split("\\"))
1052 await window.showErrorMessage("Unknown cmake selection.");
1053 this._logger.error("Unknown cmake selection.");
1058 if (example === undefined && !this._isProjectImport) {
1059 const theData = data as SubmitMessageValue;
1060 const args: NewProjectOptions = {
1061 name: theData.projectName,
1062 projectRoot: projectPath,
1063 boardType: theData.boardType as BoardType,
1065 theData.uartStdioSupport ? ConsoleOption.consoleOverUART : null,
1066 theData.usbStdioSupport ? ConsoleOption.consoleOverUSB : null,
1067 ].filter(option => option !== null),
1069 theData.spiFeature ? Library.spi : null,
1070 theData.i2cFeature ? Library.i2c : null,
1071 theData.dmaFeature ? Library.dma : null,
1072 theData.pioFeature ? Library.pio : null,
1073 theData.hwinterpolationFeature ? Library.interp : null,
1074 theData.hwtimerFeature ? Library.timer : null,
1075 theData.hwwatchdogFeature ? Library.watch : null,
1076 theData.hwclocksFeature ? Library.clocks : null,
1077 theData.boardType === "pico-w"
1078 ? Object.values(PicoWirelessOption)[theData.picoWireless]
1080 ].filter(option => option !== null) as Library[],
1082 theData.addExamples ? CodeOption.addExamples : null,
1083 theData.runFromRAM ? CodeOption.runFromRAM : null,
1084 theData.cpp ? CodeOption.cpp : null,
1085 theData.cppRtti ? CodeOption.cppRtti : null,
1086 theData.cppExceptions ? CodeOption.cppExceptions : null,
1087 ].filter(option => option !== null),
1088 debugger: data.debugger === 1 ? Debugger.swd : Debugger.debugProbe,
1090 toolchainVersion: selectedToolchain.version,
1091 toolchainPath: buildToolchainPath(selectedToolchain.version),
1092 sdkVersion: selectedSDK,
1093 sdkPath: buildSDKPath(selectedSDK),
1094 picotoolVersion: selectedPicotool,
1095 openOCDVersion: openOCDVersion,
1101 await this._executePicoProjectGenerator(
1103 python3Path.replace(HOME_VAR, homedir().replaceAll("\\", "/"))
1105 } else if (example !== undefined && !this._isProjectImport) {
1106 const theData = data as SubmitExampleMessageValue;
1107 const args: NewExampleBasedProjectOptions = {
1108 name: theData.example,
1109 libNames: example.libNames,
1110 projectRoot: projectPath,
1111 boardType: theData.boardType as BoardType,
1112 debugger: data.debugger === 1 ? Debugger.swd : Debugger.debugProbe,
1114 toolchainVersion: selectedToolchain.version,
1115 toolchainPath: buildToolchainPath(selectedToolchain.version),
1116 sdkVersion: selectedSDK,
1117 sdkPath: buildSDKPath(selectedSDK),
1118 picotoolVersion: selectedPicotool,
1119 openOCDVersion: openOCDVersion,
1125 await this._executePicoProjectGenerator(
1127 python3Path.replace(HOME_VAR, homedir().replaceAll("\\", "/")),
1130 } else if (this._isProjectImport && example === undefined) {
1131 const args: ImportProjectOptions = {
1132 projectRoot: projectPath,
1134 toolchainVersion: selectedToolchain.version,
1135 toolchainPath: buildToolchainPath(selectedToolchain.version),
1136 sdkVersion: selectedSDK,
1137 sdkPath: buildSDKPath(selectedSDK),
1138 picotoolVersion: selectedPicotool,
1139 openOCDVersion: openOCDVersion,
1143 debugger: data.debugger === 1 ? Debugger.swd : Debugger.debugProbe,
1146 await this._executePicoProjectGenerator(
1148 python3Path.replace(HOME_VAR, homedir().replaceAll("\\", "/"))
1155 await window.showErrorMessage("Unknown project type.");
1162 private async _update(forceCreateFromExample: boolean): Promise<void> {
1163 this._panel.title = this._isProjectImport
1164 ? "Import Pico Project"
1165 : forceCreateFromExample
1166 ? "New Example Pico Project"
1167 : "New Pico Project";
1168 this._panel.iconPath = Uri.joinPath(
1173 const html = await this._getHtmlForWebview(
1174 this._panel.webview,
1175 forceCreateFromExample
1180 this._panel.webview.html = html;
1183 "Failed to set webview html. Webview might have been disposed. Error: ",
1184 unknownErrorToString(error)
1186 // properly dispose panel
1191 await this._updateTheme();
1193 void window.showErrorMessage(
1194 "Failed to load available Pico SDKs and/or supported toolchains. This may be due to an outdated personal access token for GitHub or a exceeded rate limit."
1200 private async _updateTheme(): Promise<void> {
1201 await this._panel.webview.postMessage({
1202 command: "setTheme",
1204 window.activeColorTheme.kind === ColorThemeKind.Dark ||
1205 window.activeColorTheme.kind === ColorThemeKind.HighContrast
1211 public dispose(): void {
1212 NewProjectPanel.currentPanel = undefined;
1214 this._panel.dispose();
1216 while (this._disposables.length) {
1217 const x = this._disposables.pop();
1225 private async _getHtmlForWebview(
1227 forceCreateFromExample: boolean
1228 ): Promise<string> {
1229 // TODO: store in memory so on future update static stuff doesn't need to be read again
1230 const mainScriptUri = webview.asWebviewUri(
1231 Uri.joinPath(this._extensionUri, "web", "main.js")
1233 const navScriptUri = webview.asWebviewUri(
1234 Uri.joinPath(this._extensionUri, "web", "nav.js")
1236 const stateScriptUri = webview.asWebviewUri(
1237 Uri.joinPath(this._extensionUri, "web", "state.js")
1239 const tailwindcssScriptUri = webview.asWebviewUri(
1240 Uri.joinPath(this._extensionUri, "web", "tailwindcss-3_3_5.js")
1243 const mainStyleUri = webview.asWebviewUri(
1244 Uri.joinPath(this._extensionUri, "web", "main.css")
1248 const navHeaderSvgUri = webview.asWebviewUri(
1249 Uri.joinPath(this._extensionUri, "web", "raspberrypi-nav-header.svg")
1252 const navHeaderDarkSvgUri = webview.asWebviewUri(
1253 Uri.joinPath(this._extensionUri, "web", "raspberrypi-nav-header-dark.svg")
1256 const riscvWhiteSvgUri = webview.asWebviewUri(
1261 "RISC-V_Horizontal_White.svg"
1264 const riscvWhiteYellowSvgUri = webview.asWebviewUri(
1269 "RISC-V_Horizontal_White_Yellow.svg"
1272 const riscvBlackSvgUri = webview.asWebviewUri(
1277 "RISC-V_Horizontal_Black.svg"
1280 const riscvColorSvgUri = webview.asWebviewUri(
1285 "RISC-V_Horizontal_Color.svg"
1289 this._versionBundlesLoader = new VersionBundlesLoader(this._extensionUri);
1291 // construct auxiliar html
1292 // TODO: add offline handling - only load installed ones
1293 let toolchainsHtml = "";
1294 let picoSDKsHtml = "";
1295 let picotoolsHtml = "";
1296 let ninjasHtml = "";
1297 let cmakesHtml = "";
1299 //const installedSDKs = detectInstalledSDKs();
1300 //const installedToolchains = detectInstalledToolchains();
1301 //installedSDKs.sort((a, b) =>
1302 //compare(a.version.replace("v", ""), b.version.replace("v", ""))
1306 const availableSDKs = await getSDKReleases();
1307 const supportedToolchains = await getSupportedToolchains();
1308 const ninjaReleases = await getNinjaReleases();
1309 const cmakeReleases = await getCmakeReleases();
1310 const picotoolReleases = await getPicotoolReleases();
1312 if (availableSDKs.length === 0 || supportedToolchains.length === 0) {
1314 "Failed to load toolchains or SDKs. This may be due to an outdated personal access token for GitHub."
1317 throw new Error("Failed to load toolchains or SDKs.");
1320 this._versionBundle = await this._versionBundlesLoader.getModuleVersion(
1325 .sort((a, b) => compare(b, a))
1327 picoSDKsHtml += `<option ${
1328 picoSDKsHtml.length === 0 ? "selected " : ""
1330 compare(sdk, "1.5.0") < 0
1331 ? `class="advanced-option-2" disabled`
1333 }>v${sdk}</option>`;
1337 .sort((a, b) => compare(b, a))
1338 .forEach(picotool => {
1339 picotoolsHtml += `<option ${
1340 picotoolsHtml.length === 0 ? "selected " : ""
1341 }value="${picotool}">${picotool}</option>`;
1344 supportedToolchains.forEach(toolchain => {
1345 toolchainsHtml += `<option ${
1346 toolchainsHtml.length === 0 ? "selected " : ""
1347 }value="${toolchain.version}">${toolchain.version.replaceAll(
1354 .sort((a, b) => compare(b.replace("v", ""), a.replace("v", "")))
1356 ninjasHtml += `<option ${
1357 ninjasHtml.length === 0 ? "selected " : ""
1358 }value="${ninja}">${ninja}</option>`;
1361 cmakeReleases.forEach(cmake => {
1362 cmakesHtml += `<option ${
1363 cmakesHtml.length === 0 ? "selected " : ""
1364 }value="${cmake}">${cmake}</option>`;
1368 toolchainsHtml.length === 0 ||
1369 picoSDKsHtml.length === 0 ||
1370 (process.platform !== "linux" && ninjasHtml.length === 0)
1373 "Failed to load toolchains or SDKs. This may be due to an outdated personal access token for GitHub."
1376 throw new Error("Failed to load toolchains or SDKs.");
1378 this._supportedToolchains = supportedToolchains;
1379 // toolchains should be sorted and cant be sorted by compare because
1380 // of their alphanumeric structure
1383 `Error while retrieving SDK and toolchain versions: ${
1384 error instanceof Error ? error.message : (error as string)
1389 void window.showErrorMessage(
1390 "Error while retrieving SDK and toolchain versions."
1396 const isNinjaSystemAvailable =
1397 (await which("ninja", { nothrow: true })) !== null;
1398 const isCmakeSystemAvailable =
1399 (await which("cmake", { nothrow: true })) !== null;
1400 // TODO: check python version, workaround, ownly allow python3 commands on unix
1401 const isPythonSystemAvailable =
1402 (await which("python3", { nothrow: true })) !== null ||
1403 (await which("python", { nothrow: true })) !== null;
1405 if (!isNinjaSystemAvailable && NINJA_AUTO_INSTALL_DISABLED) {
1407 await window.showErrorMessage(
1408 "Not all requirements are met. Automatic ninja installation is currently not supported on aarch64 Linux systems. Please install ninja manually."
1414 this._examples = await loadExamples();
1415 this._logger.info(`Loaded ${this._examples.length} examples.`);
1417 // Restrict the webview to only load specific scripts
1418 const nonce = getNonce();
1419 const isWindows = process.platform === "win32";
1421 return `<!DOCTYPE html>
1424 <meta charset="UTF-8">
1426 <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${
1428 } 'unsafe-inline'; img-src ${
1430 } https:; script-src 'nonce-${nonce}';">
1431 <meta name="viewport" content="width=device-width, initial-scale=1.0">
1433 <link href="${mainStyleUri.toString()}" rel="stylesheet">
1436 this._isProjectImport
1437 ? "Import Pico Project"
1438 : forceCreateFromExample
1439 ? "New Example Pico Project"
1440 : "New Pico Project"
1443 <script nonce="${nonce}" src="${tailwindcssScriptUri.toString()}"></script>
1444 <script nonce="${nonce}">
1449 !this._isProjectImport && this._examples.length > 0
1451 var examples = {${this._examples
1454 `"${e.searchKey}": {"boards": [${e.boards
1456 .join(", ")}], "supportRiscV": ${e.supportRiscV}}`
1461 var doProjectImport = ${this._isProjectImport};
1462 var forceCreateFromExample = ${forceCreateFromExample};
1465 const riscvWhiteSvgUri = "${riscvWhiteSvgUri.toString()}";
1466 const riscvWhiteYellowSvgUri = "${riscvWhiteYellowSvgUri.toString()}";
1467 const riscvBlackSvgUri = "${riscvBlackSvgUri.toString()}";
1468 const riscvColorSvgUri = "${riscvColorSvgUri.toString()}";
1471 <body class="scroll-smooth w-screen">
1472 <div id="above-nav" class="container max-w-6xl mx-auto flex justify-between items-center w-full sticky top-0 z-10 pl-5 h-5">
1474 <div id="nav-overlay" class="overlay hidden md:hidden inset-y-0 right-0 w-auto z-50 overflow-y-auto ease-out bg-slate-400 dark:bg-slate-800 drop-shadow-lg">
1475 <!-- Navigation links go here -->
1476 <ul class="overlay-menu">
1477 <li id="ov-nav-basic" class="overlay-item text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md">Basic Settings</li>
1479 !this._isProjectImport
1481 <li id="ov-nav-features" class="overlay-item project-options text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md">Features</li>
1482 <li id="ov-nav-stdio" class="overlay-item project-options text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md">Stdio support</li>
1483 <li id="ov-nav-pico-wireless" class="overlay-item project-options hidden text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md">Pico wireless options</li>
1484 <li id="ov-nav-code-gen" class="overlay-item project-options text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md">Code generation options</li>
1488 <li id="ov-nav-debugger" class="overlay-item text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md">Debugger</li>
1491 <nav id="top-navbar" class="container max-w-6xl mx-auto flex justify-between items-center w-full sticky top-5 z-10 pl-5 pr-5 h-24 bg-opacity-95 bg-slate-400 dark:bg-slate-800 rounded-md">
1492 <div class="inline-flex h-32 align-middle">
1493 <img src="${navHeaderSvgUri.toString()}" alt="raspberry pi logo" class="h-32 rounded cursor-not-allowed hidden dark:block mb-2"/>
1494 <img src="${navHeaderDarkSvgUri.toString()}" alt="raspberry pi logo" class="h-32 rounded cursor-not-allowed block dark:hidden mb-2"/>
1496 <ul class="pl-3 pr-3 space-x-4 h-auto align-middle hidden md:flex">
1497 <li class="nav-item text-black dark:text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md" id="nav-basic">
1501 !this._isProjectImport
1503 <li class="nav-item project-options text-black dark:text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md" id="nav-features">
1506 <li class="nav-item project-options text-black dark:text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md" id="nav-stdio">
1509 <li class="nav-item project-options hidden text-black dark:text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md" id="nav-pico-wireless">
1510 Pico wireless options
1512 <li class="nav-item project-options text-black dark:text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md" id="nav-code-gen">
1513 Code generation options
1518 <li class="nav-item text-black dark:text-white max-h-14 text-lg flex items-center cursor-pointer p-2 hover:bg-slate-500 hover:bg-opacity-50 dark:hover:bg-slate-600 hover:shadow-md transition-colors motion-reduce:transition-none ease-in-out rounded-md" id="nav-debugger">
1522 <div id="burger-menu" class="flex md:hidden cursor-pointer h-auto me-7">
1523 <div class="bar bg-black dark:bg-white"></div>
1524 <div class="bar bg-black dark:bg-white"></div>
1525 <div class="bar bg-black dark:bg-white"></div>
1528 <main class="container max-w-3xl xl:max-w-5xl mx-auto relative top-14 snap-y mb-20">
1529 <div id="section-basic" class="snap-start">
1530 <h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Basic Settings</h3>
1533 !this._isProjectImport
1534 ? `<div id="project-name-grid" class="grid gap-6 ${
1535 // removed/added dynamic in nav.js
1536 !forceCreateFromExample ? "md:grid-cols-2" : ""
1539 <label for="inp-project-name" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Name</label>
1541 <div class="relative inline-flex w-full">
1542 <input type="text" id="inp-project-name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 invalid:border-pink-500 invalid:text-pink-600 focus:invalid:border-pink-500 focus:invalid:ring-pink-500" placeholder="${
1543 forceCreateFromExample
1544 ? "Select an example"
1546 }" required/> <!-- without this required the webview will crash every time you hit the examples button -->
1547 <button id="project-name-dropdown-button" class="absolute inset-y-0 right-0 flex items-center px-2 border bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white rounded-r-lg border-gray-300 dark:border-gray-600 ${
1548 !forceCreateFromExample ? "hidden" : ""
1551 this._examples.length > 0
1553 <ul id="examples-list" class="bg-gray-50 border-gray-300 dark:bg-gray-700 dark:border-gray-600 rounded-b-lg"></ul>
1554 <!--<datalist id="examples-list">
1555 <option value="\${this._examples
1556 .map(e => e.searchKey)
1558 '">example project</option>\n<option value="'
1559 )}">example project</option>
1564 <button id="btn-create-from-example" class="focus:outline-none bg-transparent ring-2 focus:ring-3 ring-blue-400 dark:ring-blue-700 font-medium rounded-lg px-4 ml-4 hover:bg-blue-500 dark:hover:bg-blue-700 focus:ring-blue-600 dark:focus:ring-blue-800" tooltip="Create from example">Example</button>
1567 <p id="inp-project-name-error" class="mt-2 text-sm text-red-600 dark:text-red-500" hidden>
1568 <span class="font-medium">Error</span> Please enter a valid project name.
1572 <div id="board-type-riscv-grid" class="grid gap-6 grid-cols-2">
1574 <label for="sel-board-type" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Board type</label>
1575 <select id="sel-board-type" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
1576 <option id="option-board-type-${
1578 }" value="${BoardType.pico2}">Pico 2</option>
1579 <option id="option-board-type-${
1581 }" value="${BoardType.pico}">Pico</option>
1582 <option id="option-board-type-${
1584 }" value="${BoardType.picoW}">Pico W</option>
1585 <option id="option-board-type-${
1587 }" value="${BoardType.other}">Other</option>
1590 <div class="use-riscv text-sm font-medium text-gray-900 dark:text-white" hidden>
1591 <label for="riscvToggle" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Architecture (Pico 2)</label>
1592 <div class="flex items-center justify-between p-2 bg-gray-100 rounded-lg dark:bg-gray-700">
1593 <input type="checkbox" id="sel-riscv" class="ms-2" />
1594 <img id="riscvIcon" src="${riscvColorSvgUri.toString()}" alt="RISC-V Logo" class="h-6 mx-auto w-28">
1599 : `<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">
1600 Warning: Project Import Wizard may not work for all projects, and will often require manual correction after the import
1603 <div class="mt-6 mb-4">
1604 <label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" for="file_input">Location</label>
1606 <div class="w-full left-0 flex">
1607 <span class="inline-flex items-center px-3 text-lg text-gray-900 bg-gray-200 border border-r-0 border-gray-300 rounded-l-md dark:bg-gray-600 dark:text-gray-400 dark:border-gray-600">
1608 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" xml:space="preserve" width="24" height="24">
1609 <path style="fill:#027372;" d="M471.149,51.745L438.468,503.83h32.681c17.974,0,32.681-14.706,32.681-32.681V84.426L471.149,51.745z
1611 <path style="fill:#02ACAB;" d="M471.149,51.745v419.404c0,17.974-14.706,32.681-32.681,32.681h-10.894L256,405.787L84.426,503.83
1612 H40.851c-17.974,0-32.681-14.706-32.681-32.681V40.851C8.17,22.877,22.877,8.17,40.851,8.17h76.255L256,122.553L394.894,8.17h32.681
1614 <path style="fill:#D5F6F5;" d="M362.213,155.234V40.851h-65.362v114.383H362.213z"/>
1615 <polygon style="fill:#ABECEC;" points="394.894,8.17 394.894,187.915 362.213,187.915 285.957,98.043 362.213,8.17 "/>
1616 <polygon style="fill:#D5F6F5;" points="362.213,8.17 362.213,40.851 329.532,98.041 362.213,155.231 362.213,187.915
1617 117.106,187.915 117.106,8.17 "/>
1618 <polygon style="fill:#EEEEEE;" points="427.574,329.532 427.574,503.83 394.894,503.83 340.426,285.957 "/>
1619 <polygon style="fill:#FFFFFF;" points="394.894,285.957 394.894,503.83 84.426,503.83 84.426,329.532 "/>
1620 <polygon style="fill:#4D4D4D;" points="362.213,40.851 362.213,155.234 340.426,155.234 318.638,98.043 340.426,40.851 "/>
1621 <rect x="296.851" y="40.851" style="fill:#737373;" width="43.574" height="114.383"/>
1622 <path style="fill:#FEA680;" d="M394.894,253.277h-32.681l32.681,76.255h32.681v-43.574
1623 C427.574,267.983,412.868,253.277,394.894,253.277z"/>
1624 <path style="fill:#FFC1A6;" d="M394.894,285.957v43.574H84.426v-43.574c0-17.974,14.706-32.681,32.681-32.681h245.106
1625 C380.187,253.277,394.894,267.983,394.894,285.957z"/>
1626 <path d="M509.607,78.648L433.351,2.392C431.82,0.861,429.741,0,427.574,0H40.851C18.325,0,0,18.325,0,40.851v430.298
1627 C0,493.675,18.325,512,40.851,512h430.298C493.675,512,512,493.675,512,471.149V84.426C512,82.259,511.139,80.181,509.607,78.648z
1628 M125.277,16.34h261.447v163.404H125.277V16.34z M495.66,471.149c0,13.515-10.995,24.511-24.511,24.511H40.851
1629 c-13.516,0-24.511-10.996-24.511-24.511V40.851c0-13.515,10.995-24.511,24.511-24.511h68.085v171.574c0,4.513,3.658,8.17,8.17,8.17
1630 h277.787c4.512,0,8.17-3.657,8.17-8.17V16.34h21.127l71.469,71.469V471.149z"/>
1631 <path d="M362.213,32.681h-65.362c-4.512,0-8.17,3.657-8.17,8.17v114.383c0,4.513,3.658,8.17,8.17,8.17h65.362
1632 c4.512,0,8.17-3.657,8.17-8.17V40.851C370.383,36.338,366.725,32.681,362.213,32.681z M354.043,147.064h-49.021V49.021h49.021
1634 <path d="M394.894,245.106H117.106c-22.526,0-40.851,18.325-40.851,40.851v185.191c0,4.513,3.658,8.17,8.17,8.17
1635 s8.17-3.657,8.17-8.17V337.702h57.191c4.512,0,8.17-3.657,8.17-8.17c0-4.513-3.658-8.17-8.17-8.17H92.596v-35.404
1636 c0-13.515,10.995-24.511,24.511-24.511h277.787c13.516,0,24.511,10.996,24.511,24.511v35.404H182.468c-4.512,0-8.17,3.657-8.17,8.17
1637 c0,4.513,3.658,8.17,8.17,8.17h236.936v133.447c0,4.513,3.658,8.17,8.17,8.17c4.512,0,8.17-3.657,8.17-8.17V285.957
1638 C435.745,263.432,417.419,245.106,394.894,245.106z"/>
1639 <path d="M340.426,386.723H171.574c-4.512,0-8.17,3.657-8.17,8.17c0,4.513,3.658,8.17,8.17,8.17h168.851
1640 c4.512,0,8.17-3.657,8.17-8.17C348.596,390.38,344.938,386.723,340.426,386.723z"/>
1641 <path d="M284.141,430.298H171.574c-4.512,0-8.17,3.657-8.17,8.17c0,4.513,3.658,8.17,8.17,8.17h112.567
1642 c4.512,0,8.17-3.657,8.17-8.17C292.312,433.955,288.655,430.298,284.141,430.298z"/>
1645 <input type="text" id="inp-project-location" class="w-full bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-r-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-500 dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="${
1646 !this._isProjectImport
1649 : "/home/user/MyProject"
1651 ? "C:\\Project\\To\\Import"
1652 : "/home/user/Project/To/Import"
1653 }" disabled value="${
1654 // support folder names with backslashes on linux and macOS
1655 this._projectRoot !== undefined
1656 ? process.platform === "win32"
1657 ? this._projectRoot.fsPath.replaceAll("\\", "/")
1658 : this._projectRoot.fsPath
1663 id="btn-change-project-location"
1665 class="relative inline-flex items-center justify-center standard-button-size p-1 ml-4 overflow-hidden text-sm font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-pink-500 to-orange-400 group-hover:from-pink-500 group-hover:to-orange-400 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-pink-200 dark:focus:ring-pink-800">
1666 <span class="relative px-4 py-2 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-md group-hover:bg-opacity-0">
1672 <div class="grid gap-6 md:grid-cols-2 mt-6">
1674 <label for="sel-pico-sdk" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Select Pico SDK version</label>
1675 <select id="sel-pico-sdk" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
1680 <div class="advanced-option" hidden>
1681 <label for="sel-toolchain" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Select ARM/RISCV Embeded Toolchain version</label>
1682 <select id="sel-toolchain" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
1687 <label for="sel-picotool" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Select picotool version</label>
1688 <select id="sel-picotool" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
1693 <div class="grid gap-6 md:grid-cols-${
1694 process.platform === "darwin" ||
1695 process.platform === "win32"
1698 } mt-6 advanced-option" hidden>
1700 !NINJA_AUTO_INSTALL_DISABLED
1701 ? `<div class="col-span-2">
1702 <label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Ninja Version:</label>
1705 // TODO: use versionBundleAvailableTest instead of this._versionBundle !== undefined cause if a version with bundle is later selected this section wouldn't be present
1706 this._versionBundle !== undefined
1707 ? `<div class="flex items-center mb-2">
1708 <input type="radio" id="ninja-radio-default-version" name="ninja-version-radio" value="0" class="mr-1 text-blue-500 requires-version-bundle">
1709 <label for="ninja-radio-default-version" class="text-gray-900 dark:text-white">Default version</label>
1715 isNinjaSystemAvailable
1716 ? `<div class="flex items-center mb-2" >
1717 <input type="radio" id="ninja-radio-system-version" name="ninja-version-radio" value="1" class="mr-1 text-blue-500">
1718 <label for="ninja-radio-system-version" class="text-gray-900 dark:text-white">Use system version</label>
1723 <div class="flex items-center mb-2">
1724 <input type="radio" id="ninja-radio-select-version" name="ninja-version-radio" value="2" class="mr-1 text-blue-500">
1725 <label for="ninja-radio-select-version" class="text-gray-900 dark:text-white">Select version:</label>
1726 <select id="sel-ninja" class="ml-2 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
1731 <div class="flex items-center mb-2">
1732 <input type="radio" id="ninja-radio-path-executable" name="ninja-version-radio" value="3" class="mr-1 text-blue-500">
1733 <label for="ninja-radio-path-executable" class="text-gray-900 dark:text-white">Path to executable:</label>
1734 <input type="file" id="ninja-path-executable" multiple="false" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 ms-2">
1740 <div class="col-span-2">
1741 <label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">CMake Version:</label>
1744 this._versionBundle !== undefined
1745 ? `<div class="flex items-center mb-2">
1746 <input type="radio" id="cmake-radio-default-version" name="cmake-version-radio" value="0" class="mr-1 text-blue-500 requires-version-bundle">
1747 <label for="cmake-radio-default-version" class="text-gray-900 dark:text-white">Default version</label>
1753 isCmakeSystemAvailable
1754 ? `<div class="flex items-center mb-2" >
1755 <input type="radio" id="cmake-radio-system-version" name="cmake-version-radio" value="1" class="mr-1 text-blue-500">
1756 <label for="cmake-radio-system-version" class="text-gray-900 dark:text-white">Use system version</label>
1761 <div class="flex items-center mb-2">
1762 <input type="radio" id="cmake-radio-select-version" name="cmake-version-radio" value="2" class="mr-1 text-blue-500">
1763 <label for="cmake-radio-select-version" class="text-gray-900 dark:text-white">Select version:</label>
1764 <select id="sel-cmake" class="ml-2 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
1769 <div class="flex items-center mb-2">
1770 <input type="radio" id="cmake-radio-path-executable" name="cmake-version-radio" value="3" class="mr-1 text-blue-500">
1771 <label for="cmake-radio-path-executable" class="text-gray-900 dark:text-white">Path to executable:</label>
1772 <input type="file" id="cmake-path-executable" multiple="false" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 ms-2">
1777 process.platform === "darwin" ||
1778 process.platform === "win32"
1780 <div class="col-span-2">
1781 <label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Python Version:</label>
1784 this._versionBundle !== undefined
1785 ? `<div class="flex items-center mb-2">
1786 <input type="radio" id="python-radio-default-version" name="python-version-radio" value="0" class="mr-1 text-blue-500 requires-version-bundle">
1787 <label for="python-radio-default-version" class="text-gray-900 dark:text-white">Default version</label>
1793 isPythonSystemAvailable
1794 ? `<div class="flex items-center mb-2" >
1795 <input type="radio" id="python-radio-system-version" name="python-version-radio" value="1" class="mr-1 text-blue-500">
1796 <label for="python-radio-system-version" class="text-gray-900 dark:text-white">Use system version</label>
1801 <div class="flex items-center mb-2">
1802 <input type="radio" id="python-radio-path-executable" name="python-version-radio" value="2" class="mr-1 text-blue-500">
1803 <label for="python-radio-path-executable" class="text-gray-900 dark:text-white">Path to executable:</label>
1804 <input type="file" id="python-path-executable" multiple="false" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 ms-2">
1813 !this._isProjectImport
1814 ? `<div id="section-features" class="snap-start mt-10 project-options">
1815 <h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-8">Features</h3>
1816 <ul class="mb-2 items-center w-full text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg sm:flex dark:bg-gray-700 dark:border-gray-600 dark:text-white">
1817 <li class="w-full border-b border-gray-200 sm:border-b-0 sm:border-r dark:border-gray-600">
1818 <div class="flex items-center pl-3">
1819 <input id="spi-features-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1820 <label for="spi-features-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">SPI</label>
1823 <li class="w-full border-b border-gray-200 sm:border-b-0 sm:border-r dark:border-gray-600">
1824 <div class="flex items-center pl-3">
1825 <input id="pio-features-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1826 <label for="pio-features-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">PIO interface</label>
1829 <li class="w-full border-b border-gray-200 sm:border-b-0 sm:border-r dark:border-gray-600">
1830 <div class="flex items-center pl-3">
1831 <input id="i2c-features-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1832 <label for="i2c-features-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">I2C interface</label>
1835 <li class="w-full dark:border-gray-600">
1836 <div class="flex items-center pl-3">
1837 <input id="dma-features-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1838 <label for="dma-features-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">DMA support</label>
1842 <ul class="items-center w-full text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg sm:flex dark:bg-gray-700 dark:border-gray-600 dark:text-white">
1843 <li class="w-full dark:border-gray-600">
1844 <div class="flex items-center pl-3">
1845 <input id="hwwatchdog-features-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1846 <label for="hwwatchdog-features-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">HW watchdog</label>
1849 <li class="w-full dark:border-gray-600">
1850 <div class="flex items-center pl-3">
1851 <input id="hwclocks-features-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1852 <label for="hwclocks-features-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">HW clocks</label>
1855 <li class="w-full dark:border-gray-600">
1856 <div class="flex items-center pl-3">
1857 <input id="hwinterpolation-features-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1858 <label for="hwinterpolation-features-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">HW interpolation</label>
1861 <li class="w-full dark:border-gray-600">
1862 <div class="flex items-center pl-3">
1863 <input id="hwtimer-features-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1864 <label for="hwtimer-features-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">HW timer</label>
1873 !this._isProjectImport
1874 ? `<div id="section-stdio" class="snap-start mt-10 project-options">
1875 <h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-8">Stdio support</h3>
1876 <ul class="mb-2 items-center w-full text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg sm:flex dark:bg-gray-700 dark:border-gray-600 dark:text-white">
1877 <li class="w-full border-b border-gray-200 sm:border-b-0 sm:border-r dark:border-gray-600">
1878 <div class="flex items-center pl-3">
1879 <input id="uart-stdio-support-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1880 <label for="uart-stdio-support-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Console over UART</label>
1883 <li class="w-full border-b border-gray-200 sm:border-b-0 sm:border-r dark:border-gray-600">
1884 <div class="flex items-center pl-3">
1885 <input id="usb-stdio-support-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1886 <label for="usb-stdio-support-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Console over USB (disables other USB use)</label>
1891 <div id="section-pico-wireless" class="snap-start mt-10 project-options" hidden>
1892 <h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-8">Pico wireless options</h3>
1893 <div class="flex items-stretch space-x-4">
1894 <div class="flex items-center px-4 py-2 border border-gray-200 rounded dark:border-gray-700">
1895 <input checked id="pico-wireless-radio-none" type="radio" value="0" name="pico-wireless-radio" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 outline-none focus:ring-0 focus:ring-offset-5 dark:bg-gray-700 dark:border-gray-600">
1896 <label for="pico-wireless-radio-none" class="w-full py-4 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">None</label>
1898 <div class="flex items-center px-4 py-2 border border-gray-200 rounded dark:border-gray-700">
1899 <input id="pico-wireless-radio-led" type="radio" value="1" name="pico-wireless-radio" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 outline-none focus:ring-0 focus:ring-offset-5 dark:bg-gray-700 dark:border-gray-600">
1900 <label for="pico-wireless-radio-led" class="w-full py-4 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Pico W onboard LED</label>
1902 <div class="flex items-center px-4 py-2 border border-gray-200 rounded dark:border-gray-700">
1903 <input id="pico-wireless-radio-pool" type="radio" value="2" name="pico-wireless-radio" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 outline-none focus:ring-0 focus:ring-offset-5 dark:bg-gray-700 dark:border-gray-600">
1904 <label for="pico-wireless-radio-pool" class="w-full py-4 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Polled lwIP</label>
1906 <div class="flex items-center px-4 py-2 border border-gray-200 rounded dark:border-gray-700">
1907 <input id="pico-wireless-radio-background" type="radio" value="3" name="pico-wireless-radio" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 outline-none focus:ring-0 focus:ring-offset-5 dark:bg-gray-700 dark:border-gray-600">
1908 <label for="pico-wireless-radio-background" class="w-full py-4 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Background lwIP</label>
1912 <div id="section-code-gen" class="snap-start mt-10 project-options">
1913 <h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-8">Code generation options</h3>
1914 <ul class="mb-2 items-center w-full text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg sm:flex dark:bg-gray-700 dark:border-gray-600 dark:text-white">
1915 <li class="w-full border-b border-gray-200 sm:border-b-0 sm:border-r dark:border-gray-600">
1916 <div class="flex items-center pl-3">
1917 <input id="add-examples-code-gen-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1918 <label for="add-examples-code-gen-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Add examples from Pico library</label>
1921 <li class="w-full border-b border-gray-200 sm:border-b-0 sm:border-r dark:border-gray-600">
1922 <div class="flex items-center pl-3">
1923 <input id="run-from-ram-code-gen-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1924 <label for="run-from-ram-code-gen-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Run the program from RAM rather than flash</label>
1928 <ul class="items-center w-full text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg sm:flex dark:bg-gray-700 dark:border-gray-600 dark:text-white">
1929 <li class="w-full border-b border-gray-200 sm:border-b-0 sm:border-r dark:border-gray-600">
1930 <div class="flex items-center pl-3">
1931 <input id="cpp-code-gen-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1932 <label for="cpp-code-gen-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Generate C++ code</label>
1935 <li class="w-full dark:border-gray-600">
1936 <div class="flex items-center pl-3">
1937 <input id="cpp-rtti-code-gen-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1938 <label for="cpp-rtti-code-gen-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Enable C++ RTTI (Uses more memory)</label>
1941 <li class="w-full dark:border-gray-600">
1942 <div class="flex items-center pl-3">
1943 <input id="cpp-exceptions-code-gen-cblist" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500">
1944 <label for="cpp-exceptions-code-gen-cblist" class="w-full py-3 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Enable C++ exceptions (Uses more memory)</label>
1951 <div id="section-debugger" class="snap-start mt-10">
1952 <h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-8">Debugger</h3>
1953 <div class="flex items-stretch space-x-4">
1954 <div class="flex items-center px-4 py-2 border border-gray-200 rounded dark:border-gray-700">
1955 <input checked id="debugger-radio-debug-probe" type="radio" value="0" name="debugger-radio" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 outline-none focus:ring-0 focus:ring-offset-5 dark:bg-gray-700 dark:border-gray-600">
1956 <label for="debugger-radio-debug-probe" class="w-full py-4 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">DebugProbe (CMSIS-DAP) [Default]</label>
1958 <div class="flex items-center px-4 py-2 border border-gray-200 rounded dark:border-gray-700">
1959 <input id="debugger-radio-swd" type="radio" value="1" name="debugger-radio" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 outline-none focus:ring-0 focus:ring-offset-5 dark:bg-gray-700 dark:border-gray-600">
1960 <label for="debugger-radio-swd" class="w-full py-4 ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">SWD (Pi host)</label>
1964 <div class="bottom-3 mt-8 mb-12 w-full flex justify-end">
1965 <button id="btn-advanced-options" class="focus:outline-none bg-transparent ring-2 focus:ring-4 ring-yellow-400 dark:ring-yellow-700 font-medium rounded-lg text-lg px-4 py-2 mr-4 hover:bg-yellow-500 dark:hover:bg-yellow-700 focus:ring-yellow-600 dark:focus:ring-yellow-800">Show Advanced Options</button>
1966 <button id="btn-cancel" class="focus:outline-none bg-transparent ring-2 focus:ring-4 ring-red-400 dark:ring-red-700 font-medium rounded-lg text-lg px-4 py-2 mr-4 hover:bg-red-500 dark:hover:bg-red-700 focus:ring-red-600 dark:focus:ring-red-800">Cancel</button>
1967 <button id="btn-create" class="focus:outline-none bg-transparent ring-2 focus:ring-4 ring-green-400 dark:ring-green-700 font-medium rounded-lg text-lg px-4 py-2 mr-2 hover:bg-green-500 dark:hover:bg-green-700 focus:ring-green-600 dark:focus:ring-green-800">
1968 ${this._isProjectImport ? "Import" : "Create"}
1973 <script nonce="${nonce}" src="${navScriptUri.toString()}"></script>
1974 <script nonce="${nonce}" src="${stateScriptUri.toString()}"></script>
1975 <script nonce="${nonce}" src="${mainScriptUri.toString()}"></script>
1980 private _runGenerator(
1982 options: ExecOptions
1983 ): Promise<number | null> {
1984 return new Promise<number | null>(resolve => {
1985 const generatorProcess = exec(
1988 (error, stdout, stderr) => {
1989 this._logger.debug(stdout);
1990 this._logger.info(stderr);
1992 this._logger.error(`Generator Process error: ${error.message}`);
1993 resolve(null); // indicate error
1998 generatorProcess.on("exit", code => {
1999 // Resolve with exit code or -1 if code is undefined
2005 // TODO: move out of NewProjectPanel class
2007 * Executes the Pico Project Generator with the given options.
2009 * @param options {@link NewProjectOptions} to pass to the Pico Project Generator
2011 private async _executePicoProjectGenerator(
2014 | NewExampleBasedProjectOptions
2015 | ImportProjectOptions,
2017 isExampleBased: boolean = false
2019 const customEnv: { [key: string]: string } = {
2020 ...(process.env as { [key: string]: string }),
2021 // set PICO_SDK_PATH
2022 ["PICO_SDK_PATH"]: options.toolchainAndSDK.sdkPath,
2023 // set PICO_TOOLCHAIN_PATH
2024 ["PICO_TOOLCHAIN_PATH"]: options.toolchainAndSDK.toolchainPath,
2026 // add compiler to PATH
2027 const isWindows = process.platform === "win32";
2028 /*customEnv["PYTHONHOME"] = pythonExe.includes("/")
2029 ? resolve(join(dirname(pythonExe), ".."))
2031 customEnv[isWindows ? "Path" : "PATH"] = `${join(
2032 options.toolchainAndSDK.toolchainPath,
2035 options.cmakeExecutable.includes("/")
2036 ? `${isWindows ? ";" : ":"}${dirname(options.cmakeExecutable)}`
2039 options.ninjaExecutable.includes("/")
2040 ? `${isWindows ? ";" : ":"}${dirname(options.ninjaExecutable)}`
2042 }${isWindows ? ";" : ":"}${customEnv[isWindows ? "Path" : "PATH"]}`;
2044 // convert the selected board type to a vaild option
2045 // for the project generator
2046 let boardTypeFromEnum = "";
2047 if ("boardType" in options) {
2049 boardTypeFromEnum = await enumToBoard(
2051 options.toolchainAndSDK.sdkPath
2054 await window.showErrorMessage(
2055 "Unknown board type: " + options.boardType
2062 const basicNewProjectOptions: string[] =
2063 "consoleOptions" in options
2066 ...options.consoleOptions.map(option => enumToParam(option)),
2067 !options.consoleOptions.includes(ConsoleOption.consoleOverUART)
2071 : "boardType" in options
2072 ? [boardTypeFromEnum]
2074 if (!("boardType" in options) && this._isProjectImport) {
2076 const cmakel = await readFile(
2077 this._isProjectImport
2078 ? join(options.projectRoot, "CMakeLists.txt")
2080 options.projectRoot,
2081 (options as NewExampleBasedProjectOptions).name,
2086 const match = /set\(PICO_BOARD ([A-Za-z_0-9]+) /.exec(cmakel);
2087 if (match && match[1]) {
2088 basicNewProjectOptions.push(`-board ${match[1]}`);
2094 "boardType" in options &&
2096 "name" in options &&
2097 "libNames" in options
2099 for (const libName of options.libNames) {
2100 basicNewProjectOptions.push(`-examLibs ${libName}`);
2104 const libraryAndCodeGenerationOptions: string[] =
2105 "libraries" in options && "codeOptions" in options
2107 ...options.libraries.map(option => enumToParam(option)),
2108 ...options.codeOptions.map(option => enumToParam(option)),
2110 : "name" in options // no libraries and codeOptions but name means it's an example based project
2112 : // no libraries and codeOptions and no name means it's an imported project
2118 : // no name in options means importing project so the folder name is assumed to be the project name
2119 options.projectRoot.substring(
2120 options.projectRoot.lastIndexOf("/") + 1
2123 const command: string = [
2124 `${process.env.ComSpec === "powershell.exe" ? "&" : ""}"${pythonExe}"`,
2125 `"${joinPosix(getScriptsRoot(), "pico_project.py")}"`,
2126 ...basicNewProjectOptions,
2127 ...libraryAndCodeGenerationOptions,
2128 enumToParam(options.debugger),
2129 // generate .vscode config
2133 // cause import project not the project root but the project folder is selected
2135 this._isProjectImport
2136 ? options.projectRoot.substring(
2138 options.projectRoot.lastIndexOf("/")
2141 ? options.projectRoot
2142 : options.projectRoot.replaceAll("\\", "\\\\")
2145 options.toolchainAndSDK.sdkVersion,
2146 "--toolchainVersion",
2147 options.toolchainAndSDK.toolchainVersion,
2148 "--picotoolVersion",
2149 options.toolchainAndSDK.picotoolVersion,
2151 options.toolchainAndSDK.openOCDVersion,
2153 `"${options.ninjaExecutable}"`,
2155 `"${options.cmakeExecutable}"`,
2157 // set custom python executable path used flag if python executable is not in PATH
2158 pythonExe.includes("/") ? "-cupy" : "",
2162 this._logger.debug(`Executing project generator command: ${command}`);
2165 // TODO: use exit codes to determine why the project generator failed (if it did)
2166 // to be able to show the user a more detailed error message
2167 const generatorExitCode = await this._runGenerator(command, {
2169 cwd: getScriptsRoot(),
2173 (process.platform === "linux" && generatorExitCode === null) ||
2174 generatorExitCode === 0
2176 void window.showInformationMessage(
2177 `Successfully generated new project: ${projectName}`
2180 const folderAlreadyOpen = workspace.workspaceFolders?.some(
2181 f => f.uri.fsPath === options.projectRoot
2185 void commands.executeCommand(
2186 "vscode.openFolder",
2188 this._isProjectImport
2189 ? options.projectRoot
2190 : join(options.projectRoot, projectName)
2192 (workspace.workspaceFolders?.length ?? 0) > 0
2195 // restart the extension if the folder was already open cause then vscode won't
2196 // automatically reload the window
2197 if (folderAlreadyOpen) {
2198 void commands.executeCommand("workbench.action.reloadWindow");
2202 `Generator Process exited with code: ${generatorExitCode ?? "null"}`
2205 void window.showErrorMessage(
2206 `Could not create new project: ${projectName}`
2212 export function getNonce(): string {
2215 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
2216 for (let i = 0; i < 32; i++) {
2217 text += possible.charAt(Math.floor(Math.random() * possible.length));