diff options
Diffstat (limited to 'sw/ideScripts/updateBuildData.py')
| -rwxr-xr-x | sw/ideScripts/updateBuildData.py | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/sw/ideScripts/updateBuildData.py b/sw/ideScripts/updateBuildData.py new file mode 100755 index 0000000..3e84356 --- /dev/null +++ b/sw/ideScripts/updateBuildData.py @@ -0,0 +1,371 @@ +''' +Update/generate 'buildData.json' file in '.vscode' subfolder from new Makefile. +This file also handles 'toolsPaths.json' file. +New Makefile is not updated by this script - it is updated with 'updateMakefile.py' or 'updateWorkspaceSources.py' +''' +import os +import json +import datetime + +import utilities as utils +import templateStrings as tmpStr + +import updatePaths as pth +import updateMakefile as mkf +import updateWorkspaceSources as wks + +__version__ = utils.__version__ + + +class BuildDataStrings(): + # project sources, includes, defines, .... + cSources = 'cSources' + asmSources = 'asmSources' + ldSources = 'ldSources' + + cIncludes = 'cIncludes' + asmIncludes = 'asmIncludes' + ldIncludes = 'ldIncludes' + + cDefines = 'cDefines' + asmDefines = 'asmDefines' + + cFlags = 'cFlags' + asmFlags = 'asmFlags' + ldFlags = 'ldFlags' + + buildDirPath = 'buildDir' + + # build/interface tools paths, configuration files + gccInludePath = 'gccInludePath' # GCC standard libraries root folder path + gccExePath = 'gccExePath' # path to 'gcc.exe' + + buildToolsPath = 'buildToolsPath' # path to 'make.exe' + targetExecutablePath = 'targetExecutablePath' # path to downloadable '*.elf' file + + pythonExec = 'pythonExec' + + openOcdPath = 'openOcdPath' # path to 'openocd.exe' + openOcdInterfacePath = "openOcdInterfacePath" # path to OpenOCD interface cofniguration file (currently 'stlink.cfg') + + openOcdConfig = 'openOcdConfig' # path to target '*.cfg' file + stm32SvdPath = 'stm32SvdPath' # path to target '*.svd' file + + cubeMxProjectPath = 'cubeMxProjectPath' + + # list of paths that are automatically built (default, system or once their 'parent' paths are valid) + derivedPaths = [ + pythonExec, + gccInludePath + ] + + # list of target-specific configuration paths that must exist in 'buildData.json' + targetConfigurationPaths = [ + openOcdConfig, + stm32SvdPath + ] + + # list of paths that can be cached in 'toolsPaths.json' + toolsPaths = [ + gccExePath, + buildToolsPath, + pythonExec, + openOcdPath, + openOcdInterfacePath + ] + + +class BuildData(): + def __init__(self): + self.mkfStr = mkf.MakefileStrings() + self.cPStr = wks.CPropertiesStrings() + self.bStr = BuildDataStrings() + + def prepareBuildData(self, request=False): + ''' + This function is used in all 'update*.py' scripts and makes sure, that 'toolsPaths.json' and 'buildData.json' with a + valid tools/target cofniguration paths exist. Invalid paths are updated (requested from the user). + Returns available, valid build data. + + Note: tools paths listed in 'BuildDataStrings.toolsPaths' are stored in system local 'toolsPaths.json' file, and are + copied (overwritten) to 'buildData.json' on first 'Update' task run. This makes it possible for multiple code contributors. + ''' + paths = pth.UpdatePaths() + + self.checkBuildDataFile() + buildData = self.getBuildData() + + if self.checkToolsPathFile(): # a valid toolsPaths.json exists + toolsPathsData = self.getToolsPathsData() + + else: + # no valid data from 'toolsPaths.json' file + # try to get data from current 'buildData.json' - backward compatibility for paths that already exist in 'buildData.json' + toolsPathsData = json.loads(tmpStr.toolsPathsTemplate) + for path in self.bStr.toolsPaths: + if path in buildData: + if utils.pathExists(buildData[path]): + toolsPathsData[path] = buildData[path] + + # update/overwrite tools paths file. Don't mind if paths are already valid. + toolsPathsData = paths.verifyToolsPaths(toolsPathsData, request) + self.createUserToolsFile(toolsPathsData) + + buildData = self.addToolsPathsToBuildData(buildData, toolsPathsData) + + templateBuildData = json.loads(tmpStr.buildDataTemplate) + buildData = utils.mergeCurrentDataWithTemplate(buildData, templateBuildData) + + buildData = paths.verifyTargetConfigurationPaths(buildData, request) + buildData = paths.copyTargetConfigurationFiles(buildData) + + return buildData + + def checkToolsPathFile(self): + ''' + Returns True if 'toolsPaths.json' file exists and is a valid JSON file. + If it is not a valid JSON, delete it and return False. + ''' + if utils.pathExists(utils.toolsPaths): + # file exists, check if it loads OK + try: + with open(utils.toolsPaths, 'r') as toolsFileHandler: + json.load(toolsFileHandler) + print("Valid 'toolsPaths.json' file found.") + return True + + except Exception as err: + errorMsg = "Invalid 'toolsPaths.json' file. Error:\n" + str(err) + print(errorMsg) + + try: + os.remove(utils.toolsPaths) + msg = "\tDeleted. New 'toolsPaths.json' will be created on first workspace update." + print(msg) + except Exception as err: + errorMsg = "Error deleting 'toolsPaths.json'. Error:\n" + str(err) + utils.printAndQuit(errorMsg) + + # else: toolsPaths.json does not exist + return False + + def checkBuildDataFile(self): + ''' + This function makes sure 'buildData.json' is available. + If existing 'buildData.json' file is a valid JSON, it returns immediately. + If it is not a valid JSON file OR it does not exist, new 'buildData.json' file is created from template. + + Note: There is no backup file for buildData.json, since it is always regenerated on Update task. + ''' + if utils.pathExists(utils.buildDataPath): + # file exists, check if it loads OK + try: + with open(utils.buildDataPath, 'r') as buildDataFileHandler: + json.load(buildDataFileHandler) + print("Valid 'buildData.json' file found.") + + return + + except Exception as err: + errorMsg = "Invalid 'buildData.json' file. Error:\n" + str(err) + print(errorMsg) + + try: + os.remove(utils.buildDataPath) + msg = "\tDeleted. New 'buildData.json' will be created on first workspace update." + print(msg) + except Exception as err: + errorMsg = "Error deleting 'buildData.json'. Error:\n" + str(err) + utils.printAndQuit(errorMsg) + + # else: buildData.json does not exist + self.createBuildDataFile() + + def createUserToolsFile(self, toolsPaths): + ''' + Create 'toolsPaths.json' file with current tools paths. + This pats are absolute and not project-specific. + ''' + data = json.loads(tmpStr.toolsPathsTemplate) + try: + data["VERSION"] = __version__ + data["LAST_RUN"] = str(datetime.datetime.now()) + + for path in self.bStr.toolsPaths: + data[path] = toolsPaths[path] + + data = json.dumps(data, indent=4, sort_keys=False) + with open(utils.toolsPaths, 'w+') as toolsPathsFile: + toolsPathsFile.write(data) + print("'toolsPaths.json' file updated!") + + except Exception as err: + errorMsg = "Exception error overwriting 'toolsPaths.json' file:\n" + errorMsg += str(err) + print("WARNING:", errorMsg) + + def createBuildDataFile(self): + ''' + Create fresh 'buildData.json' file. + ''' + try: + data = json.loads(tmpStr.buildDataTemplate) + dataToWrite = json.dumps(data, indent=4, sort_keys=False) + + with open(utils.buildDataPath, 'w+') as buildDataFile: + buildDataFile.truncate() + buildDataFile.write(dataToWrite) + + print("New template 'buildData.json' file created.") + except Exception as err: + errorMsg = "Exception error creating new 'buildData.json' file:\n" + errorMsg += str(err) + utils.printAndQuit(errorMsg) + + def getToolsPathsData(self): + ''' + Get data from current 'toolsPaths.json' file. + File existance is previoulsy checked in 'checkToolsPathFile()'. + ''' + with open(utils.toolsPaths, 'r') as toolsPathsFile: + data = json.load(toolsPathsFile) + + return data + + def getBuildData(self): + ''' + Get data from current 'buildData.json' file. + File existance is previoulsy checked in 'checkBuildDataFile()'. + ''' + with open(utils.buildDataPath, 'r') as buildDataFile: + data = json.load(buildDataFile) + + return data + + def addToolsPathsToBuildData(self, buildData, toolsPaths): + ''' + Get tools paths from 'toolsPaths.json' and add it to buildData + Returns new data. + ''' + allToolsPaths = [] + allToolsPaths.extend(self.bStr.toolsPaths) + allToolsPaths.extend(self.bStr.derivedPaths) + for path in allToolsPaths: + try: + buildData[path] = toolsPaths[path] + except Exception as err: + errorMsg = "Missing '" + path + "' key in tools paths data:\n" + str(toolsPaths) + print("Warning:", errorMsg) + + return buildData + + def addMakefileDataToBuildDataFile(self, buildData, makefileData): + ''' + This function fills buildData.json file with data from 'Makefile'. + Returns new data. + ''' + # sources + cSources = makefileData[self.mkfStr.cSources] + buildData[self.bStr.cSources] = cSources + + asmSources = makefileData[self.mkfStr.asmSources] + buildData[self.bStr.ldSources] = asmSources + + ldSources = makefileData[self.mkfStr.ldSources] + buildData[self.bStr.ldSources] = ldSources + + # includes + cIncludes = makefileData[self.mkfStr.cIncludes] + buildData[self.bStr.cIncludes] = cIncludes + + asmIncludes = makefileData[self.mkfStr.asmIncludes] + buildData[self.bStr.asmIncludes] = asmIncludes + + ldIncludes = makefileData[self.mkfStr.ldIncludes] + buildData[self.bStr.ldIncludes] = ldIncludes + + # defines + cDefines = makefileData[self.mkfStr.cDefines] + buildData[self.bStr.cDefines] = cDefines + + asmDefines = makefileData[self.mkfStr.asmDefines] + buildData[self.bStr.asmDefines] = asmDefines + + # compiler flags and paths + cFlags = makefileData[self.mkfStr.cFlags] + buildData[self.bStr.cFlags] = cFlags + + asmFlags = makefileData[self.mkfStr.asmFlags] + buildData[self.bStr.asmFlags] = asmFlags + + ldFlags = makefileData[self.mkfStr.ldFlags] + buildData[self.bStr.ldFlags] = ldFlags + + # build folder must be always inside workspace folder + buildDirPath = makefileData[self.mkfStr.buildDir] + buildData[self.bStr.buildDirPath] = buildDirPath + + # Target executable '.elf' file + projectName = makefileData[self.mkfStr.projectName] + targetExecutablePath = utils.getBuildElfFilePath(buildDirPath, projectName) + buildData[self.bStr.targetExecutablePath] = targetExecutablePath + + return buildData + + def addCubeMxProjectPathToBuildData(self, buildData): + ''' + If utils.cubeMxProjectFilePath is not None, add/update 'cubeMxProjectPath' field to 'buildData.json'. + ''' + if utils.cubeMxProjectFilePath is not None: + buildData[self.bStr.cubeMxProjectPath] = utils.cubeMxProjectFilePath + else: + buildData.pop(self.bStr.cubeMxProjectPath) + return buildData + + def overwriteBuildDataFile(self, data): + ''' + Overwrite existing 'buildData.json' file with new data. + ''' + try: + with open(utils.buildDataPath, 'r+') as buildDataFile: + data["VERSION"] = __version__ + data["LAST_RUN"] = str(datetime.datetime.now()) + + buildDataFile.seek(0) + buildDataFile.truncate() + dataToWrite = json.dumps(data, indent=4, sort_keys=False) + buildDataFile.write(dataToWrite) + + print("'buildData.json' file updated!") + + except Exception as err: + errorMsg = "Exception error overwriting 'buildData.json' file:\n" + errorMsg += str(err) + utils.printAndQuit(errorMsg) + + +######################################################################################################################## +if __name__ == "__main__": + utils.verifyFolderStructure() + + paths = pth.UpdatePaths() + makefile = mkf.Makefile() + bData = BuildData() + + # Makefile must exist - # point in continuing if Makefile does not exist + makefile.checkMakefileFile() + + # build data (update tools paths if neccessary) + buildData = bData.prepareBuildData() + + # data from current Makefile + makeExePath = buildData[bData.bStr.buildToolsPath] + gccExePath = buildData[bData.bStr.gccExePath] + makefileData = makefile.getMakefileData(makeExePath, gccExePath) + + # try to add CubeMX project file path + buildData = bData.addCubeMxProjectPathToBuildData(buildData) + + buildData = bData.addMakefileDataToBuildDataFile(buildData, makefileData) + + bData.overwriteBuildDataFile(buildData) |
