diff --git a/package-lock.json b/package-lock.json index 04be017c..841fcefa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vscode-arduino", - "version": "0.2.24", + "version": "0.2.25", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -41,6 +41,15 @@ "integrity": "sha512-HNXtUQQuW3ThO9DVfTNbdvClVr+8AZlNNa2pxk5qtEvObnT27qh0DdQXTN4h5PuTTGinxwXkRKXsllJxuAzGPw==", "dev": true }, + "@types/fs-extra": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.4.tgz", + "integrity": "sha512-DsknoBvD8s+RFfSGjmERJ7ZOP1HI0UZRA3FSI+Zakhrc/Gy26YQsLI+m5V5DHxroHRJqCDLKJp7Hixn8zyaF7g==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/mocha": { "version": "2.2.48", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.48.tgz", @@ -2630,6 +2639,16 @@ "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "fs-mkdirp-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", @@ -3550,8 +3569,7 @@ "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" }, "growl": { "version": "1.10.3", @@ -4960,6 +4978,14 @@ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -7892,6 +7918,11 @@ "crypto-random-string": "^1.0.0" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index e5330266..c968b4d8 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "activationEvents": [ "*", "onCommand:arduino.verify", + "onCommand:arduino.exportCompiledBinary", "onCommand:arduino.upload", "onCommand:arduino.uploadUsingProgrammer", "onCommand:arduino.selectProgrammer", @@ -77,6 +78,10 @@ "light": "images/ArduinoVerify_16.svg" } }, + { + "command": "arduino.exportCompiledBinary", + "title": "Arduino: Export Compiled Binary" + }, { "command": "arduino.upload", "title": "Arduino: Upload", @@ -538,6 +543,7 @@ ], "devDependencies": { "@types/compare-versions": "^3.0.0", + "@types/fs-extra": "^5.0.4", "@types/mocha": "^2.2.32", "@types/node": "^6.0.40", "@types/winreg": "^1.2.30", @@ -568,6 +574,7 @@ "compare-versions": "^3.4.0", "eventemitter2": "^4.1.0", "express": "^4.14.1", + "fs-extra": "^7.0.1", "glob": "^7.1.1", "iconv-lite": "^0.4.18", "properties": "^1.2.1", diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 0e10f505..dd360c73 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -2,9 +2,11 @@ // Licensed under the MIT license. import * as fs from "fs"; +import * as fs_extra from "fs-extra"; // TODO: Remove to keep only standard fs when upgrading to Node 8+ import * as glob from "glob"; import * as os from "os"; import * as path from "path"; +import * as uuidv4 from "uuid/v4"; import * as vscode from "vscode"; import * as constants from "../common/constants"; @@ -239,7 +241,7 @@ export class ArduinoApp { }); } - public async verify(output: string = "") { + public async verify(output: string = "", exportBinary: boolean = false) { const dc = DeviceContext.getInstance(); const boardDescriptor = this.getBoardBuildString(); if (!boardDescriptor) { @@ -276,8 +278,10 @@ export class ArduinoApp { if (VscodeSettings.getInstance().logLevel === "verbose") { args.push("--verbose"); } + let outputPath; + let isTmp = false; if (output || dc.output) { - const outputPath = path.resolve(ArduinoWorkspace.rootPath, output || dc.output); + 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)); @@ -289,6 +293,12 @@ export class ArduinoApp { } else { const msg = "Output path is not specified. Unable to reuse previously compiled files. Verify could be slow. See README."; arduinoChannel.warning(msg); + if (exportBinary) { + const uuid = uuidv4(); + outputPath = path.join(os.tmpdir(), uuid); + args.push("--pref", `build.path=${outputPath}`); + isTmp = true; + } } arduinoChannel.show(); @@ -296,6 +306,34 @@ export class ArduinoApp { try { await util.spawn(this._settings.commandPath, arduinoChannel.channel, args); arduinoChannel.end(`Finished verify sketch - ${dc.sketch}${os.EOL}`); + if (exportBinary) { + const options = { + nodir: true, + realpath: true, + absolute: true, + }; + glob(path.join(outputPath, "/*.{hex,bin,elf}"), options, (err, files: string[]) => { + if (err) { + arduinoChannel.warning(`Couldn't find binary files, glob returned ${err}`); + } else { + files.forEach(async (bin) => { + // TODO: Replace fs_extra.copy by fs.copyFile when upgrading to Node 8+ + await fs_extra.copy(bin, path.join(ArduinoWorkspace.rootPath, path.basename(bin)), (copyError: Error) => { + if (copyError) { + arduinoChannel.warning(`Couldn't copy binary file ${bin}, copy returned ${copyError}`); + } + }); + }); + if (isTmp) { + fs_extra.remove(outputPath, (removeError: Error) => { + if (removeError) { + arduinoChannel.warning(`Couldn't remove temporary build directory ${outputPath}, rm returned ${removeError}`); + } + }); + } + } + }); + } return true; } catch (reason) { arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); @@ -304,6 +342,10 @@ export class ArduinoApp { } + public async exportCompiledBinary(output: string = "") { + return this.verify(output, true); + } + // Add selected library path to the intellisense search path. public addLibPath(libraryPath: string) { let libPaths; diff --git a/src/extension.ts b/src/extension.ts index c4c6fe52..eedb9e5c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -185,6 +185,24 @@ export async function activate(context: vscode.ExtensionContext) { return { board: (ArduinoContext.boardManager.currentBoard === null) ? null : ArduinoContext.boardManager.currentBoard.name }; }); + registerArduinoCommand("arduino.exportCompiledBinary", async () => { + if (!status.compile) { + status.compile = "verify"; + try { + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: "Arduino: Exporting Compiled Binary...", + }, async () => { + await ArduinoContext.arduinoApp.exportCompiledBinary(); + }); + } catch (ex) { + } + delete status.compile; + } + }, () => { + return { board: (ArduinoContext.boardManager.currentBoard === null) ? null : ArduinoContext.boardManager.currentBoard.name }; + }); + registerArduinoCommand("arduino.upload", async () => { if (!status.compile) { status.compile = "upload"; diff --git a/test/commands.test.ts b/test/commands.test.ts index c188dbb5..b8ad7fac 100644 --- a/test/commands.test.ts +++ b/test/commands.test.ts @@ -113,4 +113,19 @@ suite("Arduino: Commands Tests", () => { } }); + // tslint:disable-next-line: only-arrow-functions + test("should be able to run command: arduino exportCompiledBinary", function(done) { + // Same timeout as verify, being the longest part of the command + this.timeout(3 * 60 * 1000); + try { + // run "Arduino: Export Compiled Binary" command. + vscode.commands.executeCommand("arduino.exportCompiledBinary").then((result) => { + done(); + }); + + } catch (error) { + done(new Error(error)); + } + }); + }); diff --git a/test/extension.test.ts b/test/extension.test.ts index 1309bbdc..79b9ee0f 100644 --- a/test/extension.test.ts +++ b/test/extension.test.ts @@ -33,6 +33,7 @@ suite("Arduino: Extension Tests", () => { return vscode.commands.getCommands(true).then((commands) => { const ARDUINO_COMMANDS = [ "arduino.verify", + "arduino.exportCompiledBinary", "arduino.upload", "arduino.uploadUsingProgrammer", "arduino.selectProgrammer",