Fixed handling of relative paths in configuration files (#69)
This commit is contained in:
@@ -123,16 +123,18 @@ std::string inheritable_diagram_options::simplify_template_type(
|
||||
return full_name;
|
||||
}
|
||||
|
||||
std::vector<std::string> diagram::get_translation_units(
|
||||
const std::filesystem::path &root_directory) const
|
||||
std::vector<std::string> diagram::get_translation_units() const
|
||||
{
|
||||
std::vector<std::string> translation_units{};
|
||||
|
||||
for (const auto &g : glob()) {
|
||||
const auto matches = glob::glob(g, root_directory);
|
||||
std::string glob_path =
|
||||
fmt::format("{}/{}", relative_to().string(), g.c_str());
|
||||
|
||||
auto matches = glob::glob(glob_path, true, false);
|
||||
|
||||
for (const auto &match : matches) {
|
||||
const auto path =
|
||||
std::filesystem::canonical(root_directory / match);
|
||||
const auto path = std::filesystem::canonical(relative_to() / match);
|
||||
translation_units.emplace_back(path.string());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +170,7 @@ struct diagram : public inheritable_diagram_options {
|
||||
|
||||
virtual common::model::diagram_t type() const = 0;
|
||||
|
||||
std::vector<std::string> get_translation_units(
|
||||
const std::filesystem::path &root_directory) const;
|
||||
std::vector<std::string> get_translation_units() const;
|
||||
|
||||
void initialize_type_aliases();
|
||||
|
||||
@@ -268,7 +267,8 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const option<T> &o)
|
||||
return out;
|
||||
}
|
||||
|
||||
config load(const std::string &config_file);
|
||||
config load(const std::string &config_file,
|
||||
std::optional<bool> paths_relative_to_pwd = {});
|
||||
} // namespace config
|
||||
|
||||
namespace common::model {
|
||||
|
||||
@@ -571,7 +571,30 @@ template <> struct convert<config> {
|
||||
} // namespace YAML
|
||||
|
||||
namespace clanguml::config {
|
||||
config load(const std::string &config_file)
|
||||
|
||||
namespace {
|
||||
void resolve_option_path(YAML::Node &doc, const std::string &option)
|
||||
{
|
||||
std::filesystem::path relative_to_path{
|
||||
doc["relative_to"].as<std::string>()};
|
||||
|
||||
assert(relative_to_path.is_absolute());
|
||||
|
||||
std::filesystem::path option_path{doc[option].as<std::string>()};
|
||||
|
||||
if (option_path.is_absolute())
|
||||
return;
|
||||
|
||||
option_path = relative_to_path / option_path.string();
|
||||
option_path = option_path.lexically_normal();
|
||||
option_path.make_preferred();
|
||||
|
||||
doc[option] = option_path.string();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
config load(
|
||||
const std::string &config_file, std::optional<bool> paths_relative_to_pwd)
|
||||
{
|
||||
try {
|
||||
YAML::Node doc = YAML::LoadFile(config_file);
|
||||
@@ -583,6 +606,32 @@ config load(const std::string &config_file)
|
||||
doc.force_insert(
|
||||
"__parent_path", config_file_path.parent_path().string());
|
||||
|
||||
//
|
||||
// If no relative_to path is specified in the config, make all paths
|
||||
// resolvable against the parent directory of the .clang-uml config
|
||||
// file, or against the $PWD if it was specified so in the command
|
||||
// line
|
||||
//
|
||||
if (!doc["relative_to"]) {
|
||||
bool paths_relative_to_config_file = true;
|
||||
if (doc["paths_relative_to_config_file"] &&
|
||||
!doc["paths_relative_to_config_file"].as<bool>())
|
||||
paths_relative_to_config_file = false;
|
||||
if (paths_relative_to_pwd && *paths_relative_to_pwd)
|
||||
paths_relative_to_config_file = false;
|
||||
|
||||
if (paths_relative_to_config_file)
|
||||
doc["relative_to"] = config_file_path.parent_path().string();
|
||||
else
|
||||
doc["relative_to"] = std::filesystem::current_path().string();
|
||||
}
|
||||
|
||||
//
|
||||
// Resolve common path-like config options relative to `relative_to`
|
||||
//
|
||||
resolve_option_path(doc, "output_directory");
|
||||
resolve_option_path(doc, "compilation_database_dir");
|
||||
|
||||
// If the current directory is also a git repository,
|
||||
// load some config values, which can be included in the
|
||||
// generated diagrams
|
||||
|
||||
38
src/main.cc
38
src/main.cc
@@ -158,9 +158,9 @@ int main(int argc, const char *argv[])
|
||||
CLI::App app{"Clang-based PlantUML diagram generator for C++"};
|
||||
|
||||
std::string config_path{".clang-uml"};
|
||||
std::string compilation_database_dir{};
|
||||
std::optional<std::string> compilation_database_dir{};
|
||||
std::vector<std::string> diagram_names{};
|
||||
std::optional<std::string> output_directory;
|
||||
std::optional<std::string> output_directory{};
|
||||
unsigned int thread_count{0};
|
||||
bool show_version{false};
|
||||
int verbose{0};
|
||||
@@ -172,6 +172,7 @@ int main(int argc, const char *argv[])
|
||||
std::optional<std::string> add_package_diagram;
|
||||
std::optional<std::string> add_include_diagram;
|
||||
bool dump_config{false};
|
||||
std::optional<bool> paths_relative_to_pwd{};
|
||||
|
||||
app.add_option(
|
||||
"-c,--config", config_path, "Location of configuration file");
|
||||
@@ -200,6 +201,9 @@ int main(int argc, const char *argv[])
|
||||
"Add include diagram config");
|
||||
app.add_flag(
|
||||
"--dump-config", dump_config, "Print effective config to stdout");
|
||||
app.add_flag("--paths-relative-to-pwd", paths_relative_to_pwd,
|
||||
"If true, all paths in configuration files are relative to the $PWD "
|
||||
"instead of actual location of `.clang-uml` file.");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
@@ -241,7 +245,7 @@ int main(int argc, const char *argv[])
|
||||
|
||||
clanguml::config::config config;
|
||||
try {
|
||||
config = clanguml::config::load(config_path);
|
||||
config = clanguml::config::load(config_path, paths_relative_to_pwd);
|
||||
}
|
||||
catch (std::runtime_error &e) {
|
||||
LOG_ERROR(e.what());
|
||||
@@ -253,15 +257,18 @@ int main(int argc, const char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dump_config) {
|
||||
print_config(config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_INFO("Loaded clang-uml config from {}", config_path);
|
||||
|
||||
if (!compilation_database_dir.empty()) {
|
||||
config.compilation_database_dir.set(compilation_database_dir);
|
||||
//
|
||||
// Override selected config options from command line
|
||||
//
|
||||
if (compilation_database_dir) {
|
||||
config.compilation_database_dir.set(
|
||||
util::ensure_path_is_absolute(compilation_database_dir.value()));
|
||||
}
|
||||
if (output_directory) {
|
||||
config.output_directory.set(
|
||||
util::ensure_path_is_absolute(output_directory.value()));
|
||||
}
|
||||
|
||||
LOG_INFO("Loading compilation database from {} directory",
|
||||
@@ -271,6 +278,11 @@ int main(int argc, const char *argv[])
|
||||
if (output_directory)
|
||||
od = output_directory.value();
|
||||
|
||||
if (dump_config) {
|
||||
print_config(config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ensure_output_directory_exists(od))
|
||||
return 1;
|
||||
|
||||
@@ -433,8 +445,6 @@ void find_translation_units_for_diagrams(
|
||||
const std::vector<std::string> &compilation_database_files,
|
||||
std::map<std::string, std::vector<std::string>> &translation_units_map)
|
||||
{
|
||||
const auto current_directory = std::filesystem::current_path();
|
||||
|
||||
for (const auto &[name, diagram] : config.diagrams) {
|
||||
// If there are any specific diagram names provided on the command line,
|
||||
// and this diagram is not in that list - skip it
|
||||
@@ -450,7 +460,7 @@ void find_translation_units_for_diagrams(
|
||||
// configuration
|
||||
else {
|
||||
const std::vector<std::string> translation_units =
|
||||
diagram->get_translation_units(current_directory);
|
||||
diagram->get_translation_units();
|
||||
|
||||
std::vector<std::string> valid_translation_units{};
|
||||
std::copy_if(compilation_database_files.begin(),
|
||||
@@ -563,6 +573,8 @@ int create_config_file()
|
||||
int add_config_diagram(clanguml::common::model::diagram_t type,
|
||||
const std::string &config_file_path, const std::string &name)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
fs::path config_file{config_file_path};
|
||||
|
||||
if (!fs::exists(config_file)) {
|
||||
|
||||
@@ -317,4 +317,17 @@ std::string path_to_url(const std::filesystem::path &p)
|
||||
return fmt::format("{}", fmt::join(path_tokens, "/"));
|
||||
}
|
||||
|
||||
std::filesystem::path ensure_path_is_absolute(
|
||||
const std::filesystem::path &p, const std::filesystem::path &root)
|
||||
{
|
||||
if (p.is_absolute())
|
||||
return p;
|
||||
|
||||
auto result = root / p;
|
||||
result = result.lexically_normal();
|
||||
result.make_preferred();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace clanguml::util
|
||||
|
||||
@@ -250,4 +250,17 @@ std::size_t hash_seed(std::size_t seed);
|
||||
*/
|
||||
std::string path_to_url(const std::filesystem::path &p);
|
||||
|
||||
/**
|
||||
* @brief Ensure path is absolute.
|
||||
*
|
||||
* If path is absolute, return the p. If path is not absolute, make it
|
||||
* absolute with respect to root directory.
|
||||
*
|
||||
* @param p Path to modify
|
||||
* @param root Root against which the path should be made absolute
|
||||
* @return Absolute path
|
||||
*/
|
||||
std::filesystem::path ensure_path_is_absolute(const std::filesystem::path &p,
|
||||
const std::filesystem::path &root = std::filesystem::current_path());
|
||||
|
||||
} // namespace clanguml::util
|
||||
Reference in New Issue
Block a user