diff --git a/i18n/rus/src/qbsbuildconfigurationmanager.i18n.json b/i18n/rus/src/qbsbuildconfigurationmanager.i18n.json index ceb2749..b4f25b4 100644 --- a/i18n/rus/src/qbsbuildconfigurationmanager.i18n.json +++ b/i18n/rus/src/qbsbuildconfigurationmanager.i18n.json @@ -1,4 +1,6 @@ { + "qbs.buildconfigurationmanager.nofspath.message": "Невозможно получить файл конфигураций сборки т.к. путь к нему не установлен в настройках расширения Qbs.", + "qbs.buildconfigurationmanager.noworkspace.message": "Невозможно получить файл конфигураций сборки т.к. нет ни одной открытой папки в рабочей области.", "qbs.buildconfigurationmanager.scan.select.description": "Сканировать доступные конфигурации сборки", "qbs.buildconfigurationmanager.scan.select.label": "[Сканировать конфигурации сборки]" } diff --git a/i18n/rus/src/qbsbuildsystem.i18n.json b/i18n/rus/src/qbsbuildsystem.i18n.json index 985c208..c05b1c6 100644 --- a/i18n/rus/src/qbsbuildsystem.i18n.json +++ b/i18n/rus/src/qbsbuildsystem.i18n.json @@ -5,18 +5,18 @@ "qbs.buildsystem.build.progress.failed.title": "Сборка проекта завершена с ошибками", "qbs.buildsystem.build.progress.title": "Сборка проекта", "qbs.buildsystem.build.started.message": "Сборка проекта...", - "qbs.buildsystem.compile.completed.message": "Сборка файла успешно завершена, затраченное время {0}", - "qbs.buildsystem.compile.failed.message": "Сборка файла завершена с ошибками, затраченное время {0}", - "qbs.buildsystem.compile.progress.completed.title": "Сборка файла успешно завершена", - "qbs.buildsystem.compile.progress.failed.title": "Сборка файла завершена с ошибками", - "qbs.buildsystem.compile.progress.title": "Сборка файла", - "qbs.buildsystem.compile.started.message": "Сборка файла...", "qbs.buildsystem.clean.completed.message": "Очистка проекта успешно завершена, затраченное время {0}", "qbs.buildsystem.clean.failed.message": "Очистка проекта завершена с ошибками, затраченное время {0}", "qbs.buildsystem.clean.progress.completed.title": "Очистка проекта успешно завершена", "qbs.buildsystem.clean.progress.failed.title": "Очистка проекта завершена с ошибками", "qbs.buildsystem.clean.progress.title": "Очистка проекта", "qbs.buildsystem.clean.started.message": "Очистка проекта...", + "qbs.buildsystem.compile.completed.message": "Сборка файла успешно завершена, затраченное время {0}", + "qbs.buildsystem.compile.failed.message": "Сборка файла завершена с ошибками, затраченное время {0}", + "qbs.buildsystem.compile.progress.completed.title": "Сборка файла успешно завершена", + "qbs.buildsystem.compile.progress.failed.title": "Сборка файла завершена с ошибками", + "qbs.buildsystem.compile.progress.title": "Сборка файла", + "qbs.buildsystem.compile.started.message": "Сборка файла...", "qbs.buildsystem.install.completed.message": "Установка проекта успешно завершена, затраченное время {0}", "qbs.buildsystem.install.failed.message": "Установка проекта завершена с ошибками, затраченное время {0}", "qbs.buildsystem.install.progress.completed.title": "Установка проекта успешно завершена", @@ -24,6 +24,7 @@ "qbs.buildsystem.install.progress.title": "Установка проекта", "qbs.buildsystem.install.started.message": "Установка проекта...", "qbs.buildsystem.noproject.message": "Не удалось запустить операцию т.к. проектне выбран.", + "qbs.buildsystem.nosourceroot.message": "невозможно получить путь к корневой директории исходников т.к. нет ни одной открытой папки в рабочей области.", "qbs.buildsystem.resolve.completed.message": "Сканирование проекта успешно завершено, затраченное время {0}", "qbs.buildsystem.resolve.failed.message": "Сканирование проекта завершено с ошибками, затраченное время {0}", "qbs.buildsystem.resolve.progress.completed.title": "Сканирование проекта успешно завершено", diff --git a/i18n/rus/src/qbslaunchconfigurationmanager.i18n.json b/i18n/rus/src/qbslaunchconfigurationmanager.i18n.json index 23e70bd..f3a335d 100644 --- a/i18n/rus/src/qbslaunchconfigurationmanager.i18n.json +++ b/i18n/rus/src/qbslaunchconfigurationmanager.i18n.json @@ -1,6 +1,8 @@ { "qbs.launchconfigurationmanager.auto.description": "Сыбрать автоматическую конфигурацию запуска", "qbs.launchconfigurationmanager.auto.placeholder": "Авто", + "qbs.launchconfigurationmanager.nofspath.message": "Невозможно получить файл конфигураций запуска т.к. путь к нему не установлен в настройках расширения Qbs.", + "qbs.launchconfigurationmanager.noworkspace.message": "Невозможно получить файл конфигураций запуска т.к. нет ни одной открытой папки в рабочей области.", "qbs.launchconfigurationmanager.scan.select.description": "Сканировать доступные конфигурации запуска", "qbs.launchconfigurationmanager.scan.select.label": "[Сканировать конфигурации запуска]", "qbs.launchconfigurationmanager.select.description": "Тип \"{0}\", запрос \"{1}\"" diff --git a/src/protocol/qbsprotocolresolverequest.ts b/src/protocol/qbsprotocolresolverequest.ts index b65f486..199c537 100644 --- a/src/protocol/qbsprotocolresolverequest.ts +++ b/src/protocol/qbsprotocolresolverequest.ts @@ -12,8 +12,7 @@ export class QbsProtocolResolveRequest extends QbsProtocolRequest { dryRun: boolean, errorHandlingMode: QbsProtocolErrorHandlingMode, forceProbeExecution: boolean, - logLevel: QbsProtocolLogLevel, - settingsDirectory: string) { + logLevel: QbsProtocolLogLevel) { super(); this.setBuildRoot(buildRoot); this.setDataMode(QbsProtocolDataMode.OnlyChanged); @@ -24,7 +23,6 @@ export class QbsProtocolResolveRequest extends QbsProtocolRequest { this.setLogLevel(logLevel); this.setModuleProperties(QbsProtocolModuleProperties.Exported); this.setOverrideBuildGraphData(true); - this.setSettingsDirectory(settingsDirectory); this.setType(QbsProtocolRequestType.Resolve); } } diff --git a/src/qbsbuildconfigurationmanager.ts b/src/qbsbuildconfigurationmanager.ts index d4bad8a..a7065ed 100644 --- a/src/qbsbuildconfigurationmanager.ts +++ b/src/qbsbuildconfigurationmanager.ts @@ -8,6 +8,7 @@ import { ensureFileCreated } from './qbsutils'; import { QbsBuildConfigurationData } from './datatypes/qbsbuildconfigurationdata'; import { QbsBuildConfigurationKey } from './datatypes/qbsbuildconfigurationkey'; import { QbsCommandKey } from './datatypes/qbscommandkey'; +import { QbsProjectManager } from './qbsprojectmanager'; import { QbsSettings } from './qbssettings'; const localize = nls.config({ messageFormat: nls.MessageFormat.file })(); @@ -19,6 +20,7 @@ export class QbsBuildConfigurationManager implements vscode.Disposable { private configurationSelected: vscode.EventEmitter = new vscode.EventEmitter(); private fsBuildConfigurationsWatcher?: chokidar.FSWatcher + private disposable?: vscode.Disposable; readonly onUpdated: vscode.Event = this.updated.event; readonly onConfigurationSelected: vscode.Event @@ -30,16 +32,10 @@ export class QbsBuildConfigurationManager implements vscode.Disposable { public constructor(context: vscode.ExtensionContext) { QbsBuildConfigurationManager.instance = this; - - // Create default build configurations file if this file not exists yet. - QbsBuildConfigurationManager.ensureConfigurationsCreated(); - - // Register the commands related to the profile manager. this.registerCommandsHandlers(context); - this.subscribeSettingsChanges(); } - public dispose(): void { this.fsBuildConfigurationsWatcher?.close(); } + public dispose(): void { this.stop(); } public getConfigurations(): QbsBuildConfigurationData[] { return this.configurations; } @@ -47,18 +43,15 @@ export class QbsBuildConfigurationManager implements vscode.Disposable { return this.getConfigurations().find(configuration => configuration.name === configurationName); } - private registerCommandsHandlers(context: vscode.ExtensionContext): void { - context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.ScanBuildConfigurations, - async () => { await this.scanConfigurations(); })); - context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.SelectBuildConfiguration, - async () => { await this.selectConfiguration(); })); - context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.EditBuildConfigurations, - async () => { await this.editConfigurations(); })); - } - - private subscribeSettingsChanges(): void { + public async start(): Promise { + console.log('Starting build configuration manager'); const subscribeBuildConfigurationsChanged = async () => { - const fsPath = QbsSettings.substituteFsPath(QbsSettings.getBuildConfigurationsFilePath()); + const fsProjectPath = QbsProjectManager.getInstance().getProject()?.getFsPath(); + if (!fsProjectPath) + return; + const fsPath = QbsBuildConfigurationManager.getFullBuildConfigurationsFilePath(fsProjectPath); + if (!fsPath) + return; console.log('Build configurations file name changed to: ' + fsPath); this.fsBuildConfigurationsWatcher = chokidar.watch(fsPath, { ignoreInitial: true }); this.fsBuildConfigurationsWatcher.on('change', () => { @@ -67,14 +60,37 @@ export class QbsBuildConfigurationManager implements vscode.Disposable { }); } - QbsSettings.observeSetting(QbsSettings.SettingKey.BuildConfigurationsFilePath, + this.disposable?.dispose(); + this.disposable = QbsSettings.observeSetting(QbsSettings.SettingKey.BuildConfigurationsFilePath, async () => subscribeBuildConfigurationsChanged()); - subscribeBuildConfigurationsChanged(); + await subscribeBuildConfigurationsChanged(); + } + + public async stop(): Promise { + console.log('Stopping build configuration manager'); + await this.fsBuildConfigurationsWatcher?.close(); + await this.disposable?.dispose(); + } + + public async restart(): Promise { + await this.stop(); + await this.start(); + } + + private registerCommandsHandlers(context: vscode.ExtensionContext): void { + context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.ScanBuildConfigurations, + async () => { await this.scanConfigurations(); })); + context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.SelectBuildConfiguration, + async () => { await this.selectConfiguration(); })); + context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.EditBuildConfigurations, + async () => { await QbsBuildConfigurationManager.editConfigurations(); })); } private async scanConfigurations(): Promise { + const fsPath = QbsBuildConfigurationManager.ensureConfigurationsCreated(); + if (!fsPath) + return; return new Promise((resolve) => { - const fsPath = QbsBuildConfigurationManager.ensureConfigurationsCreated(); console.log('Start reading build configurations from: ' + fsPath); fs.readFile(fsPath, (error, data) => { if (error) { @@ -126,16 +142,22 @@ export class QbsBuildConfigurationManager implements vscode.Disposable { this.configurationSelected.fire(chosen.configuration); } - private async editConfigurations(): Promise { + private static async editConfigurations(): Promise { // Create if not exists yet, and then show the `qbs-configurations.json` file. const fsPath = QbsBuildConfigurationManager.ensureConfigurationsCreated(); + if (!fsPath) + return; const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(fsPath)); await vscode.window.showTextDocument(doc); } - private static ensureConfigurationsCreated(): string { - let fsPath = QbsSettings.getBuildConfigurationsFilePath() - fsPath = QbsSettings.substituteFsPath(fsPath); + private static ensureConfigurationsCreated(): string | undefined { + const fsProjectPath = QbsProjectManager.getInstance().getProject()?.getFsPath(); + if (!fsProjectPath) + return; + const fsPath = this.getFullBuildConfigurationsFilePath(fsProjectPath); + if (!fsPath) + return; ensureFileCreated(fsPath, QbsBuildConfigurationManager.writeDefaultConfigurations); return fsPath; } @@ -173,4 +195,20 @@ export class QbsBuildConfigurationManager implements vscode.Disposable { ws.write(JSON.stringify(QbsBuildConfigurationManager.getDefaultConfigurations(), null, 4)); return true; } + + private static getFullBuildConfigurationsFilePath(fsProjectPath: string): string | undefined { + const sourceRoot = QbsSettings.getSourceRootDirectory(fsProjectPath); + if (!sourceRoot) { + vscode.window.showWarningMessage(localize('qbs.buildconfigurationmanager.noworkspace.message', + 'Unable get the build configurations file because no any workspace folder is open.')); + return; + } + const result = QbsSettings.getBuildConfigurationsFilePath(); + if (!result) { + vscode.window.showWarningMessage(localize('qbs.buildconfigurationmanager.nofspath.message', + 'Unable to get the build configurations file because its path is not set in Qbs extension settings.')); + return; + } + return QbsSettings.substituteSourceRoot(result, sourceRoot); + } } diff --git a/src/qbsbuildsystem.ts b/src/qbsbuildsystem.ts index ace95ce..0c11c3b 100644 --- a/src/qbsbuildsystem.ts +++ b/src/qbsbuildsystem.ts @@ -10,6 +10,7 @@ import { QbsCommandKey } from './datatypes/qbscommandkey'; import { QbsDiagnosticManager } from './diagnostic/qbsdiagnosticmanager'; import { QbsOutputLogger } from './qbsoutputlogger'; import { QbsProductNode } from './projectexplorer/qbsproductnode'; +import { QbsProject } from './qbsproject'; import { QbsProjectManager } from './qbsprojectmanager'; import { QbsProjectNode } from './projectexplorer/qbsprojectnode'; import { QbsProtocolBuildRequest } from './protocol/qbsprotocolbuildrequest'; @@ -143,20 +144,20 @@ export class QbsBuildSystem implements vscode.Disposable { private subscribeSessionMessages(): void { // Handle the command description messages from the Qbs session. - this.session.onCommandDescriptionReceived(async (response) => this.logMessageResponse(response)); + this.session.onCommandDescriptionReceived(async (response) => QbsBuildSystem.logMessageResponse(response)); // Handle the log/warning/error messages from the Qbs session. this.session.onLogMessageReceived(async (response) => { - this.logMessageResponse(response); - this.diagnoseQbsInformationMessages(response); + QbsBuildSystem.logMessageResponse(response); + QbsBuildSystem.diagnoseQbsInformationMessages(response); }); this.session.onWarningMessageReceived(async (response) => { - this.logMessageResponse(response); - this.diagnoseQbsWarningMessages(response); + QbsBuildSystem.logMessageResponse(response); + QbsBuildSystem.diagnoseQbsWarningMessages(response); }); this.session.onErrorMessageReceived(async (response) => { - this.logMessageResponse(response); - this.diagnoseQbsErrorMessages(response); + QbsBuildSystem.logMessageResponse(response); + QbsBuildSystem.diagnoseQbsErrorMessages(response); }); // Handle messages from the Qbs session process output (std/err). @@ -166,18 +167,18 @@ export class QbsBuildSystem implements vscode.Disposable { return; const shell = `${result.executable} ${result.arguments.join(' ')}`; - this.logMessage(shell); + QbsBuildSystem.logMessage(shell); const logStdMessages = (data: string[]) => { if (data.length) { const message = data.join('\n'); - this.logMessage(message); + QbsBuildSystem.logMessage(message); } } logStdMessages(result.stdError); logStdMessages(result.stdOutput); - this.diagnoseToolchainMessages(result); + QbsBuildSystem.diagnoseToolchainMessages(result); }); } @@ -236,11 +237,11 @@ export class QbsBuildSystem implements vscode.Disposable { private async resolveWithProgress(force: boolean, timeout: number): Promise { const request = this.createResolveRequest(force); - if (!this.ensureRequestIsReady(request)) + if (!QbsBuildSystem.ensureRequestIsReady(request)) return false; - else if (!this.ensureSaveFilesBeforeBuild()) + else if (!QbsBuildSystem.ensureSaveFilesBeforeBuild()) return false; - else if (!this.ensureClearOutputBeforeOperation()) + else if (!QbsBuildSystem.ensureClearOutputBeforeOperation()) return false; else if (!request) // Extra checking to use the method `this.session.resolve(request)`. return false; @@ -252,8 +253,8 @@ export class QbsBuildSystem implements vscode.Disposable { }, async (p, c) => { const timestamp = performance.now(); - this.logMessage(localize('qbs.buildsystem.resolve.started.message', 'Resolving project...')); - this.prepareQbsDiagnostics(); + QbsBuildSystem.logMessage(localize('qbs.buildsystem.resolve.started.message', 'Resolving project...')); + QbsBuildSystem.prepareQbsDiagnostics(); console.log('Send resolve request with force execution: ' + force + ' and timeout: ' + timeout); await this.session.resolve(request); @@ -287,7 +288,7 @@ export class QbsBuildSystem implements vscode.Disposable { }; disposables.push(this.session.onTaskStarted(async (result) => { - this.logMessage(result.description); + QbsBuildSystem.logMessage(result.description); description = result.description; maxProgress = result.maxProgress; progress = 0; @@ -304,19 +305,20 @@ export class QbsBuildSystem implements vscode.Disposable { disposables.push(this.session.onProjectResolved(async (result) => { const elapsed = msToTime(performance.now() - timestamp); QbsProjectManager.getInstance().getProject()?.setProjectData(true, result.data); + QbsProjectManager.getInstance().getProject()?.notifyOperationCompleted(); - this.logMessageResponse(result.message); - this.diagnoseQbsInformationMessages(result.message); - this.submitQbsDiagnostics(); + QbsBuildSystem.logMessageResponse(result.message); + QbsBuildSystem.diagnoseQbsInformationMessages(result.message); + QbsBuildSystem.submitQbsDiagnostics(); const success = (result.message) ? result.message.getIsEmpty() : false; console.log('Received resolve response with result: ' + success); if (success) { - this.logMessage(localize('qbs.buildsystem.resolve.completed.message', + QbsBuildSystem.logMessage(localize('qbs.buildsystem.resolve.completed.message', 'Project successfully resolved, elapsed time {0}', elapsed)); } else { - this.logMessage(localize('qbs.buildsystem.resolve.failed.message', + QbsBuildSystem.logMessage(localize('qbs.buildsystem.resolve.failed.message', 'Error resolving project, elapsed time {0}', elapsed)); } @@ -337,11 +339,11 @@ export class QbsBuildSystem implements vscode.Disposable { public async buildWithProgress(productNames: string[], timeout: number): Promise { const request = this.createBuildRequest(productNames); - if (!this.ensureRequestIsReady(request)) + if (!QbsBuildSystem.ensureRequestIsReady(request)) return false; - else if (!this.ensureSaveFilesBeforeBuild()) + else if (!QbsBuildSystem.ensureSaveFilesBeforeBuild()) return false; - else if (!this.ensureClearOutputBeforeOperation()) + else if (!QbsBuildSystem.ensureClearOutputBeforeOperation()) return false; else if (!request) // Extra checking to use the method `this.session.build(request)`. return false; @@ -353,9 +355,9 @@ export class QbsBuildSystem implements vscode.Disposable { }, async (p, c) => { const timestamp = performance.now(); - this.logMessage(localize('qbs.buildsystem.build.started.message', 'Building project...')); - this.prepareQbsDiagnostics(); - this.prepareToolchainDiagnostics(); + QbsBuildSystem.logMessage(localize('qbs.buildsystem.build.started.message', 'Building project...')); + QbsBuildSystem.prepareQbsDiagnostics(); + QbsBuildSystem.prepareToolchainDiagnostics(); console.log('Send build request for products: ' + QbsBuildSystem.getTargets(productNames) + ' and timeout: ' + timeout); @@ -408,19 +410,19 @@ export class QbsBuildSystem implements vscode.Disposable { QbsProjectManager.getInstance().getProject()?.setProjectData(false, result.data); QbsProjectManager.getInstance().getProject()?.notifyOperationCompleted(); - this.logMessageResponse(result.message); - this.diagnoseQbsInformationMessages(result.message); - this.submitQbsDiagnostics(); - this.submitToolchainDiagnostics(); + QbsBuildSystem.logMessageResponse(result.message); + QbsBuildSystem.diagnoseQbsInformationMessages(result.message); + QbsBuildSystem.submitQbsDiagnostics(); + QbsBuildSystem.submitToolchainDiagnostics(); const success = (result.message) ? result.message.getIsEmpty() : false; console.log('Received build response with result: ' + success); if (success) { - this.logMessage(localize('qbs.buildsystem.build.completed.message', + QbsBuildSystem.logMessage(localize('qbs.buildsystem.build.completed.message', 'Project successfully built, elapsed time {0}', elapsed)); } else { - this.logMessage(localize('qbs.buildsystem.build.failed.message', + QbsBuildSystem.logMessage(localize('qbs.buildsystem.build.failed.message', 'Error building project, elapsed time {0}', elapsed)); } @@ -441,9 +443,9 @@ export class QbsBuildSystem implements vscode.Disposable { private async installWithProgress(productNames: string[], timeout: number): Promise { const request = this.createInstallRequest(productNames); - if (!this.ensureRequestIsReady(request)) + if (!QbsBuildSystem.ensureRequestIsReady(request)) return false; - else if (!this.ensureClearOutputBeforeOperation()) + else if (!QbsBuildSystem.ensureClearOutputBeforeOperation()) return false; else if (!request) // Extra checking to use the method `this.session.install(request)`. return false; @@ -455,8 +457,8 @@ export class QbsBuildSystem implements vscode.Disposable { }, async (p, c) => { const timestamp = performance.now(); - this.logMessage(localize('qbs.buildsystem.install.started.message', 'Installing project...')); - this.prepareQbsDiagnostics(); + QbsBuildSystem.logMessage(localize('qbs.buildsystem.install.started.message', 'Installing project...')); + QbsBuildSystem.prepareQbsDiagnostics(); console.log('Send install request for products: ' + QbsBuildSystem.getTargets(productNames) + ' and timeout: ' + timeout); @@ -507,18 +509,18 @@ export class QbsBuildSystem implements vscode.Disposable { disposables.push(this.session.onProjectInstalled(async (result) => { const elapsed = msToTime(performance.now() - timestamp); - this.logMessageResponse(result); - this.diagnoseQbsInformationMessages(result); - this.submitQbsDiagnostics(); + QbsBuildSystem.logMessageResponse(result); + QbsBuildSystem.diagnoseQbsInformationMessages(result); + QbsBuildSystem.submitQbsDiagnostics(); const success = (result) ? result.getIsEmpty() : false; console.log('Received install response with result: ' + success); if (success) { - this.logMessage(localize('qbs.buildsystem.install.completed.message', + QbsBuildSystem.logMessage(localize('qbs.buildsystem.install.completed.message', 'Project successfully installed, elapsed time {0}', elapsed)); } else { - this.logMessage(localize('qbs.buildsystem.install.failed.message', + QbsBuildSystem.logMessage(localize('qbs.buildsystem.install.failed.message', 'Error installing project, elapsed time {0}', elapsed)); } @@ -539,9 +541,9 @@ export class QbsBuildSystem implements vscode.Disposable { private async cleanWithProgress(productNames: string[], timeout: number): Promise { const request = this.createCleanRequest(productNames); - if (!this.ensureRequestIsReady(request)) + if (!QbsBuildSystem.ensureRequestIsReady(request)) return false; - else if (!this.ensureClearOutputBeforeOperation()) + else if (!QbsBuildSystem.ensureClearOutputBeforeOperation()) return false; else if (!request) // Extra checking to use the method `this.session.clean(request)`. return false; @@ -553,8 +555,8 @@ export class QbsBuildSystem implements vscode.Disposable { }, async (p, c) => { const timestamp = performance.now(); - this.logMessage(localize('qbs.buildsystem.clean.started.message', 'Cleaning project...')); - this.prepareQbsDiagnostics(); + QbsBuildSystem.logMessage(localize('qbs.buildsystem.clean.started.message', 'Cleaning project...')); + QbsBuildSystem.prepareQbsDiagnostics(); console.log('Send clean request for products: ' + QbsBuildSystem.getTargets(productNames) + ' and timeout: ' + timeout); @@ -606,18 +608,18 @@ export class QbsBuildSystem implements vscode.Disposable { const elapsed = msToTime(performance.now() - timestamp); QbsProjectManager.getInstance().getProject()?.notifyOperationCompleted(); - this.logMessageResponse(result); - this.diagnoseQbsInformationMessages(result); - this.submitQbsDiagnostics(); + QbsBuildSystem.logMessageResponse(result); + QbsBuildSystem.diagnoseQbsInformationMessages(result); + QbsBuildSystem.submitQbsDiagnostics(); const success = (result) ? result.getIsEmpty() : false; console.log('Received clean response with result: ' + success); if (success) { - this.logMessage(localize('qbs.buildsystem.clean.completed.message', + QbsBuildSystem.logMessage(localize('qbs.buildsystem.clean.completed.message', 'Project successfully cleaned, elapsed time {0}', elapsed)); } else { - this.logMessage(localize('qbs.buildsystem.clean.failed.message', + QbsBuildSystem.logMessage(localize('qbs.buildsystem.clean.failed.message', 'Error cleaning project, elapsed time {0}', elapsed)); } @@ -648,9 +650,9 @@ export class QbsBuildSystem implements vscode.Disposable { private async compileOnlyWithProgress(fsPath: string, timeout: number): Promise { const request = this.createCompileOnlyRequest(fsPath); - if (!this.ensureRequestIsReady(request)) + if (!QbsBuildSystem.ensureRequestIsReady(request)) return false; - else if (!this.ensureClearOutputBeforeOperation()) + else if (!QbsBuildSystem.ensureClearOutputBeforeOperation()) return false; else if (!request) // Extra checking to use the method `this.session.build(request)`. return false; @@ -662,9 +664,9 @@ export class QbsBuildSystem implements vscode.Disposable { }, async (p, c) => { const timestamp = performance.now(); - this.logMessage(localize('qbs.buildsystem.compile.started.message', 'Compiling file...')); - this.prepareQbsDiagnostics(); - this.prepareToolchainDiagnostics(); + QbsBuildSystem.logMessage(localize('qbs.buildsystem.compile.started.message', 'Compiling file...')); + QbsBuildSystem.prepareQbsDiagnostics(); + QbsBuildSystem.prepareToolchainDiagnostics(); console.log('Send compile only request for file: ' + fsPath + ' and timeout: ' + timeout); await this.session.build(request); @@ -715,19 +717,19 @@ export class QbsBuildSystem implements vscode.Disposable { const elapsed = msToTime(performance.now() - timestamp); QbsProjectManager.getInstance().getProject()?.setProjectData(false, result.data); - this.logMessageResponse(result.message); - this.diagnoseQbsInformationMessages(result.message); - this.submitQbsDiagnostics(); - this.submitToolchainDiagnostics(); + QbsBuildSystem.logMessageResponse(result.message); + QbsBuildSystem.diagnoseQbsInformationMessages(result.message); + QbsBuildSystem.submitQbsDiagnostics(); + QbsBuildSystem.submitToolchainDiagnostics(); const success = (result.message) ? result.message.getIsEmpty() : false; console.log('Received compile only response with result: ' + success); if (success) { - this.logMessage(localize('qbs.buildsystem.compile.completed.message', + QbsBuildSystem.logMessage(localize('qbs.buildsystem.compile.completed.message', 'File successfully compiled, elapsed time {0}', elapsed)); } else { - this.logMessage(localize('qbs.buildsystem.compile.failed.message', + QbsBuildSystem.logMessage(localize('qbs.buildsystem.compile.failed.message', 'Error compiling file, elapsed time {0}', elapsed)); } @@ -766,73 +768,116 @@ export class QbsBuildSystem implements vscode.Disposable { private createCancelRequst(): QbsProtocolCancelRequest { return new QbsProtocolCancelRequest(); } private createResolveRequest(forceResolve: boolean): QbsProtocolResolveRequest | undefined { - const project = QbsProjectManager.getInstance().getProject(); + const project = QbsBuildSystem.getProject(); if (!project) return; - - const buildRoot = this.getBuildRootDirectoryFromSettings(); - const dryRun = this.getDryRunFromSettings(buildRoot); - const errorHandlingMode = this.getErrorHandlingModeFromSettings(); - const forceProbeExecution = (forceResolve) ? true : this.getForceProbeExecutionFromSettings(); - const logLevel = this.getLogLevelFromSettings(); - const settingsDirectory = this.getSettingsDirectoryFromSettings(); + const buildRoot = QbsBuildSystem.getBuildRootDirectoryPathFromSettings(); + if (!buildRoot) + return; + const dryRun = QbsBuildSystem.getDryRunFromSettings(buildRoot); + const errorHandlingMode = QbsBuildSystem.getErrorHandlingModeFromSettings(); + const forceProbeExecution = forceResolve || QbsBuildSystem.getForceProbeExecutionFromSettings(); + const logLevel = QbsBuildSystem.getLogLevelFromSettings(); const request = new QbsProtocolResolveRequest(buildRoot, dryRun, errorHandlingMode, - forceProbeExecution, logLevel, settingsDirectory); + forceProbeExecution, logLevel); + + const settingsDirectory = QbsBuildSystem.getSettingsDirectoryPathFromSettings(); + const fsPath = project.getFsPath(); + const profileName = project.getProfileName(); // An undefined value means the default profile. + const configurationName = project.getConfigurationName(); // An undefined value means the debug configuration. - request.setProjectFilePath(project?.getFsPath()); + request.setSettingsDirectory(settingsDirectory); + request.setProjectFilePath(fsPath); request.setConfigurationName(project.getConfigurationName()); request.setTopLevelProfile(project.getProfileName()); // Find the current configuration by it's a name to get the overriden properties. const configuration = QbsBuildConfigurationManager.getInstance().findConfiguration(project.getConfigurationName()); - request.setOverriddenProperties(configuration ? configuration.properties : undefined) + request.setOverriddenProperties(configuration ? configuration.properties : undefined); + console.log('Create resolve request:\n' + + '\tbuildRoot: ' + buildRoot + '\n' + + '\tdryRun: ' + dryRun + '\n' + + '\terrorHandlingMode: ' + errorHandlingMode + '\n' + + '\tforceProbeExecution: ' + forceProbeExecution + '\n' + + '\tlogLevel: ' + logLevel + '\n' + + '\tfsPath: ' + fsPath + '\n' + + '\tprofileName: ' + profileName + '\n' + + '\tconfigurationName: ' + configurationName + ); return request; } - private createBuildRequest(products: string[]): QbsProtocolBuildRequest | undefined { - const ckeanInstallRoot = this.getCleanInstallRootFromSettings(); - const commandEchoMode = this.getCommandEchoModeFromSettings(); - const keepGoing = this.getKeepGoingFromSettings(); - const logLevel = this.getLogLevelFromSettings(); - const maxJobs = this.getMaxJobsFromSettings(); - const install = this.getInstallFromSettings(); - const request = new QbsProtocolBuildRequest(ckeanInstallRoot, commandEchoMode, keepGoing, logLevel, maxJobs, install); - request.setInstall(install); - request.setProducts(products); + private createBuildRequest(productNames: string[]): QbsProtocolBuildRequest | undefined { + const cleanInstallRoot = QbsBuildSystem.getCleanInstallRootFromSettings(); + const commandEchoMode = QbsBuildSystem.getCommandEchoModeFromSettings(); + const keepGoing = QbsBuildSystem.getKeepGoingFromSettings(); + const logLevel = QbsBuildSystem.getLogLevelFromSettings(); + const maxJobs = QbsBuildSystem.getMaxJobsFromSettings(); + const install = QbsBuildSystem.getInstallFromSettings(); + const request = new QbsProtocolBuildRequest(cleanInstallRoot, commandEchoMode, keepGoing, logLevel, maxJobs, install); + request.setProducts(productNames); + console.log('Create build request:\n' + + '\tcleanInstallRoot: ' + cleanInstallRoot + '\n' + + '\tcommandEchoMode: ' + commandEchoMode + '\n' + + '\tkeepGoing: ' + keepGoing + '\n' + + '\tlogLevel: ' + logLevel + '\n' + + '\tmaxJobs: ' + maxJobs + '\n' + + '\tinstall: ' + install + '\n' + + '\tproducts: ' + productNames + ); return request; } - private createInstallRequest(products: string[]): QbsProtocolInstallRequest | undefined { - const keepGoing = this.getKeepGoingFromSettings(); - const logLevel = this.getLogLevelFromSettings(); + private createInstallRequest(productNames: string[]): QbsProtocolInstallRequest | undefined { + const keepGoing = QbsBuildSystem.getKeepGoingFromSettings(); + const logLevel = QbsBuildSystem.getLogLevelFromSettings(); const request = new QbsProtocolInstallRequest(keepGoing, logLevel); - request.setProducts(products); + request.setProducts(productNames); + console.log('Create install request:\n' + + '\tkeepGoing: ' + keepGoing + '\n' + + '\tlogLevel: ' + logLevel + '\n' + + '\tproducts: ' + productNames + ); return request; } - private createCleanRequest(products: string[]): QbsProtocolCleanRequest | undefined { - const keepGoing = this.getKeepGoingFromSettings(); - const logLevel = this.getLogLevelFromSettings(); + private createCleanRequest(productNames: string[]): QbsProtocolCleanRequest | undefined { + const keepGoing = QbsBuildSystem.getKeepGoingFromSettings(); + const logLevel = QbsBuildSystem.getLogLevelFromSettings(); const request = new QbsProtocolCleanRequest(keepGoing, logLevel); - request.setProducts(products); + request.setProducts(productNames); + console.log('Create clean request:\n' + + '\tkeepGoing: ' + keepGoing + '\n' + + '\tlogLevel: ' + logLevel + '\n' + + '\tproducts: ' + productNames + ); return request; } private createCompileOnlyRequest(fsPath: string): QbsProtocolBuildRequest | undefined { - const ckeanInstallRoot = this.getCleanInstallRootFromSettings(); - const commandEchoMode = this.getCommandEchoModeFromSettings(); - const keepGoing = this.getKeepGoingFromSettings(); - const logLevel = this.getLogLevelFromSettings(); - const maxJobs = this.getMaxJobsFromSettings(); - const install = this.getInstallFromSettings(); - const request = new QbsProtocolBuildRequest(ckeanInstallRoot, commandEchoMode, keepGoing, logLevel, maxJobs, install); + const cleanInstallRoot = QbsBuildSystem.getCleanInstallRootFromSettings(); + const commandEchoMode = QbsBuildSystem.getCommandEchoModeFromSettings(); + const keepGoing = QbsBuildSystem.getKeepGoingFromSettings(); + const logLevel = QbsBuildSystem.getLogLevelFromSettings(); + const maxJobs = QbsBuildSystem.getMaxJobsFromSettings(); + const install = QbsBuildSystem.getInstallFromSettings(); + const request = new QbsProtocolBuildRequest(cleanInstallRoot, commandEchoMode, keepGoing, logLevel, maxJobs, install); request.setChangedFiles([fsPath]); request.setFilesToConsider([fsPath]); request.setActiveFileTags(['obj', 'hpp']); + console.log('Create compile only request:\n' + + '\tcleanInstallRoot: ' + cleanInstallRoot + '\n' + + '\tcommandEchoMode: ' + commandEchoMode + '\n' + + '\tkeepGoing: ' + keepGoing + '\n' + + '\tlogLevel: ' + logLevel + '\n' + + '\tmaxJobs: ' + maxJobs + '\n' + + '\tinstall: ' + install + '\n' + + '\tchanged files: ' + fsPath + ); return request; } - private ensureRequestIsReady(request?: QbsProtocolRequest): boolean { + private static ensureRequestIsReady(request?: QbsProtocolRequest): boolean { if (!request) { vscode.window.showWarningMessage(localize('qbs.buildsystem.noproject.message', 'Unable to start operation due to the project is not selected.')); @@ -841,8 +886,8 @@ export class QbsBuildSystem implements vscode.Disposable { return true; } - private ensureSaveFilesBeforeBuild(): boolean { - const needsSaveOpened = this.getSaveBeforeBuildFromSettings(); + private static ensureSaveFilesBeforeBuild(): boolean { + const needsSaveOpened = QbsBuildSystem.getSaveBeforeBuildFromSettings(); if (needsSaveOpened && !trySaveAll()) { vscode.window.showErrorMessage(localize('qbs.buildsystem.save.failed.message', 'Unable to save the open files.')); @@ -851,14 +896,14 @@ export class QbsBuildSystem implements vscode.Disposable { return true; } - private ensureClearOutputBeforeOperation(): boolean { - const needsClearOutput = this.getClearOutputBeforeOperationFromSettings(); + private static ensureClearOutputBeforeOperation(): boolean { + const needsClearOutput = QbsBuildSystem.getClearOutputBeforeOperationFromSettings(); if (needsClearOutput) QbsOutputLogger.getInstance().clearOutput(); return true; } - private logMessageResponse(response?: QbsProtocolMessageResponse): void { + private static logMessageResponse(response?: QbsProtocolMessageResponse): void { if (!response || response.getIsEmpty()) return; const message = response.toString(); @@ -867,60 +912,94 @@ export class QbsBuildSystem implements vscode.Disposable { this.logMessage(message); } - private logMessage(message: string): void { QbsOutputLogger.getInstance().logOutput(message); } + private static logMessage(message: string): void { QbsOutputLogger.getInstance().logOutput(message); } // Route to diagnostics manager. - private diagnoseQbsInformationMessages(response?: QbsProtocolMessageResponse): void { + private static diagnoseQbsInformationMessages(response?: QbsProtocolMessageResponse): void { if (response) QbsDiagnosticManager.getInstance().handleQbsInformationMessages(response); } - private diagnoseQbsWarningMessages(response?: QbsProtocolMessageResponse): void { + private static diagnoseQbsWarningMessages(response?: QbsProtocolMessageResponse): void { if (response) QbsDiagnosticManager.getInstance().handleQbsWarningMessages(response); } - private diagnoseQbsErrorMessages(response?: QbsProtocolMessageResponse): void { + private static diagnoseQbsErrorMessages(response?: QbsProtocolMessageResponse): void { if (response) QbsDiagnosticManager.getInstance().handleQbsErrorMessages(response); } - private diagnoseToolchainMessages(response?: QbsProtocolProcessResponse): void { + private static diagnoseToolchainMessages(response?: QbsProtocolProcessResponse): void { if (response) QbsDiagnosticManager.getInstance().handleToolchainMessages(response); } - private prepareQbsDiagnostics(): void { QbsDiagnosticManager.getInstance().prepareQbsDiagnostics(); } - private submitQbsDiagnostics(): void { QbsDiagnosticManager.getInstance().submitQbsDiagnostics(); } + private static prepareQbsDiagnostics(): void { QbsDiagnosticManager.getInstance().prepareQbsDiagnostics(); } + private static submitQbsDiagnostics(): void { QbsDiagnosticManager.getInstance().submitQbsDiagnostics(); } - private prepareToolchainDiagnostics(): void { + private static prepareToolchainDiagnostics(): void { const projectData = QbsProjectManager.getInstance().getProject()?.getProjectData(); if (projectData) QbsDiagnosticManager.getInstance().prepareToolchainDiagnostics(projectData); } - private submitToolchainDiagnostics(): void { QbsDiagnosticManager.getInstance().submitToolchainDiagnostics(); } + private static submitToolchainDiagnostics(): void { QbsDiagnosticManager.getInstance().submitToolchainDiagnostics(); } - // From the Qbs extension settings. - private getBuildRootDirectoryFromSettings(): string { + private static getProject(): QbsProject | undefined { const project = QbsProjectManager.getInstance().getProject(); - return QbsSettings.substituteFsPath(QbsSettings.getBuildDirectory(), project?.getName(), - project?.getProfileName(), project?.getConfigurationName()); - } - - private getCleanInstallRootFromSettings(): boolean { return QbsSettings.getCleanInstallRoot(); } - private getClearOutputBeforeOperationFromSettings(): boolean { return QbsSettings.getClearOutputBeforeOperation(); } - private getCommandEchoModeFromSettings(): QbsProtocolCommandEchoMode { return QbsSettings.getCommandEchoMode(); } - private getDryRunFromSettings(buildRoot: string): boolean { return !fs.existsSync(buildRoot); } - private getErrorHandlingModeFromSettings(): QbsProtocolErrorHandlingMode { return QbsSettings.getErrorHandlingMode(); } - private getForceProbeExecutionFromSettings(): boolean { return QbsSettings.getForceProbes(); } - private getInstallFromSettings(): boolean { return QbsSettings.getInstallAfterBuild(); } - private getKeepGoingFromSettings(): boolean { return QbsSettings.getKeepGoing(); } - private getLogLevelFromSettings(): QbsProtocolLogLevel { return QbsSettings.getLogLevel(); } - private getMaxJobsFromSettings(): number { return QbsSettings.getMaxJobs(); } - private getSaveBeforeBuildFromSettings(): boolean { return QbsSettings.getSaveBeforeBuild(); } - private getSettingsDirectoryFromSettings(): string { return QbsSettings.substituteFsPath(QbsSettings.getSettingsDirectory()); } + if (project) + return project; + vscode.window.showErrorMessage(localize('qbs.buildsystem.noproject.message', + `Unable to get the Qbs project because no any project is loaded.`)); + } + + private static getSourceRootDirectoryFromSettings(fsPath: string): string | undefined { + const result = QbsSettings.getSourceRootDirectory(fsPath); + if (result) + return result; + vscode.window.showWarningMessage(localize('qbs.buildsystem.nosourceroot.message', + 'Unable get the source root directory path because no any workspace folder is open.')); + } + + private static getFullPathFromSettings(fsPath: string): string | undefined { + const project = QbsBuildSystem.getProject(); + if (!project) + return; + const sourceRoot = QbsBuildSystem.getSourceRootDirectoryFromSettings(project.getFsPath()); + if (!sourceRoot) + return; + const projectName = project.getName(); + const profileName = project.getProfileName() || 'default'; // The undefined build profile name means the `default` profile. + const configurationName = project.getConfigurationName() || 'debug'; // The undefined build configuration name means the `debug` configuration. + + fsPath = QbsSettings.substituteSourceRoot(fsPath, sourceRoot); + fsPath = QbsSettings.substituteProjectName(fsPath, projectName); + fsPath = QbsSettings.substituteBuildProfileName(fsPath, profileName); + fsPath = QbsSettings.substituteBuildConfigurationName(fsPath, configurationName); + return fsPath; + } + + private static getBuildRootDirectoryPathFromSettings(): string | undefined { + return QbsBuildSystem.getFullPathFromSettings(QbsSettings.getBuildDirectory()); + } + + private static getSettingsDirectoryPathFromSettings(): string | undefined { + return QbsBuildSystem.getFullPathFromSettings(QbsSettings.getSettingsDirectory()); + } + + private static getCleanInstallRootFromSettings(): boolean { return QbsSettings.getCleanInstallRoot(); } + private static getClearOutputBeforeOperationFromSettings(): boolean { return QbsSettings.getClearOutputBeforeOperation(); } + private static getCommandEchoModeFromSettings(): QbsProtocolCommandEchoMode { return QbsSettings.getCommandEchoMode(); } + private static getDryRunFromSettings(buildRoot: string): boolean { return !fs.existsSync(buildRoot); } + private static getErrorHandlingModeFromSettings(): QbsProtocolErrorHandlingMode { return QbsSettings.getErrorHandlingMode(); } + private static getForceProbeExecutionFromSettings(): boolean { return QbsSettings.getForceProbes(); } + private static getInstallFromSettings(): boolean { return QbsSettings.getInstallAfterBuild(); } + private static getKeepGoingFromSettings(): boolean { return QbsSettings.getKeepGoing(); } + private static getLogLevelFromSettings(): QbsProtocolLogLevel { return QbsSettings.getLogLevel(); } + private static getMaxJobsFromSettings(): number { return QbsSettings.getMaxJobs(); } + private static getSaveBeforeBuildFromSettings(): boolean { return QbsSettings.getSaveBeforeBuild(); } private static getTargets(products: string[]): string { return (products && (products.length > 0)) ? products.join(';') : localize('qbs.select.build.product.placeholder', 'ALL'); diff --git a/src/qbsextension.ts b/src/qbsextension.ts index 6b61510..f4b5eef 100644 --- a/src/qbsextension.ts +++ b/src/qbsextension.ts @@ -62,8 +62,6 @@ export async function activate(context: vscode.ExtensionContext) { await vscode.commands.executeCommand(QbsCommandKey.StartupCppCodeModel); await vscode.commands.executeCommand(QbsCommandKey.RestartSession); await vscode.commands.executeCommand(QbsCommandKey.ScanBuildProfiles); - await vscode.commands.executeCommand(QbsCommandKey.ScanBuildConfigurations); - await vscode.commands.executeCommand(QbsCommandKey.ScanLaunchConfigurations); await vscode.commands.executeCommand(QbsCommandKey.RestoreProject); } diff --git a/src/qbslaunchconfigurationmanager.ts b/src/qbslaunchconfigurationmanager.ts index 664d415..78f6801 100644 --- a/src/qbslaunchconfigurationmanager.ts +++ b/src/qbslaunchconfigurationmanager.ts @@ -8,6 +8,7 @@ import { ensureFileCreated } from './qbsutils'; import { QbsCommandKey } from './datatypes/qbscommandkey'; import { QbsLaunchConfigurationData } from './datatypes/qbslaunchconfigurationdata'; import { QbsLaunchConfigurationKey } from './datatypes/qbslaunchconfigurationkey'; +import { QbsProjectManager } from './qbsprojectmanager'; import { QbsSettings } from './qbssettings'; const localize = nls.config({ messageFormat: nls.MessageFormat.file })(); @@ -18,6 +19,7 @@ export class QbsLaunchConfigurationManager implements vscode.Disposable { private updated: vscode.EventEmitter = new vscode.EventEmitter(); private configurationSelected: vscode.EventEmitter = new vscode.EventEmitter(); private fsLaunchConfigurationsWatcher?: chokidar.FSWatcher + private disposable?: vscode.Disposable; readonly onUpdated: vscode.Event = this.updated.event; readonly onConfigurationSelected: vscode.Event = this.configurationSelected.event; @@ -26,16 +28,10 @@ export class QbsLaunchConfigurationManager implements vscode.Disposable { public constructor(context: vscode.ExtensionContext) { QbsLaunchConfigurationManager.instance = this; - - // Create default launch configurations file if this file not exists yet. - QbsLaunchConfigurationManager.ensureConfigurationsCreated(); - - // Register the commands related to the profile manager. this.registerCommandsHandlers(context); - this.subscribeSettingsChanges(); } - public dispose(): void { this.fsLaunchConfigurationsWatcher?.close(); } + public dispose(): void { this.stop(); } public getConfigurations(): QbsLaunchConfigurationData[] { return this.configurations; } @@ -43,18 +39,14 @@ export class QbsLaunchConfigurationManager implements vscode.Disposable { return this.getConfigurations().find(configuration => configuration.getName() === configurationName); } - private registerCommandsHandlers(context: vscode.ExtensionContext): void { - context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.ScanLaunchConfigurations, - async () => { await this.scanConfigurations(); })); - context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.SelectLaunchConfiguration, - async () => { await this.selectConfiguration(); })); - context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.EditLaunchConfigurations, - async () => { await this.editConfigurations(); })); - } - - private subscribeSettingsChanges(): void { + public async start(): Promise { const subscribeLaunchConfigurationsChanged = async () => { - const fsPath = QbsSettings.substituteFsPath(QbsSettings.getLaunchFilePath()); + const fsProjectPath = QbsProjectManager.getInstance().getProject()?.getFsPath(); + if (!fsProjectPath) + return; + const fsPath = QbsLaunchConfigurationManager.getFullLaunchConfigurationsFilePath(fsProjectPath); + if (!fsPath) + return; console.log('Launch configurations file name changed to: ' + fsPath); this.fsLaunchConfigurationsWatcher = chokidar.watch(fsPath, { ignoreInitial: true }); this.fsLaunchConfigurationsWatcher.on('change', () => { @@ -63,14 +55,36 @@ export class QbsLaunchConfigurationManager implements vscode.Disposable { }); } - QbsSettings.observeSetting(QbsSettings.SettingKey.LaunchFilePath, + this.disposable?.dispose(); + this.disposable = QbsSettings.observeSetting(QbsSettings.SettingKey.LaunchFilePath, async () => subscribeLaunchConfigurationsChanged()); - subscribeLaunchConfigurationsChanged(); + await subscribeLaunchConfigurationsChanged(); + } + + public async stop(): Promise { + await this.fsLaunchConfigurationsWatcher?.close(); + await this.disposable?.dispose(); + } + + public async restart(): Promise { + await this.stop(); + await this.start(); + } + + private registerCommandsHandlers(context: vscode.ExtensionContext): void { + context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.ScanLaunchConfigurations, + async () => { await this.scanConfigurations(); })); + context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.SelectLaunchConfiguration, + async () => { await this.selectConfiguration(); })); + context.subscriptions.push(vscode.commands.registerCommand(QbsCommandKey.EditLaunchConfigurations, + async () => { await QbsLaunchConfigurationManager.editConfigurations(); })); } private async scanConfigurations(): Promise { return new Promise((resolve) => { const fsPath = QbsLaunchConfigurationManager.ensureConfigurationsCreated(); + if (!fsPath) + return; console.log('Start reading launch configurations from: ' + fsPath); fs.readFile(fsPath, (error, data) => { if (error) { @@ -134,16 +148,22 @@ export class QbsLaunchConfigurationManager implements vscode.Disposable { } } - private async editConfigurations(): Promise { + private static async editConfigurations(): Promise { // Create if not exists yet, and then show the `qbs-configurations.json` file. const fsPath = QbsLaunchConfigurationManager.ensureConfigurationsCreated(); + if (!fsPath) + return; const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(fsPath)); await vscode.window.showTextDocument(doc); } - private static ensureConfigurationsCreated(): string { - let fsPath = QbsSettings.getLaunchFilePath() - fsPath = QbsSettings.substituteFsPath(fsPath); + private static ensureConfigurationsCreated(): string | undefined { + const fsProjectPath = QbsProjectManager.getInstance().getProject()?.getFsPath(); + if (!fsProjectPath) + return; + const fsPath = QbsLaunchConfigurationManager.getFullLaunchConfigurationsFilePath(fsProjectPath); + if (!fsPath) + return; ensureFileCreated(fsPath, QbsLaunchConfigurationManager.writeDefaultConfigurations); return fsPath; } @@ -162,4 +182,20 @@ export class QbsLaunchConfigurationManager implements vscode.Disposable { ws.write(JSON.stringify(QbsLaunchConfigurationManager.getDefaultConfigurations(), null, 4)); return true; } + + private static getFullLaunchConfigurationsFilePath(fsProjectPath: string): string | undefined { + const sourceRoot = QbsSettings.getSourceRootDirectory(fsProjectPath); + if (!sourceRoot) { + vscode.window.showWarningMessage(localize('qbs.launchconfigurationmanager.noworkspace.message', + 'Unable get the launch configurations file because no any workspace folder is open.')); + return; + } + const fsPath = QbsSettings.getLaunchFilePath(); + if (!fsPath) { + vscode.window.showWarningMessage(localize('qbs.launchconfigurationmanager.nofspath.message', + 'Unable to get the launch configurations file because its path is not set in Qbs extension settings.')); + return; + } + return QbsSettings.substituteSourceRoot(fsPath, sourceRoot); + } } diff --git a/src/qbsprojectmanager.ts b/src/qbsprojectmanager.ts index 0675502..5c66584 100644 --- a/src/qbsprojectmanager.ts +++ b/src/qbsprojectmanager.ts @@ -256,48 +256,48 @@ export class QbsProjectManager implements vscode.Disposable { } else if (toolchain.indexOf('gcc') !== -1) { configuration.setType(QbsLaunchConfigurationType.Gdb); - const getFsDebugger = (toolchainPath: string, debuggerName: string): string => { + const getFsDebugger = async (toolchainPath: string, debuggerName: string): Promise => { const ext = (process.platform === 'win32') ? '.exe' : ''; - let fsPath = path.join(toolchainPath, debuggerName + ext); + let fsPath: string | undefined = path.join(toolchainPath, debuggerName + ext); if (!fs.existsSync(fsPath)) - fsPath = which.sync(debuggerName + ext); + fsPath = await QbsProjectManager.which(debuggerName + ext); return fsPath; }; - const setupMi = (toolchainDir: string, debuggerName: string, miMode: string): boolean => { - const fsPath = getFsDebugger(toolchainDir, debuggerName); - if (fsPath) + const setupMi = async (toolchainDir: string, debuggerName: string, miMode: string): Promise => { + const fsPath = await getFsDebugger(toolchainDir, debuggerName); + if (!fsPath) return false; configuration?.setMiMode(miMode); configuration?.setMiDebuggerPath(fsPath); return true; } - const setupDebugger = (): boolean => { + const setupDebugger = async (): Promise => { const compilerDirectory = path.dirname(compilerPath); // Check for the LLDB-MI debugger executable. - if (setupMi(compilerDirectory, 'lldb-mi', 'lldb')) + if (await setupMi(compilerDirectory, 'lldb-mi', 'lldb')) return true; // Check for the LLDB-MI installed by CppTools. const cpptoolsExtension = vscode.extensions.getExtension('ms-vscode.cpptools'); const fsPath = cpptoolsExtension ? path.join( cpptoolsExtension.extensionPath, 'debugAdapters', 'lldb-mi', 'bin') : undefined; - if (fsPath && setupMi(fsPath, 'lldb-mi', 'lldb')) + if (fsPath && await setupMi(fsPath, 'lldb-mi', 'lldb')) return true; // Check for the GDB debugger executable. - if (setupMi(compilerDirectory, 'gdb', 'gdb')) + if (await setupMi(compilerDirectory, 'gdb', 'gdb')) return true; // Check for the LLDB debugger executable. - if (setupMi(compilerDirectory, 'lldb', 'lldb')) + if (await setupMi(compilerDirectory, 'lldb', 'lldb')) return true; return false; } - if (!setupDebugger()) + if (!await setupDebugger()) throw new Error('Unable to start auto debugging session because the debugger is undefined'); } } @@ -352,6 +352,15 @@ export class QbsProjectManager implements vscode.Disposable { private async openProject(fsPath: string): Promise { this.project = new QbsProject(fsPath); await this.loadProject(); + + // Reload the build configurations for this new loaded project. + await QbsBuildConfigurationManager.getInstance().restart(); + await vscode.commands.executeCommand(QbsCommandKey.ScanBuildConfigurations); + + // Reload the launch configurations for this new loaded project. + await QbsLaunchConfigurationManager.getInstance().restart(); + await vscode.commands.executeCommand(QbsCommandKey.ScanLaunchConfigurations); + this.projectOpen.fire(); } @@ -399,4 +408,17 @@ export class QbsProjectManager implements vscode.Disposable { private static async getWorkspaceProjects(): Promise { return (await vscode.workspace.findFiles('*.qbs')).map(uri => uri.fsPath); } + + private static async which(name: string): Promise { + return new Promise(resolve => { + which(name, (error, path) => { + if (error) { + console.error('Unable to run "which ' + name + 'bs" command, error: ' + error.message); + resolve(undefined); + } else { + resolve(path); + } + }); + }); + } } diff --git a/src/qbssession.ts b/src/qbssession.ts index 1fd90ab..4f91f5d 100644 --- a/src/qbssession.ts +++ b/src/qbssession.ts @@ -210,9 +210,9 @@ export class QbsSession implements vscode.Disposable { /** Requests the path to the Qbs executable file stored in the extension configuration and * checks for its presence in the file system. Depending on the result, displays an appropriate * message box and then returns the ensuring result. */ - private static async ensureQbsPathIsSet(qbsPath: string): Promise { + private static async ensureQbsPathIsSet(qbsPath?: string): Promise { if (qbsPath === 'qbs') - qbsPath = await which(qbsPath); + qbsPath = await this.which(qbsPath); if (!qbsPath) { await vscode.window.showErrorMessage(localize('qbs.executable.missed.error.message', 'Qbs executable not set in settings.')); @@ -224,4 +224,17 @@ export class QbsSession implements vscode.Disposable { } return true; } + + private static async which(name: string): Promise { + return new Promise(resolve => { + which(name, (error, path) => { + if (error) { + console.error('Unable to run "which qbs" command, error: ' + error.message); + resolve(undefined); + } else { + resolve(path); + } + }); + }); + } } diff --git a/src/qbssettings.ts b/src/qbssettings.ts index 1e364ae..5e07047 100644 --- a/src/qbssettings.ts +++ b/src/qbssettings.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; -import { fixFsPathSeparators } from './qbsutils'; +import { isChildOf } from './qbsutils'; import { QbsProtocolCommandEchoMode } from './protocol/qbsprotocolcommandechomode'; import { QbsProtocolErrorHandlingMode } from './protocol/qbsprotocolerrorhandlingmode'; import { QbsProtocolLogLevel } from './protocol/qbsprotocolloglevel'; @@ -36,8 +36,8 @@ export namespace QbsSettings { ShowDisabledProjectItems = 'showDisabledProjectItems', } - export function observeSetting(field: SettingKey, callback: () => void): void { - vscode.workspace.onDidChangeConfiguration(event => { + export function observeSetting(field: SettingKey, callback: () => void): vscode.Disposable { + return vscode.workspace.onDidChangeConfiguration(event => { if (event.affectsConfiguration(qbsSettingsSection + '.' + field.toString())) callback(); }); @@ -109,12 +109,29 @@ export namespace QbsSettings { export function getInstallAfterBuild(): boolean { return getBoolean(SettingKey.InstallAfterBuild, true); } - export function substituteFsPath(fsPath: string, projectName?: string, profileName?: string, configurationName?: string) { - fsPath = fsPath.replace(QbsSubstitutionPattern.SourceDirectory, getSourceRootDirectory()); - fsPath = fsPath.replace(QbsSubstitutionPattern.ProjectName, projectName || qbsUnknownEntryPath); - fsPath = fsPath.replace(QbsSubstitutionPattern.ProfileName, profileName || qbsUnknownEntryPath); - fsPath = fsPath.replace(QbsSubstitutionPattern.ConfigurationName, configurationName || qbsUnknownEntryPath); - return fixFsPathSeparators(fsPath); + export function substituteSourceRoot(fsPath: string, sourceRoot: string): string { + return fsPath.replace(QbsSubstitutionPattern.SourceDirectory, sourceRoot); + } + + export function substituteProjectName(fsPath: string, projectName: string): string { + return fsPath.replace(QbsSubstitutionPattern.ProjectName, projectName); + } + + export function substituteBuildProfileName(fsPath: string, profileName: string): string { + return fsPath.replace(QbsSubstitutionPattern.ProfileName, profileName); + } + + export function substituteBuildConfigurationName(fsPath: string, configurationName: string): string { + return fsPath.replace(QbsSubstitutionPattern.ConfigurationName, configurationName); + } + + export function getSourceRootDirectory(fsPath: string): string | undefined { + for (var workspaceFolder of vscode.workspace.workspaceFolders || []) { + const directory = workspaceFolder.uri.fsPath; + if (isChildOf(fsPath, directory)) + return directory; + } + console.log('Unable to get the source root directory because the workspace folder is not selected yet'); } // Private part. @@ -126,7 +143,6 @@ export namespace QbsSettings { const qbsDefaultBuildDirectoryPath = `${QbsSubstitutionPattern.SourceDirectory}/build/${QbsSubstitutionPattern.ProfileName}_${QbsSubstitutionPattern.ConfigurationName}`; const qbsDefaultLaunchFilePath = `${QbsSubstitutionPattern.SourceDirectory}/.vscode/launch.json`; const qbsDefaultBuildConfigurationsFilePath = `${QbsSubstitutionPattern.SourceDirectory}/.vscode/qbs-configurations.json`; - const qbsUnknownEntryPath = 'unknown'; function getSettings(): vscode.WorkspaceConfiguration { return vscode.workspace.getConfiguration(qbsSettingsSection); @@ -139,9 +155,4 @@ export namespace QbsSettings { function getString(key: string, placeholder: string): string { return getSettings().get(key, placeholder); } - - function getSourceRootDirectory(): string { - return (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0)) - ? vscode.workspace.workspaceFolders[0].uri.fsPath : ''; - } }