''' 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)