@@ -179,7 +179,7 @@ void generator::generate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (error::uml_alias_missing &e) {
|
catch (error::uml_alias_missing &e) {
|
||||||
LOG_ERROR("=== Skipping {} relation from {} to {} due "
|
LOG_DBG("=== Skipping {} relation from {} to {} due "
|
||||||
"to: {}",
|
"to: {}",
|
||||||
plantuml_common::to_plantuml(r.type(), r.style()),
|
plantuml_common::to_plantuml(r.type(), r.style()),
|
||||||
c.full_name(), destination, e.what());
|
c.full_name(), destination, e.what());
|
||||||
@@ -215,7 +215,7 @@ void generator::generate(
|
|||||||
all_relations_str << relstr.str();
|
all_relations_str << relstr.str();
|
||||||
}
|
}
|
||||||
catch (error::uml_alias_missing &e) {
|
catch (error::uml_alias_missing &e) {
|
||||||
LOG_ERROR("=== Skipping inheritance relation from {} to {} due "
|
LOG_DBG("=== Skipping inheritance relation from {} to {} due "
|
||||||
"to: {}",
|
"to: {}",
|
||||||
b.name(), c.name(), e.what());
|
b.name(), c.name(), e.what());
|
||||||
}
|
}
|
||||||
@@ -271,7 +271,7 @@ void generator::generate(
|
|||||||
relationships_ostr << relstr.str();
|
relationships_ostr << relstr.str();
|
||||||
}
|
}
|
||||||
catch (error::uml_alias_missing &ex) {
|
catch (error::uml_alias_missing &ex) {
|
||||||
LOG_ERROR("Skipping {} relation from {} to {} due "
|
LOG_DBG("Skipping {} relation from {} to {} due "
|
||||||
"to: {}",
|
"to: {}",
|
||||||
clanguml::common::generators::plantuml::to_plantuml(
|
clanguml::common::generators::plantuml::to_plantuml(
|
||||||
r.type(), r.style()),
|
r.type(), r.style()),
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ void translation_unit_visitor::process_type_alias_template(
|
|||||||
{
|
{
|
||||||
if (at.type_alias().underlying_type().kind() ==
|
if (at.type_alias().underlying_type().kind() ==
|
||||||
cppast::cpp_type_kind::unexposed_t) {
|
cppast::cpp_type_kind::unexposed_t) {
|
||||||
LOG_WARN("Template alias has unexposed underlying type: {}",
|
LOG_DBG("Template alias has unexposed underlying type: {}",
|
||||||
static_cast<const cppast::cpp_unexposed_type &>(
|
static_cast<const cppast::cpp_unexposed_type &>(
|
||||||
at.type_alias().underlying_type())
|
at.type_alias().underlying_type())
|
||||||
.name());
|
.name());
|
||||||
@@ -407,7 +407,7 @@ bool translation_unit_visitor::process_template_parameters(
|
|||||||
process_scope_template_parameters(c, scope);
|
process_scope_template_parameters(c, scope);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG_WARN("Class {} is templated but it's scope {} is not - "
|
LOG_DBG("Class {} is templated but it's scope {} is not - "
|
||||||
"probably this is a specialization",
|
"probably this is a specialization",
|
||||||
cls.name(), scope.name());
|
cls.name(), scope.name());
|
||||||
|
|
||||||
@@ -541,7 +541,7 @@ void translation_unit_visitor::
|
|||||||
{relationship_t::kInstantiation, base_template_full_name});
|
{relationship_t::kInstantiation, base_template_full_name});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG_WARN(
|
LOG_DBG(
|
||||||
"No user data for base template {}", primary_template_ref.name());
|
"No user data for base template {}", primary_template_ref.name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1321,8 +1321,7 @@ bool translation_unit_visitor::find_relationships_in_template_instantiation(
|
|||||||
found = find_relationships(args[0u].type().value(), relationships,
|
found = find_relationships(args[0u].type().value(), relationships,
|
||||||
relationship_t::kAggregation);
|
relationship_t::kAggregation);
|
||||||
else
|
else
|
||||||
LOG_WARN(
|
LOG_DBG("Failed to process template argument of std::vector at: {}",
|
||||||
"Failed to process template argument of std::vector at: {}",
|
|
||||||
fn);
|
fn);
|
||||||
}
|
}
|
||||||
else if (ctx.config().should_include(ns, name)) {
|
else if (ctx.config().should_include(ns, name)) {
|
||||||
@@ -1470,7 +1469,7 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
|
|||||||
t, tinst, template_base_params, parent, full_template_name);
|
t, tinst, template_base_params, parent, full_template_name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG_WARN("Template instantiation {} has no primary template",
|
LOG_DBG("Template instantiation {} has no primary template",
|
||||||
cppast::to_string(t));
|
cppast::to_string(t));
|
||||||
|
|
||||||
full_template_name = cppast::to_string(t);
|
full_template_name = cppast::to_string(t);
|
||||||
@@ -1769,7 +1768,7 @@ void translation_unit_visitor::build_template_instantiation_primary_template(
|
|||||||
LOG_DBG("Primary template ref set to: {}", tinst.base_template());
|
LOG_DBG("Primary template ref set to: {}", tinst.base_template());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
LOG_WARN(
|
LOG_DBG(
|
||||||
"No user data for base template {}", primary_template_ref.name());
|
"No user data for base template {}", primary_template_ref.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ void generator<C, D>::generate_config_layout_hints(std::ostream &ostr) const
|
|||||||
ostr << hint_str.str();
|
ostr << hint_str.str();
|
||||||
}
|
}
|
||||||
catch (clanguml::error::uml_alias_missing &e) {
|
catch (clanguml::error::uml_alias_missing &e) {
|
||||||
LOG_ERROR("=== Skipping layout hint from {} to {} due "
|
LOG_DBG("=== Skipping layout hint from {} to {} due "
|
||||||
"to: {}",
|
"to: {}",
|
||||||
entity, hint.entity, e.what());
|
entity, hint.entity, e.what());
|
||||||
}
|
}
|
||||||
@@ -133,8 +133,8 @@ void generator<C, D>::generate_notes(
|
|||||||
|
|
||||||
template <typename DiagramModel, typename DiagramConfig,
|
template <typename DiagramModel, typename DiagramConfig,
|
||||||
typename DiagramVisitor>
|
typename DiagramVisitor>
|
||||||
DiagramModel generate(cppast::libclang_compilation_database &db,
|
DiagramModel generate(const cppast::libclang_compilation_database &db,
|
||||||
const std::string &name, DiagramConfig &diagram)
|
const std::string &name, DiagramConfig &diagram, bool verbose = false)
|
||||||
{
|
{
|
||||||
LOG_INFO("Generating diagram {}.puml", name);
|
LOG_INFO("Generating diagram {}.puml", name);
|
||||||
DiagramModel d;
|
DiagramModel d;
|
||||||
@@ -151,8 +151,10 @@ DiagramModel generate(cppast::libclang_compilation_database &db,
|
|||||||
}
|
}
|
||||||
|
|
||||||
cppast::cpp_entity_index idx;
|
cppast::cpp_entity_index idx;
|
||||||
|
auto logger =
|
||||||
|
verbose ? cppast::default_logger() : cppast::default_quiet_logger();
|
||||||
cppast::simple_file_parser<cppast::libclang_parser> parser{
|
cppast::simple_file_parser<cppast::libclang_parser> parser{
|
||||||
type_safe::ref(idx)};
|
type_safe::ref(idx), std::move(logger)};
|
||||||
|
|
||||||
// Process all matching translation units
|
// Process all matching translation units
|
||||||
DiagramVisitor ctx(idx, d, diagram);
|
DiagramVisitor ctx(idx, d, diagram);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ std::string element::alias() const { return fmt::format("C_{:010}", m_id); }
|
|||||||
void element::add_relationship(relationship &&cr)
|
void element::add_relationship(relationship &&cr)
|
||||||
{
|
{
|
||||||
if (cr.destination().empty()) {
|
if (cr.destination().empty()) {
|
||||||
LOG_WARN("Skipping relationship '{}' - {} - '{}' due empty "
|
LOG_DBG("Skipping relationship '{}' - {} - '{}' due empty "
|
||||||
"destination",
|
"destination",
|
||||||
cr.destination(), to_string(cr.type()), full_name(true));
|
cr.destination(), to_string(cr.type()), full_name(true));
|
||||||
return;
|
return;
|
||||||
@@ -45,7 +45,7 @@ void element::add_relationship(relationship &&cr)
|
|||||||
|
|
||||||
if ((cr.type() == relationship_t::kInstantiation) &&
|
if ((cr.type() == relationship_t::kInstantiation) &&
|
||||||
(cr.destination() == full_name(true))) {
|
(cr.destination() == full_name(true))) {
|
||||||
LOG_WARN("Skipping self instantiation relationship for {}",
|
LOG_DBG("Skipping self instantiation relationship for {}",
|
||||||
cr.destination());
|
cr.destination());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,8 +81,7 @@ public:
|
|||||||
LOG_DBG("Getting nested element at path: {}", path.to_string());
|
LOG_DBG("Getting nested element at path: {}", path.to_string());
|
||||||
|
|
||||||
if (path.is_empty() || !has_element(path[0])) {
|
if (path.is_empty() || !has_element(path[0])) {
|
||||||
LOG_WARN(
|
LOG_DBG("Nested element {} not found in element", path.to_string());
|
||||||
"Nested element {} not found in element", path.to_string());
|
|
||||||
return type_safe::optional_ref<V>{};
|
return type_safe::optional_ref<V>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,13 +135,6 @@ bool diagram::should_include(
|
|||||||
return should_include(std::get<0>(name), std::get<1>(name));
|
return should_include(std::get<0>(name), std::get<1>(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// bool diagram::should_include(
|
|
||||||
// const common::model::namespace_ &ns, const std::string &name) const
|
|
||||||
//{
|
|
||||||
// auto ns_and_name = ns | name;
|
|
||||||
// return should_include(ns_and_name.to_string());
|
|
||||||
//}
|
|
||||||
|
|
||||||
bool diagram::should_include(
|
bool diagram::should_include(
|
||||||
const common::model::namespace_ &ns, const std::string &name) const
|
const common::model::namespace_ &ns, const std::string &name) const
|
||||||
{
|
{
|
||||||
@@ -346,7 +339,7 @@ std::shared_ptr<clanguml::config::diagram> parse_diagram_config(const Node &d)
|
|||||||
return std::make_shared<package_diagram>(d.as<package_diagram>());
|
return std::make_shared<package_diagram>(d.as<package_diagram>());
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_WARN("Diagrams of type {} are not supported... ", diagram_type);
|
LOG_ERROR("Diagrams of type {} are not supported... ", diagram_type);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,10 +110,6 @@ struct diagram : public inheritable_diagram_options {
|
|||||||
bool should_include(
|
bool should_include(
|
||||||
const std::pair<common::model::namespace_, std::string> &name) const;
|
const std::pair<common::model::namespace_, std::string> &name) const;
|
||||||
|
|
||||||
// bool should_include(
|
|
||||||
// const std::vector<std::string> &ns, const std::string &name)
|
|
||||||
// const;
|
|
||||||
|
|
||||||
bool should_include(
|
bool should_include(
|
||||||
const common::model::namespace_ &ns, const std::string &name) const;
|
const common::model::namespace_ &ns, const std::string &name) const;
|
||||||
|
|
||||||
|
|||||||
54
src/main.cc
54
src/main.cc
@@ -16,15 +16,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
|
|
||||||
|
|
||||||
#include "class_diagram/generators/plantuml/class_diagram_generator.h"
|
#include "class_diagram/generators/plantuml/class_diagram_generator.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "cx/compilation_database.h"
|
#include "cx/compilation_database.h"
|
||||||
#include "package_diagram/generators/plantuml/package_diagram_generator.h"
|
#include "package_diagram/generators/plantuml/package_diagram_generator.h"
|
||||||
#include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h"
|
#include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h"
|
||||||
|
|
||||||
#include "config/config.h"
|
|
||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
|
|
||||||
#include <cli11/CLI11.hpp>
|
#include <cli11/CLI11.hpp>
|
||||||
@@ -33,8 +30,10 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <future>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <util/thread_pool_executor.h>
|
||||||
|
|
||||||
using namespace clanguml;
|
using namespace clanguml;
|
||||||
using config::config;
|
using config::config;
|
||||||
@@ -44,6 +43,10 @@ void print_diagrams_list(const clanguml::config::config &cfg);
|
|||||||
|
|
||||||
bool check_output_directory(const std::string &dir);
|
bool check_output_directory(const std::string &dir);
|
||||||
|
|
||||||
|
void generate_diagram(const std::string &od, const std::string &name,
|
||||||
|
std::shared_ptr<clanguml::config::diagram> diagram,
|
||||||
|
const cppast::libclang_compilation_database &db, bool verbose);
|
||||||
|
|
||||||
int main(int argc, const char *argv[])
|
int main(int argc, const char *argv[])
|
||||||
{
|
{
|
||||||
CLI::App app{"Clang-based PlantUML diagram generator for C++"};
|
CLI::App app{"Clang-based PlantUML diagram generator for C++"};
|
||||||
@@ -52,6 +55,7 @@ int main(int argc, const char *argv[])
|
|||||||
std::string compilation_database_dir{'.'};
|
std::string compilation_database_dir{'.'};
|
||||||
std::vector<std::string> diagram_names{};
|
std::vector<std::string> diagram_names{};
|
||||||
std::optional<std::string> output_directory;
|
std::optional<std::string> output_directory;
|
||||||
|
unsigned int thread_count{0};
|
||||||
bool verbose{false};
|
bool verbose{false};
|
||||||
bool list_diagrams{false};
|
bool list_diagrams{false};
|
||||||
|
|
||||||
@@ -63,16 +67,15 @@ int main(int argc, const char *argv[])
|
|||||||
"List of diagram names to generate");
|
"List of diagram names to generate");
|
||||||
app.add_option("-o,--output-directory", output_directory,
|
app.add_option("-o,--output-directory", output_directory,
|
||||||
"Override output directory specified in config file");
|
"Override output directory specified in config file");
|
||||||
|
app.add_option("-t,--thread-count", thread_count,
|
||||||
|
"Thread pool size (0 = hardware concurrency)");
|
||||||
app.add_flag("-v,--verbose", verbose, "Verbose logging");
|
app.add_flag("-v,--verbose", verbose, "Verbose logging");
|
||||||
app.add_flag("-l,--list-diagrams", list_diagrams,
|
app.add_flag("-l,--list-diagrams", list_diagrams,
|
||||||
"Print list of diagrams defined in the config file");
|
"Print list of diagrams defined in the config file");
|
||||||
|
|
||||||
CLI11_PARSE(app, argc, argv);
|
CLI11_PARSE(app, argc, argv);
|
||||||
|
|
||||||
if (verbose) {
|
clanguml::util::setup_logging(verbose);
|
||||||
spdlog::default_logger_raw()->set_level(spdlog::level::debug);
|
|
||||||
spdlog::default_logger_raw()->set_pattern("[%l] %v");
|
|
||||||
}
|
|
||||||
|
|
||||||
clanguml::config::config config;
|
clanguml::config::config config;
|
||||||
try {
|
try {
|
||||||
@@ -93,7 +96,7 @@ int main(int argc, const char *argv[])
|
|||||||
LOG_INFO("Loading compilation database from {} directory",
|
LOG_INFO("Loading compilation database from {} directory",
|
||||||
config.compilation_database_dir());
|
config.compilation_database_dir());
|
||||||
|
|
||||||
cppast::libclang_compilation_database db(config.compilation_database_dir());
|
cppast::libclang_compilation_database db{config.compilation_database_dir()};
|
||||||
|
|
||||||
auto od = config.output_directory();
|
auto od = config.output_directory();
|
||||||
if (output_directory)
|
if (output_directory)
|
||||||
@@ -102,12 +105,32 @@ int main(int argc, const char *argv[])
|
|||||||
if (!check_output_directory(od))
|
if (!check_output_directory(od))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
util::thread_pool_executor generator_executor{thread_count};
|
||||||
|
std::vector<std::future<void>> futs;
|
||||||
|
|
||||||
for (const auto &[name, diagram] : config.diagrams) {
|
for (const auto &[name, diagram] : config.diagrams) {
|
||||||
// If there are any specific diagram names provided on the command line,
|
// If there are any specific diagram names provided on the command line,
|
||||||
// and this diagram is not in that list - skip it
|
// and this diagram is not in that list - skip it
|
||||||
if (!diagram_names.empty() && !util::contains(diagram_names, name))
|
if (!diagram_names.empty() && !util::contains(diagram_names, name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
futs.emplace_back(generator_executor.add(
|
||||||
|
[&od, &name = name, &diagram = diagram, &db = db, verbose]() {
|
||||||
|
generate_diagram(od, name, diagram, db, verbose);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &fut : futs) {
|
||||||
|
fut.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_diagram(const std::string &od, const std::string &name,
|
||||||
|
std::shared_ptr<clanguml::config::diagram> diagram,
|
||||||
|
const cppast::libclang_compilation_database &db, bool verbose)
|
||||||
|
{
|
||||||
using clanguml::config::class_diagram;
|
using clanguml::config::class_diagram;
|
||||||
using clanguml::config::diagram_type;
|
using clanguml::config::diagram_type;
|
||||||
using clanguml::config::package_diagram;
|
using clanguml::config::package_diagram;
|
||||||
@@ -118,7 +141,7 @@ int main(int argc, const char *argv[])
|
|||||||
ofs.open(path, std::ofstream::out | std::ofstream::trunc);
|
ofs.open(path, std::ofstream::out | std::ofstream::trunc);
|
||||||
|
|
||||||
if (diagram->type() == diagram_type::class_diagram) {
|
if (diagram->type() == diagram_type::class_diagram) {
|
||||||
using diagram_config = clanguml::config::class_diagram;
|
using diagram_config = class_diagram;
|
||||||
using diagram_model = clanguml::class_diagram::model::diagram;
|
using diagram_model = clanguml::class_diagram::model::diagram;
|
||||||
using diagram_visitor =
|
using diagram_visitor =
|
||||||
clanguml::class_diagram::visitor::translation_unit_visitor;
|
clanguml::class_diagram::visitor::translation_unit_visitor;
|
||||||
@@ -126,13 +149,13 @@ int main(int argc, const char *argv[])
|
|||||||
auto model =
|
auto model =
|
||||||
clanguml::common::generators::plantuml::generate<diagram_model,
|
clanguml::common::generators::plantuml::generate<diagram_model,
|
||||||
diagram_config, diagram_visitor>(db, diagram->name,
|
diagram_config, diagram_visitor>(db, diagram->name,
|
||||||
dynamic_cast<diagram_config &>(*diagram));
|
dynamic_cast<diagram_config &>(*diagram), verbose);
|
||||||
|
|
||||||
ofs << clanguml::class_diagram::generators::plantuml::generator(
|
ofs << clanguml::class_diagram::generators::plantuml::generator(
|
||||||
dynamic_cast<diagram_config &>(*diagram), model);
|
dynamic_cast<diagram_config &>(*diagram), model);
|
||||||
}
|
}
|
||||||
else if (diagram->type() == diagram_type::sequence_diagram) {
|
else if (diagram->type() == diagram_type::sequence_diagram) {
|
||||||
using diagram_config = clanguml::config::sequence_diagram;
|
using diagram_config = sequence_diagram;
|
||||||
using diagram_model = clanguml::sequence_diagram::model::diagram;
|
using diagram_model = clanguml::sequence_diagram::model::diagram;
|
||||||
using diagram_visitor =
|
using diagram_visitor =
|
||||||
clanguml::sequence_diagram::visitor::translation_unit_visitor;
|
clanguml::sequence_diagram::visitor::translation_unit_visitor;
|
||||||
@@ -140,14 +163,14 @@ int main(int argc, const char *argv[])
|
|||||||
auto model =
|
auto model =
|
||||||
clanguml::common::generators::plantuml::generate<diagram_model,
|
clanguml::common::generators::plantuml::generate<diagram_model,
|
||||||
diagram_config, diagram_visitor>(db, diagram->name,
|
diagram_config, diagram_visitor>(db, diagram->name,
|
||||||
dynamic_cast<diagram_config &>(*diagram));
|
dynamic_cast<diagram_config &>(*diagram), verbose);
|
||||||
|
|
||||||
ofs << clanguml::sequence_diagram::generators::plantuml::generator(
|
ofs << clanguml::sequence_diagram::generators::plantuml::generator(
|
||||||
dynamic_cast<clanguml::config::sequence_diagram &>(*diagram),
|
dynamic_cast<clanguml::config::sequence_diagram &>(*diagram),
|
||||||
model);
|
model);
|
||||||
}
|
}
|
||||||
else if (diagram->type() == diagram_type::package_diagram) {
|
else if (diagram->type() == diagram_type::package_diagram) {
|
||||||
using diagram_config = clanguml::config::package_diagram;
|
using diagram_config = package_diagram;
|
||||||
using diagram_model = clanguml::package_diagram::model::diagram;
|
using diagram_model = clanguml::package_diagram::model::diagram;
|
||||||
using diagram_visitor =
|
using diagram_visitor =
|
||||||
clanguml::package_diagram::visitor::translation_unit_visitor;
|
clanguml::package_diagram::visitor::translation_unit_visitor;
|
||||||
@@ -155,7 +178,7 @@ int main(int argc, const char *argv[])
|
|||||||
auto model =
|
auto model =
|
||||||
clanguml::common::generators::plantuml::generate<diagram_model,
|
clanguml::common::generators::plantuml::generate<diagram_model,
|
||||||
diagram_config, diagram_visitor>(db, diagram->name,
|
diagram_config, diagram_visitor>(db, diagram->name,
|
||||||
dynamic_cast<diagram_config &>(*diagram));
|
dynamic_cast<diagram_config &>(*diagram), verbose);
|
||||||
|
|
||||||
ofs << clanguml::package_diagram::generators::plantuml::generator(
|
ofs << clanguml::package_diagram::generators::plantuml::generator(
|
||||||
dynamic_cast<diagram_config &>(*diagram), model);
|
dynamic_cast<diagram_config &>(*diagram), model);
|
||||||
@@ -164,9 +187,6 @@ int main(int argc, const char *argv[])
|
|||||||
LOG_INFO("Written {} diagram to {}", name, path.string());
|
LOG_INFO("Written {} diagram to {}", name, path.string());
|
||||||
|
|
||||||
ofs.close();
|
ofs.close();
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check_output_directory(const std::string &dir)
|
bool check_output_directory(const std::string &dir)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ void generator::generate_relationships(
|
|||||||
ostr << relstr.str();
|
ostr << relstr.str();
|
||||||
}
|
}
|
||||||
catch (error::uml_alias_missing &e) {
|
catch (error::uml_alias_missing &e) {
|
||||||
LOG_ERROR("=== Skipping dependency relation from {} to {} due "
|
LOG_DBG("=== Skipping dependency relation from {} to {} due "
|
||||||
"to: {}",
|
"to: {}",
|
||||||
p.full_name(false), r.destination(), e.what());
|
p.full_name(false), r.destination(), e.what());
|
||||||
}
|
}
|
||||||
|
|||||||
93
src/util/thread_pool_executor.cc
Normal file
93
src/util/thread_pool_executor.cc
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* src/util/thread_pool_executor.cc
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021-2022 Bartek Kryza <bkryza@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 "thread_pool_executor.h"
|
||||||
|
|
||||||
|
namespace clanguml::util {
|
||||||
|
thread_pool_executor::thread_pool_executor()
|
||||||
|
: thread_pool_executor{0}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_pool_executor::thread_pool_executor(unsigned int pool_size)
|
||||||
|
: done_{false}
|
||||||
|
{
|
||||||
|
if (pool_size == 0U)
|
||||||
|
pool_size = std::thread::hardware_concurrency();
|
||||||
|
|
||||||
|
for (auto i = 0U; i < pool_size; i++) {
|
||||||
|
threads_.push_back(std::thread{&thread_pool_executor::worker, this});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_pool_executor::~thread_pool_executor() { stop(); }
|
||||||
|
|
||||||
|
std::future<void> thread_pool_executor::add(std::function<void()> &&task)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> l(tasks_mutex_);
|
||||||
|
|
||||||
|
std::packaged_task<void()> ptask{std::move(task)};
|
||||||
|
auto res = ptask.get_future();
|
||||||
|
|
||||||
|
tasks_.emplace_back(std::move(ptask));
|
||||||
|
|
||||||
|
l.unlock();
|
||||||
|
tasks_cond_.notify_one();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread_pool_executor::stop()
|
||||||
|
{
|
||||||
|
done_ = true;
|
||||||
|
for (auto &thread : threads_) {
|
||||||
|
if (thread.joinable())
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread_pool_executor::worker()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
while (!done_) {
|
||||||
|
auto task = get();
|
||||||
|
|
||||||
|
task();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::runtime_error &e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::packaged_task<void()> thread_pool_executor::get()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> l(tasks_mutex_);
|
||||||
|
|
||||||
|
while (tasks_.empty()) {
|
||||||
|
if (done_)
|
||||||
|
throw std::runtime_error("Thread pool closing...");
|
||||||
|
|
||||||
|
tasks_cond_.wait_for(l, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = std::move(tasks_.front());
|
||||||
|
tasks_.pop_front();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
51
src/util/thread_pool_executor.h
Normal file
51
src/util/thread_pool_executor.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* src/util/thread_pool_executor.h
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021-2022 Bartek Kryza <bkryza@gmail.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <future>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml::util {
|
||||||
|
class thread_pool_executor {
|
||||||
|
public:
|
||||||
|
thread_pool_executor();
|
||||||
|
|
||||||
|
thread_pool_executor(unsigned int pool_size);
|
||||||
|
|
||||||
|
~thread_pool_executor();
|
||||||
|
|
||||||
|
std::future<void> add(std::function<void()> &&task);
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void worker();
|
||||||
|
|
||||||
|
std::packaged_task<void()> get();
|
||||||
|
|
||||||
|
std::atomic_bool done_;
|
||||||
|
|
||||||
|
std::deque<std::packaged_task<void()>> tasks_;
|
||||||
|
std::mutex tasks_mutex_;
|
||||||
|
std::condition_variable tasks_cond_;
|
||||||
|
|
||||||
|
std::vector<std::thread> threads_;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -26,6 +26,18 @@ namespace util {
|
|||||||
|
|
||||||
const std::string WHITESPACE = " \n\r\t\f\v";
|
const std::string WHITESPACE = " \n\r\t\f\v";
|
||||||
|
|
||||||
|
void setup_logging(bool verbose)
|
||||||
|
{
|
||||||
|
auto console =
|
||||||
|
spdlog::stdout_color_mt("console", spdlog::color_mode::automatic);
|
||||||
|
|
||||||
|
console->set_pattern("[%^%l%^] [tid %t] %v");
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
console->set_level(spdlog::level::debug);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string ltrim(const std::string &s)
|
std::string ltrim(const std::string &s)
|
||||||
{
|
{
|
||||||
size_t start = s.find_first_not_of(WHITESPACE);
|
size_t start = s.find_first_not_of(WHITESPACE);
|
||||||
@@ -68,34 +80,6 @@ std::string join(const std::vector<std::string> &toks, std::string delimiter)
|
|||||||
return fmt::format("{}", fmt::join(toks, delimiter));
|
return fmt::format("{}", fmt::join(toks, delimiter));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
std::string ns_relative(
|
|
||||||
const std::vector<std::string> &namespaces, const std::string &n)
|
|
||||||
{
|
|
||||||
std::vector<std::string> namespaces_sorted{namespaces};
|
|
||||||
|
|
||||||
std::sort(namespaces_sorted.rbegin(), namespaces_sorted.rend());
|
|
||||||
|
|
||||||
auto res = n;
|
|
||||||
|
|
||||||
for (const auto &ns : namespaces_sorted) {
|
|
||||||
if (ns.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (n == ns)
|
|
||||||
return split(n, "::").back();
|
|
||||||
|
|
||||||
auto ns_prefix = ns + "::";
|
|
||||||
auto it = res.find(ns_prefix);
|
|
||||||
while (it != std::string::npos) {
|
|
||||||
res.erase(it, ns_prefix.size());
|
|
||||||
it = res.find(ns_prefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
std::string unqualify(const std::string &s)
|
std::string unqualify(const std::string &s)
|
||||||
{
|
{
|
||||||
auto toks = clanguml::util::split(s, " ");
|
auto toks = clanguml::util::split(s, " ");
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -36,20 +37,27 @@ std::string trim(const std::string &s);
|
|||||||
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||||
|
|
||||||
#define LOG_ERROR(fmt__, ...) \
|
#define LOG_ERROR(fmt__, ...) \
|
||||||
spdlog::error(std::string("[{}:{}] ") + fmt__, __FILENAME__, __LINE__, \
|
spdlog::get("console")->error(std::string("[{}:{}] ") + fmt__, \
|
||||||
##__VA_ARGS__)
|
__FILENAME__, __LINE__, ##__VA_ARGS__)
|
||||||
|
|
||||||
#define LOG_WARN(fmt__, ...) \
|
#define LOG_WARN(fmt__, ...) \
|
||||||
spdlog::warn(std::string("[{}:{}] ") + fmt__, __FILENAME__, __LINE__, \
|
spdlog::get("console")->warn(std::string("[{}:{}] ") + fmt__, \
|
||||||
##__VA_ARGS__)
|
__FILENAME__, __LINE__, ##__VA_ARGS__)
|
||||||
|
|
||||||
#define LOG_INFO(fmt__, ...) \
|
#define LOG_INFO(fmt__, ...) \
|
||||||
spdlog::info(std::string("[{}:{}] ") + fmt__, __FILENAME__, __LINE__, \
|
spdlog::get("console")->info(std::string("[{}:{}] ") + fmt__, \
|
||||||
##__VA_ARGS__)
|
__FILENAME__, __LINE__, ##__VA_ARGS__)
|
||||||
|
|
||||||
#define LOG_DBG(fmt__, ...) \
|
#define LOG_DBG(fmt__, ...) \
|
||||||
spdlog::debug(std::string("[{}:{}] ") + fmt__, __FILENAME__, __LINE__, \
|
spdlog::get("console")->debug(std::string("[{}:{}] ") + fmt__, \
|
||||||
##__VA_ARGS__)
|
__FILENAME__, __LINE__, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Setup spdlog logger.
|
||||||
|
*
|
||||||
|
* @param verbose Whether the logging should be verbose or not.
|
||||||
|
*/
|
||||||
|
void setup_logging(bool verbose);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Split a string using delimiter
|
* @brief Split a string using delimiter
|
||||||
@@ -67,25 +75,6 @@ std::vector<std::string> split(std::string str, std::string delimiter);
|
|||||||
|
|
||||||
std::string join(const std::vector<std::string> &toks, std::string delimiter);
|
std::string join(const std::vector<std::string> &toks, std::string delimiter);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get name of the identifier relative to a set of namespaces
|
|
||||||
*
|
|
||||||
* This function tries to match a given C++ identifier (e.g.
|
|
||||||
* clanguml::util::split) to the longest namespace from the provided list
|
|
||||||
* matching the identifier from the left.
|
|
||||||
* If a match is found, the relative identifier is returned. If none of
|
|
||||||
* the namespaces match the identifier or if nothing is left after
|
|
||||||
* removing the matching namespace from the identifier, original identifier is
|
|
||||||
* returned.
|
|
||||||
*
|
|
||||||
* @param namespaces List of C++ namespaces to consider
|
|
||||||
* @param n Identifier to relativize
|
|
||||||
*
|
|
||||||
* @return Identifier relative to one of the matching namespaces.
|
|
||||||
*/
|
|
||||||
// std::string ns_relative(
|
|
||||||
// const std::vector<std::string> &namespaces, const std::string &n);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove any qualifiers (e.g. const) from type.
|
* @brief Remove any qualifiers (e.g. const) from type.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -48,6 +48,13 @@ set(CLANG_UML_TEST_CONFIG_HEADER
|
|||||||
catch.h
|
catch.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(CLANG_UML_TEST_THREAD_POOL_EXECUTOR_SRC
|
||||||
|
test_thread_pool_executor.cc
|
||||||
|
)
|
||||||
|
set(CLANG_UML_TEST_THREAD_POOL_EXECUTOR_HEADER
|
||||||
|
catch.h
|
||||||
|
)
|
||||||
|
|
||||||
add_executable(test_util
|
add_executable(test_util
|
||||||
${CLANG_UML_TEST_UTIL_SRC}
|
${CLANG_UML_TEST_UTIL_SRC}
|
||||||
${CLANG_UML_TEST_UTIL_HEADER})
|
${CLANG_UML_TEST_UTIL_HEADER})
|
||||||
@@ -88,6 +95,16 @@ target_link_libraries(test_config
|
|||||||
${YAML_CPP_LIBRARIES}
|
${YAML_CPP_LIBRARIES}
|
||||||
spdlog::spdlog clang-umllib cppast)
|
spdlog::spdlog clang-umllib cppast)
|
||||||
|
|
||||||
|
add_executable(test_thread_pool_executor
|
||||||
|
${CLANG_UML_TEST_THREAD_POOL_EXECUTOR_SRC}
|
||||||
|
${CLANG_UML_TEST_THREAD_POOL_EXECUTOR_HEADER})
|
||||||
|
|
||||||
|
target_link_libraries(test_thread_pool_executor
|
||||||
|
PRIVATE
|
||||||
|
${LIBCLANG_LIBRARIES}
|
||||||
|
${YAML_CPP_LIBRARIES}
|
||||||
|
spdlog::spdlog clang-umllib cppast)
|
||||||
|
|
||||||
add_executable(test_cases
|
add_executable(test_cases
|
||||||
${CLANG_UML_TEST_CASES_SRC}
|
${CLANG_UML_TEST_CASES_SRC}
|
||||||
${CLANG_UML_TEST_CASES_HEADER})
|
${CLANG_UML_TEST_CASES_HEADER})
|
||||||
@@ -125,4 +142,6 @@ endforeach()
|
|||||||
add_test(NAME test_util COMMAND test_util)
|
add_test(NAME test_util COMMAND test_util)
|
||||||
add_test(NAME test_decorator_parser COMMAND test_decorator_parser)
|
add_test(NAME test_decorator_parser COMMAND test_decorator_parser)
|
||||||
add_test(NAME test_config COMMAND test_config)
|
add_test(NAME test_config COMMAND test_config)
|
||||||
|
add_test(NAME test_model COMMAND test_model)
|
||||||
|
add_test(NAME test_thread_pool_executor COMMAND test_thread_pool_executor)
|
||||||
add_test(NAME test_cases COMMAND test_cases)
|
add_test(NAME test_cases COMMAND test_cases)
|
||||||
|
|||||||
@@ -222,10 +222,7 @@ int main(int argc, char *argv[])
|
|||||||
if (returnCode != 0)
|
if (returnCode != 0)
|
||||||
return returnCode;
|
return returnCode;
|
||||||
|
|
||||||
if (debug_log) {
|
clanguml::util::setup_logging(debug_log);
|
||||||
spdlog::default_logger_raw()->set_level(spdlog::level::debug);
|
|
||||||
spdlog::default_logger_raw()->set_pattern("[%l] %v");
|
|
||||||
}
|
|
||||||
|
|
||||||
return session.run();
|
return session.run();
|
||||||
}
|
}
|
||||||
|
|||||||
45
tests/test_thread_pool_executor.cc
Normal file
45
tests/test_thread_pool_executor.cc
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* tests/test_thread_pool_executor.cc
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021-2022 Bartek Kryza <bkryza@gmail.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
|
||||||
|
#include "catch.h"
|
||||||
|
|
||||||
|
#include "util/thread_pool_executor.h"
|
||||||
|
|
||||||
|
TEST_CASE("Test thread_pool_executor", "[unit-test]")
|
||||||
|
{
|
||||||
|
using clanguml::util::thread_pool_executor;
|
||||||
|
|
||||||
|
thread_pool_executor pool{4};
|
||||||
|
|
||||||
|
std::atomic_int counter{0};
|
||||||
|
|
||||||
|
std::vector<std::future<void>> futs;
|
||||||
|
|
||||||
|
const unsigned int kTaskCount = 1000;
|
||||||
|
|
||||||
|
for (auto i = 0U; i < kTaskCount; i++) {
|
||||||
|
futs.emplace_back(pool.add([&counter]() { counter++; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &f : futs) {
|
||||||
|
f.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(counter == kTaskCount);
|
||||||
|
}
|
||||||
2
thirdparty/cppast
vendored
2
thirdparty/cppast
vendored
Submodule thirdparty/cppast updated: 7c460cccc3...87beb6e2b8
Reference in New Issue
Block a user