diff --git a/src/common/model/source_file.h b/src/common/model/source_file.h index 47ad6629..a3613938 100644 --- a/src/common/model/source_file.h +++ b/src/common/model/source_file.h @@ -50,6 +50,13 @@ class source_file public: source_file() = default; + source_file(const std::filesystem::path &p) + { + set_path(p.parent_path().string()); + set_name(p.filename()); + is_absolute_ = p.is_absolute(); + } + void set_path(const filesystem_path &p) { path_ = p; } void set_absolute() { is_absolute_ = true; } @@ -70,6 +77,8 @@ public: return (path_ | name()).to_string(); } + auto full_path() const { return path() | name(); } + void add_file(std::unique_ptr &&f) { LOG_DBG("Adding source file: {}, {}", f->name(), f->full_name(true)); diff --git a/src/include_diagram/model/diagram.cc b/src/include_diagram/model/diagram.cc index 5f1bcf7d..a46c50e2 100644 --- a/src/include_diagram/model/diagram.cc +++ b/src/include_diagram/model/diagram.cc @@ -42,6 +42,24 @@ void diagram::add_file(std::unique_ptr &&f) auto p = f->path(); + if (!f->path().is_empty()) { + // If the parent path is not empty, ensure relative parent directories + // of this source_file are in the diagram + common::model::filesystem_path parent_path_so_far; + for (const auto &directory : f->path()) { + auto dir = std::make_unique(); + if (!parent_path_so_far.is_empty()) + dir->set_path(parent_path_so_far); + dir->set_name(directory); + dir->set_type(common::model::source_file_t::kDirectory); + + if (!get_element(parent_path_so_far | directory).has_value()) + add_file(std::move(dir)); + + parent_path_so_far.append(directory); + } + } + add_element(p, std::move(f)); } diff --git a/src/include_diagram/visitor/translation_unit_visitor.cc b/src/include_diagram/visitor/translation_unit_visitor.cc index 4037900b..7eeca607 100644 --- a/src/include_diagram/visitor/translation_unit_visitor.cc +++ b/src/include_diagram/visitor/translation_unit_visitor.cc @@ -37,125 +37,132 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file) { assert(file.kind() == cppast::cpp_entity_kind::file_t); - const auto &f = static_cast(file); - - auto file_path = f.name(); - - LOG_DBG("Processing source file {}", file_path); - - process_file(file_path, true); + process_source_file(static_cast(file)); cppast::visit(file, [&, this](const cppast::cpp_entity &e, cppast::visitor_info info) { if (e.kind() == cppast::cpp_entity_kind::include_directive_t) { - assert(ctx.get_current_file().has_value()); - - auto file_path_cpy = file.name(); - const auto &inc = static_cast(e); - LOG_DBG("Processing include directive {} in file {}", - inc.full_path(), ctx.get_current_file().value().name()); - - process_file(inc.full_path(), false, inc.include_kind()); + process_include_directive(inc); } }); } -void translation_unit_visitor::process_file(const std::string &file, - bool register_as_current, - std::optional include_kind) +void translation_unit_visitor::process_include_directive( + const cppast::cpp_include_directive &include_directive) { - auto include_path = std::filesystem::path(file); + using common::model::relationship; + using common::model::source_file; + using common::model::source_file_t; - const std::filesystem::path base_directory{ctx.config().base_directory()}; + assert(ctx.get_current_file().has_value()); + LOG_DBG("Processing include directive {} in file {}", + include_directive.full_path(), ctx.get_current_file().value().name()); + + auto include_path = std::filesystem::path(include_directive.full_path()); + + // Make sure the include_path is absolute with respect to the + // filesystem, and in normal form if (include_path.is_relative()) { include_path = ctx.config().base_directory() / include_path; } include_path = include_path.lexically_normal(); - std::string full_file_path = include_path; - auto f_abs = std::make_unique(); - - if (include_path.is_absolute()) - f_abs->set_absolute(); - - f_abs->set_path(include_path.parent_path().string()); - f_abs->set_name(include_path.filename().string()); - - if (ctx.diagram().should_include(*f_abs)) { + if (ctx.diagram().should_include(source_file{include_path})) { + // Relativize the path with respect to relative_to config option + auto relative_include_path = include_path; if (ctx.config().relative_to) { const std::filesystem::path relative_to{ctx.config().relative_to()}; - - include_path = std::filesystem::relative(include_path, relative_to); + relative_include_path = + std::filesystem::relative(include_path, relative_to); } - auto f = std::make_unique(); - - auto parent_path_str = include_path.parent_path().string(); - if (!parent_path_str.empty()) - f->set_path(parent_path_str); - f->set_name(include_path.filename().string()); - f->set_type(common::model::source_file_t::kHeader); - - if (register_as_current && - ctx.diagram().get_element(f->path() | f->name()).has_value()) { - // This file is already in the diagram (e.g. it was added through an - // include directive before it was visited by the parser) - ctx.set_current_file( - ctx.diagram().get_element(f->path() | f->name())); - return; + // Check if this source file is already registered in the diagram, + // if not add it + auto diagram_path = source_file{relative_include_path}.full_path(); + if (!ctx.diagram().get_element(diagram_path).has_value()) { + ctx.diagram().add_file( + std::make_unique(relative_include_path)); } - if (!f->path().is_empty()) { - // Ensure parent path directories source_files - // are in the diagram - common::model::filesystem_path parent_path_so_far; - for (const auto &directory : f->path()) { - auto dir = std::make_unique(); - if (!parent_path_so_far.is_empty()) - dir->set_path(parent_path_so_far); - dir->set_name(directory); - dir->set_type(common::model::source_file_t::kDirectory); + auto &include_file = ctx.diagram().get_element(diagram_path).value(); - if (!ctx.diagram() - .get_element(parent_path_so_far | directory) - .has_value()) - ctx.diagram().add_file(std::move(dir)); + include_file.set_type(source_file_t::kHeader); - parent_path_so_far.append(directory); - } + // Add relationship from the currently parsed source file to this + // include file + auto relationship_type = common::model::relationship_t::kAssociation; + if (include_directive.include_kind() == + cppast::cpp_include_kind::system) + relationship_type = common::model::relationship_t::kDependency; + + ctx.get_current_file().value().add_relationship( + relationship{relationship_type, include_file.alias()}); + + include_file.set_file(std::filesystem::absolute(include_path) + .lexically_normal() + .string()); + include_file.set_line(0); + } + else { + LOG_DBG("Skipping include directive to file {}", include_path.string()); + } +} + +void translation_unit_visitor::process_source_file(const cppast::cpp_file &file) +{ + using common::model::relationship; + using common::model::source_file; + using common::model::source_file_t; + + LOG_DBG("Processing source file {}", file.name()); + + auto file_path = std::filesystem::path(file.name()); + + // Make sure the file_path is absolute with respect to the + // filesystem, and in normal form + if (file_path.is_relative()) { + file_path = ctx.config().base_directory() / file_path; + } + + file_path = file_path.lexically_normal(); + + if (ctx.diagram().should_include(source_file{file_path})) { + // Relativize the path with respect to relative_to config option + auto relative_file_path = file_path; + if (ctx.config().relative_to) { + const std::filesystem::path relative_to{ctx.config().relative_to()}; + relative_file_path = + std::filesystem::relative(file_path, relative_to); } - auto diagram_file_path = f->path() | f->name(); - - if (!ctx.diagram().get_element(diagram_file_path).has_value()) { - ctx.diagram().add_file(std::move(f)); + // Check if this source file is already registered in the diagram, + // if not add it + auto diagram_path = source_file{relative_file_path}.full_path(); + if (!ctx.diagram().get_element(diagram_path).has_value()) { + ctx.diagram().add_file( + std::make_unique(relative_file_path)); } - auto &diagram_source_file = - ctx.diagram().get_element(diagram_file_path).value(); + auto &source_file = ctx.diagram().get_element(diagram_path).value(); - if (!register_as_current) { - auto relationship_type = - common::model::relationship_t::kAssociation; - if (include_kind.has_value() && - include_kind.value() == cppast::cpp_include_kind::system) - relationship_type = common::model::relationship_t::kDependency; - - ctx.get_current_file().value().add_relationship( - common::model::relationship{ - relationship_type, diagram_source_file.alias()}); - - auto fp = std::filesystem::absolute(file).lexically_normal(); - diagram_source_file.set_file(fp.string()); - diagram_source_file.set_line(0); + const std::string implementation_suffix_prefix{".c"}; + if (file_path.has_extension() && + util::starts_with( + file_path.extension().string(), implementation_suffix_prefix)) { + source_file.set_type(source_file_t::kImplementation); } + else + source_file.set_type(source_file_t::kHeader); - ctx.set_current_file(type_safe::opt_ref(diagram_source_file)); + ctx.set_current_file(type_safe::opt_ref(source_file)); + } + else { + LOG_DBG("Skipping source file {}", file_path.string()); } } } diff --git a/src/include_diagram/visitor/translation_unit_visitor.h b/src/include_diagram/visitor/translation_unit_visitor.h index 5b034882..e4a44bca 100644 --- a/src/include_diagram/visitor/translation_unit_visitor.h +++ b/src/include_diagram/visitor/translation_unit_visitor.h @@ -46,8 +46,10 @@ public: void operator()(const cppast::cpp_entity &file); private: - void process_file(const std::string &file, bool register_as_current = false, - std::optional include_kind = {}); + void process_include_directive( + const cppast::cpp_include_directive &include_directive); + + void process_source_file(const cppast::cpp_file &file); // ctx allows to track current visitor context, e.g. current namespace translation_unit_context ctx; diff --git a/src/util/util.cc b/src/util/util.cc index 88f108c0..6eddd2e1 100644 --- a/src/util/util.cc +++ b/src/util/util.cc @@ -230,5 +230,10 @@ bool starts_with( return pref == pat; } +template <> bool starts_with(const std::string &s, const std::string &prefix) +{ + return s.rfind(prefix, 0) == 0; +} + } } diff --git a/src/util/util.h b/src/util/util.h index d12ad9af..5cd75bd9 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -164,6 +164,8 @@ template <> bool starts_with( const std::filesystem::path &path, const std::filesystem::path &prefix); +template <> bool starts_with(const std::string &s, const std::string &prefix); + template bool ends_with(const std::vector &col, const std::vector &suffix) { @@ -175,7 +177,7 @@ bool ends_with(const std::vector &col, const std::vector &suffix) } /** - * @brief Removes prefix sequence of elements from the beggining of col. + * @brief Removes prefix sequence of elements from the beginning of col. * * @tparam T * @param col diff --git a/tests/t40002/include/lib1/lib1.h b/tests/t40002/include/lib1/lib1.h index 250e3389..8c7388ea 100644 --- a/tests/t40002/include/lib1/lib1.h +++ b/tests/t40002/include/lib1/lib1.h @@ -2,6 +2,8 @@ #include "../lib2/lib2.h" +#include + namespace clanguml::t40002::lib1 { int foo0(); diff --git a/tests/t40002/include/lib2/lib2.h b/tests/t40002/include/lib2/lib2.h index dc7b5dba..3b9fcad5 100644 --- a/tests/t40002/include/lib2/lib2.h +++ b/tests/t40002/include/lib2/lib2.h @@ -1,7 +1,5 @@ #pragma once -#include "../lib1/lib1.h" - namespace clanguml::t40002::lib2 { int foo2();