实现对于 vivado 的关闭

This commit is contained in:
锦恢 2025-01-06 14:13:44 +08:00
parent 991686cc00
commit 25a52bc547
11 changed files with 157 additions and 30 deletions

View File

@ -110,5 +110,6 @@
"toolbar.save-as-svg": "Aktuelle Ansicht als SVG speichern", "toolbar.save-as-svg": "Aktuelle Ansicht als SVG speichern",
"toolbar.save-as-pdf": "Aktuelle Ansicht als PDF speichern", "toolbar.save-as-pdf": "Aktuelle Ansicht als PDF speichern",
"pdf-file": "PDF-Datei", "pdf-file": "PDF-Datei",
"export-pdf": "PDF wird exportiert" "export-pdf": "PDF wird exportiert",
"info.process-killed": "Prozess {0} wurde zerstört"
} }

View File

@ -110,5 +110,6 @@
"toolbar.save-as-svg": "Save current view as SVG", "toolbar.save-as-svg": "Save current view as SVG",
"toolbar.save-as-pdf": "Save current view as PDF", "toolbar.save-as-pdf": "Save current view as PDF",
"pdf-file": "PDF file", "pdf-file": "PDF file",
"export-pdf": "Exporting PDF" "export-pdf": "Exporting PDF",
"info.process-killed": "Process {0} has been destroyed"
} }

View File

@ -110,5 +110,6 @@
"toolbar.save-as-svg": "現在のビューをSVGとして保存", "toolbar.save-as-svg": "現在のビューをSVGとして保存",
"toolbar.save-as-pdf": "現在のビューをPDFとして保存", "toolbar.save-as-pdf": "現在のビューをPDFとして保存",
"pdf-file": "PDFファイル", "pdf-file": "PDFファイル",
"export-pdf": "PDFをエクスポート中" "export-pdf": "PDFをエクスポート中",
"info.process-killed": "プロセス {0} は破棄されました"
} }

View File

@ -110,5 +110,6 @@
"toolbar.save-as-svg": "将当前视图保存为 svg", "toolbar.save-as-svg": "将当前视图保存为 svg",
"toolbar.save-as-pdf": "将当前视图保存为 pdf", "toolbar.save-as-pdf": "将当前视图保存为 pdf",
"pdf-file": "pdf 文件", "pdf-file": "pdf 文件",
"export-pdf": "正在导出 pdf" "export-pdf": "正在导出 pdf",
"info.process-killed": "进程 {0} 已经被销毁"
} }

View File

@ -110,5 +110,6 @@
"toolbar.save-as-svg": "將目前視圖儲存為SVG", "toolbar.save-as-svg": "將目前視圖儲存為SVG",
"toolbar.save-as-pdf": "將目前視圖儲存為PDF", "toolbar.save-as-pdf": "將目前視圖儲存為PDF",
"pdf-file": "PDF檔案", "pdf-file": "PDF檔案",
"export-pdf": "正在匯出PDF" "export-pdf": "正在匯出PDF",
"info.process-killed": "進程 {0} 已經被銷毀"
} }

View File

@ -93,7 +93,7 @@
"digital-ide.function.lsp.file-parse-maxsize.title": "最大解析的文件阈值,大小超出这个值的文件不会被解析。单位为 MB必须是整数。默认为 1MB", "digital-ide.function.lsp.file-parse-maxsize.title": "最大解析的文件阈值,大小超出这个值的文件不会被解析。单位为 MB必须是整数。默认为 1MB",
"digital-ide.structure.from-xilinx-to-standard.title": "将 Xilinx 项目转换成 Digital IDE 标准项目结构", "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.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.title": "指定诊断器的诊断模式",
"digital-ide.function.lsp.linter.mode.0.title": "将所有设计源直接进行诊断,并报错,无论文件是否打开。", "digital-ide.function.lsp.linter.mode.0.title": "将所有设计源直接进行诊断,并报错,无论文件是否打开。",
"digital-ide.function.lsp.linter.mode.1.title": "单文件关闭时,对应报错去除,打开哪个文件就对哪个文件进行诊断。", "digital-ide.function.lsp.linter.mode.1.title": "单文件关闭时,对应报错去除,打开哪个文件就对哪个文件进行诊断。",

View File

