Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a persistent cache for FMU contents #30

Merged
merged 11 commits into from
Apr 1, 2020
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ file(WRITE "${generatedFilesDir}/project_version_from_cmake.hpp"
"constexpr const char* project_version = \"${PROJECT_VERSION}\";\n")

add_executable(cse
"src/cache.hpp"
"src/cache.cpp"
"src/clean_cache.hpp"
"src/clean_cache.cpp"
"src/cli_application.hpp"
"src/cli_application.cpp"
"src/console_utils.hpp"
Expand Down
93 changes: 93 additions & 0 deletions src/cache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "cache.hpp"

#include <boost/filesystem.hpp>
#include <cse/file_cache.hpp>
#include <cse/log/logger.hpp>

#include <cstdlib>
#include <optional>
#include <stdexcept>


namespace
{

// A wrapper for `std::getenv()` that checks for both null and emptiness,
// and logs a debug message and returns null in either case.
const char* getenv(const char* variableName)
{
const auto e = std::getenv(variableName);
if (e && *e) return e;
BOOST_LOG_SEV(cse::log::logger(), cse::log::debug)
<< "Environment variable '" << variableName << "' not set.";
return nullptr;
}

// Obtain the (platform-specific) application cache path for the current user.
std::optional<boost::filesystem::path> user_cache_directory_path()
{
#if defined(_WIN32)
if (const auto localAppData = getenv("LocalAppData")) {
return boost::filesystem::path(localAppData);
}
if (const auto userProfile = getenv("UserProfile")) {
return boost::filesystem::path(userProfile) / "AppData" / "Local";
}

#elif defined(__APPLE__)
if (const auto home = getenv("HOME")) {
return boost::filesystem::path(home) / "Library" / "Caches";
}

#else // some other UNIX-like system
if (const auto xdgCacheHome = getenv("XDG_CACHE_HOME")) {
return boost::filesystem::path(xdgCacheHome);
}
if (const auto home = getenv("HOME")) {
return boost::filesystem::path(home) / ".cache";
}

#endif
return std::nullopt;
}

// Returns the (platform-specific) user cache path for this application.
std::optional<boost::filesystem::path> cache_directory_path()
{
if (const auto userCache = user_cache_directory_path()) {
return *userCache / "cse";
} else {
return std::nullopt;
}
}

} // namespace


std::shared_ptr<cse::model_uri_resolver> caching_model_uri_resolver()
{
if (const auto cachePath = cache_directory_path()) {
const auto cache = std::make_shared<cse::persistent_file_cache>(*cachePath);
BOOST_LOG_SEV(cse::log::logger(), cse::log::info)
<< "Using cache directory: " << *cachePath;
return cse::default_model_uri_resolver(cache);
} else {
BOOST_LOG_SEV(cse::log::logger(), cse::log::warning)
<< "Unable to determine user cache directory; caching is disabled.";
return cse::default_model_uri_resolver();
}
}


void clean_cache()
{
if (const auto cachePath = cache_directory_path()) {
const auto cache = std::make_shared<cse::persistent_file_cache>(*cachePath);
BOOST_LOG_SEV(cse::log::logger(), cse::log::info)
<< "Cleaning cache directory: " << *cachePath;
cache->cleanup();
} else {
throw std::runtime_error(
"Unable to determine user cache directory; cannot delete it.");
}
}
17 changes: 17 additions & 0 deletions src/cache.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef CSECLI_CACHE_HPP
#define CSECLI_CACHE_HPP

#include <cse/orchestration.hpp>

#include <memory>


/// Returns a caching model URI resolver.
std::shared_ptr<cse::model_uri_resolver> caching_model_uri_resolver();


/// Removes unused data from the application cache directory.
void clean_cache();


#endif // header guard
20 changes: 20 additions & 0 deletions src/clean_cache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "clean_cache.hpp"

#include "cache.hpp"


void clean_cache_subcommand::setup_options(
boost::program_options::options_description& /*options*/,
boost::program_options::options_description& /*positionalOptions*/,
boost::program_options::positional_options_description& /*positions*/)
const noexcept
{
// no options
}


int clean_cache_subcommand::run(const boost::program_options::variables_map& /*args*/) const
{
clean_cache();
return 0;
}
49 changes: 49 additions & 0 deletions src/clean_cache.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#ifndef CSECLI_CLEAN_CACHE_HPP
#define CSECLI_CLEAN_CACHE_HPP

#include "cli_application.hpp"


/// The `clean-cache` subcommand.
class clean_cache_subcommand : public cli_subcommand
{
public:
std::string name() const noexcept override
{
return "clean-cache";
}

std::string brief_description() const noexcept override
{
return "Removes unused data from the program cache";
}

std::string long_description() const noexcept override
{
return "This command removes unused files from the directory that "
"contains cached CSE data for the current user.\n"
"\n"
"The primary use for the cache is to unpack FMUs in a "
"persistent location, "
"so they don't have to be unpacked over and over for each run. "
"This can be a major time saver, "
"especially when working with large FMUs. "
"Over time, however, the cache can grow to take up "
"a significant amount of disk space.\n"
"\n"
"This command allows for safe removal of files from the cache. "
"It will remove all files that are not currently in use by a "
"CSE process.";
}

void setup_options(
boost::program_options::options_description& options,
boost::program_options::options_description& positionalOptions,
boost::program_options::positional_options_description& positions)
const noexcept override;

int run(const boost::program_options::variables_map& args) const override;
};


#endif
3 changes: 2 additions & 1 deletion src/inspect.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "inspect.hpp"

#include "cache.hpp"
#include "tools.hpp"

#include <boost/filesystem.hpp>
Expand Down Expand Up @@ -69,7 +70,7 @@ int inspect_subcommand::run(const boost::program_options::variables_map& args) c
currentPath += boost::filesystem::path::preferred_separator;
const auto baseUri = cse::path_to_file_uri(currentPath);
const auto uriReference = to_uri(args["uri_or_path"].as<std::string>());
const auto uriResolver = cse::default_model_uri_resolver();
const auto uriResolver = caching_model_uri_resolver();
const auto model = uriResolver->lookup_model(baseUri, uriReference);
print_model_description(*model->description());
if (args.count("no-vars") == 0) {
Expand Down
2 changes: 2 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "clean_cache.hpp"
#include "cli_application.hpp"
#include "inspect.hpp"
#include "logging_options.hpp"
Expand Down Expand Up @@ -31,6 +32,7 @@ int main(int argc, char const* const* argv)
"The Core Simulation Environment is free and open-source software for running distributed co-simulations.");
app.add_global_options(std::make_unique<logging_options>());
app.add_global_options(std::make_unique<version_option>("CSE CLI", project_version));
app.add_subcommand(std::make_unique<clean_cache_subcommand>());
app.add_subcommand(std::make_unique<inspect_subcommand>());
app.add_subcommand(std::make_unique<run_subcommand>());
app.add_subcommand(std::make_unique<run_single_subcommand>());
Expand Down
3 changes: 2 additions & 1 deletion src/run.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "run.hpp"

#include "cache.hpp"
#include "run_common.hpp"

#include <boost/filesystem.hpp>
Expand Down Expand Up @@ -170,7 +171,7 @@ int run_subcommand::run(const boost::program_options::variables_map& args) const
const auto systemStructurePath =
boost::filesystem::path(args["system_structure_path"].as<std::string>());

const auto uriResolver = cse::default_model_uri_resolver();
const auto uriResolver = caching_model_uri_resolver();
auto execution = load_system_structure(
systemStructurePath,
*uriResolver,
Expand Down
3 changes: 2 additions & 1 deletion src/run_single.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#endif
#include "run_single.hpp"

#include "cache.hpp"
#include "run_common.hpp"
#include "tools.hpp"

Expand Down Expand Up @@ -253,7 +254,7 @@ int run_single_subcommand::run(const boost::program_options::variables_map& args
currentPath += boost::filesystem::path::preferred_separator;
const auto baseUri = cse::path_to_file_uri(currentPath);
const auto uriReference = to_uri(args["uri_or_path"].as<std::string>());
const auto uriResolver = cse::default_model_uri_resolver();
const auto uriResolver = caching_model_uri_resolver();
const auto model = uriResolver->lookup_model(baseUri, uriReference);

std::optional<variable_values> initialValues;
Expand Down