From bfcd53483bab3c0c0870fe94d4ea435ab613fc84 Mon Sep 17 00:00:00 2001 From: Vincenzo Gibiino Date: Sat, 8 Sep 2018 18:47:04 +0200 Subject: [PATCH 1/4] add default output path --- README.md | 4 +++- package.json | 4 ++++ src/arduino/arduino.ts | 13 ++++++++++++- src/arduino/arduinoSettings.ts | 18 ++++++++++++++++++ src/arduino/vscodeSettings.ts | 6 ++++++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d37f2e14..179f3e59 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,8 @@ The following Visual Studio Code settings are available for the Arduino extensio "https://raw.githubusercontent.com/VSChina/azureiotdevkit_tools/master/package_azureboard_index.json", "http://arduino.esp8266.com/stable/package_esp8266com_index.json" ], - "arduino.defaultBaudRate": 115200 + "arduino.defaultBaudRate": 115200, + "arduino.defaultOutputPath": "" } ``` - `arduino.path` - Path to Arduino, 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. @@ -81,6 +82,7 @@ serialport-list -f jsonline - `arduino.disableTestingOpen` - Disable/enable auto sending a test message to serial port for checking open status. The default value is `false` (a test message will be sent). - `arduino.skipHeaderProvider` - Enable/disable the extension providing completion items for headers. The functionality is included in newer versions of the C++ extension. The default value is `false`. - `arduino.defaultBaudRate` - Default baud rate for serial port monitor. The default value is 115200. Supported values are 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400 and 250000. +- `arduino.defaultOutputPath` - Default output path for the compiler. By default, this option is not set. The following settings are per sketch settings of the Arduino extension. You can find them in `.vscode/arduino.json` under the workspace. diff --git a/package.json b/package.json index f4e23d9e..1faab0c5 100644 --- a/package.json +++ b/package.json @@ -462,6 +462,10 @@ "defaultBaudRate": { "type": "number", "default": 115200 + }, + "arduino.defaultOutputPath": { + "type": "string", + "default": "" } } }, diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index d07bf535..af0e556a 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -147,6 +147,10 @@ export class ArduinoApp { return; } + args.push("--pref", `build.path=${outputPath}`); + arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); + } else if (VscodeSettings.getInstance().defaultOutputPath != "") { + const outputPath = VscodeSettings.getInstance().defaultOutputPath; args.push("--pref", `build.path=${outputPath}`); arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); } else { @@ -207,6 +211,9 @@ export class ArduinoApp { if (dc.output) { const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); args.push("--pref", `build.path=${outputPath}`); + } else if (VscodeSettings.getInstance().defaultOutputPath != "") { + const outputPath = VscodeSettings.getInstance().defaultOutputPath; + args.push("--pref", `build.path=${outputPath}`); } else { const msg = "Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README."; arduinoChannel.warning(msg); @@ -267,13 +274,17 @@ export class ArduinoApp { return; } + args.push("--pref", `build.path=${outputPath}`); + arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); + } else if (VscodeSettings.getInstance().defaultOutputPath != "") { + const outputPath = VscodeSettings.getInstance().defaultOutputPath; args.push("--pref", `build.path=${outputPath}`); arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); } else { const msg = "Output path is not specified. Unable to reuse previously compiled files. Verify could be slow. See README."; arduinoChannel.warning(msg); } - + arduinoChannel.show(); // we need to return the result of verify try { diff --git a/src/arduino/arduinoSettings.ts b/src/arduino/arduinoSettings.ts index 58419519..bb4a1def 100644 --- a/src/arduino/arduinoSettings.ts +++ b/src/arduino/arduinoSettings.ts @@ -21,6 +21,7 @@ export interface IArduinoSettings { sketchbookPath: string; preferencePath: string; defaultBaudRate: number; + defaultOutputPath: string; preferences: Map; reloadPreferences(): void; } @@ -35,6 +36,8 @@ export class ArduinoSettings implements IArduinoSettings { private _sketchbookPath: string; private _defaultBaudRate: number; + + private _defaultOutputPath: string; private _preferences: Map; @@ -46,6 +49,7 @@ export class ArduinoSettings implements IArduinoSettings { this._commandPath = VscodeSettings.getInstance().commandPath; await this.tryResolveArduinoPath(); await this.tryGetDefaultBaudRate(); + await this.tryGetDefaultOutputPath(); if (platform === "win32") { await this.updateWindowsPath(); if (this._commandPath === "") { @@ -153,6 +157,10 @@ export class ArduinoSettings implements IArduinoSettings { public get defaultBaudRate() { return this._defaultBaudRate; } + + public get defaultOutputPath(): string { + return this._defaultOutputPath; + } public reloadPreferences() { this._preferences = util.parseConfigFile(this.preferencePath); @@ -227,4 +235,14 @@ export class ArduinoSettings implements IArduinoSettings { this._defaultBaudRate = configValue; } } + + private async tryGetDefaultOutputPath(): Promise { + const configValue = VscodeSettings.getInstance().defaultOutputPath; + if (!configValue || !configValue.trim()) { + //not set + //return ""? + } else { + this._defaultOutputPath = configValue; + } + } } diff --git a/src/arduino/vscodeSettings.ts b/src/arduino/vscodeSettings.ts index a2fce5ab..a028219e 100644 --- a/src/arduino/vscodeSettings.ts +++ b/src/arduino/vscodeSettings.ts @@ -14,6 +14,7 @@ const configKeys = { IGNORE_BOARDS: "arduino.ignoreBoards", SKIP_HEADER_PROVIDER: "arduino.skipHeaderProvider", DEFAULT_BAUD_RATE: "arduino.defaultBaudRate", + DEFAULT_OUTPUT_PATH: "arduino.defaultOutputPath", }; export interface IVscodeSettings { @@ -26,6 +27,7 @@ export interface IVscodeSettings { ignoreBoards: string[]; skipHeaderProvider: boolean; defaultBaudRate: number; + defaultOutputPath: string; updateAdditionalUrls(urls: string | string[]): void; } @@ -76,6 +78,10 @@ export class VscodeSettings implements IVscodeSettings { public get defaultBaudRate(): number { return this.getConfigValue(configKeys.DEFAULT_BAUD_RATE); } + + public get defaultOutputPath(): string { + return this.getConfigValue(configKeys.DEFAULT_OUTPUT_PATH); + } public get skipHeaderProvider(): boolean { return this.getConfigValue(configKeys.SKIP_HEADER_PROVIDER); From add34d7c21974e2b41ddfacc281af47e62eba611 Mon Sep 17 00:00:00 2001 From: aster94 Date: Tue, 2 Oct 2018 12:21:49 +0200 Subject: [PATCH 2/4] make tslint happy again --- src/arduino/arduino.ts | 8 ++++---- src/arduino/arduinoSettings.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index af0e556a..c0418671 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -149,7 +149,7 @@ export class ArduinoApp { args.push("--pref", `build.path=${outputPath}`); arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); - } else if (VscodeSettings.getInstance().defaultOutputPath != "") { + } else if (VscodeSettings.getInstance().defaultOutputPath !== "") { const outputPath = VscodeSettings.getInstance().defaultOutputPath; args.push("--pref", `build.path=${outputPath}`); arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); @@ -211,9 +211,10 @@ export class ArduinoApp { if (dc.output) { const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); args.push("--pref", `build.path=${outputPath}`); - } else if (VscodeSettings.getInstance().defaultOutputPath != "") { + } else if (VscodeSettings.getInstance().defaultOutputPath !== "") { const outputPath = VscodeSettings.getInstance().defaultOutputPath; args.push("--pref", `build.path=${outputPath}`); + arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); } else { const msg = "Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README."; arduinoChannel.warning(msg); @@ -276,7 +277,7 @@ export class ArduinoApp { args.push("--pref", `build.path=${outputPath}`); arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); - } else if (VscodeSettings.getInstance().defaultOutputPath != "") { + } else if (VscodeSettings.getInstance().defaultOutputPath !== "") { const outputPath = VscodeSettings.getInstance().defaultOutputPath; args.push("--pref", `build.path=${outputPath}`); arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); @@ -295,7 +296,6 @@ export class ArduinoApp { arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); return false; } - } // Add selected library path to the intellisense search path. diff --git a/src/arduino/arduinoSettings.ts b/src/arduino/arduinoSettings.ts index bb4a1def..a8d77b98 100644 --- a/src/arduino/arduinoSettings.ts +++ b/src/arduino/arduinoSettings.ts @@ -239,8 +239,8 @@ export class ArduinoSettings implements IArduinoSettings { private async tryGetDefaultOutputPath(): Promise { const configValue = VscodeSettings.getInstance().defaultOutputPath; if (!configValue || !configValue.trim()) { - //not set - //return ""? + // not set + // return ""? } else { this._defaultOutputPath = configValue; } From b61e034902f0de28859c46b0299cffce6d38975e Mon Sep 17 00:00:00 2001 From: aster94 Date: Tue, 2 Oct 2018 12:28:29 +0200 Subject: [PATCH 3/4] added forgotten arduinoChannel.info in upload using programmer --- src/arduino/arduino.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index c0418671..13e49a90 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -211,6 +211,7 @@ export class ArduinoApp { if (dc.output) { const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); args.push("--pref", `build.path=${outputPath}`); + arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); } else if (VscodeSettings.getInstance().defaultOutputPath !== "") { const outputPath = VscodeSettings.getInstance().defaultOutputPath; args.push("--pref", `build.path=${outputPath}`); From 4c3a0f418ebff4e0d3deedab1b25d44178504aab Mon Sep 17 00:00:00 2001 From: Vincenzo Gibiino Date: Thu, 18 Oct 2018 12:54:03 +0200 Subject: [PATCH 4/4] if defaultoutputpath is not set is undefinied --- src/arduino/arduino.ts | 1381 ++++++++++++++++---------------- src/arduino/arduinoSettings.ts | 3 +- 2 files changed, 690 insertions(+), 694 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index f49ff63f..f06581f9 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -1,692 +1,689 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import * as fs from "fs"; -import * as glob from "glob"; -import * as os from "os"; -import * as path from "path"; -import * as vscode from "vscode"; - -import * as constants from "../common/constants"; -import * as util from "../common/util"; -import * as Logger from "../logger/logger"; - -import { DeviceContext } from "../deviceContext"; -import { IArduinoSettings } from "./arduinoSettings"; -import { BoardManager } from "./boardManager"; -import { ExampleManager } from "./exampleManager"; -import { LibraryManager } from "./libraryManager"; -import { VscodeSettings } from "./vscodeSettings"; - -import { arduinoChannel } from "../common/outputChannel"; -import { ArduinoWorkspace } from "../common/workspace"; -import { SerialMonitor } from "../serialmonitor/serialMonitor"; -import { UsbDetector } from "../serialmonitor/usbDetector"; -import { ProgrammerManager } from "./programmerManager"; - -/** - * Represent an Arduino application based on the official Arduino IDE. - */ -export class ArduinoApp { - - private _boardManager: BoardManager; - - private _libraryManager: LibraryManager; - - private _exampleManager: ExampleManager; - - private _programmerManager: ProgrammerManager; - - /** - * @param {IArduinoSettings} _settings ArduinoSetting object. - */ - constructor(private _settings: IArduinoSettings) { - } - - /** - * Need refresh Arduino IDE's setting when starting up. - * @param {boolean} force - Whether force initialize the arduino - */ - public async initialize(force: boolean = false) { - if (!util.fileExistsSync(this._settings.preferencePath)) { - try { - // Use empty pref value to initialize preference.txt file - await this.setPref("boardsmanager.additional.urls", ""); - this._settings.reloadPreferences(); // reload preferences. - } catch (ex) { - } - } - if (force || !util.fileExistsSync(path.join(this._settings.packagePath, "package_index.json"))) { - try { - // Use the dummy package to initialize the Arduino IDE - await this.installBoard("dummy", "", "", true); - } catch (ex) { - } - } - } - - /** - * Initialize the arduino library. - * @param {boolean} force - Whether force refresh library index file - */ - public async initializeLibrary(force: boolean = false) { - if (force || !util.fileExistsSync(path.join(this._settings.packagePath, "library_index.json"))) { - try { - // Use the dummy library to initialize the Arduino IDE - await this.installLibrary("dummy", "", true); - } catch (ex) { - } - } - } - - /** - * Set the Arduino preferences value. - * @param {string} key - The preference key - * @param {string} value - The preference value - */ - public async setPref(key, value) { - try { - await util.spawn(this._settings.commandPath, - null, - ["--pref", `${key}=${value}`, "--save-prefs"]); - } catch (ex) { - } - } - - public async upload() { - const dc = DeviceContext.getInstance(); - const boardDescriptor = this.getBoardBuildString(); - if (!boardDescriptor) { - return; - } - - if (!ArduinoWorkspace.rootPath) { - vscode.window.showWarningMessage("Cannot find the sketch file."); - return; - } - - if (!dc.sketch || !util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, dc.sketch))) { - await this.getMainSketch(dc); - } - if (!dc.port) { - vscode.window.showErrorMessage("Please specify the upload serial port."); - return; - } - - arduinoChannel.show(); - arduinoChannel.start(`Upload sketch - ${dc.sketch}`); - - const serialMonitor = SerialMonitor.getInstance(); - - const needRestore = await serialMonitor.closeSerialMonitor(dc.port); - UsbDetector.getInstance().pauseListening(); - await vscode.workspace.saveAll(false); - - if (dc.prebuild) { - arduinoChannel.info(`Run prebuild command: ${dc.prebuild}`); - const prebuildargs = dc.prebuild.split(" "); - const prebuildCommand = prebuildargs.shift(); - try { - await util.spawn(prebuildCommand, arduinoChannel.channel, prebuildargs, {shell: true, cwd: ArduinoWorkspace.rootPath}); - } catch (ex) { - arduinoChannel.error(`Run prebuild failed: \n${ex.error}`); - return; - } - } - - const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); - const args = ["--upload", "--board", boardDescriptor, "--port", dc.port, appPath]; - if (VscodeSettings.getInstance().logLevel === "verbose") { - args.push("--verbose"); - } - if (dc.output) { - const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); - const dirPath = path.dirname(outputPath); - if (!util.directoryExistsSync(dirPath)) { - Logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); - return; - } - - args.push("--pref", `build.path=${outputPath}`); - arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); - } else if (VscodeSettings.getInstance().defaultOutputPath !== "") { - const outputPath = VscodeSettings.getInstance().defaultOutputPath; - args.push("--pref", `build.path=${outputPath}`); - arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); - } else { - const msg = "Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README."; - arduinoChannel.warning(msg); - } - await util.spawn(this._settings.commandPath, arduinoChannel.channel, args).then(async () => { - UsbDetector.getInstance().resumeListening(); - if (needRestore) { - await serialMonitor.openSerialMonitor(); - } - arduinoChannel.end(`Uploaded the sketch: ${dc.sketch}${os.EOL}`); - }, (reason) => { - arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); - }); - } - - public async uploadUsingProgrammer() { - const dc = DeviceContext.getInstance(); - const boardDescriptor = this.getBoardBuildString(); - if (!boardDescriptor) { - return; - } - - const selectProgrammer = this.getProgrammerString(); - if (!selectProgrammer) { - return; - } - - if (!ArduinoWorkspace.rootPath) { - vscode.window.showWarningMessage("Cannot find the sketch file."); - return; - } - - if (!dc.sketch || !util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, dc.sketch))) { - await this.getMainSketch(dc); - } - if (!dc.port) { - vscode.window.showErrorMessage("Please specify the upload serial port."); - return; - } - - arduinoChannel.show(); - arduinoChannel.start(`Upload sketch - ${dc.sketch}`); - - const serialMonitor = SerialMonitor.getInstance(); - - const needRestore = await serialMonitor.closeSerialMonitor(dc.port); - UsbDetector.getInstance().pauseListening(); - await vscode.workspace.saveAll(false); - - const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); - const args = ["--upload", "--board", boardDescriptor, "--port", dc.port, "--useprogrammer", - "--pref", "programmer=" + selectProgrammer, appPath]; - if (VscodeSettings.getInstance().logLevel === "verbose") { - args.push("--verbose"); - } - if (dc.output) { - const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); - const dirPath = path.dirname(outputPath); - if (!util.directoryExistsSync(dirPath)) { - Logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); - return; - } - - args.push("--pref", `build.path=${outputPath}`); - arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); - } else if (VscodeSettings.getInstance().defaultOutputPath !== "") { - const outputPath = VscodeSettings.getInstance().defaultOutputPath; - args.push("--pref", `build.path=${outputPath}`); - arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); - } else { - const msg = "Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README."; - arduinoChannel.warning(msg); - } - await util.spawn(this._settings.commandPath, arduinoChannel.channel, args).then(async () => { - UsbDetector.getInstance().resumeListening(); - if (needRestore) { - await serialMonitor.openSerialMonitor(); - } - arduinoChannel.end(`Uploaded the sketch: ${dc.sketch}${os.EOL}`); - }, (reason) => { - arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); - }); - } - - public async verify(output: string = "") { - const dc = DeviceContext.getInstance(); - const boardDescriptor = this.getBoardBuildString(); - if (!boardDescriptor) { - return; - } - - if (!ArduinoWorkspace.rootPath) { - vscode.window.showWarningMessage("Cannot find the sketch file."); - return; - } - - if (!dc.sketch || !util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, dc.sketch))) { - await this.getMainSketch(dc); - } - - await vscode.workspace.saveAll(false); - - arduinoChannel.start(`Verify sketch - ${dc.sketch}`); - - if (dc.prebuild) { - arduinoChannel.info(`Run prebuild command: ${dc.prebuild}`); - const prebuildargs = dc.prebuild.split(" "); - const prebuildCommand = prebuildargs.shift(); - try { - await util.spawn(prebuildCommand, arduinoChannel.channel, prebuildargs, {shell: true, cwd: ArduinoWorkspace.rootPath}); - } catch (ex) { - arduinoChannel.error(`Run prebuild failed: \n${ex.error}`); - return; - } - } - - const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); - const args = ["--verify", "--board", boardDescriptor, appPath]; - if (VscodeSettings.getInstance().logLevel === "verbose") { - args.push("--verbose"); - } - if (output || dc.output) { - const outputPath = path.resolve(ArduinoWorkspace.rootPath, output || dc.output); - const dirPath = path.dirname(outputPath); - if (!util.directoryExistsSync(dirPath)) { - Logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); - return; - } - - args.push("--pref", `build.path=${outputPath}`); - arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); - } else if (VscodeSettings.getInstance().defaultOutputPath !== "") { - const outputPath = VscodeSettings.getInstance().defaultOutputPath; - args.push("--pref", `build.path=${outputPath}`); - arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); - } else { - const msg = "Output path is not specified. Unable to reuse previously compiled files. Verify could be slow. See README."; - arduinoChannel.warning(msg); - } - - arduinoChannel.show(); - // we need to return the result of verify - try { - await util.spawn(this._settings.commandPath, arduinoChannel.channel, args); - arduinoChannel.end(`Finished verify sketch - ${dc.sketch}${os.EOL}`); - return true; - } catch (reason) { - arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); - return false; - } - } - - // Add selected library path to the intellisense search path. - public addLibPath(libraryPath: string) { - let libPaths; - if (libraryPath) { - libPaths = [libraryPath]; - } else { - libPaths = this.getDefaultPackageLibPaths(); - } - - const defaultForcedInclude = this.getDefaultForcedIncludeFiles(); - - if (!ArduinoWorkspace.rootPath) { - return; - } - const configFilePath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); - let deviceContext = null; - if (!util.fileExistsSync(configFilePath)) { - util.mkdirRecursivelySync(path.dirname(configFilePath)); - deviceContext = {}; - } else { - deviceContext = util.tryParseJSON(fs.readFileSync(configFilePath, "utf8")); - } - if (!deviceContext) { - Logger.notifyAndThrowUserError("arduinoFileError", new Error(constants.messages.ARDUINO_FILE_ERROR)); - } - - deviceContext.configurations = deviceContext.configurations || []; - let configSection = null; - deviceContext.configurations.forEach((section) => { - if (section.name === util.getCppConfigPlatform()) { - configSection = section; - } - }); - - if (!configSection) { - configSection = { - name: util.getCppConfigPlatform(), - includePath: [], - }; - deviceContext.configurations.push(configSection); - } - - libPaths.forEach((childLibPath) => { - childLibPath = path.resolve(path.normalize(childLibPath)); - if (configSection.includePath && configSection.includePath.length) { - for (const existingPath of configSection.includePath) { - if (childLibPath === path.resolve(path.normalize(existingPath))) { - return; - } - } - } else { - configSection.includePath = []; - } - configSection.includePath.unshift(childLibPath); - }); - - if (!configSection.forcedInclude) { - configSection.forcedInclude = defaultForcedInclude; - } else { - for (let i = 0; i < configSection.forcedInclude.length; i++) { - if (/arduino\.h$/i.test(configSection.forcedInclude[i])) { - configSection.forcedInclude.splice(i, 1); - i--; - } - } - configSection.forcedInclude = defaultForcedInclude.concat(configSection.forcedInclude); - } - - fs.writeFileSync(configFilePath, JSON.stringify(deviceContext, null, 4)); - } - - // Include the *.h header files from selected library to the arduino sketch. - public async includeLibrary(libraryPath: string) { - if (!ArduinoWorkspace.rootPath) { - return; - } - const dc = DeviceContext.getInstance(); - const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); - if (util.fileExistsSync(appPath)) { - const hFiles = glob.sync(`${libraryPath}/*.h`, { - nodir: true, - matchBase: true, - }); - const hIncludes = hFiles.map((hFile) => { - return `#include <${path.basename(hFile)}>`; - }).join(os.EOL); - - // Open the sketch and bring up it to current visible view. - const textDocument = await vscode.workspace.openTextDocument(appPath); - await vscode.window.showTextDocument(textDocument, vscode.ViewColumn.One, true); - const activeEditor = vscode.window.visibleTextEditors.find((textEditor) => { - return path.resolve(textEditor.document.fileName) === path.resolve(appPath); - }); - if (activeEditor) { - // Insert *.h at the beginning of the sketch code. - await activeEditor.edit((editBuilder) => { - editBuilder.insert(new vscode.Position(0, 0), `${hIncludes}${os.EOL}${os.EOL}`); - }); - } - } - } - - /** - * Install arduino board package based on package name and platform hardware architecture. - */ - public async installBoard(packageName: string, arch: string = "", version: string = "", showOutput: boolean = true) { - arduinoChannel.show(); - const updatingIndex = packageName === "dummy" && !arch && !version; - if (updatingIndex) { - arduinoChannel.start(`Update package index files...`); - } else { - try { - const packagePath = path.join(this._settings.packagePath, "packages", packageName); - if (util.directoryExistsSync(packagePath)) { - util.rmdirRecursivelySync(packagePath); - } - arduinoChannel.start(`Install package - ${packageName}...`); - } catch (error) { - arduinoChannel.start(`Install package - ${packageName} failed under directory : ${error.path}${os.EOL} -Please make sure the folder is not occupied by other procedures .`); - arduinoChannel.error(`Error message - ${error.message}${os.EOL}`); - arduinoChannel.error(`Exit with code=${error.code}${os.EOL}`); - return; - } - } - try { - await util.spawn(this._settings.commandPath, - showOutput ? arduinoChannel.channel : null, - ["--install-boards", `${packageName}${arch && ":" + arch}${version && ":" + version}`]); - - if (updatingIndex) { - arduinoChannel.end("Updated package index files."); - } else { - arduinoChannel.end(`Installed board package - ${packageName}${os.EOL}`); - } - } catch (error) { - // If a platform with the same version is already installed, nothing is installed and program exits with exit code 1 - if (error.code === 1) { - if (updatingIndex) { - arduinoChannel.end("Updated package index files."); - } else { - arduinoChannel.end(`Installed board package - ${packageName}${os.EOL}`); - } - } else { - arduinoChannel.error(`Exit with code=${error.code}${os.EOL}`); - } - } - } - - public uninstallBoard(boardName: string, packagePath: string) { - arduinoChannel.start(`Uninstall board package - ${boardName}...`); - util.rmdirRecursivelySync(packagePath); - arduinoChannel.end(`Uninstalled board package - ${boardName}${os.EOL}`); - } - - public async installLibrary(libName: string, version: string = "", showOutput: boolean = true) { - arduinoChannel.show(); - const updatingIndex = (libName === "dummy" && !version); - if (updatingIndex) { - arduinoChannel.start("Update library index files..."); - } else { - arduinoChannel.start(`Install library - ${libName}`); - } - try { - await util.spawn(this._settings.commandPath, - showOutput ? arduinoChannel.channel : null, - ["--install-library", `${libName}${version && ":" + version}`]); - - if (updatingIndex) { - arduinoChannel.end("Updated library index files."); - } else { - arduinoChannel.end(`Installed library - ${libName}${os.EOL}`); - } - } catch (error) { - // If a library with the same version is already installed, nothing is installed and program exits with exit code 1 - if (error.code === 1) { - if (updatingIndex) { - arduinoChannel.end("Updated library index files."); - } else { - arduinoChannel.end(`Installed library - ${libName}${os.EOL}`); - } - } else { - arduinoChannel.error(`Exit with code=${error.code}${os.EOL}`); - } - } - } - - public uninstallLibrary(libName: string, libPath: string) { - arduinoChannel.start(`Remove library - ${libName}`); - util.rmdirRecursivelySync(libPath); - arduinoChannel.end(`Removed library - ${libName}${os.EOL}`); - } - - public getDefaultPackageLibPaths(): string[] { - const result = []; - const boardDescriptor = this._boardManager.currentBoard; - if (!boardDescriptor) { - return result; - } - const toolsPath = boardDescriptor.platform.rootBoardPath; - result.push(path.normalize(path.join(toolsPath, "**"))); - // if (util.directoryExistsSync(path.join(toolsPath, "cores"))) { - // const coreLibs = fs.readdirSync(path.join(toolsPath, "cores")); - // if (coreLibs && coreLibs.length > 0) { - // coreLibs.forEach((coreLib) => { - // result.push(path.normalize(path.join(toolsPath, "cores", coreLib))); - // }); - // } - // } - // return result; - - // /hardware// -> /tools - const toolPath = path.join(toolsPath, "..", "..", "..", "tools"); - if (fs.existsSync(toolPath)) { - result.push(path.normalize(path.join(toolPath, "**"))); - } - return result; - } - - public getDefaultForcedIncludeFiles(): string[] { - const result = []; - const boardDescriptor = this._boardManager.currentBoard; - if (!boardDescriptor) { - return result; - } - const arduinoHeadFilePath = path.normalize(path.join(boardDescriptor.platform.rootBoardPath, "cores", "arduino", "Arduino.h")); - if (fs.existsSync(arduinoHeadFilePath)) { - result.push(arduinoHeadFilePath); - } - return result; - } - - public openExample(example) { - function tmpName(name) { - let counter = 0; - let candidateName = name; - while (true) { - if (!util.fileExistsSync(candidateName) && !util.directoryExistsSync(candidateName)) { - return candidateName; - } - counter++; - candidateName = `${name}_${counter}`; - } - } - - // Step 1: Copy the example project to a temporary directory. - const sketchPath = path.join(this._settings.sketchbookPath, "generated_examples"); - if (!util.directoryExistsSync(sketchPath)) { - util.mkdirRecursivelySync(sketchPath); - } - let destExample = ""; - if (util.directoryExistsSync(example)) { - destExample = tmpName(path.join(sketchPath, path.basename(example))); - util.cp(example, destExample); - } else if (util.fileExistsSync(example)) { - const exampleName = path.basename(example, path.extname(example)); - destExample = tmpName(path.join(sketchPath, exampleName)); - util.mkdirRecursivelySync(destExample); - util.cp(example, path.join(destExample, path.basename(example))); - } - if (destExample) { - // Step 2: Scaffold the example project to an arduino project. - const items = fs.readdirSync(destExample); - const sketchFile = items.find((item) => { - return util.isArduinoFile(path.join(destExample, item)); - }); - if (sketchFile) { - // Generate arduino.json - const dc = DeviceContext.getInstance(); - const arduinoJson = { - sketch: sketchFile, - port: dc.port || "COM1", - board: dc.board, - configuration: dc.configuration, - }; - const arduinoConfigFilePath = path.join(destExample, constants.ARDUINO_CONFIG_FILE); - util.mkdirRecursivelySync(path.dirname(arduinoConfigFilePath)); - fs.writeFileSync(arduinoConfigFilePath, JSON.stringify(arduinoJson, null, 4)); - - // Generate cpptools intellisense config - const cppConfigFilePath = path.join(destExample, constants.CPP_CONFIG_FILE); - - // Current workspace - let includePath = ["${workspaceRoot}"]; - // Defaut package for this board - const defaultPackageLibPaths = this.getDefaultPackageLibPaths(); - includePath = includePath.concat(defaultPackageLibPaths); - // Arduino built-in package tools - includePath.push(path.join(this._settings.arduinoPath, "hardware", "tools", "**")); - // Arduino built-in libraries - includePath.push(path.join(this._settings.arduinoPath, "libraries", "**")); - // Arduino custom package tools - includePath.push(path.join(os.homedir(), "Documents", "Arduino", "hardware", "tools", "**")); - // Arduino custom libraries - includePath.push(path.join(os.homedir(), "Documents", "Arduino", "libraries", "**")); - - const forcedInclude = this.getDefaultForcedIncludeFiles(); - - const defines = [ - "ARDUINO=10800", - ]; - const cppConfig = { - configurations: [{ - name: util.getCppConfigPlatform(), - defines, - includePath, - forcedInclude, - intelliSenseMode: "clang-x64", - cStandard: "c11", - cppStandard: "c++17", - }], - version: 3, - }; - util.mkdirRecursivelySync(path.dirname(cppConfigFilePath)); - fs.writeFileSync(cppConfigFilePath, JSON.stringify(cppConfig, null, 4)); - } - - // Step 3: Open the arduino project at a new vscode window. - vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(destExample), true); - } - return destExample; - } - - public get settings() { - return this._settings; - } - - public get boardManager() { - return this._boardManager; - } - - public set boardManager(value: BoardManager) { - this._boardManager = value; - } - - public get libraryManager() { - return this._libraryManager; - } - - public set libraryManager(value: LibraryManager) { - this._libraryManager = value; - } - - public get exampleManager() { - return this._exampleManager; - } - - public set exampleManager(value: ExampleManager) { - this._exampleManager = value; - } - - public get programmerManager() { - return this._programmerManager; - } - - public set programmerManager(value: ProgrammerManager) { - this._programmerManager = value; - } - - private getProgrammerString(): string { - const selectProgrammer = this.programmerManager.currentProgrammer; - if (!selectProgrammer) { - Logger.notifyUserError("getProgrammerString", new Error(constants.messages.NO_PROGRAMMMER_SELECTED)); - return; - } - return selectProgrammer; - } - - private getBoardBuildString(): string { - const selectedBoard = this.boardManager.currentBoard; - if (!selectedBoard) { - Logger.notifyUserError("getBoardBuildString", new Error(constants.messages.NO_BOARD_SELECTED)); - return; - } - return selectedBoard.getBuildConfig(); - } - - private async getMainSketch(dc: DeviceContext) { - await dc.resolveMainSketch(); - if (!dc.sketch) { - vscode.window.showErrorMessage("No sketch file was found. Please specify the sketch in the arduino.json file"); - throw new Error("No sketch file was found."); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import * as fs from "fs"; +import * as glob from "glob"; +import * as os from "os"; +import * as path from "path"; +import * as vscode from "vscode"; + +import * as constants from "../common/constants"; +import * as util from "../common/util"; +import * as Logger from "../logger/logger"; + +import { DeviceContext } from "../deviceContext"; +import { IArduinoSettings } from "./arduinoSettings"; +import { BoardManager } from "./boardManager"; +import { ExampleManager } from "./exampleManager"; +import { LibraryManager } from "./libraryManager"; +import { VscodeSettings } from "./vscodeSettings"; + +import { arduinoChannel } from "../common/outputChannel"; +import { ArduinoWorkspace } from "../common/workspace"; +import { SerialMonitor } from "../serialmonitor/serialMonitor"; +import { UsbDetector } from "../serialmonitor/usbDetector"; +import { ProgrammerManager } from "./programmerManager"; + +/** + * Represent an Arduino application based on the official Arduino IDE. + */ +export class ArduinoApp { + + private _boardManager: BoardManager; + + private _libraryManager: LibraryManager; + + private _exampleManager: ExampleManager; + + private _programmerManager: ProgrammerManager; + + /** + * @param {IArduinoSettings} _settings ArduinoSetting object. + */ + constructor(private _settings: IArduinoSettings) { + } + + /** + * Need refresh Arduino IDE's setting when starting up. + * @param {boolean} force - Whether force initialize the arduino + */ + public async initialize(force: boolean = false) { + if (!util.fileExistsSync(this._settings.preferencePath)) { + try { + // Use empty pref value to initialize preference.txt file + await this.setPref("boardsmanager.additional.urls", ""); + this._settings.reloadPreferences(); // reload preferences. + } catch (ex) { + } + } + if (force || !util.fileExistsSync(path.join(this._settings.packagePath, "package_index.json"))) { + try { + // Use the dummy package to initialize the Arduino IDE + await this.installBoard("dummy", "", "", true); + } catch (ex) { + } + } + } + + /** + * Initialize the arduino library. + * @param {boolean} force - Whether force refresh library index file + */ + public async initializeLibrary(force: boolean = false) { + if (force || !util.fileExistsSync(path.join(this._settings.packagePath, "library_index.json"))) { + try { + // Use the dummy library to initialize the Arduino IDE + await this.installLibrary("dummy", "", true); + } catch (ex) { + } + } + } + + /** + * Set the Arduino preferences value. + * @param {string} key - The preference key + * @param {string} value - The preference value + */ + public async setPref(key, value) { + try { + await util.spawn(this._settings.commandPath, + null, + ["--pref", `${key}=${value}`, "--save-prefs"]); + } catch (ex) { + } + } + + public async upload() { + const dc = DeviceContext.getInstance(); + const boardDescriptor = this.getBoardBuildString(); + if (!boardDescriptor) { + return; + } + + if (!ArduinoWorkspace.rootPath) { + vscode.window.showWarningMessage("Cannot find the sketch file."); + return; + } + + if (!dc.sketch || !util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, dc.sketch))) { + await this.getMainSketch(dc); + } + if (!dc.port) { + vscode.window.showErrorMessage("Please specify the upload serial port."); + return; + } + + arduinoChannel.show(); + arduinoChannel.start(`Upload sketch - ${dc.sketch}`); + + const serialMonitor = SerialMonitor.getInstance(); + + const needRestore = await serialMonitor.closeSerialMonitor(dc.port); + UsbDetector.getInstance().pauseListening(); + await vscode.workspace.saveAll(false); + + if (dc.prebuild) { + arduinoChannel.info(`Run prebuild command: ${dc.prebuild}`); + const prebuildargs = dc.prebuild.split(" "); + const prebuildCommand = prebuildargs.shift(); + try { + await util.spawn(prebuildCommand, arduinoChannel.channel, prebuildargs, {shell: true, cwd: ArduinoWorkspace.rootPath}); + } catch (ex) { + arduinoChannel.error(`Run prebuild failed: \n${ex.error}`); + return; + } + } + + const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); + const args = ["--upload", "--board", boardDescriptor, "--port", dc.port, appPath]; + if (VscodeSettings.getInstance().logLevel === "verbose") { + args.push("--verbose"); + } + if (dc.output) { + const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); + const dirPath = path.dirname(outputPath); + if (!util.directoryExistsSync(dirPath)) { + Logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); + return; + } + args.push("--pref", `build.path=${outputPath}`); + arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); + } else if (!VscodeSettings.getInstance().defaultOutputPath) { + const outputPath = VscodeSettings.getInstance().defaultOutputPath; + args.push("--pref", `build.path=${outputPath}`); + arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); + } else { + const msg = "Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README."; + arduinoChannel.warning(msg); + } + await util.spawn(this._settings.commandPath, arduinoChannel.channel, args).then(async () => { + UsbDetector.getInstance().resumeListening(); + if (needRestore) { + await serialMonitor.openSerialMonitor(); + } + arduinoChannel.end(`Uploaded the sketch: ${dc.sketch}${os.EOL}`); + }, (reason) => { + arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); + }); + } + + public async uploadUsingProgrammer() { + const dc = DeviceContext.getInstance(); + const boardDescriptor = this.getBoardBuildString(); + if (!boardDescriptor) { + return; + } + + const selectProgrammer = this.getProgrammerString(); + if (!selectProgrammer) { + return; + } + + if (!ArduinoWorkspace.rootPath) { + vscode.window.showWarningMessage("Cannot find the sketch file."); + return; + } + + if (!dc.sketch || !util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, dc.sketch))) { + await this.getMainSketch(dc); + } + if (!dc.port) { + vscode.window.showErrorMessage("Please specify the upload serial port."); + return; + } + + arduinoChannel.show(); + arduinoChannel.start(`Upload sketch - ${dc.sketch}`); + + const serialMonitor = SerialMonitor.getInstance(); + + const needRestore = await serialMonitor.closeSerialMonitor(dc.port); + UsbDetector.getInstance().pauseListening(); + await vscode.workspace.saveAll(false); + + const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); + const args = ["--upload", "--board", boardDescriptor, "--port", dc.port, "--useprogrammer", + "--pref", "programmer=" + selectProgrammer, appPath]; + if (VscodeSettings.getInstance().logLevel === "verbose") { + args.push("--verbose"); + } + if (dc.output) { + const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); + const dirPath = path.dirname(outputPath); + if (!util.directoryExistsSync(dirPath)) { + Logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); + return; + } + args.push("--pref", `build.path=${outputPath}`); + arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); + } else if (!VscodeSettings.getInstance().defaultOutputPath) { + const outputPath = VscodeSettings.getInstance().defaultOutputPath; + args.push("--pref", `build.path=${outputPath}`); + arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); + } else { + const msg = "Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README."; + arduinoChannel.warning(msg); + } + await util.spawn(this._settings.commandPath, arduinoChannel.channel, args).then(async () => { + UsbDetector.getInstance().resumeListening(); + if (needRestore) { + await serialMonitor.openSerialMonitor(); + } + arduinoChannel.end(`Uploaded the sketch: ${dc.sketch}${os.EOL}`); + }, (reason) => { + arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); + }); + } + + public async verify(output: string = "") { + const dc = DeviceContext.getInstance(); + const boardDescriptor = this.getBoardBuildString(); + if (!boardDescriptor) { + return; + } + + if (!ArduinoWorkspace.rootPath) { + vscode.window.showWarningMessage("Cannot find the sketch file."); + return; + } + + if (!dc.sketch || !util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, dc.sketch))) { + await this.getMainSketch(dc); + } + + await vscode.workspace.saveAll(false); + + arduinoChannel.start(`Verify sketch - ${dc.sketch}`); + + if (dc.prebuild) { + arduinoChannel.info(`Run prebuild command: ${dc.prebuild}`); + const prebuildargs = dc.prebuild.split(" "); + const prebuildCommand = prebuildargs.shift(); + try { + await util.spawn(prebuildCommand, arduinoChannel.channel, prebuildargs, {shell: true, cwd: ArduinoWorkspace.rootPath}); + } catch (ex) { + arduinoChannel.error(`Run prebuild failed: \n${ex.error}`); + return; + } + } + + const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); + const args = ["--verify", "--board", boardDescriptor, appPath]; + if (VscodeSettings.getInstance().logLevel === "verbose") { + args.push("--verbose"); + } + if (output || dc.output) { + const outputPath = path.resolve(ArduinoWorkspace.rootPath, output || dc.output); + const dirPath = path.dirname(outputPath); + if (!util.directoryExistsSync(dirPath)) { + Logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); + return; + } + args.push("--pref", `build.path=${outputPath}`); + arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); + } else if (!VscodeSettings.getInstance().defaultOutputPath) { + const outputPath = VscodeSettings.getInstance().defaultOutputPath; + args.push("--pref", `build.path=${outputPath}`); + arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); + } else { + const msg = "Output path is not specified. Unable to reuse previously compiled files. Verify could be slow. See README."; + arduinoChannel.warning(msg); + } + + arduinoChannel.show(); + // we need to return the result of verify + try { + await util.spawn(this._settings.commandPath, arduinoChannel.channel, args); + arduinoChannel.end(`Finished verify sketch - ${dc.sketch}${os.EOL}`); + return true; + } catch (reason) { + arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); + return false; + } + } + + // Add selected library path to the intellisense search path. + public addLibPath(libraryPath: string) { + let libPaths; + if (libraryPath) { + libPaths = [libraryPath]; + } else { + libPaths = this.getDefaultPackageLibPaths(); + } + + const defaultForcedInclude = this.getDefaultForcedIncludeFiles(); + + if (!ArduinoWorkspace.rootPath) { + return; + } + const configFilePath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); + let deviceContext = null; + if (!util.fileExistsSync(configFilePath)) { + util.mkdirRecursivelySync(path.dirname(configFilePath)); + deviceContext = {}; + } else { + deviceContext = util.tryParseJSON(fs.readFileSync(configFilePath, "utf8")); + } + if (!deviceContext) { + Logger.notifyAndThrowUserError("arduinoFileError", new Error(constants.messages.ARDUINO_FILE_ERROR)); + } + + deviceContext.configurations = deviceContext.configurations || []; + let configSection = null; + deviceContext.configurations.forEach((section) => { + if (section.name === util.getCppConfigPlatform()) { + configSection = section; + } + }); + + if (!configSection) { + configSection = { + name: util.getCppConfigPlatform(), + includePath: [], + }; + deviceContext.configurations.push(configSection); + } + + libPaths.forEach((childLibPath) => { + childLibPath = path.resolve(path.normalize(childLibPath)); + if (configSection.includePath && configSection.includePath.length) { + for (const existingPath of configSection.includePath) { + if (childLibPath === path.resolve(path.normalize(existingPath))) { + return; + } + } + } else { + configSection.includePath = []; + } + configSection.includePath.unshift(childLibPath); + }); + + if (!configSection.forcedInclude) { + configSection.forcedInclude = defaultForcedInclude; + } else { + for (let i = 0; i < configSection.forcedInclude.length; i++) { + if (/arduino\.h$/i.test(configSection.forcedInclude[i])) { + configSection.forcedInclude.splice(i, 1); + i--; + } + } + configSection.forcedInclude = defaultForcedInclude.concat(configSection.forcedInclude); + } + + fs.writeFileSync(configFilePath, JSON.stringify(deviceContext, null, 4)); + } + + // Include the *.h header files from selected library to the arduino sketch. + public async includeLibrary(libraryPath: string) { + if (!ArduinoWorkspace.rootPath) { + return; + } + const dc = DeviceContext.getInstance(); + const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); + if (util.fileExistsSync(appPath)) { + const hFiles = glob.sync(`${libraryPath}/*.h`, { + nodir: true, + matchBase: true, + }); + const hIncludes = hFiles.map((hFile) => { + return `#include <${path.basename(hFile)}>`; + }).join(os.EOL); + + // Open the sketch and bring up it to current visible view. + const textDocument = await vscode.workspace.openTextDocument(appPath); + await vscode.window.showTextDocument(textDocument, vscode.ViewColumn.One, true); + const activeEditor = vscode.window.visibleTextEditors.find((textEditor) => { + return path.resolve(textEditor.document.fileName) === path.resolve(appPath); + }); + if (activeEditor) { + // Insert *.h at the beginning of the sketch code. + await activeEditor.edit((editBuilder) => { + editBuilder.insert(new vscode.Position(0, 0), `${hIncludes}${os.EOL}${os.EOL}`); + }); + } + } + } + + /** + * Install arduino board package based on package name and platform hardware architecture. + */ + public async installBoard(packageName: string, arch: string = "", version: string = "", showOutput: boolean = true) { + arduinoChannel.show(); + const updatingIndex = packageName === "dummy" && !arch && !version; + if (updatingIndex) { + arduinoChannel.start(`Update package index files...`); + } else { + try { + const packagePath = path.join(this._settings.packagePath, "packages", packageName); + if (util.directoryExistsSync(packagePath)) { + util.rmdirRecursivelySync(packagePath); + } + arduinoChannel.start(`Install package - ${packageName}...`); + } catch (error) { + arduinoChannel.start(`Install package - ${packageName} failed under directory : ${error.path}${os.EOL} +Please make sure the folder is not occupied by other procedures .`); + arduinoChannel.error(`Error message - ${error.message}${os.EOL}`); + arduinoChannel.error(`Exit with code=${error.code}${os.EOL}`); + return; + } + } + try { + await util.spawn(this._settings.commandPath, + showOutput ? arduinoChannel.channel : null, + ["--install-boards", `${packageName}${arch && ":" + arch}${version && ":" + version}`]); + + if (updatingIndex) { + arduinoChannel.end("Updated package index files."); + } else { + arduinoChannel.end(`Installed board package - ${packageName}${os.EOL}`); + } + } catch (error) { + // If a platform with the same version is already installed, nothing is installed and program exits with exit code 1 + if (error.code === 1) { + if (updatingIndex) { + arduinoChannel.end("Updated package index files."); + } else { + arduinoChannel.end(`Installed board package - ${packageName}${os.EOL}`); + } + } else { + arduinoChannel.error(`Exit with code=${error.code}${os.EOL}`); + } + } + } + + public uninstallBoard(boardName: string, packagePath: string) { + arduinoChannel.start(`Uninstall board package - ${boardName}...`); + util.rmdirRecursivelySync(packagePath); + arduinoChannel.end(`Uninstalled board package - ${boardName}${os.EOL}`); + } + + public async installLibrary(libName: string, version: string = "", showOutput: boolean = true) { + arduinoChannel.show(); + const updatingIndex = (libName === "dummy" && !version); + if (updatingIndex) { + arduinoChannel.start("Update library index files..."); + } else { + arduinoChannel.start(`Install library - ${libName}`); + } + try { + await util.spawn(this._settings.commandPath, + showOutput ? arduinoChannel.channel : null, + ["--install-library", `${libName}${version && ":" + version}`]); + + if (updatingIndex) { + arduinoChannel.end("Updated library index files."); + } else { + arduinoChannel.end(`Installed library - ${libName}${os.EOL}`); + } + } catch (error) { + // If a library with the same version is already installed, nothing is installed and program exits with exit code 1 + if (error.code === 1) { + if (updatingIndex) { + arduinoChannel.end("Updated library index files."); + } else { + arduinoChannel.end(`Installed library - ${libName}${os.EOL}`); + } + } else { + arduinoChannel.error(`Exit with code=${error.code}${os.EOL}`); + } + } + } + + public uninstallLibrary(libName: string, libPath: string) { + arduinoChannel.start(`Remove library - ${libName}`); + util.rmdirRecursivelySync(libPath); + arduinoChannel.end(`Removed library - ${libName}${os.EOL}`); + } + + public getDefaultPackageLibPaths(): string[] { + const result = []; + const boardDescriptor = this._boardManager.currentBoard; + if (!boardDescriptor) { + return result; + } + const toolsPath = boardDescriptor.platform.rootBoardPath; + result.push(path.normalize(path.join(toolsPath, "**"))); + // if (util.directoryExistsSync(path.join(toolsPath, "cores"))) { + // const coreLibs = fs.readdirSync(path.join(toolsPath, "cores")); + // if (coreLibs && coreLibs.length > 0) { + // coreLibs.forEach((coreLib) => { + // result.push(path.normalize(path.join(toolsPath, "cores", coreLib))); + // }); + // } + // } + // return result; + + // /hardware// -> /tools + const toolPath = path.join(toolsPath, "..", "..", "..", "tools"); + if (fs.existsSync(toolPath)) { + result.push(path.normalize(path.join(toolPath, "**"))); + } + return result; + } + + public getDefaultForcedIncludeFiles(): string[] { + const result = []; + const boardDescriptor = this._boardManager.currentBoard; + if (!boardDescriptor) { + return result; + } + const arduinoHeadFilePath = path.normalize(path.join(boardDescriptor.platform.rootBoardPath, "cores", "arduino", "Arduino.h")); + if (fs.existsSync(arduinoHeadFilePath)) { + result.push(arduinoHeadFilePath); + } + return result; + } + + public openExample(example) { + function tmpName(name) { + let counter = 0; + let candidateName = name; + while (true) { + if (!util.fileExistsSync(candidateName) && !util.directoryExistsSync(candidateName)) { + return candidateName; + } + counter++; + candidateName = `${name}_${counter}`; + } + } + + // Step 1: Copy the example project to a temporary directory. + const sketchPath = path.join(this._settings.sketchbookPath, "generated_examples"); + if (!util.directoryExistsSync(sketchPath)) { + util.mkdirRecursivelySync(sketchPath); + } + let destExample = ""; + if (util.directoryExistsSync(example)) { + destExample = tmpName(path.join(sketchPath, path.basename(example))); + util.cp(example, destExample); + } else if (util.fileExistsSync(example)) { + const exampleName = path.basename(example, path.extname(example)); + destExample = tmpName(path.join(sketchPath, exampleName)); + util.mkdirRecursivelySync(destExample); + util.cp(example, path.join(destExample, path.basename(example))); + } + if (destExample) { + // Step 2: Scaffold the example project to an arduino project. + const items = fs.readdirSync(destExample); + const sketchFile = items.find((item) => { + return util.isArduinoFile(path.join(destExample, item)); + }); + if (sketchFile) { + // Generate arduino.json + const dc = DeviceContext.getInstance(); + const arduinoJson = { + sketch: sketchFile, + port: dc.port || "COM1", + board: dc.board, + configuration: dc.configuration, + }; + const arduinoConfigFilePath = path.join(destExample, constants.ARDUINO_CONFIG_FILE); + util.mkdirRecursivelySync(path.dirname(arduinoConfigFilePath)); + fs.writeFileSync(arduinoConfigFilePath, JSON.stringify(arduinoJson, null, 4)); + + // Generate cpptools intellisense config + const cppConfigFilePath = path.join(destExample, constants.CPP_CONFIG_FILE); + + // Current workspace + let includePath = ["${workspaceRoot}"]; + // Defaut package for this board + const defaultPackageLibPaths = this.getDefaultPackageLibPaths(); + includePath = includePath.concat(defaultPackageLibPaths); + // Arduino built-in package tools + includePath.push(path.join(this._settings.arduinoPath, "hardware", "tools", "**")); + // Arduino built-in libraries + includePath.push(path.join(this._settings.arduinoPath, "libraries", "**")); + // Arduino custom package tools + includePath.push(path.join(os.homedir(), "Documents", "Arduino", "hardware", "tools", "**")); + // Arduino custom libraries + includePath.push(path.join(os.homedir(), "Documents", "Arduino", "libraries", "**")); + + const forcedInclude = this.getDefaultForcedIncludeFiles(); + + const defines = [ + "ARDUINO=10800", + ]; + const cppConfig = { + configurations: [{ + name: util.getCppConfigPlatform(), + defines, + includePath, + forcedInclude, + intelliSenseMode: "clang-x64", + cStandard: "c11", + cppStandard: "c++17", + }], + version: 3, + }; + util.mkdirRecursivelySync(path.dirname(cppConfigFilePath)); + fs.writeFileSync(cppConfigFilePath, JSON.stringify(cppConfig, null, 4)); + } + + // Step 3: Open the arduino project at a new vscode window. + vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(destExample), true); + } + return destExample; + } + + public get settings() { + return this._settings; + } + + public get boardManager() { + return this._boardManager; + } + + public set boardManager(value: BoardManager) { + this._boardManager = value; + } + + public get libraryManager() { + return this._libraryManager; + } + + public set libraryManager(value: LibraryManager) { + this._libraryManager = value; + } + + public get exampleManager() { + return this._exampleManager; + } + + public set exampleManager(value: ExampleManager) { + this._exampleManager = value; + } + + public get programmerManager() { + return this._programmerManager; + } + + public set programmerManager(value: ProgrammerManager) { + this._programmerManager = value; + } + + private getProgrammerString(): string { + const selectProgrammer = this.programmerManager.currentProgrammer; + if (!selectProgrammer) { + Logger.notifyUserError("getProgrammerString", new Error(constants.messages.NO_PROGRAMMMER_SELECTED)); + return; + } + return selectProgrammer; + } + + private getBoardBuildString(): string { + const selectedBoard = this.boardManager.currentBoard; + if (!selectedBoard) { + Logger.notifyUserError("getBoardBuildString", new Error(constants.messages.NO_BOARD_SELECTED)); + return; + } + return selectedBoard.getBuildConfig(); + } + + private async getMainSketch(dc: DeviceContext) { + await dc.resolveMainSketch(); + if (!dc.sketch) { + vscode.window.showErrorMessage("No sketch file was found. Please specify the sketch in the arduino.json file"); + throw new Error("No sketch file was found."); + } + } +} diff --git a/src/arduino/arduinoSettings.ts b/src/arduino/arduinoSettings.ts index a8d77b98..bee7ccbd 100644 --- a/src/arduino/arduinoSettings.ts +++ b/src/arduino/arduinoSettings.ts @@ -239,8 +239,7 @@ export class ArduinoSettings implements IArduinoSettings { private async tryGetDefaultOutputPath(): Promise { const configValue = VscodeSettings.getInstance().defaultOutputPath; if (!configValue || !configValue.trim()) { - // not set - // return ""? + this._defaultOutputPath = undefined; } else { this._defaultOutputPath = configValue; }