@ -93,7 +93,7 @@
"digital-ide.function.lsp.file-parse-maxsize.title": "", "digital-ide.function.lsp.file-parse-maxsize.title": "",
"digital-ide.structure.from-xilinx-to-standard.title": "將 Xilinx 專案轉換成 Digital IDE 標準專案結構", "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.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.title": "指定診斷器的診斷模式",
"digital-ide.function.lsp.linter.mode.0.title": "將所有設計源直接進行診斷,並報錯,無論文件是否打開。", "digital-ide.function.lsp.linter.mode.0.title": "將所有設計源直接進行診斷,並報錯,無論文件是否打開。",
"digital-ide.function.lsp.linter.mode.1.title": "單文件關閉時,對應報錯去除,打開哪個文件就對哪個文件進行診斷。", "digital-ide.function.lsp.linter.mode.1.title": "單文件關閉時,對應報錯去除,打開哪個文件就對哪個文件進行診斷。",

View File

@ -138,4 +138,5 @@ export function activate(context: vscode.ExtensionContext) {
export function deactivate() { export function deactivate() {
lspClient.deactivate(); lspClient.deactivate();
manager.prjManage.pl?.exit();
} }

View File

@ -3,7 +3,8 @@ import * as os from 'os';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as childProcess from 'child_process'; import * as childProcess from 'child_process';
import { AbsPath } from "."; import { AbsPath, MainOutput } from ".";
import { t } from '../i18n';
export class PathSet { export class PathSet {
files: Set<AbsPath> = new Set<AbsPath>(); files: Set<AbsPath> = new Set<AbsPath>();
@ -160,4 +161,94 @@ export function getPlatformPlatformSignature(): IPlatformSignature {
default: return IPlatformSignature.unsupport; default: return IPlatformSignature.unsupport;
} }
}
/**
* @description {name} pid
* @returns
*/
export function getPIDsWithName(name: string): Promise<number[]> {
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<void> {
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();
});
});
} }

View File

