Skip to content

Commit

Permalink
Merge pull request #1580 from mwestphal/rework_applicative_options
Browse files Browse the repository at this point in the history
Rework applicative options
  • Loading branch information
mwestphal authored Sep 2, 2024
2 parents 6ef173d + 0555a6e commit 9ee6912
Show file tree
Hide file tree
Showing 45 changed files with 1,595 additions and 1,289 deletions.
9 changes: 7 additions & 2 deletions application/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/F3DConfig.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/F3DConfig.h")

set(F3D_SOURCE_FILES
${CMAKE_CURRENT_BINARY_DIR}/F3DIcon.cxx
${CMAKE_CURRENT_SOURCE_DIR}/F3DColorMapTools.cxx
${CMAKE_CURRENT_SOURCE_DIR}/F3DConfigFileTools.cxx
${CMAKE_CURRENT_BINARY_DIR}/F3DIcon.cxx
${CMAKE_CURRENT_SOURCE_DIR}/F3DOptionsParser.cxx
${CMAKE_CURRENT_SOURCE_DIR}/F3DOptionsTools.cxx
${CMAKE_CURRENT_SOURCE_DIR}/F3DPluginsTools.cxx
${CMAKE_CURRENT_SOURCE_DIR}/F3DStarter.cxx
${CMAKE_CURRENT_SOURCE_DIR}/F3DSystemTools.cxx
${CMAKE_CURRENT_SOURCE_DIR}/main.cxx
Expand Down Expand Up @@ -86,6 +87,10 @@ set(f3d_compile_options_private "")
set(f3d_compile_options_public "")
set(f3d_link_options_public "")

# This is required to avoid cxxopts
# splitting input positional option by commas
target_compile_definitions(f3d PRIVATE "CXXOPTS_VECTOR_DELIMITER='\\0'")

# Headless EGL build
if (VTK_OPENGL_HAS_EGL)
target_compile_definitions(f3d PRIVATE F3D_HEADLESS_BUILD)
Expand Down
12 changes: 5 additions & 7 deletions application/F3DColorMapTools.cxx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "F3DColorMapTools.h"

#include "F3DConfigFileTools.h"
#include "F3DSystemTools.h"

#include "image.h"
#include "log.h"
Expand All @@ -23,16 +23,14 @@ std::string Find(const std::string& str)
}
}

std::vector<fs::path> dirsToCheck;
dirsToCheck.emplace_back(F3DConfigFileTools::GetUserConfigFileDirectory() / "colormaps");
std::vector<fs::path> dirsToCheck{ F3DSystemTools::GetUserConfigFileDirectory() / "colormaps",
#ifdef __APPLE__
dirsToCheck.emplace_back("/usr/local/etc/f3d/colormaps");
"/usr/local/etc/f3d/colormaps",
#endif
#ifdef __linux__
dirsToCheck.emplace_back("/etc/f3d/colormaps");
dirsToCheck.emplace_back("/usr/share/f3d/colormaps");
"/etc/f3d/colormaps", "/usr/share/f3d/colormaps",
#endif
dirsToCheck.emplace_back(F3DConfigFileTools::GetBinaryResourceDirectory() / "colormaps");
F3DSystemTools::GetBinaryResourceDirectory() / "colormaps" };

