Skip to content

Keep the simulator hooks of a VUnit object when another one is created - #1234

Merged
LarsAsplund merged 2 commits into
VUnit:masterfrom
ru551n:simulator-hooks-per-project
Oct 14, 2026
Merged

LarsAsplund merged 2 commits into
VUnit:masterfrom
ru551n:simulator-hooks-per-project

Conversation

@ru551n

@ru551n ru551n commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

Problem

The simulator hooks that VUnit packages register (#1221) were kept in a module-global registry, and every new VUnit object cleared it. A run script that creates a second VUnit object after add_package(...) therefore loses the hooks of the first one. Its tests then run without the flags and environment the package registered, even though the package's setup has run and its files exist.

This was found with vunit-python-bridge. Its GHDL run_env hook puts the folder of its native library on LD_LIBRARY_PATH. A run script that created another VUnit object made every test fail with:

python_bridge_pkg.vhd:28:19:error: cannot load VHPIDIRECT shared library 'libvunit_python_bridge.so'

NVC similarly loses --load=..., and ModelSim/Questa loses its process_flags and run_env. The docs already say the hooks "apply only to the project in which they are registered", so this makes the code do what the docs describe.

Changes

  • vunit/sim_if/hooks.py: a SimulatorHooks class holds the hooks of one VUnit object (register, get_flags, get_run_env), replacing the global _HOOKS, register_hooks and clear_hooks. The module functions get_flags(simulator_interface, kind) and get_run_env(simulator_interface, env) keep their signatures. They use the registry the interface was given, or none for an interface not created by a VUnit object. The simulator interfaces' call sites are unchanged.
  • VUnit object: it creates its own SimulatorHooks instead of clearing the global registry, and attaches it to the simulator interface it creates (simulator_if.simulator_hooks).
  • PackageContext.register_simulator_hooks: now a method with the same keyword arguments, registering into the registry of the context's VUnit object.
  • vsim based interfaces: VsimSimulatorMixin used to read process_flags and run_env in __init__, before the VUnit object can attach its registry. It now reads them when the persistent vsim process is started, through a weak reference so the closure still doesn't keep the interface alive. Without this, persistent ModelSim/Questa and Riviera-PRO processes would have lost their hooks.
  • Docs: vunit_packages.rst now says that creating another VUnit object neither removes a project's hooks nor gives them to the new object.

Tests

  • tests/unit/test_sim_if_hooks.py: the tests use a SimulatorHooks instance per test instead of the global registry. New tests check that:

    • a simulator interface gets the hooks of the VUnit object that created it,
    • a VUnit object's hooks are kept when another VUnit object is created, and the new one has none,
    • an interface not created by a VUnit object has no hooks.

    The persistent-vsim test attaches the hooks after constructing the interface, so it covers the deferred lookup.

  • tests/unit/test_builtins.py: the mocked VUnit object gets a registry.

  • Locally on Windows: pytest tests/unit (593 passed) and pytest tests/lint (pycodestyle, pylint, mypy, license) pass, and black is clean.

  • End to end: vunit-python-bridge's feature tests on AlmaLinux 9.6 with Python 3.11 and GHDL 6.0.0 (mcode), with an extra unused VUnit.from_args(args) after add_package in the run script:

    VUnit Feature tests
    5.0.0.dev13 0 of 108, all with cannot load VHPIDIRECT shared library
    this branch 108 of 108

    Without the extra object, both pass 108 of 108.

🤖 Generated with Claude Code

ru551n and others added 2 commits October 12, 2026 15:06
The simulator hooks registered by packages were kept in a module global
registry, which every new VUnit object cleared. A run script creating a
second VUnit object after adding a package therefore lost the hooks of the
first one: its tests ran without the flags and environment the package
registered, for example without the library search path of a native
library, and failed with errors like GHDL's "cannot load VHPIDIRECT shared
library".

Every VUnit object now has a SimulatorHooks registry of its own, which the
package context registers into and the VUnit object gives to the simulator
interface it creates. The vsim based interfaces ask for the hooks of their
persistent process when it is started, since the interface gets the hooks
after it has been created.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@LarsAsplund
LarsAsplund merged commit 74acf65 into VUnit:master Oct 14, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants