diff --git a/.gitignore b/.gitignore index f36cdc72..61153f78 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ node_modules src/views/app/sprites-generated test/**/c_cpp_properties.json *.vsix +assets/platform/*/arduino-cli diff --git a/NOTICE_arduino-cli.txt b/NOTICE_arduino-cli.txt new file mode 100644 index 00000000..b61411f9 --- /dev/null +++ b/NOTICE_arduino-cli.txt @@ -0,0 +1,16 @@ +NOTICES AND INFORMATION +Do Not Translate or Localize + +Microsoft makes the arduino-cli open source code available at +https://3rdpartysource.microsoft.com, or you may send a check or money order for +US $5.00, including the product name, the open source component name, platform, +and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent +required to debug changes to any libraries licensed under the GNU Lesser General Public License. \ No newline at end of file diff --git a/README.md b/README.md index 60b29a54..72bfaed8 100644 --- a/README.md +++ b/README.md @@ -15,19 +15,31 @@ Welcome to the Visual Studio Code extension for **Arduino** preview ! * Integrated Arduino Debugging New ## Prerequisites -Either the Arduino IDE or Arduino CLI are required. +Either the legacy Arduino IDE or Arduino CLI are required. The recommended +approach is to use the version of Arduino CLI that comes bundled with the +extension, which works out of the box. Support for the legacy Arduino IDE will +be removed in a future version of the extension. -### Arduino IDE -The Arduino IDE can be installed the Arduino [download page](https://www.arduino.cc/en/main/software#download). -- The supported Arduino IDE versions are `1.6.x` and up to, but not including, 2.0.0. +### Arduino CLI +To use the bundled version of Arduino CLI, `arduino.useArduinoCli` should be `true`, +and `arduino.path` and `arduino.commandPath` should be empty or unset. +`arduino.useArduinoCli` defaults to `false` while we deprecate support for the +Arduino IDE, but there will be a prompt when the extension first activates to +switch to the Arduino CLI. + +If you want to use a custom version of Arduino CLI, it can be downloaded from +the repository's [release page](https://github.com/arduino/arduino-cli/releases/). +For custom versions, `arduino.path` must be set to the directory containing the +Arduino CLI executable. + +### Legacy Arduino IDE +Use of the legacy Arduino IDE is not recommended, and support for the legacy +Arduino IDE will be removed in a future version of the extension. The legacy +Arduino IDE can be installed from the Arduino [download page](https://www.arduino.cc/en/main/software#download). +- The supported legacy Arduino IDE versions are `1.6.x` and up to, but not including, `2.0.0`. - The Windows Store's version of the Arduino IDE is not supported because of the sandbox environment that the application runs in. - *Note:* Arduino IDE `1.8.7` had some breaking changes, causing board package and library installation failures. These failures were corrected in `1.8.8` and later. -- *Note:* Arduino IDE `2.X.Y` is not supported at this time [issue 1477](https://github.com/microsoft/vscode-arduino/issues/1477) - -### Arduino CLI -The Arduino CLI can be downloaded from the repository's [release page](https://github.com/arduino/arduino-cli/releases/tag/0.13.0) -- The extension has only been tested with v0.13.0. -- If you use the CLI you will have to set `arduino.path` since the CLI does not have a default path. +- *Note:* Arduino IDE `2.X.Y` is not supported and there are no plans for support in the future ([issue 1477](https://github.com/microsoft/vscode-arduino/issues/1477)). ## Installation Open VS Code and press F1 or Ctrl + Shift + P *or* Cmd + Shift + P to open command palette, select **Install Extension** and type `vscode-arduino`. @@ -71,9 +83,9 @@ This extension provides several commands in the Command Palette (F1 o ## Options | Option | Description | | --- | --- | -| `arduino.useArduinoCli` | Whether to use the Arduino CLI (`true`) or the Arduino IDE (`false`) -- defaults to `false`. If using `true`, make sure to set the `arduino.path` and `arduino.commandPath` values correctly. | -| `arduino.path` | Path to the Arduino installation. You can use a custom version of Arduino by modifying this setting to include the full path. Example: `C:\\Program Files\\Arduino` for Windows, `/Applications` for Mac, `/home//Downloads/arduino-1.8.1` for Linux. (Requires a restart after change). The default value is automatically detected from your Arduino IDE installation path. To use the Arduino CLI, use the path that contains the `arduino-cli` executable (e.g. `/usr/local/bin`). | -| `arduino.commandPath` | Path to an executable (or script) relative to `arduino.path`. The default value is `arduino_debug.exe` for Windows, `Contents/MacOS/Arduino` for Mac and `arduino` for Linux, You also can use a custom launch script to run Arduino by modifying this setting. (Requires a restart after change) Example: `run-arduino.bat` for Windows, `Contents/MacOS/run-arduino.sh` for Mac and `bin/run-arduino.sh` for Linux. To use the Arduino CLI, use `arduino-cli`. | +| `arduino.useArduinoCli` | Whether to use the Arduino CLI (`true`) or the legacy Arduino IDE (`false`) -- defaults to `false`. If using `true`, either leave the `arduino.path` and `arduino.commandPath` values unset to use the bundled version of Arduino CLI, or point them at a custom version of Arduino CLI. Note that a future version of the extension will change this default to `true` and remove support for legacy Arduino IDE. | +| `arduino.path` | Path to the Arduino installation. You can use a custom version of Arduino by modifying this setting to include the full path. Example: `C:\\Program Files\\Arduino` for Windows, `/Applications` for Mac, `/home//Downloads/arduino-1.8.1` for Linux. (Requires a restart after change). The default value is automatically detected from your legacy Arduino IDE installation path. To use the Arduino CLI, use the path that contains the `arduino-cli` executable (e.g. `/usr/local/bin`), or leave it unset to use the bundled version of Arduino CLI. | +| `arduino.commandPath` | Path to an executable (or script) relative to `arduino.path`. The default value is `arduino_debug.exe` for Windows, `Contents/MacOS/Arduino` for Mac and `arduino` for Linux, You also can use a custom launch script to run Arduino by modifying this setting. (Requires a restart after change) Example: `run-arduino.bat` for Windows, `Contents/MacOS/run-arduino.sh` for Mac and `bin/run-arduino.sh` for Linux. To use the bundled version of Arduino CLI, leave this option unset. To use a custom version of Arduino CLI, use `arduino-cli`. | | `arduino.additionalUrls` | Additional Boards Manager URLs for 3rd party packages as a string array. The default value is empty. | | `arduino.logLevel` | CLI output log level. Could be info or verbose. The default value is `"info"`. | | `arduino.clearOutputOnBuild` | Clear the output logs before uploading or verifying. Default value is `false`. | @@ -91,8 +103,7 @@ The following Visual Studio Code settings are available for the Arduino extensio ```json { - "arduino.path": "C:/Program Files (x86)/Arduino", - "arduino.commandPath": "arduino_debug.exe", + "arduino.useArduinoCli": true, "arduino.logLevel": "info", "arduino.allowPDEFiletype": false, "arduino.enableUSBDetection": true, @@ -105,7 +116,6 @@ The following Visual Studio Code settings are available for the Arduino extensio "arduino.defaultBaudRate": 115200 } ``` -*Note:* You only need to set `arduino.path` in Visual Studio Code settings, other options are not required. The following settings are as per sketch settings of the Arduino extension. You can find them in `.vscode/arduino.json` under the workspace. diff --git a/build/assets.json b/build/assets.json new file mode 100644 index 00000000..0cce4e73 --- /dev/null +++ b/build/assets.json @@ -0,0 +1,33 @@ +{ + "version": "0.30.0", + "assets": { + "arduino-cli_0.30.0_Linux_64bit.tar.gz": { + "hash": "f6b1cddf3459b1b6ca9dafe36315c2fa1f6f77386ab3795bbad6a117cbe4230b", + "platforms": ["linux-x64"] + }, + "arduino-cli_0.30.0_Linux_ARM64.tar.gz": { + "hash": "20d2d036a5af4586b5a046f65d926ed012f7fd85469b484a5fc57ef9ef72fb4b", + "platforms": ["linux-arm64"] + }, + "arduino-cli_0.30.0_Linux_ARMv7.tar.gz": { + "hash": "1d873f3cb2b939b2df5a7d25e998bded9a4f0c8ac1226d1d5f9abc2bd95a66c3", + "platforms": ["linux-armhf"] + }, + "arduino-cli_0.30.0_macOS_64bit.tar.gz": { + "hash": "ece83e0bd15813e728d07ce584388a278d62e54529deb84ad07d1521bfe74748", + "platforms": ["darwin-x64"] + }, + "arduino-cli_0.30.0_macOS_ARM64.tar.gz": { + "hash": "e6c1a35df995ecb464ffa85fe8a96b82bd06135ea5ae961cb34d9c9e99e6c2fa", + "platforms": ["darwin-arm64"] + }, + "arduino-cli_0.30.0_Windows_32bit.zip": { + "hash": "cccd4b90524581783cf78a3e4942a5c6bf7508a2a5ec4e008bb9c43f1cdb5dbe", + "platforms": ["win32-ia32"] + }, + "arduino-cli_0.30.0_Windows_64bit.zip": { + "hash": "1808d288382f16594ad73f4797303058b2074b1b375fbb19fca0978033a633af", + "platforms": ["win32-x64"] + } + } +} diff --git a/build/downloadAssets.js b/build/downloadAssets.js new file mode 100644 index 00000000..850ddf65 --- /dev/null +++ b/build/downloadAssets.js @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// This script downloads and verifies platform-specific arduino-cli binaries +// from GitHub releases. The release is specified by the "version" field in +// assets.json. + +const { execSync } = require("child_process"); +const { createHash } = require("crypto"); +const { readFileSync, mkdirSync, rmSync, renameSync } = require("fs"); +const { resolve } = require("path"); + +function run(command) { + console.log(command); + execSync(command); +} + +const config = require('./assets.json'); + +for (const asset in config.assets) { + if (Object.hasOwnProperty.call(config.assets, asset)) { + const platforms = config.assets[asset].platforms; + const hash = config.assets[asset].hash; + for (const platform of platforms) { + const directory = resolve(__dirname, "..", "assets", "platform", platform); + const destination = resolve(directory, asset); + + // Download the asset. + run([ + "curl", + `https://github.com/arduino/arduino-cli/releases/download/${config.version}/${asset}`, + "--location", + `--output-dir ${directory}`, + `--remote-name`, + "--silent", + "--show-error", + ].join(" ")); + + // Verify the hash. + const actualHash = createHash("sha256") + .update(readFileSync(destination)) + .digest("hex"); + if (actualHash !== hash) { + throw new Error( + `Hash mismatch for ${asset} on ${platform}. Expected ${hash} but got ${actualHash}.` + ); + } + + // Extract to an "arduino-cli" directory. + const extractDirectory = resolve(directory, "arduino-cli"); + mkdirSync(extractDirectory, { recursive: true }); + // tar on Linux doesn't understand zip files. + if (asset.endsWith(".zip") && process.platform === 'linux') { + run(`unzip ${destination} -d ${extractDirectory}`); + } else { + run(`tar -xf ${destination} -C ${extractDirectory}`); + } + + // Remove the downloaded archive. We don't need to ship it. + rmSync(destination); + + // VSIX signing will silently strip any extensionless files. Artificially + // add a ".app" extension to extensionless executables. + const executable = resolve(extractDirectory, "arduino-cli"); + try { + renameSync(executable, `${executable}.app`); + } catch { + // The file might not exist. This is expected for Windows. + } + } + } +} diff --git a/cgmanifest.json b/cgmanifest.json new file mode 100644 index 00000000..235a6276 --- /dev/null +++ b/cgmanifest.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://json.schemastore.org/component-detection-manifest.json", + "version": 1, + "registrations": [ + { + "component": { + "type": "other", + "other": { + "name": "arduino-cli (Linux x64)", + "version": "0.30.0", + "downloadUrl": "https://github.com/arduino/arduino-cli/releases/download/0.30.0/arduino-cli_0.30.0_Linux_64bit.tar.gz", + } + } + }, + { + "component": { + "type": "other", + "other": { + "name": "arduino-cli (Linux ARM64)", + "version": "0.30.0", + "downloadUrl": "https://github.com/arduino/arduino-cli/releases/download/0.30.0/arduino-cli_0.30.0_Linux_ARM64.tar.gz", + } + } + }, + { + "component": { + "type": "other", + "other": { + "name": "arduino-cli (Linux ARMv7)", + "version": "0.30.0", + "downloadUrl": "https://github.com/arduino/arduino-cli/releases/download/0.30.0/arduino-cli_0.30.0_Linux_ARMv7.tar.gz", + } + } + }, + { + "component": { + "type": "other", + "other": { + "name": "arduino-cli (macOS x64)", + "version": "0.30.0", + "downloadUrl": "https://github.com/arduino/arduino-cli/releases/download/0.30.0/arduino-cli_0.30.0_macOS_64bit.tar.gz", + } + } + }, + { + "component": { + "type": "other", + "other": { + "name": "arduino-cli (macOS ARM64)", + "version": "0.30.0", + "downloadUrl": "https://github.com/arduino/arduino-cli/releases/download/0.30.0/arduino-cli_0.30.0_macOS_ARM64.tar.gz", + } + } + }, + { + "component": { + "type": "other", + "other": { + "name": "arduino-cli (Windows x86)", + "version": "0.30.0", + "downloadUrl": "https://github.com/arduino/arduino-cli/releases/download/0.30.0/arduino-cli_0.30.0_Windows_32bit.zip", + } + } + }, + { + "component": { + "type": "other", + "other": { + "name": "arduino-cli (Windows x64)", + "version": "0.30.0", + "downloadUrl": "https://github.com/arduino/arduino-cli/releases/download/0.30.0/arduino-cli_0.30.0_Windows_64bit.zip", + } + } + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e34173d7..56fbfe85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,7 +72,7 @@ "webpack": "^4.44.1" }, "engines": { - "vscode": "^1.56.0" + "vscode": "^1.61.0" } }, "node_modules/@ampproject/remapping": { diff --git a/package.json b/package.json index b2ebfec4..4531ba2d 100644 --- a/package.json +++ b/package.json @@ -473,7 +473,7 @@ "arduino.useArduinoCli": { "type": "boolean", "default": false, - "markdownDescription": "Use Arduino CLI installed instead of Arduino IDE. `#arduino.path#` must be set, as there is no default path for 'arduino-cli'. (Requires a restart after change)" + "markdownDescription": "Use Arduino CLI installed instead of the legacy Arduino IDE. If `#arduino.path#` and `#arduino.commandPath#` are not set, the extension will use a version of Arduino CLI bundled with the extension. (Requires a restart after change)" }, "arduino.path": { "type": "string", @@ -596,7 +596,7 @@ }, "scripts": { "build": "gulp build --mode=production", - "postinstall": "cd ./src/views && npm install", + "postinstall": "node ./build/downloadAssets.js && cd ./src/views && npm install", "test": "gulp test" }, "extensionDependencies": [ diff --git a/src/arduino/arduinoSettings.ts b/src/arduino/arduinoSettings.ts index e8f9bbba..26bacaef 100644 --- a/src/arduino/arduinoSettings.ts +++ b/src/arduino/arduinoSettings.ts @@ -1,13 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +import { chmod } from "fs/promises"; import * as os from "os"; import * as path from "path"; +import * as vscode from "vscode"; import * as WinReg from "winreg"; import * as util from "../common/util"; import { resolveArduinoPath } from "../common/platform"; +import * as Logger from "../logger/logger"; import { VscodeSettings } from "./vscodeSettings"; export interface IArduinoSettings { @@ -22,6 +25,7 @@ export interface IArduinoSettings { defaultBaudRate: number; preferences: Map; useArduinoCli: boolean; + usingBundledArduinoCli: boolean; defaultTimestampFormat: string; analyzeOnSettingChange: boolean; reloadPreferences(): void; @@ -44,7 +48,19 @@ export class ArduinoSettings implements IArduinoSettings { private _defaultTimestampFormat: string; - public constructor() { + private _usingBundledArduinoCli: boolean = false; + + private readonly bundledArduinoCliName: { [platform: string]: string } = { + "darwin-arm64": "arduino-cli.app", + "darwin-x64": "arduino-cli.app", + "linux-arm64": "arduino-cli.app", + "linux-armhf": "arduino-cli.app", + "linux-x64": "arduino-cli.app", + "win32-ia32": "arduino-cli.exe", + "win32-x64": "arduino-cli.exe", + }; + + public constructor(private readonly _context: vscode.ExtensionContext) { } public async initialize() { @@ -136,7 +152,7 @@ export class ArduinoSettings implements IArduinoSettings { public get commandPath(): string { const platform = os.platform(); - if (platform === "darwin") { + if (platform === "darwin" && !this._usingBundledArduinoCli) { return path.join(util.resolveMacArduinoAppPath(this._arduinoPath, this._useArduinoCli), path.normalize(this._commandPath)); } else { return path.join(this._arduinoPath, path.normalize(this._commandPath)); @@ -162,6 +178,10 @@ export class ArduinoSettings implements IArduinoSettings { return this._useArduinoCli; } + public get usingBundledArduinoCli() { + return this._usingBundledArduinoCli; + } + public get defaultBaudRate() { return this._defaultBaudRate; } @@ -225,14 +245,35 @@ export class ArduinoSettings implements IArduinoSettings { } } + private async bundledArduinoCliPath(): Promise { + const platform = await util.getPlatform(); + const name = this.bundledArduinoCliName[platform]; + if (!name) { + return undefined; + } + return this._context.asAbsolutePath(path.join("assets", "platform", platform, "arduino-cli", name)); + } + private async tryResolveArduinoPath(): Promise { // Query arduino path sequentially from the following places such as "vscode user settings", "system environment variables", // "usual software installation directory for each os". // 1. Search vscode user settings first. const configValue = VscodeSettings.getInstance().arduinoPath; if (!configValue || !configValue.trim()) { - // 2 & 3. Resolve arduino path from system environment variables and usual software installation directory. - this._arduinoPath = await Promise.resolve(resolveArduinoPath()); + // 2. Resolve arduino path from the bundled arduino-cli, if CLI support is enabled. + const bundledPath = await this.bundledArduinoCliPath(); + if (bundledPath && this._useArduinoCli && !this._commandPath) { + // The extension VSIX stripped the executable bit, so we need to set it. + // 0x755 means rwxr-xr-x (read and execute for everyone, write for owner). + await chmod(bundledPath, 0o755); + this._usingBundledArduinoCli = true; + Logger.traceUserData("using-bundled-arduino-cli"); + this._arduinoPath = path.dirname(bundledPath); + this._commandPath = path.basename(bundledPath); + } else { + // 3 & 4. Resolve arduino path from system environment variables and usual software installation directory. + this._arduinoPath = await Promise.resolve(resolveArduinoPath()); + } } else { this._arduinoPath = configValue; } diff --git a/src/arduino/vscodeSettings.ts b/src/arduino/vscodeSettings.ts index ae2617d4..54d43dfb 100644 --- a/src/arduino/vscodeSettings.ts +++ b/src/arduino/vscodeSettings.ts @@ -42,6 +42,9 @@ export interface IVscodeSettings { analyzeOnOpen: boolean; analyzeOnSettingChange: boolean; updateAdditionalUrls(urls: string[]): void; + setUseArduinoCli(value: boolean): Promise; + setArduinoPath(value: string): Promise; + setCommandPath(value: string): Promise; } export class VscodeSettings implements IVscodeSettings { @@ -60,10 +63,18 @@ export class VscodeSettings implements IVscodeSettings { return this.getConfigValue(configKeys.ARDUINO_PATH); } + public setArduinoPath(value: string): Promise { + return this.setConfigValue(configKeys.ARDUINO_PATH, value, true); + } + public get commandPath(): string { return this.getConfigValue(configKeys.ARDUINO_COMMAND_PATH); } + public setCommandPath(value: string): Promise { + return this.setConfigValue(configKeys.ARDUINO_COMMAND_PATH, value, true); + } + public get additionalUrls(): string[] { const value = this.getConfigValue(configKeys.ADDITIONAL_URLS); @@ -116,6 +127,10 @@ export class VscodeSettings implements IVscodeSettings { return this.getConfigValue(configKeys.USE_ARDUINO_CLI); } + public setUseArduinoCli(value: boolean): Promise { + return this.setConfigValue(configKeys.USE_ARDUINO_CLI, value, true); + } + public get skipHeaderProvider(): boolean { return this.getConfigValue(configKeys.SKIP_HEADER_PROVIDER); } diff --git a/src/arduinoActivator.ts b/src/arduinoActivator.ts index a593a311..2758e52e 100644 --- a/src/arduinoActivator.ts +++ b/src/arduinoActivator.ts @@ -15,6 +15,7 @@ import ArduinoContext from "./arduinoContext"; import { DeviceContext } from "./deviceContext"; class ArduinoActivator { + public context: vscode.ExtensionContext; private _initializePromise: Promise; public async activate() { if (this._initializePromise) { @@ -23,7 +24,7 @@ class ArduinoActivator { } this._initializePromise = (async () => { - const arduinoSettings = new ArduinoSettings(); + const arduinoSettings = new ArduinoSettings(this.context); await arduinoSettings.initialize(); const arduinoApp = new ArduinoApp(arduinoSettings); diff --git a/src/common/constants.ts b/src/common/constants.ts index c8a7de10..d3ed20b4 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -29,9 +29,10 @@ export const EXAMPLES_URI = vscode.Uri.parse("arduino-manager://arduino/arduino- export const messages = { ARDUINO_FILE_ERROR: "The arduino.json file format is not correct.", NO_BOARD_SELECTED: "Please select the board type first.", - INVALID_ARDUINO_PATH: "Cannot find Arduino IDE. Please specify the \"arduino.path\" in the User Settings. Requires a restart after change.", - INVALID_COMMAND_PATH: "Please check the \"arduino.commandPath\" in the User Settings." + -"Requires a restart after change.Cannot find the command file:", + INVALID_ARDUINO_PATH: "Cannot find Arduino tools.", + INVALID_COMMAND_PATH: "Cannot find the command file:", + SWITCH_TO_BUNDLED_CLI: "Use Arduino CLI bundled with this extension instead?", + REMOVE_ARDUINO_IDE_SUPPORT: "Support for the legacy Arduino IDE will be removed soon.", FAILED_SEND_SERIALPORT: "Failed to send message to serial port.", SERIAL_PORT_NOT_STARTED: "Serial Monitor has not been started.", SEND_BEFORE_OPEN_SERIALPORT: "Please open a serial port first.", diff --git a/src/common/util.ts b/src/common/util.ts index c1ba5f26..eff20d34 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -468,3 +468,32 @@ export function toStringArray(value: string | string[]): string[] { return []; } + +// Ideally VS Code would provide an API to get the target platform name. For +// now, copy the logic from VS Code. +// https://github.com/microsoft/vscode/issues/170196 +// tslint:disable-next-line +// https://github.com/microsoft/vscode/blob/78d05ca56a6881e7503a5173131c9803b059012d/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts#L171-L196 +export async function getPlatform(): Promise { + let platform: string = process.platform; + if (platform === "linux") { + let content: string | undefined; + try { + content = await fs.promises.readFile("/etc/os-release", "ascii"); + } catch (error) { + try { + content = await fs.promises.readFile("/usr/lib/os-release", "ascii"); + } catch (error) { + /* Ignore */ + } + } + if ( + !!content && + // eslint-disable-next-line no-control-regex + (content.match(/^ID=([^\u001b\r\n]*)/m) || [])[1] === "alpine" + ) { + platform = "alpine"; + } + } + return `${platform}-${process.arch}`; +} diff --git a/src/extension.ts b/src/extension.ts index fdcf1ba9..fafbd7a1 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -33,6 +33,7 @@ const usbDetectorModule = impor("./serialmonitor/usbDetector") as typeof import export async function activate(context: vscode.ExtensionContext) { Logger.configure(context); + arduinoActivatorModule.default.context = context; const activeGuid = uuidModule().replace(/-/g, ""); Logger.traceUserData("start-activate-extension", { correlationId: activeGuid }); // Show a warning message if the working file is not under the workspace folder. @@ -78,6 +79,39 @@ export async function activate(context: vscode.ExtensionContext) { nsatModule.NSAT.takeSurvey(context); }; + + async function askSwitchToBundledCli(message: string): Promise { + const result = await vscode.window.showErrorMessage( + message, "Use bundled arduino-cli", "View settings"); + switch (result) { + case "Use bundled arduino-cli": { + Logger.traceUserData("switched-to-bundled-arduino-cli"); + await vscodeSettings.setUseArduinoCli(true); + await vscodeSettings.setArduinoPath(undefined); + await vscodeSettings.setCommandPath(undefined); + await vscode.commands.executeCommand("workbench.action.reloadWindow"); + break; + } + case "View settings": + await vscode.commands.executeCommand("workbench.action.openGlobalSettings"); + break; + } + } + + if (!vscodeSettings.useArduinoCli) { + // This notification is intentionally a little bit annoying (popping on + // workspace open with no permanent dismissal) because we want to move + // users off of Arduino IDE. + // + // Unfortunately, we can't simply switch the default value of + // useArduinoCli to true because that would break users that were + // intentionally using the Arduino IDE but relied on the current default + // value of false. A future will make this breaking change with + // appropriate messaging. + Logger.traceUserData("using-legacy-arduino-ide"); + void askSwitchToBundledCli(constants.messages.REMOVE_ARDUINO_IDE_SUPPORT + " " + constants.messages.SWITCH_TO_BUNDLED_CLI); + } + const registerArduinoCommand = (command: string, commandBody: (...args: any[]) => any, getUserData?: () => any): number => { return context.subscriptions.push(vscode.commands.registerCommand(command, async (...args: any[]) => { if (!arduinoContextModule.default.initialized) { @@ -91,12 +125,20 @@ export async function activate(context: vscode.ExtensionContext) { const arduinoPath = arduinoContextModule.default.arduinoApp.settings.arduinoPath; const commandPath = arduinoContextModule.default.arduinoApp.settings.commandPath; const useArduinoCli = arduinoContextModule.default.arduinoApp.settings.useArduinoCli; - // Pop up vscode User Settings page when cannot resolve arduino path. - if (!arduinoPath || !validateArduinoPath(arduinoPath, useArduinoCli)) { - Logger.notifyUserError("InvalidArduinoPath", new Error(constants.messages.INVALID_ARDUINO_PATH)); - vscode.commands.executeCommand("workbench.action.openGlobalSettings"); + const usingBundledArduinoCli = arduinoContextModule.default.arduinoApp.settings.usingBundledArduinoCli; + + // Ask the user to switch to the bundled Arduino CLI if we can't resolve the specified path. + if (!usingBundledArduinoCli && (!arduinoPath || !validateArduinoPath(arduinoPath, useArduinoCli))) { + Logger.traceError("InvalidArduinoPath", new Error(constants.messages.INVALID_ARDUINO_PATH)); + await askSwitchToBundledCli(constants.messages.INVALID_ARDUINO_PATH + " " + constants.messages.SWITCH_TO_BUNDLED_CLI); } else if (!commandPath || !util.fileExistsSync(commandPath)) { - Logger.notifyUserError("InvalidCommandPath", new Error(constants.messages.INVALID_COMMAND_PATH + commandPath)); + const error = new Error(constants.messages.INVALID_COMMAND_PATH + commandPath); + if (usingBundledArduinoCli) { + Logger.notifyUserError("InvalidCommandPath", error); + } else { + Logger.traceError("InvalidCommandPath", error); + await askSwitchToBundledCli(error.message + " " + constants.messages.SWITCH_TO_BUNDLED_CLI); + } } else { await commandExecution(command, commandBody, args, getUserData); }