From 9dffad52ec69385561f6b32aa6939b1e6a3282cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jack=C2=B7Boos=C2=B7Yu?= <47264268+JackBoosY@users.noreply.github.com> Date: Wed, 28 Feb 2024 18:31:00 +0800 Subject: [PATCH] Support debug port's CMakeLists (#10) Co-authored-by: jyu49 --- README.md | 8 +- package.json | 2 +- src/cmakeDebugger.ts | 260 +++++++++++++++++++++++++++++++++++++++++++ src/configuration.ts | 48 ++++---- src/extension.ts | 46 +++++++- src/vcpkgDebugger.ts | 19 +++- 6 files changed, 354 insertions(+), 29 deletions(-) create mode 100644 src/cmakeDebugger.ts diff --git a/README.md b/README.md index 9133fc5..38c4868 100644 --- a/README.md +++ b/README.md @@ -98,5 +98,11 @@ Note: please only enable/disable in `workspace`! ### Debug portfile or other vcpkg provided cmake code -1. Set debug point in the portfile.cmake (required) or other cmake file. +1. Set breakpoint in the portfile.cmake (required) or other cmake file. 2. Open `Run and Debug` in left side bar, select `Debug portfile(s)`, click the green triangle to run debugging. + +### Debug port's CMake code + +1. Set breakpoint in the portfile.cmake. +2. Run install command with `--editable`(important!) then set breakpoint in source cmake file(folder name without `.clean`). +3. Open `Run and Debug` in left side bar, select `Debug portfile(s)`, click the green triangle to run debugging. \ No newline at end of file diff --git a/package.json b/package.json index 702ea08..9443549 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "icon": "images/vcpkg-logo.png", "publisher": "JackBoosY", "license": "MIT", - "version": "2.1.0", + "version": "2.2.0", "engines": { "vscode": "^1.76.0" }, diff --git a/src/cmakeDebugger.ts b/src/cmakeDebugger.ts new file mode 100644 index 0000000..8d6ca14 --- /dev/null +++ b/src/cmakeDebugger.ts @@ -0,0 +1,260 @@ +import * as vscode from 'vscode'; +import {VcpkgLogMgr} from './log'; +import {VcpkgDebugger} from './vcpkgDebugger'; +import { debug } from 'vscode'; +import * as fs from 'fs'; + +function sleep(time: number){ + return new Promise((resolve) => setTimeout(resolve, time)); + } + +export class CmakeDebugger { + private _vcpkgDbg: VcpkgDebugger; + private _logMgr : VcpkgLogMgr; + private _waitDebug : boolean; + + constructor(vcpkgDebugger: VcpkgDebugger, logMgr : VcpkgLogMgr) + { + this._vcpkgDbg = vcpkgDebugger; + this._logMgr = logMgr; + this._waitDebug = false; + + this.updateConfigurations(); + } + + private generatePipeline() + { + if (process.platform === 'win32') + { + return "\\\\.\\\\pipe\\\\vscode-vcpkg-cmake-debugger-pipe"; + } + else + { + return "/tmp/vscode-vcpkg-cmakelists-debugger-pipe"; + } + } + + private getTasksJsonContent() + { + this._logMgr.logInfo("Loading tasks json content."); + return this.readFromFile("tasks"); + } + + private async updateConfig() + { + this._logMgr.logInfo("Updating tasks.json"); + + let fullContent = this.getTasksJsonContent(); + + if (JSON.stringify(fullContent) === "{}") + { + this._logMgr.logErr("tasks json is empty!"); + return; + } + else + { + if (fullContent.has("tasks")) + { + let taskArray = new Array; + let originCommand = ""; + let currTask; + for (let index = 0; index < fullContent["tasks"].length; index++) + { + const element = fullContent["tasks"][index]; + if (element["label"] === "Debug vcpkg commands") + { + this._logMgr.logInfo("Found exists task, update now."); + + currTask = element; + originCommand = element["command"]; + } + else + { + taskArray.push(element); + } + } + + if (originCommand) + { + if (originCommand.indexOf("--x-cmake-configure-debug") === -1) + { + let config = originCommand + " --x-cmake-configure-debug " + this.generatePipeline() + " --editable"; + currTask["command"] = config; + + taskArray.push(currTask); + + this._logMgr.logInfo("Update command " + config + " in Tasks json command"); + await this.writeToFile("tasks", "tasks", taskArray); + } + else + { + this._logMgr.logInfo("Already update command"); + } + } + } + else + { + this._logMgr.logInfo("Tasks item not found, new one now."); + return; + } + } + } + + private readFromFile(fileName: string) + { + return vscode.workspace.getConfiguration(fileName); + } + + private async writeToFile(fileName: string, scope: string, content: any) + { + this._logMgr.logInfo("Updating " + fileName + " - " + scope); + await vscode.workspace.getConfiguration(fileName).update(scope, content, null); + } + + private async cleanConfig() + { + this._logMgr.logInfo("Clean command in Tasks json command."); + + let fullContent = this.getTasksJsonContent(); + + if (JSON.stringify(fullContent) === "{}") + { + this._logMgr.logErr("tasks json is empty!"); + return; + } + else + { + if (fullContent.has("tasks")) + { + let taskArray = new Array; + let originCommand = ""; + let currTask; + for (let index = 0; index < fullContent["tasks"].length; index++) + { + const element = fullContent["tasks"][index]; + if (element["label"] === "Debug vcpkg commands") + { + this._logMgr.logInfo("Found exists task, update now."); + + currTask = element; + originCommand = element["command"]; + } + else + { + taskArray.push(element); + } + } + + if (originCommand) + { + if (currTask["command"].indexOf(" --x-cmake-configure-debug") !== -1) + { + let config = currTask["command"]; + config = config.substring(0, config.indexOf(" --x-cmake-configure-debug")); + currTask["command"] = config; + + taskArray.push(currTask); + + this._logMgr.logInfo("Delete command in Tasks json command"); + await this.writeToFile("tasks", "tasks", taskArray); + } + else + { + this._logMgr.logInfo("Command is alreay cleaned."); + } + } + } + else + { + this._logMgr.logInfo("Tasks item not found, new one now."); + return; + } + } + } + + public updateConfigurations() + { + let breakPoints = debug.breakpoints; + let validBreakPoint = false; + for (let index = 0; index < breakPoints.length; index++) + { + const element = breakPoints[index]; + // @ts-ignore + if (element.location.uri.toString().search("buildtrees") !== -1) + { + // @ts-ignore + this._logMgr.logInfo("Found breakpoint path: " + element.location.uri.toString()); + validBreakPoint = true; + break; + } + } + + if (validBreakPoint) + { + this._logMgr.logInfo("Found valid CMake breakpoint."); + this.updateConfig(); + } + else + { + this._logMgr.logInfo("No valid CMake breakpoint was found."); + this.cleanConfig(); + } + } + + public stopWaitingDebug() + { + this._waitDebug = false; + } + + public async startDebugging(vcpkgRoot: any, currentTriplet : any) + { + this._logMgr.logInfo("Starting debug cmake."); + if (vcpkgRoot === undefined || !vcpkgRoot.length) + { + this._logMgr.logErr("vcpkgRoot(" + vcpkgRoot + ") is empty!"); + vscode.window.showErrorMessage("Vcpkg root is empty! Please manually set."); + return; + } + else if (currentTriplet === undefined || !currentTriplet.length) + { + this._logMgr.logErr("currentTriplet(" + currentTriplet + ") is empty!"); + vscode.window.showErrorMessage("Current default triplet is empty! Please manually set first."); + return; + } + + let portName = this._vcpkgDbg.getModifiedPorts(); + portName = portName?.replace(" ", ""); + let outName = vcpkgRoot + "/buildtrees/" + portName + "/stdout-" + currentTriplet + ".log"; + let content = ""; + let whenConfigure = false; + + // wait for configure + this._logMgr.logInfo("Waiting for configure, reading output in " + outName); + this._waitDebug = true; + do { + if (!this._waitDebug) + { + this._logMgr.logInfo("Cancel debug CMakeLists."); + return; + } + content = fs.readFileSync(outName, { encoding: 'utf8', flag: 'r' }); + await sleep(100); + if (content.search("-- Configuring ") !== -1 && content.search("-- Performing post-build validation") === -1) + { + whenConfigure = true; + } + } while (!whenConfigure); + + this._waitDebug = false; + + this._logMgr.logInfo("Connecting cmake debug pipe."); + vscode.debug.startDebugging(undefined, { + name: "Vcpkg extension Debugger", + request: "launch", + type: "cmake", + cmakeDebugType: "external", + pipeName: this.generatePipeline(), + fromCommand: true + }); + } +} diff --git a/src/configuration.ts b/src/configuration.ts index 41456ee..0d145c1 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -51,13 +51,12 @@ export class ConfigurationManager implements vscode.Disposable this._logMgr = logMgr; this._versionMgr = verMgr; - this.getVcpkgPathFromConfig().then((vcpkgPath) => { - if (vcpkgPath !== undefined) - { - this._versionMgr.setVcpkgRoot(vcpkgPath); - this.updateVcpkgSetting(this._vcpkgPathConfig, vcpkgPath, true); - } - }); + let vcpkgPath= this.getVcpkgPathFromConfig(); + if (vcpkgPath !== undefined) + { + this._versionMgr.setVcpkgRoot(vcpkgPath); + this.updateVcpkgSetting(this._vcpkgPathConfig, vcpkgPath, true); + } // Update vcpkg target triplet let automaticUpdateTriplet = workspace.getConfiguration('vcpkg').get(this._autoUpdateTriplet); @@ -213,7 +212,7 @@ export class ConfigurationManager implements vscode.Disposable return path; } - private async getVcpkgPathFromEnv() + private getVcpkgPathFromEnv() { let envVar = process.env[this._vcpkgRootConfig]; // let envVar = this._context.environmentVariableCollection.get(this._vcpkgRootConfig); @@ -226,11 +225,11 @@ export class ConfigurationManager implements vscode.Disposable return undefined; } - private async getVcpkgPathFromConfig() + private getVcpkgPathFromConfig() { let tryFirst = workspace.getConfiguration('vcpkg').get(this._vcpkgPathConfig); - if ((tryFirst !== undefined && tryFirst.length !== 0) && await this.isVcpkgExistInPath(tryFirst)) + if ((tryFirst !== undefined && tryFirst.length !== 0) && this.isVcpkgExistInPath(tryFirst)) { return tryFirst; } @@ -265,7 +264,7 @@ export class ConfigurationManager implements vscode.Disposable return config !== undefined ? config : false; } - private async isVcpkgExistInPath(path: string) + private isVcpkgExistInPath(path: string) { let fullPath = ''; // check whether this path is a environment variable @@ -287,17 +286,6 @@ export class ConfigurationManager implements vscode.Disposable if (fs.existsSync(fullPath)) { return true; - // too strict, skip the following code - let version = await this.runCommand(fullPath, '--version', path); - if (version.match('vcpkg package management program version') !== null) - { - return true; - } - else - { - this.logErr('Run command: ' + fullPath + ' --version failed with ' + version); - return false; - } } else { @@ -814,10 +802,24 @@ export class ConfigurationManager implements vscode.Disposable await this.updateCMakeSetting(this._cmakeOptionConfig, newConfigs); } + public getVcpkgRealPath() + { + let config = this.getVcpkgPathFromConfig(); + // environment variable + if (config?.indexOf("$ENV") === 0) { + return this.getVcpkgPathFromEnv(); + } + else + { + // true real path + return config; + } + } + private getAllSupportedTriplets() { let triplets = []; - let vcpkgPath = this.getVcpkgPathFromConfig(); + let vcpkgPath = this.getVcpkgRealPath(); if (vcpkgPath === undefined) { diff --git a/src/extension.ts b/src/extension.ts index e6edc2e..90cabfe 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,6 +6,7 @@ import { ConfigurationManager } from './configuration'; import {SettingsDocument} from './settingsDocument'; import { VersionManager } from './versionManager'; import {VcpkgLogMgr} from './log'; +import {CmakeDebugger} from './cmakeDebugger'; import {VcpkgDebugger} from './vcpkgDebugger'; let logMgr : VcpkgLogMgr; @@ -13,6 +14,7 @@ let configMgr : ConfigurationManager; let verMgr : VersionManager; let vcpkgDebugger : VcpkgDebugger; let disposables: vscode.Disposable[]; +let cmakeDbg: CmakeDebugger; // This method is called when your extension is activated // Your extension is activated the very first time the command is executed @@ -23,6 +25,7 @@ export function activate(context: vscode.ExtensionContext) { verMgr = new VersionManager(); configMgr = new ConfigurationManager(/*context, */verMgr, logMgr); vcpkgDebugger = new VcpkgDebugger(logMgr); + cmakeDbg = new CmakeDebugger(vcpkgDebugger, logMgr); configMgr.logInfo('Trying to active vcpkg plugin...'); @@ -74,12 +77,51 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.debug.onDidChangeBreakpoints( session => { - vcpkgDebugger.updateConfigurations(); + configMgr.getCurrentTriplet().then(triplet => { + if (vcpkgDebugger.setDefaultTriplet(triplet)) + { + vcpkgDebugger.updateConfigurations(); + cmakeDbg.updateConfigurations(); + } + }); } - )) + )); + + context.subscriptions.push(vscode.debug.onDidStartDebugSession( + session => { + if (session.name === "Debug portfile(s)") + { + logMgr.logInfo("Starting debug portfile."); + let root : any; + if(vscode.workspace.workspaceFolders !== undefined) + { + root = vscode.workspace.workspaceFolders[0].uri.fsPath; + } + else + { + logMgr.logErr("Should not reach here to getVcpkgRealPath."); + root = configMgr.getVcpkgRealPath(); + } + configMgr.getCurrentTriplet().then(triplet => { + cmakeDbg.startDebugging(root, triplet); + }); + } + } + )); + + context.subscriptions.push(vscode.debug.onDidTerminateDebugSession( + session => { + if (session.name === "Debug portfile(s)") + { + logMgr.logInfo("Stop debug."); + cmakeDbg.stopWaitingDebug(); + } + } + )); configMgr.logInfo('All the event are registered.'); } // This method is called when your extension is deactivated export function deactivate() {} + diff --git a/src/vcpkgDebugger.ts b/src/vcpkgDebugger.ts index 19ab465..14b8f12 100644 --- a/src/vcpkgDebugger.ts +++ b/src/vcpkgDebugger.ts @@ -6,6 +6,8 @@ export class VcpkgDebugger { private tasksJsonFileName = "tasks"; private launchJsonFileName = "launch"; + private _defaultTripket = ""; + private _logMgr : VcpkgLogMgr; constructor(logMgr: VcpkgLogMgr) @@ -21,7 +23,7 @@ export class VcpkgDebugger { return this.readFromFile(this.tasksJsonFileName); } - private getModifiedPorts() + public getModifiedPorts() { let ports = new Array; let breakPoints = debug.breakpoints; @@ -78,6 +80,18 @@ export class VcpkgDebugger { } } + public setDefaultTriplet(triplet: any) + { + if (triplet === undefined || !triplet.length) { + this._logMgr.logErr("Current default triplet is empty!"); + vscode.window.showErrorMessage("Current default triplet is empty! Please manually set first."); + return false; + } + this._defaultTripket = triplet; + + return true; + } + private generateCommand() { this._logMgr.logInfo("Genereating commands."); @@ -92,7 +106,8 @@ export class VcpkgDebugger { exeSuffix = ".exe"; } - return "\"${workspaceFolder}/vcpkg" + exeSuffix + "\" remove " + modifiedPorts + " --recurse; & \"${workspaceFolder}/vcpkg" + exeSuffix + "\" install " + modifiedPorts + " --no-binarycaching --x-cmake-debug " + this.getDebuggerPipe(); + return "\"${workspaceFolder}/vcpkg" + exeSuffix + "\" remove " + modifiedPorts + " --recurse; & \"${workspaceFolder}/vcpkg"+ exeSuffix + "\" install " + modifiedPorts + + " --triplet " + this._defaultTripket + " --no-binarycaching --x-cmake-debug " + this.getDebuggerPipe(); } private async cleanConfigurations()