@ -3,6 +3,8 @@
* Hardware Programming * Hardware Programming
*/ */
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { exec } from 'child_process';
import { platform } from 'os';
import { PLContext, XilinxOperation } from './xilinx'; import { PLContext, XilinxOperation } from './xilinx';
import { BaseManage } from '../common'; import { BaseManage } from '../common';
@ -89,16 +91,12 @@ class PlManage extends BaseManage {
this.context.ope.gui(this.context); this.context.ope.gui(this.context);
} }
public exit() { public async exit() {
if (this.context.process === undefined) { if (this.context.process === undefined) {
return; return;
} }
HardwareOutput.show();
HardwareOutput.show(); this.context.ope.exit(this.context);
this.context.process.stdin.write('exit\n');
HardwareOutput.report(t('info.pl.exit.title'));
this.context.process = undefined;
} }
@ -199,4 +197,4 @@ class PlManage extends BaseManage {
export { export {
PlManage, PlManage,
}; };

View File

@ -11,7 +11,7 @@ import { PropertySchema } from '../../global/propertySchema';
import { XilinxIP } from '../../global/enum'; import { XilinxIP } from '../../global/enum';
import { HardwareOutput, MainOutput, ReportType } from '../../global/outputChannel'; import { HardwareOutput, MainOutput, ReportType } from '../../global/outputChannel';
import { debounce } from '../../global/util'; import { debounce, getPIDsWithName, killProcess } from '../../global/util';
import { t } from '../../i18n'; import { t } from '../../i18n';
import { HdlFileProjectType } from '../../hdlParser/common'; import { HdlFileProjectType } from '../../hdlParser/common';
@ -60,8 +60,10 @@ interface BootInfo {
*/ */
class XilinxOperation { class XilinxOperation {
guiLaunched: boolean; guiLaunched: boolean;
guiPid: number;
constructor() { constructor() {
this.guiLaunched = false; this.guiLaunched = false;
this.guiPid = -1;
} }
public get xipRepo(): XilinxIP[] { public get xipRepo(): XilinxIP[] {
@ -142,8 +144,9 @@ class XilinxOperation {
*/ */
public async launch(context: PLContext): Promise<string | undefined> { public async launch(context: PLContext): Promise<string | undefined> {
this.guiLaunched = false; this.guiLaunched = false;
let scripts: string[] = []; this.guiPid = -1;
let scripts: string[] = [];
let prjFilePath = this.prjPath as AbsPath; let prjFilePath = this.prjPath as AbsPath;
// 找到所有的 xilinx 工程文件 // 找到所有的 xilinx 工程文件
const prjFiles = hdlFile.pickFileRecursive(prjFilePath, const prjFiles = hdlFile.pickFileRecursive(prjFilePath,
@ -188,11 +191,12 @@ class XilinxOperation {
_this.onVivadoClose(); _this.onVivadoClose();
}, 100); }, 100);
function launchScript(): Promise<ChildProcessWithoutNullStreams | undefined> { function launchScript(pids: number[]): Promise<ChildProcessWithoutNullStreams | undefined> {
if (!opeParam.workspacePath) { if (!opeParam.workspacePath) {
return Promise.resolve(undefined); return Promise.resolve(undefined);
} }
// 执行 cmd 启动
const vivadoPids = new Set<number>(pids);
const vivadoProcess = spawn(cmd, [], { shell: true, stdio: 'pipe', cwd: opeParam.workspacePath }); const vivadoProcess = spawn(cmd, [], { shell: true, stdio: 'pipe', cwd: opeParam.workspacePath });
let status: 'pending' | 'fulfilled' = 'pending'; let status: 'pending' | 'fulfilled' = 'pending';
@ -207,11 +211,16 @@ class XilinxOperation {
}); });
return new Promise(resolve => { return new Promise(resolve => {
vivadoProcess.stdout.on('data', data => { vivadoProcess.stdout.on('data', async data => {
const message: string = _this.handleMessage(data.toString(), status); const message: string = _this.handleMessage(data.toString(), status);
if (status === 'pending') { if (status === 'pending') {
HardwareOutput.clear(); HardwareOutput.clear();
HardwareOutput.show(); HardwareOutput.show();
const pids = await getPIDsWithName('vivado');
const newPid = pids.find(p => !vivadoPids.has(p));
if (newPid) {
_this.guiPid = newPid;
}
resolve(vivadoProcess); resolve(vivadoProcess);
} }
HardwareOutput.report(message, { HardwareOutput.report(message, {
@ -251,7 +260,8 @@ class XilinxOperation {
location: vscode.ProgressLocation.Notification, location: vscode.ProgressLocation.Notification,
cancellable: true cancellable: true
}, async () => { }, async () => {
return await launchScript(); const originVivadoPids = await getPIDsWithName('vivado');
return await launchScript(originVivadoPids);
}); });
context.process = process; context.process = process;
@ -274,7 +284,7 @@ class XilinxOperation {
} }
} }
private onVivadoClose() { private async onVivadoClose() {
const workspacePath = opeParam.workspacePath; const workspacePath = opeParam.workspacePath;
const plName = opeParam.prjInfo.prjName.PL; const plName = opeParam.prjInfo.prjName.PL;
const targetPath = fspath.dirname(opeParam.prjInfo.arch.hardware.src); const targetPath = fspath.dirname(opeParam.prjInfo.arch.hardware.src);
@ -287,6 +297,8 @@ class XilinxOperation {
hdlDir.mvdir(sourceBdPath, targetPath, true); hdlDir.mvdir(sourceBdPath, targetPath, true);
HardwareOutput.report("move dir from " + sourceBdPath + " to " + targetPath); HardwareOutput.report("move dir from " + sourceBdPath + " to " + targetPath);
await this.closeAllWindows();
} }
public create(scripts: string[]) { public create(scripts: string[]) {
@ -436,6 +448,22 @@ class XilinxOperation {
context.process?.stdin.write(cmd + '\n'); 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) { public simulate(context: PLContext) {
this.simulateCli(context); this.simulateCli(context);
} }
@ -630,14 +658,18 @@ file delete ${scriptPath} -force\n`;
await this.launch(context); await this.launch(context);
} }
if (context.process) { const tclProcess = context.process;
context.process.stdin.write('start_gui -quiet\n'); if (tclProcess === undefined) {
HardwareOutput.report(t('info.pl.gui.report-title'), { return;
level: ReportType.Info
});
HardwareOutput.show();
this.guiLaunched = true;
} }
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) { public addFiles(files: string[], context: PLContext) {