From d492efb59da5afa42dd26e8a5d7a99bdd21f56ee Mon Sep 17 00:00:00 2001 From: Jason Tranchida Date: Sat, 31 Oct 2020 23:32:34 -0700 Subject: [PATCH 1/3] Fix serial restore of serial monitor There were several cases in which serial monitor and USB detection would not be properly restored, including early outs and build failures. Move restoration to a finally clause to guarantee the restore path will be called. --- src/arduino/arduino.ts | 81 ++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 73619a7b..2f24d4b1 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -125,53 +125,56 @@ export class ArduinoApp { 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; + try { + 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]; - if (dc.port) { - args.push("--port", dc.port); - } - args.push(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; + const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); + const args = ["--upload", "--board", boardDescriptor]; + if (dc.port) { + args.push("--port", dc.port); + } + args.push(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 { - 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 () => { + 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 () => { + arduinoChannel.end(`Uploaded the sketch: ${dc.sketch}${os.EOL}`); + }, (reason) => { + arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); + }); + } finally { 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() { From 6b6d3999e3dd87b57c1467cf322573e25b69177f Mon Sep 17 00:00:00 2001 From: Jason Tranchida Date: Sat, 31 Oct 2020 23:48:00 -0700 Subject: [PATCH 2/3] Merge upload & uploadUsingProgrammer --- src/arduino/arduino.ts | 106 +++++++++++++---------------------------- src/extension.ts | 5 +- 2 files changed, 35 insertions(+), 76 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 2f24d4b1..af73a110 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -24,6 +24,11 @@ import { SerialMonitor } from "../serialmonitor/serialMonitor"; import { UsbDetector } from "../serialmonitor/usbDetector"; import { ProgrammerManager } from "./programmerManager"; +export enum BuildMode { + Upload = "Upload", + UploadUsingProgrammer = "Upload (w/programmer)", +}; + /** * Represent an Arduino application based on the official Arduino IDE. */ @@ -93,7 +98,7 @@ export class ArduinoApp { } } - public async upload() { + public async buildSketch(mode: BuildMode) { const dc = DeviceContext.getInstance(); const boardDescriptor = this.getBoardBuildString(); if (!boardDescriptor) { @@ -109,7 +114,7 @@ export class ArduinoApp { await this.getMainSketch(dc); } - if ((!dc.configuration || !/upload_method=[^=,]*st[^,]*link/i.test(dc.configuration)) && !dc.port) { + if (this.configRequiresSerialForUpload(mode) && !dc.port) { const choice = await vscode.window.showInformationMessage( "Serial port is not specified. Do you want to select a serial port for uploading?", "Yes", "No"); @@ -120,7 +125,7 @@ export class ArduinoApp { } arduinoChannel.show(); - arduinoChannel.start(`Upload sketch - ${dc.sketch}`); + arduinoChannel.start(`${mode} sketch - ${dc.sketch}`); const serialMonitor = SerialMonitor.getInstance(); @@ -146,6 +151,15 @@ export class ArduinoApp { if (dc.port) { args.push("--port", dc.port); } + + if (mode === BuildMode.UploadUsingProgrammer) { + const programmer = this.getProgrammerString(); + if (!programmer) { + return; + } + args.push("--useprogrammer", "--pref", "programmer=" + programmer); + } + args.push(appPath); if (VscodeSettings.getInstance().logLevel === "verbose") { args.push("--verbose"); @@ -165,7 +179,7 @@ export class ArduinoApp { arduinoChannel.warning(msg); } await util.spawn(this._settings.commandPath, arduinoChannel.channel, args).then(async () => { - arduinoChannel.end(`Uploaded the sketch: ${dc.sketch}${os.EOL}`); + arduinoChannel.end(`${mode} complete: ${dc.sketch}${os.EOL}`); }, (reason) => { arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); }); @@ -177,76 +191,6 @@ export class ArduinoApp { } } - 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) { - const choice = await vscode.window.showInformationMessage( - "Serial port is not specified. Do you want to select a serial port for uploading?", - "Yes", "No"); - if (choice === "Yes") { - vscode.commands.executeCommand("arduino.selectSerialPort"); - } - 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 { - 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(); @@ -763,4 +707,18 @@ Please make sure the folder is not occupied by other procedures .`); throw new Error("No sketch file was found."); } } + + private configRequiresSerialForUpload(mode: BuildMode): boolean { + const dc = DeviceContext.getInstance(); + + if (mode === BuildMode.UploadUsingProgrammer) { + // Currently all programmer configurations require a selected serial port + return true; + } + + // Certain boards allow configuration of the STLink programmers as part of the board + // config and use during the regular upload, rather than "Upload using programmer" + // https://github.com/microsoft/vscode-arduino/issues/595 + return (!dc.configuration || !/upload_method=[^=,]*st[^,]*link/i.test(dc.configuration)); + } } diff --git a/src/extension.ts b/src/extension.ts index c4112bd7..2cf1f0bc 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -27,6 +27,7 @@ const completionProviderModule = impor("./langService/completionProvider") as ty import * as Logger from "./logger/logger"; const nsatModule = impor("./nsat") as typeof import ("./nsat"); +import { BuildMode } from "./arduino/arduino"; import { SerialMonitor } from "./serialmonitor/serialMonitor"; const usbDetectorModule = impor("./serialmonitor/usbDetector") as typeof import ("./serialmonitor/usbDetector"); @@ -142,7 +143,7 @@ export async function activate(context: vscode.ExtensionContext) { location: vscode.ProgressLocation.Window, title: "Arduino: Uploading...", }, async () => { - await arduinoContextModule.default.arduinoApp.upload(); + await arduinoContextModule.default.arduinoApp.buildSketch(BuildMode.Upload); }); } catch (ex) { } @@ -177,7 +178,7 @@ export async function activate(context: vscode.ExtensionContext) { if (!status.compile) { status.compile = "upload"; try { - await arduinoContextModule.default.arduinoApp.uploadUsingProgrammer(); + await arduinoContextModule.default.arduinoApp.buildSketch(BuildMode.UploadUsingProgrammer); } catch (ex) { } delete status.compile; From 863a8fa4f86c7c673f914bea916d27f3d8cf0574 Mon Sep 17 00:00:00 2001 From: Jason Tranchida Date: Sun, 1 Nov 2020 00:05:45 -0700 Subject: [PATCH 3/3] Merge verify functionality into buildSketch Also reduce scope/duration that serial monitor is disabled to only include build, but not prebuild --- src/arduino/arduino.ts | 137 ++++++++++------------------- src/debug/configurationProvider.ts | 4 +- src/extension.ts | 2 +- 3 files changed, 50 insertions(+), 93 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index af73a110..865032d9 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -25,6 +25,7 @@ import { UsbDetector } from "../serialmonitor/usbDetector"; import { ProgrammerManager } from "./programmerManager"; export enum BuildMode { + Verify = "Verify", Upload = "Upload", UploadUsingProgrammer = "Upload (w/programmer)", }; @@ -98,7 +99,7 @@ export class ArduinoApp { } } - public async buildSketch(mode: BuildMode) { + public async buildSketch(mode: BuildMode, output: string = "") { const dc = DeviceContext.getInstance(); const boardDescriptor = this.getBoardBuildString(); if (!boardDescriptor) { @@ -129,25 +130,28 @@ export class ArduinoApp { const serialMonitor = SerialMonitor.getInstance(); - const needRestore = await serialMonitor.closeSerialMonitor(dc.port); - try { - 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; - } + 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 = []; + if (mode === BuildMode.Verify) { + args.push("--verify"); + } else { + args.push("--upload"); - const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); - const args = ["--upload", "--board", boardDescriptor]; if (dc.port) { args.push("--port", dc.port); } @@ -157,77 +161,19 @@ export class ArduinoApp { if (!programmer) { return; } - args.push("--useprogrammer", "--pref", "programmer=" + programmer); - } - args.push(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 { - 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 () => { - arduinoChannel.end(`${mode} complete: ${dc.sketch}${os.EOL}`); - }, (reason) => { - arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); - }); - } finally { - UsbDetector.getInstance().resumeListening(); - if (needRestore) { - await serialMonitor.openSerialMonitor(); + args.push("--useprogrammer", "--pref", "programmer=" + programmer); } } - } - public async verify(output: string = "") { - const dc = DeviceContext.getInstance(); - const boardDescriptor = this.getBoardBuildString(); - if (!boardDescriptor) { - return; - } + args.push("--board", boardDescriptor) - 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}`); + args.push(appPath); - 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); @@ -237,28 +183,37 @@ export class ArduinoApp { } 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."; + const msg = `Output path is not specified. Unable to reuse previously compiled files. ${mode} could be slow. See README.`; arduinoChannel.warning(msg); } - - arduinoChannel.show(); - // we need to return the result of verify + // we need to return the result of the process + let restoreSerial: boolean = false; try { + if (mode !== BuildMode.Verify) { + restoreSerial = await serialMonitor.closeSerialMonitor(dc.port); + UsbDetector.getInstance().pauseListening(); + } + await util.spawn(this._settings.commandPath, arduinoChannel.channel, args); - arduinoChannel.end(`Finished verify sketch - ${dc.sketch}${os.EOL}`); + arduinoChannel.end(`${mode} finished - ${dc.sketch}${os.EOL}`); return true; } catch (reason) { const msg = reason.code ? `Exit with code=${reason.code}${os.EOL}` : - reason.message ? - reason.message : - JSON.stringify(reason); + reason.message ? reason.message : JSON.stringify(reason); arduinoChannel.error(msg); return false; + } finally { + if (mode !== BuildMode.Verify) { + UsbDetector.getInstance().resumeListening(); + if (restoreSerial) { + await serialMonitor.openSerialMonitor(); + } + } } - } public tryToUpdateIncludePaths() { @@ -711,7 +666,9 @@ Please make sure the folder is not occupied by other procedures .`); private configRequiresSerialForUpload(mode: BuildMode): boolean { const dc = DeviceContext.getInstance(); - if (mode === BuildMode.UploadUsingProgrammer) { + if (mode === BuildMode.Verify) { + return false; + } else if (mode === BuildMode.UploadUsingProgrammer) { // Currently all programmer configurations require a selected serial port return true; } diff --git a/src/debug/configurationProvider.ts b/src/debug/configurationProvider.ts index c08546f2..614dc542 100644 --- a/src/debug/configurationProvider.ts +++ b/src/debug/configurationProvider.ts @@ -4,7 +4,7 @@ import * as path from "path"; import * as vscode from "vscode"; -import { ArduinoApp } from "../arduino/arduino"; +import { BuildMode } from "../arduino/arduino"; import ArduinoActivator from "../arduinoActivator"; import ArduinoContext from "../arduinoContext"; @@ -136,7 +136,7 @@ export class ArduinoDebugConfigurationProvider implements vscode.DebugConfigurat config.program = path.join(ArduinoWorkspace.rootPath, outputFolder, `${path.basename(dc.sketch)}.elf`); // always compile elf to make sure debug the right elf - if (!await ArduinoContext.arduinoApp.verify(outputFolder)) { + if (!await ArduinoContext.arduinoApp.buildSketch(BuildMode.Verify, outputFolder)) { vscode.window.showErrorMessage("Failure to verify the program, please check output for details."); return false; } diff --git a/src/extension.ts b/src/extension.ts index 2cf1f0bc..e999abae 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -122,7 +122,7 @@ export async function activate(context: vscode.ExtensionContext) { location: vscode.ProgressLocation.Window, title: "Arduino: Verifying...", }, async () => { - await arduinoContextModule.default.arduinoApp.verify(); + await arduinoContextModule.default.arduinoApp.buildSketch(BuildMode.Verify); }); } catch (ex) { }