From 250d9c28b88dcde4de81e73f509ee5f46ea9258d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Tue, 7 Nov 2023 11:02:41 +0100 Subject: [PATCH] sim_if/modelsim: use vmap to find modelsim.ini * This creates a single source of truth for finding modelsim.ini. It is now possible to use MODELSIM=/path/to/custom/modelsim.ini and both ModelSim and vunit will use that. (The VUNIT_MODELSIM_INI environment variable can still be used to override modelsim.ini only for vunit.) * This fixes detecting modelsim.ini in the quartus-prime-lite package from https://github.com/nixos/nixpkgs, which exposes binaries at $prefix/bin, but not $prefix/modelsim.ini, because the latter is an FHS violation. --- vunit/sim_if/modelsim.py | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/vunit/sim_if/modelsim.py b/vunit/sim_if/modelsim.py index c2fd3d309..d9ddbd753 100644 --- a/vunit/sim_if/modelsim.py +++ b/vunit/sim_if/modelsim.py @@ -10,6 +10,7 @@ from pathlib import Path import os +import re import logging from configparser import RawConfigParser from ..exceptions import CompileError @@ -65,11 +66,7 @@ def find_prefix_from_path(cls): """ Find first valid modelsim toolchain prefix """ - - def has_modelsim_ini(path): - return os.path.isfile(str(Path(path).parent / "modelsim.ini")) - - return cls.find_toolchain(["vsim"], constraints=[has_modelsim_ini]) + return cls.find_toolchain(["vsim"]) @classmethod def supports_vhdl_package_generics(cls): @@ -98,6 +95,32 @@ def __init__(self, prefix, output_path, persistent=False, gui=False): assert not (persistent and gui) self._create_modelsim_ini() + def _get_modelsim_ini_path_from_vmap(self): + """ + Get the path to modelsim.ini, as used by vmap. + + This means it listens to the MODELSIM environment variable, allowing + both vunit and other code/scripts to use the same modelsim.ini. + """ + vmap_output = [] + proc = Process([str(Path(self._prefix) / "vmap")]) + try: + proc.consume_output(callback=lambda line: vmap_output.append(line)) + except Process.NonZeroExitCode: + # The responsibility of this code is only detecting where + # modelsim.ini is, not to check that all libraries defined in it + # exist. So suppress non-zero exit codes. + pass + for line in vmap_output: + m = re.match("Reading (.*modelsim\.ini)", line) + if m is None: + continue + modelsim_ini = Path(m.group(1)).resolve() + if not modelsim_ini.exists(): + raise FileNotFoundError(modelsim_ini) + return modelsim_ini + raise Exception("Failed to get the path to modelsim.ini from vmap") + def _create_modelsim_ini(self): """ Create the modelsim.ini file @@ -106,7 +129,11 @@ def _create_modelsim_ini(self): if not file_exists(parent): os.makedirs(parent) - original_modelsim_ini = os.environ.get("VUNIT_MODELSIM_INI", str(Path(self._prefix).parent / "modelsim.ini")) + # Try vunit specific environment variable first, then query vmap, which + # reads the MODELSIM environment variable if it exists, then checks the + # current working directory and eventually falls back to modelsim.ini + # bundled with ModelSim. + original_modelsim_ini = os.environ.get("VUNIT_MODELSIM_INI", self._get_modelsim_ini_path_from_vmap()) with Path(original_modelsim_ini).open("rb") as fread: with Path(self._sim_cfg_file_name).open("wb") as fwrite: fwrite.write(fread.read())