From ace300d8d7df2b7bd4171865fb1078c296756d5c Mon Sep 17 00:00:00 2001 From: Avi Levin Date: Sun, 28 Jun 2015 15:53:18 +0300 Subject: [PATCH] Eolian: adding new tool for extracting info on eolian classes Eo_Info is a tool aimed to show specific Eolian information about Eo classes. It enables easily graphical/text representation of chosen Eo classes. Eolian is used to get this information. The output is a .dot file, convertible to other formats with Graphviz. Write: "eolian_info -h" for more info. --- src/Makefile_Eolian.am | 10 + src/bin/eolian/eolian_info_main.c | 569 ++++++++++++++++++++++++++++++ 2 files changed, 579 insertions(+) create mode 100644 src/bin/eolian/eolian_info_main.c diff --git a/src/Makefile_Eolian.am b/src/Makefile_Eolian.am index a5635fd179..775f8451f2 100644 --- a/src/Makefile_Eolian.am +++ b/src/Makefile_Eolian.am @@ -66,6 +66,16 @@ bin_eolian_eolian_gen_CPPFLAGS = -I$(top_builddir)/src/lib/efl @EOLIAN_CFLAGS@ bin_eolian_eolian_gen_LDADD = @USE_EOLIAN_LIBS@ bin_eolian_eolian_gen_DEPENDENCIES = @USE_EOLIAN_INTERNAL_LIBS@ + +bin_PROGRAMS += \ + bin/eolian/eolian_info + +bin_eolian_eolian_info_SOURCES = \ + bin/eolian/eolian_info_main.c + +bin_eolian_eolian_info_CPPFLAGS = -I$(top_builddir)/src/lib/efl @EOLIAN_CFLAGS@ +bin_eolian_eolian_info_LDADD = @USE_EOLIAN_LIBS@ + ### Helper for other modules using Eolian include Makefile_Eolian_Helper.am diff --git a/src/bin/eolian/eolian_info_main.c b/src/bin/eolian/eolian_info_main.c new file mode 100644 index 0000000000..8c7ae04eff --- /dev/null +++ b/src/bin/eolian/eolian_info_main.c @@ -0,0 +1,569 @@ +/** +* Eolian_Info is a tool aimed to show specific Eolian information about Eo classes. +* It enables easily graphical/text representation of chosen Eo classes. +* Eolian is used to get this information. +* The output is a .dot file, convertible to other formats with Graphviz. +* To get help about the supported commands run "$ eolian_info -h". +* +* Examples: +* +* To Get dot representation of inheritance tree of elm_layout.eo till depth 3. +* Using system Eo files (used when no include is defined). +* $ eolian_info -d 3 -o test.dot elm_layout.eo +* +* To Get dot representation of inheritors tree of elm_layout.eo and elm_button.eo till depth 3. +* $ eolian_info -d 3 -n -i -o test.dot elm_layout.eo Elm.Button +* +* To Get dot representation of inheritance tree of elm_layout.eo till depth 3. +* Using user defined Eo files dirs. +* $ eolian_info -d 3 -o test.dot -I /home/avilog/git/efl/efl/src/lib/ -I /home/avilog/git/efl/elementary/src/lib/ elm_layout.eo +* +* Other: +* +* If you have Graphviz installed, you can convert the dot file to many other formats: +* SVG: +* dot -Tsvg test.dot -o test.svg +* PNG: +* dot -Tpng test.dot -o test.png +* +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include + +int _eolian_info_log_dom; + +#ifdef ERR +# undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eolian_info_log_dom, __VA_ARGS__) + +#ifdef DBG +# undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eolian_info_log_dom, __VA_ARGS__) + +#ifdef INF +# undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_eolian_info_log_dom, __VA_ARGS__) + +#ifdef WRN +# undef WRN +#endif +#define WRN(...) EINA_LOG_DOM_WARN(_eolian_info_log_dom, __VA_ARGS__) + +#ifdef CRIT +# undef CRIT +#endif +#define CRIT(...) EINA_LOG_DOM_CRIT(_eolian_info_log_dom, __VA_ARGS__) + +#define MAX_CHARS_FILTER 45 + +static void _file_filter(const Eolian_Class *ekl, Eina_Strbuf *buf); +static void _event_filter(const Eolian_Class *ekl, Eina_Strbuf *buf); + +FILE* _file = NULL; +Eina_Inlist *classes_edges_list = NULL; + +typedef struct +{ + const char* name; + void (*func)(const Eolian_Class*, Eina_Strbuf *); + Eina_Bool active; +} _filter; + +static _filter _filters[] = { + {"events", _event_filter, EINA_FALSE}, + {"file", _file_filter, EINA_FALSE}, + {NULL, NULL, EINA_FALSE} +}; + +typedef struct +{ + EINA_INLIST; + const char *class, *parent; +} _Class_Node; + +static void +_file_filter(const Eolian_Class *ekl, Eina_Strbuf *buf) +{ + Eina_Stringshare* class_file = eolian_class_file_get(ekl); + + eina_strbuf_append(buf, "Class file: "); + eina_strbuf_append(buf, class_file); +} + +static void +_event_filter(const Eolian_Class *ekl, Eina_Strbuf *buf) +{ + Eina_Iterator *lst_events = eolian_class_events_get(ekl); + const Eolian_Event* event; + const char* event_title = "Events: "; + int dist_start = strlen(event_title)+3; + + if(lst_events){ + eina_strbuf_append(buf, event_title); + } + + int chars = 0; + EINA_ITERATOR_FOREACH(lst_events, event){ + const char* event_name = eolian_event_name_get(event); + chars += strlen(event_name)+1; + if(chars-3 + dist_start > MAX_CHARS_FILTER) + { + /* willl write in a new line */ + chars = strlen(event_name)+1; + eina_strbuf_append(buf, "\\l"); + for (int i = 0; i < dist_start; i++, eina_strbuf_append(buf, " ")); + } + eina_strbuf_append(buf, event_name); + eina_strbuf_append(buf, " "); + } + + eina_iterator_free(lst_events); +} + +static void +_print_class(const char *class_name) +{ + const Eolian_Class *ekl = eolian_class_get_by_name(class_name); + const char* desc = eolian_class_description_get(ekl); + fprintf(_file, "\"%s\" [tooltip= \"%s\" ", + class_name, ( desc ? eina_str_escape(desc) : class_name)); + + _filter *current_f = _filters; + int i = 0; + + fprintf(_file, "label=\"{"); + +/* Allways print class name */ + fprintf(_file, " ", i); + i++; + fprintf(_file, "%s\n", class_name); + + + Eina_Strbuf *buf = eina_strbuf_new(); + + while(current_f->name != NULL ) + { + if(current_f->active) + { + current_f->func(ekl, buf); + if(eina_strbuf_length_get(buf)>0){ + if(i>0) + fprintf(_file, "|"); + fflush(_file); + fprintf(_file, " ", i); + i++; + + fprintf(_file, "%s\\l", eina_strbuf_string_get(buf)); + eina_strbuf_reset(buf); + } + + } + + current_f++; + } + eina_strbuf_free(buf); + fprintf(_file, "}\"]\n"); +} + +static void +_print_graph() +{ + _Class_Node *node = NULL; + Eina_Inlist *nnode; + Eina_Hash *classes_hash = NULL; + classes_hash = eina_hash_string_superfast_new(NULL); + + fprintf(_file, "digraph \"Best Eo Graph Ever\" { \n\ + fontname = \"Bitstream Vera Sans\" \n\ + fontsize = 8 \n\ + \n\ + node [ \n\ + fontname = \"Bitstream Vera Sans\"\n \ + fontsize = 8\n \ + shape = \"record\"\n \ + ]\n \ + \n\ + edge [\n\ + arrowtail = \"empty\" \n\ + ]\n"); + + /* print classes info */ + EINA_INLIST_FOREACH_SAFE(classes_edges_list, nnode, node) + { + if(node->class && !eina_hash_find(classes_hash, node->class)) + { + _print_class(node->class); + eina_hash_add(classes_hash, node->class, node->class); + } + if(node->parent && !eina_hash_find(classes_hash, node->parent)) + { + _print_class(node->parent); + eina_hash_add(classes_hash, node->parent, node->parent); + } + } + eina_hash_free(classes_hash); + + /* mark the roots */ + fprintf(_file, "{rank=same; "); + EINA_INLIST_FOREACH_SAFE(classes_edges_list, nnode, node) + { + if(!node->parent) + { + fprintf(_file, "\"%s\" ", node->class); + } + } + fprintf(_file, "}\n"); + +/*print classes edges */ + EINA_INLIST_FOREACH_SAFE(classes_edges_list, nnode, node) + { + if(node->parent && node->class) + fprintf(_file, "\"%s\" -> \"%s\"\n [dir=back]", node->parent, node->class); + } + + fprintf(_file, "}\n"); + + /* Delete the inlist */ + EINA_INLIST_FOREACH_SAFE(classes_edges_list, nnode, node) + { + classes_edges_list = eina_inlist_remove(classes_edges_list, + EINA_INLIST_GET(node)); + + free(node); + } +} + +static void +_add_class_edge(const char *class_name, const char *parent_name) +{ + _Class_Node* d; + _Class_Node *node = NULL; + Eina_Inlist *nnode; + + /* dont add double edges */ + if(parent_name) + EINA_INLIST_FOREACH_SAFE(classes_edges_list, nnode, node) + { + if((node->parent && !strcmp(parent_name, node->parent)) && + !strcmp(class_name, node->class)) + return; + } + + d = malloc(sizeof(*d)); + d->parent = parent_name; + d->class = class_name; + + classes_edges_list = eina_inlist_append(classes_edges_list, EINA_INLIST_GET(d)); +} + +static void +_inherit_tree_scope_rec(const Eolian_Class *ekl, int depth) +{ + if(depth<= 0 ) + return; + + Eina_Iterator *lst = eolian_class_inherits_get(ekl); + const char* class_name = eolian_class_full_name_get(ekl); + const char* ekl_name; + + EINA_ITERATOR_FOREACH(lst, ekl_name) + { + ekl=eolian_class_get_by_name(ekl_name); + + _add_class_edge( + class_name, eolian_class_full_name_get(ekl)); + + _inherit_tree_scope_rec(ekl, depth - 1); + } + eina_iterator_free(lst); +} + +static void +_inherit_tree_scope(const Eolian_Class *ekl, int depth_max) +{ + if(depth_max<= 0 ) + return; + + _add_class_edge( + eolian_class_full_name_get(ekl), NULL); + + _inherit_tree_scope_rec(ekl, depth_max); +} + +static int +_inherits_from_scope_recur(const Eolian_Class *ekl, + Eina_Hash *classes_hash, const char *des_class_name, int depth_max) +{ + const char *class_name = eolian_class_full_name_get(ekl); + if(class_name == des_class_name) + return 1; + + int min_found_depth = -1; + Eina_Iterator *lst_i = eolian_class_inherits_get(ekl); + const char *ekl_i; + + if(!lst_i) return -1; + + EINA_ITERATOR_FOREACH(lst_i, ekl_i) + { + ekl = eolian_class_get_by_name(ekl_i); + void *hash = eina_hash_find(classes_hash, ekl); + int depth_class = -1; + if(!hash) + { + depth_class = _inherits_from_scope_recur( + ekl, classes_hash, des_class_name, depth_max); + eina_hash_add(classes_hash, ekl, (void*)(uintptr_t)depth_class ); + + } + else depth_class = (int)(uintptr_t)hash; + + if(depth_class>=0){ + if(depth_class<=depth_max){ + _add_class_edge( + class_name, ekl_i); + } + if((min_found_depth == -1) ||(min_found_depth>depth_class)) + min_found_depth = depth_class; + } + } + eina_iterator_free(lst_i); + + return min_found_depth>=0 ? min_found_depth+1 : -1; +} + +static void +_inherits_from_scope(const Eolian_Class *ekl, int depth_max) +{ + const char* class_name = eolian_class_full_name_get(ekl); + if(depth_max<= 0 ) + return; + + Eina_Hash *classes_hash = eina_hash_string_superfast_new(NULL); + + _add_class_edge( + class_name, NULL); + + Eina_Iterator *lst = eolian_all_classes_get(); + + EINA_ITERATOR_FOREACH(lst, ekl) + { + if(!eina_hash_find(classes_hash, ekl)) + { + _inherits_from_scope_recur( + ekl, classes_hash, class_name, depth_max); + } + } + + eina_iterator_free(lst); +} + +static void +_print_help(const char *program) +{ + printf("Usage: %s [options] class_path/class_name...\n", program); + printf("Options:\n"); + printf(" --help/-h Display this information\n"); + printf(" --no-inheritance/-n By default, we create the Inheritance tree, this flag disables it\n"); + printf(" --inheritors/-i Add the Inheritors tree (classes that inherit from this class)\n"); + printf(" --output/-o Set output filename to \n"); + printf(" --depth/-d Set to be the the max depth (up and down from the class of the printed tree)\n"); + printf(" --include/-I Add to the list of include\n" \ + " dirs for scanning. If no directory included we scan the system dir.\n"); + printf(" --filters/-f Set filters (the info of the class that will be printed)\n" \ + " as a comma-separated list of:\n" \ + " "); + _filter *current_f = _filters; + while(current_f->name != NULL ) printf("%s, ", current_f++->name); + printf("\b\b \n"); + + printf("\nFor example:\n"); + printf("eo_info_cmd -d 3 -o test.dot -f events,file elm_layout.eo Elm.Button\n"); +} + +int main(int argc, char **argv) +{ + int ret = 1; + int max_depth = 0; + char *output = NULL, *filter = NULL; + Eina_Bool help = EINA_FALSE, includes = EINA_FALSE; + Eina_Bool no_inheritance = EINA_FALSE, inheritors = EINA_FALSE; + Eina_Bool no_input = EINA_TRUE; + + eina_init(); + eolian_init(); + + const char *log_dom = "eolian_info"; + _eolian_info_log_dom = eina_log_domain_register(log_dom, EINA_COLOR_GREEN); + if (_eolian_info_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: %s", log_dom); + goto end; + } + + static struct option long_options[] = + { + /* These options set a flag. */ + {"help", no_argument, 0, 'h'}, + {"no-inheritance", no_argument, 0, 'n'}, + {"inheritors", no_argument, 0, 'i'}, + {"output", required_argument, 0, 'o'}, + {"depth", required_argument, 0, 'd'}, + {"filters", required_argument, 0, 'f'}, + {"Include", required_argument, 0, 'I'}, + {0, 0, 0, 0} + }; + int long_index = 0, opt; + while ((opt = getopt_long(argc, argv,"hnio:d:f:I:", long_options, &long_index )) != -1) + { + switch (opt) { + case 0: break; + case 'h': help = EINA_TRUE; break; + case 'o': + { + output = optarg; + + break; + } + case 'd': + { + max_depth = atoi(optarg); + break; + } + case 'n': + { + no_inheritance = EINA_TRUE; + + break; + } + case 'i': + { + inheritors = EINA_TRUE; + + break; + } + case 'I': + { + includes = EINA_TRUE; + const char *dir = optarg; + if (!eolian_directory_scan(dir))//todo works even if dir dont exist + { + ERR("Failed to scan %s", dir); + goto end; + } + break; + } + case 'f': + { + filter = optarg; + + break; + } + default: { + ERR("Unrecognized option %s\n", argv[optind-1]); + help = EINA_TRUE; + } + } + } + int name_start = optind++, name_end = argc; + + if (help) + { + _print_help(argv[0]); + goto end; + } + + if(filter) + { + unsigned int elements = 0, i; + char **f_names = eina_str_split_full(filter, ",", 100, &elements); + + for(i = 0; i < elements; i++)/* go over all given filter names*/ + { + + _filter *current_f = _filters; + + while(current_f->name != NULL ) + { + + if(!strcmp(current_f->name, f_names[i])) + { + current_f->active = EINA_TRUE; + break; + } + + current_f++; + } + } + } + + if(!includes) eolian_system_directory_scan(); + eolian_all_eo_files_parse(); + while(name_start < name_end) + { + char *class_path = argv[name_start++]; + const Eolian_Class *ekl = NULL; + if(eina_str_has_extension(class_path, ".eo")) + { + ekl = eolian_class_get_by_file(class_path); + } + else + { + ekl = eolian_class_get_by_name(class_path); + } + if(!ekl) + { + ERR("Class %s not found", class_path); + goto end; + } + no_input = EINA_FALSE; + + if(!no_inheritance) + { + _inherit_tree_scope(ekl, max_depth); + } + if(inheritors) + { + _inherits_from_scope(ekl, max_depth); + } + + } + if (no_input) + { + ERR("No input files given"); + _print_help(argv[0]); + goto end; + } + _file = stdout; + if(output) + { + _file = fopen(output, "w"); + if (_file == NULL) + { + ERR("Error opening file!\n"); + goto end; + } + } + _print_graph(); + if(output) + fclose(_file); + +end: + + eina_log_timing(_eolian_info_log_dom, + EINA_LOG_STATE_START, + EINA_LOG_STATE_SHUTDOWN); + eina_log_domain_unregister(_eolian_info_log_dom); + _eolian_info_log_dom = -1; + + eolian_shutdown(); + eina_shutdown(); + return ret; +}