diff --git a/doc/index.rst b/doc/index.rst index 2261ff7..02e2431 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -54,20 +54,20 @@ How does it work? 2. Scan the Xilinx installation directory for available Vivado versions. 3. If a matching version was found, start Vivado and pass the ``*.xpr`` as a parameter. -Differences to opening the ``*.xpr`` from GUI? -============================================== +Differences to opening the ``*.xpr`` from within Vivado GUI? +============================================================ By default, Xilinx Vivado has its working directory in ``AppData``, but the working directory should be in the directory where the ``*.xpr`` file is located. This is fixed by **pyEDAA.Launcher** as a side effect. Now, Vivado saves log and journal files to the correct locations. -.. _usecase: +.. #_usecase: -Use Cases -********* + Use Cases + ********* -* Handle multiple parallel Xilinx Vivado installations. + * Handle multiple parallel Xilinx Vivado installations. .. _news: diff --git a/pyEDAA/Launcher/__init__.py b/pyEDAA/Launcher/__init__.py index 6049242..891a907 100644 --- a/pyEDAA/Launcher/__init__.py +++ b/pyEDAA/Launcher/__init__.py @@ -36,84 +36,129 @@ __version__ = "0.1.0" __keywords__ = ["launcher", "version selector", "xilinx", "vivado"] +from re import compile as re_compile + import sys import subprocess from pathlib import Path from textwrap import dedent import time -from typing import NoReturn +from typing import NoReturn, Generator from pyTooling.Decorators import export sub_path_bat = Path("bin/vivado.bat") sub_path_vvgl = Path("bin/unwrapped/win64.o/vvgl.exe") -match_line = "") - while True: - line = project_file.readline() - if match_line in line: - break - version = line.split(match_line)[1] - version = version.split(" ")[0] - version = version.split(".") - version_major = version[0] - version_minor = version[1] - project_file.close() - return str(version_major + "." + version_minor) +@export +class Program: + """Program instance of pyEDAA.Launcher.""" + + _projectFilePath: Path + + def __init__(self, projectFilePath: Path): + """Initializer. + + :param projectFilePath: Path to the ``*.xpr`` file. + :raises Exception: When the given ``*.xpr`` file doesn't exist. + """ + if not projectFilePath.exists(): + raise Exception(f"Vivado project file '{projectFilePath}' not found.") from FileNotFoundError(f"File '{projectFilePath}' not found.") + + self._projectFilePath = projectFilePath + + def GetVersion(self) -> str: + """Opens an ``*.xpr`` file and returns the Vivado version used to save this file. + + :returns: Used Vivado version to save the given ``*.xpr`` file. + :raises Exception: When the version information isn't found in the file. + """ + with self._projectFilePath.open("r") as file: + for line in file: + match = versionLineRegExp.match(line) + if match is not None: + return f"{match['major']}.{match['minor']}" + else: + raise Exception(f"Pattern not found in '{self._projectFilePath}'.") + + @classmethod + def GetVivadoVersions(self, installPath: Path) -> Generator[str, None, None]: + """Scan a given directory for installed Vivado versions. + + :param installPath: Xilinx installation directory. + :returns: A generator for a sequence of installed Vivado versions. + """ + for item in installPath.iterdir(): + if item.is_dir(): + yield item.name + + @classmethod + def PrintHelp(cls, scriptPath: Path) -> None: + """Print a help page. + + :param scriptPath: Path to this script. + """ + print(dedent(f"""\ + Run-Path '{scriptPath}' + + For using this Launcher, please bind the *.xpr file extension to this executable with: + * Put this executable into the Vivado installation folder. E.g: C:\\Xilinx\\Vivado\\ + * Change *.xpr association: right-click -> open with -> VivadoManager.exe + """)) @export -def get_vivado_versions(install_path): - return [item.name for item in install_path.iterdir() if item.is_dir()] +def main() -> NoReturn: + """Entry point function. + It creates an instance of :class:`Program` and hands over the execution to the OOP world. + """ + install_path = Path.cwd() + script_path = Path(sys.argv[0]) -@export -def PrintHelp(script_path: Path) -> None: - print(dedent(f"""\ - Run-Path '{script_path}' + if len(sys.argv) == 0: + Program.PrintHelp(script_path) - For using this VivadoManager please bind xpr extension to this executable with: - * Put this executable into the Vivado installation folder. E.g: c:\\Xilinx\\Vivado\\ - * Change *.xpr association: right-click-> open-width-> VivadoManager.exe - """)) + print(f"Current path '{install_path}' has following folders in it:") + for version in Program.GetVivadoVersions(install_path): + print(version) + print("") + print("Press any key to exit.") -@export -def main() -> NoReturn: - install_path = Path.cwd() - script_path = Path(sys.argv[0]) + # wait on user interaction + input() + sys.exit(0) - if len(sys.argv) > 1: - inputArg1 = sys.argv[1] - file_path = Path(inputArg1) + elif len(sys.argv) == 1: + projectFileArgument = sys.argv[1] + projectFilePath = Path(projectFileArgument) - file_version = get_version(file_path) - vivado_versions = get_vivado_versions(install_path) + program = Program(projectFilePath) - for version in vivado_versions: - if file_version == str(version): - exec_path1 = install_path / file_version / sub_path_vvgl - exec_path2 = install_path / file_version / sub_path_bat - a = str(file_path) - cmd = [str(exec_path1), str(exec_path2), a] - subprocess.Popen(cmd, cwd=file_path.parent)#, creationflags=subprocess.DETACHED_PROCESS) + try: + versionFromXPRFile = program.GetVersion() + except Exception as ex: + print(f"[ERROR] {ex}") + exit(1) + + for version in program.GetVivadoVersions(install_path): + if version == versionFromXPRFile: + vvglWrapperPath = install_path / versionFromXPRFile / sub_path_vvgl + vivadoBatchfilePath = install_path / versionFromXPRFile / sub_path_bat + cmd = [str(vvglWrapperPath), str(vivadoBatchfilePath), str(projectFilePath)] + subprocess.Popen(cmd, cwd=projectFilePath.parent)#, creationflags=subprocess.DETACHED_PROCESS) print("") - print(f"Open Project with Vivado Version {file_version}.") + print(f"Open Project with Vivado Version {versionFromXPRFile}.") time.sleep(2) sys.exit(0) else: - vivadoPath = install_path / file_version + vivadoPath = install_path / versionFromXPRFile print(dedent(f"""\ - ERROR: Vivado version {file_version} not available at path '{vivadoPath}'. Please start manually! + ERROR: Vivado version {versionFromXPRFile} not available at path '{vivadoPath}'. Please start manually! Press any key to exit. """)) @@ -122,22 +167,8 @@ def main() -> NoReturn: input() sys.exit(1) - else: - PrintHelp(script_path) - - vivado_versions = get_vivado_versions(install_path) - print(f"Current path '{install_path}' has following files/folders in it:") - for version in vivado_versions: - print(version) - - print("") - print("Press any key to exit.") - - # wait on user interaction - input() - sys.exit(0) # Entry point if __name__ == "__main__": - main() + main() diff --git a/tests/unit/Vivado.py b/tests/unit/Vivado.py index fc63086..2298264 100644 --- a/tests/unit/Vivado.py +++ b/tests/unit/Vivado.py @@ -32,12 +32,13 @@ from pathlib import Path from unittest import TestCase -from pyEDAA.Launcher import get_version +from pyEDAA.Launcher import Program class ReadXPRFile(TestCase): def test_ExtractVersionFromXPRFile(self): xprFilePath = Path("StopWatch.xpr") - version = get_version(xprFilePath) + program = Program(xprFilePath) + version = program.GetVersion() self.assertEqual("2021.2", version)