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

Rework applicative options #1580

Merged
merged 4 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
"/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 @@

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;

Check warning on line 164 in application/F3DConfigFileTools.cxx

View check run for this annotation

Codecov / codecov/patch

application/F3DConfigFileTools.cxx#L162-L164

Added lines #L162 - L164 were not covered by tests
}

// 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