Open search paths for assets, gdts and source in Linker

This commit is contained in:
Jan 2021-03-10 12:26:09 +01:00
parent 8c7926e745
commit c47ea48b6b
6 changed files with 370 additions and 112 deletions

View File

@ -12,6 +12,7 @@ The result should be oriented at the original game's modding tools.
│ ├───source_data
│ ├───zone_raw
│ └───zone_source
├───zone_out
├───zone_raw
└───zone_source
```
@ -39,6 +40,10 @@ Contains all data dumped using Unlinker which includes raw assets in ``zone_raw`
Separating the data of dumped files serves the purpose of separating it from manually created zones and prevents accidentally overwriting self created data.
## zone_out
The folder that contains the output of the linking process.
## zone_raw
Contains raw assets that should only be used for a zone with the same name as its subfolder.

View File

@ -16,15 +16,17 @@
#include "LinkerArgs.h"
#include "Utils/ObjFileStream.h"
#include "Zone/Definition/ZoneDefinitionStream.h"
namespace fs = std::filesystem;
class Linker::Impl
{
LinkerArgs m_args;
SearchPaths m_search_paths;
SearchPathFilesystem* m_last_zone_search_path;
std::set<std::string> m_absolute_search_paths;
std::vector<std::unique_ptr<ISearchPath>> m_loaded_zone_search_paths;
SearchPaths m_asset_search_paths;
SearchPaths m_gdt_search_paths;
SearchPaths m_source_search_paths;
/**
* \brief Loads a search path.
@ -53,34 +55,33 @@ class Linker::Impl
ObjLoading::UnloadIWDsInSearchPath(searchPath);
}
/**
* \brief Loads all search paths that are valid for the specified zone and returns them.
* \param zonePath The path to the zone file that should be prepared for.
* \return A \c SearchPaths object that contains all search paths that should be considered when loading the specified zone.
*/
SearchPaths GetSearchPathsForZone(const std::string& zonePath)
SearchPaths GetAssetSearchPathsForZone(const std::string& zoneName)
{
SearchPaths searchPathsForZone;
const auto absoluteZoneDirectory = fs::absolute(std::filesystem::path(zonePath).remove_filename()).string();
if (m_last_zone_search_path != nullptr && m_last_zone_search_path->GetPath() == absoluteZoneDirectory)
for (const auto& searchPathStr : m_args.GetAssetSearchPathsForZone(zoneName))
{
searchPathsForZone.IncludeSearchPath(m_last_zone_search_path);
}
else if (m_absolute_search_paths.find(absoluteZoneDirectory) == m_absolute_search_paths.end())
{
if (m_last_zone_search_path != nullptr)
auto absolutePath = fs::absolute(searchPathStr);
if (!fs::is_directory(absolutePath))
{
UnloadSearchPath(m_last_zone_search_path);
delete m_last_zone_search_path;
if (m_args.m_verbose)
std::cout << "Adding asset search path (Not found): " << absolutePath.string() << std::endl;
continue;
}
m_last_zone_search_path = new SearchPathFilesystem(absoluteZoneDirectory);
searchPathsForZone.IncludeSearchPath(m_last_zone_search_path);
LoadSearchPath(m_last_zone_search_path);
if (m_args.m_verbose)
std::cout << "Adding asset search path: " << absolutePath.string() << std::endl;
auto searchPath = std::make_unique<SearchPathFilesystem>(searchPathStr);
LoadSearchPath(searchPath.get());
searchPathsForZone.IncludeSearchPath(searchPath.get());
m_loaded_zone_search_paths.emplace_back(std::move(searchPath));
}
searchPathsForZone.IncludeSearchPath(&m_asset_search_paths);
for (auto* iwd : IWD::Repository)
{
searchPathsForZone.IncludeSearchPath(iwd);
@ -88,62 +89,159 @@ class Linker::Impl
return searchPathsForZone;
}
SearchPaths GetGdtSearchPathsForZone(const std::string& zoneName)
{
SearchPaths searchPathsForZone;
for (const auto& searchPathStr : m_args.GetGdtSearchPathsForZone(zoneName))
{
auto absolutePath = fs::absolute(searchPathStr);
if (!fs::is_directory(absolutePath))
{
if (m_args.m_verbose)
std::cout << "Adding gdt search path (Not found): " << absolutePath.string() << std::endl;
continue;
}
if (m_args.m_verbose)
std::cout << "Adding gdt search path: " << absolutePath.string() << std::endl;
searchPathsForZone.CommitSearchPath(std::make_unique<SearchPathFilesystem>(searchPathStr));
}
searchPathsForZone.IncludeSearchPath(&m_gdt_search_paths);
return searchPathsForZone;
}
SearchPaths GetSourceSearchPathsForZone(const std::string& zoneName)
{
SearchPaths searchPathsForZone;
for (const auto& searchPathStr : m_args.GetSourceSearchPathsForZone(zoneName))
{
auto absolutePath = fs::absolute(searchPathStr);
if (!fs::is_directory(absolutePath))
{
if (m_args.m_verbose)
std::cout << "Adding source search path (Not found): " << absolutePath.string() << std::endl;
continue;
}
if (m_args.m_verbose)
std::cout << "Adding source search path: " << absolutePath.string() << std::endl;
searchPathsForZone.CommitSearchPath(std::make_unique<SearchPathFilesystem>(searchPathStr));
}
searchPathsForZone.IncludeSearchPath(&m_source_search_paths);
return searchPathsForZone;
}
/**
* \brief Initializes the Linker object's search paths based on the user's input.
* \return \c true if building the search paths was successful, otherwise \c false.
*/
bool BuildSearchPaths()
bool BuildZoneIndependentSearchPaths()
{
for (const auto& path : m_args.m_user_search_paths)
for (const auto& path : m_args.GetZoneIndependentAssetSearchPaths())
{
auto absolutePath = fs::absolute(path);
if (!fs::is_directory(absolutePath))
{
printf("Could not find directory of search path: \"%s\"\n", path.c_str());
return false;
if(m_args.m_verbose)
std::cout << "Adding asset search path (Not found): " << absolutePath.string() << std::endl;
continue;
}
if(m_args.m_verbose)
std::cout << "Adding asset search path: " << absolutePath.string() << std::endl;
auto searchPath = std::make_unique<SearchPathFilesystem>(absolutePath.string());
LoadSearchPath(searchPath.get());
m_search_paths.CommitSearchPath(std::move(searchPath));
m_absolute_search_paths.insert(absolutePath.string());
m_asset_search_paths.CommitSearchPath(std::move(searchPath));
}
if (m_args.m_verbose)
for (const auto& path : m_args.GetZoneIndependentGdtSearchPaths())
{
printf("%u SearchPaths%s\n", m_absolute_search_paths.size(), !m_absolute_search_paths.empty() ? ":" : "");
for (const auto& absoluteSearchPath : m_absolute_search_paths)
auto absolutePath = fs::absolute(path);
if (!fs::is_directory(absolutePath))
{
printf(" \"%s\"\n", absoluteSearchPath.c_str());
if (m_args.m_verbose)
std::cout << "Loading gdt search path (Not found): " << absolutePath.string() << std::endl;
continue;
}
if (!m_absolute_search_paths.empty())
if (m_args.m_verbose)
std::cout << "Adding gdt search path: " << absolutePath.string() << std::endl;
m_gdt_search_paths.CommitSearchPath(std::make_unique<SearchPathFilesystem>(absolutePath.string()));
}
for (const auto& path : m_args.GetZoneIndependentSourceSearchPaths())
{
auto absolutePath = fs::absolute(path);
if (!fs::is_directory(absolutePath))
{
puts("");
if (m_args.m_verbose)
std::cout << "Loading source search path (Not found): " << absolutePath.string() << std::endl;
continue;
}
if (m_args.m_verbose)
std::cout << "Adding source search path: " << absolutePath.string() << std::endl;
m_source_search_paths.CommitSearchPath(std::make_unique<SearchPathFilesystem>(absolutePath.string()));
}
return true;
}
/**
* \brief Performs the tasks specified by the command line arguments on the specified zone.
* \param zone The zone to handle.
* \return \c true if handling the zone was successful, otherwise \c false.
*/
bool BuildZone(const std::string& zoneName) const
bool BuildZone(const std::string& zoneName)
{
auto assetSearchPaths = GetAssetSearchPathsForZone(zoneName);
auto gdtSearchPaths = GetGdtSearchPathsForZone(zoneName);
auto sourceSearchPaths = GetSourceSearchPathsForZone(zoneName);
std::unique_ptr<ZoneDefinition> zoneDefinition;
{
const auto definitionFileName = zoneName + ".zone";
const auto definitionStream = sourceSearchPaths.Open(definitionFileName);
if (!definitionStream)
{
std::cout << "Could not find zone definition file for zone \"" << zoneName << "\"." << std::endl;
return false;
}
ZoneDefinitionInputStream zoneDefinitionInputStream(*definitionStream, definitionFileName, m_args.m_verbose);
zoneDefinition = zoneDefinitionInputStream.ReadDefinition();
}
if(!zoneDefinition)
{
std::cout << "Failed to read zone definition file for zone \"" << zoneName << "\"." << std::endl;
return false;
}
for(const auto& loadedSearchPath : m_loaded_zone_search_paths)
{
UnloadSearchPath(loadedSearchPath.get());
}
m_loaded_zone_search_paths.clear();
return true;
}
public:
Impl()
{
m_last_zone_search_path = nullptr;
}
= default;
/**
* \copydoc Linker::Start
@ -153,7 +251,7 @@ public:
if (!m_args.ParseArgs(argc, argv))
return false;
if (!BuildSearchPaths())
if (!BuildZoneIndependentSearchPaths())
return false;
std::vector<std::unique_ptr<Zone>> zones;
@ -168,9 +266,6 @@ public:
auto absoluteZoneDirectory = absolute(std::filesystem::path(zonePath).remove_filename()).string();
auto searchPathsForZone = GetSearchPathsForZone(absoluteZoneDirectory);
searchPathsForZone.IncludeSearchPath(&m_search_paths);
auto zone = std::unique_ptr<Zone>(ZoneLoading::LoadZone(zonePath));
if (zone == nullptr)
{
@ -182,13 +277,10 @@ public:
{
printf("Loaded zone \"%s\"\n", zone->m_name.c_str());
}
ObjLoading::LoadReferencedContainersForZone(&searchPathsForZone, zone.get());
ObjLoading::LoadObjDataForZone(&searchPathsForZone, zone.get());
}
auto result = true;
for(const auto& zone : m_args.m_zones_to_build)
for (const auto& zone : m_args.m_zones_to_build)
{
if (!BuildZone(zone))
{
@ -197,10 +289,6 @@ public:
}
}
for(const auto& zone : zones)
{
ObjLoading::UnloadContainersOfZone(zone.get());
}
zones.clear();
return result;

View File

@ -1,5 +1,6 @@
#include "LinkerArgs.h"
#include <filesystem>
#include <regex>
#include <type_traits>
@ -8,6 +9,8 @@
#include "ObjWriting.h"
#include "Utils/FileUtils.h"
namespace fs = std::filesystem;
const CommandLineOption* const OPTION_HELP =
CommandLineOption::Builder::Create()
.WithShortName("?")
@ -22,19 +25,40 @@ const CommandLineOption* const OPTION_VERBOSE =
.WithDescription("Outputs a lot more and more detailed messages.")
.Build();
const CommandLineOption* const OPTION_BASE_FOLDER =
CommandLineOption::Builder::Create()
.WithShortName("b")
.WithLongName("base-folder")
.WithDescription("Specifies the base folder that can be used to specify other folders. Defaults to the current directory.")
.WithParameter("baseFolderPath")
.Build();
const CommandLineOption* const OPTION_OUTPUT_FOLDER =
CommandLineOption::Builder::Create()
.WithShortName("o")
.WithLongName("output-folder")
.WithDescription("Specifies the output folder containing the contents of the unlinked zones. Defaults to ./%zoneName%")
.WithDescription("Specifies the output folder containing the contents of the unlinked zones. Defaults to \"" + std::string(LinkerArgs::DEFAULT_OUTPUT_FOLDER) + "\".")
.WithParameter("outputFolderPath")
.Build();
const CommandLineOption* const OPTION_SEARCH_PATH =
const CommandLineOption* const OPTION_ASSET_SEARCH_PATH =
CommandLineOption::Builder::Create()
.WithLongName("search-path")
.WithDescription("Specifies a semi-colon separated list of paths to search for additional game files.")
.WithParameter("searchPathString")
.WithLongName("asset-search-path")
.WithDescription("Specifies the search paths used for assets. Defaults to \"" + std::string(LinkerArgs::DEFAULT_ASSET_SEARCH_PATH) + "\".")
.WithParameter("assetSearchPathString")
.Build();
const CommandLineOption* const OPTION_GDT_SEARCH_PATH =
CommandLineOption::Builder::Create()
.WithLongName("gdt-search-path")
.WithDescription("Specifies the search paths used for assets. Defaults to \"" + std::string(LinkerArgs::DEFAULT_GDT_SEARCH_PATH) + "\".")
.WithParameter("gdtSearchPathString")
.Build();
const CommandLineOption* const OPTION_SOURCE_SEARCH_PATH =
CommandLineOption::Builder::Create()
.WithLongName("source-search-path")
.WithDescription("Specifies the search paths used for assets. Defaults to \"" + std::string(LinkerArgs::DEFAULT_SOURCE_SEARCH_PATH) + "\".")
.WithParameter("sourceSearchPathString")
.Build();
const CommandLineOption* const OPTION_LOAD =
@ -50,14 +74,20 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]
{
OPTION_HELP,
OPTION_VERBOSE,
OPTION_BASE_FOLDER,
OPTION_OUTPUT_FOLDER,
OPTION_SEARCH_PATH,
OPTION_ASSET_SEARCH_PATH,
OPTION_GDT_SEARCH_PATH,
OPTION_SOURCE_SEARCH_PATH,
OPTION_LOAD
};
LinkerArgs::LinkerArgs()
: m_argument_parser(COMMAND_LINE_OPTIONS, std::extent<decltype(COMMAND_LINE_OPTIONS)>::value),
m_output_folder("zone_out/%zoneName%"),
m_base_pattern(R"(\?base\?)"),
m_zone_pattern(R"(\?zone\?)"),
m_base_folder_depends_on_zone(false),
m_out_folder_depends_on_zone(false),
m_verbose(false)
{
}
@ -84,6 +114,69 @@ void LinkerArgs::SetVerbose(const bool isVerbose)
ObjWriting::Configuration.Verbose = isVerbose;
}
std::string LinkerArgs::GetBasePathForZone(const std::string& zoneName) const
{
return std::regex_replace(m_base_folder, m_zone_pattern, zoneName);
}
void LinkerArgs::SetDefaultBasePath()
{
const auto currentDir = fs::absolute(fs::current_path());
if (currentDir.filename() == "bin")
{
m_base_folder = currentDir.parent_path().string();
}
else
{
m_base_folder = currentDir.string();
}
}
std::set<std::string> LinkerArgs::GetZoneIndependentSearchPaths(const std::set<std::string>& set) const
{
std::set<std::string> out;
for(const auto& path : set)
{
if(path.find(PATTERN_ZONE) != std::string::npos)
continue;
if(path.find(PATTERN_BASE) != std::string::npos)
{
if(m_base_folder_depends_on_zone)
continue;
out.emplace(std::regex_replace(path, m_base_pattern, m_base_folder));
}
else
{
out.emplace(path);
}
}
return out;
}
std::set<std::string> LinkerArgs::GetSearchPathsForZone(const std::set<std::string>& set, const std::string& zoneName) const
{
std::set<std::string> out;
const auto basePath = GetBasePathForZone(zoneName);
for (const auto& path : set)
{
if (path.find(PATTERN_ZONE) == std::string::npos
&& (!m_base_folder_depends_on_zone || path.find(PATTERN_BASE) == std::string::npos))
{
continue;
}
out.emplace(std::regex_replace(std::regex_replace(path, m_zone_pattern, zoneName), m_base_pattern, basePath));
}
return out;
}
bool LinkerArgs::ParseArgs(const int argc, const char** argv)
{
if (!m_argument_parser.ParseArguments(argc - 1, &argv[1]))
@ -110,18 +203,57 @@ bool LinkerArgs::ParseArgs(const int argc, const char** argv)
// -v; --verbose
SetVerbose(m_argument_parser.IsOptionSpecified(OPTION_VERBOSE));
// -o; --output-folder
if (m_argument_parser.IsOptionSpecified(OPTION_OUTPUT_FOLDER))
m_output_folder = m_argument_parser.GetValueForOption(OPTION_OUTPUT_FOLDER);
// b; --base-folder
if (m_argument_parser.IsOptionSpecified(OPTION_BASE_FOLDER))
m_base_folder = m_argument_parser.GetValueForOption(OPTION_BASE_FOLDER);
else
SetDefaultBasePath();
m_base_folder_depends_on_zone = m_base_folder.find(PATTERN_ZONE) != std::string::npos;
// --search-path
if (m_argument_parser.IsOptionSpecified(OPTION_SEARCH_PATH))
// --output-folder
if (m_argument_parser.IsOptionSpecified(OPTION_OUTPUT_FOLDER))
m_out_folder = m_argument_parser.GetValueForOption(OPTION_OUTPUT_FOLDER);
else
m_out_folder = DEFAULT_OUTPUT_FOLDER;
m_out_folder_depends_on_zone = m_out_folder.find(PATTERN_ZONE) != std::string::npos;
// --asset-search-path
if (m_argument_parser.IsOptionSpecified(OPTION_ASSET_SEARCH_PATH))
{
if (!FileUtils::ParsePathsString(m_argument_parser.GetValueForOption(OPTION_SEARCH_PATH), m_user_search_paths))
if (!FileUtils::ParsePathsString(m_argument_parser.GetValueForOption(OPTION_ASSET_SEARCH_PATH), m_asset_search_paths))
return false;
}
else
{
if (!FileUtils::ParsePathsString(DEFAULT_ASSET_SEARCH_PATH, m_asset_search_paths))
return false;
}
if(m_argument_parser.IsOptionSpecified(OPTION_LOAD))
// --gdt-search-path
if (m_argument_parser.IsOptionSpecified(OPTION_GDT_SEARCH_PATH))
{
if (!FileUtils::ParsePathsString(m_argument_parser.GetValueForOption(OPTION_GDT_SEARCH_PATH), m_gdt_search_paths))
return false;
}
else
{
if (!FileUtils::ParsePathsString(DEFAULT_GDT_SEARCH_PATH, m_gdt_search_paths))
return false;
}
// --source-search-path
if (m_argument_parser.IsOptionSpecified(OPTION_SOURCE_SEARCH_PATH))
{
if (!FileUtils::ParsePathsString(m_argument_parser.GetValueForOption(OPTION_SOURCE_SEARCH_PATH), m_source_search_paths))
return false;
}
else
{
if (!FileUtils::ParsePathsString(DEFAULT_SOURCE_SEARCH_PATH, m_source_search_paths))
return false;
}
if (m_argument_parser.IsOptionSpecified(OPTION_LOAD))
m_zones_to_load = m_argument_parser.GetParametersForOption(OPTION_LOAD);
return true;
@ -129,5 +261,35 @@ bool LinkerArgs::ParseArgs(const int argc, const char** argv)
std::string LinkerArgs::GetOutputFolderPathForZone(const std::string& zoneName) const
{
return std::regex_replace(m_output_folder, std::regex("%zoneName%"), zoneName);
return std::regex_replace(std::regex_replace(m_out_folder, m_zone_pattern, zoneName), m_base_pattern, GetBasePathForZone(zoneName));
}
std::set<std::string> LinkerArgs::GetZoneIndependentAssetSearchPaths() const
{
return GetZoneIndependentSearchPaths(m_asset_search_paths);
}
std::set<std::string> LinkerArgs::GetZoneIndependentGdtSearchPaths() const
{
return GetZoneIndependentSearchPaths(m_gdt_search_paths);
}
std::set<std::string> LinkerArgs::GetZoneIndependentSourceSearchPaths() const
{
return GetZoneIndependentSearchPaths(m_source_search_paths);
}
std::set<std::string> LinkerArgs::GetAssetSearchPathsForZone(const std::string& zoneName) const
{
return GetSearchPathsForZone(m_asset_search_paths, zoneName);
}
std::set<std::string> LinkerArgs::GetGdtSearchPathsForZone(const std::string& zoneName) const
{
return GetSearchPathsForZone(m_gdt_search_paths, zoneName);
}
std::set<std::string> LinkerArgs::GetSourceSearchPathsForZone(const std::string& zoneName) const
{
return GetSearchPathsForZone(m_source_search_paths, zoneName);
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <vector>
#include <set>
#include <regex>
#include "Utils/ClassUtils.h"
#include "Utils/Arguments/ArgumentParser.h"
@ -8,7 +9,21 @@
class LinkerArgs
{
public:
static constexpr const char* PATTERN_BASE = "?base?";
static constexpr const char* PATTERN_ZONE = "?zone?";
static constexpr const char* DEFAULT_BASE_FOLDER = ".";
static constexpr const char* DEFAULT_BASE_FOLDER_MOD_TOOLS = "..";
static constexpr const char* DEFAULT_OUTPUT_FOLDER = "?base?/zone_out/?zone?";
static constexpr const char* DEFAULT_ASSET_SEARCH_PATH = "?base?/raw;?base?/zone_raw/?zone?";
static constexpr const char* DEFAULT_GDT_SEARCH_PATH = "?base?/source_data";
static constexpr const char* DEFAULT_SOURCE_SEARCH_PATH = "?base?/zone_source;?base?/zone_raw/?zone?/zone_source";
private:
ArgumentParser m_argument_parser;
std::regex m_base_pattern;
std::regex m_zone_pattern;
/**
* \brief Prints a command line usage help text for the Linker tool to stdout.
@ -17,12 +32,23 @@ class LinkerArgs
void SetVerbose(bool isVerbose);
_NODISCARD std::string GetBasePathForZone(const std::string& zoneName) const;
void SetDefaultBasePath();
_NODISCARD std::set<std::string> GetZoneIndependentSearchPaths(const std::set<std::string>& set) const;
_NODISCARD std::set<std::string> GetSearchPathsForZone(const std::set<std::string>& set, const std::string& zoneName) const;
public:
std::vector<std::string> m_zones_to_load;
std::vector<std::string> m_zones_to_build;
std::set<std::string> m_user_search_paths;
std::string m_output_folder;
std::string m_base_folder;
std::string m_out_folder;
bool m_base_folder_depends_on_zone;
bool m_out_folder_depends_on_zone;
std::set<std::string> m_asset_search_paths;
std::set<std::string> m_gdt_search_paths;
std::set<std::string> m_source_search_paths;
bool m_verbose;
@ -35,4 +61,12 @@ public:
* \return An output path for the zone based on the user input.
*/
_NODISCARD std::string GetOutputFolderPathForZone(const std::string& zoneName) const;
_NODISCARD std::set<std::string> GetZoneIndependentAssetSearchPaths() const;
_NODISCARD std::set<std::string> GetZoneIndependentGdtSearchPaths() const;
_NODISCARD std::set<std::string> GetZoneIndependentSourceSearchPaths() const;
_NODISCARD std::set<std::string> GetAssetSearchPathsForZone(const std::string& zoneName) const;
_NODISCARD std::set<std::string> GetGdtSearchPathsForZone(const std::string& zoneName) const;
_NODISCARD std::set<std::string> GetSourceSearchPathsForZone(const std::string& zoneName) const;
};

View File

@ -2,37 +2,6 @@
#include <filesystem>
SearchPaths::SearchPaths() = default;
SearchPaths::~SearchPaths()
= default;
SearchPaths::SearchPaths(const SearchPaths& other)
: m_search_paths(other.m_search_paths)
{
}
SearchPaths::SearchPaths(SearchPaths&& other) noexcept
: m_search_paths(std::move(other.m_search_paths))
{
}
SearchPaths& SearchPaths::operator=(const SearchPaths& other)
{
m_search_paths = other.m_search_paths;
return *this;
}
SearchPaths& SearchPaths::operator=(SearchPaths&& other) noexcept
{
m_search_paths = std::move(other.m_search_paths);
return *this;
}
std::unique_ptr<std::istream> SearchPaths::Open(const std::string& fileName)
{
for(auto* searchPathEntry : m_search_paths)

View File

@ -12,17 +12,17 @@ class SearchPaths final : public ISearchPath
public:
using iterator = std::vector<ISearchPath*>::iterator;
SearchPaths();
~SearchPaths() override;
SearchPaths() = default;
~SearchPaths() override = default;
std::unique_ptr<std::istream> Open(const std::string& fileName) override;
std::string GetPath() override;
void Find(const SearchPathSearchOptions& options, const std::function<void(const std::string&)>& callback) override;
SearchPaths(const SearchPaths& other);
SearchPaths(SearchPaths&& other) noexcept;
SearchPaths& operator=(const SearchPaths& other);
SearchPaths& operator=(SearchPaths&& other) noexcept;
SearchPaths(const SearchPaths& other) = delete;
SearchPaths(SearchPaths&& other) noexcept = default;
SearchPaths& operator=(const SearchPaths& other) = delete;
SearchPaths& operator=(SearchPaths&& other) noexcept = default;
/**
* \brief Adds a search path that gets deleted upon destruction of the \c SearchPaths object.