2023-05-04 16:55:40 +08:00
|
|
|
|
import * as fs from 'fs';
|
|
|
|
|
import * as os from 'os';
|
|
|
|
|
import * as path from 'path';
|
2023-05-31 16:10:53 +08:00
|
|
|
|
import * as childProcess from 'child_process';
|
|
|
|
|
|
2023-05-22 13:14:51 +08:00
|
|
|
|
import { parseArgsStringToArgv } from 'string-argv';
|
2023-05-04 16:55:40 +08:00
|
|
|
|
|
2023-05-22 13:14:51 +08:00
|
|
|
|
import { logger } from './logger';
|
2023-05-04 16:55:40 +08:00
|
|
|
|
import { spawn, exec } from 'child_process';
|
2023-05-31 16:10:53 +08:00
|
|
|
|
import { UiUtilWrapper } from './uiUtil';
|
2023-05-04 16:55:40 +08:00
|
|
|
|
|
|
|
|
|
export function createTempSubdirectory(subdir: string): string {
|
2023-05-22 13:14:51 +08:00
|
|
|
|
// 获取系统临时目录
|
|
|
|
|
const tempDir = os.tmpdir();
|
|
|
|
|
// 构建完整的目录路径
|
|
|
|
|
let targetDir = path.join(tempDir, subdir, Date.now().toString());
|
|
|
|
|
// 检查目录是否存在,如果存在则重新生成目录名称
|
|
|
|
|
while (fs.existsSync(targetDir)) {
|
|
|
|
|
targetDir = path.join(tempDir, subdir, Date.now().toString());
|
|
|
|
|
}
|
|
|
|
|
// 递归创建目录
|
|
|
|
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
|
|
|
// 返回创建的目录的绝对路径
|
|
|
|
|
return targetDir;
|
2023-05-04 16:55:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-06-02 09:58:44 +08:00
|
|
|
|
export interface CommandResult {
|
2023-05-22 13:14:51 +08:00
|
|
|
|
exitCode: number | null;
|
|
|
|
|
stdout: string;
|
|
|
|
|
stderr: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class CommandRun {
|
|
|
|
|
private childProcess: any;
|
|
|
|
|
|
|
|
|
|
// init childProcess in construction function
|
|
|
|
|
constructor() {
|
|
|
|
|
this.childProcess = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async spawnAsync(command: string, args: string[], options: object, onData: ((data: string) => void) | undefined, onError: ((data: string) => void) | undefined, onOutputFile: ((command: string, stdout: string, stderr: string) => string) | undefined, outputFile: string | undefined): Promise<CommandResult> {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
2023-06-02 09:58:44 +08:00
|
|
|
|
logger.channel()?.info(`Running command: ${command} ${args.join(' ')}`);
|
2023-05-22 13:14:51 +08:00
|
|
|
|
this.childProcess = spawn(command, args, options);
|
|
|
|
|
|
|
|
|
|
let stdout = '';
|
|
|
|
|
let stderr = '';
|
|
|
|
|
|
|
|
|
|
this.childProcess.stdout.on('data', (data: { toString: () => any; }) => {
|
|
|
|
|
const dataStr = data.toString();
|
|
|
|
|
if (onData) {
|
|
|
|
|
onData(dataStr);
|
|
|
|
|
}
|
|
|
|
|
stdout += dataStr;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.childProcess.stderr.on('data', (data: string) => {
|
|
|
|
|
const dataStr = data.toString();
|
|
|
|
|
if (onError) {
|
|
|
|
|
onError(dataStr);
|
|
|
|
|
}
|
|
|
|
|
stderr += dataStr;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.childProcess.on('close', (code: number) => {
|
|
|
|
|
let outputData = stdout;
|
|
|
|
|
if (onOutputFile) {
|
|
|
|
|
outputData = onOutputFile(command + " " + args.join(" "), stdout, stderr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (outputFile) {
|
|
|
|
|
fs.writeFileSync(outputFile, outputData);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-07 08:56:28 +08:00
|
|
|
|
if (stderr && !onError) {
|
2023-05-22 13:14:51 +08:00
|
|
|
|
logger.channel()?.error(stderr);
|
|
|
|
|
logger.channel()?.show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (code === 0) {
|
|
|
|
|
resolve({ exitCode: code, stdout, stderr });
|
|
|
|
|
} else {
|
2023-05-31 16:10:53 +08:00
|
|
|
|
resolve({ exitCode: code, stdout, stderr });
|
2023-05-22 13:14:51 +08:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Add error event listener to handle command not found exception
|
|
|
|
|
this.childProcess.on('error', (error: any) => {
|
2023-06-06 11:30:58 +08:00
|
|
|
|
let errorMessage = error.message;
|
2023-05-22 13:14:51 +08:00
|
|
|
|
if (error.code === 'ENOENT') {
|
2023-06-06 11:30:58 +08:00
|
|
|
|
errorMessage = `Command not found: ${command}`;
|
2023-06-13 10:45:11 +08:00
|
|
|
|
logger.channel()?.error(`Command "${command}" not found`);
|
2023-05-22 13:14:51 +08:00
|
|
|
|
logger.channel()?.show();
|
|
|
|
|
} else {
|
2023-06-13 10:45:11 +08:00
|
|
|
|
logger.channel()?.error(`Error: ${error.message}`);
|
2023-05-22 13:14:51 +08:00
|
|
|
|
logger.channel()?.show();
|
|
|
|
|
}
|
2023-06-06 11:30:58 +08:00
|
|
|
|
resolve({ exitCode: error.code, stdout: "", stderr: errorMessage });
|
2023-05-22 13:14:51 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public stop() {
|
|
|
|
|
if (this.childProcess) {
|
|
|
|
|
this.childProcess.kill();
|
|
|
|
|
this.childProcess = null;
|
2023-05-18 16:43:06 +08:00
|
|
|
|
}
|
2023-05-22 13:14:51 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function runCommandAndWriteOutput(
|
|
|
|
|
command: string,
|
|
|
|
|
args: string[],
|
2023-06-02 09:58:44 +08:00
|
|
|
|
outputFile: string | undefined
|
2023-05-22 13:14:51 +08:00
|
|
|
|
): Promise<CommandResult> {
|
|
|
|
|
const run = new CommandRun();
|
|
|
|
|
const options = {
|
2023-05-31 16:10:53 +08:00
|
|
|
|
cwd: UiUtilWrapper.workspaceFoldersFirstPath() || '.',
|
2023-05-22 13:14:51 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return run.spawnAsync(command, args, options, undefined, undefined, undefined, outputFile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function runCommandStringAndWriteOutput(
|
2023-05-04 16:55:40 +08:00
|
|
|
|
commandString: string,
|
|
|
|
|
outputFile: string
|
2023-05-22 13:14:51 +08:00
|
|
|
|
): Promise<CommandResult> {
|
|
|
|
|
const run = new CommandRun();
|
|
|
|
|
const options = {
|
2023-05-31 16:10:53 +08:00
|
|
|
|
cwd: UiUtilWrapper.workspaceFoldersFirstPath() || '.'
|
2023-05-22 13:14:51 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Split the commandString into command and args array using string-argv
|
|
|
|
|
const commandParts = parseArgsStringToArgv(commandString);
|
|
|
|
|
const command = commandParts[0];
|
|
|
|
|
const args = commandParts.slice(1);
|
|
|
|
|
|
|
|
|
|
const onOutputFile = (command: string, stdout: string, stderr: string): string => {
|
|
|
|
|
const data = {
|
|
|
|
|
command: commandString,
|
|
|
|
|
content: stdout,
|
|
|
|
|
};
|
|
|
|
|
return JSON.stringify(data);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return run.spawnAsync(command, args, options, undefined, undefined, onOutputFile, outputFile);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-02 09:58:44 +08:00
|
|
|
|
export async function runCommandStringArrayAndWriteOutput(
|
|
|
|
|
commandStringList: string[],
|
|
|
|
|
outputFile: string
|
|
|
|
|
): Promise<CommandResult> {
|
|
|
|
|
const run = new CommandRun();
|
|
|
|
|
const options = {
|
|
|
|
|
cwd: UiUtilWrapper.workspaceFoldersFirstPath() || '.'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const commandString = commandStringList[0];
|
|
|
|
|
const args: string[] = commandStringList.slice(1);
|
|
|
|
|
const onOutputFile = (command: string, stdout: string, stderr: string): string => {
|
|
|
|
|
const data = {
|
|
|
|
|
command: commandString,
|
|
|
|
|
content: stdout,
|
|
|
|
|
};
|
|
|
|
|
return JSON.stringify(data);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return run.spawnAsync(commandString, args, options, undefined, undefined, onOutputFile, outputFile);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-22 13:14:51 +08:00
|
|
|
|
export async function getLanguageIdByFileName(fileName: string): Promise<string | undefined> {
|
|
|
|
|
try {
|
2023-05-31 16:10:53 +08:00
|
|
|
|
const languageId = await UiUtilWrapper.languageId(fileName);
|
2023-05-22 13:14:51 +08:00
|
|
|
|
return languageId;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// 如果无法打开文件或发生其他错误,返回undefined
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
2023-05-31 16:10:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function runCommand(command: string): string {
|
|
|
|
|
return childProcess.execSync(command).toString();
|
2023-06-07 08:16:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function runCommandStringAndWriteOutputSync(command: string, outputFile: string): CommandResult {
|
|
|
|
|
try {
|
|
|
|
|
const options = {
|
|
|
|
|
cwd: UiUtilWrapper.workspaceFoldersFirstPath() || '.'
|
|
|
|
|
};
|
|
|
|
|
const output = childProcess.execSync(command, options).toString();
|
|
|
|
|
const onOutputFile = (command: string, stdout: string): string => {
|
|
|
|
|
const data = {
|
|
|
|
|
"command": command,
|
|
|
|
|
"content": stdout,
|
|
|
|
|
};
|
|
|
|
|
return JSON.stringify(data);
|
|
|
|
|
};
|
|
|
|
|
fs.writeFileSync(outputFile, onOutputFile(command, output));
|
|
|
|
|
return { exitCode: 0, stdout: output, stderr: '' }
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.channel()?.error(`Error occurred: ${error}`);
|
|
|
|
|
logger.channel()?.show();
|
|
|
|
|
return { exitCode: 1, stdout: '', stderr: String(error) }
|
|
|
|
|
}
|
2023-05-22 13:14:51 +08:00
|
|
|
|
}
|