Ejemplo n.º 1
0
def _fixRelativePaths(paths: Paths, relativePaths: list):
    '''
    Correct relative paths according to the folder structure as it is expected.
    Relative paths in Keil project file are relative to the keil file path,
    while we need paths relative to root folder where 'ideScripts' is.

    Return list of a VALID relative paths paths.
    '''
    keilProjectAbsPath = os.path.normpath(os.path.join(paths.rootFolder, paths.keilProject))

    allPaths = []
    for relativePath in relativePaths:
        if os.path.isabs(relativePath):
            relativePath = os.path.normpath(relativePath)
            relativePath = utils.pathWithForwardSlashes(relativePath)
            allPaths.append(relativePath)
            continue

        absolutePath = os.path.normpath(os.path.join(paths.keilProjectFolder, relativePath))
        if os.path.exists(absolutePath):
            # path is valid, build correct relative path
            try:
                newRelativePath = os.path.relpath(absolutePath, paths.rootFolder)
                newRelativePath = utils.pathWithForwardSlashes(newRelativePath)
                allPaths.append(newRelativePath)
            except:
                absolutePath = utils.pathWithForwardSlashes(absolutePath)
                allPaths.append(absolutePath)
        else:
            print("WARNING: unable to find file/folder:", absolutePath)
            print("\tBuilt from relative path:", relativePath)

    return allPaths
Ejemplo n.º 2
0
def getCubeMxExePath():
    '''
    Get absolute path to STM32CubeMX.exe either by windows default associated program or user input.
    '''
    cubeMxPath = utils.findExecutablePath('ioc', raiseException=False)
    if cubeMxPath is not None:
        if os.path.exists(cubeMxPath):
            cubeMxPath = utils.pathWithForwardSlashes(cubeMxPath)
            print("STM32CubeMX.exe path automatically updated.")
            return cubeMxPath
    else:
        while cubeMxPath is None:
            cubeMxPath = utils.getUserPath('STM32CubeMX.exe')
            if os.path.exists(cubeMxPath):
                cubeMxPath = utils.pathWithForwardSlashes(cubeMxPath)
                return cubeMxPath
            else:
                cubeMxPath = None
Ejemplo n.º 3
0
def _copyStartupFile(paths: Paths, keilProjData: KeilProjectData):
    '''
    Get '*.s' startup file in the same folder as CubeMX template Makefile file and
    copy it into the same location as current startup file is.
    '''
    # find CubeMX temporary generated startup file
    filesInMakefileDir = os.listdir(os.path.dirname(paths.tmpMakefile))
    for theFile in filesInMakefileDir:
        name, ext = os.path.splitext(theFile)
        if ext == '.s':
            startupFile = os.path.join(os.path.dirname(paths.tmpMakefile), theFile)
            newStartupFilePath = os.path.join(paths.rootFolder, theFile)
            try:
                shutil.copy(startupFile, newStartupFilePath)
                print("Default STM32CubeMX startup file copied to:", newStartupFilePath)

                relativeStartupFilePath = os.path.relpath(newStartupFilePath, paths.rootFolder)
                relativeStartupFilePath = utils.pathWithForwardSlashes(relativeStartupFilePath)
                break
            except Exception as err:
                pass
                #print("Seems like default STM32CubeMX startup file already exist:", newStartupFilePath)

    # find startup file in current keil project data and replace it with this one
    if len(keilProjData.asmSources) == 1:
        # no problem only one '*.s' file, assume this is the startup file
        originalStartupFile = keilProjData.asmSources[0]
        keilProjData.asmSources = [relativeStartupFilePath]

        msg = "Default " + originalStartupFile + " source was replaced with CubeMX one: " + relativeStartupFilePath
        print(msg)
        return

    else:
        # more than one assembler file found, try to find file with 'startup' string or throw error
        possibleStartupFiles = []
        for startupFileListIndex, asmFile in enumerate(keilProjData.asmSources):
            _, fileName = os.path.split(asmFile)
            if fileName.lower().find('startup') != -1:
                possibleStartupFiles.append((asmFile, startupFileListIndex))  # asm file, file index in list

        if len(possibleStartupFiles) == 1:
            # OK, only one file with startup string
            originalStartupFile = keilProjData.asmSources[possibleStartupFiles[0][1]]
            keilProjData.asmSources[possibleStartupFiles[0][1]] = relativeStartupFilePath

            msg = "WARNING: Multiple '*.s' files found. "
            msg += originalStartupFile + " source file was replaced with CubeMX one: " + relativeStartupFilePath
            print(msg)

        else:
            errorMsg = "Multiple '*.s' source files listed. Can't determine startup file (searched with 'startup' string)."
            errorMsg += "\n\tAsm files: " + str(keilProjData.asmSources)
            utils.printAndQuit(errorMsg)
