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:
-
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.
-
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.
-
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.
Problem
Redirecting where lvt loads plugins from is only possible through the
LVT_PLUGIN_DIRenvironment variable. The TAP DLL directory has both a programmatic setter and an environment variable, so the two are inconsistent:set_tap_directory()LVT_TAP_DIR<module dir>LVT_PLUGIN_DIR<module dir>/plugins,%USERPROFILE%\.lvt\pluginsThis matters now that
lvt_coreis consumable as a library:Environment variables are process-global. An embedding application has to call
SetEnvironmentVariableWto 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.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. Forlvt.exethat is the intended feature. For a third-party application embeddinglvt_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.Test isolation. Any test wanting a controlled plugin directory has to mutate the environment.
Plugin loading is opt-in —
load_plugins()is only called frommain.cpp, andtree_builder.cppjust 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:
When set,
load_plugins()searches only these directories, in order, keeping the existing de-duplication by filename. This mirrorsset_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()andLVT_PLUGIN_DIRdiffer 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_coreconsumable as a static library.