Skip to content

Add set_plugin_directories() so embedders can control plugin loading without mutating the environment #41

Description

@asklar

Problem

Redirecting where lvt loads plugins from is only possible through the LVT_PLUGIN_DIR environment variable. The TAP DLL directory has both a programmatic setter and an environment variable, so the two are inconsistent:

Concern Programmatic Env var Implicit
TAP DLLs set_tap_directory() LVT_TAP_DIR <module dir>
Plugins (none) LVT_PLUGIN_DIR <module dir>/plugins, %USERPROFILE%\.lvt\plugins

This matters now that lvt_core is consumable as a library:

  1. Environment variables are process-global. An embedding application has to call SetEnvironmentVariableW to redirect plugin loading, mutating state visible to every other library and thread in the process. There is no way to scope it to a single call, and it is not thread-safe.

  2. There is no way to opt out of the user-profile directory. Once a consumer calls load_plugins(), it also loads whatever DLLs are in %USERPROFILE%\.lvt\plugins. For lvt.exe that is the intended feature. For a third-party application embedding lvt_core, it means loading arbitrary user-supplied native DLLs into their process, which a host with its own plugin policy would reasonably want to refuse.

  3. Test isolation. Any test wanting a controlled plugin directory has to mutate the environment.

Plugin loading is opt-in — load_plugins() is only called from main.cpp, and tree_builder.cpp just reads the already-loaded list — so this is not urgent, but it is a real gap in the embedding story.

Proposal

Add an explicit list with replace semantics:

// plugin_loader.h
// Directories to search for plugins, replacing the default search entirely.
// Pass an empty vector to restore the default order.
void set_plugin_directories(const std::vector<std::wstring>& dirs);

When set, load_plugins() searches only these directories, in order, keeping the existing de-duplication by filename. This mirrors set_tap_directory(), which short-circuits its whole chain, and it is the only option that gives an embedder full control over what gets loaded into their process.

Note this means set_plugin_directories() and LVT_PLUGIN_DIR differ in meaning — the environment variable prepends, the setter replaces. That is a deliberate trade-off: the setter exists precisely to give exclusive control, which prepending would not provide. Worth documenting clearly on the declaration.

Additive, no behaviour change for existing callers. Roughly 15 lines plus a test.

Context

Came out of the review in #40, which made lvt_core consumable as a static library.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions