diff --git a/CHANGELOG.md b/CHANGELOG.md index 50aa579..a24088b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ All notable version changes will be recorded in this file. *** +### [v3.26.4] revision + +**Improve**: + - `Project Diagnostics`: Add code action for stub func missed warning. (to auto fix gcc warning: "_xxx is not implemented and will always fail") + - `Toolchain Setup`: Goto eide tools folder by default when user start to select a location. + +*** + ### [v3.26.3] revision **New**: diff --git a/package.json b/package.json index 8a1b420..dc3cdd0 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "homepage": "https://em-ide.com", "license": "MIT", "description": "A mcu development environment for 8051/AVR/STM8/Cortex-M/MIPS/RISC-V", - "version": "3.26.3", + "version": "3.26.4", "preview": false, "engines": { "vscode": "^1.67.0" @@ -538,6 +538,11 @@ } ], "commands": [ + { + "command": "eide.project.create_sys_stubs", + "category": "eide", + "title": "Create gcc system stubs for project" + }, { "command": "eide.debug.start", "category": "eide", diff --git a/res/data/gcc_posix_stubs.c b/res/data/gcc_posix_stubs.c new file mode 100644 index 0000000..964b54c --- /dev/null +++ b/res/data/gcc_posix_stubs.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include + +__attribute__((weak)) int _chown(const char *path, uid_t owner, gid_t group) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _execve(char *name, char **argv, char **env) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _fork(void) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _fstat(int fildes, struct stat *st) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _getpid(void) +{ + errno = ENOSYS; + return -1; +} + +struct timeval; + +__attribute__((weak)) int _gettimeofday(struct timeval *ptimeval, void *ptimezone) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _isatty(int file) +{ + errno = ENOSYS; + return 0; +} + +__attribute__((weak)) int _kill(int pid, int sig) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _link(char *existing, char *new) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _lseek(int file, int ptr, int dir) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _open(char *file, int flags, int mode) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _close(int fildes) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _read(int file, char *ptr, int len) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _readlink(const char *path, char *buf, size_t bufsize) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _stat(const char *file, struct stat *st) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _symlink(const char *path1, const char *path2) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) clock_t _times(struct tms *buf) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _unlink(char *name) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _wait(int *status) +{ + errno = ENOSYS; + return -1; +} + +__attribute__((weak)) int _write(int file, char *ptr, int len) +{ + errno = ENOSYS; + return -1; +} diff --git a/src/EIDEProject.ts b/src/EIDEProject.ts index bf40c71..1526817 100644 --- a/src/EIDEProject.ts +++ b/src/EIDEProject.ts @@ -1266,6 +1266,9 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr return (this.sourceRoots.getFileGroups()).concat(this.virtualSource.getFileGroups()); } + /** + * @note Not contains excluded source files. if you want to list all sources, please use getFileGroups + */ getAllSources(): { path: string, virtualPath?: string; }[] { const srcList: { path: string, virtualPath?: string; }[] = []; diff --git a/src/EIDEProjectExplorer.ts b/src/EIDEProjectExplorer.ts index 82e4d36..0ffa749 100644 --- a/src/EIDEProjectExplorer.ts +++ b/src/EIDEProjectExplorer.ts @@ -88,7 +88,8 @@ import { txt_no, remove_this_item, view_str$prompt$filesOptionsComment, - view_str$virual_doc_provider_banner + view_str$virual_doc_provider_banner, + view_str$missed_stubs_added } from './StringTable'; import { CodeBuilder, BuildOptions } from './CodeBuilder'; import { ExceptionToMessage, newMessage } from './Message'; @@ -128,7 +129,7 @@ import { } from 'vscode-cpptools'; import * as eclipseParser from './EclipseProjectParser'; import { isArray } from 'util'; -import { parseIarCompilerLog, CompilerDiagnostics, parseGccCompilerLog, parseArmccCompilerLog, parseKeilc51CompilerLog, parseSdccCompilerLog, parseCosmicStm8CompilerLog } from './ProblemMatcher'; +import { parseIarCompilerLog, CompilerDiagnostics, parseGccCompilerLog, parseArmccCompilerLog, parseKeilc51CompilerLog, parseSdccCompilerLog, parseCosmicStm8CompilerLog, EideDiagnosticCode } from './ProblemMatcher'; import * as iarParser from './IarProjectParser'; import * as ArmCpuUtils from './ArmCpuUtils'; import { ShellFlasherIndexItem } from './WebInterface/WebInterface'; @@ -3488,6 +3489,16 @@ export class ProjectExplorer implements CustomConfigurationProvider { this._event.emit(event, arg); } + /** + * + * @param callbk return true to break foreach + */ + foreachProjects(callbk: (val: AbstractProject, index: number) => boolean | undefined) { + this.dataProvider.traverseProjects((prj, idx) => { + return callbk(prj, idx); + }); + } + getProjectByTreeItem(prjItem?: ProjTreeItem): AbstractProject | undefined { return prjItem instanceof ProjTreeItem ? this.dataProvider.GetProjectByIndex(prjItem.val.projectIndex) : @@ -3518,6 +3529,36 @@ export class ProjectExplorer implements CustomConfigurationProvider { // ------- + createSysStubs(prjuid: string | undefined) { + + const prj = prjuid + ? this.dataProvider.getProjectByUid(prjuid) + : this.dataProvider.getActiveProject(); + if (!prj) + return; // no project + + const tarFile = File.from(prj.getRootDir().path, 'sys_stubs.c'); + for (const grp of prj.getFileGroups()) { + for (const e of grp.files) { + if (tarFile.path === e.file.path) + return; // it's existed, abort. + } + } + + try { + const srcFile = File.from(ResManager.instance().getAppDataDir().path, 'gcc_posix_stubs.c'); + tarFile.Write(srcFile.Read()); + const vSrcManger = prj.getVirtualSourceManager(); + vSrcManger.addFile(VirtualSource.rootName, tarFile.path); + // clear diags + const uri = vscode.Uri.file(File.from(prj.getOutputFolder().path, 'compiler.log').path); + this.compiler_diags.get(prj.getUid())?.delete(uri); + GlobalEvent.show_msgbox('Info', view_str$missed_stubs_added.replace('{}', tarFile.name)); + } catch (error) { + GlobalEvent.show_msgbox('Error', error); + } + } + openLibsGeneratorConfig(prjItem?: ProjTreeItem) { const proj = this.getProjectByTreeItem(prjItem); @@ -3871,13 +3912,52 @@ export class ProjectExplorer implements CustomConfigurationProvider { } } + private parseLdLogs(file: File): {content: string, idx: number}[] { + + const logLines: {content: string, idx: number}[] = []; + + try { + + const fileLines = file.Read().split(/\r\n|\n/); + + let logStarted = false; + let logEnd = false; + + fileLines.forEach((line, idx) => { + + if (logEnd) + return; + + if (!logStarted) { + if (line.startsWith('>>> ld')) { + logStarted = true; + } + } else { + if (line.startsWith('>>>')) { + logEnd = true; + } else { + logLines.push({ + content: line, + idx + }); + } + } + }); + + } catch (error) { + // nothing todo + } + + return logLines; + } + private updateCompilerDiagsAfterBuild(prj: AbstractProject) { let diag_res: CompilerDiagnostics | undefined; - try { + const logFile = File.from(prj.getOutputFolder().path, 'compiler.log'); - const logFile = File.fromArray([prj.getOutputFolder().path, 'compiler.log']); + try { switch (prj.getToolchain().name) { case 'IAR_ARM': @@ -3906,6 +3986,46 @@ export class ProjectExplorer implements CustomConfigurationProvider { GlobalEvent.log_warn(error); } + if (isGccFamilyToolchain(prj.toolchainName())) { + // examples: + // >>> ld + // .... + // warning: _getpid is not implemented and will always fail + const allStubs = [ + '_chown', '_execve', '_fork', '_fstat', '_getpid', + '_gettimeofday', '_isatty', '_kill', '_link', '_lseek', + '_open', '_close', '_read', '_readlink', '_stat', '_symlink', '_times', '_unlink', '_wait', '_write', + ]; + const missedStubs: {name: string, lineIdx: number}[] = []; + + this.parseLdLogs(logFile).forEach(line => { + const m = /warning: (\w+) is not implemented and will always fail/.exec(line.content); + if (m && m.length > 1) { + const name = m[1]; + if (allStubs.includes(name)) + missedStubs.push({ name, lineIdx: line.idx }); + } + }); + + if (missedStubs.length > 0) { + if (diag_res == undefined) + diag_res = {}; + const diags: vscode.Diagnostic[] = []; + for (const ele of missedStubs) { + const range = new vscode.Range( + new vscode.Position(ele.lineIdx, 0), + new vscode.Position(ele.lineIdx, 54)); + const diag = new vscode.Diagnostic(range, + `warning: ${ele.name} is not implemented and will always fail`, + vscode.DiagnosticSeverity.Warning); + diag.source = "eide"; + diag.code = EideDiagnosticCode.GCC_SYS_STUB_MISSED; + diags.push(diag); + } + diag_res[logFile.path] = diags; + } + } + if (diag_res) { const uid = prj.getUid(); diff --git a/src/OperationExplorer.ts b/src/OperationExplorer.ts index 3b5a74f..f0de324 100644 --- a/src/OperationExplorer.ts +++ b/src/OperationExplorer.ts @@ -923,28 +923,28 @@ export class OperationExplorer { if (tool && !tool.no_binaries) { /* have online package */ const pickItems: vscode.QuickPickItem[] = [ - { - label: 'Online', - description: 'Download from website and install', - detail: view_str$prompt$tool_install_mode_online - }, { label: 'Local', description: 'Use existed installation directory', detail: view_str$prompt$tool_install_mode_local + }, + { + label: 'Online', + description: 'Download from website and install', + detail: view_str$prompt$tool_install_mode_online } ]; - const onlineInstall = await vscode.window.showQuickPick(pickItems, { + const select = await vscode.window.showQuickPick(pickItems, { canPickMany: false, placeHolder: view_str$prompt$select_tool_install_mode }); - if (onlineInstall == undefined) { + if (select == undefined) { return; } - if (onlineInstall.label == 'Online') { + if (select.label == 'Online') { await resInstaller.installTool(item.type); return; } @@ -973,6 +973,10 @@ export class OperationExplorer { }; } + if (item.type !== 'Keil_C51' && !item.label.includes('MDK') && + item.type !== 'IAR_ARM' && item.type !== 'IAR_STM8') + dialogOption.defaultUri = vscode.Uri.file(ResManager.instance().getEideToolsInstallDir()); + const path = await vscode.window.showOpenDialog(dialogOption); if (path === undefined || path.length === 0) { return; diff --git a/src/ProblemMatcher.ts b/src/ProblemMatcher.ts index c970557..2e822a7 100644 --- a/src/ProblemMatcher.ts +++ b/src/ProblemMatcher.ts @@ -99,6 +99,10 @@ function newVscFileRange(line: number, start: number, len: number): vscode.Range ////////////////////////////////////////////////////////////////////// +export enum EideDiagnosticCode { + GCC_SYS_STUB_MISSED = 10, +}; + export type CompilerDiagnostics = { [path: string]: vscode.Diagnostic[]; } export function parseArmccCompilerLog(projApi: ProjectBaseApi, logFile: File): CompilerDiagnostics { diff --git a/src/StringTable.ts b/src/StringTable.ts index 7a7ca50..213884f 100644 --- a/src/StringTable.ts +++ b/src/StringTable.ts @@ -459,6 +459,16 @@ export const view_str$env_desc$py3_cmd = [ //---------------Other--------------- +export const view_str$missed_stubs_added = [ + '桩函数源文件 {} 已经添加到项目。', + 'The stubs file "{}" has been added to the project.' +][langIndex]; + +export const view_str$add_missed_stubs = [ + '添加缺失的桩函数', + 'Add missed stubs' +][langIndex]; + export const view_str$prompt$migrationFailed = [ `迁移旧项目失败!路径:{}`, `Migrate Old Project Failed ! Path: {}` diff --git a/src/extension.ts b/src/extension.ts index 7913d90..557a42c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -44,7 +44,7 @@ import { view_str$prompt$install_dotnet_and_restart_vscode, view_str$prompt$install_dotnet_failed, view_str$prompt$not_found_compiler, view_str$prompt$debugCfgNotSupported, not_support_no_arm_project, - view_str$prompt$requireOtherExtension + view_str$prompt$requireOtherExtension, view_str$add_missed_stubs } from './StringTable'; import { LogDumper } from './LogDumper'; import { StatusBarManager } from './StatusBarManager'; @@ -170,6 +170,7 @@ export async function activate(context: vscode.ExtensionContext) { subscriptions.push(vscode.commands.registerCommand('eide.project.buildAndFlash', (item) => projectExplorer.BuildSolution(item, { not_rebuild: true, flashAfterBuild: true }))); subscriptions.push(vscode.commands.registerCommand('eide.project.genBuilderParams', (item) => projectExplorer.BuildSolution(item, { not_rebuild: true, onlyDumpBuilderParams: true }))); subscriptions.push(vscode.commands.registerCommand('eide.open.makelibs.cfg', (item) => projectExplorer.openLibsGeneratorConfig(item))); + subscriptions.push(vscode.commands.registerCommand('eide.project.create_sys_stubs', (projuid) => projectExplorer.createSysStubs(projuid))); // operations bar subscriptions.push(vscode.commands.registerCommand('_cl.eide.project.historyRecord', () => projectExplorer.openHistoryRecords())); @@ -294,6 +295,8 @@ export async function activate(context: vscode.ExtensionContext) { // others vscode.workspace.registerTextDocumentContentProvider(VirtualDocument.scheme, VirtualDocument.instance()); vscode.workspace.registerTaskProvider(EideTaskProvider.TASK_TYPE_BASH, new EideTaskProvider()); + vscode.languages.registerCodeActionsProvider({ scheme: 'file', pattern: '**/compiler.log' }, + new CompilerLogCodeActionProvider(), { providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] }); // auto save project projectExplorer.enableAutoSave(true); @@ -1609,6 +1612,7 @@ import { PyOCDFlashOptions, STLinkOptions, ProbeRSFlashOptions, STVPFlasherOptions } from './HexUploader'; import { AbstractProject } from './EIDEProject'; +import { EideDiagnosticCode } from './ProblemMatcher'; type MapViewParserType = 'memap' | 'builtin'; @@ -2309,6 +2313,50 @@ async function startDebugging(attach?: boolean) { } } +class CompilerLogCodeActionProvider implements vscode.CodeActionProvider { + + provideCodeActions(document: vscode.TextDocument, + range: vscode.Range | vscode.Selection, + context: vscode.CodeActionContext, token: vscode.CancellationToken): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> { + const results: (vscode.CodeAction | vscode.Command)[] = []; + + let prjuid: string | undefined; + projectExplorer.foreachProjects(prj => { + const root = prj.getRootDir().path; + if (File.isSubPathOf(root, document.uri.fsPath)) { + prjuid = prj.getUid(); + return true; + } + }); + + if (prjuid === undefined) + return results; + + const stub_missed_diags: vscode.Diagnostic[] = []; + + context.diagnostics.forEach((ele) => { + if (ele.source === 'eide') { + if (ele.code == EideDiagnosticCode.GCC_SYS_STUB_MISSED) { + stub_missed_diags.push(ele); + } + } + }); + + if (stub_missed_diags.length > 0) { + const act = new vscode.CodeAction(view_str$add_missed_stubs, vscode.CodeActionKind.QuickFix); + act.diagnostics = stub_missed_diags; + act.command = { + title: view_str$add_missed_stubs, + command: 'eide.project.create_sys_stubs', + arguments: [prjuid] + }; + results.push(act); + } + + return results; + } +} + /////////////////////////////////////////////////// // KEIL_C51 -> SDCC converter ///////////////////////////////////////////////////