diff --git a/CHANGELOG.md b/CHANGELOG.md index 21c9be10..5a6fd1c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ All notable version changes will be recorded in this file. *** +### [v3.25.1] revision + +**Improve**: + - `Import Project`: Improve project parser when import an eclipse project. + - `Debug`: One-click to start debugging. NOT NEED ANY `launch.json`. [See Here](https://em-ide.com/docs/advance/debug_project) + - `Toolchain Options`: New option `use-newlib-nano`, `not-use-syscalls` for `arm-none-eabi-gcc` toolchain. + +*** + ### [v3.25.0] update **New**: diff --git a/lang/arm.gcc.verify.json b/lang/arm.gcc.verify.json index 042b3c1f..e442c054 100644 --- a/lang/arm.gcc.verify.json +++ b/lang/arm.gcc.verify.json @@ -144,6 +144,14 @@ "dwarf-3" ] }, + "use-newlib-nano": { + "markdownDescription": "Use newlib-nano (--specs=nano.specs)", + "type": "boolean" + }, + "not-use-syscalls": { + "markdownDescription": "Do not use syscalls (--specs=nosys.specs)", + "type": "boolean" + }, "misc-control": { "markdownDescription": "Other Global Options", "description.zh-cn": "编译器附加选项(全局)", diff --git a/package.json b/package.json index 7a5eb2d9..dc2cba1c 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.25.0", + "version": "3.25.1", "preview": false, "engines": { "vscode": "^1.67.0" @@ -114,6 +114,12 @@ "yaml": "^1.10.2" }, "contributes": { + "debuggers": [ + { + "type": "eide.cortex-debug", + "label": "EIDE (Cortex-Debug)" + } + ], "terminal": { "profiles": [ { @@ -541,6 +547,20 @@ } ], "commands": [ + { + "command": "eide.debug.start", + "category": "eide", + "title": "%eide.project.debug.start%", + "icon": { + "dark": "./res/icon/Run_16x.svg", + "light": "./res/icon/Run_16x.svg" + } + }, + { + "command": "eide.refresh.external_tools_index", + "category": "eide", + "title": "Refresh External Tools Index" + }, { "command": "eide.open.makelibs.cfg", "category": "eide", @@ -1217,17 +1237,22 @@ { "when": "cl.eide.projectActived && config.EIDE.Option.ShowToolbarInEditerTitle", "command": "eide.project.build", - "group": "navigation" + "group": "navigation@1" }, { "when": "cl.eide.projectActived && config.EIDE.Option.ShowToolbarInEditerTitle", - "command": "eide.project.clean", - "group": "navigation" + "command": "eide.project.uploadToDevice", + "group": "navigation@2" }, { "when": "cl.eide.projectActived && config.EIDE.Option.ShowToolbarInEditerTitle", - "command": "eide.project.uploadToDevice", - "group": "navigation" + "command": "eide.debug.start", + "group": "navigation@3" + }, + { + "when": "cl.eide.projectActived && config.EIDE.Option.ShowToolbarInEditerTitle", + "command": "eide.project.clean", + "group": "navigation@4" } ], "editor/context": [ @@ -1291,10 +1316,15 @@ "when": "viewItem == SOLUTION && view == cl.eide.view.projects" }, { - "command": "eide.project.clean", + "command": "eide.debug.start", "group": "inline@4", "when": "viewItem == SOLUTION && view == cl.eide.view.projects" }, + { + "command": "eide.project.clean", + "group": "inline@5", + "when": "viewItem == SOLUTION && view == cl.eide.view.projects" + }, { "command": "_cl.eide.project.setActive", "when": "viewItem == SOLUTION && view == cl.eide.view.projects && cl.eide.enable.active", @@ -1315,6 +1345,11 @@ "when": "viewItem == SOLUTION && view == cl.eide.view.projects", "group": "2_build@2" }, + { + "command": "eide.debug.start", + "when": "viewItem == SOLUTION && view == cl.eide.view.projects", + "group": "2_build@3" + }, { "command": "eide.project.uploadToDevice", "when": "viewItem == SOLUTION && view == cl.eide.view.projects", diff --git a/package.nls.json b/package.nls.json index 02a8b5cc..d653b08d 100644 --- a/package.nls.json +++ b/package.nls.json @@ -32,6 +32,7 @@ "eide.project.modify.files.options": "Show Extra Options Of All Source Files", "eide.project.import.ext.project.src.struct": "Import SourceFile Tree From Other Project", "eide.project.generate_builder_params": "Generate builder.params", + "eide.project.debug.start": "Start Debugging (Cortex-Debug)", "eide.prj.menus.main.static-check": "Static Check", "eide.prj.menus.sub.static-check.cppcheck": "Run Cppcheck", diff --git a/package.nls.zh-CN.json b/package.nls.zh-CN.json index 8c68b003..4d62d194 100644 --- a/package.nls.zh-CN.json +++ b/package.nls.zh-CN.json @@ -32,6 +32,7 @@ "eide.project.modify.files.options": "查看所有源文件的附加编译参数", "eide.project.import.ext.project.src.struct": "从其他 IDE 的项目中导入源文件树", "eide.project.generate_builder_params": "生成 builder.params", + "eide.project.debug.start": "启动调试(Cortex-Debug)", "eide.prj.menus.main.static-check": "静态检查", "eide.prj.menus.sub.static-check.cppcheck": "执行 Cppcheck", diff --git a/res/data/builtin_headers/lint_sdcc.h b/res/data/builtin_headers/lint_sdcc.h index d7c90154..2887979e 100644 --- a/res/data/builtin_headers/lint_sdcc.h +++ b/res/data/builtin_headers/lint_sdcc.h @@ -22,7 +22,6 @@ #define __interrupt(x) #define __using(x) #define __at(x) -#define __asm__(x) #define __naked // for pic diff --git a/res/data/models/arm.gcc.model.json b/res/data/models/arm.gcc.model.json index 255872f6..4bc51580 100644 --- a/res/data/models/arm.gcc.model.json +++ b/res/data/models/arm.gcc.model.json @@ -299,6 +299,32 @@ "cpp" ] }, + "use-newlib-nano": { + "type": "selectable", + "command": { + "true": "--specs=nano.specs", + "false": "" + }, + "group": [ + "c", + "cpp", + "asm", + "linker" + ] + }, + "not-use-syscalls": { + "type": "selectable", + "command": { + "true": "--specs=nosys.specs", + "false": "" + }, + "group": [ + "c", + "cpp", + "asm", + "linker" + ] + }, "misc-control": { "type": "list", "command": "", diff --git a/res/icon/Run_16x.svg b/res/icon/Run_16x.svg new file mode 100644 index 00000000..0713e150 --- /dev/null +++ b/res/icon/Run_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/icon/Run_blue_16x.svg b/res/icon/Run_blue_16x.svg new file mode 100644 index 00000000..aab6137b --- /dev/null +++ b/res/icon/Run_blue_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/icon/Run_grey_16x.svg b/res/icon/Run_grey_16x.svg new file mode 100644 index 00000000..e7abc48b --- /dev/null +++ b/res/icon/Run_grey_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/EIDEProjectExplorer.ts b/src/EIDEProjectExplorer.ts index d2b0a98a..6371a296 100644 --- a/src/EIDEProjectExplorer.ts +++ b/src/EIDEProjectExplorer.ts @@ -2668,6 +2668,10 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco .map(d => ePrjRoot.ToRelativePath(d.path) || d.path); } + // init source args + const srcOptsObj = { version: EIDE_FILE_OPTION_VERSION, options: {} }; + srcOptsObj.version = EIDE_FILE_OPTION_VERSION; + // init all target for (const eTarget of ePrjInfo.targets) { @@ -2687,108 +2691,48 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco } }; - nEideTarget.custom_dep.defineList = eTarget.globalArgs.cMacros; - nEideTarget.custom_dep.incList = eTarget.globalArgs.cIncDirs; - - var getRootIncompatibleArgs = (t: eclipseParser.EclipseProjectTarget): string[] => { - - const res: string[] = []; - - if (!t.incompatibleArgs['/']) return res; - const bArgs: any = t.incompatibleArgs['/']; - for (const key in bArgs) { - if (!isArray(bArgs[key])) continue; - for (const arg of bArgs[key]) { - res.push(arg); - } - } - - return res; - }; + nEideTarget.custom_dep.defineList = eTarget.builldArgs.cMacros; + nEideTarget.custom_dep.incList = eTarget.builldArgs.cIncDirs; + nEideTarget.custom_dep.libList = eTarget.builldArgs.linkerLibSearchDirs; // for arm gcc toolchain if (nEideTarget.toolchain == 'GCC') { - var guessArmCpuType = (t: eclipseParser.EclipseProjectTarget): string | undefined => { - + var guessArmCpuType = (archName?: string): string | undefined => { + if (!archName) + return undefined; // @note: this list is trimed, not full - const armCpuTypeList = [ - 'Cortex-M0', - 'Cortex-M23', - 'Cortex-M33', - 'Cortex-M3', - 'Cortex-M4', - 'Cortex-M7' - ]; - - for (const arg of getRootIncompatibleArgs(t)) { - const mRes = /(cortex-m[\w\+]+)/.exec(arg); - if (mRes && mRes.length > 1) { - const name = mRes[1].toLowerCase(); - const idx = armCpuTypeList.map(c => c.toLowerCase()) - .findIndex(c => name == c || name.startsWith(c)); - if (idx != -1) return armCpuTypeList[idx]; - } - } + const armCpuTypeMap: any = { + 'cortex-m0plus' : 'Cortex-M0+', + 'cortex-m0+' : 'Cortex-M0+', + 'cortex-m23' : 'Cortex-M23', + 'cortex-m33' : 'Cortex-M33', + 'cortex-m35p' : 'Cortex-M35P', + 'cortex-m55' : 'Cortex-M55', + 'cortex-m85' : 'Cortex-M85', + 'cortex-m0' : 'Cortex-M0', + 'cortex-m3' : 'Cortex-M3', + 'cortex-m4' : 'Cortex-M4', + 'cortex-m7' : 'Cortex-M7' + }; + return armCpuTypeMap[archName.toLowerCase()]; }; const compilerOpt = nEideTarget.compileConfig; - compilerOpt.cpuType = guessArmCpuType(eTarget) || 'Cortex-M3'; + compilerOpt.cpuType = guessArmCpuType(eTarget.archName) || 'Cortex-M3'; compilerOpt.floatingPointHardware = ArmCpuUtils.hasFpu(compilerOpt.cpuType) ? 'single' : 'none'; compilerOpt.useCustomScatterFile = true; - compilerOpt.scatterFilePath = ''; - - getRootIncompatibleArgs(eTarget).forEach(arg => { - if (/linker script/i.test(arg)) { - const mRes = /[^=]+=(.+)$/.exec(arg); - if (mRes && mRes.length > 1) { - const p = eclipseParser.formatFilePath(mRes[1].trim()); - if (/\.ld[s]?$/i.test(p)) { - compilerOpt.scatterFilePath = p; - } - } - } - }); + compilerOpt.scatterFilePath = eTarget.linkerScriptPath || ''; } - // for riscv gcc toolchain else if (nEideTarget.toolchain == 'RISCV_GCC') { - const compilerOpt = nEideTarget.compileConfig; - - compilerOpt.linkerScriptPath = ''; - - getRootIncompatibleArgs(eTarget).forEach(arg => { - if (/linker script/i.test(arg)) { - const mRes = /[^=]+=(.+)$/.exec(arg); - if (mRes && mRes.length > 1) { - const p = eclipseParser.formatFilePath(mRes[1].trim()); - if (/\.ld[s]?$/i.test(p)) { - compilerOpt.linkerScriptPath = p; - } - } - } - }); + compilerOpt.linkerScriptPath = eTarget.linkerScriptPath || ''; } - // for any gcc toolchain else if (nEideTarget.toolchain == 'ANY_GCC') { - const compilerOpt = nEideTarget.compileConfig; - - compilerOpt.linkerScriptPath = ''; - - getRootIncompatibleArgs(eTarget).forEach(arg => { - if (/linker script/i.test(arg)) { - const mRes = /[^=]+=(.+)$/.exec(arg); - if (mRes && mRes.length > 1) { - const p = eclipseParser.formatFilePath(mRes[1].trim()); - if (/\.ld[s]?$/i.test(p)) { - compilerOpt.linkerScriptPath = p; - } - } - } - }); + compilerOpt.linkerScriptPath = eTarget.linkerScriptPath || ''; } // init compiler args for target @@ -2797,7 +2741,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco const toolchainDefConf = toolchain.getDefaultConfig(); // glob - toolchainDefConf.global['misc-control'] = eTarget.globalArgs.globalArgs.filter(a => a.trim() != ''); + toolchainDefConf.global['misc-control'] = eTarget.builldArgs.globalArgs.filter(a => a.trim() != ''); // asm { @@ -2805,8 +2749,8 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco const asmCfg = toolchainDefConf["asm-compiler"]; if (asmCfg['ASM_FLAGS']) flags.push(asmCfg['ASM_FLAGS']); - eTarget.globalArgs.sMacros.forEach(m => flags.push(`-D${m}`)); - eTarget.globalArgs.assemblerArgs.forEach(arg => flags.push(arg)); + eTarget.builldArgs.sMacros.forEach(m => flags.push(`-D${m}`)); + eTarget.builldArgs.assemblerArgs.forEach(arg => flags.push(arg)); flags = flags.filter(p => p.trim() != ''); if (asmCfg['ASM_FLAGS'] != undefined) { @@ -2822,12 +2766,24 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco let cxxFlags: string[] = []; const ccCfg = toolchainDefConf["c/cpp-compiler"]; - if (ccCfg['C_FLAGS']) flags.push(ccCfg['C_FLAGS']); - if (ccCfg['CXX_FLAGS']) cxxFlags.push(ccCfg['CXX_FLAGS']); - - eTarget.globalArgs.cCompilerArgs.forEach(arg => { + if (eTarget.builldArgs.optimization) + ccCfg['optimization'] = eTarget.builldArgs.optimization; + if (eTarget.builldArgs.cLanguageStd) + ccCfg['language-c'] = eTarget.builldArgs.cLanguageStd; + if (eTarget.builldArgs.cppLanguageStd) + ccCfg['language-cpp'] = eTarget.builldArgs.cppLanguageStd; + if (eTarget.builldArgs.signedChar) + ccCfg['signed-char'] = true; + + if (ccCfg['C_FLAGS']) + flags.push(ccCfg['C_FLAGS']); + if (ccCfg['CXX_FLAGS']) + cxxFlags.push(ccCfg['CXX_FLAGS']); + + eTarget.builldArgs.cCompilerArgs.forEach(arg => { flags.push(arg); - cxxFlags.push(arg); + //TODO not support C++ options now + //cxxFlags.push(arg); }); flags = flags.filter(p => p.trim() != ''); @@ -2845,21 +2801,59 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco if (!toolchainDefConf.linker) toolchainDefConf.linker = {}; const ldCfg = toolchainDefConf.linker; - const flags: string[] = eTarget.globalArgs.linkerArgs.filter(a => a.trim() != ''); + const flags: string[] = eTarget.builldArgs.linkerArgs.filter(a => a.trim() != ''); if (ldCfg['LD_FLAGS'] != undefined) { ldCfg['LD_FLAGS'] = flags.join(' '); - const libFlags = eTarget.globalArgs.linkerLibArgs.filter(a => a.trim() != ''); + const libFlags = eTarget.builldArgs.linkerLibArgs.filter(a => a.trim() != ''); if (ldCfg['LIB_FLAGS'] != undefined) { ldCfg['LIB_FLAGS'] = libFlags.join(' '); } } else { ldCfg['misc-control'] = flags.join(' '); } + + // setup link order + if (eTarget.objsOrder.length) { + const linkOrder: { pattern: string, order: number }[] = []; + eTarget.objsOrder.forEach((e, idx) => { + linkOrder.push({ + pattern: e, + order: idx + }); + }); + ldCfg['object-order'] = linkOrder; + } } nEideTarget.builderOptions[toolchain.name] = toolchainDefConf; } + // setup source options + if (eTarget.sourceArgs) { + srcOptsObj.options[eTarget.name] = { files: {} }; + const srcOptions: any = srcOptsObj.options[eTarget.name].files; + const srcFilters = AbstractProject.getSourceFileFilter(); + for (const fpath in eTarget.sourceArgs) { + const flags: string[] = []; + const sourceArgs = eTarget.sourceArgs[fpath]; + if (AbstractProject.asmfileFilter.test(fpath)) { + sourceArgs.sIncDirs.forEach(arg => flags.push(`-I${arg}`)); + sourceArgs.sMacros.forEach(arg => flags.push(`-D${arg}`)); + sourceArgs.assemblerArgs.forEach(arg => flags.push(arg)); + } else { + sourceArgs.cIncDirs.forEach(arg => flags.push(`-I${arg}`)); + sourceArgs.cMacros.forEach(arg => flags.push(`-D${arg}`)); + sourceArgs.cCompilerArgs.forEach(arg => flags.push(arg)); + } + if (flags.length > 0) { + if (srcFilters.some(r => r.test(fpath))) + srcOptions[fpath] = ArrayDelRepetition(flags).join(' '); + else + srcOptions[fpath + '/*'] = ArrayDelRepetition(flags).join(' '); + } + } + } + nPrjConfig.targets[eTarget.name] = nEideTarget; } @@ -2880,62 +2874,9 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco // save all config basePrj.prjConfig.Save(); - - // show warning - - var getAllKeys = (obj: any): string[] => { - if (typeof obj != 'object') return []; - const keys: string[] = []; - for (const key in obj) keys.push(key); - return keys; - }; - - if (getAllKeys(ePrjInfo.envs).length > 0 || - ePrjInfo.targets.some(t => getAllKeys(t.incompatibleArgs).length > 0)) { - - let warnLines = [ - `!!! ${WARNING} !!!`, - '', - view_str$prompt$eclipse_imp_warning, - '', - '---', - '' - ]; - - if (getAllKeys(ePrjInfo.envs).length > 0) { - warnLines.push( - `##### Eclipse Project Environment Variables #####`, - ``, - yaml.stringify({ 'Envs': ePrjInfo.envs }), - `` - ); - } - - warnLines.push( - `##### Configurations For All Targets #####`, - `` - ); - - ePrjInfo.targets.forEach(target => { - warnLines.push( - `//`, - `///// Target: '${target.name}' /////`, - `//`, - '', - yaml.stringify({ 'Incompatible Args': target.incompatibleArgs }), - '' - ); - }); - - const f = File.fromArray([ePrjRoot.path, `eclipse.${AbstractProject.importerWarningBaseName}`]); - f.Write(warnLines.join(os.EOL)); - const doc = await vscode.workspace.openTextDocument(vscode.Uri.parse(f.ToUri())); - - vscode.window.showTextDocument(doc, { - preview: false, - selection: doc.lineAt(0).range, - }); - } + // save src options + const optFile = File.fromArray([basePrj.rootFolder.path, AbstractProject.EIDE_DIR, `files.options.yml`]); + optFile.Write(view_str$prompt$filesOptionsComment + yaml.stringify(srcOptsObj, { indent: 4 })); // switch project const selection = await vscode.window.showInformationMessage( diff --git a/src/EclipseProjectParser.ts b/src/EclipseProjectParser.ts index 0308a33f..c1c1dd9a 100644 --- a/src/EclipseProjectParser.ts +++ b/src/EclipseProjectParser.ts @@ -7,6 +7,7 @@ import { VirtualSource, AbstractProject } from './EIDEProject'; import { isArray } from 'util'; import { ArrayDelRepetition } from '../lib/node-utility/Utility'; import { File } from '../lib/node-utility/File'; +import { GlobalEvent } from './GlobalEvents'; export type EclipseProjectType = 'arm' | 'sdcc' | 'riscv' | 'gcc'; @@ -21,6 +22,15 @@ export interface EclipseProjectInfo { export interface EclipseBuilderArgs { + // "level-0", "level-1", "level-2", "level-3", "level-size", "level-size-Oz", "level-fast", "level-debug" + optimization: string; + // "c89", "c90", "c99", "c11", "c17", "c23", "gnu89", "gnu90", "gnu99", "gnu11", "gnu17", "gnu23" + cLanguageStd: string; + // "c++98", "gnu++98", "c++11", "gnu++11", "c++14", "gnu++14", "c++17", "gnu++17", "c++20", "gnu++20", "c++23", "gnu++23" + cppLanguageStd: string; + // Signed Char (-fsigned-char) + signedChar: boolean; + globalArgs: string[]; cIncDirs: string[]; @@ -33,17 +43,25 @@ export interface EclipseBuilderArgs { linkerArgs: string[]; linkerLibArgs: string[]; + linkerLibSearchDirs: string[]; } export interface EclipseProjectTarget { name: string; excList: string[]; - globalArgs: EclipseBuilderArgs; - incompatibleArgs: { [path: string]: EclipseBuilderArgs }; // only use to show for user, not use in program + builldArgs: EclipseBuilderArgs; + sourceArgs: { [path: string]: EclipseBuilderArgs }; + archName?: string; + linkerScriptPath?: string; + objsOrder: string[]; } function newEclipseBuilderArgs(): EclipseBuilderArgs { return { + optimization: 'level-debug', + cLanguageStd: 'c11', + cppLanguageStd: 'c++11', + signedChar: false, globalArgs: [], cIncDirs: [], cMacros: [], @@ -52,7 +70,8 @@ function newEclipseBuilderArgs(): EclipseBuilderArgs { sMacros: [], assemblerArgs: [], linkerArgs: [], - linkerLibArgs: [] + linkerLibArgs: [], + linkerLibSearchDirs: [] }; } @@ -84,12 +103,16 @@ export function formatFilePath(path: string): string { .replace('${ProjName}/', '') .replace('${ProjName}', '.') .replace('PROJECT_LOC/', '') - .replace('${PROJECT_LOC}/', '') - .replace(/^"+/, '') - .replace('${workspace_loc:/', '') - .replace(/"+$/, '') - .replace(/\}$/, '') - .replace(/\/+$/, ''); + .replace('${PROJECT_LOC}/', ''); + + // remove leading and tailing '"' + if (path.charAt(0) == '"' && path.charAt(path.length - 1) == '"') + path = path.substr(1, path.length - 2); + // conv ${workspace_loc:/xxx} to xxx + if (path.startsWith('${workspace_loc:/') && path.charAt(path.length - 1) == '}') + path = path.substr(17, path.length - 18); + // remove tailing '/' + path = path.replace(/\/+$/, ''); if (path.startsWith('/')) path = '.' + path; @@ -127,6 +150,8 @@ export async function parseEclipseProject(cprojectPath: string): Promise'`); cprjDom = node; + GlobalEvent.log_info(`[EclipseParser] start parsing ${cprojectPath} ...`); + const root_virtualsrcs: string[] = []; const root_srcdirs: string[] = []; const eclipseTargetList: string[] = []; @@ -161,24 +186,24 @@ export async function parseEclipseProject(cprojectPath: string): Promise') { + GlobalEvent.log_info(`[EclipseParser] skip option "${opVal.val[0]}" of path "${folderPath}"`); + } else { + builderArgs.globalArgs = builderArgs.globalArgs.concat(opVal.val); + } } else { - incompatibleArgs.globalArgs.push(`<${globOpts.$['name']}> = ${globOpts.$['value']}`); + GlobalEvent.log_info( + `[EclipseParser] unknown option "${globOpts.$['name'] || globOpts.$['id']}" of path "${folderPath}"`); } } @@ -201,21 +249,34 @@ export async function parseEclipseProject(cprojectPath: string): Promise { - const opVal = parseToolOption(op); + const opVal = parseToolOption(op, PROJ_INFO.type); if (opVal) { - if (opVal.type == 'definedSymbols') { + if (opVal.type == 'linkerScriptPath') { + if (isRootResource) + tInfo.linkerScriptPath = opVal.val.join(','); + } else if (opVal.type == 'archName') { + if (isRootResource) + tInfo.archName = opVal.val[0]; + } else if (opVal.type == 'definedSymbols') { builderArgs.cMacros = builderArgs.cMacros.concat(opVal.val); - } - else if (opVal.type == 'includePath') { + } else if (opVal.type == 'includePath') { builderArgs.cIncDirs = builderArgs.cIncDirs.concat(opVal.val); - } - else { + } else if (opVal.type == 'optimization') { + builderArgs.optimization = opVal.val[0]; + } else if (opVal.type == 'cLanguageStd') { + builderArgs.cLanguageStd = opVal.val[0]; + } else if (opVal.type == 'signedChar') { + builderArgs.signedChar = opVal.val[0] == 'true'; + } else if (opVal.type == '') { + GlobalEvent.log_info(`[EclipseParser] skip option "${opVal.val[0]}" of path "${folderPath}"`); + } else { builderArgs.cCompilerArgs = builderArgs.cCompilerArgs.concat(opVal.val); } } else { - incompatibleArgs.cCompilerArgs.push(`<${op.$['name']}> = ${op.$['value']}`); + GlobalEvent.log_info( + `[EclipseParser] unknown option "${op.$['name'] || op.$['id']}" of path "${folderPath}"`); } }); } @@ -223,35 +284,50 @@ export async function parseEclipseProject(cprojectPath: string): Promise { - const opVal = parseToolOption(op); + const opVal = parseToolOption(op, PROJ_INFO.type); if (opVal) { if (opVal.type == 'definedSymbols') { builderArgs.sMacros = builderArgs.sMacros.concat(opVal.val); - } - else if (opVal.type == 'includePath') { + } else if (opVal.type == 'includePath') { builderArgs.sIncDirs = builderArgs.sIncDirs.concat(opVal.val); - } - else { + } else if (opVal.type == '') { + GlobalEvent.log_info(`[EclipseParser] skip option "${opVal.val[0]}" of path "${folderPath}"`); + } else { builderArgs.assemblerArgs = builderArgs.assemblerArgs.concat(opVal.val); } } else { - incompatibleArgs.assemblerArgs.push(`<${op.$['name']}> = ${op.$['value']}`); + GlobalEvent.log_info( + `[EclipseParser] unknown option "${op.$['name'] || op.$['id']}" of path "${folderPath}"`); } }); } // for linker - else if (toolOptName.includes('Linker')) { + else if (toolOptName.includes('C Linker')) { toArray(toolOpts.option).forEach(op => { - const opVal = parseToolOption(op); + const opVal = parseToolOption(op, PROJ_INFO.type); if (opVal) { - if (opVal.type == 'libs' || opVal.type == 'userObjs') { + if (opVal.type == 'linkerScriptPath') { + if (isRootResource) + tInfo.linkerScriptPath = opVal.val.join(','); + } else if (opVal.type == 'archName') { + if (isRootResource) + tInfo.archName = opVal.val[0]; + } else if (opVal.type == 'objsOrder') { + if (isRootResource) + tInfo.objsOrder = tInfo.objsOrder.concat(opVal.val); + } else if (opVal.type == 'libs' || opVal.type == 'userObjs') { builderArgs.linkerLibArgs = builderArgs.linkerLibArgs.concat(opVal.val); + } else if (opVal.type == 'linkerLibSearchDirs') { + builderArgs.linkerLibSearchDirs = builderArgs.linkerLibSearchDirs.concat(opVal.val); + } else if (opVal.type == '') { + GlobalEvent.log_info(`[EclipseParser] skip option "${opVal.val[0]}" of path "${folderPath}"`); } else { builderArgs.linkerArgs = builderArgs.linkerArgs.concat(opVal.val); } } else { - incompatibleArgs.linkerArgs.push(`<${op.$['name']}> = ${op.$['value']}`); + GlobalEvent.log_info( + `[EclipseParser] unknown option "${op.$['name'] || op.$['id']}" of path "${folderPath}"`); } }); } @@ -402,46 +478,35 @@ export async function parseEclipseProject(cprojectPath: string): Promisetarget.incompatibleArgs[pName]; - for (const key in obj) { - if (isArray(obj[key])) { - obj[key] = ArrayDelRepetition(obj[key]); - } - } - } } + GlobalEvent.log_info(`[EclipseParser] parse done.`); return PROJ_INFO; } -function parseToolOption(optionObj: any): { type: string, val: string[] } | undefined { +function parseToolOption(optionObj: any, prjtype: EclipseProjectType): { type: string, val: string[] } | undefined { - if (optionObj.$['id']) { - // skip output args - if (['.converthex', '.convertbin', '.convert'] - .some(s => optionObj.$['id'].includes(s))) { - return; - } - } + if (!optionObj.$['id']) + return { type: '', val: [optionObj.$['name'] || 'noid'] }; //-- + const VALUE_ID: string = optionObj.$['id']; const VALUE_NAME: string = optionObj.$['name'] || ''; - const VALUE_VAL: string = optionObj.$['value'] || ''; + const VALUE_VALS: string[] = []; const VALUE_TYPE: string | undefined = optionObj.$['valueType']; + const IGNORED_VAL = { type: '', val: [VALUE_NAME || VALUE_ID] }; - let makeResult = (value: string | string[], typ?: string): { type: string, val: string[] } | undefined => { + const makeResult = (value: string | string[], typ?: string): { type: string, val: string[] } | undefined => { if (value == '') return undefined; if (isArray(value) && value.length == 0) return undefined; return { @@ -450,35 +515,75 @@ function parseToolOption(optionObj: any): { type: string, val: string[] } | unde }; }; - let formatArgs = (fmt: string, arg: string): string => { - - if (fmt.includes('[option]')) { + const formatArgs = (fmt: string, arg: string): string => { + if (fmt.includes('[option]')) return fmt.replace('[option]', arg); - } - return fmt + arg; } - // - // match by name - // + const cLangStds = [ "c89", "c90", "c99", "c11", "c17", "c23", "gnu89", "gnu90", "gnu99", "gnu11", "gnu17", "gnu23" ]; - // = option.std.gnu99 - if (/Language Standard/i.test(VALUE_NAME)) { - const m = /std\.(\w+)/.exec(VALUE_VAL); - if (m && m.length > 1) { - const langStd = m[1]; - return makeResult(`-std=${langStd}`, 'string'); - } + // ---- + // parse special type values + // ---- + + if (VALUE_TYPE == 'definedSymbols') { + toArray(optionObj.listOptionValue) + .forEach(item => VALUE_VALS.push(item.$['value'])); + return makeResult(VALUE_VALS, 'definedSymbols'); + } else if (VALUE_TYPE == 'includePath') { + toArray(optionObj.listOptionValue).forEach(item => { + let p = formatFilePath(item.$['value']); + if (p == '..') p = '.'; + if (p.startsWith('../')) p = p.substr(3); // for eclipse, include path is base 'Debug' folder + VALUE_VALS.push(p); + }); + return makeResult(VALUE_VALS, 'includePath'); + } else if (VALUE_TYPE == 'libPaths') { + toArray(optionObj.listOptionValue).forEach(item => { + let p = formatFilePath(item.$['value']); + if (p == '..') p = '.'; + if (p.startsWith('../')) p = p.substr(3); // for eclipse, include path is base 'Debug' folder + VALUE_VALS.push(p); + }); + return makeResult(VALUE_VALS, 'linkerLibSearchDirs'); + } else if (VALUE_TYPE == 'libs') { + toArray(optionObj.listOptionValue) + .forEach(item => VALUE_VALS.push(`-l${item.$['value']}`)); + return makeResult(VALUE_VALS, 'libs'); + } else if (VALUE_TYPE == 'userObjs') { + toArray(optionObj.listOptionValue) + .forEach(item => VALUE_VALS.push(formatFilePath(item.$['value']))); + return makeResult(VALUE_VALS, 'userObjs'); + } + + // ---- + // parse generic type values + // ---- + + if (VALUE_TYPE == 'boolean') { + VALUE_VALS.push(optionObj.$['value'] || 'false'); + } else if (VALUE_TYPE == 'stringList') { + toArray(optionObj.listOptionValue) + .forEach(item => VALUE_VALS.push(formatFilePath(item.$['value']))); + } else { // string or enums + const val = optionObj.$['value']?.trim() || ''; + VALUE_VALS.push(formatFilePath(val)); } - if (VALUE_NAME.includes('(-D)')) { + if (VALUE_VALS.length == 0) + return IGNORED_VAL; + + // ---- + // Eclipse Generic + // ---- + + if (VALUE_NAME.includes('(-D)') || /Defined symbols/i.test(VALUE_NAME)) { const li: string[] = []; toArray(optionObj.listOptionValue).forEach(item => li.push(item.$['value'])); return makeResult(li, 'definedSymbols'); } - - if (VALUE_NAME.includes('(-I)')) { + if (VALUE_NAME.includes('(-I)') || VALUE_ID.includes('include.paths')) { const li: string[] = []; toArray(optionObj.listOptionValue).forEach(item => { let p = formatFilePath(item.$['value']); @@ -488,77 +593,201 @@ function parseToolOption(optionObj: any): { type: string, val: string[] } | unde }); return makeResult(li, 'includePath'); } - - // - // match by type - // - - if (VALUE_TYPE == undefined) - return; - - if (VALUE_TYPE == 'boolean') { - if (VALUE_VAL == 'true') { - const mRes = /\((\-.+)\)/.exec(VALUE_NAME); - if (mRes && mRes.length > 1) { - return makeResult(mRes[1]); - } + // ilg.gnuarmeclipse.managedbuild.cross.option.c.linker.scriptfile + if (VALUE_ID.includes('linker.script')) { + return makeResult(VALUE_VALS, 'linkerScriptPath'); + } + // ilg.gnuarmeclipse.managedbuild.cross.option.optimization.level = optimization.level.debug + if (VALUE_ID.includes('optimization.level')) { + if (/level\.none/.test(VALUE_VALS[0])) { + return makeResult(`level-0`, 'optimization'); + } else if (/level\.debug/.test(VALUE_VALS[0])) { + return makeResult(`level-debug`, 'optimization'); + } else if (/level\.optimize/.test(VALUE_VALS[0])) { + return makeResult(`level-1`, 'optimization'); + } else if (/level\.more/.test(VALUE_VALS[0])) { + return makeResult(`level-2`, 'optimization'); + } else if (/level\.most/.test(VALUE_VALS[0])) { + return makeResult(`level-3`, 'optimization'); + } else if (/level\.size/.test(VALUE_VALS[0])) { + return makeResult(`level-size`, 'optimization'); + } else { + return IGNORED_VAL; } } - - else if (VALUE_TYPE == 'definedSymbols') { - const li: string[] = []; - toArray(optionObj.listOptionValue).forEach(item => li.push(item.$['value'])); - return makeResult(li); + // linker.nostdlibs = false + if (VALUE_ID.includes('linker.nostdlibs')) { + if (VALUE_VALS[0] === 'true') + return makeResult(`-nostdlib`, 'string'); + return IGNORED_VAL; } - - else if (VALUE_TYPE == 'includePath') { - const li: string[] = []; + // arm.target.family + if (VALUE_ID.includes('arm.target.family')) { + const m = /\.(cortex-m\w+)/.exec(VALUE_VALS[0]); + if (m && m.length > 1) + return makeResult(m[1], 'archName'); + return IGNORED_VAL; + } + // option.c.linker.paths + if (VALUE_ID.includes('option.c.linker.paths')) { toArray(optionObj.listOptionValue).forEach(item => { let p = formatFilePath(item.$['value']); if (p == '..') p = '.'; if (p.startsWith('../')) p = p.substr(3); // for eclipse, include path is base 'Debug' folder - li.push(p); + VALUE_VALS.push(p); }); - return makeResult(li); + return makeResult(VALUE_VALS, 'linkerLibSearchDirs'); + } + // optimization.signedchar + if (VALUE_ID.includes('optimization.signedchar')) { + if (VALUE_VALS[0] === 'true') + return makeResult('true', 'signedChar'); + return IGNORED_VAL; + } + // option.warnings.extrawarn + if (VALUE_ID.includes('option.warnings.extrawarn')) { + if (VALUE_VALS[0] === 'true') + return makeResult(`-Wextra`, 'string'); + return IGNORED_VAL; + } + // option.warnings.allwarn + if (VALUE_ID.includes('option.warnings.allwarn')) { + if (VALUE_VALS[0] === 'true') + return makeResult(`-Wall`, 'string'); + return IGNORED_VAL; + } + // option.c.compiler.otherwarnings + if (VALUE_ID.includes('option.c.compiler.otherwarnings')) { + if (VALUE_VALS[0]) + return makeResult(VALUE_VALS[0], 'string'); + return IGNORED_VAL; + } + // option.c.linker.nostart + if (VALUE_ID.includes('option.c.linker.nostart')) { + if (VALUE_VALS[0] === 'true') + return makeResult(`-nostartfiles`, 'string'); + return IGNORED_VAL; + } + // ignore these options + if (VALUE_NAME.includes('-fno-rtti') || + VALUE_NAME.includes('-fno-use-cxa-atexit') || + VALUE_NAME.includes('-fno-threadsafe-statics') || + VALUE_NAME.includes('-ffunction-sections') || + VALUE_NAME.includes('-fdata-sections') || + VALUE_NAME.includes('--gc-sections')) { + return IGNORED_VAL; + } + + // ---- + // silabs + // ---- + + // = false + if (VALUE_ID.includes('optimization.shortenums')) { + const v = VALUE_VALS[0]; + if (v === "true") + return makeResult(`-fshort-enums`, 'string'); + return IGNORED_VAL; + } + // = option.std.gnu99 + // compiler.misc.dialect = com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.compiler.misc.dialect.c99 + if (/Language (?:Standard|Dialect)/i.test(VALUE_NAME)) { + let m = /std\.(\w+)/.exec(VALUE_VALS[0]); + if (m && m.length > 1) { + const langStd = m[1]; + if (cLangStds.includes(langStd)) + return makeResult(langStd, 'cLanguageStd'); + } + m = /dialect\.(\w+)/.exec(VALUE_VALS[0]); + if (m && m.length > 1) { + const langStd = m[1]; + if (cLangStds.includes(langStd)) + return makeResult(langStd, 'cLanguageStd'); + } + return IGNORED_VAL; + } + // = ./platform/Device/SiliconLabs/EFR32BG22/Source/GCC/startup_efr32bg22.o; + if (VALUE_NAME.trim() == 'Linker input ordering') { + const paths = VALUE_VALS[0].split(';') + .filter(p => p.trim() !== '') + .map(p => '**/' + formatFilePath(p).replace(/^\.\//, '')); + if (paths.length == 0) + return IGNORED_VAL; + return makeResult(paths.slice(0, Math.min(5, paths.length)), 'objsOrder'); } + if (prjtype == 'arm') { + if (VALUE_NAME.includes('--specs=nano.specs') || + VALUE_NAME.includes('--specs=nosys.specs')) + return IGNORED_VAL; + } + + // ---- + // STM32CubeIDE + // ---- + + // value="STM32F407VGTx" valueType="string" + if (VALUE_ID.includes('com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_mcu')) { + const mcuName = VALUE_VALS[0].toLowerCase(); + if (/stm32f0/.test(mcuName)) + return makeResult('cortex-m0', 'archName'); + else if (/stm32(g0|c0|l0|u0)/.test(mcuName)) + return makeResult('cortex-m0+', 'archName'); + else if (/stm32(f1|f2|l1)/.test(mcuName)) + return makeResult('cortex-m3', 'archName'); + else if (/stm32(f4|g4|f3|l4|wb[0-9]|wl)/.test(mcuName)) + return makeResult('cortex-m4', 'archName'); + else if (/stm32(h5|u5|u3|l5|wba)/.test(mcuName)) + return makeResult('cortex-m33', 'archName'); + else if (/stm32(h7|f7)/.test(mcuName)) + return makeResult('cortex-m7', 'archName'); + return IGNORED_VAL; + } + + // value="${workspace_loc:/${ProjName}/STM32F407VGTX_FLASH.ld}" valueType="string" + if (VALUE_ID.includes('com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.option.script')) { + if (VALUE_VALS[0]) + return makeResult(formatFilePath(VALUE_VALS[0]), 'linkerScriptPath'); + } + + // ---- + // match (-xxx) options by types + // ---- - else if (VALUE_TYPE == 'string') { + if (VALUE_TYPE == 'boolean') { + if (VALUE_VALS[0] == 'true') { + const mRes = /\((\-.+)\)/.exec(VALUE_NAME); + if (mRes && mRes.length > 1) { + return makeResult(mRes[1]); + } + } + } + if (VALUE_TYPE == 'string') { const mRes = /\((\-.+)\)/.exec(VALUE_NAME); if (mRes && mRes.length > 1) { const fmt = mRes[1]; - const arg = formatArgs(fmt, formatFilePath(VALUE_VAL)); + const arg = formatArgs(fmt, formatFilePath(VALUE_VALS[0])); return makeResult(arg); - } - else if (VALUE_VAL.startsWith('-')) { - return makeResult(VALUE_VAL); + } else if (VALUE_VALS[0].startsWith('-')) { + return makeResult(VALUE_VALS[0]); } } - - else if (VALUE_TYPE == 'stringList') { + if (VALUE_TYPE == 'stringList') { const mRes = /\((\-.+)\)/.exec(VALUE_NAME); if (mRes && mRes.length > 1) { const fmt = mRes[1]; const li: string[] = []; - toArray(optionObj.listOptionValue).forEach(item => li.push(formatArgs(fmt, formatFilePath(item.$['value'])))); + toArray(optionObj.listOptionValue) + .forEach(item => li.push(formatArgs(fmt, formatFilePath(item.$['value'])))); return makeResult(li); } else { const li: string[] = []; - toArray(optionObj.listOptionValue).forEach(item => li.push(formatFilePath(item.$['value']))); + toArray(optionObj.listOptionValue) + .forEach(item => li.push(formatFilePath(item.$['value']))); return makeResult(li); } } - else if (VALUE_TYPE == 'libs') { - const r: string[] = []; - toArray(optionObj.listOptionValue).forEach(item => r.push(`-l${item.$['value']}`)); - return makeResult(r); - } - - else if (VALUE_TYPE == 'userObjs') { - const r: string[] = []; - toArray(optionObj.listOptionValue).forEach(item => r.push(formatFilePath(item.$['value']))); - return makeResult(r); - } + return IGNORED_VAL; } function toArray(obj: any): any[] { diff --git a/src/GlobalEvents.ts b/src/GlobalEvents.ts index 6140146b..351429d9 100644 --- a/src/GlobalEvents.ts +++ b/src/GlobalEvents.ts @@ -23,7 +23,7 @@ */ import * as events from 'events'; -import { Message, ExceptionToMessage, newMessage } from './Message'; +import { Message, ExceptionToMessage, newMessage, MessageType } from './Message'; let _globalEvent: GlobalEvent | undefined; @@ -75,6 +75,9 @@ export class GlobalEvent { //msg static emit(event: 'error', error: Error): boolean; // 错误弹框(vscode 右下角) + /** + * @deprecated 已废弃,改用 GlobalEvent.show_msgbox + */ static emit(event: 'msg', msg: Message): boolean; // 消息弹框(vscode 右下角) static emit(event: 'globalLog', msg: Message): boolean; // 打印日志+换行 -> 输出面板 static emit(event: 'globalLog.append', log: string): boolean; // 打印原始日志字串 -> 输出面板 @@ -83,6 +86,14 @@ export class GlobalEvent { return GlobalEvent.GetInstance()._emitter.emit(event, args); } + //vscode notify + static show_msgbox(type: MessageType, msg: string | Error) { + if (msg instanceof Error) + return GlobalEvent.GetInstance()._emitter.emit('msg', ExceptionToMessage(msg, type)); + else + return GlobalEvent.GetInstance()._emitter.emit('msg', newMessage(type, msg)); + } + //log static log_info(msg: string) { GlobalEvent.GetInstance()._emitter.emit('globalLog', newMessage('Info', msg)); diff --git a/src/HexUploader.ts b/src/HexUploader.ts index 37837fa3..327df385 100644 --- a/src/HexUploader.ts +++ b/src/HexUploader.ts @@ -787,7 +787,7 @@ class STLinkUploader extends HexUploader { // run let cmd = `${commandLine} ${options.otherCmds || ''}`.trimEnd(); - if (osType() == 'win32' && exe.noSuffixName.toLowerCase().startsWith('stm32_programmer_cli')) + if (osType() == 'win32') cmd = 'chcp 437 && ' + cmd; // chcp 437: 去除进度条乱码 this.executeShellCommand(this.toolType, cmd, diff --git a/src/ResInstaller.ts b/src/ResInstaller.ts index e4c1600c..7da025be 100644 --- a/src/ResInstaller.ts +++ b/src/ResInstaller.ts @@ -295,7 +295,7 @@ export class ResInstaller { this.locker.delete(name.toLowerCase()); } - async refreshExternalToolsIndex() { + async refreshExternalToolsIndex(force?: boolean) { const indexCacheFile = File.from(ResManager.instance().GetTmpDir().path, 'eide_external_tools.index.json'); const defIdxUrl = 'https://raw.githubusercontent.com/github0null/eide_default_external_tools_index/master/index.json'; @@ -304,6 +304,8 @@ export class ResInstaller { let cont: string | Error | undefined; try { + if (force) + throw Error(); if (indexCacheFile.IsFile() && new Date().getTime() < fs.statSync(indexCacheFile.path).mtime.getTime() + (6 * 3600 * 1000)) cont = indexCacheFile.Read(); diff --git a/src/StringTable.ts b/src/StringTable.ts index ababa2cf..485dd1df 100644 --- a/src/StringTable.ts +++ b/src/StringTable.ts @@ -757,7 +757,7 @@ export const rating_text = [ export const later_text = [ '稍后', - 'Later on' + 'Later' ][langIndex]; export const continue_text = [ diff --git a/src/ToolchainManager.ts b/src/ToolchainManager.ts index 93817740..eed8b19f 100644 --- a/src/ToolchainManager.ts +++ b/src/ToolchainManager.ts @@ -2400,7 +2400,9 @@ class GCC implements IToolchian { global: { "$float-abi-type": 'hard', "output-debug-info": 'enable', - "misc-control": "--specs=nosys.specs --specs=nano.specs" + "use-newlib-nano": true, + "not-use-syscalls": true, + "misc-control": "" }, 'c/cpp-compiler': { "language-c": "c11", diff --git a/src/extension.ts b/src/extension.ts index ea282c59..614a825f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -42,7 +42,8 @@ import { view_str$operation$serialport, view_str$operation$baudrate, view_str$operation$serialport_name, txt_install_now, txt_yes, view_str$prompt$feedback, rating_text, later_text, sponsor_author_text, view_str$prompt$install_dotnet_and_restart_vscode, - view_str$prompt$install_dotnet_failed + view_str$prompt$install_dotnet_failed, + view_str$prompt$not_found_compiler } from './StringTable'; import { LogDumper } from './LogDumper'; import { StatusBarManager } from './StatusBarManager'; @@ -118,6 +119,19 @@ export async function activate(context: vscode.ExtensionContext) { subscriptions.push(vscode.commands.registerCommand('eide.ReloadStm8Devs', () => reloadStm8Devices())); subscriptions.push(vscode.commands.registerCommand('eide.create.clang-format.file', () => newClangFormatFile())); subscriptions.push(vscode.commands.registerCommand('eide.cleanCache', () => cleanCache())); + subscriptions.push(vscode.commands.registerCommand('eide.refresh.external_tools_index', () => { + ResInstaller.instance() + .refreshExternalToolsIndex(true) + .then(() => GlobalEvent.emit('msg', newMessage('Info', 'Refresh Done.'))) + .catch(err => { + GlobalEvent.log_warn(err); + GlobalEvent.log_show(); + }); + })); + subscriptions.push(vscode.commands.registerCommand('eide.debug.start', () => { + startDebugging() + .catch(err => GlobalEvent.emit('error', err)); + })); // internal command // TODO @@ -283,6 +297,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.debug.registerDebugConfigurationProvider('eide.cortex-debug', new CortexDebugConfigProvider(), + vscode.DebugConfigurationProviderTriggerKind.Dynamic); // auto save project projectExplorer.enableAutoSave(true); @@ -1573,6 +1589,8 @@ class EideTerminalProvider implements vscode.TerminalProfileProvider { import { FileWatcher } from '../lib/node-utility/FileWatcher'; import { ToolchainManager, ToolchainName } from './ToolchainManager'; +import { JLinkOptions, JLinkProtocolType, OpenOCDFlashOptions, PyOCDFlashOptions, STLinkOptions } from './HexUploader'; +import { AbstractProject } from './EIDEProject'; type MapViewParserType = 'memap' | 'builtin'; @@ -1868,6 +1886,275 @@ class MapViewEditorProvider implements vscode.CustomTextEditorProvider { } } +//------------------------------------------------------------ +//- Debug Config Provider +//------------------------------------------------------------ + +class CortexDebugConfigProvider implements vscode.DebugConfigurationProvider { + + provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, + token?: vscode.CancellationToken): vscode.ProviderResult { + + const result: vscode.DebugConfiguration[] = []; + + if (!folder) + return result; + + const prj = projectExplorer.getActiveProject(); + if (!prj) + return result; + + let wsDir = folder.uri.fsPath; + let prjDir = prj.getRootDir().path; + if (platform.osType() == 'win32') { + wsDir = wsDir.toLowerCase(); + prjDir = prjDir.toLowerCase(); + } + if (wsDir != prjDir) + return result; + + const toolchain = prj.getToolchain(); + const toolchainManager = ToolchainManager.getInstance(); + const settingManager = SettingManager.instance(); + + switch (toolchain.name) { + case 'AC5': + case 'AC6': + case 'GCC': + if (!toolchainManager.isToolchainPathReady('GCC')) { + const msg = view_str$prompt$not_found_compiler.replace('{}', 'arm-none-eabi-gcc'); + ResInstaller.instance().setOrInstallTools('GCC', msg, 'EIDE.ARM.GCC.InstallDirectory'); + return result; + } + break; + case 'RISCV_GCC': + if (!toolchainManager.isToolchainPathReady(toolchain.name)) { + const msg = view_str$prompt$not_found_compiler.replace('{}', 'EIDE.RISCV.InstallDirectory'); + GlobalEvent.emit('msg', newMessage('Warning', msg)); + return result; + } + break; + case 'ANY_GCC': + if (!toolchainManager.isToolchainPathReady(toolchain.name)) { + const msg = view_str$prompt$not_found_compiler.replace('{}', 'EIDE.Toolchain.AnyGcc.InstallDirectory'); + GlobalEvent.emit('msg', newMessage('Warning', msg)); + return result; + } + break; + default: + return result; + } + + const newDebugCfg = (prj: AbstractProject) => { + + const dbgCfg: vscode.DebugConfiguration = { + type: 'cortex-debug', + name: 'Debug', + request: 'launch' + }; + + const toolchain = prj.getToolchain(); + const settingManager = SettingManager.instance(); + switch (toolchain.name) { + case 'AC5': + case 'AC6': + case 'GCC': + dbgCfg['toolchainPrefix'] = settingManager.getGCCPrefix().replace(/-$/, ''); + dbgCfg['armToolchainPath'] = NodePath.join(settingManager.getGCCDir().path, 'bin'); + break; + default: + if (toolchain.getToolchainPrefix) + dbgCfg['toolchainPrefix'] = toolchain.getToolchainPrefix().replace(/-$/, ''); + dbgCfg['armToolchainPath'] = NodePath.join(toolchain.getToolchainDir().path, 'bin'); + break; + } + + dbgCfg['cwd'] = prj.getRootDir().path; + dbgCfg['executable'] = prj.getExecutablePathWithoutSuffix() + '.elf'; + dbgCfg['runToEntryPoint'] = 'main'; + dbgCfg['liveWatch'] = { + 'enabled': true, + 'samplesPerSecond': 4 + }; + + const device = prj.GetPackManager().getCurrentDevInfo(); + if (device && device.svdPath) { + dbgCfg['svdFile'] = prj.ToRelativePath(device.svdPath) || device.svdPath; + GlobalEvent.log_info(`[debug config] Use svd file: ${dbgCfg['svdFile']}`); + } else { + const searchDirs = [ + prj.getRootDir(), + prj.getEideDir(), + File.from(prj.getRootDir().path, 'tools') + ]; + for (const d of searchDirs) { + const r = d.GetList([/\.svd$/i], File.EXCLUDE_ALL_FILTER); + if (r.length) { + dbgCfg['svdFile'] = prj.ToRelativePath(r[0].path) || r[0].path; + GlobalEvent.log_info(`[debug config] Use svd file: ${dbgCfg['svdFile']}`); + break; + } + } + } + + return dbgCfg; + }; + + const fmtOpenocdCfgPath = (type: 'interface' | 'target', path: string) => { + let cfgpath = path.startsWith('${workspaceFolder}/') + ? path.replace('${workspaceFolder}/', '') + : `${type}/${path}`; + if (cfgpath && !cfgpath.endsWith('.cfg')) + cfgpath += '.cfg'; + return cfgpath; + }; + + const flasherOpts = prj.GetConfiguration().uploadConfigModel.data; + const flashertype = prj.getUploaderType(); + + if (flashertype == 'JLink') { + const dbgCfg = newDebugCfg(prj); + const flasherCfg = (flasherOpts); + dbgCfg['name'] = 'Debug: JLINK'; + dbgCfg['servertype'] = 'jlink'; + dbgCfg['interface'] = flasherCfg.proType == JLinkProtocolType.JTAG ? 'jtag' : 'swd'; + dbgCfg['device'] = flasherCfg.cpuInfo.cpuName; + dbgCfg['serverpath'] = NodePath.join(settingManager.getJlinkDir(), + platform.osType() == 'win32' ? 'JLinkGDBServerCL.exe' : 'JLinkGDBServerCLExe'); + if (flasherCfg.otherCmds) { + // -IP Selects a specific J-Link + let m = /-IP ([^\s]+)/.exec(flasherCfg.otherCmds); + if (m && m.length > 1) + dbgCfg['ipAddress'] = m[1]; + // -USB Selects a specific J-Link + m = /(?:-USB|-SelectEmuBySN) ([^\s]+)/.exec(flasherCfg.otherCmds); + if (m && m.length > 1) + dbgCfg['serialNumber'] = m[1]; + } + result.push(dbgCfg); + } + + else if (flashertype == 'OpenOCD') { + const dbgCfg = newDebugCfg(prj); + const flasherCfg = (flasherOpts); + dbgCfg['name'] = 'Debug: OpenOCD'; + dbgCfg['servertype'] = 'openocd'; + const ocdCfgs: string[] = []; + if (flasherCfg.interface.trim()) + ocdCfgs.push(fmtOpenocdCfgPath('interface', flasherCfg.interface)); + if (flasherCfg.target.trim()) + ocdCfgs.push(fmtOpenocdCfgPath('target', flasherCfg.target)); + dbgCfg['configFiles'] = ocdCfgs; + dbgCfg['serverpath'] = settingManager.getOpenOCDExePath(); + result.push(dbgCfg); + } + + else if (flashertype == 'pyOCD') { + const dbgCfg = newDebugCfg(prj); + const flasherCfg = (flasherOpts); + dbgCfg['name'] = 'Debug: pyOCD'; + dbgCfg['servertype'] = 'pyocd'; + dbgCfg['targetId'] = flasherCfg.targetName; + const cliArgs: string[] = []; + if (flasherCfg.config) { + const confFile = new File(prj.ToAbsolutePath(flasherCfg.config)); + if (confFile.IsFile()) { + cliArgs.push('--config'); + cliArgs.push(confFile.path); + } + } + if (flasherCfg.otherCmds) + cliArgs.push(flasherCfg.otherCmds); + if (flasherCfg.speed) { + cliArgs.push('-f'); + cliArgs.push(flasherCfg.speed); + } + dbgCfg['serverArgs'] = cliArgs; + result.push(dbgCfg); + } + + else if (flashertype == 'STLink') { + const resManager = ResManager.instance(); + const flasherCfg = (flasherOpts); + // find ST-LINK_gdbserver + let gdbserverPath: string | undefined; + if (platform.osType() == 'win32') { + const gdbserver = File.from(resManager.getEideToolsInstallDir(), + 'stlink_gdb_server', 'bin', 'ST-LINK_gdbserver.exe'); + if (!gdbserver.IsFile()) { + ResInstaller.instance().setOrInstallTools('stlink_gdb_server', 'Not found ST-LINK_gdbserver.exe'); + return []; + } + gdbserverPath = gdbserver.path; + } else { + gdbserverPath = platform.find('ST-LINK_gdbserver'); + } + // find STM32_Programmer_CLI + let cubeProgramerPath: string | undefined; + if (platform.osType() == 'win32') { + const cubeProgramerExe = File.from(resManager.getEideToolsInstallDir(), + 'st_cube_programer', 'bin', 'STM32_Programmer_CLI.exe'); + if (!cubeProgramerExe.IsFile()) { + ResInstaller.instance().setOrInstallTools('STLink', 'Not found STM32_Programmer_CLI.exe'); + return []; + } + cubeProgramerPath = cubeProgramerExe.dir; + } else { + const p = platform.find('STM32_Programmer_CLI'); + if (p) + cubeProgramerPath = NodePath.dirname(p); + } + const dbgCfg = newDebugCfg(prj); + dbgCfg['name'] = 'Debug: STLink'; + dbgCfg['servertype'] = 'stlink'; + dbgCfg['interface'] = flasherCfg.proType == 'SWD' ? 'swd' : 'jtag'; + dbgCfg['stlinkPath'] = gdbserverPath; + dbgCfg['stm32cubeprogrammer'] = cubeProgramerPath; + result.push(dbgCfg); + } + + else { + GlobalEvent.emit('msg', newMessage('Warning', + `Only support 'jlink', 'stlink', 'openocd', 'pyocd'. Not support this flasher: '${flashertype}' !`)); + } + + // GlobalEvent.log_info(`provide Cortex-Debug DebugConfig`); + // GlobalEvent.log_info(yaml.stringify(result)); + return result; + } +} + +async function startDebugging() { + + const prj = projectExplorer.getActiveProject(); + if (!prj) { + GlobalEvent.show_msgbox('Warning', `No actived project to debug.`); + return; + } + + const vscWorkspaceFolder: vscode.WorkspaceFolder = { + uri: vscode.Uri.file(prj.getRootDir().path), + name: prj.getRootDir().name, + index: 0 + }; + + const cfgs = await (new CortexDebugConfigProvider()) + .provideDebugConfigurations(vscWorkspaceFolder); + if (cfgs && cfgs.length > 0) { + let cfg = cfgs[0]; + if (cfgs.length > 1) { + const val = await vscode.window.showQuickPick(cfgs.map(e => e.name), { + placeHolder: 'Select a debug configuration' + }); + if (val == undefined) + return; // user canceled + const idx = cfgs.findIndex(v => v.name === val); + cfg = cfgs[idx]; + } + await vscode.debug.startDebugging(vscWorkspaceFolder, cfg); + } +} + /////////////////////////////////////////////////// // KEIL_C51 -> SDCC converter ///////////////////////////////////////////////////