功能基本可用
This commit is contained in:
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "fmd-c-compiler",
|
"name": "fmd-c-compiler",
|
||||||
"version": "0.1.0",
|
"version": "0.2.8",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "fmd-c-compiler",
|
"name": "fmd-c-compiler",
|
||||||
"version": "0.1.0",
|
"version": "0.2.8",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.0.0",
|
"@types/node": "^20.0.0",
|
||||||
"@types/vscode": "^1.85.0",
|
"@types/vscode": "^1.85.0",
|
||||||
|
|||||||
+23
-3
@@ -2,7 +2,7 @@
|
|||||||
"name": "fmd-c-compiler",
|
"name": "fmd-c-compiler",
|
||||||
"displayName": "FMD C Compiler",
|
"displayName": "FMD C Compiler",
|
||||||
"description": "FMD/FT61FC6X 系列 MCU 编译器支持(C.exe 工具链)",
|
"description": "FMD/FT61FC6X 系列 MCU 编译器支持(C.exe 工具链)",
|
||||||
"version": "0.2.0",
|
"version": "0.2.8",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"icon": "resources/icon.png",
|
"icon": "resources/icon.png",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -78,6 +78,11 @@
|
|||||||
{
|
{
|
||||||
"command": "fmdCompiler.exportEepromHex",
|
"command": "fmdCompiler.exportEepromHex",
|
||||||
"title": "FMD: Export EEPROM HEX"
|
"title": "FMD: Export EEPROM HEX"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "fmdCompiler.regenerateConfig",
|
||||||
|
"title": "FMD: Regenerate VS Code Config",
|
||||||
|
"icon": "$(gear)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"keybindings": [
|
"keybindings": [
|
||||||
@@ -98,6 +103,11 @@
|
|||||||
"command": "fmdCompiler.download",
|
"command": "fmdCompiler.download",
|
||||||
"when": "resourceExtname =~ /\\.[cChH]$/",
|
"when": "resourceExtname =~ /\\.[cChH]$/",
|
||||||
"group": "navigation"
|
"group": "navigation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "fmdCompiler.regenerateConfig",
|
||||||
|
"when": "resourceExtname =~ /\\.[cChH]$/",
|
||||||
|
"group": "navigation"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"editor/context": [
|
"editor/context": [
|
||||||
@@ -125,6 +135,11 @@
|
|||||||
"command": "fmdCompiler.openEeprom",
|
"command": "fmdCompiler.openEeprom",
|
||||||
"when": "resourceExtname =~ /\\.[cChH]$/",
|
"when": "resourceExtname =~ /\\.[cChH]$/",
|
||||||
"group": "fmd@5"
|
"group": "fmd@5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "fmdCompiler.regenerateConfig",
|
||||||
|
"when": "resourceExtname =~ /\\.[cChH]$/",
|
||||||
|
"group": "fmd@6"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"explorer/context": [
|
"explorer/context": [
|
||||||
@@ -152,6 +167,11 @@
|
|||||||
"command": "fmdCompiler.openEeprom",
|
"command": "fmdCompiler.openEeprom",
|
||||||
"when": "resourceExtname == '.prj' || resourceExtname == '.hex'",
|
"when": "resourceExtname == '.prj' || resourceExtname == '.hex'",
|
||||||
"group": "fmd@5"
|
"group": "fmd@5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "fmdCompiler.regenerateConfig",
|
||||||
|
"when": "resourceExtname == '.prj' || resourceExtname =~ /\\.[cChH]$/",
|
||||||
|
"group": "fmd@6"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -186,8 +206,8 @@
|
|||||||
},
|
},
|
||||||
"fmdCompiler.outputDir": {
|
"fmdCompiler.outputDir": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "build",
|
||||||
"description": "输出目录(留空则与工程同目录)"
|
"description": "输出目录。默认 build 会输出到工程目录下的 build 文件夹;留空则与工程同目录;也可填写绝对路径。"
|
||||||
},
|
},
|
||||||
"fmdCompiler.extraArgs": {
|
"fmdCompiler.extraArgs": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
+10
-2
@@ -70,7 +70,8 @@ export class FmdCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const projectName = projectInfo?.projectName || path.basename(projectDir);
|
const projectName = projectInfo?.projectName || path.basename(projectDir);
|
||||||
const outputDir = cfg.outputDir || projectDir;
|
const outputDir = this.resolveOutputDir(projectDir, cfg.outputDir);
|
||||||
|
fs.mkdirSync(outputDir, { recursive: true });
|
||||||
const artifacts = this.getOutputArtifacts(projectDir, projectName, outputDir);
|
const artifacts = this.getOutputArtifacts(projectDir, projectName, outputDir);
|
||||||
|
|
||||||
this.building = true;
|
this.building = true;
|
||||||
@@ -229,7 +230,7 @@ export class FmdCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const projectName = projectInfo?.projectName || path.basename(projectDir);
|
const projectName = projectInfo?.projectName || path.basename(projectDir);
|
||||||
const outputDir = cfg.outputDir || projectDir;
|
const outputDir = this.resolveOutputDir(projectDir, cfg.outputDir);
|
||||||
return this.getOutputArtifacts(projectDir, projectName, outputDir);
|
return this.getOutputArtifacts(projectDir, projectName, outputDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,6 +244,13 @@ export class FmdCompiler {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private resolveOutputDir(projectDir: string, outputDir: string): string {
|
||||||
|
if (!outputDir) {
|
||||||
|
return projectDir;
|
||||||
|
}
|
||||||
|
return path.isAbsolute(outputDir) ? outputDir : path.join(projectDir, outputDir);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造编译参数
|
* 构造编译参数
|
||||||
* 基于对 .map 文件的分析,c.exe 是 XC8-style 驱动器
|
* 基于对 .map 文件的分析,c.exe 是 XC8-style 驱动器
|
||||||
|
|||||||
+366
-4
@@ -36,13 +36,16 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
vscode.commands.registerCommand('fmdCompiler.clean', () => compiler.cleanProject()),
|
vscode.commands.registerCommand('fmdCompiler.clean', () => compiler.cleanProject()),
|
||||||
vscode.commands.registerCommand('fmdCompiler.selectProject', (uri?: vscode.Uri) => {
|
vscode.commands.registerCommand('fmdCompiler.selectProject', async (uri?: vscode.Uri) => {
|
||||||
if (uri) {
|
if (uri) {
|
||||||
projectManager.setProjectFile(uri.fsPath);
|
projectManager.setProjectFile(uri.fsPath);
|
||||||
vscode.window.showInformationMessage(`已选择工程: ${path.basename(uri.fsPath)}`);
|
vscode.window.showInformationMessage(`已选择工程: ${path.basename(uri.fsPath)}`);
|
||||||
} else {
|
} else {
|
||||||
projectManager.pickProjectFile();
|
await projectManager.pickProjectFile();
|
||||||
}
|
}
|
||||||
|
await ensureWorkspaceSettings();
|
||||||
|
ensureCppProperties();
|
||||||
|
ensureGitignore();
|
||||||
updateStatusBars();
|
updateStatusBars();
|
||||||
}),
|
}),
|
||||||
vscode.commands.registerCommand('fmdCompiler.openOutput', () => {
|
vscode.commands.registerCommand('fmdCompiler.openOutput', () => {
|
||||||
@@ -59,6 +62,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
vscode.commands.registerCommand('fmdCompiler.readEeprom', () => eepromManager.readEeprom()),
|
vscode.commands.registerCommand('fmdCompiler.readEeprom', () => eepromManager.readEeprom()),
|
||||||
vscode.commands.registerCommand('fmdCompiler.writeEeprom', () => eepromManager.writeEeprom()),
|
vscode.commands.registerCommand('fmdCompiler.writeEeprom', () => eepromManager.writeEeprom()),
|
||||||
vscode.commands.registerCommand('fmdCompiler.exportEepromHex', () => eepromManager.exportEepromHex()),
|
vscode.commands.registerCommand('fmdCompiler.exportEepromHex', () => eepromManager.exportEepromHex()),
|
||||||
|
vscode.commands.registerCommand('fmdCompiler.regenerateConfig', () => regenerateConfig()),
|
||||||
diagnosticsCollection
|
diagnosticsCollection
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -69,6 +73,8 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
chipStatusBar.command = 'fmdCompiler.selectChip';
|
chipStatusBar.command = 'fmdCompiler.selectChip';
|
||||||
const downloadStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 98);
|
const downloadStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 98);
|
||||||
downloadStatusBar.command = 'fmdCompiler.download';
|
downloadStatusBar.command = 'fmdCompiler.download';
|
||||||
|
const configStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 97);
|
||||||
|
configStatusBar.command = 'fmdCompiler.regenerateConfig';
|
||||||
|
|
||||||
const update = () => {
|
const update = () => {
|
||||||
const cfg = getConfig();
|
const cfg = getConfig();
|
||||||
@@ -79,13 +85,16 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
chipStatusBar.tooltip = '切换 FMD 目标芯片';
|
chipStatusBar.tooltip = '切换 FMD 目标芯片';
|
||||||
downloadStatusBar.text = '$(cloud-upload) FMD Download';
|
downloadStatusBar.text = '$(cloud-upload) FMD Download';
|
||||||
downloadStatusBar.tooltip = cfg.programmerPath ? `下载程序: ${cfg.programmerPath}` : '未配置烧录工具,点击配置后可下载';
|
downloadStatusBar.tooltip = cfg.programmerPath ? `下载程序: ${cfg.programmerPath}` : '未配置烧录工具,点击配置后可下载';
|
||||||
|
configStatusBar.text = '$(gear) FMD Config';
|
||||||
|
configStatusBar.tooltip = '一键重新生成 .gitignore 和 .vscode 配置';
|
||||||
statusBar.show();
|
statusBar.show();
|
||||||
chipStatusBar.show();
|
chipStatusBar.show();
|
||||||
downloadStatusBar.show();
|
downloadStatusBar.show();
|
||||||
|
configStatusBar.show();
|
||||||
};
|
};
|
||||||
updateStatusBars = update;
|
updateStatusBars = update;
|
||||||
updateStatusBars();
|
updateStatusBars();
|
||||||
context.subscriptions.push(statusBar, chipStatusBar, downloadStatusBar);
|
context.subscriptions.push(statusBar, chipStatusBar, downloadStatusBar, configStatusBar);
|
||||||
|
|
||||||
// 监听配置变化
|
// 监听配置变化
|
||||||
context.subscriptions.push(
|
context.subscriptions.push(
|
||||||
@@ -99,6 +108,9 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
|
|
||||||
// 尝试自动找工程文件
|
// 尝试自动找工程文件
|
||||||
projectManager.autoDetectProject();
|
projectManager.autoDetectProject();
|
||||||
|
ensureWorkspaceSettings();
|
||||||
|
ensureCppProperties();
|
||||||
|
ensureGitignore();
|
||||||
updateStatusBars();
|
updateStatusBars();
|
||||||
|
|
||||||
outputChannel.appendLine('[FMD] 插件已激活');
|
outputChannel.appendLine('[FMD] 插件已激活');
|
||||||
@@ -112,6 +124,18 @@ export function deactivate() {
|
|||||||
|
|
||||||
let updateStatusBars = () => {};
|
let updateStatusBars = () => {};
|
||||||
|
|
||||||
|
async function regenerateConfig(): Promise<void> {
|
||||||
|
outputChannel.show(true);
|
||||||
|
outputChannel.appendLine('');
|
||||||
|
outputChannel.appendLine('========== FMD 重新生成 VS Code 配置 ==========');
|
||||||
|
await ensureWorkspaceSettings();
|
||||||
|
ensureCppProperties();
|
||||||
|
ensureGitignore();
|
||||||
|
updateStatusBars();
|
||||||
|
outputChannel.appendLine('========== FMD 配置生成完成 ==========');
|
||||||
|
vscode.window.showInformationMessage('FMD: 已重新生成 .gitignore 和 .vscode 配置');
|
||||||
|
}
|
||||||
|
|
||||||
async function setCompilerPath(): Promise<void> {
|
async function setCompilerPath(): Promise<void> {
|
||||||
const cfg = getConfig();
|
const cfg = getConfig();
|
||||||
const files = await vscode.window.showOpenDialog({
|
const files = await vscode.window.showOpenDialog({
|
||||||
@@ -191,6 +215,344 @@ async function syncChipFromProject(): Promise<void> {
|
|||||||
vscode.window.showInformationMessage(`FMD: 已从工程同步芯片: ${projectChip}`);
|
vscode.window.showInformationMessage(`FMD: 已从工程同步芯片: ${projectChip}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function ensureWorkspaceSettings(): Promise<void> {
|
||||||
|
const folders = vscode.workspace.workspaceFolders;
|
||||||
|
if (!folders || folders.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const folder of folders) {
|
||||||
|
const settingsDir = path.join(folder.uri.fsPath, '.vscode');
|
||||||
|
const settingsFile = path.join(settingsDir, 'settings.json');
|
||||||
|
let settings: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(settingsFile)) {
|
||||||
|
settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8')) as Record<string, unknown>;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
outputChannel.appendLine(`[警告] 无法解析工作区设置,跳过自动写入: ${settingsFile}: ${err}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
const cfg = getConfig();
|
||||||
|
const projectFile = projectManager.getProjectFile() || findSinglePrjFile(folder.uri.fsPath) || cfg.projectFile;
|
||||||
|
const projectChip = projectFile ? projectManager.getCurrentChip(projectFile) : undefined;
|
||||||
|
|
||||||
|
if (settings['fmdCompiler.outputDir'] === undefined) {
|
||||||
|
settings['fmdCompiler.outputDir'] = 'build';
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (settings['fmdCompiler.compilerPath'] === undefined) {
|
||||||
|
settings['fmdCompiler.compilerPath'] = cfg.compilerPath;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (settings['fmdCompiler.chip'] === undefined) {
|
||||||
|
settings['fmdCompiler.chip'] = projectChip || cfg.chip || 'FT61FC6X';
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (projectFile && settings['fmdCompiler.projectFile'] === undefined) {
|
||||||
|
settings['fmdCompiler.projectFile'] = projectFile;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (settings['fmdCompiler.autoSaveBeforeBuild'] === undefined) {
|
||||||
|
settings['fmdCompiler.autoSaveBeforeBuild'] = true;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (settings['fmdCompiler.showOutputOnBuild'] === undefined) {
|
||||||
|
settings['fmdCompiler.showOutputOnBuild'] = true;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
fs.mkdirSync(settingsDir, { recursive: true });
|
||||||
|
fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + '\n');
|
||||||
|
outputChannel.appendLine(`[FMD] 已自动生成/更新工作区设置: ${settingsFile}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureCppProperties(): void {
|
||||||
|
const folders = vscode.workspace.workspaceFolders;
|
||||||
|
if (!folders || folders.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cfg = getConfig();
|
||||||
|
const compilerInclude = path.join(path.dirname(cfg.compilerPath), '..', 'include');
|
||||||
|
|
||||||
|
for (const folder of folders) {
|
||||||
|
const vscodeDir = path.join(folder.uri.fsPath, '.vscode');
|
||||||
|
const propertiesFile = path.join(vscodeDir, 'c_cpp_properties.json');
|
||||||
|
const projectFile = projectManager.getProjectFile() || findSinglePrjFile(folder.uri.fsPath) || cfg.projectFile;
|
||||||
|
const projectDir = projectFile ? path.dirname(projectFile) : folder.uri.fsPath;
|
||||||
|
const chip = (projectFile ? projectManager.getCurrentChip(projectFile) : undefined) || cfg.chip;
|
||||||
|
const intellisenseHeader = ensureFmdIntellisenseHeader(vscodeDir, compilerInclude, chip);
|
||||||
|
const includePath = [
|
||||||
|
'${workspaceFolder}/**',
|
||||||
|
normalizeForCppProperties(projectDir),
|
||||||
|
normalizeForCppProperties(path.join(projectDir, '**')),
|
||||||
|
normalizeForCppProperties(compilerInclude),
|
||||||
|
];
|
||||||
|
const defines = [
|
||||||
|
`_${chip}`,
|
||||||
|
'__GCC8PRO__',
|
||||||
|
'_CHIP_SELECT_H_',
|
||||||
|
];
|
||||||
|
const forcedInclude = [
|
||||||
|
normalizeForCppProperties(intellisenseHeader),
|
||||||
|
];
|
||||||
|
let properties: {
|
||||||
|
configurations?: Array<Record<string, unknown>>;
|
||||||
|
version?: number;
|
||||||
|
[key: string]: unknown;
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(propertiesFile)) {
|
||||||
|
properties = JSON.parse(fs.readFileSync(propertiesFile, 'utf8'));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
outputChannel.appendLine(`[警告] 无法解析 C/C++ 配置,跳过自动写入: ${propertiesFile}: ${err}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(properties.configurations) || properties.configurations.length === 0) {
|
||||||
|
properties.configurations = [{
|
||||||
|
name: 'FMD',
|
||||||
|
includePath,
|
||||||
|
defines,
|
||||||
|
forcedInclude,
|
||||||
|
compilerPath: cfg.compilerPath,
|
||||||
|
cStandard: 'c99',
|
||||||
|
intelliSenseMode: 'windows-gcc-x86',
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
const configuration = properties.configurations[0];
|
||||||
|
const currentIncludePath = Array.isArray(configuration.includePath) ? configuration.includePath as string[] : [];
|
||||||
|
const currentDefines = Array.isArray(configuration.defines) ? configuration.defines as string[] : [];
|
||||||
|
const currentForcedInclude = Array.isArray(configuration.forcedInclude) ? configuration.forcedInclude as string[] : [];
|
||||||
|
configuration.includePath = mergeUnique(currentIncludePath, includePath);
|
||||||
|
configuration.defines = mergeUnique(currentDefines, defines);
|
||||||
|
configuration.forcedInclude = mergeUnique(currentForcedInclude, forcedInclude);
|
||||||
|
if (!configuration.compilerPath) {
|
||||||
|
configuration.compilerPath = cfg.compilerPath;
|
||||||
|
}
|
||||||
|
if (!configuration.cStandard) {
|
||||||
|
configuration.cStandard = 'c99';
|
||||||
|
}
|
||||||
|
if (!configuration.intelliSenseMode) {
|
||||||
|
configuration.intelliSenseMode = 'windows-gcc-x86';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!properties.version) {
|
||||||
|
properties.version = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.mkdirSync(vscodeDir, { recursive: true });
|
||||||
|
fs.writeFileSync(propertiesFile, JSON.stringify(properties, null, 2) + '\n');
|
||||||
|
outputChannel.appendLine(`[FMD] 已自动生成/更新 C/C++ 头文件路径: ${propertiesFile}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureFmdIntellisenseHeader(vscodeDir: string, compilerInclude: string, chip: string): string {
|
||||||
|
fs.mkdirSync(vscodeDir, { recursive: true });
|
||||||
|
const target = path.join(vscodeDir, 'fmd_intellisense.h');
|
||||||
|
const chipHeader = findChipHeader(compilerInclude, chip);
|
||||||
|
const names = chipHeader ? extractChipSymbols(chipHeader) : [];
|
||||||
|
const lines = [
|
||||||
|
'/* Auto-generated by FMD C Compiler extension. */',
|
||||||
|
'/* This file is only for VS Code IntelliSense and is not used by c.exe. */',
|
||||||
|
'#ifndef FMD_INTELLISENSE_H',
|
||||||
|
'#define FMD_INTELLISENSE_H',
|
||||||
|
'',
|
||||||
|
'#ifndef __FMD_INTELLISENSE__',
|
||||||
|
'#define __FMD_INTELLISENSE__ 1',
|
||||||
|
'#endif',
|
||||||
|
'',
|
||||||
|
'#ifndef bit',
|
||||||
|
'typedef unsigned char bit;',
|
||||||
|
'#endif',
|
||||||
|
'',
|
||||||
|
'#ifndef asm',
|
||||||
|
'#define asm(...)',
|
||||||
|
'#endif',
|
||||||
|
'',
|
||||||
|
'#ifndef interrupt',
|
||||||
|
'#define interrupt',
|
||||||
|
'#endif',
|
||||||
|
'',
|
||||||
|
`#ifndef _${chip}`,
|
||||||
|
`#define _${chip}`,
|
||||||
|
'#endif',
|
||||||
|
'',
|
||||||
|
...names.map(name => `extern volatile unsigned char ${name};`),
|
||||||
|
'',
|
||||||
|
'#endif',
|
||||||
|
'',
|
||||||
|
];
|
||||||
|
|
||||||
|
fs.writeFileSync(target, lines.join('\n'));
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findChipHeader(compilerInclude: string, chip: string): string | undefined {
|
||||||
|
const candidates = [
|
||||||
|
path.join(compilerInclude, `${chip}.h`),
|
||||||
|
path.join(compilerInclude, `${chip}.H`),
|
||||||
|
];
|
||||||
|
return candidates.find(file => fs.existsSync(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractChipSymbols(chipHeader: string): string[] {
|
||||||
|
const text = fs.readFileSync(chipHeader, 'utf8');
|
||||||
|
const names = new Set<string>();
|
||||||
|
const patterns = [
|
||||||
|
/volatile\s+(?:unsigned\s+char|bit)\s+([A-Za-z_][A-Za-z0-9_]*)\s*@/g,
|
||||||
|
/volatile\s+union\s*\{[\s\S]*?\}\s*([A-Za-z_][A-Za-z0-9_]*)\s*@/g,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const pattern of patterns) {
|
||||||
|
let m: RegExpExecArray | null;
|
||||||
|
while ((m = pattern.exec(text)) !== null) {
|
||||||
|
names.add(m[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(names).sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeForCppProperties(filePath: string): string {
|
||||||
|
return filePath.replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeUnique(first: string[], second: string[]): string[] {
|
||||||
|
const result: string[] = [];
|
||||||
|
for (const value of [...first, ...second]) {
|
||||||
|
if (value && !result.includes(value)) {
|
||||||
|
result.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureGitignore(): void {
|
||||||
|
const folders = vscode.workspace.workspaceFolders;
|
||||||
|
if (!folders || folders.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const patterns = [
|
||||||
|
'.vscode',
|
||||||
|
'**/*.as',
|
||||||
|
'**/*.asm',
|
||||||
|
'**/*.bin',
|
||||||
|
'**/*.cmf',
|
||||||
|
'**/*.cof',
|
||||||
|
'**/*.d',
|
||||||
|
'**/*.hex',
|
||||||
|
'**/*.lpp',
|
||||||
|
'**/*.map',
|
||||||
|
'**/*.obj',
|
||||||
|
'**/*.p1',
|
||||||
|
'**/*.pre',
|
||||||
|
'**/*.rlf',
|
||||||
|
'**/*.sdb',
|
||||||
|
'**/*.sym',
|
||||||
|
'**/*.hxl',
|
||||||
|
'**/*.ini',
|
||||||
|
'**/*.rar',
|
||||||
|
'**/*.o',
|
||||||
|
'**/*.crf',
|
||||||
|
'**/*.htm',
|
||||||
|
'**/*.dep',
|
||||||
|
'**/*.bak',
|
||||||
|
'**/*.lnp',
|
||||||
|
'**/*.lst',
|
||||||
|
'**/*.iex',
|
||||||
|
'**/*.sct',
|
||||||
|
'**/*.scvd',
|
||||||
|
'**/*.uvguix',
|
||||||
|
'**/*.dbg*',
|
||||||
|
'**/*.uvguix.*',
|
||||||
|
'**/.mxproject',
|
||||||
|
'**/*.uvopt',
|
||||||
|
'**/*.uvgui.*',
|
||||||
|
'**/Listings',
|
||||||
|
'**/output',
|
||||||
|
'**/*.zip',
|
||||||
|
];
|
||||||
|
|
||||||
|
const blockStart = '# FMD generated ignores';
|
||||||
|
const blockEnd = '# End FMD generated ignores';
|
||||||
|
const block = [blockStart, ...patterns, blockEnd].join('\n');
|
||||||
|
|
||||||
|
for (const folder of folders) {
|
||||||
|
const gitignoreFile = path.join(folder.uri.fsPath, '.gitignore');
|
||||||
|
let text = '';
|
||||||
|
|
||||||
|
if (fs.existsSync(gitignoreFile)) {
|
||||||
|
text = fs.readFileSync(gitignoreFile, 'utf8');
|
||||||
|
if (text.includes(blockStart) && text.includes(blockEnd)) {
|
||||||
|
const pattern = new RegExp(`${escapeRegExp(blockStart)}[\\s\\S]*?${escapeRegExp(blockEnd)}`);
|
||||||
|
const nextText = text.replace(pattern, block);
|
||||||
|
if (nextText !== text) {
|
||||||
|
fs.writeFileSync(gitignoreFile, ensureTrailingNewline(nextText));
|
||||||
|
outputChannel.appendLine(`[FMD] 已更新 .gitignore: ${gitignoreFile}`);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const missing = patterns.filter(p => !hasGitignorePattern(text, p));
|
||||||
|
if (missing.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix = text.trim().length > 0 ? ensureTrailingNewline(text).replace(/\s*$/, '\n\n') : '';
|
||||||
|
fs.writeFileSync(gitignoreFile, `${prefix}${block}\n`);
|
||||||
|
outputChannel.appendLine(`[FMD] 已自动生成/更新 .gitignore: ${gitignoreFile}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasGitignorePattern(text: string, pattern: string): boolean {
|
||||||
|
return text.split(/\r?\n/).some(line => line.trim() === pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureTrailingNewline(text: string): string {
|
||||||
|
return text.endsWith('\n') ? text : text + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeRegExp(value: string): string {
|
||||||
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
|
function findSinglePrjFile(folderPath: string): string | undefined {
|
||||||
|
const result: string[] = [];
|
||||||
|
const walk = (dir: string, depth: number) => {
|
||||||
|
if (depth > 2 || result.length > 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||||
|
const fullPath = path.join(dir, entry.name);
|
||||||
|
if (entry.isFile() && entry.name.toLowerCase().endsWith('.prj')) {
|
||||||
|
result.push(fullPath);
|
||||||
|
} else if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
||||||
|
walk(fullPath, depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// 忽略权限错误
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
walk(folderPath, 0);
|
||||||
|
return result.length === 1 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
function collectChipCandidates(): string[] {
|
function collectChipCandidates(): string[] {
|
||||||
const cfg = getConfig();
|
const cfg = getConfig();
|
||||||
const chips = new Set<string>(['FT61FC6X', cfg.chip]);
|
const chips = new Set<string>(['FT61FC6X', cfg.chip]);
|
||||||
@@ -224,7 +586,7 @@ export function getConfig() {
|
|||||||
]),
|
]),
|
||||||
projectFile: cfg.get<string>('projectFile', ''),
|
projectFile: cfg.get<string>('projectFile', ''),
|
||||||
chip: cfg.get<string>('chip', 'FT61FC6X'),
|
chip: cfg.get<string>('chip', 'FT61FC6X'),
|
||||||
outputDir: cfg.get<string>('outputDir', ''),
|
outputDir: cfg.get<string>('outputDir', 'build'),
|
||||||
extraArgs: cfg.get<string>('extraArgs', ''),
|
extraArgs: cfg.get<string>('extraArgs', ''),
|
||||||
autoSaveBeforeBuild: cfg.get<boolean>('autoSaveBeforeBuild', true),
|
autoSaveBeforeBuild: cfg.get<boolean>('autoSaveBeforeBuild', true),
|
||||||
showOutputOnBuild: cfg.get<boolean>('showOutputOnBuild', true),
|
showOutputOnBuild: cfg.get<boolean>('showOutputOnBuild', true),
|
||||||
|
|||||||
Reference in New Issue
Block a user