From 25a52bc5479194a60da26d8e15ec414f5965b294 Mon Sep 17 00:00:00 2001 From: Kirigaya <1193466151@qq.com> Date: Mon, 6 Jan 2025 14:13:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=AF=B9=E4=BA=8E=20vivado?= =?UTF-8?q?=20=E7=9A=84=E5=85=B3=E9=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- l10n/bundle.l10n.de.json | 3 +- l10n/bundle.l10n.en.json | 3 +- l10n/bundle.l10n.ja.json | 3 +- l10n/bundle.l10n.zh-cn.json | 3 +- l10n/bundle.l10n.zh-tw.json | 3 +- package.nls.zh-cn.json | 2 +- package.nls.zh-tw.json | 2 +- src/extension.ts | 1 + src/global/util.ts | 93 ++++++++++++++++++++++++++++++++++++- src/manager/PL/index.ts | 14 +++--- src/manager/PL/xilinx.ts | 60 ++++++++++++++++++------ 11 files changed, 157 insertions(+), 30 deletions(-) diff --git a/l10n/bundle.l10n.de.json b/l10n/bundle.l10n.de.json index bc0cd8a..ffcb9fe 100644 --- a/l10n/bundle.l10n.de.json +++ b/l10n/bundle.l10n.de.json @@ -110,5 +110,6 @@ "toolbar.save-as-svg": "Aktuelle Ansicht als SVG speichern", "toolbar.save-as-pdf": "Aktuelle Ansicht als PDF speichern", "pdf-file": "PDF-Datei", - "export-pdf": "PDF wird exportiert" + "export-pdf": "PDF wird exportiert", + "info.process-killed": "Prozess {0} wurde zerstört" } \ No newline at end of file diff --git a/l10n/bundle.l10n.en.json b/l10n/bundle.l10n.en.json index 6f44ac0..6d8f114 100644 --- a/l10n/bundle.l10n.en.json +++ b/l10n/bundle.l10n.en.json @@ -110,5 +110,6 @@ "toolbar.save-as-svg": "Save current view as SVG", "toolbar.save-as-pdf": "Save current view as PDF", "pdf-file": "PDF file", - "export-pdf": "Exporting PDF" + "export-pdf": "Exporting PDF", + "info.process-killed": "Process {0} has been destroyed" } \ No newline at end of file diff --git a/l10n/bundle.l10n.ja.json b/l10n/bundle.l10n.ja.json index eb55965..2f12f40 100644 --- a/l10n/bundle.l10n.ja.json +++ b/l10n/bundle.l10n.ja.json @@ -110,5 +110,6 @@ "toolbar.save-as-svg": "現在のビューをSVGとして保存", "toolbar.save-as-pdf": "現在のビューをPDFとして保存", "pdf-file": "PDFファイル", - "export-pdf": "PDFをエクスポート中" + "export-pdf": "PDFをエクスポート中", + "info.process-killed": "プロセス {0} は破棄されました" } \ No newline at end of file diff --git a/l10n/bundle.l10n.zh-cn.json b/l10n/bundle.l10n.zh-cn.json index 019e12b..f28c4d0 100644 --- a/l10n/bundle.l10n.zh-cn.json +++ b/l10n/bundle.l10n.zh-cn.json @@ -110,5 +110,6 @@ "toolbar.save-as-svg": "将当前视图保存为 svg", "toolbar.save-as-pdf": "将当前视图保存为 pdf", "pdf-file": "pdf 文件", - "export-pdf": "正在导出 pdf" + "export-pdf": "正在导出 pdf", + "info.process-killed": "进程 {0} 已经被销毁" } \ No newline at end of file diff --git a/l10n/bundle.l10n.zh-tw.json b/l10n/bundle.l10n.zh-tw.json index 88210b8..7c3ba19 100644 --- a/l10n/bundle.l10n.zh-tw.json +++ b/l10n/bundle.l10n.zh-tw.json @@ -110,5 +110,6 @@ "toolbar.save-as-svg": "將目前視圖儲存為SVG", "toolbar.save-as-pdf": "將目前視圖儲存為PDF", "pdf-file": "PDF檔案", - "export-pdf": "正在匯出PDF" + "export-pdf": "正在匯出PDF", + "info.process-killed": "進程 {0} 已經被銷毀" } \ No newline at end of file diff --git a/package.nls.zh-cn.json b/package.nls.zh-cn.json index 5a0275e..fa5948d 100644 --- a/package.nls.zh-cn.json +++ b/package.nls.zh-cn.json @@ -93,7 +93,7 @@ "digital-ide.function.lsp.file-parse-maxsize.title": "最大解析的文件阈值,大小超出这个值的文件不会被解析。单位为 MB,必须是整数。默认为 1MB", "digital-ide.structure.from-xilinx-to-standard.title": "将 Xilinx 项目转换成 Digital IDE 标准项目结构", "digital-ide.prj.verible.install.path.title": "verible 的安装目录路径,也就是包含 verible-verilog-syntax 可执行文件的文件夹的绝对路径。如果不指定,默认采用 verible-verilog-syntax 执行诊断。", - "digital-ide.prj.verilator.install.path.title": "verilator 的安装目录路径,也就是包含了 verilator 可执行文件的文件夹的绝对路径。不如不指定,默认采用 verilator 执行诊断。", + "digital-ide.prj.verilator.install.path.title": "verilator 的安装目录路径,也就是包含了 verilator 可执行文件的文件夹的绝对路径。如果不指定,默认采用 verilator 执行诊断。", "digital-ide.function.lsp.linter.mode.title": "指定诊断器的诊断模式", "digital-ide.function.lsp.linter.mode.0.title": "将所有设计源直接进行诊断,并报错,无论文件是否打开。", "digital-ide.function.lsp.linter.mode.1.title": "单文件关闭时,对应报错去除,打开哪个文件就对哪个文件进行诊断。", diff --git a/package.nls.zh-tw.json b/package.nls.zh-tw.json index 89c0f2f..410a920 100644 --- a/package.nls.zh-tw.json +++ b/package.nls.zh-tw.json @@ -93,7 +93,7 @@ "digital-ide.function.lsp.file-parse-maxsize.title": "", "digital-ide.structure.from-xilinx-to-standard.title": "將 Xilinx 專案轉換成 Digital IDE 標準專案結構", "digital-ide.prj.verible.install.path.title": "verible 的安裝目錄路徑,也就是包含 verible-verilog-syntax 可執行文件的文件夾的絕對路徑。如果不指定,默認採用 verible-verilog-syntax 執行診斷。", - "digital-ide.prj.verilator.install.path.title": "verilator 的安裝目錄路徑,也就是包含了 verilator 可執行文件的文件夾的絕對路徑。不如不指定,默認採用 verilator 執行診斷。", + "digital-ide.prj.verilator.install.path.title": "verilator 的安裝目錄路徑,也就是包含了 verilator 可執行文件的文件夾的絕對路徑。如果不指定,默認採用 verilator 執行診斷。", "digital-ide.function.lsp.linter.mode.title": "指定診斷器的診斷模式", "digital-ide.function.lsp.linter.mode.0.title": "將所有設計源直接進行診斷,並報錯,無論文件是否打開。", "digital-ide.function.lsp.linter.mode.1.title": "單文件關閉時,對應報錯去除,打開哪個文件就對哪個文件進行診斷。", diff --git a/src/extension.ts b/src/extension.ts index 85fe56c..32c5a9b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -138,4 +138,5 @@ export function activate(context: vscode.ExtensionContext) { export function deactivate() { lspClient.deactivate(); + manager.prjManage.pl?.exit(); } \ No newline at end of file diff --git a/src/global/util.ts b/src/global/util.ts index e2f32d2..0f5c03c 100644 --- a/src/global/util.ts +++ b/src/global/util.ts @@ -3,7 +3,8 @@ import * as os from 'os'; import * as vscode from 'vscode'; import * as childProcess from 'child_process'; -import { AbsPath } from "."; +import { AbsPath, MainOutput } from "."; +import { t } from '../i18n'; export class PathSet { files: Set = new Set(); @@ -160,4 +161,94 @@ export function getPlatformPlatformSignature(): IPlatformSignature { default: return IPlatformSignature.unsupport; } +} + +/** + * @description 获取包含 {name} 的所有 pid + * @returns + */ +export function getPIDsWithName(name: string): Promise { + return new Promise((resolve, reject) => { + let command: string; + let parseOutput: (output: string) => number[]; + + const currentPlatform = os.platform(); + if (currentPlatform === 'win32') { + // Windows 使用 tasklist 命令 + command = `tasklist /FI "IMAGENAME eq ${name}*" /FO CSV /NH`; + parseOutput = (output: string) => { + return output + .split('\n') + .filter(line => line.includes(name)) + .map(line => { + const [imageName, pid] = line.split('","'); + return parseInt(pid.replace(/"/g, ''), 10); + }); + }; + } else { + // macOS 和 Linux 使用 ps 命令 + command = 'ps -e -o pid,comm='; + parseOutput = (output: string) => { + return output + .split('\n') + .filter(line => line.includes(name)) + .map(line => { + const [pid] = line.trim().split(' '); + return parseInt(pid, 10); + }); + }; + } + + // 执行命令 + childProcess.exec(command, (error, stdout, stderr) => { + if (error) { + reject(`Error: ${error.message}`); + return; + } + + if (stderr) { + reject(`Stderr: ${stderr}`); + return; + } + + // 解析输出并返回进程号 + const processes = parseOutput(stdout); + resolve(processes); + }); + }); +} + +export function killProcess(pid: number): Promise { + return new Promise((resolve, reject) => { + let command: string; + + const currentPlatform = os.platform(); + + if (currentPlatform === 'win32') { + // Windows 使用 taskkill 命令 + command = `taskkill /PID ${pid} /F`; + } else { + // macOS 和 Linux 使用 kill 命令 + command = `kill -9 ${pid}`; + } + + // 执行命令 + childProcess.exec(command, (error, stdout, stderr) => { + if (error) { + reject(`Error: ${error.message}`); + return; + } + + if (stderr) { + reject(`Stderr: ${stderr}`); + return; + } + const message = t('info.process-killed', pid.toString()); + + MainOutput.report(message); + console.log(message); + + resolve(); + }); + }); } \ No newline at end of file diff --git a/src/manager/PL/index.ts b/src/manager/PL/index.ts index eed32e0..d6feece 100644 --- a/src/manager/PL/index.ts +++ b/src/manager/PL/index.ts @@ -3,6 +3,8 @@ * Hardware Programming */ import * as vscode from 'vscode'; +import { exec } from 'child_process'; +import { platform } from 'os'; import { PLContext, XilinxOperation } from './xilinx'; import { BaseManage } from '../common'; @@ -89,16 +91,12 @@ class PlManage extends BaseManage { this.context.ope.gui(this.context); } - public exit() { - + public async exit() { if (this.context.process === undefined) { return; } - - HardwareOutput.show(); - this.context.process.stdin.write('exit\n'); - HardwareOutput.report(t('info.pl.exit.title')); - this.context.process = undefined; + HardwareOutput.show(); + this.context.ope.exit(this.context); } @@ -199,4 +197,4 @@ class PlManage extends BaseManage { export { PlManage, -}; \ No newline at end of file +}; diff --git a/src/manager/PL/xilinx.ts b/src/manager/PL/xilinx.ts index 269f60a..a826ed7 100644 --- a/src/manager/PL/xilinx.ts +++ b/src/manager/PL/xilinx.ts @@ -11,7 +11,7 @@ import { PropertySchema } from '../../global/propertySchema'; import { XilinxIP } from '../../global/enum'; import { HardwareOutput, MainOutput, ReportType } from '../../global/outputChannel'; -import { debounce } from '../../global/util'; +import { debounce, getPIDsWithName, killProcess } from '../../global/util'; import { t } from '../../i18n'; import { HdlFileProjectType } from '../../hdlParser/common'; @@ -60,8 +60,10 @@ interface BootInfo { */ class XilinxOperation { guiLaunched: boolean; + guiPid: number; constructor() { this.guiLaunched = false; + this.guiPid = -1; } public get xipRepo(): XilinxIP[] { @@ -142,8 +144,9 @@ class XilinxOperation { */ public async launch(context: PLContext): Promise { this.guiLaunched = false; - let scripts: string[] = []; + this.guiPid = -1; + let scripts: string[] = []; let prjFilePath = this.prjPath as AbsPath; // 找到所有的 xilinx 工程文件 const prjFiles = hdlFile.pickFileRecursive(prjFilePath, @@ -188,11 +191,12 @@ class XilinxOperation { _this.onVivadoClose(); }, 100); - function launchScript(): Promise { + function launchScript(pids: number[]): Promise { if (!opeParam.workspacePath) { return Promise.resolve(undefined); } - // 执行 cmd 启动 + + const vivadoPids = new Set(pids); const vivadoProcess = spawn(cmd, [], { shell: true, stdio: 'pipe', cwd: opeParam.workspacePath }); let status: 'pending' | 'fulfilled' = 'pending'; @@ -207,11 +211,16 @@ class XilinxOperation { }); return new Promise(resolve => { - vivadoProcess.stdout.on('data', data => { + vivadoProcess.stdout.on('data', async data => { const message: string = _this.handleMessage(data.toString(), status); if (status === 'pending') { HardwareOutput.clear(); HardwareOutput.show(); + const pids = await getPIDsWithName('vivado'); + const newPid = pids.find(p => !vivadoPids.has(p)); + if (newPid) { + _this.guiPid = newPid; + } resolve(vivadoProcess); } HardwareOutput.report(message, { @@ -251,7 +260,8 @@ class XilinxOperation { location: vscode.ProgressLocation.Notification, cancellable: true }, async () => { - return await launchScript(); + const originVivadoPids = await getPIDsWithName('vivado'); + return await launchScript(originVivadoPids); }); context.process = process; @@ -274,7 +284,7 @@ class XilinxOperation { } } - private onVivadoClose() { + private async onVivadoClose() { const workspacePath = opeParam.workspacePath; const plName = opeParam.prjInfo.prjName.PL; const targetPath = fspath.dirname(opeParam.prjInfo.arch.hardware.src); @@ -287,6 +297,8 @@ class XilinxOperation { hdlDir.mvdir(sourceBdPath, targetPath, true); HardwareOutput.report("move dir from " + sourceBdPath + " to " + targetPath); + + await this.closeAllWindows(); } public create(scripts: string[]) { @@ -436,6 +448,22 @@ class XilinxOperation { context.process?.stdin.write(cmd + '\n'); } + public async closeAllWindows() { + if (this.guiPid > 0) { + await killProcess(this.guiPid); + } + + const srcscannerPids = await getPIDsWithName('srcscanner'); + for (const pid of srcscannerPids) { + await killProcess(pid); + } + } + + public async exit(context: PLContext) { + context.process?.stdin.write('exit' + '\n'); + await this.closeAllWindows(); + } + public simulate(context: PLContext) { this.simulateCli(context); } @@ -630,14 +658,18 @@ file delete ${scriptPath} -force\n`; await this.launch(context); } - if (context.process) { - context.process.stdin.write('start_gui -quiet\n'); - HardwareOutput.report(t('info.pl.gui.report-title'), { - level: ReportType.Info - }); - HardwareOutput.show(); - this.guiLaunched = true; + const tclProcess = context.process; + if (tclProcess === undefined) { + return; } + + tclProcess.stdin.write('start_gui -quiet\n'); + HardwareOutput.report(t('info.pl.gui.report-title'), { + level: ReportType.Info + }); + + HardwareOutput.show(); + this.guiLaunched = true; } public addFiles(files: string[], context: PLContext) {