blob: 3686dba5175dfa2b640400cd01d4c3da05a19503 [file] [edit]
// Copyright 2022 The clvk authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "config.hpp"
#include "log.hpp"
#include "unit.hpp"
#include "utils.hpp"
#include <cassert>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <unordered_map>
#include <vector>
const config_struct config;
namespace {
template <typename T> constexpr config_option_type option_type() = delete;
#define DEFINE_OPTION_TYPE_GETTER(ctype, type) \
template <> constexpr config_option_type option_type<ctype>() { \
return type; \
}
DEFINE_OPTION_TYPE_GETTER(std::string, config_option_type::string)
DEFINE_OPTION_TYPE_GETTER(uint32_t, config_option_type::uint32)
DEFINE_OPTION_TYPE_GETTER(bool, config_option_type::boolean)
std::vector<config_option> gConfigOptions = {
#define OPTION(type, name, valdef) \
{option_type<type>(), #name, &config.name, false},
#define EARLY_OPTION(type, name, valdef) \
{option_type<type>(), #name, &config.name, true},
#include "config.def"
#undef EARLY_OPTION
#undef OPTION
};
void parse_string(void* value_ptr, const char* txt) {
auto cfgval = static_cast<config_value<std::string>*>(value_ptr);
cfgval->value.assign(txt);
cfgval->set = true;
}
void parse_boolean(void* value_ptr, const char* txt) {
auto cfgval = static_cast<config_value<bool>*>(value_ptr);
cfgval->value = atoi(txt);
cfgval->set = true;
}
void parse_uint32(void* value_ptr, const char* txt) {
auto cfgval = static_cast<config_value<uint32_t>*>(value_ptr);
int base = 10;
if (strlen(txt) >= 2 && txt[0] == '0' && txt[1] == 'x') {
base = 16;
}
cfgval->value = std::stoul(txt, nullptr, base);
cfgval->set = true;
}
#define TXT_SIZE FILENAME_MAX
bool print_string(char* txt, void* value_ptr) {
auto cfgval = static_cast<config_value<std::string>*>(value_ptr);
snprintf(txt, TXT_SIZE, "'%s'", cfgval->value.c_str());
return cfgval->set;
}
bool print_boolean(char* txt, void* value_ptr) {
auto cfgval = static_cast<config_value<bool>*>(value_ptr);
snprintf(txt, TXT_SIZE, "%s", cfgval->value ? "true" : "false");
return cfgval->set;
}
bool print_uint32(char* txt, void* value_ptr) {
auto cfgval = static_cast<config_value<uint32_t>*>(value_ptr);
snprintf(txt, TXT_SIZE, "%u (0x%x)", cfgval->value, cfgval->value);
return cfgval->set;
}
// Helper function to trim whitespace.
std::string trim(const std::string& str) {
size_t first = str.find_first_not_of(" \t\n");
size_t last = str.find_last_not_of(" \t\n");
// Check for valid range
if (first == std::string::npos || last == std::string::npos) {
return str; // Empty or only whitespace
}
// Extract the trimmed string
std::string trimmed = str.substr(first, (last - first + 1));
return trimmed;
}
std::string get_clvk_env_name(const std::string& name) {
std::string var_name = "CLVK_";
std::string optname_upper(name);
std::transform(optname_upper.begin(), optname_upper.end(),
optname_upper.begin(), ::toupper);
var_name += optname_upper;
return var_name;
}
void read_config_file(std::unordered_map<std::string, std::string>& umap,
std::ifstream& config_stream) {
std::string line;
while (std::getline(config_stream, line)) {
// Ignore comments and empty lines
line = trim(line);
if (line.empty() || line[0] == '#') {
continue;
}
// Parse key-value pairs
size_t pos = line.find('=');
if (pos != std::string::npos) {
std::string key = trim(line.substr(0, pos));
std::string value = trim(line.substr(pos + 1));
// Store values (if any)
if (value != "") {
umap[key] = value;
cvk_debug_group_fn(loggroup::cfg, "'%s' = '%s'", key.c_str(),
value.c_str());
}
} else {
cvk_warn_group_fn(loggroup::cfg, "%s , %s",
"The following line is malformed", line.c_str());
}
}
config_stream.close();
}
void parse_config_file(bool early_option) {
std::unordered_map<std::string, std::string> file_config_values;
std::string conf_file = "clvk.conf";
std::ifstream config_stream;
std::vector<std::string> config_file_paths;
config_file_paths.push_back("/etc/clvk.conf");
#ifdef __ANDROID__
config_file_paths.push_back("/system/etc/clvk.conf");
config_file_paths.push_back("/vendor/etc/clvk.conf");
#endif
config_file_paths.push_back("/usr/local/etc/clvk.conf");
config_file_paths.push_back("~/.config/clvk.conf");
config_file_paths.push_back(
(std::filesystem::current_path() / conf_file).string());
// First check if env var has file
std::string conv_file_env_var = "CLVK_CONFIG_FILE";
const char* conf_file_env_path = getenv(conv_file_env_var.c_str());
if (conf_file_env_path != nullptr) {
config_file_paths.push_back(conf_file_env_path);
}
for (auto& curr_path : config_file_paths) {
if (!std::filesystem::exists(curr_path)) {
continue;
}
config_stream.open(curr_path);
if (!config_stream.is_open()) {
cvk_error("Error opening config file - %s", curr_path.c_str());
}
cvk_info_group_fn(loggroup::cfg, "Parsing config file '%s'",
curr_path.c_str());
read_config_file(file_config_values, config_stream);
}
for (auto& opt : gConfigOptions) {
if (early_option != opt.early_option) {
continue;
}
if (file_config_values.find(opt.name) == file_config_values.end()) {
continue;
}
CVK_ASSERT(file_config_values[opt.name].length() > 0);
auto curr_conf = (file_config_values[opt.name]).c_str();
void* optval = const_cast<void*>(opt.value);
switch (opt.type) {
case config_option_type::string:
parse_string(optval, curr_conf);
break;
case config_option_type::boolean:
parse_boolean(optval, curr_conf);
break;
case config_option_type::uint32:
parse_uint32(optval, curr_conf);
break;
}
}
return;
}
void parse_env(bool early_option) {
for (auto& opt : gConfigOptions) {
if (early_option != opt.early_option) {
continue;
}
auto var_name = get_clvk_env_name(opt.name);
const char* txt = getenv(var_name.c_str());
if (txt == nullptr) {
continue;
}
cvk_debug_group_fn(loggroup::cfg, "'%s' = '%s'", var_name.c_str(), txt);
void* optval = const_cast<void*>(opt.value);
switch (opt.type) {
case config_option_type::string:
parse_string(optval, txt);
break;
case config_option_type::boolean:
parse_boolean(optval, txt);
break;
case config_option_type::uint32:
parse_uint32(optval, txt);
break;
}
}
}
void print_config() {
cvk_info_group_fn(loggroup::cfg, "");
std::sort(gConfigOptions.begin(), gConfigOptions.end(),
[](const config_option& a, const config_option& b) {
return a.name < b.name;
});
for (auto& opt : gConfigOptions) {
char txt[TXT_SIZE];
void* optval = const_cast<void*>(opt.value);
int set;
switch (opt.type) {
case config_option_type::string:
set = print_string(txt, optval);
break;
case config_option_type::boolean:
set = print_boolean(txt, optval);
break;
case config_option_type::uint32:
set = print_uint32(txt, optval);
break;
}
if (set) {
cvk_info_group(loggroup::cfg, " *%s: %s", opt.name.c_str(), txt);
} else {
cvk_debug_group(loggroup::cfg, " %s: %s", opt.name.c_str(), txt);
}
}
}
} // namespace
void init_config() {
parse_config_file(false);
parse_env(false);
print_config();
}
void init_early_config() {
parse_config_file(true);
parse_env(true);
}