Ejemplo n.º 4
0
def createMakefileTemplate(paths: Paths, keilProjData: KeilProjectData):
    '''
    Create Makefile template with CubeMX.
    '''
    # create script that CubeMX executes
    paths.tmpCubeMxFolder = os.path.join(paths.rootFolder, tmpStr.cubeMxTmpFolderName)
    paths.tmpCubeMxFolder = utils.pathWithForwardSlashes(paths.tmpCubeMxFolder)
    if not os.path.exists(paths.tmpCubeMxFolder):
        try:
            os.mkdir(paths.tmpCubeMxFolder)
        except Exception as err:
            errorMsg = "Unable to create existing temporary folder:\n" + str(err)
            print(errorMsg)

    # even if any error occured, try to create files anyway
    _createCubeMxTmpScript(paths, keilProjData)

    # run CubeMX as subprocess with this script as a parameter
    cmd = ['java', '-jar', paths.cubeMxExe, '-s', paths.tmpCubeMxScript]
    if _checkCubeMxFirmwarePackage(paths, keilProjData):
        cmd.append('-q')  # no-gui mode
        print("\tSTM32CubeMX GUI set to non-visible mode.")
    else:
        print("\tSTM32CubeMX GUI set to visible because of repository warning.")

    try:
        print("Generating template Makefile with STM32CubeMX...")
        proc = subprocess.run(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
        if proc.returncode == 0:
            print("\tSTM32CubeMX project generated.")
        else:
            errorMsg = "CubeMx returned non-zero exit code. Something went wrong:\n"
            errorMsg += str(proc.stderr) + '\n'
            errorMsg += str(proc.stdout)

            utils.printAndQuit(errorMsg)
    except Exception as err:
        errorMsg = "Exception error while creating template Makefile with STM32CubeMX:\n" + str(err)
        utils.printAndQuit(errorMsg)

    # get makefile path
    allGeneratedFiles = utils.getAllFilesInFolderTree(paths.tmpCubeMxFolder)
    for theFile in allGeneratedFiles:
        _, fileName = os.path.split(theFile)
        if fileName == 'Makefile':
            paths.tmpMakefile = theFile
            print("\tMakefile found: " + paths.tmpMakefile)

            _copyStartupFile(paths, keilProjData)
            return
    else:
        errorMsg = "Unable to find template Makefile generated by STM32CubeMX. Was project really generated?"
        utils.printAndQuit(errorMsg)
Ejemplo n.º 5
0
    def addBuildDataToWorkspaceFile(self, workspaceData, buildData):
        '''
        This function ads "cortex-debug.*" items to workspace file, if they don't exist yet.
        Returns new data.
        '''
        armToolchainPath = os.path.dirname(buildData[self.bStr.gccExePath])
        armToolchainPath = utils.pathWithForwardSlashes(armToolchainPath)

        workspaceData["settings"][
            "cortex-debug.armToolchainPath"] = armToolchainPath
        workspaceData["settings"]["cortex-debug.openocdPath"] = buildData[
            self.bStr.openOcdPath]

        return workspaceData
Ejemplo n.º 6
0
    def getBuildTask(self):
        '''
        Add build task (execute 'make' command). Also the VS Code default 'build' task.
        '''
        taskData = """
        {
            "label": "will be replaced with templateStrings string",
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "type": "shell",
            "command": "specified below",
            "args": ["specified below"],
            "problemMatcher": {
                "pattern": {
                    "regexp": "^(.*):(\\\\d+):(\\\\d+):\\\\s+(warning|error):\\\\s+(.*)$",
                    "file": 1,
                    "line": 2,
                    "column": 3,
                    "severity": 4,
                    "message": 5
                }
            },
            "presentation": {
                "focus": true
            }
        }
        """
        jsonTaskData = json.loads(taskData)

        buildData = build.BuildData().getBuildData()
        jsonTaskData["label"] = tmpStr.taskName_build
        jsonTaskData["command"] = buildData[self.bStr.buildToolsPath]

        gccFolderPath = os.path.dirname(buildData[self.bStr.gccExePath])
        gccFolderPath = utils.pathWithForwardSlashes(gccFolderPath)
        jsonTaskData["args"] = ["GCC_PATH=" + gccFolderPath
                                ]  # specify compiler path to make command

        numOfCores = os.cpu_count()
        parallelJobsNumber = int(
            numOfCores * 1.5
        )  # https://stackoverflow.com/questions/15289250/make-j4-or-j8/15295032
        parallelJobsStr = "-j" + str(parallelJobsNumber)
        jsonTaskData["args"].append(
            parallelJobsStr)  # set 'make' parallel job execution

        return jsonTaskData
Ejemplo n.º 7
0
def _getAbsolutePaths(relativePaths):
    '''
    Get list of relative paths and try to build absolute paths.
    If any path does not exist, print warning message.
    Return list of valid absolute paths.
    '''
    absolutePaths = []
    for relativePath in relativePaths:
        relativePath = relativePath.strip()
        relativePath = os.path.normpath(os.path.join(paths.keilProjectFolder, relativePath))
        if os.path.exists(relativePath):
            relativePath = utils.pathWithForwardSlashes(relativePath)
            absolutePaths.append(relativePath)
        else:
            print("WARNING: unable to find file/folder:", relativePath)

    return absolutePaths
Ejemplo n.º 8
0
    def copyTargetConfigurationFiles(self, buildData):
        '''
        This function checks if paths to target configuration files listed in 'BuildDataStrings.targetConfigurationPaths'
        are available, stored inside this workspace '.vscode' subfolder. Once this files are copied, paths are updated and
        new buildData is returned.

        Paths are previously checked/updated in 'verifyTargetConfigurationPaths()'
        '''
        for pathName in self.bStr.targetConfigurationPaths:
            currentPaths = buildData[pathName]

            if isinstance(currentPaths, list):
                isList = True
            else:
                isList = False
                currentPaths = [currentPaths]

            newPaths = []
            for currentPath in currentPaths:
                fileName = utils.getFileName(currentPath, withExtension=True)
                fileInVsCodeFolder = os.path.join(utils.vsCodeFolderPath,
                                                  fileName)

                if not utils.pathExists(fileInVsCodeFolder):
                    # file does not exist in '.vscode' folder
                    try:
                        newPath = shutil.copy(currentPath,
                                              utils.vsCodeFolderPath)
                    except Exception as err:
                        errorMsg = "Unable to copy file '" + fileName + "' to '.vscode' folder. Exception:\n" + str(
                            err)
                        utils.printAndQuit(errorMsg)

                newPath = os.path.relpath(fileInVsCodeFolder)
                newPath = utils.pathWithForwardSlashes(newPath)
                newPaths.append(newPath)

            if isList:
                buildData[pathName] = newPaths
            else:
                buildData[pathName] = newPaths[0]

        return buildData
Ejemplo n.º 9
0
def _createCubeMxTmpScript(paths: Paths, keilProjData: KeilProjectData):
    '''
    Create tempory script for CubeMX Makefile generation.
    Raises exception on error.
    '''
    paths.tmpCubeMxScript = os.path.join(paths.tmpCubeMxFolder, tmpStr.cubeMxTmpFileName)
    paths.tmpCubeMxScript = utils.pathWithForwardSlashes(paths.tmpCubeMxScript)

    dataToWrite = "// Temporary script for generating Base Makefile with STM32CubeMX.\n"
    dataToWrite += "load " + _getCPUName(paths, keilProjData) + "\n"
    dataToWrite += "project name " + keilProjData.projName + "\n"
    dataToWrite += "project toolchain Makefile\n"
    dataToWrite += "project path \"" + paths.tmpCubeMxFolder + "\"\n"
    dataToWrite += "project generate\n"
    dataToWrite += "exit"

    with open(paths.tmpCubeMxScript, 'w+') as scriptHandler:
        scriptHandler.write(dataToWrite)

    print("Temporary STM32CubeMX script created.")
Ejemplo n.º 10
0
    def getBuildTask(self):
        '''
        Add build task (execute 'make' command). Also the VS Code default 'build' task.
        '''
        taskData = """
        {
            "label": "will be replaced with templateStrings string",
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "type": "shell",
            "command": "specified below",
            "args": ["specified below"],
            "problemMatcher": {
                "pattern": {
                    "regexp": "^(.*):(\\\\d+):(\\\\d+):\\\\s+(warning|error):\\\\s+(.*)$",
                    "file": 1,
                    "line": 2,
                    "column": 3,
                    "severity": 4,
                    "message": 5
                }
            },
            "presentation": {
                "focus": true
            }
        }
        """
        jsonTaskData = json.loads(taskData)

        buildData = build.BuildData().getBuildData()
        jsonTaskData["label"] = tmpStr.taskName_build
        jsonTaskData["command"] = buildData[self.bStr.buildToolsPath]

        gccFolderPath = os.path.dirname(buildData[self.bStr.gccExePath])
        gccFolderPath = utils.pathWithForwardSlashes(gccFolderPath)
        jsonTaskData["args"] = ["GCC_PATH=" + gccFolderPath
                                ]  # specify compiler path to make command

        return jsonTaskData
Ejemplo n.º 11
0
    def addDownloadAndRunTask(self, tasksData):
        '''
        Create/repair 'CPU: Download and run' task.
        '''
        # User edit BEGIN
        taskData = """
        {
            "label": "CPU: Download and run",
            "type": "shell",
            "command": "specified below",
            "args": ["specified below"],
            "problemMatcher": []
        }
        """
        jsonTaskData = json.loads(taskData)

        buildData = build.BuildData().getBuildData()
        jsonTaskData["command"] = buildData[self.bStr.openOCDPath]
        jsonTaskData["args"] = []
        jsonTaskData["args"].append("-f")
        jsonTaskData["args"].append(buildData[self.bStr.openOCDInterfacePath])
        jsonTaskData["args"].append("-f")
        jsonTaskData["args"].append(buildData[self.bStr.openOCDTargetPath])

        # -c program filename [verify] [reset] [exit] [offset] ([] are optional arguments)
        # Note: due problems with VS Code OpenOCD Tasks in case of workspace path containing spaces, target executable is passed
        # as relative path. Not a problem since VS Code shell is started from workspace folder.
        workspacePath = utils.workspacePath
        targetExecutablePath = buildData[self.bStr.targetExecutablePath]
        relativeTargetExecutablePath = os.path.relpath(targetExecutablePath,
                                                       workspacePath)
        relativeTargetExecutablePath = utils.pathWithForwardSlashes(
            relativeTargetExecutablePath)
        jsonTaskData["args"].append("-c")
        programString = "program " + relativeTargetExecutablePath + " verify reset exit"
        jsonTaskData["args"].append(programString)

        # User edit END
        tasksData = self.addOrReplaceTask(tasksData, jsonTaskData)
        return tasksData
Ejemplo n.º 12
0
    def addBuildTask(self, tasksData):
        '''
        Add build task (execute 'make' command).
        '''
        # User edit BEGIN

        taskData = """
        {
            "label": "Build project",
            "type": "shell",
            "command": "specified below",
            "args": ["specified below"],
            "problemMatcher": {
                "pattern": {
                    "regexp": "^(.*):(\\\\d+):(\\\\d+):\\\\s+(warning|error):\\\\s+(.*)$",
                    "file": 1,
                    "line": 2,
                    "column": 3,
                    "severity": 4,
                    "message": 5
                }
            },
            "presentation": {
                "focus": true
            }
        }
        """
        jsonTaskData = json.loads(taskData)

        buildData = build.BuildData().getBuildData()
        jsonTaskData["command"] = buildData[self.bStr.buildToolsPath]

        gccFolderPath = os.path.dirname(buildData[self.bStr.gccExePath])
        gccFolderPath = utils.pathWithForwardSlashes(gccFolderPath)
        jsonTaskData["args"] = ["GCC_PATH=" + gccFolderPath
                                ]  # specify compiler path to make command

        # User edit END
        tasksData = self.addOrReplaceTask(tasksData, jsonTaskData)
        return tasksData
Ejemplo n.º 13
0
    def getDownloadAndRunTask(self):
        '''
        Create Download and run task.
        '''
        taskData = """
        {
            "label": "will be replaced with templateStrings string",
            "type": "shell",
            "command": "specified below",
            "args": ["specified below"],
            "problemMatcher": []
        }
        """
        jsonTaskData = json.loads(taskData)

        buildData = build.BuildData().getBuildData()
        jsonTaskData["label"] = tmpStr.taskName_CPU_downloadRun
        jsonTaskData["command"] = buildData[self.bStr.openOcdPath]
        jsonTaskData["args"] = []
        jsonTaskData["args"].append("-f")
        jsonTaskData["args"].append(buildData[self.bStr.openOcdInterfacePath])
        for arg in buildData[self.bStr.openOcdConfig]:
            jsonTaskData["args"].append("-f")
            jsonTaskData["args"].append(arg)

        # -c program filename [verify] [reset] [exit] [offset] ([] are optional arguments)
        # Note: due problems with VS Code OpenOCD Tasks in case of workspace path containing spaces, target executable is passed
        # as relative path. Not a problem since VS Code shell is started from workspace folder.
        workspacePath = utils.workspacePath
        targetExecutablePath = buildData[self.bStr.targetExecutablePath]
        relativeTargetExecutablePath = os.path.relpath(targetExecutablePath,
                                                       workspacePath)
        relativeTargetExecutablePath = utils.pathWithForwardSlashes(
            relativeTargetExecutablePath)
        jsonTaskData["args"].append("-c")
        programString = "program " + relativeTargetExecutablePath + " verify reset exit"
        jsonTaskData["args"].append(programString)

        return jsonTaskData
Ejemplo n.º 14
0
    data = json.loads(dataToWrite)
    data = json.dumps(data, indent=4, sort_keys=False)

    codeWorkspaceFileName = keilProjData.projName + '.code-workspace'
    codeWorkspaceFilePath = os.path.join(paths.rootFolder, codeWorkspaceFileName)
    with open(codeWorkspaceFilePath, 'w+') as fileHandler:
        fileHandler.write(data)

    print("VS Code workspace file created:", codeWorkspaceFilePath)


if __name__ == "__main__":
    paths = Paths()
    thisFileAbsPath = os.path.abspath(sys.argv[0])
    paths.rootFolder = os.path.dirname(os.path.dirname(thisFileAbsPath))
    paths.rootFolder = utils.pathWithForwardSlashes(paths.rootFolder)

    paths.cubeMxExe = getCubeMxExePath()
    paths.keilProject = getKeilProjectPath(paths)
    paths.keilProjectFolder = utils.pathWithForwardSlashes(os.path.dirname(paths.keilProject))
    paths.outputMakefile = utils.pathWithForwardSlashes(os.path.join(paths.rootFolder, 'Makefile'))

    keilProjData = getKeilProjectData(paths)

    createMakefileTemplate(paths, keilProjData)
    cleanMakefileData = cleanTempMakefile(paths)
    createNewMakefile(paths, keilProjData, cleanMakefileData)
    deleteTemporaryFiles(paths)

    createVSCodeWorkspace(paths, keilProjData)