Skip to content

Commit

Permalink
Make the "labs" cli implementation the default
Browse files Browse the repository at this point in the history
The old implementation will be under the "classic" sub command.

close #5005
  • Loading branch information
kbenne committed Oct 28, 2023
1 parent 6fb3ded commit c5cbbea
Showing 1 changed file with 64 additions and 71 deletions.
135 changes: 64 additions & 71 deletions src/cli/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,32 @@ int main(int argc, char* argv[]) {
const std::string programName = std::move(args.front());
args.erase(args.begin());

const bool is_labs = !args.empty() && (args[0] == "labs");
if (is_labs) {
const bool is_classic = !args.empty() && (args[0] == "classic");

if (is_classic) {
// The "classic" cli implementation will not be expecting the first arg
// to be the word classic so we need to remove the first arg
args.erase(args.begin());
} else {
// Replace backward slashes with forward slashes... cf #4856
std::for_each(args.begin(), args.end(), [](auto& s) {
//std::replace(s.begin(), s.end(), '\\', '/');
boost::replace_all(s, "\\", "\\\\");
});
// fmt::print("Cleaned after slash replacement arguments: {}\n", args);
std::for_each(args.begin(), args.end(), [](auto& s) { boost::replace_all(s, "\\", "\\\\"); });
}

// ScriptEngineInstance will delay load the engines
openstudio::ScriptEngineInstance rubyEngine("rubyengine", args);
openstudio::ScriptEngineInstance pythonEngine("pythonengine", args);

if (is_labs) {
if (is_classic) {
#if defined _WIN32
// Poor man's hack #4847
// Disable this logger, we have a duplicate in the ruby shared lib
openstudio::Logger::instance().standardOutLogger().disable();
openstudio::Logger::instance().standardErrLogger().disable();
// Avoid getting some messages during getOpenStudioModule() when we locate the DLL
openstudio::StringStreamLogSink sink;
#endif
result = openstudio::rubyCLI(rubyEngine);
} else {
CLI::App app{"openstudio"};
app.name(programName);

Expand All @@ -89,27 +100,17 @@ int main(int argc, char* argv[]) {
}
}

// fmt::print("args={}\n", args);

fmt::print(fmt::fg(fmt::color::red),
"┌{0:─^{2}}┐\n"
"│{1: ^{2}}│\n"
"└{0:─^{2}}┘",
"", "The `labs` command is experimental - Do not use in production", 80);
fmt::print("\n");
app.get_formatter()->column_width(35);

auto* const experimentalApp = app.add_subcommand("labs");

// specify string->value mappings
const std::map<std::string, LogLevel> logLevelMap{
{"Trace", LogLevel::Trace}, {"Debug", LogLevel::Debug}, {"Info", LogLevel::Info},
{"Warn", LogLevel::Warn}, {"Error", LogLevel::Error}, {"Fatal", LogLevel::Fatal},
};
static constexpr std::array<std::string_view, 6> logLevelStrs = {"Trace", "Debug", "Info", "Warn", "Error", "Fatal"};

experimentalApp
->add_option_function<LogLevel>(
app
.add_option_function<LogLevel>(
"-l,--loglevel",
[](const LogLevel& level) {
fmt::print("Setting Log Level to {} ({})\n", logLevelStrs[static_cast<size_t>(level) - static_cast<size_t>(LogLevel::Trace)], level);
Expand All @@ -120,56 +121,53 @@ int main(int argc, char* argv[]) {
->transform(CLI::CheckedTransformer(logLevelMap, CLI::ignore_case));

std::vector<std::string> executeRubyCmds;
CLI::Option* execRubyOption = experimentalApp
->add_option("-e,--execute", executeRubyCmds,
"Execute one line of Ruby script (may be used more than once). Returns after executing commands.")
CLI::Option* execRubyOption = app
.add_option("-e,--execute", executeRubyCmds,
"Execute one line of Ruby script (may be used more than once). Returns after executing commands.")
->option_text("CMD");

std::vector<std::string> executePythonCmds;
CLI::Option* execPythonOption =
experimentalApp
->add_option("-c,--pyexecute", executePythonCmds,
"Execute one line of Python script (may be used more than once). Returns after executing commands.")
->option_text("CMD");
CLI::Option* execPythonOption = app
.add_option("-c,--pyexecute", executePythonCmds,
"Execute one line of Python script (may be used more than once). Returns after executing commands.")
->option_text("CMD");

// ========================== R U B Y O P T I O N S ==========================
std::vector<openstudio::path> includeDirs;
experimentalApp
->add_option("-I,--include", includeDirs, "Add additional directory to add to front of Ruby $LOAD_PATH (may be used more than once)")
app.add_option("-I,--include", includeDirs, "Add additional directory to add to front of Ruby $LOAD_PATH (may be used more than once)")
->option_text("DIR")
->check(CLI::ExistingDirectory)
->group(rubySpecificOptionsGroupName);

std::vector<openstudio::path> gemPathDirs;
experimentalApp
->add_option("--gem_path", gemPathDirs,
"Add additional directory to add to front of GEM_PATH environment variable (may be used more than once)")
app
.add_option("--gem_path", gemPathDirs, "Add additional directory to add to front of GEM_PATH environment variable (may be used more than once)")
->option_text("DIR")
->check(CLI::ExistingDirectory)
->group(rubySpecificOptionsGroupName);

openstudio::path gemHomeDir;
experimentalApp->add_option("--gem_home", gemHomeDir, "Set GEM_HOME environment variable")
app.add_option("--gem_home", gemHomeDir, "Set GEM_HOME environment variable")
->option_text("DIR")
->check(CLI::ExistingDirectory)
->group(rubySpecificOptionsGroupName);

openstudio::path bundleGemFilePath;
experimentalApp->add_option("--bundle", bundleGemFilePath, "Use bundler for GEMFILE")
app.add_option("--bundle", bundleGemFilePath, "Use bundler for GEMFILE")
->option_text("GEMFILE")
->check(CLI::ExistingFile)
->group(rubySpecificOptionsGroupName);

openstudio::path bundleGemDirPath;
experimentalApp->add_option("--bundle_path", bundleGemDirPath, "Use bundler installed gems in BUNDLE_PATH")
app.add_option("--bundle_path", bundleGemDirPath, "Use bundler installed gems in BUNDLE_PATH")
->option_text("BUNDLE_PATH")
->check(CLI::ExistingDirectory)
->group(rubySpecificOptionsGroupName);

// std::vector<std::string>
std::string bundleWithoutGroups;
experimentalApp
->add_option(
app
.add_option(
"--bundle_without", bundleWithoutGroups,
"Space separated list of groups for bundler to exclude in WITHOUT_GROUPS. Surround multiple groups with quotes like \"test development\"")
->option_text("WITHOUT_GROUPS")
Expand All @@ -194,15 +192,15 @@ int main(int argc, char* argv[]) {
// ========================== P Y T H O N O P T I O N S ==========================

std::vector<openstudio::path> pythonPathDirs;
experimentalApp
->add_option("--python_path", pythonPathDirs,
"Add additional directory to add to front of PYTHONPATH environment variable (may be used more than once)")
app
.add_option("--python_path", pythonPathDirs,
"Add additional directory to add to front of PYTHONPATH environment variable (may be used more than once)")
->option_text("DIR")
->check(CLI::ExistingDirectory)
->group(pythonSpecificOptionsGroupName);

openstudio::path pythonHomeDir;
experimentalApp->add_option("--python_home", pythonHomeDir, "Set PYTHONHOME environment variable")
app.add_option("--python_home", pythonHomeDir, "Set PYTHONHOME environment variable")
->option_text("DIR")
->check(CLI::ExistingDirectory)
->group(pythonSpecificOptionsGroupName);
Expand All @@ -220,7 +218,7 @@ int main(int argc, char* argv[]) {
pythonEngine.registerInitializationFunction(runSetupPythonPath);

// {
auto* execute_ruby_scriptCommand = experimentalApp->add_subcommand("execute_ruby_script", "Executes a ruby file");
auto* execute_ruby_scriptCommand = app.add_subcommand("execute_ruby_script", "Executes a ruby file");
openstudio::filesystem::path rubyScriptPath;
execute_ruby_scriptCommand->add_option("path", rubyScriptPath, "Path to ruby file")->required(true)->check(CLI::ExistingFile);
// We can't do this because that means we can't pass extra **flags**
Expand All @@ -237,7 +235,7 @@ int main(int argc, char* argv[]) {
// }

// {
auto* execute_python_scriptCommand = experimentalApp->add_subcommand("execute_python_script", "Executes a python file");
auto* execute_python_scriptCommand = app.add_subcommand("execute_python_script", "Executes a python file");
openstudio::filesystem::path pythonScriptPath;
execute_python_scriptCommand->add_option("path", pythonScriptPath, "Path to python file")->required(true)->check(CLI::ExistingFile);

Expand All @@ -249,22 +247,21 @@ int main(int argc, char* argv[]) {
});
// }

[[maybe_unused]] auto* gem_listCommand =
experimentalApp->add_subcommand("gem_list", "Lists the set gems available to openstudio")->callback([&rubyEngine]() {
openstudio::cli::executeGemListCommand(rubyEngine);
});
[[maybe_unused]] auto* gem_listCommand = app.add_subcommand("gem_list", "Lists the set gems available to openstudio")->callback([&rubyEngine]() {
openstudio::cli::executeGemListCommand(rubyEngine);
});

// Not hidding any commands right now
// [[maybe_unused]] auto* list_commandsCommand = experimentalApp->add_subcommand("list_commands", "Lists the entire set of available commands");

// run command
openstudio::cli::setupRunOptions(experimentalApp, rubyEngine, pythonEngine);
openstudio::cli::setupRunOptions(&app, rubyEngine, pythonEngine);

// update (model) command
// openstudio::cli::setupUpdateCommand(experimentalApp);
{
bool keep = false;
auto* updateCommand = experimentalApp->add_subcommand("update", "Updates OpenStudio Models to the current version");
auto* updateCommand = app.add_subcommand("update", "Updates OpenStudio Models to the current version");
updateCommand->add_flag("-k,--keep", keep, "Keep original files");

openstudio::filesystem::path updateOsmPath;
Expand All @@ -277,28 +274,34 @@ int main(int argc, char* argv[]) {
});
}

openstudio::cli::MeasureUpdateOptions::setupMeasureUpdateOptions(experimentalApp, rubyEngine, pythonEngine);
openstudio::cli::MeasureUpdateOptions::setupMeasureUpdateOptions(&app, rubyEngine, pythonEngine);

// ========================== V E R S I O N ==========================
[[maybe_unused]] auto* openstudio_versionCommand =
experimentalApp->add_subcommand("openstudio_version", "Returns the OpenStudio version used by the CLI")
->group(versionGroupname)
->callback([]() { fmt::print("{}\n", openStudioLongVersion()); });
app.add_subcommand("openstudio_version", "Returns the OpenStudio version used by the CLI")->group(versionGroupname)->callback([]() {
fmt::print("{}\n", openStudioLongVersion());
});

[[maybe_unused]] auto* energyplus_versionCommand =
experimentalApp->add_subcommand("energyplus_version", "Returns the EnergyPlus version used by the CLI")
->group(versionGroupname)
->callback([]() { fmt::print("{}+{}\n", energyPlusVersion(), energyPlusBuildSHA()); });
app.add_subcommand("energyplus_version", "Returns the EnergyPlus version used by the CLI")->group(versionGroupname)->callback([]() {
fmt::print("{}+{}\n", energyPlusVersion(), energyPlusBuildSHA());
});
[[maybe_unused]] auto* ruby_versionCommand =
experimentalApp->add_subcommand("ruby_version", "Returns the Ruby version used by the CLI")->group(versionGroupname)->callback([&rubyEngine]() {
app.add_subcommand("ruby_version", "Returns the Ruby version used by the CLI")->group(versionGroupname)->callback([&rubyEngine]() {
rubyEngine->exec("puts RUBY_VERSION");
});
[[maybe_unused]] auto* python_versionCommand = experimentalApp->add_subcommand("python_version", "Returns the Python version used by the CLI")
->group(versionGroupname)
->callback([&pythonEngine]() { pythonEngine->exec("import sys; print(sys.version)"); });
[[maybe_unused]] auto* python_versionCommand =
app.add_subcommand("python_version", "Returns the Python version used by the CLI")->group(versionGroupname)->callback([&pythonEngine]() {
pythonEngine->exec("import sys; print(sys.version)");
});

// ====================================================================

// ========================== C L A S S I C ==========================
// This exists to document access to the classic implementation
app.add_subcommand("classic", "For backwards compatibility, invoke an older version of the OpenStudio CLI that does not support Python");
// ====================================================================

// CLI11_PARSE(app, argc, argv);
// CLI11_PARSE(app, args);
try {
Expand Down Expand Up @@ -328,16 +331,6 @@ int main(int argc, char* argv[]) {
// fmt::print("includeDirs={}\n", fmt::join(includeDirs, ","));
// fmt::print("gemPathDirs={}\n", fmt::join(gemPathDirs, ","));
// fmt::print("gemHomeDir={}\n", gemHomeDir);
} else {
#if defined _WIN32
// Poor man's hack #4847
// Disable this logger, we have a duplicate in the ruby shared lib
openstudio::Logger::instance().standardOutLogger().disable();
openstudio::Logger::instance().standardErrLogger().disable();
// Avoid getting some messages during getOpenStudioModule() when we locate the DLL
openstudio::StringStreamLogSink sink;
#endif
result = openstudio::rubyCLI(rubyEngine);
}

// Important to destroy RubyEngine and finalize Ruby at the right time
Expand Down

0 comments on commit c5cbbea

Please sign in to comment.