Skip to content

Commit da7ead0

Browse files
committed
Try the MSVC debug postfix before the plain name when resolving a bundle library
1 parent de0cf8d commit da7ead0

5 files changed

Lines changed: 94 additions & 15 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ A plugin with compatibility version `C` and current version `V` satisfies a requ
307307
`PluginSystem` supports three filesystem layouts:
308308

309309
- `Flat` is the default. It puts plugin libraries directly below each search path and reads their embedded metadata.
310-
- `Bundle` gives every plugin a child directory containing its library and a sidecar `plugin.json`. The metadata must contain a root `name` without a platform library prefix or suffix. For example, `"name": "editor"` can resolve to `editor.dll`, `libeditor.dll`, `libeditor.so`, or `libeditor.dylib`.
310+
- `Bundle` gives every plugin a child directory containing its library and a sidecar `plugin.json`. The metadata must contain a root `name` without a platform library prefix or suffix. For example, `"name": "editor"` can resolve to `editor.dll`, `libeditor.dll`, `libeditor.so`, or `libeditor.dylib`. A debug build of MSVC tries the debug postfix `d` first and resolves `editord.dll` before `editor.dll`, because a debug build and a release build of MSVC link different C runtimes and are not binary compatible. A release build does not try the postfix.
311311
- `CustomLayout` is reported when the constructor receives a user-provided `PluginFactory`. Its `scanPluginPaths()` and `resolvePluginPath()` overrides can implement recursive packages, another metadata path, or a separate library subdirectory. `PluginSystem` accepts only filesystem plugins returned by this factory.
312312

313313
In Bundle layout, `name` belongs to `BundlePluginFactory`, while `displayName` belongs to `PluginSystem`. They happen to coexist in the same root metadata object and do not describe the same thing.