for (const fs::path& dir : dirsToCheck)
{
Expand Down
206 changes: 141 additions & 65 deletions application/F3DConfigFileTools.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -3,78 +3,24 @@
#include "F3DConfig.h"
#include "F3DSystemTools.h"

#include "nlohmann/json.hpp"

#include "log.h"

#include <cstring>
#include <filesystem>
#include <fstream>
#include <set>
#include <vector>

namespace fs = std::filesystem;

//----------------------------------------------------------------------------
fs::path F3DConfigFileTools::GetUserConfigFileDirectory()
{
std::string applicationName = "f3d";
fs::path dirPath;
#if defined(_WIN32)
const char* appData = std::getenv("APPDATA");
if (!appData)
{
return {};
}
dirPath = fs::path(appData);
#else
// Implementing XDG specifications
const char* xdgConfigHome = std::getenv("XDG_CONFIG_HOME");
if (xdgConfigHome && strlen(xdgConfigHome) > 0)
{
dirPath = fs::path(xdgConfigHome);
}
else
{
const char* home = std::getenv("HOME");
if (!home || strlen(home) == 0)
{
return {};
}
dirPath = fs::path(home);
dirPath /= ".config";
}
#endif
dirPath /= applicationName;
return dirPath;
}

//----------------------------------------------------------------------------
fs::path F3DConfigFileTools::GetBinaryResourceDirectory()
namespace
{
fs::path dirPath;
try
{
dirPath = F3DSystemTools::GetApplicationPath();

// transform path to exe to path to install
// /install/bin/f3d -> /install
dirPath = fs::canonical(dirPath).parent_path().parent_path();

// Add binary specific paths
#if F3D_MACOS_BUNDLE
dirPath /= "Resources";
#else
dirPath /= "share/f3d";
#endif
}
catch (const fs::filesystem_error&)
{
f3d::log::debug("Cannot recover binary configuration file directory: ", dirPath.string());
return {};
}

return dirPath;
}

//----------------------------------------------------------------------------
std::vector<fs::path> F3DConfigFileTools::GetConfigPaths(const std::string& configSearch)
/**
* Recover a OS-specific vector of potential config file directories
*/
std::vector<fs::path> GetConfigPaths(const std::string& configSearch)
{
std::vector<std::filesystem::path> paths;

Expand All @@ -88,8 +34,8 @@ std::vector<fs::path> F3DConfigFileTools::GetConfigPaths(const std::string& conf
"/etc/f3d",
"/usr/share/f3d/configs",
#endif
F3DConfigFileTools::GetBinaryResourceDirectory() / "configs",
F3DConfigFileTools::GetUserConfigFileDirectory(),
F3DSystemTools::GetBinaryResourceDirectory() / "configs",
F3DSystemTools::GetUserConfigFileDirectory(),
};

for (const fs::path& dir : dirsToCheck)
Expand Down Expand Up @@ -131,3 +77,133 @@ std::vector<fs::path> F3DConfigFileTools::GetConfigPaths(const std::string& conf

return paths;
}
}

//----------------------------------------------------------------------------
F3DOptionsTools::OptionsEntries F3DConfigFileTools::ReadConfigFiles(const std::string& userConfig)
{
// Default config directory name
std::string configSearch = "config";
if (!userConfig.empty())
{
// Check if provided userConfig is a full path
auto path = fs::path(userConfig);
if (path.stem() == userConfig || path.filename() == userConfig)
{
// Only a stem or a filename, use provided userConfig as configSearch
configSearch = userConfig;
}
else
{
// Assume its a full path and use as is, not searching for config files
configSearch = "";
}
}

// Recover config paths to search for config files
std::vector<fs::path> configPaths;
if (!configSearch.empty())
{
for (const auto& path : ::GetConfigPaths(configSearch))
{
configPaths.emplace_back(path);
}
}
else
{
configPaths.emplace_back(userConfig);
}

// Recover actual individual config file paths
std::set<fs::path> actualConfigFilePaths;
for (auto configPath : configPaths)
{
// Recover an absolute canonical path to config file
try
{
configPath = fs::canonical(fs::path(configPath)).string();
}
catch (const fs::filesystem_error&)
{
f3d::log::error("Configuration file does not exist: ", configPath.string(), " , ignoring it");
continue;
}

// Recover all config files if needed in directories
if (fs::is_directory(configPath))
{
f3d::log::debug("Using config directory ", configPath.string());
for (auto& entry : std::filesystem::directory_iterator(configPath))
{
actualConfigFilePaths.emplace(entry);
}
}
else
{
f3d::log::debug("Using config file ", configPath.string());
actualConfigFilePaths.emplace(configPath);
}
}

// If we used a configSearch but did not find any, warn the user
if (!configSearch.empty() && actualConfigFilePaths.empty())
{
f3d::log::warn("Configuration file for \"", configSearch, "\" could not be found");
}

// Read config files
F3DOptionsTools::OptionsEntries confEntries;
for (const auto& configFilePath : actualConfigFilePaths)
{
std::ifstream file(configFilePath);
if (!file.is_open())
{
// Cannot be tested
f3d::log::warn(
"Unable to open the configuration file: ", configFilePath.string(), " , ignoring it");
continue;
}

// Read the file into a json
nlohmann::ordered_json json;
try
{
file >> json;
}
catch (const std::exception& ex)
{
f3d::log::error(
"Unable to parse the configuration file ", configFilePath.string(), " , ignoring it");
f3d::log::error(ex.what());
continue;
}

// For each config "pattern"
for (const auto& configBlock : json.items())
{
// Add each config entry into an option dict
F3DOptionsTools::OptionsDict entry;
for (const auto& item : configBlock.value().items())
{
if (item.value().is_number() || item.value().is_boolean())
{
entry[item.key()] = nlohmann::to_string(item.value());
}
else if (item.value().is_string())
{
entry[item.key()] = item.value().get<std::string>();
}
else
{
f3d::log::error(item.key(), " from ", configFilePath.string(),
" must be a string, a boolean or a number, ignoring entry");
continue;
}
}

// Emplace the option dict for that pattern into the config entries vector
confEntries.emplace_back(entry, configFilePath, configBlock.key());
}
}
return confEntries;
}
19 changes: 9 additions & 10 deletions application/F3DConfigFileTools.h
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
#ifndef F3DConfigFileTools_h
#define F3DConfigFileTools_h
/**
* @class F3DConfigFileTools
* @brief A namespace to recover path to config file and related directories, cross platform
*
* @brief A namespace to parse config files
*/
#include "F3DOptionsTools.h"

#ifndef F3DConfigFileTools_h
#define F3DConfigFileTools_h

#include <filesystem>
#include <string>
#include <vector>

namespace F3DConfigFileTools
{
std::filesystem::path GetUserConfigFileDirectory();
std::filesystem::path GetBinaryResourceDirectory();
std::vector<std::filesystem::path> GetConfigPaths(const std::string& configSearch);
/**
* Read config files using userConfig if any, return an optionEntries
* containing ordered optionDict.
*/
F3DOptionsTools::OptionsEntries ReadConfigFiles(const std::string& userConfig);
}

#endif
Loading

0 comments on commit 9ee6912

Please sign in to comment.