summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvi Levin <avi.levin@samsung.com>2015-06-28 15:53:18 +0300
committerAvi Levin <avi.levin@samsung.com>2015-06-29 09:43:45 +0300
commitace300d8d7df2b7bd4171865fb1078c296756d5c (patch)
tree6363fdbb4982bb5480a2bf5ac65d7f303e3c84c0
parent7c2690eac062881e29ecd466957050c9219f7030 (diff)
Eolian: adding new tool for extracting info on eolian classesdevs/avilog/adding_eolian_info
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.
-rw-r--r--src/Makefile_Eolian.am10
-rw-r--r--src/bin/eolian/eolian_info_main.c569
2 files changed, 579 insertions, 0 deletions
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@
66bin_eolian_eolian_gen_LDADD = @USE_EOLIAN_LIBS@ 66bin_eolian_eolian_gen_LDADD = @USE_EOLIAN_LIBS@
67bin_eolian_eolian_gen_DEPENDENCIES = @USE_EOLIAN_INTERNAL_LIBS@ 67bin_eolian_eolian_gen_DEPENDENCIES = @USE_EOLIAN_INTERNAL_LIBS@
68 68
69
70bin_PROGRAMS += \
71 bin/eolian/eolian_info
72
73bin_eolian_eolian_info_SOURCES = \
74 bin/eolian/eolian_info_main.c
75
76bin_eolian_eolian_info_CPPFLAGS = -I$(top_builddir)/src/lib/efl @EOLIAN_CFLAGS@
77bin_eolian_eolian_info_LDADD = @USE_EOLIAN_LIBS@
78
69### Helper for other modules using Eolian 79### Helper for other modules using Eolian
70include Makefile_Eolian_Helper.am 80include Makefile_Eolian_Helper.am
71 81
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 @@
1/**
2* Eolian_Info is a tool aimed to show specific Eolian information about Eo classes.
3* It enables easily graphical/text representation of chosen Eo classes.
4* Eolian is used to get this information.
5* The output is a .dot file, convertible to other formats with Graphviz.
6* To get help about the supported commands run "$ eolian_info -h".
7*
8* Examples:
9*
10* To Get dot representation of inheritance tree of elm_layout.eo till depth 3.
11* Using system Eo files (used when no include is defined).
12* $ eolian_info -d 3 -o test.dot elm_layout.eo
13*
14* To Get dot representation of inheritors tree of elm_layout.eo and elm_button.eo till depth 3.
15* $ eolian_info -d 3 -n -i -o test.dot elm_layout.eo Elm.Button
16*
17* To Get dot representation of inheritance tree of elm_layout.eo till depth 3.
18* Using user defined Eo files dirs.
19* $ 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
20*
21* Other:
22*
23* If you have Graphviz installed, you can convert the dot file to many other formats:
24* SVG:
25* dot -Tsvg test.dot -o test.svg
26* PNG:
27* dot -Tpng test.dot -o test.png
28*
29*/
30
31#define _GNU_SOURCE
32#include <stdio.h>
33#include <getopt.h>
34#include <Eina.h>
35#include <Eolian.h>
36
37int _eolian_info_log_dom;
38
39#ifdef ERR
40# undef ERR
41#endif
42#define ERR(...) EINA_LOG_DOM_ERR(_eolian_info_log_dom, __VA_ARGS__)
43
44#ifdef DBG
45# undef DBG
46#endif
47#define DBG(...) EINA_LOG_DOM_DBG(_eolian_info_log_dom, __VA_ARGS__)
48
49#ifdef INF
50# undef INF
51#endif
52#define INF(...) EINA_LOG_DOM_INFO(_eolian_info_log_dom, __VA_ARGS__)
53
54#ifdef WRN
55# undef WRN
56#endif
57#define WRN(...) EINA_LOG_DOM_WARN(_eolian_info_log_dom, __VA_ARGS__)
58
59#ifdef CRIT
60# undef CRIT
61#endif
62#define CRIT(...) EINA_LOG_DOM_CRIT(_eolian_info_log_dom, __VA_ARGS__)
63
64#define MAX_CHARS_FILTER 45
65
66static void _file_filter(const Eolian_Class *ekl, Eina_Strbuf *buf);
67static void _event_filter(const Eolian_Class *ekl, Eina_Strbuf *buf);
68
69FILE* _file = NULL;
70Eina_Inlist *classes_edges_list = NULL;
71
72typedef struct
73{
74 const char* name;
75 void (*func)(const Eolian_Class*, Eina_Strbuf *);
76 Eina_Bool active;
77} _filter;
78
79static _filter _filters[] = {
80 {"events", _event_filter, EINA_FALSE},
81 {"file", _file_filter, EINA_FALSE},
82 {NULL, NULL, EINA_FALSE}
83};
84
85typedef struct
86{
87 EINA_INLIST;
88 const char *class, *parent;
89} _Class_Node;
90
91static void
92_file_filter(const Eolian_Class *ekl, Eina_Strbuf *buf)
93{
94 Eina_Stringshare* class_file = eolian_class_file_get(ekl);
95
96 eina_strbuf_append(buf, "Class file: ");
97 eina_strbuf_append(buf, class_file);
98}
99
100static void
101_event_filter(const Eolian_Class *ekl, Eina_Strbuf *buf)
102{
103 Eina_Iterator *lst_events = eolian_class_events_get(ekl);
104 const Eolian_Event* event;
105 const char* event_title = "Events: ";
106 int dist_start = strlen(event_title)+3;
107
108 if(lst_events){
109 eina_strbuf_append(buf, event_title);
110 }
111
112 int chars = 0;
113 EINA_ITERATOR_FOREACH(lst_events, event){
114 const char* event_name = eolian_event_name_get(event);
115 chars += strlen(event_name)+1;
116 if(chars-3 + dist_start > MAX_CHARS_FILTER)
117 {
118 /* willl write in a new line */
119 chars = strlen(event_name)+1;
120 eina_strbuf_append(buf, "\\l");
121 for (int i = 0; i < dist_start; i++, eina_strbuf_append(buf, "&nbsp;"));
122 }
123 eina_strbuf_append(buf, event_name);
124 eina_strbuf_append(buf, " ");
125 }
126
127 eina_iterator_free(lst_events);
128}
129
130static void
131_print_class(const char *class_name)
132{
133 const Eolian_Class *ekl = eolian_class_get_by_name(class_name);
134 const char* desc = eolian_class_description_get(ekl);
135 fprintf(_file, "\"%s\" [tooltip= \"%s\" ",
136 class_name, ( desc ? eina_str_escape(desc) : class_name));
137
138 _filter *current_f = _filters;
139 int i = 0;
140
141 fprintf(_file, "label=\"{");
142
143/* Allways print class name */
144 fprintf(_file, "<f%d> ", i);
145 i++;
146 fprintf(_file, "%s\n", class_name);
147
148
149 Eina_Strbuf *buf = eina_strbuf_new();
150
151 while(current_f->name != NULL )
152 {
153 if(current_f->active)
154 {
155 current_f->func(ekl, buf);
156 if(eina_strbuf_length_get(buf)>0){
157 if(i>0)
158 fprintf(_file, "|");
159 fflush(_file);
160 fprintf(_file, "<f%d> ", i);
161 i++;
162
163 fprintf(_file, "%s\\l", eina_strbuf_string_get(buf));
164 eina_strbuf_reset(buf);
165 }
166
167 }
168
169 current_f++;
170 }
171 eina_strbuf_free(buf);
172 fprintf(_file, "}\"]\n");
173}
174
175static void
176_print_graph()
177{
178 _Class_Node *node = NULL;
179 Eina_Inlist *nnode;
180 Eina_Hash *classes_hash = NULL;
181 classes_hash = eina_hash_string_superfast_new(NULL);
182
183 fprintf(_file, "digraph \"Best Eo Graph Ever\" { \n\
184 fontname = \"Bitstream Vera Sans\" \n\
185 fontsize = 8 \n\
186 \n\
187 node [ \n\
188 fontname = \"Bitstream Vera Sans\"\n \
189 fontsize = 8\n \
190 shape = \"record\"\n \
191 ]\n \
192 \n\
193 edge [\n\
194 arrowtail = \"empty\" \n\
195 ]\n");
196
197 /* print classes info */
198 EINA_INLIST_FOREACH_SAFE(classes_edges_list, nnode, node)
199 {
200 if(node->class && !eina_hash_find(classes_hash, node->class))
201 {
202 _print_class(node->class);
203 eina_hash_add(classes_hash, node->class, node->class);
204 }
205 if(node->parent && !eina_hash_find(classes_hash, node->parent))
206 {
207 _print_class(node->parent);
208 eina_hash_add(classes_hash, node->parent, node->parent);
209 }
210 }
211 eina_hash_free(classes_hash);
212
213 /* mark the roots */
214 fprintf(_file, "{rank=same; ");
215 EINA_INLIST_FOREACH_SAFE(classes_edges_list, nnode, node)
216 {
217 if(!node->parent)
218 {
219 fprintf(_file, "\"%s\" ", node->class);
220 }
221 }
222 fprintf(_file, "}\n");
223
224/*print classes edges */
225 EINA_INLIST_FOREACH_SAFE(classes_edges_list, nnode, node)
226 {
227 if(node->parent && node->class)
228 fprintf(_file, "\"%s\" -> \"%s\"\n [dir=back]", node->parent, node->class);
229 }
230
231 fprintf(_file, "}\n");
232
233 /* Delete the inlist */
234 EINA_INLIST_FOREACH_SAFE(classes_edges_list, nnode, node)
235 {
236 classes_edges_list = eina_inlist_remove(classes_edges_list,
237 EINA_INLIST_GET(node));
238
239 free(node);
240 }
241}
242
243static void
244_add_class_edge(const char *class_name, const char *parent_name)
245{
246 _Class_Node* d;
247 _Class_Node *node = NULL;
248 Eina_Inlist *nnode;
249
250 /* dont add double edges */
251 if(parent_name)
252 EINA_INLIST_FOREACH_SAFE(classes_edges_list, nnode, node)
253 {
254 if((node->parent && !strcmp(parent_name, node->parent)) &&
255 !strcmp(class_name, node->class))
256 return;
257 }
258
259 d = malloc(sizeof(*d));
260 d->parent = parent_name;
261 d->class = class_name;
262
263 classes_edges_list = eina_inlist_append(classes_edges_list, EINA_INLIST_GET(d));
264}
265
266static void
267_inherit_tree_scope_rec(const Eolian_Class *ekl, int depth)
268{
269 if(depth<= 0 )
270 return;
271
272 Eina_Iterator *lst = eolian_class_inherits_get(ekl);
273 const char* class_name = eolian_class_full_name_get(ekl);
274 const char* ekl_name;
275
276 EINA_ITERATOR_FOREACH(lst, ekl_name)
277 {
278 ekl=eolian_class_get_by_name(ekl_name);
279
280 _add_class_edge(
281 class_name, eolian_class_full_name_get(ekl));
282
283 _inherit_tree_scope_rec(ekl, depth - 1);
284 }
285 eina_iterator_free(lst);
286}
287
288static void
289_inherit_tree_scope(const Eolian_Class *ekl, int depth_max)
290{
291 if(depth_max<= 0 )
292 return;
293
294 _add_class_edge(
295 eolian_class_full_name_get(ekl), NULL);
296
297 _inherit_tree_scope_rec(ekl, depth_max);
298}
299
300static int
301_inherits_from_scope_recur(const Eolian_Class *ekl,
302 Eina_Hash *classes_hash, const char *des_class_name, int depth_max)
303{
304 const char *class_name = eolian_class_full_name_get(ekl);
305 if(class_name == des_class_name)
306 return 1;
307
308 int min_found_depth = -1;
309 Eina_Iterator *lst_i = eolian_class_inherits_get(ekl);
310 const char *ekl_i;
311
312 if(!lst_i) return -1;
313
314 EINA_ITERATOR_FOREACH(lst_i, ekl_i)
315 {
316 ekl = eolian_class_get_by_name(ekl_i);
317 void *hash = eina_hash_find(classes_hash, ekl);
318 int depth_class = -1;
319 if(!hash)
320 {
321 depth_class = _inherits_from_scope_recur(
322 ekl, classes_hash, des_class_name, depth_max);
323 eina_hash_add(classes_hash, ekl, (void*)(uintptr_t)depth_class );
324
325 }
326 else depth_class = (int)(uintptr_t)hash;
327
328 if(depth_class>=0){
329 if(depth_class<=depth_max){
330 _add_class_edge(
331 class_name, ekl_i);
332 }
333 if((min_found_depth == -1) ||(min_found_depth>depth_class))
334 min_found_depth = depth_class;
335 }
336 }
337 eina_iterator_free(lst_i);
338
339 return min_found_depth>=0 ? min_found_depth+1 : -1;
340}
341
342static void
343_inherits_from_scope(const Eolian_Class *ekl, int depth_max)
344{
345 const char* class_name = eolian_class_full_name_get(ekl);
346 if(depth_max<= 0 )
347 return;
348
349 Eina_Hash *classes_hash = eina_hash_string_superfast_new(NULL);
350
351 _add_class_edge(
352 class_name, NULL);
353
354 Eina_Iterator *lst = eolian_all_classes_get();
355
356 EINA_ITERATOR_FOREACH(lst, ekl)
357 {
358 if(!eina_hash_find(classes_hash, ekl))
359 {
360 _inherits_from_scope_recur(
361 ekl, classes_hash, class_name, depth_max);
362 }
363 }
364
365 eina_iterator_free(lst);
366}
367
368static void
369_print_help(const char *program)
370{
371 printf("Usage: %s [options] class_path/class_name...\n", program);
372 printf("Options:\n");
373 printf(" --help/-h Display this information\n");
374 printf(" --no-inheritance/-n By default, we create the Inheritance tree, this flag disables it\n");
375 printf(" --inheritors/-i Add the Inheritors tree (classes that inherit from this class)\n");
376 printf(" --output/-o <outFile> Set output filename to <outFile>\n");
377 printf(" --depth/-d <deptth> Set <depth> to be the the max depth (up and down from the class of the printed tree)\n");
378 printf(" --include/-I <includeDir> Add <includeDir> to the list of include\n" \
379 " dirs for scanning. If no directory included we scan the system dir.\n");
380 printf(" --filters/-f <filter1,...> Set filters (the info of the class that will be printed)\n" \
381 " as a comma-separated list of:\n" \
382 " ");
383 _filter *current_f = _filters;
384 while(current_f->name != NULL ) printf("%s, ", current_f++->name);
385 printf("\b\b \n");
386
387 printf("\nFor example:\n");
388 printf("eo_info_cmd -d 3 -o test.dot -f events,file elm_layout.eo Elm.Button\n");
389}
390
391int main(int argc, char **argv)
392{
393 int ret = 1;
394 int max_depth = 0;
395 char *output = NULL, *filter = NULL;
396 Eina_Bool help = EINA_FALSE, includes = EINA_FALSE;
397 Eina_Bool no_inheritance = EINA_FALSE, inheritors = EINA_FALSE;
398 Eina_Bool no_input = EINA_TRUE;
399
400 eina_init();
401 eolian_init();
402
403 const char *log_dom = "eolian_info";
404 _eolian_info_log_dom = eina_log_domain_register(log_dom, EINA_COLOR_GREEN);
405 if (_eolian_info_log_dom < 0)
406 {
407 EINA_LOG_ERR("Could not register log domain: %s", log_dom);
408 goto end;
409 }
410
411 static struct option long_options[] =
412 {
413 /* These options set a flag. */
414 {"help", no_argument, 0, 'h'},
415 {"no-inheritance", no_argument, 0, 'n'},
416 {"inheritors", no_argument, 0, 'i'},
417 {"output", required_argument, 0, 'o'},
418 {"depth", required_argument, 0, 'd'},
419 {"filters", required_argument, 0, 'f'},
420 {"Include", required_argument, 0, 'I'},
421 {0, 0, 0, 0}
422 };
423 int long_index = 0, opt;
424 while ((opt = getopt_long(argc, argv,"hnio:d:f:I:", long_options, &long_index )) != -1)
425 {
426 switch (opt) {
427 case 0: break;
428 case 'h': help = EINA_TRUE; break;
429 case 'o':
430 {
431 output = optarg;
432
433 break;
434 }
435 case 'd':
436 {
437 max_depth = atoi(optarg);
438 break;
439 }
440 case 'n':
441 {
442 no_inheritance = EINA_TRUE;
443
444 break;
445 }
446 case 'i':
447 {
448 inheritors = EINA_TRUE;
449
450 break;
451 }
452 case 'I':
453 {
454 includes = EINA_TRUE;
455 const char *dir = optarg;
456 if (!eolian_directory_scan(dir))//todo works even if dir dont exist
457 {
458 ERR("Failed to scan %s", dir);
459 goto end;
460 }
461 break;
462 }
463 case 'f':
464 {
465 filter = optarg;
466
467 break;
468 }
469 default: {
470 ERR("Unrecognized option %s\n", argv[optind-1]);
471 help = EINA_TRUE;
472 }
473 }
474 }
475 int name_start = optind++, name_end = argc;
476
477 if (help)
478 {
479 _print_help(argv[0]);
480 goto end;
481 }
482
483 if(filter)
484 {
485 unsigned int elements = 0, i;
486 char **f_names = eina_str_split_full(filter, ",", 100, &elements);
487
488 for(i = 0; i < elements; i++)/* go over all given filter names*/
489 {
490
491 _filter *current_f = _filters;
492
493 while(current_f->name != NULL )
494 {
495
496 if(!strcmp(current_f->name, f_names[i]))
497 {
498 current_f->active = EINA_TRUE;
499 break;
500 }
501
502 current_f++;
503 }
504 }
505 }
506
507 if(!includes) eolian_system_directory_scan();
508 eolian_all_eo_files_parse();
509 while(name_start < name_end)
510 {
511 char *class_path = argv[name_start++];
512 const Eolian_Class *ekl = NULL;
513 if(eina_str_has_extension(class_path, ".eo"))
514 {
515 ekl = eolian_class_get_by_file(class_path);
516 }
517 else
518 {
519 ekl = eolian_class_get_by_name(class_path);
520 }
521 if(!ekl)
522 {
523 ERR("Class %s not found", class_path);
524 goto end;
525 }
526 no_input = EINA_FALSE;
527
528 if(!no_inheritance)
529 {
530 _inherit_tree_scope(ekl, max_depth);
531 }
532 if(inheritors)
533 {
534 _inherits_from_scope(ekl, max_depth);
535 }
536
537 }
538 if (no_input)
539 {
540 ERR("No input files given");
541 _print_help(argv[0]);
542 goto end;
543 }
544 _file = stdout;
545 if(output)
546 {
547 _file = fopen(output, "w");
548 if (_file == NULL)
549 {
550 ERR("Error opening file!\n");
551 goto end;
552 }
553 }
554 _print_graph();
555 if(output)
556 fclose(_file);
557
558end:
559
560 eina_log_timing(_eolian_info_log_dom,
561 EINA_LOG_STATE_START,
562 EINA_LOG_STATE_SHUTDOWN);
563 eina_log_domain_unregister(_eolian_info_log_dom);
564 _eolian_info_log_dom = -1;
565
566 eolian_shutdown();
567 eina_shutdown();
568 return ret;
569}