diff --git a/html/app/actions/api.ts b/html/app/actions/api.ts index b3637d04..e9b4223a 100644 --- a/html/app/actions/api.ts +++ b/html/app/actions/api.ts @@ -14,8 +14,8 @@ function postHTTP(url, postData) { return window.fetch(request); } -export function getBoardPackages() { - return window.fetch("/api/boardpackages").then((response) => response.json()); +export function getBoardPackages(update) { + return window.fetch(`/api/boardpackages?update=${update}`).then((response) => response.json()); } export function installBoard(packageName, arch, version) { @@ -39,8 +39,8 @@ export function openLink(link) { }).then((response) => response.json()); } -export function getLibraries() { - return window.fetch("/api/libraries").then((response) => response.json()); +export function getLibraries(update) { + return window.fetch(`/api/libraries?update=${update}`).then((response) => response.json()); } export function installLibrary(libraryName, version) { diff --git a/html/app/actions/index.ts b/html/app/actions/index.ts index af633145..2e1a4f12 100644 --- a/html/app/actions/index.ts +++ b/html/app/actions/index.ts @@ -149,9 +149,9 @@ export function uninstallLibraryFailure(libraryName, errorMessage) { }; } -export function getBoardPackages(dispatch) { +export function getBoardPackages(dispatch, update: boolean) { dispatch(boardPackagesRequest()); - API.getBoardPackages().then((response) => { + API.getBoardPackages(update).then((response) => { const { platforms } = response; dispatch(boardPackagesSuccess(JSONHelper.retrocycle(platforms))); }).catch((error) => { @@ -183,9 +183,9 @@ export function uninstallBoard(dispatch, boardName, packagePath, callback?: Func }); } -export function getLibraries(dispatch, callback?: Function) { +export function getLibraries(dispatch, update: boolean, callback?: Function) { dispatch(librariesRequest()); - API.getLibraries().then((response) => { + API.getLibraries(update).then((response) => { const { libraries } = response; dispatch(librariesSuccess(libraries)); if (callback) { diff --git a/html/app/components/BoardManager.tsx b/html/app/components/BoardManager.tsx index 755d25ef..ca7d118c 100644 --- a/html/app/components/BoardManager.tsx +++ b/html/app/components/BoardManager.tsx @@ -45,12 +45,12 @@ const mapStateToProps = (state) => { const mapDispatchToProps = (dispatch) => { return { - loadBoardPackages: () => actions.getBoardPackages(dispatch), + loadBoardPackages: () => actions.getBoardPackages(dispatch, true), installBoard: (boardName, packageName, arch, version) => actions.installBoard(dispatch, boardName, packageName, arch, version, () => { - actions.getBoardPackages(dispatch); + actions.getBoardPackages(dispatch, false); }), uninstallBoard: (boardName, packagePath) => actions.uninstallBoard(dispatch, boardName, packagePath, () => { - actions.getBoardPackages(dispatch); + actions.getBoardPackages(dispatch, false); }), }; }; diff --git a/html/app/components/LibraryManager.tsx b/html/app/components/LibraryManager.tsx index 72b2e71b..6fff40d9 100644 --- a/html/app/components/LibraryManager.tsx +++ b/html/app/components/LibraryManager.tsx @@ -45,11 +45,11 @@ const mapStateToProps = (store) => { const mapDispatchToProps = (dispatch) => { return { - loadLibraries: () => actions.getLibraries(dispatch), + loadLibraries: () => actions.getLibraries(dispatch, true), installLibrary: (libraryName, version, callback) => actions.installLibrary(dispatch, libraryName, version, (error) => { if (!error) { // Refresh library manager view - actions.getLibraries(dispatch, callback); + actions.getLibraries(dispatch, false, callback); } else { callback(); } @@ -57,7 +57,7 @@ const mapDispatchToProps = (dispatch) => { uninstallLibrary: (libraryName, libraryPath, callback) => actions.uninstallLibrary(dispatch, libraryName, libraryPath, (error) => { if (!error) { // Refresh library manager view - actions.getLibraries(dispatch, callback); + actions.getLibraries(dispatch, false, callback); } else { callback(); } diff --git a/package.json b/package.json index ffd2c239..ca5fc41d 100644 --- a/package.json +++ b/package.json @@ -126,6 +126,11 @@ "info", "verbose" ] + }, + "arduino.autoUpdateIndexFiles": { + "type": "boolean", + "default": false, + "description": "Controls auto update of package_index.json and library_index.json index files. If enabled, each time when you open Boards Manager/Libraries Manager, download latest index files first. Otherwise, using index files cached on local disk for Boards Manager/Libraries Manager." } } }, diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 0db52af1..0f74c62b 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -41,7 +41,7 @@ export class ArduinoApp { 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", "dummy", "", false); + await this.installBoard("dummy", "", "", true); } catch (ex) { } } @@ -49,12 +49,13 @@ export class ArduinoApp { /** * Initialize the arduino library. + * @param {boolean} force - Whether force refresh library index file */ - public async initializeLibrary() { - if (!util.fileExistsSync(path.join(this._settings.packagePath, "library_index.json"))) { + 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", "", false); + await this.installLibrary("dummy", "", true); } catch (ex) { } } @@ -177,17 +178,35 @@ export class ArduinoApp { /** * Install arduino board package based on package name and platform hardware architecture. */ - public async installBoard(packageName: string, arch: string, version: string = "", showOutput: boolean = true) { + public async installBoard(packageName: string, arch: string = "", version: string = "", showOutput: boolean = true) { arduinoChannel.show(); - arduinoChannel.start(`Install package - ${packageName}...`); + const updatingIndex = packageName === "dummy" && !arch && !version; + if (updatingIndex) { + arduinoChannel.start(`Update package index files...`); + } else { + arduinoChannel.start(`Install package - ${packageName}...`); + } try { await util.spawn(this._settings.commandPath, showOutput ? arduinoChannel.channel : null, - ["--install-boards", `${packageName}:${arch}${version && ":" + version}`]); + ["--install-boards", `${packageName}${arch && ":" + arch}${version && ":" + version}`]); - arduinoChannel.end(`Installed board package - ${packageName}${os.EOL}`); + if (updatingIndex) { + arduinoChannel.end("Updated package index files."); + } else { + arduinoChannel.end(`Installed board package - ${packageName}${os.EOL}`); + } } catch (error) { - arduinoChannel.error(`Exit with code=${error.code}${os.EOL}`); + // 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}`); + } } } @@ -199,15 +218,33 @@ export class ArduinoApp { public async installLibrary(libName: string, version: string = "", showOutput: boolean = true) { arduinoChannel.show(); - arduinoChannel.start(`Install library - ${libName}`); + 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}`]); - arduinoChannel.end(`Installed library - ${libName}${os.EOL}`); + if (updatingIndex) { + arduinoChannel.end("Updated library index files."); + } else { + arduinoChannel.end(`Installed library - ${libName}${os.EOL}`); + } } catch (error) { - arduinoChannel.error(`Exit with code=${error.code}${os.EOL}`); + // 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}`); + } } } diff --git a/src/arduino/arduinoContentProvider.ts b/src/arduino/arduinoContentProvider.ts index b6dbf326..2ba27afd 100644 --- a/src/arduino/arduinoContentProvider.ts +++ b/src/arduino/arduinoContentProvider.ts @@ -11,12 +11,14 @@ import { ArduinoApp } from "./arduino"; import { BoardManager } from "./boardManager"; import { LibraryManager } from "./libraryManager"; import LocalWebServer from "./localWebServer"; +import { IArduinoSettings } from "./settings"; export class ArduinoContentProvider implements vscode.TextDocumentContentProvider { private _webserver: LocalWebServer; private _onDidChange = new vscode.EventEmitter(); constructor( + private _settings: IArduinoSettings, private _arduinoApp: ArduinoApp, private _boardManager: BoardManager, private _libraryManager: LibraryManager, @@ -86,7 +88,8 @@ export class ArduinoContentProvider implements vscode.TextDocumentContentProvide } public async getBoardPackages(req, res) { - await this._boardManager.loadPackages(); + const update = (this._settings.autoUpdateIndexFiles && req.query.update === "true"); + await this._boardManager.loadPackages(update); return res.json({ platforms: JSONHelper.decycle(this._boardManager.platforms, undefined), }); @@ -142,7 +145,8 @@ export class ArduinoContentProvider implements vscode.TextDocumentContentProvide } public async getLibraries(req, res) { - await this._libraryManager.loadLibraries(); + const update = (this._settings.autoUpdateIndexFiles && req.query.update === "true"); + await this._libraryManager.loadLibraries(update); return res.json({ libraries: this._libraryManager.libraries, }); diff --git a/src/arduino/boardManager.ts b/src/arduino/boardManager.ts index 14e3c2d2..b0bc4879 100644 --- a/src/arduino/boardManager.ts +++ b/src/arduino/boardManager.ts @@ -194,16 +194,21 @@ export class BoardManager { this._boardStatusBar.tooltip = "Change Board Type"; } - public async loadPackages() { + public async loadPackages(update: boolean = false) { this._packages = []; this._platforms = []; + this._installedPlatforms = []; - let rootPackgeFolder = this._settings.packagePath; - let indexFiles = ["package_index.json"]; - let preferences = this._arduinoApp.preferences; - indexFiles = indexFiles.concat(this.getAddtionalIndexFiles()); + if (update) { // Update index files. + await this._arduinoApp.setPref("boardsmanager.additional.urls", this.getAdditionalUrls().join(",")); + await this._arduinoApp.initialize(true); + } + + // Parse package index files. + const indexFiles = ["package_index.json"].concat(this.getAddtionalIndexFiles()); + const rootPackgeFolder = this._settings.packagePath; for (let indexFile of indexFiles) { - if (!util.fileExistsSync(path.join(rootPackgeFolder, indexFile))) { + if (!update && !util.fileExistsSync(path.join(rootPackgeFolder, indexFile))) { await this._arduinoApp.setPref("boardsmanager.additional.urls", this.getAdditionalUrls().join(",")); await this._arduinoApp.initialize(true); } @@ -211,9 +216,10 @@ export class BoardManager { this.parsePackageIndex(JSON.parse(packageContent)); } - this.loadDefaultPlatforms(); + // Load default platforms from arduino installation directory and user manually installed platforms. this.loadInstalledPlatforms(); + // Load all supported boards type. this.loadInstalledBoards(); this.updateStatusBar(); this._boardStatusBar.show(); @@ -256,10 +262,6 @@ export class BoardManager { return this._platforms; } - public get installedPlatforms(): IPlatform[] { - return this._installedPlatforms; - } - public get installedBoards(): Map { return this._boards; } @@ -268,6 +270,25 @@ export class BoardManager { return this._currentBoard; } + public getInstalledPlatforms(): any[] { + // Always using manually installed platforms to overwrite the same platform from arduino installation directory. + const installedPlatforms = this.getDefaultPlatforms(); + const manuallyInstalled = this.getManuallyInstalledPlatforms(); + manuallyInstalled.forEach((plat) => { + const find = installedPlatforms.find((_plat) => { + return _plat.packageName === plat.packageName && _plat.architecture === plat.architecture; + }); + if (!find) { + installedPlatforms.push(plat); + } else { + find.defaultPlatform = plat.defaultPlatform; + find.version = plat.version; + find.rootBoardPath = plat.rootBoardPath; + } + }); + return installedPlatforms; + } + private updateStatusBar(): void { const dc = DeviceContext.getIntance(); let selectedBoard = this._boards.get(dc.board); @@ -279,45 +300,6 @@ export class BoardManager { } } - private loadDefaultPlatforms() { - // Default arduino package information: - const packageName = "arduino"; - const archName = "avr"; - try { - let packageBundled = fs.readFileSync(path.join(this._settings.defaultPackagePath, "package_index_bundled.json"), "utf8"); - if (!packageBundled) { - return; - } - let bundledObject = JSON.parse(packageBundled); - if (bundledObject && bundledObject.packages && bundledObject.packages.length && bundledObject.packages[0].platforms) { - let platforms = bundledObject.packages[0].platforms; - if (platforms && platforms.length && platforms.length > 0) { - const v = platforms[0].version; - if (v) { - let filteredPlat = this._platforms.find((_plat) => _plat.package.name === packageName && _plat.architecture === archName); - if (!filteredPlat) { - return; - } - filteredPlat.defaultPlatform = true; - if (filteredPlat.installedVersion) { - let installedPlat = this.installedPlatforms - .find((_plat) => _plat.package.name === packageName && _plat.architecture === archName); - if (installedPlat) { - installedPlat.defaultPlatform = true; - } - return; - } else { - filteredPlat.installedVersion = v; - filteredPlat.rootBoardPath = path.join(this._settings.defaultPackagePath, "arduino", "avr"); - this.installedPlatforms.push(filteredPlat); - } - } - } - } - } catch (ex) { - } - } - private parsePackageIndex(rawModel: any): void { this._packages.concat(rawModel.packages); @@ -342,38 +324,85 @@ export class BoardManager { }); } - private loadInstalledPlatforms(): void { - this._installedPlatforms = []; + private loadInstalledPlatforms() { + const installed = this.getInstalledPlatforms(); + installed.forEach((platform) => { + let existingPlatform = this._platforms.find((_plat) => { + return _plat.package.name === platform.packageName && _plat.architecture === platform.architecture; + }); + if (existingPlatform) { + existingPlatform.defaultPlatform = platform.defaultPlatform; + if (!existingPlatform.installedVersion) { + existingPlatform.installedVersion = platform.version; + existingPlatform.rootBoardPath = platform.rootBoardPath; + this._installedPlatforms.push(existingPlatform); + } + } + }); + } + + // Default arduino package information from arduino installation directory. + private getDefaultPlatforms(): any[] { + const defaultPlatforms = []; + try { + let packageBundled = fs.readFileSync(path.join(this._settings.defaultPackagePath, "package_index_bundled.json"), "utf8"); + if (!packageBundled) { + return defaultPlatforms; + } + let bundledObject = JSON.parse(packageBundled); + if (bundledObject && bundledObject.packages) { + for (let pkg of bundledObject.packages) { + for (let platform of pkg.platforms) { + if (platform.version) { + defaultPlatforms.push({ + packageName: pkg.name, + architecture: platform.architecture, + version: platform.version, + rootBoardPath: path.join(this._settings.defaultPackagePath, pkg.name, platform.architecture), + defaultPlatform: true, + }); + } + } + } + } + } catch (ex) { + } + return defaultPlatforms; + } + + // User manually installed packages. + private getManuallyInstalledPlatforms(): any[] { + const manuallyInstalled = []; let rootPackagePath = path.join(path.join(this._settings.packagePath, "packages")); if (!util.directoryExistsSync(rootPackagePath)) { - return; + return manuallyInstalled; } const dirs = util.filterJunk(util.readdirSync(rootPackagePath, true)); // in Mac, filter .DS_Store file. - dirs.forEach((packageName) => { + for (let packageName of dirs) { let archPath = path.join(this._settings.packagePath, "packages", packageName, "hardware"); if (!util.directoryExistsSync(archPath)) { - return; + continue; } let architectures = util.filterJunk(fs.readdirSync(archPath)); - if (!architectures || !architectures.length) { - return; - } architectures.forEach((architecture) => { let allVersion = util.filterJunk(fs.readdirSync(path.join(archPath, architecture))); - let existingPlatform = this._platforms.find((_plat) => _plat.package.name === packageName && _plat.architecture === architecture); - if (existingPlatform && allVersion && allVersion.length) { - existingPlatform.defaultPlatform = false; - existingPlatform.installedVersion = allVersion[0]; - existingPlatform.rootBoardPath = path.join(archPath, architecture, allVersion[0]); - this._installedPlatforms.push(existingPlatform); + if (allVersion && allVersion.length) { + manuallyInstalled.push({ + packageName, + architecture, + version: allVersion[0], + rootBoardPath: path.join(archPath, architecture, allVersion[0]), + defaultPlatform: false, + }); } }); - }); + } + return manuallyInstalled; } private loadInstalledBoards(): void { this._boards = new Map(); - this.installedPlatforms.forEach((plat) => { + this._installedPlatforms.forEach((plat) => { let dir = plat.rootBoardPath; if (util.fileExistsSync(path.join(plat.rootBoardPath, "boards.txt"))) { let boardContent = fs.readFileSync(path.join(plat.rootBoardPath, "boards.txt"), "utf8"); diff --git a/src/arduino/libraryManager.ts b/src/arduino/libraryManager.ts index d6f07f71..3e5c700f 100644 --- a/src/arduino/libraryManager.ts +++ b/src/arduino/libraryManager.ts @@ -43,17 +43,16 @@ export class LibraryManager { return this._libraries; } - public async loadLibraries() { + public async loadLibraries(update: boolean = false) { this._libraryMap = new Map(); this._libraries = []; - await this._arduinoApp.boardManager.loadPackages(); - - // Parse libraries index file "library_index.json" let libraryIndexFilePath = path.join(this._settings.packagePath, "library_index.json"); - if (!util.fileExistsSync(libraryIndexFilePath)) { - await this._arduinoApp.initializeLibrary(); + if (update || !util.fileExistsSync(libraryIndexFilePath)) { + await this._arduinoApp.initializeLibrary(true); } + + // Parse libraries index file "library_index.json" let packageContent = fs.readFileSync(libraryIndexFilePath, "utf8"); this.parseLibraryIndex(JSON.parse(packageContent)); @@ -114,11 +113,10 @@ export class LibraryManager { private async loadBoardLibraries() { let builtinLibs = []; const librarySet = new Set(this._libraryMap.keys()); - for (let board of this._arduinoApp.boardManager.platforms) { - if (board.installedVersion) { - const libs = await this.parseBoardLibraries(board.rootBoardPath, board.architecture, librarySet); - builtinLibs = builtinLibs.concat(libs); - } + const installedPlatforms = this._arduinoApp.boardManager.getInstalledPlatforms(); + for (let board of installedPlatforms) { + const libs = await this.parseBoardLibraries(board.rootBoardPath, board.architecture, librarySet); + builtinLibs = builtinLibs.concat(libs); } return builtinLibs; } diff --git a/src/arduino/settings.ts b/src/arduino/settings.ts index a7a1bbd6..056a72fb 100644 --- a/src/arduino/settings.ts +++ b/src/arduino/settings.ts @@ -17,6 +17,7 @@ import { resolveArduinoPath, validateArduinoPath } from "../common/platform"; export interface IArduinoSettings { arduinoPath: string; additionalUrls: string | string[]; + autoUpdateIndexFiles: boolean; logLevel: string; commandPath: string; packagePath: string; @@ -116,6 +117,11 @@ export class ArduinoSettings implements IArduinoSettings { } } + public get autoUpdateIndexFiles(): boolean { + const workspaceConfig = vscode.workspace.getConfiguration(); + return workspaceConfig.get("arduino.autoUpdateIndexFiles"); + } + public get logLevel(): string { const workspaceConfig = vscode.workspace.getConfiguration(); return workspaceConfig.get("arduino.logLevel") || "info"; diff --git a/src/extension.ts b/src/extension.ts index d35f0260..e072d9f0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -38,7 +38,7 @@ export async function activate(context: vscode.ExtensionContext) { await boardManager.loadPackages(); const libraryManager = new LibraryManager(arduinoSettings, arduinoApp); - const arduinoManagerProvider = new ArduinoContentProvider(arduinoApp, boardManager, libraryManager, context.extensionPath); + const arduinoManagerProvider = new ArduinoContentProvider(arduinoSettings, arduinoApp, boardManager, libraryManager, context.extensionPath); context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(ARDUINO_MANAGER_PROTOCOL, arduinoManagerProvider)); let registerCommand = (command: string, commandBody: (...args: any[]) => any, getUserData?: () => any): vscode.Disposable => {