carb/FindPlugins.h
File members: carb/FindPlugins.h
// Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#pragma once
#include "Framework.h"
#include "extras/Library.h"
#include "filesystem/IFileSystem.h"
#include "filesystem/FindFiles.h"
#include "logging/Log.h"
#include "../omni/str/Wildcard.h"
#include <cstring>
namespace carb
{
using FindPluginsOnMatchedFn = void(const char* canonical, bool reloadable, void* context);
struct FindPluginsArgs
{
const char* const* searchPaths;
size_t searchPathCount;
bool searchRecursive;
const char* const* loadedFileWildcards;
size_t loadedFileWildcardCount;
const char* const* reloadableFileWildcards;
size_t reloadableFileWildcardCount;
const char* const* excludedFileWildcards;
size_t excludedFileWildcardCount;
FindPluginsOnMatchedFn* onMatched;
void* onMatchedContext;
filesystem::FindFilesOnExcludedFn* onExcluded;
void* onExcludedContext;
filesystem::FindFilesOnSkippedFn* onSkipped;
void* onSkippedContext;
filesystem::FindFilesOnSearchPathFn* onSearchPath;
void* onSearchPathContext;
filesystem::IFileSystem* fs;
};
#ifndef DOXYGEN_BUILD
namespace detail
{
inline bool caseInsensitiveEndsWith(const char* str, const char* tail)
{
const size_t strLen = std::strlen(str);
const size_t tailLen = std::strlen(tail);
// String should be at least as long as tail
if (strLen < tailLen)
{
return false;
}
// Compare with tail, character by character
for (size_t i = 0; i < tailLen; ++i)
{
// Tail is assumed to already be lowercase
if (tail[tailLen - i - 1] != std::tolower(str[strLen - i - 1]))
{
return false;
}
}
return true;
}
} // namespace detail
#endif
inline bool findPlugins(const FindPluginsArgs& inArgs) noexcept
{
filesystem::FindFilesArgs args{};
args.searchPaths = inArgs.searchPaths;
args.searchPathsCount = uint32_t(inArgs.searchPathCount);
PluginLoadingDesc defaultPluginDesc = PluginLoadingDesc::getDefault();
if (!args.searchPaths || (0 == args.searchPathsCount))
{
// If search path count it not specified, fall back to the default desc search paths
args.searchPaths = defaultPluginDesc.searchPaths;
args.searchPathsCount = uint32_t(defaultPluginDesc.searchPathCount);
}
args.matchWildcards = inArgs.loadedFileWildcards;
args.matchWildcardsCount = uint32_t(inArgs.loadedFileWildcardCount);
args.excludeWildcards = inArgs.excludedFileWildcards;
args.excludeWildcardsCount = uint32_t(inArgs.excludedFileWildcardCount);
#if CARB_PLATFORM_LINUX || CARB_PLATFORM_MACOS
constexpr const char* const kIgnorePrefixes[] = { "lib" };
constexpr uint32_t kIgnorePrefixesCount = 1;
#elif CARB_PLATFORM_WINDOWS
constexpr const char* const* kIgnorePrefixes = nullptr;
constexpr uint32_t kIgnorePrefixesCount = 0;
#else
CARB_UNSUPPORTED_PLATFORM();
#endif
args.ignorePrefixes = kIgnorePrefixes;
args.ignorePrefixesCount = kIgnorePrefixesCount;
args.fs = inArgs.fs;
// to avoid the expensive filename canonicalization and pattern matching, we do a quick check to make sure the
// extension is for a plugin
args.onFilterNonCanonical = [](const char* path, void*) {
if (detail::caseInsensitiveEndsWith(path, carb::extras::getDefaultLibraryExtension()))
{
return filesystem::WalkAction::eContinue; // could be a plugin (i.e. correct .ext)
}
else
{
return filesystem::WalkAction::eSkip; // not a plug .ext. skip
}
};
args.onMatched = [](const char* canonical, void* context) {
auto inArgs = static_cast<FindPluginsArgs*>(context);
bool reloadable = false;
if (inArgs->reloadableFileWildcards && inArgs->reloadableFileWildcardCount)
{
extras::Path path(canonical);
auto stemBuffer = path.getStem();
const char* stem = stemBuffer.getStringBuffer();
reloadable = omni::str::matchWildcards(
stem, inArgs->reloadableFileWildcards, uint32_t(inArgs->reloadableFileWildcardCount));
#if CARB_PLATFORM_LINUX || CARB_PLATFORM_MACOS
if (!reloadable)
{
if (extras::startsWith(stem, "lib"))
{
stem += 3;
reloadable = omni::str::matchWildcards(
stem, inArgs->reloadableFileWildcards, uint32_t(inArgs->reloadableFileWildcardCount));
}
}
#endif
}
inArgs->onMatched(canonical, reloadable, inArgs->onMatchedContext);
};
args.onMatchedContext = const_cast<FindPluginsArgs*>(&inArgs);
args.onExcluded = inArgs.onExcluded;
args.onExcludedContext = inArgs.onExcludedContext;
if (!args.onExcluded)
{
args.onExcluded = [](const char* canonical, void*) {
CARB_LOG_VERBOSE("Excluding potential plugin file: %s.", canonical);
};
}
args.onSkipped = inArgs.onSkipped;
args.onSkippedContext = inArgs.onSkippedContext;
args.onSearchPath = inArgs.onSearchPath;
args.onSearchPathContext = inArgs.onSearchPathContext;
if (!args.onSearchPath)
{
args.onSearchPath = [](const char* path, void* context) {
auto inArgs = static_cast<FindPluginsArgs*>(context);
CARB_LOG_VERBOSE("Searching plugins %sin folder: %s", (inArgs->searchRecursive ? "recursively " : ""), path);
};
args.onSearchPathContext = const_cast<FindPluginsArgs*>(&inArgs);
}
args.flags = (filesystem::kFindFilesFlagMatchStem | filesystem::kFindFilesFlagReplaceEnvironmentVariables);
if (inArgs.searchRecursive)
{
args.flags |= filesystem::kFindFilesFlagRecursive;
}
return filesystem::findFiles(args);
}
} // namespace carb