‎include/stdcorelib/plugin/pluginfactory.h‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ namespace stdc::plugin {
139139
///
140140
/// Each bundle has external metadata and a plugin library. The metadata's root \c name
141141
/// field gives the library's platform-independent name. For example, \c editor can resolve to
142-
/// \c editor.dll, \c libeditor.so, or \c libeditor.dylib.
142+
/// \c editor.dll, \c libeditor.so, or \c libeditor.dylib. A debug build of MSVC tries the
143+
/// name with the debug postfix \c d first, such as \c editord.dll, and then the name alone.
143144
class STDC_PLUGIN_EXPORT BundlePluginFactory : public PluginFactory {
144145
public:
145146
/// Creates a bundle factory with a configurable metadata path.
@@ -166,7 +167,8 @@ namespace stdc::plugin {
166167
/// Resolves the plugin library described by bundle metadata.
167168
///
168169
/// The default implementation reads the root \c name field, searches the bundle root, and
169-
/// accepts the platform's library prefix and suffix around that name.
170+
/// accepts the platform's library prefix and suffix around that name. A debug build of
171+
/// MSVC tries the name with the debug postfix \c d before the name alone.
170172
///
171173
/// \param bundlePath The bundle directory returned by scanPluginPaths().
172174
/// \param metadata The complete root object read from the bundle metadata file.

‎src/plugin/pluginfactory.cpp‎

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -450,16 +450,27 @@ namespace stdc::plugin {
450450
".so",
451451
#endif
452452
};
453+
// Debug and release builds of MSVC use incompatible C runtimes and STL layouts. A debug
454+
// build tries the postfix d first and then the plain name, because some plugins omit the
455+
// postfix. A release build does not try the postfix.
456+
#if defined(_MSC_VER) && defined(_DEBUG)
457+
constexpr std::array<std::string_view, 2> postfixes{"d", ""};
458+
#else
459+
constexpr std::array<std::string_view, 1> postfixes{""};
460+
#endif
453461

454-
for (const auto prefix : prefixes) {
455-
for (const auto suffix : suffixes) {
456-
fs::path candidateName = std::string(prefix);
457-
candidateName += requestedPath.native();
458-
candidateName += std::string(suffix);
459-
const auto candidate = bundlePath / candidateName;
460-
std::error_code ec;
461-
if (fs::is_regular_file(candidate, ec) && SharedLibrary::isLibrary(candidate)) {
462-
return candidate;
462+
for (const auto postfix : postfixes) {
463+
for (const auto prefix : prefixes) {
464+
for (const auto suffix : suffixes) {
465+
fs::path candidateName = std::string(prefix);
466+
candidateName += requestedPath.native();
467+
candidateName += std::string(postfix);
468+
candidateName += std::string(suffix);
469+
const auto candidate = bundlePath / candidateName;
470+
std::error_code ec;
471+
if (fs::is_regular_file(candidate, ec) && SharedLibrary::isLibrary(candidate)) {
472+
return candidate;
473+
}
463474
}
464475
}
465476
}

‎tests/auto/test_pluginfactory.cpp‎

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111

1212
namespace {
1313

14+
// Returns the bundle name of a plugin library, which excludes the debug postfix of MSVC that
15+
// BundlePluginFactory appends.
16+
std::string bundleName(const std::filesystem::path &library) {
17+
auto name = library.stem().string();
18+
#if defined(_MSC_VER) && defined(_DEBUG)
19+
name.pop_back();
20+
#endif
21+
return name;
22+
}
23+
1424
class RuntimePlugin : public stdc::plugin::Plugin {};
1525

1626
class DefaultPluginFactoryProbe : public stdc::plugin::PluginFactory {
@@ -100,13 +110,19 @@ namespace {
100110
}
101111

102112
std::filesystem::path addBundle() const {
113+
const auto source = std::filesystem::path(TEST_PLUGINLOADER_PLUGIN_PATH);
114+
return addBundle(source.filename());
115+
}
116+
117+
// Adds a bundle whose library is the test plugin copied to fileName.
118+
std::filesystem::path addBundle(const std::filesystem::path &fileName) const {
103119
const auto source = std::filesystem::path(TEST_PLUGINLOADER_PLUGIN_PATH);
104120
const auto bundle = _path / "bundle";
105121
std::filesystem::create_directory(bundle);
106-
std::filesystem::copy_file(source, bundle / source.filename());
122+
std::filesystem::copy_file(source, bundle / fileName);
107123

108124
std::ofstream metadata(bundle / "plugin.json");
109-
metadata << R"({"name":")" << source.stem().string() << R"(","answer":42})";
125+
metadata << R"({"name":")" << bundleName(source) << R"(","answer":42})";
110126
return bundle;
111127
}
112128

@@ -284,6 +300,45 @@ BOOST_AUTO_TEST_CASE(test_bundle_scan_and_resolve) {
284300
BOOST_CHECK_EQUAL(plugins.front()->metadata()["answer"].toInt(), 42);
285301
}
286302

303+
#ifdef _MSC_VER
304+
BOOST_AUTO_TEST_CASE(test_bundle_debug_postfix) {
305+
const auto source = std::filesystem::path(TEST_PLUGINLOADER_PLUGIN_PATH);
306+
const auto name = bundleName(source);
307+
const auto extension = source.extension().string();
308+
309+
# ifdef _DEBUG
310+
// The name alone resolves if the library has no postfix.
311+
{
312+
TemporaryPluginDirectory directory;
313+
directory.addBundle(name + extension);
314+
315+
stdc::plugin::BundlePluginFactory factory;
316+
factory.addPluginPath("org.stdcorelib.LoaderTest", directory.path());
317+
BOOST_CHECK_EQUAL(factory.plugins("org.stdcorelib.LoaderTest").size(), 1u);
318+
}
319+
320+
// The postfix precedes the name alone, which is a library without plugin metadata here.
321+
{
322+
TemporaryPluginDirectory directory;
323+
const auto bundle = directory.addBundle(name + "d" + extension);
324+
std::filesystem::copy_file(TEST_PLUGINLOADER_LIBRARY_PATH, bundle / (name + extension));
325+
326+
stdc::plugin::BundlePluginFactory factory;
327+
factory.addPluginPath("org.stdcorelib.LoaderTest", directory.path());
328+
BOOST_CHECK_EQUAL(factory.plugins("org.stdcorelib.LoaderTest").size(), 1u);
329+
}
330+
# else
331+
// A release build does not resolve a debug library.
332+
TemporaryPluginDirectory directory;
333+
directory.addBundle(name + "d" + extension);
334+
335+
stdc::plugin::BundlePluginFactory factory;
336+
factory.addPluginPath("org.stdcorelib.LoaderTest", directory.path());
337+
BOOST_CHECK(factory.plugins("org.stdcorelib.LoaderTest").empty());
338+
# endif
339+
}
340+
#endif
341+
287342
BOOST_AUTO_TEST_CASE(test_ignores_runtime_plugin_without_iid) {
288343
RuntimePlugin plugin;
289344
stdc::plugin::PluginFactory factory;

‎tests/auto/test_pluginsystem.cpp‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,23 @@
1010
#include <chrono>
1111
#include <fstream>
1212
#include <functional>
13+
#include <string>
1314
#include <thread>
1415

1516
#include <boost/test/unit_test.hpp>
1617

1718
namespace {
1819

20+
// Returns the bundle name of a plugin library, which excludes the debug postfix of MSVC that
21+
// BundlePluginFactory appends.
22+
std::string bundleName(const std::filesystem::path &library) {
23+
auto name = library.stem().string();
24+
#if defined(_MSC_VER) && defined(_DEBUG)
25+
name.pop_back();
26+
#endif
27+
return name;
28+
}
29+
1930
class TemporaryPluginSystemDirectory {
2031
public:
2132
explicit TemporaryPluginSystemDirectory(
@@ -54,7 +65,7 @@ namespace {
5465
std::filesystem::copy_file(source, pluginPath);
5566

5667
std::ofstream metadataFile(pluginDirectory / "plugin.json");
57-
metadataFile << R"({"name":")" << source.stem().string() << '"';
68+
metadataFile << R"({"name":")" << bundleName(source) << '"';
5869
if (metadata.size() > 2) {
5970
metadataFile << ',' << metadata.substr(1, metadata.size() - 2);
6071
}

0 commit comments

Comments
 (0)