diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index 142c2a456..4a98bed63 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -4,9 +4,10 @@ "description": "An extension for Theia building the Arduino IDE", "license": "AGPL-3.0-or-later", "scripts": { - "prepare": "yarn download-cli && yarn download-ls && yarn clean && yarn download-examples && yarn build", + "prepare": "yarn download-cli && yarn download-fwuploader && yarn download-ls && yarn clean && yarn download-examples && yarn build", "clean": "rimraf lib", "download-cli": "node ./scripts/download-cli.js", + "download-fwuploader": "node ./scripts/download-fwuploader.js", "download-ls": "node ./scripts/download-ls.js", "download-examples": "node ./scripts/download-examples.js", "generate-protocol": "node ./scripts/generate-protocol.js", @@ -137,6 +138,9 @@ "arduino": { "cli": { "version": "0.18.3" + }, + "fwuploader": { + "version": "1.0.2" } } } diff --git a/arduino-ide-extension/scripts/download-fwuploader.js b/arduino-ide-extension/scripts/download-fwuploader.js new file mode 100755 index 000000000..4c13b628a --- /dev/null +++ b/arduino-ide-extension/scripts/download-fwuploader.js @@ -0,0 +1,166 @@ +// @ts-check + +(async () => { + const fs = require('fs'); + const path = require('path'); + const temp = require('temp'); + const shell = require('shelljs'); + const semver = require('semver'); + const downloader = require('./downloader'); + + const version = (() => { + const pkg = require(path.join(__dirname, '..', 'package.json')); + if (!pkg) { + return undefined; + } + + const { arduino } = pkg; + if (!arduino) { + return undefined; + } + + const { fwuploader } = arduino; + if (!fwuploader) { + return undefined; + } + + const { version } = fwuploader; + return version; + })(); + + if (!version) { + shell.echo( + `Could not retrieve Firmware Uploader version info from the 'package.json'.` + ); + shell.exit(1); + } + + const { platform, arch } = process; + const buildFolder = path.join(__dirname, '..', 'build'); + const fwuploderName = `arduino-fwuploader${ + platform === 'win32' ? '.exe' : '' + }`; + const destinationPath = path.join(buildFolder, fwuploderName); + + if (typeof version === 'string') { + const suffix = (() => { + switch (platform) { + case 'darwin': + return 'macOS_64bit.tar.gz'; + case 'win32': + return 'Windows_64bit.zip'; + case 'linux': { + switch (arch) { + case 'arm': + return 'Linux_ARMv7.tar.gz'; + case 'arm64': + return 'Linux_ARM64.tar.gz'; + case 'x64': + return 'Linux_64bit.tar.gz'; + default: + return undefined; + } + } + default: + return undefined; + } + })(); + if (!suffix) { + shell.echo( + `The Firmware Uploader is not available for ${platform} ${arch}.` + ); + shell.exit(1); + } + if (semver.valid(version)) { + const url = `https://downloads.arduino.cc/arduino-fwuploader/arduino-fwuploader_${version}_${suffix}`; + shell.echo( + `📦 Identified released version of the Firmware Uploader. Downloading version ${version} from '${url}'` + ); + await downloader.downloadUnzipFile( + url, + destinationPath, + 'arduino-fwuploader' + ); + } else { + shell.echo(`🔥 Could not interpret 'version': ${version}`); + shell.exit(1); + } + } else { + // We assume an object with `owner`, `repo`, commitish?` properties. + const { owner, repo, commitish } = version; + if (!owner) { + shell.echo(`Could not retrieve 'owner' from ${JSON.stringify(version)}`); + shell.exit(1); + } + if (!repo) { + shell.echo(`Could not retrieve 'repo' from ${JSON.stringify(version)}`); + shell.exit(1); + } + const url = `https://github.com/${owner}/${repo}.git`; + shell.echo( + `Building Firmware Uploader from ${url}. Commitish: ${ + commitish ? commitish : 'HEAD' + }` + ); + + if (fs.existsSync(destinationPath)) { + shell.echo( + `Skipping the Firmware Uploader build because it already exists: ${destinationPath}` + ); + return; + } + + if (shell.mkdir('-p', buildFolder).code !== 0) { + shell.echo('Could not create build folder.'); + shell.exit(1); + } + + const tempRepoPath = temp.mkdirSync(); + shell.echo(`>>> Cloning Firmware Uploader source to ${tempRepoPath}...`); + if (shell.exec(`git clone ${url} ${tempRepoPath}`).code !== 0) { + shell.exit(1); + } + shell.echo('<<< Cloned Firmware Uploader repo.'); + + if (commitish) { + shell.echo(`>>> Checking out ${commitish}...`); + if ( + shell.exec(`git -C ${tempRepoPath} checkout ${commitish}`).code !== 0 + ) { + shell.exit(1); + } + shell.echo(`<<< Checked out ${commitish}.`); + } + + shell.echo(`>>> Building the Firmware Uploader...`); + if (shell.exec('go build', { cwd: tempRepoPath }).code !== 0) { + shell.exit(1); + } + shell.echo('<<< Firmware Uploader build done.'); + + if (!fs.existsSync(path.join(tempRepoPath, fwuploderName))) { + shell.echo( + `Could not find the Firmware Uploader at ${path.join( + tempRepoPath, + fwuploderName + )}.` + ); + shell.exit(1); + } + + const builtFwUploaderPath = path.join(tempRepoPath, fwuploderName); + shell.echo( + `>>> Copying Firmware Uploader from ${builtFwUploaderPath} to ${destinationPath}...` + ); + if (shell.cp(builtFwUploaderPath, destinationPath).code !== 0) { + shell.exit(1); + } + shell.echo(`<<< Copied the Firmware Uploader.`); + + shell.echo('<<< Verifying Firmware Uploader...'); + if (!fs.existsSync(destinationPath)) { + shell.exit(1); + } + shell.echo('>>> Verified Firmware Uploader.'); + } +})(); diff --git a/arduino-ide-extension/src/node/executable-service-impl.ts b/arduino-ide-extension/src/node/executable-service-impl.ts index a136ed8ff..8ebede450 100644 --- a/arduino-ide-extension/src/node/executable-service-impl.ts +++ b/arduino-ide-extension/src/node/executable-service-impl.ts @@ -13,16 +13,19 @@ export class ExecutableServiceImpl implements ExecutableService { clangdUri: string; cliUri: string; lsUri: string; + fwuploaderUri: string; }> { - const [ls, clangd, cli] = await Promise.all([ + const [ls, clangd, cli, fwuploader] = await Promise.all([ getExecPath('arduino-language-server', this.onError.bind(this)), getExecPath('clangd', this.onError.bind(this), undefined, true), getExecPath('arduino-cli', this.onError.bind(this)), + getExecPath('arduino-fwuploader', this.onError.bind(this)), ]); return { clangdUri: FileUri.create(clangd).toString(), cliUri: FileUri.create(cli).toString(), lsUri: FileUri.create(ls).toString(), + fwuploaderUri: FileUri.create(fwuploader).toString(), }; }