diff --git a/.gitignore b/.gitignore index 3fb4fce..060d0bc 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ vsc-extension-quickstart.md ./lib /dist /doc +/note.md pack.js test.js *.js.map diff --git a/CHANGELOG.md b/CHANGELOG.md index 10126de..a94822a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,28 @@ All notable version changes will be recorded in this file. *** +### [v3.26.0] update + +**Change**: + - `Project File`: Use yaml instead of json and update project data schema. Rename `eide.json` to `eide.yml`. + +**Fix**: + - `Target Switch`: Fix file's options was overrided when switch to other target. + +**Improve**: + - `Project Templates`: Add local cache for fetching remote templates repository. + +**Notice:** This version contains important changes. After the automatic migration is completed, you will no longer be able to open your project using the old version of the plugin. + +*** + +### [v3.25.7] revision + +**Improve**: + - `Builder Options`: Support ARM/Thumb Mode select options. Fix [issues 475](https://github.com/github0null/eide/issues/475) + +*** + ### [v3.25.6] revision **Fix**: diff --git a/lang/arm.gcc.verify.json b/lang/arm.gcc.verify.json index e442c05..f0ce73b 100644 --- a/lang/arm.gcc.verify.json +++ b/lang/arm.gcc.verify.json @@ -122,6 +122,27 @@ "description.zh-cn": "全局选项", "type": "object", "properties": { + "arm-thumb-mode": { + "readable_name": "ARM/Thumb Mode", + "readable_name.zh-cn": "ARM/Thumb Mode", + "markdownDescription": "Select between generating code that executes in ARM and Thumb states.", + "type": "string", + "default": "thumb", + "enum": [ + "arm", + "thumb" + ], + "enumDescriptions": [ + "ARM Mode", + "Thumb Mode" + ] + }, + "arm-thumb-interwork": { + "readable_name": "Thumb Interwork", + "readable_name.zh-cn": "Thumb Interwork", + "markdownDescription": "Generate code that supports calling between the ARM and Thumb instruction sets.", + "type": "boolean" + }, "$float-abi-type": { "markdownDescription": "Hardware floating-point ABI", "description.zh-cn": "硬件浮点 ABI", diff --git a/lang/arm.iar.verify.json b/lang/arm.iar.verify.json index 6e53f0a..5382fe1 100644 --- a/lang/arm.iar.verify.json +++ b/lang/arm.iar.verify.json @@ -122,6 +122,21 @@ "description.zh-cn": "全局选项", "type": "object", "properties": { + "arm-thumb-mode": { + "readable_name": "ARM/Thumb Mode", + "readable_name.zh-cn": "ARM/Thumb Mode", + "markdownDescription": "Select between generating code that executes in ARM and Thumb states.", + "type": "string", + "default": "thumb", + "enum": [ + "arm", + "thumb" + ], + "enumDescriptions": [ + "ARM Mode", + "Thumb Mode" + ] + }, "endian-mode": { "description": "Endian Mode", "type": "string", diff --git a/package-lock.json b/package-lock.json index e8ef1ef..e50de91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "eide", - "version": "3.21.1", + "version": "3.26.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "eide", - "version": "3.21.1", + "version": "3.26.0", "license": "MIT", "dependencies": { "iconv-lite": "^0.5.0", @@ -21,7 +21,7 @@ "x2js": "^3.4.1", "xml-formatter": "^2.6.1", "xml2js": "^0.6.2", - "yaml": "^1.10.2" + "yaml": "2.8.1" }, "devDependencies": { "@babel/core": "^7.24.7", @@ -4674,11 +4674,14 @@ "dev": true }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmmirror.com/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "bin": { + "yaml": "bin.mjs" + }, "engines": { - "node": ">= 6" + "node": ">= 14.6" } } }, @@ -8077,9 +8080,9 @@ "dev": true }, "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmmirror.com/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==" } } } diff --git a/package.json b/package.json index b2fa919..1f65be4 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.6", + "version": "3.26.0", "preview": false, "engines": { "vscode": "^1.67.0" @@ -54,9 +54,6 @@ }, "qna": "https://discuss.em-ide.com/t/FAQ", "activationEvents": [ - "workspaceContains:.eide/eide.json", - "workspaceContains:.eide/EIDE.json", - "workspaceContains:.EIDE/EIDE.json", "onStartupFinished" ], "icon": "res/icon/icon.png", @@ -111,7 +108,7 @@ "x2js": "^3.4.1", "xml-formatter": "^2.6.1", "xml2js": "^0.6.2", - "yaml": "^1.10.2" + "yaml": "2.8.1" }, "contributes": { "terminal": { diff --git a/res/data/models/arm.gcc.model.json b/res/data/models/arm.gcc.model.json index 4bc5158..047d39d 100644 --- a/res/data/models/arm.gcc.model.json +++ b/res/data/models/arm.gcc.model.json @@ -32,6 +32,35 @@ "linker" ] }, + "arm-thumb-interwork": { + "type": "selectable", + "command": { + "true": "-mthumb-interwork", + "false": "" + }, + "group": [ + "c", + "cpp", + "asm", + "linker" + ], + "location": "first" + }, + "arm-thumb-mode": { + "type": "selectable", + "command": { + "arm" : "-marm", + "thumb": "-mthumb", + "false": "-mthumb" + }, + "group": [ + "c", + "cpp", + "asm", + "linker" + ], + "location": "first" + }, "microcontroller-float": { "type": "selectable", "command": { @@ -381,8 +410,7 @@ "$output": "-o ${out} -MMD ${in}", "$default": [ "-c", - "-xc", - "-mthumb" + "-xc" ], "$language-c": { "type": "value", @@ -433,8 +461,7 @@ }, "$output": "-o ${out} -MMD ${in}", "$default": [ - "-c", - "-mthumb" + "-c" ], "$language-cpp": { "type": "value", @@ -482,8 +509,7 @@ }, "$default": [ "-c", - "-x assembler-with-cpp", - "-mthumb" + "-x assembler-with-cpp" ], "defines": { "type": "list", @@ -500,9 +526,7 @@ "useFile": true, "body": "@${value}" }, - "$default": [ - "-mthumb" - ], + "$default": [], "$output": "-o ${out} ${in} ${lib_flags}", "$outputSuffix": ".elf", "$linkMap": { diff --git a/res/data/models/arm.iar.model.json b/res/data/models/arm.iar.model.json index 44e73ba..303e60c 100644 --- a/res/data/models/arm.iar.model.json +++ b/res/data/models/arm.iar.model.json @@ -33,6 +33,20 @@ ], "location": "first" }, + "arm-thumb-mode": { + "type": "selectable", + "command": { + "arm" : "--arm", + "thumb": "--thumb", + "false": "--thumb" + }, + "group": [ + "c", + "cpp", + "asm" + ], + "location": "first" + }, "microcontroller-cpu": { "type": "keyValue", "command": "", @@ -169,7 +183,6 @@ }, "$output": "--dependencies=m + -o ${out} ${in}", "$default": [ - "--thumb", "--silent" ], "$listPath": { @@ -271,7 +284,6 @@ }, "$output": "--dependencies=m + -o ${out} ${in}", "$default": [ - "--thumb", "--silent" ], "$listPath": { @@ -365,7 +377,6 @@ "$output": "-o ${out} ${in}", "$default": [ "-S", - "--thumb", "-w+" ], "$listPath": { diff --git a/src/CodeBuilder.ts b/src/CodeBuilder.ts index 20a969e..f02e4f6 100644 --- a/src/CodeBuilder.ts +++ b/src/CodeBuilder.ts @@ -370,7 +370,7 @@ export abstract class CodeBuilder { const toolchain = this.project.getToolchain(); const outDir = File.ToUnixPath(this.project.getOutputDir()); - const compileOptions: BuilderOptions = this.project.GetConfiguration().compileConfigModel.getOptions(); + const compileOptions: BuilderOptions = this.project.GetConfiguration().toolchainConfigModel.getOptions(); const memMaxSize = this.getMcuMemorySize(); const sourceInfo = this.genSourceInfo(); const builderModeList: string[] = []; // build mode @@ -882,7 +882,7 @@ export class ARMCodeBuilder extends CodeBuilder { + File.sep + config.name + '.sct')); let data = ''; - const memTxt = this.GenMemTxtBySct(this.GetMemoryScatter(config.compileConfig.storageLayout)); + const memTxt = this.GenMemTxtBySct(this.GetMemoryScatter(config.toolchainConfig.storageLayout)); memTxt.forEach(tItem => { let str = tItem.name + tItem.addr + '{\r\n'; @@ -946,9 +946,9 @@ export class ARMCodeBuilder extends CodeBuilder { const prjConfig = this.project.GetConfiguration(); - if (!prjConfig.config.compileConfig.useCustomScatterFile) { + if (!prjConfig.config.toolchainConfig.useCustomScatterFile) { - const memLayout: ARMStorageLayout = JSON.parse(JSON.stringify(prjConfig.config.compileConfig.storageLayout)); + const memLayout: ARMStorageLayout = JSON.parse(JSON.stringify(prjConfig.config.toolchainConfig.storageLayout)); const res: MemorySize = {}; try { @@ -1001,9 +1001,9 @@ export class ARMCodeBuilder extends CodeBuilder { * * unify_builder 会根据这些条目来匹配 options.global['microcontroller-cpu'] 传入的 cpu_id, 然后生成对应的编译参数 */ - const cpu_id = config.compileConfig.cpuType.toLowerCase(); - let fpu_suffix = ARMCodeBuilder.getFpuSuffix(config.compileConfig.cpuType, - config.compileConfig.floatingPointHardware); + const cpu_id = config.toolchainConfig.cpuType.toLowerCase(); + let fpu_suffix = ARMCodeBuilder.getFpuSuffix(config.toolchainConfig.cpuType, + config.toolchainConfig.floatingPointHardware); /** * 某些情况下,fpu是通过添加 +<扩展名> 来开启,因此无需 fpu_suffix 标记 @@ -1029,7 +1029,7 @@ export class ARMCodeBuilder extends CodeBuilder { options.global['$clang-arch-extensions'] = ''; options.global['$armlink-arch-extensions'] = ''; if (ArmCpuUtils.getArchExtensions(cpu_id, toolchain.name).length > 0) { - const opts = (config.compileConfig.archExtensions || '').split(','); + const opts = (config.toolchainConfig.archExtensions || '').split(','); // for gcc if (isGccFamilyToolchain(toolchain.name)) { options.global['$arch-extensions'] = opts.join(''); @@ -1051,7 +1051,7 @@ export class ARMCodeBuilder extends CodeBuilder { const ldFileList: string[] = []; - let scatterFilePath: string = config.compileConfig.scatterFilePath; + let scatterFilePath: string = config.toolchainConfig.scatterFilePath; switch (toolchain.name) { @@ -1059,7 +1059,7 @@ export class ARMCodeBuilder extends CodeBuilder { case 'AC5': case 'AC6': { - if (config.compileConfig.useCustomScatterFile) { // use custom linker script files + if (config.toolchainConfig.useCustomScatterFile) { // use custom linker script files scatterFilePath.split(',') .filter(s => s.trim() != '') .forEach((sctPath) => { @@ -1154,7 +1154,7 @@ class RiscvCodeBuilder extends CodeBuilder { const config = this.project.GetConfiguration().config; const ldFileList: string[] = []; - config.compileConfig.linkerScriptPath.split(',') + config.toolchainConfig.linkerScriptPath.split(',') .filter(s => s.trim() != '') .forEach((sctPath) => { ldFileList.push(this.convLinkerScriptPathForCompiler(sctPath)); @@ -1180,7 +1180,7 @@ class MipsCodeBuilder extends CodeBuilder { const config = this.project.GetConfiguration().config; const ldFileList: string[] = []; - config.compileConfig.linkerScriptPath.split(',') + config.toolchainConfig.linkerScriptPath.split(',') .filter(s => s.trim() != '') .forEach((sctPath) => { ldFileList.push(this.convLinkerScriptPathForCompiler(sctPath)); @@ -1210,7 +1210,7 @@ class AnyGccCodeBuilder extends CodeBuilder { } // set linker script - options.linker['linker-script'] = config.compileConfig.linkerScriptPath.split(',') + options.linker['linker-script'] = config.toolchainConfig.linkerScriptPath.split(',') .filter(s => s.trim() != '') .map((sctPath) => { return this.convLinkerScriptPathForCompiler(sctPath); @@ -1262,7 +1262,7 @@ class C51CodeBuilder extends CodeBuilder { const results = super.getLibDirs(); const toolchain = this.project.getToolchain(); - const options: BuilderOptions = this.project.GetConfiguration().compileConfigModel.getOptions(); + const options: BuilderOptions = this.project.GetConfiguration().toolchainConfigModel.getOptions(); /* for sdcc + binutils toolchain, we need provide -L for ld */ if (toolchain.name == 'GNU_SDCC_MCS51') { @@ -1284,11 +1284,11 @@ class C51CodeBuilder extends CodeBuilder { const toolchain = this.project.getToolchain(); /* set linker script if it's existed */ - if (config.compileConfig.linkerScript) { + if (config.toolchainConfig.linkerScript) { const ldFileList: string[] = []; - config.compileConfig.linkerScript.split(',') + config.toolchainConfig.linkerScript.split(',') .filter(s => s.trim() != '') .forEach((sctPath) => { ldFileList.push(this.convLinkerScriptPathForCompiler(sctPath, true)); diff --git a/src/DependenceManager.ts b/src/DependenceManager.ts index 59456d9..dfd62de 100644 --- a/src/DependenceManager.ts +++ b/src/DependenceManager.ts @@ -550,7 +550,7 @@ export class DependenceManager implements ManagerInterface { const toolchain = this.project.getToolchain(); const prjConfig = this.project.GetConfiguration(); - const builderOpts = prjConfig.compileConfigModel.getOptions(); + const builderOpts = prjConfig.toolchainConfigModel.getOptions(); const incList = ArrayDelRepetition(toolchain.getSystemIncludeList(builderOpts) .concat(toolchain.getDefaultIncludeList())); diff --git a/src/EIDEProject.ts b/src/EIDEProject.ts index dcdfb22..23650ad 100644 --- a/src/EIDEProject.ts +++ b/src/EIDEProject.ts @@ -71,6 +71,7 @@ import { view_str$prompt$userCanceledOperation, continue_text, cancel_text, + view_str$prompt$migrationFailed, } from './StringTable'; import { SettingManager } from './SettingManager'; import { ExeCmd } from '../lib/node-utility/Executable'; @@ -807,7 +808,7 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr static readonly EIDE_DIR = '.eide'; static readonly cppConfigName = 'c_cpp_properties.json'; - static readonly prjConfigName = 'eide.json'; + static readonly prjConfigName = 'eide.yml'; static readonly importerWarningBaseName = 'importer.warning.txt'; @@ -837,6 +838,7 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr protected toolchain: IToolchian | undefined; protected prevToolchain: IToolchian | undefined; protected eideDirWatcher: FileWatcher | undefined; + protected workspaceState: vscode.Memento; // sources protected sourceRoots: SourceRootList; @@ -846,7 +848,9 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr private lock_handle: number | undefined; - ////////////////////////////////// cpptools provider interface /////////////////////////////////// + //----------------------------------------------------------- + //- cpptools provider interface + //----------------------------------------------------------- name: string = 'eide'; extensionId: string = 'cl.eide'; @@ -859,7 +863,9 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr abstract dispose(): void; abstract forceUpdateCpptoolsConfig(): void; - ////////////////////////////////// Base Api /////////////////////////////////////////// + //----------------------------------------------------------- + //- Base Api + //----------------------------------------------------------- public getProjectName(): string { return this.GetConfiguration().config.name; @@ -887,20 +893,6 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr */ public getExecutablePath(): string { return this.getExecutablePathWithoutSuffix() + this.getToolchain().elfSuffix; - // @discard parse from file - // ------------------------------ - // let suffix = '.axf'; - // try { - // let fpath = `${ResManager.GetInstance().getBuilderModelsDir().path}/${this.getToolchain().modelName}`; - // let model = JSON.parse(fs.readFileSync(fpath).toString()); - // if (model['groups']['linker'] && - // model['groups']['linker']['$outputSuffix'] != undefined) { - // suffix = model['groups']['linker']['$outputSuffix']; - // } - // } catch (error) { - // GlobalEvent.log_error(error); - // } - // return this.getExecutablePathWithoutSuffix() + suffix; } public getUid(): string { @@ -979,6 +971,7 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr public getProjectFile(): File { return File.from(this.getEideDir().path, AbstractProject.prjConfigName); } + public getEideProjectFile(): File { return this.getProjectFile(); } @@ -1045,7 +1038,8 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr ////////////////////////////////// Abstract Project /////////////////////////////////// - constructor() { + constructor(workspaceState: vscode.Memento) { + this.workspaceState = workspaceState; this._event = new events.EventEmitter(); this.sourceRoots = new SourceRootList(this); this.virtualSource = new VirtualSource(this); @@ -1600,7 +1594,7 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr // update project data for (const name in oldTarget) { - if (name === 'custom_dep') { + if (name === 'cppPreprocessAttrs') { const curDep = this.GetConfiguration().CustomDep_getDependence(); const oldDep = oldTarget[name]; for (const key in curDep) { (curDep)[key] = copyObject(oldDep[key]); } @@ -1610,16 +1604,16 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr continue; } - if (name === 'compileConfig') { - const compileConfig = prjConfig.compileConfigModel.data; + if (name === 'toolchainConfig') { + const toolchainConfig = prjConfig.toolchainConfigModel.data; const oldCompileConfig = oldTarget[name]; // delete all properties - for (const key in compileConfig) { - delete compileConfig[key]; + for (const key in toolchainConfig) { + delete toolchainConfig[key]; } // update properties for (const key in oldCompileConfig) { - compileConfig[key] = copyObject(oldCompileConfig[key]); + toolchainConfig[key] = copyObject(oldCompileConfig[key]); } continue; } @@ -1645,9 +1639,10 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr } // copy source options if not exist - const opts = this.getSourceExtraArgsCfg(prevTargetName); - if (opts) { - this.setSourceExtraArgsCfg(opts, targetName); + if (isNewTarget) { + const opts = this.getSourceExtraArgsCfg(prevTargetName); + if (opts) + this.setSourceExtraArgsCfg(opts, targetName); } this.sourceRoots.forceUpdateAllFolders(); @@ -1893,7 +1888,7 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr .filter(f => keywords.some(k => f.name.toLowerCase().includes(k))); // matched dirs, filter files if (subdirs.length > 0) { - const cpuTyp = (this.GetConfiguration().compileConfigModel).data.cpuType; + const cpuTyp = (this.GetConfiguration().toolchainConfigModel).data.cpuType; const allfiles = subdirs[0].GetList(undefined, File.EXCLUDE_ALL_FILTER); const unusedFiles = allfiles.filter(f => !matchLibFileAndCpuTyp(f.name, cpuTyp)); libPaths.push(subdirs[0].path); @@ -1924,7 +1919,7 @@ export abstract class AbstractProject implements CustomConfigurationProvider, Pr let isUseCustomScatterFile = false; if (this.getProjectType() == 'ARM') isUseCustomScatterFile = this.GetConfiguration().config - .compileConfig.useCustomScatterFile; + .toolchainConfig.useCustomScatterFile; const toolchainName = this.getToolchain().name; return (toolchainName === 'AC5' || toolchainName === 'AC6') && !isUseCustomScatterFile; } @@ -2258,7 +2253,7 @@ $(OUT_DIR): const optsObj = { version: EIDE_FILE_OPTION_VERSION, options: {} }; optsObj.version = EIDE_FILE_OPTION_VERSION; optsObj.options[this.getCurrentTarget()] = { files: {}, virtualPathFiles: {} }; - optFile.Write(view_str$prompt$filesOptionsComment + yaml.stringify(optsObj, { indent: 4 })); + optFile.Write(view_str$prompt$filesOptionsComment + yaml.stringify(optsObj, { indent: 4, lineWidth: 1000 })); } return optFile; @@ -2284,7 +2279,7 @@ $(OUT_DIR): const optsObj = yaml.parse(optFile.Read()); optsObj.version = EIDE_FILE_OPTION_VERSION; optsObj.options[targetName || this.getCurrentTarget()] = cfg; - optFile.Write(view_str$prompt$filesOptionsComment + yaml.stringify(optsObj, { indent: 4 })); + optFile.Write(view_str$prompt$filesOptionsComment + yaml.stringify(optsObj, { indent: 4, lineWidth: 1000 })); } catch (error) { GlobalEvent.log_error(error); } @@ -2526,7 +2521,7 @@ $(OUT_DIR): } getBuilderOptions(): BuilderOptions { - return this.GetConfiguration().compileConfigModel.getOptions(); + return this.GetConfiguration().toolchainConfigModel.getOptions(); } //--- @@ -2540,10 +2535,10 @@ $(OUT_DIR): let toolchainRoot = this.toolchain.getToolchainDir().path; this.registerBuiltinVar('ToolchainRoot', () => toolchainRoot); - const curOptions = prjConfig.compileConfigModel.getOptions(); + const curOptions = prjConfig.toolchainConfigModel.getOptions(); const newOptions = toolManager.migrateBuilderOptions(curOptions, this.toolchain); if (newOptions) { - prjConfig.compileConfigModel.setOptions(newOptions, undefined, this.toolchain.name); + prjConfig.toolchainConfigModel.setOptions(newOptions, undefined, this.toolchain.name); } } @@ -2594,7 +2589,7 @@ $(OUT_DIR): protected LoadConfigurations(wsFile: File) { //////////////////////////////////// - // load eide.json, init project + // init project // init folders this.rootDir = new File(wsFile.dir); @@ -2602,8 +2597,8 @@ $(OUT_DIR): // init cfgs this.configMap.Set(new WorkspaceConfiguration(wsFile).load(), AbstractProject.workspaceSuffix); - const eideJsonFile = File.fromArray([wsFile.dir, AbstractProject.EIDE_DIR, AbstractProject.prjConfigName]); - this.configMap.Set(new ProjectConfiguration(eideJsonFile).load()); + const eideJsonFile = File.from(wsFile.dir, AbstractProject.EIDE_DIR, AbstractProject.prjConfigName); + this.configMap.Set(new ProjectConfiguration(eideJsonFile, this.workspaceState).load()); // create '.vscode' folder if it's not existed File.fromArray([wsFile.dir, AbstractProject.vsCodeDir]).CreateDir(true); @@ -2654,7 +2649,7 @@ $(OUT_DIR): prjConfig.on('dataChanged', (type) => this.onPrjConfigChanged(type)); - prjConfig.compileConfigModel.on('event', (eDat) => this.onConfigurationEvent(eDat)); + prjConfig.toolchainConfigModel.on('event', (eDat) => this.onConfigurationEvent(eDat)); prjConfig.uploadConfigModel.on('event', (eDat) => this.onConfigurationEvent(eDat)); @@ -2695,7 +2690,7 @@ $(OUT_DIR): // check project version const eideFile = File.fromArray([wsFile.dir, AbstractProject.EIDE_DIR, AbstractProject.prjConfigName]); - const conf = >JSON.parse(eideFile.Read()); + const conf = ProjectConfiguration.parseProjectFile(eideFile.Read()); if (conf.version) { const prj_version = conf.version; const eide_conf_v = EIDE_CONF_VERSION; @@ -2729,7 +2724,7 @@ $(OUT_DIR): } } - // move old builder config files into eide.json + // move old builder config files into eide.yml if (this.isOldVersionProject) { const eideDir = File.from(eideFile.dir); const toolchainManager = ToolchainManager.getInstance(); @@ -2746,28 +2741,33 @@ $(OUT_DIR): } // get all builder options - const allOptions: { target_l: string, toolchain: string, options: BuilderOptions }[] = []; + const allOptions: { + target_l: string, + toolchain: string, + options: BuilderOptions, + file: File, + notCleanup?: boolean + }[] = []; for (const file of eideDir.GetList(undefined, File.EXCLUDE_ALL_FILTER)) { const tidx = toolchainInfos.findIndex(info => file.name.endsWith(info.configFileName)); if (tidx != -1) { const toolInfo = toolchainInfos[tidx]; // get lower case target name - let targetName_l: string = file.name.replace(toolInfo.configFileName, ''); - if (targetName_l == '') targetName_l = 'release'; - else targetName_l = targetName_l.substr(0, targetName_l.length - 1); + const t = file.name.replace(toolInfo.configFileName, ''); + const targetName_l = t === '' + ? 'release' : t.substr(0, t.length - 1); // use 't.length - 1' remove tailing '.' // append in list const options = jsonc.parse(file.Read()); allOptions.push({ target_l: targetName_l, toolchain: toolInfo.name, options: options, + file: file }); - // delete old builder options file, we don't need it anymore. - try { fs.unlinkSync(file.path); } catch {}; } } - // copy all builder options into eide.json + // copy all builder options into eide.yml const allTargetNames = Object.keys(conf.targets); for (const optionsInfo of allOptions) { const idx = allTargetNames.findIndex( @@ -2775,19 +2775,31 @@ $(OUT_DIR): if (idx != -1) { const targetName = allTargetNames[idx]; const target = conf.targets[targetName]; - if (target.builderOptions == undefined) target.builderOptions = {}; - target.builderOptions[optionsInfo.toolchain] = optionsInfo.options; + if (target.toolchainConfigMap && + target.toolchainConfigMap[optionsInfo.toolchain]) { + target.toolchainConfigMap[optionsInfo.toolchain].options = optionsInfo.options; + } else { + optionsInfo.notCleanup = true; + GlobalEvent.log_warn(`No config for toolchain "${optionsInfo.toolchain}" for target "${targetName}"`); + GlobalEvent.log_show(); + } } } + + // delete old builder options file, we don't need it anymore. + for (const optionsInfo of allOptions) { + if (!optionsInfo.notCleanup) + try { fs.unlinkSync(optionsInfo.file.path); } catch {}; + } } // remove 'sourceDirList' of custom_dep if (this.isOldVersionProject) { for (const name in conf.targets) { const target = conf.targets[name]; - if (target.custom_dep) { - if ((target.custom_dep)['sourceDirList'] != undefined) { - (target.custom_dep)['sourceDirList'] = undefined; + if (target.cppPreprocessAttrs) { + if ((target.cppPreprocessAttrs)['sourceDirList'] != undefined) { + (target.cppPreprocessAttrs)['sourceDirList'] = undefined; } } } @@ -2829,14 +2841,18 @@ $(OUT_DIR): optionsObj.options[opts.targetName] = obj; }); // write to file - optionsFile.Write(view_str$prompt$filesOptionsComment + yaml.stringify(optionsObj, { indent: 4 })); + optionsFile.Write(view_str$prompt$filesOptionsComment + yaml.stringify(optionsObj, { indent: 4, lineWidth: 1000 })); } } /* udpate project version to lastest */ if (this.isOldVersionProject) { conf.version = EIDE_CONF_VERSION; - eideFile.Write(JSON.stringify(conf, undefined, 2)); + eideFile.Write(ProjectConfiguration.dumpProjectFile(conf)); + // rm old eide.json file + const eideJsonFile = File.from(wsFile.dir, AbstractProject.EIDE_DIR, 'eide.json'); + if (eideJsonFile.IsExist()) + fs.unlinkSync(eideJsonFile.path); } } @@ -3059,8 +3075,8 @@ $(OUT_DIR): abstract ExportToKeilProject(): File | undefined; - static NewProject(): AbstractProject { - return new EIDEProject(); + static NewProject(workspaceState: vscode.Memento): AbstractProject { + return new EIDEProject(workspaceState); } } @@ -3169,7 +3185,7 @@ class EIDEProject extends AbstractProject { protected onDeviceChanged(oldDevice?: CurrentDevice | undefined): void { const prjConfig = this.GetConfiguration(); - const cConfig = prjConfig.compileConfigModel; + const cConfig = prjConfig.toolchainConfigModel; // project type is ARM if (cConfig instanceof ArmBaseCompileConfigModel) { @@ -3435,7 +3451,7 @@ class EIDEProject extends AbstractProject { const wsConfig = new WorkspaceConfiguration(wsFile).load(); const eideFile = File.fromArray([wsFile.dir, AbstractProject.EIDE_DIR, AbstractProject.prjConfigName]); - const prjConfig = new ProjectConfiguration(eideFile, option.type).load(); + const prjConfig = new ProjectConfiguration(eideFile, this.workspaceState, option.type).load(); // set project name prjConfig.config.name = option.projectName || AbstractProject.formatProjectName(option.name); @@ -3793,7 +3809,7 @@ class EIDEProject extends AbstractProject { '/.vscode/launch.json', '/.settings', '/.eide/log', - '/' + ProjectConfiguration.USR_CTX_FILE_NAME, + '/.eide.usr.ctx.json', '', '# project out', '/build', '/bin', '/obj', '/out', @@ -3982,7 +3998,7 @@ class EIDEProject extends AbstractProject { // get project includes and defines const depMerge = prjConfig.GetAllMergeDep(); const defMacros: string[] = ['__VSCODE_CPPTOOL']; // it's for internal force include header - const intrDefs = this._getCompilerIntrDefsForCpptools(toolchain, prjConfig.config.compileConfig, builderOpts); + const intrDefs = this._getCompilerIntrDefsForCpptools(toolchain, prjConfig.config.toolchainConfig, builderOpts); const defLi = defMacros.concat(depMerge.defineList, intrDefs); depMerge.incList = depMerge.incList.concat(this.getSourceIncludeList()).map(p => this.ToAbsolutePath(p)); @@ -3998,11 +4014,11 @@ class EIDEProject extends AbstractProject { this.cppToolsConfig.cppCompilerArgs = undefined; // preset cpu info for arm project - if (prjConfig.compileConfigModel instanceof ArmBaseCompileConfigModel) { + if (prjConfig.toolchainConfigModel instanceof ArmBaseCompileConfigModel) { builderOpts.global = builderOpts.global || {}; - const cpuName: string = prjConfig.compileConfigModel.data.cpuType.toLowerCase(); - const fpuName: string = prjConfig.compileConfigModel.data.floatingPointHardware; - const archExt: string = prjConfig.compileConfigModel.data.archExtensions || ''; + const cpuName: string = prjConfig.toolchainConfigModel.data.cpuType.toLowerCase(); + const fpuName: string = prjConfig.toolchainConfigModel.data.floatingPointHardware; + const archExt: string = prjConfig.toolchainConfigModel.data.archExtensions || ''; // 将 cpu 信息作为上下文传递给 updateCppIntellisenceCfg, // 以便 toolchain 能够生成合适的 compiler args 用于执行 Intellisence builderOpts.global['_cpuName'] = cpuName; diff --git a/src/EIDEProjectExplorer.ts b/src/EIDEProjectExplorer.ts index 6371a29..7f3384e 100644 --- a/src/EIDEProjectExplorer.ts +++ b/src/EIDEProjectExplorer.ts @@ -112,7 +112,7 @@ import { generateDotnetProgramCmd, isGccFamilyToolchain } from './utility'; -import { concatSystemEnvPath, DeleteDir, exeSuffix, kill, osType, DeleteAllChildren, userhome } from './Platform'; +import { concatSystemEnvPath, DeleteDir, exeSuffix, kill, osType, DeleteAllChildren, userhome, getGlobalState } from './Platform'; import { KeilARMOption, KeilC51Option, KeilParser, KeilRteDependence } from './KeilXmlParser'; import { VirtualDocument } from './VirtualDocsProvider'; import { ResInstaller } from './ResInstaller'; @@ -134,6 +134,7 @@ import { ShellFlasherIndexItem } from './WebInterface/WebInterface'; import { jsonc } from 'jsonc'; import { SimpleUIConfig, SimpleUIConfigData_input, SimpleUIConfigData_options, SimpleUIConfigData_text, SimpleUIConfigData_table, SimpleUIConfigData_boolean, SimpleUIConfigData_divider, SimpleUIConfigData_tag } from "./SimpleUIDef"; import { StatusBarManager } from './StatusBarManager'; +import { doMigration, detectProject } from './EIDEProjectMigration'; enum TreeItemType { SOLUTION, @@ -828,30 +829,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco prj.Save(false, 1000); // save project file with a delay } - private toLowercaseEIDEFolder(wsFolder: File) { - - if (wsFolder.IsDir()) { - - // rename eide folder name - const folderList = wsFolder.GetList(File.EXCLUDE_ALL_FILTER, [/^\.EIDE$/]); - if (folderList.length > 0) { - const oldEideFolder = folderList[0]; - fs.renameSync(oldEideFolder.path, `${oldEideFolder.dir}${File.sep}${AbstractProject.EIDE_DIR}`); - } - - // rename eide conf file - const eideFolder = File.fromArray([wsFolder.path, AbstractProject.EIDE_DIR]); - if (eideFolder.IsDir()) { - const fList = eideFolder.GetList([/^EIDE\.json$/], File.EXCLUDE_ALL_FILTER); - if (fList.length > 0) { - const oldEideConfFile = fList[0]; - fs.renameSync(oldEideConfFile.path, `${oldEideConfFile.dir}${File.sep}${AbstractProject.prjConfigName}`); - } - } - } - } - - LoadWorkspaceProject() { + LoadWorkspaceProject(workspaceState: vscode.Memento) { const workspaceManager = WorkspaceManager.getInstance(); @@ -866,12 +844,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco for (const wsDir of wsFolders) { const wsList = wsDir.GetList([/.code-workspace$/i], File.EXCLUDE_ALL_FILTER); if (wsList.length > 0) { - - // convert .EIDE to .eide - this.toLowercaseEIDEFolder(wsDir); - - const eideConfigFile = File.fromArray([wsDir.path, AbstractProject.EIDE_DIR, AbstractProject.prjConfigName]); - if (eideConfigFile.IsFile()) { + if (detectProject(wsDir)) { validList.push(wsList[0]); } } @@ -890,7 +863,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco } for (const wsFile of validList) { - this._OpenProject(wsFile.path); + this._OpenProject(wsFile.path, workspaceState); } } @@ -1295,7 +1268,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco break; case TreeItemType.COMPILE_CONFIGURATION: { - const cConfig = project.GetConfiguration().compileConfigModel; + const cConfig = project.GetConfiguration().toolchainConfigModel; const keyMap = cConfig.GetDefault(); const excludeKeys = project.getToolchain().excludeViewList || []; @@ -2193,7 +2166,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco }); } - private async _OpenProject(workspaceFilePath: string): Promise { + private async _OpenProject(workspaceFilePath: string, workspaceState: vscode.Memento): Promise { const wsFile: File = new File(workspaceFilePath); if (!wsFile.IsFile()) { @@ -2205,28 +2178,14 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco return undefined; } - const eideConfigFile = File.fromArray([wsFile.dir, AbstractProject.EIDE_DIR, AbstractProject.prjConfigName]); - if (!eideConfigFile.IsFile()) { - GlobalEvent.emit('msg', newMessage('Warning', `File not existed, [path]: ${eideConfigFile.path}`)); + if (!detectProject(File.from(wsFile.dir))) { + GlobalEvent.emit('msg', newMessage('Warning', `File not existed, [path]: ${wsFile.dir}`)); return undefined; } - let eideProjInfo: ProjectConfigData; try { - eideProjInfo = jsonc.parse(eideConfigFile.Read()); - } catch (error) { - GlobalEvent.emit('msg', newMessage('Warning', `Load '${eideConfigFile.path}' failed !`)); - GlobalEvent.log_error(error); - } - - const existedPrjIdx = this.prjList.findIndex((prj) => prj.getWsPath() == workspaceFilePath || prj.getUid() == eideProjInfo.miscInfo.uid); - if (existedPrjIdx != -1) { - GlobalEvent.emit('msg', newMessage('Warning', project_is_opened)); - return undefined; - } - - try { - const prj = AbstractProject.NewProject(); + await doMigration(File.from(wsFile.dir)); + const prj = AbstractProject.NewProject(workspaceState); await prj.Load(wsFile); this.registerProject(prj); GlobalEvent.emit('project.opened', prj); @@ -2253,12 +2212,8 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco const wsFolder = new File(NodePath.dirname(workspaceFilePath)); - // convert .EIDE to .eide - this.toLowercaseEIDEFolder(wsFolder); - // check workspace - const prjFile = File.fromArray([wsFolder.path, AbstractProject.EIDE_DIR, AbstractProject.prjConfigName]); - if (!prjFile.IsFile()) { // not found project file, open workspace ? + if (!detectProject(wsFolder)) { // not found project file, open workspace ? const msg = `Not found eide project in this workspace !, Open this workspace directly ?`; const selection = await vscode.window.showInformationMessage(msg, continue_text, cancel_text); if (selection === continue_text) { WorkspaceManager.getInstance().openWorkspace(new File(workspaceFilePath)); } @@ -2270,7 +2225,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco location: vscode.ProgressLocation.Notification, }, (progress) => { progress.report({ message: `${workspaceFilePath}` }); - return this._OpenProject(workspaceFilePath); + return this._OpenProject(workspaceFilePath, getGlobalState()); }); if (prj) { this.SwitchProject(prj, switchWorkspaceImmediately); @@ -2292,7 +2247,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco } try { - const prj = AbstractProject.NewProject(); + const prj = AbstractProject.NewProject(getGlobalState()); await prj.Create(option); this.registerProject(prj); this.SwitchProject(prj); @@ -2384,7 +2339,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco const iarPrjRoot = new File(NodePath.dirname(path_)); const needCreateNewDir = File.normalize(iarPrjRoot.path) == File.normalize(ewwRoot.path); - const basePrj = AbstractProject.NewProject().createBase({ + const basePrj = AbstractProject.NewProject(getGlobalState()).createBase({ name: iarproj.name, projectName: iarproj.name, type: 'ARM', @@ -2441,11 +2396,12 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco const nEideTarget: ProjectTargetInfo = { excludeList: iarTarget.excludeList, toolchain: eidePrjCfg.toolchain, - compileConfig: copyObject(eidePrjCfg.compileConfig), + toolchainConfig: copyObject(eidePrjCfg.toolchainConfig), + toolchainConfigMap: copyObject(eidePrjCfg.toolchainConfigMap), uploader: eidePrjCfg.uploader, uploadConfig: copyObject(eidePrjCfg.uploadConfig), uploadConfigMap: copyObject(eidePrjCfg.uploadConfigMap), - custom_dep: { + cppPreprocessAttrs: { name: 'default', incList: [], defineList: [], @@ -2455,14 +2411,14 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco }; eidePrjCfg.targets[targetName] = nEideTarget; - nEideTarget.custom_dep.defineList = toArray(iarTarget.settings['ICCARM.CCDefines']); - nEideTarget.custom_dep.incList = toArray(iarTarget.settings['ICCARM.CCIncludePath2']); + nEideTarget.cppPreprocessAttrs.defineList = toArray(iarTarget.settings['ICCARM.CCDefines']); + nEideTarget.cppPreprocessAttrs.incList = toArray(iarTarget.settings['ICCARM.CCIncludePath2']); // // compiler base config // - const compilerMod = basePrj.prjConfig.compileConfigModel; - const compilerOpt = nEideTarget.compileConfig; + const compilerMod = basePrj.prjConfig.toolchainConfigModel; + const compilerOpt = nEideTarget.toolchainConfig; if (iarTarget.core) { const expname = iarTarget.core; @@ -2599,7 +2555,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco const curTarget: any = eidePrjCfg.targets[tname]; eidePrjCfg.mode = tname; // set current target name for (const key in curTarget) { - if (key === 'custom_dep') { + if (key === 'cppPreprocessAttrs') { eidePrjCfg.dependenceList = [{ groupName: 'custom', depList: [curTarget[key]] }]; continue; @@ -2647,7 +2603,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco break; } - const basePrj = AbstractProject.NewProject().createBase({ + const basePrj = AbstractProject.NewProject(getGlobalState()).createBase({ name: ePrjInfo.name, projectName: ePrjInfo.name, type: nPrjType, @@ -2678,12 +2634,13 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco const nEideTarget: ProjectTargetInfo = { excludeList: eTarget.excList, toolchain: nPrjConfig.toolchain, - compileConfig: copyObject(nPrjConfig.compileConfig), + toolchainConfig: copyObject(nPrjConfig.toolchainConfig), + toolchainConfigMap: copyObject(nPrjConfig.toolchainConfigMap), uploader: nPrjConfig.uploader, uploadConfig: copyObject(nPrjConfig.uploadConfig), uploadConfigMap: copyObject(nPrjConfig.uploadConfigMap), builderOptions: {}, - custom_dep: { + cppPreprocessAttrs: { name: 'default', incList: [], defineList: [], @@ -2691,9 +2648,9 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco } }; - nEideTarget.custom_dep.defineList = eTarget.builldArgs.cMacros; - nEideTarget.custom_dep.incList = eTarget.builldArgs.cIncDirs; - nEideTarget.custom_dep.libList = eTarget.builldArgs.linkerLibSearchDirs; + nEideTarget.cppPreprocessAttrs.defineList = eTarget.builldArgs.cMacros; + nEideTarget.cppPreprocessAttrs.incList = eTarget.builldArgs.cIncDirs; + nEideTarget.cppPreprocessAttrs.libList = eTarget.builldArgs.linkerLibSearchDirs; // for arm gcc toolchain if (nEideTarget.toolchain == 'GCC') { @@ -2718,7 +2675,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco return armCpuTypeMap[archName.toLowerCase()]; }; - const compilerOpt = nEideTarget.compileConfig; + const compilerOpt = nEideTarget.toolchainConfig; compilerOpt.cpuType = guessArmCpuType(eTarget.archName) || 'Cortex-M3'; compilerOpt.floatingPointHardware = ArmCpuUtils.hasFpu(compilerOpt.cpuType) ? 'single' : 'none'; compilerOpt.useCustomScatterFile = true; @@ -2726,12 +2683,12 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco } // for riscv gcc toolchain else if (nEideTarget.toolchain == 'RISCV_GCC') { - const compilerOpt = nEideTarget.compileConfig; + const compilerOpt = nEideTarget.toolchainConfig; compilerOpt.linkerScriptPath = eTarget.linkerScriptPath || ''; } // for any gcc toolchain else if (nEideTarget.toolchain == 'ANY_GCC') { - const compilerOpt = nEideTarget.compileConfig; + const compilerOpt = nEideTarget.toolchainConfig; compilerOpt.linkerScriptPath = eTarget.linkerScriptPath || ''; } @@ -2861,7 +2818,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco const curTarget: any = nPrjConfig.targets[ePrjInfo.targets[0].name]; nPrjConfig.mode = ePrjInfo.targets[0].name; // set current target name for (const name in curTarget) { - if (name === 'custom_dep') { + if (name === 'cppPreprocessAttrs') { nPrjConfig.dependenceList = [{ groupName: 'custom', depList: [curTarget[name]] }]; @@ -2876,7 +2833,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco basePrj.prjConfig.Save(); // 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 })); + optFile.Write(view_str$prompt$filesOptionsComment + yaml.stringify(srcOptsObj, { indent: 4, lineWidth: 1000 })); // switch project const selection = await vscode.window.showInformationMessage( @@ -2898,7 +2855,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco const nPrjOutDir = option.outDir; - const baseInfo = AbstractProject.NewProject().createBase({ + const baseInfo = AbstractProject.NewProject(getGlobalState()).createBase({ name: nPrjOutDir.name, projectName: keilPrjFile.noSuffixName, type: targets[0].type, @@ -3150,7 +3107,8 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco const defIncList: string[] = []; // copy from cur proj info - newTarget.compileConfig = copyObject(projectInfo.compileConfig); + newTarget.toolchainConfig = copyObject(projectInfo.toolchainConfig); + newTarget.toolchainConfigMap = copyObject(projectInfo.toolchainConfigMap); newTarget.uploader = projectInfo.uploader; newTarget.uploadConfig = copyObject(projectInfo.uploadConfig); newTarget.uploadConfigMap = copyObject(projectInfo.uploadConfigMap); @@ -3179,7 +3137,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco // ARM project else { const keilCompileConf = keilTarget.compileOption; - const prjCompileOption = (newTarget.compileConfig); + const prjCompileOption = (newTarget.toolchainConfig); // base config newTarget.toolchain = keilCompileConf.toolchain; prjCompileOption.cpuType = keilCompileConf.cpuType; @@ -3204,11 +3162,11 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco } // init custom dependence after specific configs done - newTarget.custom_dep = { name: 'default' }; + newTarget.cppPreprocessAttrs = { name: 'default' }; const incList = keilTarget.incList.map((path) => baseInfo.rootFolder.ToRelativePath(path) || path); - newTarget.custom_dep.incList = defIncList.concat(incList); - newTarget.custom_dep.defineList = keilTarget.defineList; - newTarget.custom_dep.libList = []; + newTarget.cppPreprocessAttrs.incList = defIncList.concat(incList); + newTarget.cppPreprocessAttrs.defineList = keilTarget.defineList; + newTarget.cppPreprocessAttrs.libList = []; // fill exclude list newTarget.excludeList = []; @@ -3234,7 +3192,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco const curTarget: any = projectInfo.targets[targets[0].name]; projectInfo.mode = targets[0].name; // current target name for (const name in curTarget) { - if (name === 'custom_dep') { + if (name === 'cppPreprocessAttrs') { projectInfo.dependenceList = [{ groupName: 'custom', depList: [curTarget[name]] }]; @@ -3256,7 +3214,7 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco // save src options const optFile = File.fromArray([baseInfo.rootFolder.path, AbstractProject.EIDE_DIR, `files.options.yml`]); - optFile.Write(view_str$prompt$filesOptionsComment + yaml.stringify(srcOptsObj, { indent: 4 })); + optFile.Write(view_str$prompt$filesOptionsComment + yaml.stringify(srcOptsObj, { indent: 4, lineWidth: 1000 })); // switch project const selection = await vscode.window.showInformationMessage( @@ -3308,22 +3266,19 @@ class ProjectDataProvider implements vscode.TreeDataProvider, vsco // rename project if (templateFile.suffix != '.ewt') { // ignore eide workspace project - // convert .EIDE to .eide - this.toLowercaseEIDEFolder(targetDir); - // init project - { - const prjFile = File.fromArray([targetDir.path, AbstractProject.EIDE_DIR, AbstractProject.prjConfigName]); - if (!prjFile.IsFile()) throw Error(`project file: '${prjFile.path}' is not exist !`); - - try { - const prjConf: ProjectConfigData = JSON.parse(prjFile.Read()); - prjConf.name = option.name; // set project name - if (prjConf.miscInfo) prjConf.miscInfo.uid = undefined; // reset uid - prjFile.Write(JSON.stringify(prjConf, undefined, 2)); - } catch (error) { - throw Error(`Init project failed !, msg: ${error.message}`); - } + if (!detectProject(targetDir)) + throw Error(`No found any project in this workspace.`); + + try { + await doMigration(targetDir); + const pfile = File.from(targetDir.path, AbstractProject.EIDE_DIR, AbstractProject.prjConfigName); + const prjConf = ProjectConfiguration.parseProjectFile(pfile.Read()); + prjConf.name = option.name; // set project name + if (prjConf.miscInfo) prjConf.miscInfo.uid = undefined; // reset uid + pfile.Write(ProjectConfiguration.dumpProjectFile(prjConf)); + } catch (error) { + throw Error(`Init project failed !, msg: ${error.message}`); } } } @@ -3622,8 +3577,8 @@ export class ProjectExplorer implements CustomConfigurationProvider { this.dataProvider.onDispose(); } - loadWorkspace() { - this.dataProvider.LoadWorkspaceProject(); + loadWorkspace(workspaceState: vscode.Memento) { + this.dataProvider.LoadWorkspaceProject(workspaceState); } enableAutoSave(enable: boolean) { @@ -3938,7 +3893,7 @@ export class ProjectExplorer implements CustomConfigurationProvider { const compilerFlags: string[] = cfg['CompileFlags']['Add'] || []; toolchain.getSystemIncludeList(builderOpts) .forEach(p => compilerFlags.push(`-I"${p}"`)); - toolchain.getInternalDefines(prjConfig.config.compileConfig, builderOpts) + toolchain.getInternalDefines(prjConfig.config.toolchainConfig, builderOpts) .forEach(d => compilerFlags.push(`-D"${d.name}=${d.value}"`)); cfg['CompileFlags']['Add'] = ArrayDelRepetition(compilerFlags); // 禁用所有诊断错误,因为 clangd 不支持这些编译器 @@ -5414,9 +5369,9 @@ export class ProjectExplorer implements CustomConfigurationProvider { } } - const compileConfig = project.GetConfiguration().config.compileConfig; - const ramLayout = compileConfig.storageLayout.RAM; - const romLayout = compileConfig.storageLayout.ROM; + const toolchainConfig = project.GetConfiguration().config.toolchainConfig; + const ramLayout = toolchainConfig.storageLayout.RAM; + const romLayout = toolchainConfig.storageLayout.ROM; // Use for config enum. let roList: string[] = ['default']; @@ -6118,7 +6073,7 @@ export class ProjectExplorer implements CustomConfigurationProvider { const toolchain = prj.getToolchain(); const prjConfig = prj.GetConfiguration(); const depMerge = prjConfig.GetAllMergeDep(); - const builderOpts = prjConfig.compileConfigModel.getOptions(); + const builderOpts = prjConfig.toolchainConfigModel.getOptions(); const defMacros: string[] = ['__VSCODE_CPPTOOL']; /* it's for internal force include header */ let defList: string[] = defMacros.concat(depMerge.defineList); depMerge.incList = ArrayDelRepetition(depMerge.incList.concat(prj.getSourceIncludeList())); @@ -6156,7 +6111,7 @@ export class ProjectExplorer implements CustomConfigurationProvider { return; } - toolchain.getInternalDefines(prjConfig.config.compileConfig, builderOpts).forEach(d => { + toolchain.getInternalDefines(prjConfig.config.toolchainConfig, builderOpts).forEach(d => { if (d.type === 'var') defList.push(`${d.name}=${d.value}`); }); @@ -6420,7 +6375,7 @@ export class ProjectExplorer implements CustomConfigurationProvider { ModifyCompileConfig(item: ProjTreeItem) { const prj = this.dataProvider.GetProjectByIndex(item.val.projectIndex); - prj.GetConfiguration().compileConfigModel.ShowModifyWindow(item.val.key, prj.GetRootDir()); + prj.GetConfiguration().toolchainConfigModel.ShowModifyWindow(item.val.key, prj.GetRootDir()); } async ModifyCompileConfig_openFile(item: ProjTreeItem) { @@ -7955,7 +7910,8 @@ export class ProjectExplorer implements CustomConfigurationProvider { // try to show it by eide, if failed, show it // by vscode default api - if (this.showBinaryFiles(file, isPreview)) return; + if (this.showBinaryFiles(file, isPreview)) + return; if (item.val.isVirtualFile) { const uri = vscode.Uri.parse(VirtualDocument.instance().getUriByPath(file.path)); diff --git a/src/EIDEProjectMigration.ts b/src/EIDEProjectMigration.ts new file mode 100644 index 0000000..8501263 --- /dev/null +++ b/src/EIDEProjectMigration.ts @@ -0,0 +1,89 @@ +import * as NodePath from "path"; +import * as os from "os"; +import * as fs from "fs"; +import { File } from "../lib/node-utility/File"; +import { EIDE_CONF_VERSION, ProjectConfiguration } from "./EIDETypeDefine"; +import { compareVersion } from "./utility"; +import { view_str$prompt$migrationFailed } from "./StringTable"; +import { GlobalEvent } from "./GlobalEvents"; + +export function detectProject(dir: File): boolean { + if (File.IsExist(NodePath.join(dir.path, '.eide', 'eide.yml'))) + return true; + if (File.IsExist(NodePath.join(dir.path, '.eide', 'eide.json'))) + return true; + return false; +} + +async function _doMigration(projectRootDir: File) { + + // move eide.json to eide.yml + const eideJsonFile = File.from(projectRootDir.path, '.eide', 'eide.json'); + const projCfgFile = File.from(projectRootDir.path, '.eide', 'eide.yml'); + if (eideJsonFile.IsExist()) { + const obj = ProjectConfiguration.parseProjectFile(eideJsonFile.Read()); + projCfgFile.Write(ProjectConfiguration.dumpProjectFile(obj)); + } + + const prjCfg = ProjectConfiguration.parseProjectFile(projCfgFile.Read()); + + if (compareVersion(prjCfg.version, '4.0') < 0) { + GlobalEvent.log_info(`migration ${projCfgFile.path} from ${prjCfg.version} to 4.0`); + for (const key in prjCfg.targets) { + const target = prjCfg.targets[key]; + // rename compileConfig to toolchainConfig + if ((target).compileConfig) { + (target).toolchainConfig = (target).compileConfig; + (target).compileConfig = undefined; + } + if ((target).compileConfigMap) { + (target).toolchainConfigMap = (target).compileConfigMap; + (target).compileConfigMap = undefined; + } + // toolchain config + if (target.toolchainConfigMap == undefined) + target.toolchainConfigMap = {}; + if (target.toolchainConfig) { + target.toolchainConfigMap[target.toolchain] = target.toolchainConfig; + target.toolchainConfig = undefined; + } + if (target.builderOptions) { + for (const key in target.builderOptions) { + const opts = target.builderOptions[key]; + if (target.toolchainConfigMap[key]) + target.toolchainConfigMap[key].options = opts; + } + target.builderOptions = undefined; + } + // uploader config + if (target.uploadConfigMap == undefined) + target.uploadConfigMap = {}; + if (target.uploadConfig) { + target.uploadConfigMap[target.uploader] = target.uploadConfig; + target.uploadConfig = undefined; + } + // custom_dep -> cppPreprocessAttrs + if ((target)['custom_dep']) { + target.cppPreprocessAttrs = (target)['custom_dep']; + (target)['custom_dep'] = undefined; + } + } + projCfgFile.Write(ProjectConfiguration.dumpProjectFile(prjCfg)); + } + + // rm .eide.usr.ctx.json + const p = NodePath.join(projectRootDir.path, '.eide.usr.ctx.json'); + if (File.IsFile(p)) + try { fs.unlinkSync(p); } catch (error) {} +} + +export async function doMigration(projectRootDir: File) { + try { + await _doMigration(projectRootDir); + } catch (error) { + GlobalEvent.log_error(view_str$prompt$migrationFailed.replace('{}', projectRootDir.path)); + GlobalEvent.log_error(error); + GlobalEvent.log_show(); + throw error; + } +} diff --git a/src/EIDEProjectModules.ts b/src/EIDEProjectModules.ts index 60c95d7..7d36626 100644 --- a/src/EIDEProjectModules.ts +++ b/src/EIDEProjectModules.ts @@ -1013,7 +1013,7 @@ export abstract class ArmBaseCompileConfigModel } ] }, - options: 'null' + options: '' }; } @@ -1133,7 +1133,7 @@ export class GccCompileConfigModel extends ArmBaseCompileConfigModel { scatterFilePath: '.lds', useCustomScatterFile: true, storageLayout: { RAM: [], ROM: [] }, - options: 'null' + options: '' }; } @@ -1193,7 +1193,7 @@ export class LLVMArmCompileConfigModel extends ArmBaseCompileConfigModel { scatterFilePath: '.lds', useCustomScatterFile: true, storageLayout: { RAM: [], ROM: [] }, - options: 'null' + options: '' }; } @@ -1293,7 +1293,7 @@ class IarArmCompileConfigModel extends ArmBaseCompileConfigModel { scatterFilePath: '${ToolchainRoot}/config/.icf', useCustomScatterFile: true, storageLayout: { RAM: [], ROM: [] }, - options: 'null' + options: '' }; } @@ -1397,7 +1397,7 @@ export class RiscvCompileConfigModel extends CompileConfigModel static getDefaultConfig(): MipsCompileData { return { linkerScriptPath: '', - options: 'null' + options: '' }; } @@ -1601,7 +1601,7 @@ export class AnyGccCompileConfigModel extends CompileConfigModel { super(api); this.on('NotifyUpdate', (prjConfig) => { // update start address - const model = prjConfig.compileConfigModel; + const model = prjConfig.toolchainConfigModel; if (prjConfig.config.uploader === 'STLink' && !model.data.useCustomScatterFile) { const mem = model.getIROMx(1); if (mem) { (prjConfig.uploadConfigModel.data).address = mem.startAddr; } diff --git a/src/EIDETypeDefine.ts b/src/EIDETypeDefine.ts index bd8d6b0..eaf995b 100644 --- a/src/EIDETypeDefine.ts +++ b/src/EIDETypeDefine.ts @@ -29,6 +29,7 @@ import * as vscode from 'vscode'; import * as NodePath from 'path'; import { jsonc } from 'jsonc'; import * as child_process from 'child_process'; +import * as yaml from "yaml"; import { File } from "../lib/node-utility/File"; import { FileWatcher } from "../lib/node-utility/FileWatcher"; @@ -50,7 +51,7 @@ import { CompileConfigModel, UploadConfigModel, SdccCompileConfigModel, GccCompi // ---------------------------------------------------------- // !! eide project config file version !! -export const EIDE_CONF_VERSION = '3.6'; +export const EIDE_CONF_VERSION = '4.1'; // // 'C51': 8BIT MCU Project (like: mcs51, stm8, ...) @@ -111,20 +112,22 @@ export interface BuilderOptions { export const MAPPED_KEYS_IN_TARGET_INFO = [ 'excludeList', 'toolchain', - 'compileConfig', + 'toolchainConfig', + 'toolchainConfigMap', 'uploader', 'uploadConfig', 'uploadConfigMap', - 'custom_dep', + 'cppPreprocessAttrs', ]; export interface ProjectTargetInfo { excludeList: string[]; toolchain: ToolchainName; - compileConfig: any; + toolchainConfig: any; + toolchainConfigMap: { [toolchain: string]: any }; uploader: HexUploaderType; uploadConfig: any | null; uploadConfigMap: { [uploader: string]: any }; - custom_dep: Dependence; + cppPreprocessAttrs: Dependence; builderOptions: { [toolchain: string]: BuilderOptions }; } @@ -149,11 +152,13 @@ const EXCL_KEYS_IN_EIDE_JSON = [ 'mode', 'excludeList', 'toolchain', - 'compileConfig', + 'toolchainConfig', + 'toolchainConfigMap', 'uploader', 'uploadConfig', 'uploadConfigMap' ]; + export interface ProjectConfigData { name: string; @@ -163,7 +168,8 @@ export interface ProjectConfigData { mode: string; // target name (And for historical reasons, that's what it's called) excludeList: string[]; toolchain: ToolchainName; - compileConfig: T; //【历史遗留属性】用于储存一些 公共的 编译相关的 属性 + toolchainConfig: T; //【历史遗留属性】用于储存一些 公共的 编译相关的 属性 + toolchainConfigMap: { [toolchain: string]: any }; uploader: HexUploaderType; uploadConfig: any | null; uploadConfigMap: { [uploader: string]: any }; @@ -282,7 +288,7 @@ export abstract class Configuration { this.FILE_NAME = configFile.name; this.watcher = new FileWatcher(configFile, false); this.watcher.on('error', (err) => GlobalEvent.log_error(err)); - this.watcher.OnChanged = () => this.InitConfig(this.watcher.file.Read()); + this.watcher.OnChanged = () => this.loadConfig(this.watcher.file.Read()); this.config = this.GetDefault(this.readTypeFromFile(configFile) || type); } @@ -296,7 +302,7 @@ export abstract class Configuration { } if (this.cfgFile.IsFile()) { - this.InitConfig(this.cfgFile.Read()); + this.loadConfig(this.cfgFile.Read()); } else { this.Save(true); } @@ -361,9 +367,9 @@ export abstract class Configuration { this._eventMergeFlag = false; } - protected InitConfig(json: string | object): void { + protected loadConfig(json: string): void { - const _configFromFile: any = (typeof json === 'string') ? this.Parse(json) : json; + const _configFromFile: any = this.parse(json); // clear invalid property if (this.isDelUnknownKeysWhenLoad) { @@ -378,54 +384,38 @@ export abstract class Configuration { this.config = _configFromFile; // - this.afterInitConfigData(); + this.postLoadConfig(); this._event.emit('dataChanged'); } - private _json_equal(str1: string, str2: string): boolean { - - try { - const s1 = jsonc.uglify(str1); - const s2 = jsonc.uglify(str2); - return s1 == s2; - } catch (error) { - // nothing todo - } - - return str1 == str2; - } - Save(force?: boolean): void { let oldContent: string | undefined; - let newContent: string = this.ToJson(); + let newContent: string = this.toString(); try { - if (this.cfgFile.IsExist()) oldContent = this.cfgFile.Read(); + if (this.cfgFile.IsExist()) + oldContent = this.cfgFile.Read(); } catch (error) { GlobalEvent.log_error(error); } - // ! 注意这里比较两个 json 字符串是否相等,需要去除空白字符,不要直接比较字符串, - // ! 不同平台上项目文件中的 \n 可能不同,会导致 git 提示有更改 - if (oldContent == undefined || !this._json_equal(oldContent, newContent)) { + if (oldContent == undefined || !this.compare(oldContent, newContent)) { this.lastSaveTime = Date.now(); this.cfgFile.Write(newContent); } } - protected afterInitConfigData() { + protected postLoadConfig() { // TODO } - protected Parse(jsonStr: string): ConfigType { - return JSON.parse(jsonStr); - } + protected abstract parse(jsonStr: string): ConfigType; - protected ToJson(): string { - return JSON.stringify(this.config, undefined, 4); - } + protected abstract toString(): string; + + protected abstract compare(old_str: string, new_str: string): boolean; protected abstract readTypeFromFile(configFile: File): ProjectType | undefined; @@ -477,18 +467,20 @@ export class ProjectConfiguration static readonly BUILD_IN_GROUP_NAME = 'build-in'; static readonly CUSTOM_GROUP_NAME = 'custom'; static readonly MERGE_DEP_NAME = 'merge'; - static readonly USR_CTX_FILE_NAME = '.eide.usr.ctx.json'; - compileConfigModel: CompileConfigModel = null; + toolchainConfigModel: CompileConfigModel = null; uploadConfigModel: UploadConfigModel = null; protected project: ProjectBaseApi; protected rootDir: File; + protected workspaceState: vscode.Memento; - constructor(eideJsonFile: File, type?: ProjectType) { + constructor(eideJsonFile: File, workspaceState: vscode.Memento, type?: ProjectType) { super(eideJsonFile, type); + this.workspaceState = workspaceState; + this.rootDir = new File(NodePath.dirname(this.cfgFile.dir)); this.project = { @@ -514,6 +506,43 @@ export class ProjectConfiguration this.watcher.OnRename = _cb; } + static parseProjectFile(input: string): ProjectConfigData { + return yaml.parse(input); + } + + static dumpProjectFile(obj: ProjectConfigData): string { + const keyOrder = [ + 'version', + 'name', + 'type', + 'deviceName', + 'packDir', + 'srcDirs', + 'virtualFolder', + 'dependenceList', + 'outDir', + 'miscInfo', + 'targets' + ]; + return yaml.stringify(obj, { + indent: 2, + lineWidth: 1000, + sortMapEntries: (a: any, b: any) => { + const i_a = keyOrder.findIndex(e => e == a.key); + const i_b = keyOrder.findIndex(e => e == b.key); + if (i_a == -1 && i_b == -1) { + return a.key < b.key ? -1 : a.key > b.key ? 1 : 0; + } else if (i_a == -1) { + return 1; + } else if (i_b == -1) { + return -1; + } else { + return i_a - i_b; + } + } + }); + } + private __fileChgEvtEmitDelayTimer: NodeJS.Timeout | undefined; private onProjectFileChanged() { if (this.__fileChgEvtEmitDelayTimer) { @@ -530,19 +559,19 @@ export class ProjectConfiguration super.load(); - this.compileConfigModel = CompileConfigModel.getInstance(this.config); + this.toolchainConfigModel = CompileConfigModel.getInstance(this.config); this.uploadConfigModel = UploadConfigModel.getInstance(this.config.uploader, this.project); - this.compileConfigModel.Update(this.config.compileConfig); - this.config.compileConfig = this.compileConfigModel.data; - this.compileConfigModel.on('dataChanged', () => this.emit('dataChanged', { type: 'compiler' })); + this.toolchainConfigModel.Update(this.config.toolchainConfig); + this.config.toolchainConfig = this.toolchainConfigModel.data; + this.toolchainConfigModel.on('dataChanged', () => this.emit('dataChanged', { type: 'compiler' })); this.uploadConfigModel.Update(this.config.uploadConfig); this.config.uploadConfig = this.uploadConfigModel.data; this.uploadConfigModel.on('dataChanged', () => this.emit('dataChanged', { type: 'uploader' })); // update upload model - this.compileConfigModel.on('dataChanged', () => this.uploadConfigModel.emit('NotifyUpdate', this)); + this.toolchainConfigModel.on('dataChanged', () => this.uploadConfigModel.emit('NotifyUpdate', this)); this.Watch(); @@ -590,7 +619,7 @@ export class ProjectConfiguration protected readTypeFromFile(configFile: File): ProjectType | undefined { if (configFile.IsFile()) { try { - const prjObj = JSON.parse(configFile.Read()); + const prjObj = ProjectConfiguration.parseProjectFile(configFile.Read()); return prjObj['type']; } catch (error) { GlobalEvent.emit('msg', ExceptionToMessage(error, 'Hidden')); @@ -602,13 +631,15 @@ export class ProjectConfiguration switch (type) { case 'C51': return { + version: EIDE_CONF_VERSION, name: 'undefined', type: type, mode: 'Debug', toolchain: 'SDCC', uploader: 'Custom', dependenceList: [], - compileConfig: SdccCompileConfigModel.getDefaultConfig(), + toolchainConfig: SdccCompileConfigModel.getDefaultConfig(), + toolchainConfigMap: {}, srcDirs: [], virtualFolder: { name: VirtualSource.rootName, files: [], folders: [] }, excludeList: [], @@ -618,17 +649,18 @@ export class ProjectConfiguration uploadConfig: null, uploadConfigMap: {}, miscInfo: {}, - targets: {}, - version: EIDE_CONF_VERSION + targets: {} }; case 'ARM': return { + version: EIDE_CONF_VERSION, name: 'undefined', type: type, mode: 'Debug', toolchain: 'GCC', dependenceList: [], - compileConfig: GccCompileConfigModel.getDefaultConfig(), + toolchainConfig: GccCompileConfigModel.getDefaultConfig(), + toolchainConfigMap: {}, uploader: 'JLink', srcDirs: [], virtualFolder: { name: VirtualSource.rootName, files: [], folders: [] }, @@ -639,17 +671,18 @@ export class ProjectConfiguration uploadConfig: null, uploadConfigMap: {}, miscInfo: {}, - targets: {}, - version: EIDE_CONF_VERSION + targets: {} }; case 'RISC-V': return { + version: EIDE_CONF_VERSION, name: 'undefined', type: type, mode: 'Debug', toolchain: 'RISCV_GCC', dependenceList: [], - compileConfig: RiscvCompileConfigModel.getDefaultConfig(), + toolchainConfig: RiscvCompileConfigModel.getDefaultConfig(), + toolchainConfigMap: {}, uploader: 'JLink', srcDirs: [], virtualFolder: { name: VirtualSource.rootName, files: [], folders: [] }, @@ -660,17 +693,18 @@ export class ProjectConfiguration uploadConfig: null, uploadConfigMap: {}, miscInfo: {}, - targets: {}, - version: EIDE_CONF_VERSION + targets: {} }; case 'MIPS': return { + version: EIDE_CONF_VERSION, name: 'undefined', type: type, mode: 'Debug', toolchain: 'MTI_GCC', dependenceList: [], - compileConfig: MipsCompileConfigModel.getDefaultConfig(), + toolchainConfig: MipsCompileConfigModel.getDefaultConfig(), + toolchainConfigMap: {}, uploader: 'Custom', srcDirs: [], virtualFolder: { name: VirtualSource.rootName, files: [], folders: [] }, @@ -681,17 +715,18 @@ export class ProjectConfiguration uploadConfig: null, uploadConfigMap: {}, miscInfo: {}, - targets: {}, - version: EIDE_CONF_VERSION + targets: {} }; case 'ANY-GCC': return { + version: EIDE_CONF_VERSION, name: 'undefined', type: type, mode: 'Debug', toolchain: 'ANY_GCC', dependenceList: [], - compileConfig: AnyGccCompileConfigModel.getDefaultConfig(), + toolchainConfig: AnyGccCompileConfigModel.getDefaultConfig(), + toolchainConfigMap: {}, uploader: 'JLink', srcDirs: [], virtualFolder: { name: VirtualSource.rootName, files: [], folders: [] }, @@ -702,8 +737,7 @@ export class ProjectConfiguration uploadConfig: null, uploadConfigMap: {}, miscInfo: {}, - targets: {}, - version: EIDE_CONF_VERSION + targets: {} }; default: throw new Error(`not support this project type: '${type}'`); @@ -714,10 +748,8 @@ export class ProjectConfiguration const oldModel = this.uploadConfigModel; - // save as old config + // save and recover config this.config.uploadConfigMap[oldModel.uploader] = utility.copyObject(oldModel.data); - - // get old config from map const oldCfg = this.config.uploadConfigMap[uploader]; if (oldCfg && oldCfg.bin == null) { oldCfg.bin = ''; // compat old cfg @@ -726,29 +758,37 @@ export class ProjectConfiguration // update new config this.uploadConfigModel = UploadConfigModel.getInstance(uploader, this.project); this.config.uploader = uploader; - this.uploadConfigModel.data = utility.copyObject(oldCfg) || this.uploadConfigModel.data; + if (oldCfg) + this.uploadConfigModel.data = utility.copyObject(oldCfg); this.config.uploadConfig = this.uploadConfigModel.data; // bind obj // update listeners this.uploadConfigModel.copyListenerFrom(oldModel); - // update compileConfigModel listener - this.compileConfigModel.on('dataChanged', () => this.uploadConfigModel.emit('NotifyUpdate', this)); + // update toolchainConfigModel listener + this.toolchainConfigModel.on('dataChanged', () => this.uploadConfigModel.emit('NotifyUpdate', this)); } setToolchain(toolchain: ToolchainName) { - const oldModel = this.compileConfigModel; + const oldModel = this.toolchainConfigModel; const oldToolchain = this.config.toolchain; + // save and recover config + this.config.toolchainConfigMap[oldToolchain] = utility.copyObject(oldModel.data); + const oldCfg = this.config.toolchainConfigMap[toolchain]; + // bind this.config.toolchain = toolchain; // update toolchain name - this.compileConfigModel = CompileConfigModel.getInstance(this.config); - this.config.compileConfig = this.compileConfigModel.data; // bind obj + this.toolchainConfigModel = CompileConfigModel.getInstance(this.config); + if (oldCfg) + this.toolchainConfigModel.data = utility.copyObject(oldCfg); + this.config.toolchainConfig = this.toolchainConfigModel.data; // bind obj // update - this.compileConfigModel.copyCommonCompileConfigFrom(oldToolchain, oldModel); - this.compileConfigModel.copyListenerFrom(oldModel); + if (!oldCfg) + this.toolchainConfigModel.copyCommonCompileConfigFrom(oldToolchain, oldModel); + this.toolchainConfigModel.copyListenerFrom(oldModel); } //--- @@ -1315,11 +1355,12 @@ export class ProjectConfiguration return { excludeList: Array.from(target.excludeList), toolchain: target.toolchain, - compileConfig: utility.deepCloneObject(target.compileConfig), + toolchainConfig: utility.deepCloneObject(target.toolchainConfig), + toolchainConfigMap: utility.deepCloneObject(target.toolchainConfigMap), uploader: target.uploader, uploadConfig: utility.deepCloneObject(target.uploadConfig), uploadConfigMap: utility.deepCloneObject(target.uploadConfigMap), - custom_dep: custom_dep, + cppPreprocessAttrs: custom_dep, builderOptions: builderOpts }; } @@ -1341,70 +1382,118 @@ export class ProjectConfiguration const target = this.config.targets[targetName]; if (!target) - throw new Error(`not found target: '${targetName}' in 'eide.json' !`); + throw new Error(`not found target: '${targetName}' in 'eide.yml' !`); this.config.mode = targetName; this.config.excludeList = Array.from(target.excludeList); this.config.toolchain = target.toolchain; - this.config.compileConfig = utility.deepCloneObject(target.compileConfig); + this.config.toolchainConfig = utility.deepCloneObject(target.toolchainConfig); + this.config.toolchainConfigMap = utility.deepCloneObject(target.toolchainConfigMap); this.config.uploader = target.uploader; this.config.uploadConfig = utility.deepCloneObject(target.uploadConfig); this.config.uploadConfigMap = utility.deepCloneObject(target.uploadConfigMap); const custom_dep = this.CustomDep_getDependence(); - custom_dep.incList = Array.from(target.custom_dep.incList); - custom_dep.libList = Array.from(target.custom_dep.libList); - custom_dep.defineList = Array.from(target.custom_dep.defineList); - } - - getProjectUsrCtxFile(): File { - return File.fromArray([this.getRootDir().path, ProjectConfiguration.USR_CTX_FILE_NAME]); + custom_dep.incList = Array.from(target.cppPreprocessAttrs.incList); + custom_dep.libList = Array.from(target.cppPreprocessAttrs.libList); + custom_dep.defineList = Array.from(target.cppPreprocessAttrs.defineList); } getProjectUsrCtx(): ProjectUserContextData { + const key = `project.${this.config.miscInfo.uid || 'unknown'}`; + const val = this.workspaceState.get(key); + if (!val) + return {}; + try { + return JSON.parse(val); + } catch (error) { + GlobalEvent.log_error(error); + return {}; // empty obj + } + }; + + setProjectUsrCtx(data: ProjectUserContextData) { + const key = `project.${this.config.miscInfo.uid || 'unknown'}`; + const val = this.workspaceState.get(key); + const saveVal = JSON.stringify(data); + if (val !== saveVal) { + this.workspaceState.update(key, saveVal); + } + } - const f = this.getProjectUsrCtxFile(); + //--- - if (f.IsFile()) { - try { - return JSON.parse(f.Read()); - } catch (error) { - GlobalEvent.log_error(error); - return {}; // empty obj + protected parse(str: string): ProjectConfigData { + const cfg: ProjectConfigData = ProjectConfiguration.parseProjectFile(str); + for (const key in cfg.targets) { + const target = cfg.targets[key]; + target.builderOptions = {}; + for (const toolchain in target.toolchainConfigMap) { + target.builderOptions[toolchain] = target.toolchainConfigMap[toolchain].options; + target.toolchainConfigMap[toolchain].options = ''; } + target.toolchainConfig = target.toolchainConfigMap[target.toolchain]; + target.uploadConfig = target.uploadConfigMap[target.uploader]; + target.cppPreprocessAttrs.name = 'default'; } + return cfg; + } - return {}; // empty obj - }; - - setProjectUsrCtx(data: ProjectUserContextData) { + protected toString(): string { - const usrCtxFile = this.getProjectUsrCtxFile(); + const eidePrjObj = >utility.deepCloneObject(this.config); - let oldUsrCtxCont: string | undefined; - if (usrCtxFile.IsExist()) { - try { - oldUsrCtxCont = usrCtxFile.Read(); - } catch (error) { - GlobalEvent.log_error(error); + // store target + eidePrjObj.targets[eidePrjObj.mode] = this.cloneCurrentTarget(); + for (const key in eidePrjObj.targets) { + const target = eidePrjObj.targets[key]; + target.toolchainConfigMap[target.toolchain] = target.toolchainConfig; + target.toolchainConfig = undefined; + target.uploadConfigMap[target.uploader] = target.uploadConfig; + target.uploadConfig = undefined; + for (const toolchain in target.builderOptions) { + if (target.toolchainConfigMap[toolchain]) + target.toolchainConfigMap[toolchain].options = target.builderOptions[toolchain]; } + target.builderOptions = undefined; + target.cppPreprocessAttrs.name = undefined; } - try { - let newUsrCtxCont = JSON.stringify(data, undefined, 4); - if (oldUsrCtxCont != newUsrCtxCont) { - usrCtxFile.Write(newUsrCtxCont); + // convert abspath to relative path before save to file + eidePrjObj.srcDirs = eidePrjObj.srcDirs.map((path) => this.toRelativePath(path)); + + // ignore some 'dynamic' dependence + eidePrjObj.dependenceList = eidePrjObj.dependenceList.filter((g) => { + return g.groupName !== ProjectConfiguration.BUILD_IN_GROUP_NAME + && g.groupName !== ProjectConfiguration.CUSTOM_GROUP_NAME; + }); + + for (const depGroup of eidePrjObj.dependenceList) { + for (const dep of depGroup.depList) { + dep.incList = dep.incList.map((path) => this.toRelativePath(path)); + dep.libList = dep.libList.map((path) => this.toRelativePath(path)); } - } catch (error) { - GlobalEvent.log_error(error); } + + for (const key in eidePrjObj) { + if (EXCL_KEYS_IN_EIDE_JSON.includes(key)) + (eidePrjObj)[key] = undefined; + } + + return ProjectConfiguration.dumpProjectFile(eidePrjObj); } - //--- + // ! 注意这里比较两个 json 字符串是否相等,需要去除空白字符,不要直接比较字符串, + // ! 不同平台上项目文件中的 \n 可能不同,会导致 git 提示有更改 + protected compare(s1: string, s2: string): boolean { + const a = yaml.parse(s1); + const b = yaml.parse(s2); + return JSON.stringify(a) === JSON.stringify(b); + } - protected afterInitConfigData() { + protected postLoadConfig() { // // load target @@ -1460,38 +1549,6 @@ export class ProjectConfiguration } } - protected ToJson(): string { - - const eidePrjObj = >utility.deepCloneObject(this.config); - - // - // store target - // - - eidePrjObj.targets[eidePrjObj.mode] = this.cloneCurrentTarget(); - - // - // convert abspath to relative path before save to file - // - - eidePrjObj.srcDirs = eidePrjObj.srcDirs.map((path) => this.toRelativePath(path)); - - // ignore some 'dynamic' dependence - eidePrjObj.dependenceList = eidePrjObj.dependenceList.filter((g) => { - return g.groupName !== ProjectConfiguration.BUILD_IN_GROUP_NAME - && g.groupName !== ProjectConfiguration.CUSTOM_GROUP_NAME; - }); - - for (const depGroup of eidePrjObj.dependenceList) { - for (const dep of depGroup.depList) { - dep.incList = dep.incList.map((path) => this.toRelativePath(path)); - dep.libList = dep.libList.map((path) => this.toRelativePath(path)); - } - } - - return utility.ToJsonStringExclude(eidePrjObj, EXCL_KEYS_IN_EIDE_JSON, 2); - } - Save(force?: boolean) { const usrCtx = this.getProjectUsrCtx(); usrCtx.target = this.config.mode; // save current target @@ -1544,7 +1601,7 @@ export class WorkspaceConfiguration extends Configuration { return undefined; } - protected Parse(jsonStr: string): WorkspaceConfig { + protected parse(jsonStr: string): WorkspaceConfig { try { const obj = jsonc.parse(jsonStr); this.isLoadFailed = false; @@ -1558,10 +1615,14 @@ export class WorkspaceConfiguration extends Configuration { } } - protected ToJson(): string { + protected toString(): string { return jsonc.stringify(this.config, undefined, 4); } + protected compare(s1: string, s2: string): boolean { + return s1 === s2; + } + // workspace only can be force save, because user will modify this file, // so we can not override it Save(force?: boolean) { diff --git a/src/IarProjectParser.ts b/src/IarProjectParser.ts index d1afa4c..d28b6e8 100644 --- a/src/IarProjectParser.ts +++ b/src/IarProjectParser.ts @@ -225,11 +225,11 @@ function parseTarget(proj: IarProjectInfo, configNodes: any) { if (settingName == 'BUILDACTION') { if (isArray(dataNode.prebuild)) { nTarget.builderActions.prebuild = - formatEnvNameAndPathSep(dataNode.prebuild[0]); + formatEnvNameAndPathSep(dataNode.prebuild[0], true); } if (isArray(dataNode.postbuild)) { nTarget.builderActions.postbuild = - formatEnvNameAndPathSep(dataNode.postbuild[0]); + formatEnvNameAndPathSep(dataNode.postbuild[0], true); } } } @@ -300,9 +300,10 @@ function tryGetIarChipInfo(iarToolRoot: File, rawChipNameStr: string): { [key: s } } -export function formatEnvNameAndPathSep(str: string): string { - return str.replace(/\\/g, '/') - .replace(/\/$/, '') +export function formatEnvNameAndPathSep(str: string, notFormatPathSep?: boolean): string { + if (!notFormatPathSep) + str = str.replace(/\\/g, '/'); + return str.replace(/(\/|\\)$/, '') .replace(/\$TOOLKIT_DIR\$/g, () => '${ToolchainRoot}') .replace(/\$(\w+)\$/g, '$${$1}'); } diff --git a/src/KeilXmlParser.ts b/src/KeilXmlParser.ts index 1757199..2eac92d 100644 --- a/src/KeilXmlParser.ts +++ b/src/KeilXmlParser.ts @@ -197,7 +197,7 @@ export abstract class KeilParser { return result; } - protected JudgeFileType(f: File): number { + protected getFileType(f: File): number { if (AbstractProject.cppfileFilter.test(f.name)) { if (f.suffix.toLowerCase() == '.c') @@ -210,6 +210,10 @@ export abstract class KeilParser { return 2; } + else if (AbstractProject.libFileFilter.test(f.name)) { + return 4; + } + return 5; } @@ -452,7 +456,7 @@ class C51Parser extends KeilParser { private setOption(targetOptionObj: any, prj: AbstractProject) { const target51 = targetOptionObj.Target51; - const options = prj.GetConfiguration().compileConfigModel.getOptions(); + const options = prj.GetConfiguration().toolchainConfigModel.getOptions(); if (target51.Target51Misc) { switch (options.global['ram-mode']) { @@ -558,7 +562,7 @@ class C51Parser extends KeilParser { const fileElement = { FileName: _f.file.name, - FileType: this.JudgeFileType(_f.file).toString(), + FileType: this.getFileType(_f.file).toString(), FilePath: prj.ToRelativePath(_f.file.path) || _f.file.path }; @@ -1158,10 +1162,10 @@ class ARMParser extends KeilParser { const armAdsObj = targetOptionObj.TargetArmAds; const prjConfig = >prj.GetConfiguration(); - const config = prjConfig.config.compileConfig; + const config = prjConfig.config.toolchainConfig; const eidePath = prj.getEideDir().path; - const compileModel = prjConfig.compileConfigModel; + const compileModel = prjConfig.toolchainConfigModel; try { if (!armAdsObj) { @@ -1328,7 +1332,7 @@ class ARMParser extends KeilParser { } } else { const buildOpts = prj.GetConfiguration().config; - const cpuname = buildOpts.compileConfig.cpuType.toLowerCase(); + const cpuname = buildOpts.toolchainConfig.cpuType.toLowerCase(); const valMap = [ ['Cortex-M35P.Dsp', 'ARMCM35P_DSP_FP'], ['Cortex-M33.Dsp', 'ARMCM33_DSP_FP'], @@ -1383,7 +1387,7 @@ class ARMParser extends KeilParser { const fileElement = { FileName: _f.file.name, - FileType: this.JudgeFileType(_f.file).toString(), + FileType: this.getFileType(_f.file).toString(), FilePath: prj.ToRelativePath(_f.file.path) || _f.file.path }; diff --git a/src/OperationExplorer.ts b/src/OperationExplorer.ts index e999267..3b5a74f 100644 --- a/src/OperationExplorer.ts +++ b/src/OperationExplorer.ts @@ -1209,55 +1209,71 @@ export class OperationExplorer { try { + const resManager = ResManager.GetInstance(); const pathArr = (remoteUrl).split('/'); const hostName = pathArr[0]; const path = '/' + pathArr.slice(1).join('/'); - const res = await vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - title: `Connect repo '${rawUrl}' ...`, - cancellable: true - }, (_, token): Thenable> => { - return new Promise(async (resolve) => { + let repoFileList: GitFileInfo[]; - token.onCancellationRequested(() => { - netReq.emit('abort'); - }); + const cache = resManager.getCache('eide-template-list.json'); + if (cache && (cache.lastUpdateTime || 0) + (10 * utility.TIME_ONE_MINUTE) > Date.now() && + resManager.getCachedFileByName(cache.name).IsFile()) { + repoFileList = JSON.parse(resManager.getCachedFileByName(cache.name).Read()); + } + // if no cache, fetch it. + else { + const res = await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: `Connect repo '${rawUrl}' ...`, + cancellable: true + }, (_, token): Thenable> => { + return new Promise(async (resolve) => { + + token.onCancellationRequested(() => { + netReq.emit('abort'); + }); - const headers: any = utility.setProxyHeader({ - 'User-Agent': 'Mozilla/5.0' - }); + const headers: any = utility.setProxyHeader({ + 'User-Agent': 'Mozilla/5.0' + }); - if (acToken) { // if token is enabled, use it - headers['Authorization'] = `token ${acToken}`; - } + if (acToken) { // if token is enabled, use it + headers['Authorization'] = `token ${acToken}`; + } - const res = await netReq.Request({ - host: hostName, - path: path, - timeout: 3000, - headers: headers - }, 'https'); + const res = await netReq.Request({ + host: hostName, + path: path, + timeout: 3000, + headers: headers + }, 'https'); - resolve(res); + resolve(res); + }); }); - }); - if (!res.success) { - GlobalEvent.emit('msg', newMessage('Warning', `Can't connect to Github repository !, msg: ${res.msg || 'null'}`)); - this.locked = false; - return; - } else if (res.content === undefined) { - GlobalEvent.emit('msg', newMessage('Warning', `Can't get content from Github repository !, msg: ${res.msg || 'null'}`)); - this.locked = false; - return; - } + if (!res.success) { + GlobalEvent.emit('msg', newMessage('Warning', `Can't connect to Github repository !, msg: ${res.msg || 'null'}`)); + this.locked = false; + return; + } else if (res.content === undefined) { + GlobalEvent.emit('msg', newMessage('Warning', `Can't get content from Github repository !, msg: ${res.msg || 'null'}`)); + this.locked = false; + return; + } - const resManager = ResManager.GetInstance(); + repoFileList = res.content; + + resManager.addCache({ + name: 'eide-template-list.json', + lastUpdateTime: Date.now() + }, JSON.stringify(repoFileList, undefined, 2)); + } // get file list const file_list = new Map(); - (res.content) + repoFileList .filter((obj) => { return obj.type === 'file'; }) .forEach((fInfo) => { file_list.set(fInfo.name, fInfo); }); @@ -1275,32 +1291,46 @@ export class OperationExplorer { return; } - // load index.json - const indexFileBuf = await vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - title: 'Fetching templates index ...', - cancellable: false - }, (_, __): Thenable => { - return new Promise(async (resolve) => { - if (indexFileInfo.download_url) { - resolve(await utility.downloadFile(indexFileInfo.download_url)); - } else { - resolve(new Error('download url is null !')); - } - }); - }); - let templateIndexInfo: TemplateIndexDef = null; let templateInfoList: TemplateInfo[] = null; - if (indexFileBuf instanceof Buffer) { - templateIndexInfo = JSON.parse(indexFileBuf.toString()); + const idxCache = resManager.getCache('template.index.json'); + if (idxCache && (idxCache.lastUpdateTime || 0) + (15 * utility.TIME_ONE_MINUTE) > Date.now() && + resManager.getCachedFileByName(idxCache.name).IsFile()) { + templateIndexInfo = JSON.parse(resManager.getCachedFileByName(idxCache.name).Read()); templateInfoList = templateIndexInfo.template_list; - } else { - const msg: string = indexFileBuf instanceof Error ? `, msg: ${indexFileBuf.message}` : ''; - GlobalEvent.emit('msg', newMessage('Warning', `Download template 'index.json' failed !${msg}`)); - this.locked = false; - return; + } + // if no cache, fetch it. + else { + // load index.json + const indexFileBuf = await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: 'Fetching templates index ...', + cancellable: false + }, (_, __): Thenable => { + return new Promise(async (resolve) => { + if (indexFileInfo.download_url) { + resolve(await utility.downloadFile(indexFileInfo.download_url)); + } else { + resolve(new Error('download url is null !')); + } + }); + }); + + if (indexFileBuf instanceof Buffer) { + templateIndexInfo = JSON.parse(indexFileBuf.toString()); + templateInfoList = templateIndexInfo.template_list; + } else { + const msg: string = indexFileBuf instanceof Error ? `, msg: ${indexFileBuf.message}` : ''; + GlobalEvent.emit('msg', newMessage('Warning', `Download template 'index.json' failed !${msg}`)); + this.locked = false; + return; + } + + resManager.addCache({ + name: 'template.index.json', + lastUpdateTime: Date.now() + }, indexFileBuf.toString()); } const rootTemplateGroup: TemplateGroup = { diff --git a/src/PackageManager.ts b/src/PackageManager.ts index d098c12..f9d8dd3 100644 --- a/src/PackageManager.ts +++ b/src/PackageManager.ts @@ -145,7 +145,7 @@ export class PackageManager { packManager.LoadPackage(packDir); const devName = prjConfig.deviceName; if (devName) { - packManager.SetDeviceInfo(devName, prjConfig.compileConfig.cpuType); + packManager.SetDeviceInfo(devName, prjConfig.toolchainConfig.cpuType); } } catch (error) { GlobalEvent.emit('msg', newMessage('Error', 'Fail to load chip package for this project !')); diff --git a/src/Platform.ts b/src/Platform.ts index 4cdb19b..5f06d75 100644 --- a/src/Platform.ts +++ b/src/Platform.ts @@ -22,6 +22,7 @@ SOFTWARE. */ +import * as vscode from 'vscode'; import * as child_process from 'child_process'; import * as NodePath from 'path'; import * as os from 'os'; @@ -48,6 +49,24 @@ const ARCH_ID_MAP: { [id: string]: string[] } = { 'arm64': ['arm64', 'aarch64'], }; +// platform info, default value is for 'Windows-x64' +let runtimeId: string = 'win32'; +let archId: string = 'x86_64'; + +// platform requirements +const SUPPORTED_OS_TYPE: NodeJS.Platform[] = ['win32', 'linux', 'darwin']; +const SUPPORTED_ARCH_TYPE: string[] = ARCH_ID_MAP['x86_64'].concat(ARCH_ID_MAP['arm64']); + +let globalState: vscode.Memento & { + setKeysForSync(keys: readonly string[]): void; +}; + +export function getGlobalState(): vscode.Memento & { + setKeysForSync(keys: readonly string[]): void; +} { + return globalState; +} + function fmtArchId(n: string): string { n = n.toLowerCase(); for (const key in ARCH_ID_MAP) { @@ -58,15 +77,9 @@ function fmtArchId(n: string): string { return n; } -// platform info, default value is for 'Windows-x64' -let runtimeId: string = 'win32'; -let archId: string = 'x86_64'; - -// platform requirements -const SUPPORTED_OS_TYPE: NodeJS.Platform[] = ['win32', 'linux', 'darwin']; -const SUPPORTED_ARCH_TYPE: string[] = ARCH_ID_MAP['x86_64'].concat(ARCH_ID_MAP['arm64']); +export function init(context: vscode.ExtensionContext) { -export function init() { + globalState = context.globalState; if (!SUPPORTED_OS_TYPE.includes(os.platform())) { const msg = `${ERROR} : This plug-in is only for '${SUPPORTED_OS_TYPE.join('/')}' platform, but your OS is '${os.platform()}' !`; diff --git a/src/ResManager.ts b/src/ResManager.ts index 7e02fc2..a9dcca2 100644 --- a/src/ResManager.ts +++ b/src/ResManager.ts @@ -72,10 +72,10 @@ const cacheName = 'eide.cache'; export interface FileCacheInfo { name: string; - size: number; - version: string; + size?: number; + version?: string; sha?: string; - lastUpdateTime?: string; + lastUpdateTime?: number; } export interface HostInfo { @@ -266,7 +266,7 @@ export class ResManager extends events.EventEmitter { return new File(this.GetTmpDir().path + File.sep + name); } - addCache(cacheInfo: FileCacheInfo) { + addCache(cacheInfo: FileCacheInfo, content?: Buffer | string) { const oldCache = this.getCache(cacheInfo.name); if (oldCache === undefined) { this.cacheInfoList.push(cacheInfo); @@ -275,6 +275,10 @@ export class ResManager extends events.EventEmitter { (oldCache)[key] = (cacheInfo)[key]; } } + if (content) { + const f = this.getCachedFileByName(cacheInfo.name); + fs.writeFileSync(f.path, content); + } } GetIconByName(name: string): File { diff --git a/src/StringTable.ts b/src/StringTable.ts index 66e1c35..7a7ca50 100644 --- a/src/StringTable.ts +++ b/src/StringTable.ts @@ -459,6 +459,11 @@ export const view_str$env_desc$py3_cmd = [ //---------------Other--------------- +export const view_str$prompt$migrationFailed = [ + `迁移旧项目失败!路径:{}`, + `Migrate Old Project Failed ! Path: {}` +][langIndex]; + export const view_str$prompt$requireOtherExtension = [ `请先安装扩展 "{}"`, `Please install extension "{}" first.` @@ -559,8 +564,8 @@ export const view_str$prompt$requestAndActivateLicence_warn_setupPath = [ ][langIndex]; export const view_str$prompt$need_reload_project = [ - `'{}' 的项目文件 'eide.json' 已被更改,重新加载项目?`, - `The project file 'eide.json' of '{}' has been changed !, reload it ?` + `'{}' 的项目文件 'eide.yml' 已被更改,重新加载项目?`, + `The project file 'eide.yml' of '{}' has been changed !, reload it ?` ][langIndex]; export const view_str$prompt$src_folder_must_be_a_child_of_root = [ diff --git a/src/ToolchainManager.ts b/src/ToolchainManager.ts index d9cf203..3f84f1e 100644 --- a/src/ToolchainManager.ts +++ b/src/ToolchainManager.ts @@ -180,8 +180,8 @@ export class ToolchainManager { private readonly toolchainNames: ToolchainEnums = { 'C51': ['GNU_SDCC_MCS51', 'Keil_C51', 'SDCC', 'IAR_STM8', 'COSMIC_STM8'], - 'ARM': ['AC5', 'AC6', 'GCC', 'LLVM_ARM', 'IAR_ARM'], - 'RISC-V': ['RISCV_GCC'], + 'ARM': ['AC5', 'AC6', 'GCC', 'LLVM_ARM', 'IAR_ARM', 'ANY_GCC'], + 'RISC-V': ['RISCV_GCC', 'ANY_GCC'], 'MIPS': ['MTI_GCC'], 'ANY-GCC': ['ANY_GCC'] }; @@ -2286,16 +2286,27 @@ class GCC implements IToolchian { return result; } + private getThumbOptionStr(builderOpts: BuilderOptions): string { + if (builderOpts.global && builderOpts.global['arm-thumb-mode'] === 'arm') + return '-marm'; + else + return '-mthumb'; + } + updateCppIntellisenceCfg(builderOpts: BuilderOptions, cppToolsConfig: CppConfigItem): void { cppToolsConfig.cStandard = 'c11'; cppToolsConfig.cppStandard = 'c++11'; - cppToolsConfig.compilerArgs = ['-std=${c_cppStandard}', '-mthumb']; + cppToolsConfig.compilerArgs = [ + '-std=${c_cppStandard}', this.getThumbOptionStr(builderOpts)]; // pass global args for cpptools if (builderOpts.global) { + if (builderOpts.global['arm-thumb-interwork']) + cppToolsConfig.compilerArgs.push('-mthumb-interwork'); + const cpuName = builderOpts.global['_cpuName'] || 'cortex-m3'; const fpuType = builderOpts.global['_fpuType'] || ''; const archExt = builderOpts.global['_archExt'] || ''; @@ -2350,6 +2361,10 @@ class GCC implements IToolchian { // should be specified at compile time and during the final link. options['global']['optimization-lto'] = options['c/cpp-compiler']['optimization-lto']; options['c/cpp-compiler']['optimization-lto'] = undefined; + + // 默认状态下是 thumb 模式,以兼容旧的项目 + if (!options.global['arm-thumb-mode']) + options.global['arm-thumb-mode'] = 'thumb'; } getInternalDefines(builderCfg: T, builderOpts: BuilderOptions): utility.CppMacroDefine[] { @@ -2361,7 +2376,7 @@ class GCC implements IToolchian { const compilerArgs = this.getCompilerTargetArgs(cpuName, fpuType, archExt, builderOpts); if (compilerArgs.length > 0) { - compilerArgs.push('-mthumb'); + compilerArgs.push(this.getThumbOptionStr(builderOpts)); const gccpath = this.getGccFamilyCompilerPathForCpptools('c'); const defines = utility.getGccInternalDefines(gccpath, compilerArgs); if (defines) { @@ -2482,7 +2497,7 @@ class IARARM implements IToolchian { preHandleOptions(prjInfo: IProjectInfo, options: BuilderOptions): void { // init null options - for (const key of ['linker', 'c/cpp-compiler']) { + for (const key of ['global', 'linker', 'c/cpp-compiler']) { if ((options)[key] === undefined) { (options)[key] = Object.create(null); } @@ -2492,6 +2507,10 @@ class IARARM implements IToolchian { if (options['linker']['output-format'] === 'lib') { options['linker']['$use'] = 'linker-lib'; } + + // 默认状态下是 thumb 模式,以兼容旧的项目 + if (!options.global['arm-thumb-mode']) + options.global['arm-thumb-mode'] = 'thumb'; } getToolchainDir(): File { diff --git a/src/WebPanelManager.ts b/src/WebPanelManager.ts index 7d79a8b..18993ba 100644 --- a/src/WebPanelManager.ts +++ b/src/WebPanelManager.ts @@ -165,7 +165,7 @@ export class WebPanelManager { this.memoryLayoutViewRef.delete(project.getUid()); }); - const compileModel = project.GetConfiguration().compileConfigModel; + const compileModel = project.GetConfiguration().toolchainConfigModel; panel.webview.onDidReceiveMessage((_data: any) => { /* it's a message */ @@ -264,7 +264,7 @@ export class WebPanelManager { /* prepare page-init event data */ const initMsg = { model: JSON.parse(File.from(resManager.getAppRootFolder().path, 'lang', toolchain.verifyFileName).Read()), - data: projectConfig.compileConfigModel.getOptions(), + data: projectConfig.toolchainConfigModel.getOptions(), info: { lang: vscode.env.language, envList: envList, @@ -291,27 +291,6 @@ export class WebPanelManager { // it's a event from web view if (typeof data === 'string') { switch (data) { - // require open config file - case 'open-config': { - const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(project.getEideProjectFile().path)); - // get builder options location in file - let docSelection: vscode.Range | undefined; - const rootNode = jsonc_parser.parseTree(project.getEideProjectFile().Read()); - if (rootNode) { - const targetName = project.getCurrentTarget(); - const toolchainName = project.getToolchain().name; - const node = jsonc_parser.findNodeAtLocation(rootNode, - ['targets', targetName, 'builderOptions', toolchainName]); - if (node) { - let s = doc.positionAt(node.offset); - let e = doc.positionAt(node.offset + node.length); - docSelection = new vscode.Range(s, e); - } - } - // open document - vscode.window.showTextDocument(doc, { preview: true, selection: docSelection }); - break; - } /* post page-init event */ case 'eide.options_view.launched': panel.webview.postMessage(initMsg); @@ -329,7 +308,7 @@ export class WebPanelManager { }; try { - projectConfig.compileConfigModel.setOptions(data); + projectConfig.toolchainConfigModel.setOptions(data); status.success = true; status.msg = view_str$operation$done; project.onBuilderConfigChanged(); diff --git a/src/extension.ts b/src/extension.ts index 58fc7ac..779c6dc 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -61,11 +61,6 @@ const extension_deps: string[] = []; let projectExplorer: ProjectExplorer; let operationExplorer: OperationExplorer; -// set yaml global style -yaml.scalarOptions.str.fold.lineWidth = 1000; -yaml.defaultOptions.indent = 4; -yaml.defaultOptions.indentSeq = true; - // this method is called when your extension is activated // your extension is activated the very first time the command is executed export async function activate(context: vscode.ExtensionContext) { @@ -78,7 +73,7 @@ export async function activate(context: vscode.ExtensionContext) { // init platform try { - platform.init(); + platform.init(context); } catch (error) { const msg = (error).message; vscode.window.showErrorMessage(msg); @@ -315,7 +310,7 @@ export async function activate(context: vscode.ExtensionContext) { projectExplorer.enableAutoSave(true); // load project in this workspace - projectExplorer.loadWorkspace(); + projectExplorer.loadWorkspace(context.globalState); // hook postLaunchHook(context);