pax_global_header00006660000000000000000000000064133557345000014517gustar00rootroot0000000000000052 comment=656a5a996e2aa70db8c9f0bb506003a24f3666cb makefile2graph-2021.11.06/000077500000000000000000000000001335573450000147725ustar00rootroot00000000000000makefile2graph-2021.11.06/.gitignore000066400000000000000000000003161335573450000167620ustar00rootroot00000000000000# Object files *.o *.ko *.obj *.elf # Libraries *.lib *.a # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex *~ jeter* a.out make2graph makefile2graph-2021.11.06/.travis.yml000066400000000000000000000001601335573450000171000ustar00rootroot00000000000000language: C script: make test before_install: - sudo apt-get update -qq - sudo apt-get install graphviz makefile2graph-2021.11.06/LICENSE000066400000000000000000000020741335573450000160020ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Pierre Lindenbaum Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. makefile2graph-2021.11.06/Makefile000066400000000000000000000017011335573450000164310ustar00rootroot00000000000000prefix = /usr/local bindir = $(prefix)/bin sharedir = $(prefix)/share docdir = $(sharedir)/doc pkgdocdir = $(sharedir)/makefile2graph mandir = $(sharedir)/man man1dir = $(mandir)/man1 bin_PROGRAMS = make2graph bin_SCRIPTS = makefile2graph pkgdoc_DATA = LICENSE README.md screenshot.png man1_MANS = make2graph.1 makefile2graph.1 CFLAGS ?= -O3 -Wall .PHONY: all clean install test .DELETE_ON_ERROR: all: $(bin_PROGRAMS) clean: rm -f $(bin_PROGRAMS) install: install -d $(DESTDIR)$(bindir) $(DESTDIR)$(pkgdocdir) $(DESTDIR)$(man1dir) install $(bin_PROGRAMS) $(bin_SCRIPTS) $(DESTDIR)$(bindir) install $(pkgdoc_DATA) $(DESTDIR)$(pkgdocdir) install $(man1_MANS) $(DESTDIR)$(man1dir) test: all $(MAKE) -Bnd | ./make2graph | dot $(MAKE) -Bnd | ./make2graph --format x $(MAKE) -Bnd | ./make2graph --format l $(MAKE) -Bnd | ./make2graph --format e $(MAKE) -Bnd | ./make2graph --root PATH=.:$(PATH) ./makefile2graph PATH=.:$(PATH) ./makefile2graph -B makefile2graph-2021.11.06/README.md000066400000000000000000000215271335573450000162600ustar00rootroot00000000000000makefile2graph ============== Creates a graph of dependencies from GNU-Make ( http://www.gnu.org/software/make/manual/make.html ) Output is a graphviz-dot file ( http://www.graphviz.org/ ), a Gexf-XML file ( https://gephi.github.io/ ) or a list of the deepest independent targets that should be make. sub-makefiles are not supported. ![https://travis-ci.org/lindenb/makefile2graph.svg](https://travis-ci.org/lindenb/makefile2graph) ## History * 2014-12-31 added option `--format`, removed otpions 'x' and 'd' * 2014-12-22 added 'deep' output. I need this when I'm working on a cluster and I need to know the deepest independent targets that should be make. * 2014-10-07 print version * 2014-10-06 added --root option * 2014-09-17 added long_opt , options basename and suffix * 2014-09-16 fixed new format for GNU make v4.0 ## Screenshot ![screenshot.png](screenshot.png) ## Compilation ``` make ``` ## Options * -h|--help help (this screen) * -f|--format (format) * (d)ot graphiz dot output format (default). * (x)xml (g)exf XML output (gexf) * (E) print the deepest indepedent targets. * (L)ist all targets. * -s|--suffix only print file extension * -b|--basename only print file basename * -r|--root show root node * -v|--version print version ## Usage ``` make -Bnd | make2graph > output.dot ``` ``` make -Bnd | make2graph | dot -Tpng -o out.png ``` ``` make -Bnd | make2graph --format x > output.xml ``` ## Locale make2graph only parses english messages from GNU make. If you're using another locale, you should set `LC_ALL=C`. ## Examples ### Tabix 0.2.5 - DOT ```bash $ cd tabix-0.2.5 $ make -Bnd |make2graph ``` ```dot digraph G { n1[label="", color="green"]; n2[label="Makefile", color="green"]; n4[label="all", color="red"]; n3[label="all-recur", color="red"]; n23[label="bedidx.c", color="green"]; n22[label="bedidx.o", color="red"]; n9[label="bgzf.c", color="green"]; n10[label="bgzf.h", color="green"]; n8[label="bgzf.o", color="red"]; n27[label="bgzip", color="red"]; n29[label="bgzip.c", color="green"]; n28[label="bgzip.o", color="red"]; n18[label="index.c", color="green"]; n17[label="index.o", color="red"]; n20[label="khash.h", color="green"]; n16[label="knetfile.c", color="green"]; n11[label="knetfile.h", color="green"]; n15[label="knetfile.o", color="red"]; n24[label="kseq.h", color="green"]; n21[label="ksort.h", color="green"]; n13[label="kstring.c", color="green"]; n14[label="kstring.h", color="green"]; n12[label="kstring.o", color="red"]; n6[label="lib", color="red"]; n7[label="libtabix.a", color="red"]; n26[label="main.c", color="green"]; n25[label="main.o", color="red"]; n5[label="tabix", color="red"]; n19[label="tabix.h", color="green"]; n2 -> n1 ; n4 -> n1 ; n3 -> n1 ; n27 -> n4 ; n5 -> n4 ; n23 -> n22 ; n20 -> n22 ; n24 -> n22 ; n9 -> n8 ; n10 -> n8 ; n11 -> n8 ; n8 -> n27 ; n28 -> n27 ; n15 -> n27 ; n10 -> n28 ; n29 -> n28 ; n10 -> n17 ; n18 -> n17 ; n20 -> n17 ; n21 -> n17 ; n14 -> n17 ; n19 -> n17 ; n16 -> n15 ; n11 -> n15 ; n13 -> n12 ; n14 -> n12 ; n7 -> n6 ; n22 -> n7 ; n8 -> n7 ; n17 -> n7 ; n15 -> n7 ; n12 -> n7 ; n10 -> n25 ; n14 -> n25 ; n26 -> n25 ; n19 -> n25 ; n6 -> n5 ; n25 -> n5 ; } ``` ### Tabix 0.2.5 - XML ```bash $ cd tabix-0.2.5 $ make -Bnd |make2graph --format x ``` ```xml Pierre Lindenbaum Makefile Graph ``` ### Deep output deep output was needed when using a cluster: I needed a list of all deepest independant targets that must be re-built. For the following Makefile ```make NUMS=1 2 3 4 5 .PHONY:all clean %.c: %.b echo "$<" > $@ %.b: %.a echo "$<" > $@ %.a: echo "$@" > $@ all:$(addsuffix .c,${NUMS}) clean: rm -f $(foreach P,a b c,$(addsuffix .${P},${NUMS})) ``` make a few targets: ``` $ make 1.a 2.a 3.b 4.c echo "1.a" > 1.a echo "2.a" > 2.a echo "3.a" > 3.a echo "3.a" > 3.b echo "4.a" > 4.a echo "4.a" > 4.b echo "4.b" > 4.c rm 3.a 4.a 4.b ``` deep output : ``` $ make -nd all | ./make2graph --format e 1.b 2.b 3.c 5.a ``` build other targets ``` $ make 1.b 3.c echo "1.a" > 1.b echo "3.b" > 3.c ``` new deep output : ``` $ make -nd all | ./make2graph --format e 1.c 2.b 5.a ``` ## Gallery https://twitter.com/yokofakun/status/514329843065167872 ![workflow](https://pbs.twimg.com/media/ByNEQ7PCAAAxoBt.png) https://twitter.com/yokofakun/status/278086490809040896 ![workflow](https://pbs.twimg.com/media/A9v2MKXCAAA8hmJ.jpg) ## Misc

@yokofakun Using your https://t.co/Z1xg8dhW2r, and Graphviz 2.36 for OS X. Large graph. How can I scale it better? pic.twitter.com/d6ZQX2MnqH

— Lex Nederbragt (@lexnederbragt) August 20, 2014
> > try the gexf+ #gephi output or another grafviz algo like neato > ## See also * J4Make https://github.com/lindenb/j4make java equivalent of makefile2graph makefile2graph-2021.11.06/make2graph.1000066400000000000000000000013671335573450000171040ustar00rootroot00000000000000.\" This is a comment .\" Contact @yokofakun .TH make2graph 1 "17 Sept 2014" ".1" "Creates a graph of dependencies from GNU-Make" .SH NAME makefile2graph \- Creates a graph of dependencies from GNU-Make .SH SYNOPSIS make -Bnd | make2graph > output.dot .SH DESCRIPTION Creates a graph of dependencies from GNU-Make; Output is a graphiz-dot file or a Gexf-XML file. sub-makefiles are not supported. .SH OPTIONS .TP .B \-\^f formats :(x) XML output (gexf), (E) print a list of deepest targets, (l) print a list of targets, (d) dot output (default) .TP .B \-\^b print file basename .TP .B \-\^s print file extension .TP .B \-\^r show root node .TP .B \-\^v print version .SH SEE ALSO makefile2graph(1), make(1), dot(1) .SH AUTHOR Pierre Lindenbaum (@yokofakun) makefile2graph-2021.11.06/make2graph.c000066400000000000000000000372701335573450000171700ustar00rootroot00000000000000/* The MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. contact: Pierre Lindenbaum PhD @yokofakun History: * 2014 first commit * Sept 2014: fixed new format for GNU-Make v4. ( https://github.com/lindenb/makefile2graph/issues/1 ) * Sept 2014: added long_opt , options basename and suffix * Nov 2014: added option to hide ROOT node * Dec 2014: new output. Print the deepest independant targets * Desc 2014: MacOS bug, changed options */ #include #include #include #include #include #include #include #include /* version */ #define M2G_VERSION "1.5.0" #define OUT_OF_MEMORY do { fprintf(stderr,"%s: %d : OUT_OF_MEMORY.\n",__FILE__,__LINE__); exit(EXIT_FAILURE);} while(0) /* as https://github.com/lindenb/makefile2graph/issues/9 */ static char* StrNDup (const char *s, size_t n) { char *result; size_t len = strlen (s); if (n < len) len = n; result = (char *) malloc (len + 1); if (!result) return 0; result[len] = '\0'; return (char *) memcpy (result, s, len); } enum output_type { output_dot, output_gexf, output_deep, output_list }; /** a Target */ typedef struct target_t { /* target id */ size_t id; /* filename */ char* name; /* associated children, sorted by name */ struct target_t** children; /* number of children */ size_t n_children; /* target is dirty */ int must_remake; }Target,*TargetPtr; /** the Makefile graph */ typedef struct make2graph_t { /** all the targets , sorted by name */ TargetPtr* targets; /** number of target */ size_t target_count; /** root target */ TargetPtr root; /** target id-generator */ size_t id_generator; /** flag print only basename */ int print_basename_only; /** flag print only extension */ int print_suffix_only; /** show node https://github.com/lindenb/makefile2graph/issues/3 */ int show_root; }Graph,*GraphPtr; /** compare target by name */ static int TargetCmp(const void * a, const void * b) { return strcmp((*(TargetPtr*)a)->name,(*(TargetPtr*)b)->name); } /** creates a new target */ static TargetPtr TargetNew(GraphPtr graph,const char* name) { TargetPtr target=(TargetPtr)calloc(1,sizeof(Target)); if(target==NULL) OUT_OF_MEMORY; target->id=(++graph->id_generator); target->name=strdup(name); if(target->name==NULL) OUT_OF_MEMORY; return target; } /** add a children to the specified target */ static void TargetAddChildren(TargetPtr root, TargetPtr c) { TargetPtr* t=(TargetPtr*)bsearch((const void*)&c, (void*)root->children, root->n_children, sizeof(TargetPtr),TargetCmp); if(t!=0) return; root->children=realloc(root->children,sizeof(TargetPtr)*(root->n_children+1)); if(root->children==NULL) OUT_OF_MEMORY; root->children[root->n_children++]=c; qsort(root->children, root->n_children ,sizeof(TargetPtr) , TargetCmp); } /** does string starts with substring */ static int startsWith(const char* str,const char* pre) { size_t lenpre = strlen(pre), lenstr = strlen(str); return lenstr < lenpre ? 0 : strncmp(pre, str, lenpre) == 0; } static int endsWith(const char *str, const char *suffix) { if (!str || !suffix) return 0; size_t lenstr = strlen(str); size_t lensuffix = strlen(suffix); if (lensuffix > lenstr) return 0; return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; } /** extract filename between '`' and "'" * Make v4.0 changed this: the first separator is now "'" */ static char* targetName(const char* line) { char* p; char* b=strchr(line,'`'); if(b==NULL) b=strchr(line,'\'');//GNU make 4.0 char* e=( b==NULL ? NULL : strchr(b+1,'\'') ); if(b==NULL || e==NULL || b>e) { fprintf(stderr,"Cannot get target name in \"%s\".\n",line); exit(EXIT_FAILURE); } p= StrNDup(b+1,(e-b)-1); if(p==NULL) OUT_OF_MEMORY; return p; } /** get a label for this target name */ static char* targetLabel(GraphPtr g,const char* s) { char* p=(char*)s; if( g->print_basename_only ) { char* slash=strrchr(p,'/'); if(slash!=NULL) p=slash+1; } if( g->print_suffix_only ) { char* slash=strrchr(p,'.'); if(slash!=NULL) p=slash+1; } return p; } /** read line character by character */ static char* readline(FILE* in, size_t* level) { assert(level); *level= 0UL; int c; char* p=NULL; size_t len=0UL; if(feof(in)) return NULL; p=(char*)malloc(sizeof(char)); if(p==NULL) OUT_OF_MEMORY; while((c=fgetc(in))!=-1 && c!='\n') { p=(char*)realloc((char*)p,sizeof(char)*(len+2)); if(p==NULL) OUT_OF_MEMORY; if(!(len==0 && isspace(c))) /* trim on the fly */ { p[len++]=c; } else { (*level)++; } } p[len]=0; return p; } /** get target, create it it doesn't exist */ static TargetPtr GraphGetTarget(GraphPtr graph,const char* name) { TargetPtr t; Target key; TargetPtr pkey=&key; key.name=(char*)name; TargetPtr* prev=(TargetPtr*)bsearch( (const void*) &pkey, (void*)graph->targets, graph->target_count, sizeof(TargetPtr), TargetCmp ); if(prev!=0) { return *prev; } t=TargetNew(graph,name); graph->targets = (TargetPtr*)realloc( (void*)graph->targets, sizeof(TargetPtr)*(1+graph->target_count) ); if(graph->targets==NULL) OUT_OF_MEMORY; graph->targets[ graph->target_count ] = t; graph->target_count++; qsort(graph->targets, graph->target_count ,sizeof(TargetPtr) , TargetCmp); return t; } /** scan the makefile -nd output */ static void GraphScan(GraphPtr graph,TargetPtr root,FILE* in, size_t level) { char* line=NULL; char* makefile_name=NULL; size_t iLevel=0UL; while((line=readline(in, &iLevel))!=NULL) { if(startsWith(line,"Considering target file")) { char* tName=targetName(line); if(!graph->show_root && makefile_name!=NULL && strcmp(tName,makefile_name)==0) { free(tName); free(line); //skip lines size_t iLevelDump=0UL; while((line=readline(in, &iLevelDump))!=NULL) { if(startsWith(line,"Finished prerequisites of target file ") || endsWith(line, "was considered already.") ) { tName=targetName(line); free(line); if(strcmp(tName,makefile_name)==0) { free(tName); break; } free(tName); continue; } free(line); } continue; } TargetPtr child=GraphGetTarget(graph,tName); // fprintf(stderr, "a: %zu(%s)->%zu(%s)\n", level, root->name, iLevel, tName); free(tName); if(level+1 >= iLevel) { TargetAddChildren(root,child); // fprintf(stderr, "b: %zu(%s)->%zu(%s)\n", level, root->name, iLevel, child->name); GraphScan(graph,child,in,iLevel+1); } } else if(startsWith(line,"Must remake target ")) { char* tName=targetName(line); GraphGetTarget(graph,tName)->must_remake=1; free(tName); } else if(startsWith(line,"Pruning file ")) { char* tName=targetName(line); TargetAddChildren(root,GraphGetTarget(graph,tName)); free(tName); } else if( (startsWith(line,"Finished prerequisites of target file ") || endsWith(line, "was considered already.")) && (level+1 >= iLevel)) { char* tName=targetName(line); if(strcmp(tName,root->name)!=0) { fprintf(stderr,"expected %s got %s\n", root->name , line); exit(EXIT_FAILURE); } free(tName); free(line); break; } else if(startsWith(line,"Reading makefile ")) { free(makefile_name); makefile_name=targetName(line); } free(line); } free(makefile_name); } /** export a graphiz dot */ static void DumpGraphAsDot(GraphPtr g,FILE* out) { size_t i=0,j=0; fputs("digraph G {\n",out); for(i=0; i< g->target_count; ++i) { TargetPtr t= g->targets[i]; if( !g->show_root && t==g->root ) continue; if(t==g->root) { fprintf(out, "n%zu[shape=point, label=\"\"];\n", t->id ); } else { const char* label=targetLabel(g,t->name); fprintf(out, "n%zu[label=\"", t->id); while(*label) { if(*label=='\"') fputs("\\\"",out); else fputc(*label,out); label++; } fprintf(out, "\", color=\"%s\"];\n", (t->must_remake?"red":"green") ); } } for(i=0; i< g->target_count; ++i) { TargetPtr t= g->targets[i]; if( !g->show_root && t==g->root) continue; for(j=0; j< t->n_children; ++j) { TargetPtr c = t->children[j]; fprintf(out,"n%zu -> n%zu ; \n", c->id , t->id); } } fputs("}\n",out); } /** export a Gephi / Gexf */ void DumpGraphAsGexf(GraphPtr g,FILE* out) { size_t i=0,j=0,k=0UL; fputs("\n",out); fputs("\n",out); fputs(" \n",out); fputs(" https://github.com/lindenb/makefile2graph version:" M2G_VERSION "\n",out); fputs(" Creates a graph from a Makefile\n",out); fputs(" \n",out); fputs(" \n",out); fputs(" \n",out); fputs(" \n",out); for(i=0; i< g->target_count; ++i) { j=0UL; TargetPtr t= g->targets[i]; const char* label=targetLabel(g,t->name); if( !g->show_root && t==g->root ) continue; fprintf(out, " id); while(label[j]!=0) { switch(t->name[j]) { case '<': fputs("<",out);break; case '>': fputs(">",out);break; case '\"': fputs("'",out);break; case '\'': fputs(""",out);break; case '&': fputs("&",out);break; default: fputc(label[j],out);break; } ++j; } fputs("\"/>\n",out); } fputs(" \n",out); fputs(" \n",out); for(i=0; i< g->target_count; ++i) { TargetPtr t= g->targets[i]; if( !g->show_root && t==g->root ) continue; for(j=0; j< t->n_children; ++j) { TargetPtr c = t->children[j]; fprintf(out, " \n", ++k,c->id , t->id); } } fputs(" \n",out); fputs(" \n",out); fputs("\n",out); fflush(out); } /** for deep output , recursively look the targets to check is they're 'deep' */ static int IsDeepFlag(GraphPtr g,const TargetPtr t,int depth) { int dirty_children=0; size_t j; /* test if children must be remake */ for(j=0; j< t->n_children; ++j) { TargetPtr c = t->children[j]; /* test if one CHILDREN must be rebuilt */ if(IsDeepFlag(g,c,depth+1)) { dirty_children=1; break; } } /** depth is 0 */ if(depth==0) { /** any dirty children ? */ if( dirty_children==1) return 0; return t->must_remake; } return t->must_remake || dirty_children; } /** print a list of independant deep targets */ static void DumpGraphAsDeep(GraphPtr g,FILE* out) { size_t i=0; for(i=0; i< g->target_count; ++i) { TargetPtr t= g->targets[i]; if(!IsDeepFlag(g,t,0)) continue; fputs(t->name,out); fputc('\n',out); } } /** print a list of targets */ static void DumpGraphAsList(GraphPtr g,FILE* out) { size_t i=0; for(i=0; i< g->target_count; ++i) { TargetPtr t= g->targets[i]; fputs(t->name,out); fputc('\n',out); } } /** print usage */ static void usage(FILE* out) { fprintf(out,"Compilation:\n\t%s %s\n",__DATE__,__TIME__); fprintf(out,"Version:\n\t%s\n",M2G_VERSION); fputs("Author:\n\tPierre Lindenbaum PhD @yokofakun\n",out); fputs("WWW:\n\thttps://github.com/lindenb/makefile2graph\n",out); fputs("Usage:\n\tmake -Bnd | make2graph\n",out); fputs( "Options:\n" "\t-h|--help help (this screen)\n",out); fputs("\t-f|--format (format)\n",out); fputs("\t\t(d)ot graphiz dot output format (default).\n",out); fputs("\t\t(x)xml (g)exf XML output (gexf)\n",out); fputs("\t\t(E) print the deepest indepedent targets.\n",out); fputs("\t\t(L)ist all targets.\n",out); fputs("\t-b|--basename only print file basename.\n",out); fputs("\t-s|--suffix only print file extension.\n",out); fputs("\t-r|--root show node.\n",out); fputs("\t-v|--version print version.\n",out); fputs("\n",out); } int main(int argc,char** argv) { int out_format = output_dot; int print_basename_only=0; int print_suffix_only=0; int show_root=0; GraphPtr app=NULL; for(;;) { static struct option long_options[] = { {"format", required_argument ,0, 'f'}, {"help", no_argument, 0, 'h'}, {"basename", no_argument, 0, 'b'}, {"suffix", no_argument, 0, 's'}, {"root", no_argument, 0, 'r'}, {"version", no_argument, 0, 'v'}, {0, 0, 0, 0} }; int option_index = 0; int c = getopt_long (argc, argv, "hbsrvf:", long_options, &option_index); if (c == -1) break; switch (c) { case 'v': printf("%s\n",M2G_VERSION); return EXIT_SUCCESS; case 'f': { switch(optarg[0]) { case 'x':case 'X': case 'g':case 'G': out_format = output_gexf; break; case 'd':case 'D': out_format = output_dot; break; case 'e':case 'E': out_format = output_deep; break; case 'l':case 'L': out_format = output_list; break; default: { fprintf(stderr,"Bad value for --format=%s\n",optarg); return EXIT_FAILURE; break; } } break; } case 'h': usage(stdout); return EXIT_SUCCESS; case 'b': print_basename_only=1; break; case 's': print_suffix_only=1; break; case 'r': show_root=1; break; case '?': fprintf (stderr, "Unknown option `-%c'.\n", optopt); return EXIT_FAILURE; default: fprintf (stderr, "Bad input.\n"); return EXIT_FAILURE; } } app=(GraphPtr)calloc(1,sizeof(Graph)); if(app==NULL) { fputs("OUT OF MEMORY\n",stderr); exit(EXIT_FAILURE); } app-> print_basename_only = print_basename_only ; app-> print_suffix_only = print_suffix_only ; app-> show_root = show_root; app->root=GraphGetTarget(app,""); if(optind==argc) { GraphScan(app,app->root,stdin,0); } else if(optind+1==argc) { FILE* in=fopen(argv[optind],"r"); if(in==NULL) { fprintf(stderr,"Cannot open \"%s\" : \"%s\".\n",argv[optind],strerror(errno)); return EXIT_FAILURE; } GraphScan(app,app->root,in,0); fclose(in); } else { fprintf(stderr,"Illegal number of arguments.\n"); return EXIT_FAILURE; } switch(out_format) { case output_gexf : DumpGraphAsGexf(app,stdout); break; case output_deep : DumpGraphAsDeep(app,stdout); break; case output_list : DumpGraphAsList(app,stdout); break; case output_dot : default: DumpGraphAsDot(app,stdout); break; } free(app); return EXIT_SUCCESS; } makefile2graph-2021.11.06/makefile2graph000077500000000000000000000000771335573450000176050ustar00rootroot00000000000000#!/bin/sh set -eu export LANG=C exec make -nd "$@" |make2graph makefile2graph-2021.11.06/makefile2graph.1000066400000000000000000000012201335573450000177300ustar00rootroot00000000000000.TH makefile2graph 1 "2014-11-06" "lindenb/makefile2graph" "User Commands" .SH NAME makefile2graph \- Creates a graph of dependencies from GNU-Make .SH SYNOPSIS makefile2graph .br makefile2graph -B .SH DESCRIPTION Creates a graph of dependencies from GNU-Make; Output is a graphiz-dot file. sub-makefiles are not supported. .SH OPTIONS The options of makefile2graph are the same as make(1). .TP \fB\-B\fR, \fB\-\-always\-make\fR Show the graph as though all targets would be remade from scratch. .SH SEE ALSO make2graph(1), make(1), dot(1) .SH AUTHOR Shaun jackmnan (@sjackman) wrote this small script. .br Pierre Lindenbaum (@yokofakun) wrote make2graph. makefile2graph-2021.11.06/screenshot.png000066400000000000000000001404741335573450000176670ustar00rootroot00000000000000PNG  IHDRh}X^sRGBbKGD׺ pHYs  tIME!S IDATx{\Tu?D2Ky7P1SIKn9 W\vk~-hl)nvBaV@?ym4L+R~ 9gyg9yޯz""PB *TPB *TT TPB *TPB * E$hd J0TBu Tmʏ _Qr ^gF34<> aI(@Q-xL=1 Ӛ 08؍1qH.Boxb5zN!pq^LjA"JJP25zArp T `v5z3aFl9SBw[SꌯP/i+glP 8@XJl&m(j255hLd")Ebkаy)2UB'dş3\ _x 1x5 B"mN=~!r^O0xM0IvGۨ)ڟ]/ŷb0.W*?M<s:%-TФ"_ @ RP$>! FLr, b\s1ըF:QjԠFr4 |Z`iJ_&!R&8I4F9Yba P:M ٳ 0" aHCX~sZX/k#>4N#D%O5D+v:= HSj}QUbE)!?ERq|w'gf)frf+sd!YA P'N q45~ 7H(X ^JJQ˸I8a B|^|qבDld!62>JC J_ H#xqX1Na-j9gފQ T a;)HA jdEp6Qb~'A2! oSi~ǑTXiwPQ{`%0o<+:VPm-ТARIA l(5ad8ܤ]Is$ (6nae%%eޥ P"LIJO+bJN4k`V*9L"Z;ɮ_@KiJk%O)2H&k|_;c'V@ @鶒)AU⺌XVrXۆ U~ +WbRƳReRq#}qo܉[L1X[)>co@nVb%w)]G2ld7 $ \r y#)E)t!hehJQI|G`v;lfq^+Y` +H&ۢ-t(Eqג3Bvr=#x!^FLX/* 0`:#M/! Q0ˆX؉ &N=87@ă>e\a Qm؆ y|~gtLWL:x1Sd$ rd{;`Hv=n&p'=2'! Yd;B7;") dJWB*i4BU>]lMZ :ATs\1~PjOevt/!Q= :AZl}weYH iMB*ص "(;L #ˡz iBC~Q 0 IFad :4 @!4/}} ß}N{l7 m'M٤%-8HIKZʦl'_mQhAiX.wV[!§:N_猵ə/2zKfVW9Zz"f4orտ9_Pq@bS1Nžh/pƟ8lqA~` +IHJ8eX&B0ѧ[!>:l6ᑿJgYkD ¯3B(TX;V2)@;,@ހ A8)i;;Q+[ XwEQ2˫x/dL "WcQiXPr!KVZr%NhX-#/yۺY% ,Hذ\`%G<`,++^/N#%(L@L0+ 0#G}Z>lqȳY%d@rMr2Dg6ŋy{rcy&.\ aUWW\$aDO;]GSG"B$"qg$5@2'J d?c36Cd[/˭#*6Y5NǑW@2!2[Y܂ BOƭE6n(@OE3;z+55fΜ)I]H[m֬׽tG-xo~/>3:sLf3uUkB)B#}to詧s'<998uݻi׮]v g,.lђ%KFо{ĖzCn;:t(>/bP\\LO>GwFB>k>}XBMGJb}26_s]tŭFac\W]tJG6ߗln,-?Xv-uڕeg1>0 S₈jk.وča޽Xt)^z%ם?Qy|W/?l&v wX>_~lbqG8˝z9GL΄l+î]aR:&oݏ%`qGAA'G;8xģ8#6>&A\G8ݾح[jQRR+W 66:t@=?g >[quѫW/t o6 xK.aРA Ÿqqz"jut_fƒ>]r-Oϟ3}>Çsrf>VZ={M6͵ waxhptW^N@og&?ӧOG֭\t =Ο?F_|ҥ &M"¾}0a!e˖GAV-kbqG9݊- MVZ˗1"£>AAբm۶(**ҥKh>°j*t-Zhe#yrmRVZ @؄MG F cu֘;w>}:^{5k?v,8y}2cڵ+ﶞ){ R"B6_?cРA4hh8x <#sF9lծ**}%yq 0~x_z!{yŋyf<38|''e/3}m}PY;ر#Y,͛7ѣw!"wyt:Y,j޼9W6xbz'bPXXݺuNnpŋSNoޫ%pbzLDD{%dX_~\9͛ԢE u5hqJJJ$""&\Ď;'"276[* V;~EErr„ 13f{}1+M<%MI>.+/4L}ocO}=zp;s5*$bs="B\\5^^R烈mnݺ0~28x HL;ѣG7Č3p9sߏ5kpߵ~3A'ݷÝ;wF\\l̙3w6mPSSO>'Oħ~[/`ҤI4i^|E|'6h󄆆܃3xb; ®]~z C ? j*Zh?APPƌT=QFqFk B2-۫;opz}bh4x'ŋ_""";4h;Zn);C ~o… 8t, 4 Ν;̜9oҥK?}݇ZlݺsԩSa4.,o><31h޼c Cmr+dl]EEE۷o:ۣG\p׮]ùsAD1b|.RIotG>fΜɽ>|8cC!::F|}EvRL/l,w+zzdԩSݻ7uV߿?#nݺccrh-X͚5⬇zK˖-֑?Xz5lҨ۳gf͚baaa\ ۷× '?Rocذax"L&zmG/^ĵk5k֠#Gd´i\ל;wݺui֬RSmI<G9r!7M!C`޽ѣ/3gϞ:1h-/'%dՍ6n0slS8}۷oc˖-߿?Ϸ7.\hqwy.\@.]^s]eA[kh0 D]۾|Ghٲ%wFb<}⥩k2/ԩ_I#I7pa:t;v >2?>|80olϟG}}=BBBl_Y;DuDDSѣG':Թ]3̨ ٌ#G"((.]BLL V\;M|OUU;wnpN{.*PjavKV6 :{ʗF8d"85\lb(Sx% +b\1cͅ ТE l޼={Ĉ#c!9㧟~BTT |W:wl-Z4j*++1ydܼySp=){T*?R1">Yhz-hZ9;#&ڵٳQWF|`7iժUgAu>n泵#o||%?Dmؾ}{hZl߾X?ƥKgO?^z t邿/?>V\TE5u\w6n3w=h48}4.]޽{K.?>^yL8?<ƌU+N:|{ի9o >32220zhNYPKdƮb`ԨQĻヒI&… #L:;vpmĉx78ݿp p^رc}6yyyhݺC;FDٳ'lق0h!WG{+~b*>:t(^y|7oh9r?s/\м&<<\VbbA ,5SL͛7ǒ%KСC9P~صkW!A8[)4qU\ݔdذaq8_=O6^n,ƺ԰BBBvZj iiix'mbŊ7xqqqxpkx"9|-np6U#aKu*wEMGbC-F#..yyyu!$$Z֦ePrJšcǎ2e jkkq)عs'f͚:?Dqq1;#>qqq իS(XPxZW`gݻo߾HJJQ[[kYhh(^x5 HKKC֭ W^tٳ'ӹs۟Yfxw1 Nr6g|o1`t Zo͛9x IDAT{EDD nݺ͛6:ۺukرcx"p!,YUU* nWp7Hd^mܸQPO_uO>ٳ'jkkѭ[7aŊ "lٲ+AF /;wDzz:郯 QQQEXX&Nmb…ظq9GłݻcҤIر#Z4k Dk֬C!>>=܃Y$c1ﻻ 6{=;wܹsׯC`ѢExѷo_{t,vkp׿؏ɍ70rH,[ #GD1i$L2s]&xIEEqM :t /Syڿ`yw+n۷/"##3grrboBCC'FRRz!m[nEMM _6V3]yw{%#/Ï(Q VVVAAA(//V۷hРAxꩧpc0uTnwDJo!!4 Bbb".ŗ?J9WTZѓ?nt:AXUUtl޼:tC=3f 88à 10`a#uމm}Ns1rhgΜ͛7ŋ/Rxxg68+[b[˙/^HgϞP>},Qi[یi mqkxN˷~KW^[bs"sL׮]s2*~ϭ~1q˝ő#Gȑ#DDtDD|K_K,aZH E/&jf!gϞmfySrU٢nw,|nIbw|>|KDD߹w|R|>L?hXT^^NϟztR뺢|>n?1].^H%%%DD"$"-VmO]:_/^ؤO!MvN A/B/Y\);CѥKDq&ֿgW31nS!BTRß]Ǝڵko߾F}36Mh^\$Q$JC$tɷGG ""}Qeee s\@UTo퍍IHf3/< 劷-d*x]%fCw^̚5Kp 1 85p2 I֊c 0=ʧt[n`@D0ΥloHKDhӦ ~#888{,v*C/49s&0k,,[ ?<,Yx?/{#`$MNI;ɘMv_ AJ vkorreS4}Svv܉_W$}A*Aa|E4)[nX` @API9hEm@)(J zŷ_9HCO&g`N~ ^4X.\rfe0 |rكK.cǎ܂r,?I}{"gq]cŧQ'k*?M#xO`,1<JTb;#؏N0#Y`e THK/w{=iIKg'_4hI#)ÒA{h"9C{8}dȮ%-eOcsN{iIK Pa@!4V ˔CY:xTjܒCsϥ1A}MV˷_Ed) ˝?J!m߲JztCӓ,|O_gWL:"SI5%Уnϛ^vl)E}.65ҡdlTf-ʳERbm29R/Wb*? 3|Y+_m z Jɦt^zyU9k\1T;in~OR$` "^]v!55N}%g` p gpG%*q lʧ^R3 vcLZC??uHA jPd$ _67mڄ~\fAAA'4!k F RC[qq1*** 1b/_gm-뼱p# .iw`@AAv˗+&}J9&JQjT#F5` ƘcvZJ|*??&>kW{[fku:ɓ'SY*,r$$)gyEG#<<ԩS{Ɗ+0ydla҅cy6܋gU]ۋVZ#wO|DEEa娩ArrrtPq! Tao;w.0tP 2DIi&bvcp5g' J8dA0C'tHLQ8߀,~? !!-uwHwX4w{[򐚚cǎ$l y̏K"jruoMew|thQ'\]vaѢE8u믿F~~/`Rb~ c#Wt 㓍 &&FQ`0`(**?^;J;vL3m,fV$<++ !!!6zژwf2/H5?f;X#)%XYOޱ6nKHڃe_Z'3q _^R9am SOzIYYKo1,\JݖЦ [Iɛ uRKGtJ-B%84~DJ ǥUV0>IRjX,-¾]Nvbq/R# ?$O?QbU}˷>F. eYmJ rd̄2HMXlSdXd攛|Eۏ˕߯so \H\@v @DizfqF?M\8}47*pfW4gdoHAD(..8ѣGqސɛr'wd]Lj@ɈGS"QpJ+ٌ[T\;@.rkBΝA:q*' bk?NgdI' {^Gl|4hJ\_5бcqVtt4oS,-;QP-j4·7۶m|0ϟ3f8M\OW`_썘Y? -n :=zq:XHb_ 뤘#"c&L*j);;[bޖ[vv_S{1ݛZ-m޼f@{BҀh˖-\@U__qA{QeG$"##`0˗}K.ddd([GddO鼿n7nH7o&VKZ.\Q\\Lj#  9cǎX,viZ*--(ɉ>3ɡX%U*Uc|ʪ!45 jt:fs<@L3ǷR#"xoS& &L*V| maadJn(]qPPz=L&O8(:d'Lu=TSoTac Gv$A6׹UC}A* !::گk6Rˁ߶mnɗ鬬,F$''#&&FUx`t L&bbbVPn2LX~=rss (q[wjj*a4AD\/^?Gˈht:dffh4h4b6 _-87nǮWəRNƲȖ:uFM&gUnS!&&ƆO_> ƅm3 !''SNU1&Mv7c\ٮK>}p(<=lIo ==iii{:EEEh۶Gضm.$?'O a0Xw=sLuMo+FhxdKB"9vZjƌ˗e\ge0ТE !,,T4gJJJP4^{ɴ/!>>ަ[|7U>Ic{iHD5eJl+;\$%%x<%|{oeRޔf=\z[x_]9U>5Kdϟu֩䤛%NNǵ'ʾ T_ o>[0<=h4ؒڕV&GKO!:\˒3J?u./ÁbC-=&&*vI*SK6UqOߟJäIԒ<wD@N0dȐM`;$''57K.6m<"0IJ!"ܸqƍi)| ŕv9}޴i}Yύ)1Z `#R>o256}y0uTc Wgvv6Ν;ܖCXkXAA=4T!BMJ*\&Cj6XA+xh4(,,^W߼6 |J\ BRRK ;~^gV&ɇ;/+{y S*yj S!O}7R RTQy{RI łu?dee!44aaa\V^삋K `VqGdc...F~~_.)a`@AAv˗h#5;wvMUxg8[ol6#::G{/((b&|PWW8I;wjj*~$$$Llkzl۶7UꫯbСUhӀ^ !!ARn՗mGB@Fze'(OD|-`GExx8н{wmPDd&oMbb4u͐ZyO"Bzz:,Y䱬}ѢE8u2$[%ۂLMʇЎQ@F'V{[TkJl^BO|BW'Jo_leRj,XK#?%~r**PvlWV*f3-[泼$P1h4"77 BLLw&Sm۶Y3@nڴf+cldvEzg $3ΠA%<`})"ƈvNb;FlٲAAAu+JXݕI>ZY5NæM&|PwK@})ZAEbYhdffJfEׯ_ǯku11?qu^So h4HLL1OҀ8j-))hy$ޖs>)?G}TyѬYF)((ӧ|h1h_nej̏*xx/+;}I]7}+hݗռ)zt$;;?3͛"bQ UlgyFd2ʕ+ukbwdݭ`ҤIj؛oiR0n8l۶MF`0.*I@xx8*ܾ}ɲN>=v'Nl,ZD,]K. 'OV}ٗg8** 'OTFEEaո}vĒ`hp|͑w/rgTTTm۪>Ԙx{'Gcjj*ˑ#b-..FEE2339$W/gU a6JU5ABAʩRjfYjT8rvL~˲ȍv0_|;십%JˣSȦśr:P) *=Y >[q6n+{̗F/Ͷʫnj`_Jo}pz8pT֯_$uBP ,N>|#c淃eee!$$VB]]7޲"t:kD~~~90NIIADDDBFѣG{eR*ѣG]lldB~~>vh#556[>:[x_}U :I塞/ޥKu+ gUՍrt]}YUcjԩSR!U"UHTH,lҤIwG-mJ X?$t9ٮՒ/}ڵ+ƌ3fmڴ >Ò| aÆ׮]SO=;xpUܹӭv6ujܱ9V˜1c|EلkqB{ꭠO)([QL'}I0Og]ņD؝;%a„ 6z IDAT%6Jl7𖏖R}u2i޺#:'nV/3ϭ|mIbc%UVU4Cj<77+vͤTp 7 =}:t(QZZ259r v (_u YLQb׮]OXl; lݻ7?^ \|))), *Nt?8㑖K~T ":+XaРAx@3O?4Dl6cXdkm8}}/8[!jcN#(([li iӦj&oX=ꫯzj\zU_jgb  FNOz9+Ι2!!ݻwFQd@l?1 HѠ aaa;399YO9dE8r.\0gl7, ?~ܹqQ/Bxgcfrit:$$$`\[g s!??ߦOWΟuRm_R&hT8kK*T(aˮ2z)a֧" mw;wĨQ$/\ꀸZ^ܩ/'rkf<16V&ʹ]թ{y=~Vj !-jX$bAYYY^u|+VQQe ON ш|t:_a00L)--ZP`0T1=z4% ٹADصk:t:` ".I':::00X~~@nnzk^^e3_9<<\VOSrhzye`u .bСC6IT$''sƍa_|?!55 hDVVV+ĉ~)--|Yvvv~U[Bt V%l*$%%9躚鍈O?cǎw޲())ibتPє ,, "}6os{ګmڻH>CzTNv{ǚ5k$ͼ<#,,{SZ}||ghӦ iii sk=HUFHwrɧoU^'ēO6}pq>szgU˔CwWn%QQZ`M~… ϓMZz햝j.x/nr|.gN$7n jtAE+Ӟ=Tkr-'9ӧO4*Txj&s+PcTs8S\q» *܃}bKޙ{+cGx_0;~Mr=[gitw%t |'vgj1|zK$Ŷ>Zģqo5\,v ٓdo=6ǣvK6,dWܹלǎ&_>QB)Pנ @X,|9rd߅R`\{{/Lwx?jjbf&l7"@-=Tedgg#N®ROXX2338pLLLht2XRJco`͚P (X ֮؂@w[؂bxv[obnj =7R/8Zl-VEBD8q`K 5ϐS'<1pxq#jʀ|pnd~pA(7f[Ѕb1@cT0P@fϑIEuu5%[J?t<;co`rp=qܺuWxsV…ֆለ,l/9L9>, HKmoO}F#ruV@Ϟ=q t:TWW#==O*5A C_uB[sFzyLՀ%"2D}*THhI0&M`~~/ei.f :u!wD,FZYY6m8UU +W]tQm kdd$N>?8 ˗zqqqon`ZK }nl:+55I "##i>8{X$,іt$%YX jT&'PY l={/t{?/BE 88lO=BB߷W_)#愭(++CTTTϻwS.`}rUUQ@v;Z(66[#MTBMШj(aC|+0 B@W+jTӁ|"wpi[5`@RRR?OcvD֠Q@' ψK*%W?dC3,,,xVf2yޚO@Cd娨Ϗv|vݒY!l\)ÑQbAUdB .Iwv|#BQ!F ; ,Xܾ zk/֒u"`^J I㤆-GR0b,{#'¬,"??GZ[2ł={`ذaxQVV ЬE 2:wзޒ[&cNJwD߿_}A2<'oC}&_UǀB2j2ӹ3}90n&()ף|xqƍ܉QQj#VxkWIIlBVO'+[g f GkګW/?~\{ڮ]|ΛбjR=z4vءp* @^&2822Zz:v4AP%z>RRRP[[߲$63BRRw͝7mm5JJ>v+_[oYᇀNǏ^9$-- mא95YOӧ[?mzoO/IK)~( \n}NFUZ ;^wS'Of9ݯEA/nqK0e x`VoXy)um 4K,Q2jFƒK9V pƫBBFL?bq_# 5ϵ:(hʃeї2hIrNUI'gm-] 4;ls ~uu@EIHV@5lg(*T( 2';;FF& 111ttHMM߭ or.<<F6y`w@5㭁?qTzRy5nZ>dǎ?dcKM nQ'gVڷ9oj:N}JN_dx&ۢjM>ׅ]Oľ%qt=j=]Yu Aum*]mSuuuHIII$9)2ydƟrs_ 6[ڒhݭ#2ҺFhtc5)Sn>ʥ#t@l,H볚7x- bNIuѣ]nT3\ edD'byߩP!# @KJ|65kۆ3g΄FwZnΞ=/T ׯf3f3[$&&")) IIIHLLFqhSz=W͛a.%g6l؀ 68}'O믿VP5)mX293aK"Bffۣ۷1dϡ3gĩSNΙ q!@DXt(fC**㕲Mhtlڴƞxg jF$XE#G}隚|駨/xLqq͎/Bu`P󟆉_"/**FA}ٻ-تU+j\\%L@لI`0iLqY>~gvڡyH ᮯx#_-L\i7ouصkKk߿?:0yTϚ% i ^!lfIsT6m'|زe >-ݻ?xǶY+TʀŋwOPQQQ}m۶2dWX^^R<ׯyii)Ν;uuu9'ٳݺuC-pKJJ 80`;:u v A?@2w}馛pE8q;vΝ;ѣG 2D&ą Я_?uYxᦛnc+(/:tmG!!MD?r P_[СCѦM?}ѷo_kuCu)uԏy<7ސj>$ ZÇqQzl߾0`.^mۢH}z聣GgϞ6 ŋ***{ƌ8\GxS}lnWWWk׮صkN'|$!99GFqq1"""ԡ0n8l޼Ǐm>zҥ ۇz _|z>}:N: B޽UUUҥ ^{5DEEހVwQ`geeDDش+V`Ŋyl^o,< &&3f?ܹ$a˖-ׯN8 w3#IN<aҤIx7/_UU'|"G]Ô)nFc!բm۶¾5X^fv+uR1j(1|"FHLLT_sωgyFFZmnkZI>)x|[NtY})yfOl?^zU|ĉB!O.:cÆ bI7oxw< F!֮i["ѫW/QVV&g̘! fް0dvYnԩYϙ3uB-qo<<|ppBu7pB.]{vmX|g⣏>BoL'Ç6m7nw>LvNVWmriAQ87x#ڵk;v… 7nƍɓ'[nw}`udpppm9s,?w O?4;fuݢ"׏;_}}_cGsf:w[oU ȫLMtP2_ns8xS0l0uiVpeW^QZ‘#GP]]D96233O?9~Z\4@V6.]֭úu됙saذa8{zk N:Cê9>իWY:t?>>v:**%KXW㠠 䛬ڲsN"((ƺu됞}'=z(6nhu`Ǐ2Oc 0 IDAT2EEE8p x rjo>c֬YhӦMϫ]xu^SVV>}_F+Wȵ2bY6BXr%֭[ӧ[TWNM^3nZ޽{{f+2ojzԩSC矿nMϞ=Ͷo>7\ݥKԟжm:uҥKjyX6l_|auU82 :\B@i=RRt~m_RR]tފ>}XvⓧO"h~BիWW^СƌW_}UUUX|9u놞={R!!!ζt:y\r7oV;'0edffb׮]xG)S I?b޼yr f 2ľgr6"r_!_!h4"** QUUKg?GUUƍ(8p qF3bժU׿z(((\|9RSS#oz->}x1zh{yV"Qvoc"""IIIؿ?N>7|~~~8tfϞ>@-_~2œ9sHH33s-n4ꫯbܹ@AA:bh4ZߚP$%%`0۱n:TTT@ӡԱӲƲC.cEA0ߎ T'7n郠 8qBݻXd ?_1}/--E`` N_;wơCЩS'MvO>#G˗=Μ9}ͭ3ԽI 1K"sMNcǪ?wU&?Or j*HBL0/2Fdaɒ%;pw`ݺuo0bh4 6`ߠP/=A3C~|}}O>ׯO?  $$999#GbȐ!XvJBZ;v$IfqԩyRtt4z:uRSڻヒӧ#((zRs6M)p=yU#RpMdDDv ر#FvaĈҥ ztL6 $S#b̸hXv-{=̘1SNŤI> [n8y$n3Y+##aaaj$I8s ySjj*z!H_QQQh9UV'C۷7yO]yǎAv0{l_gΜȑ# <z)--Ō3cǎaر:ur:T.vus㏡jqM7jjjh"Dxx8ڶm瘇zH )enj3믿bڴi7n/_vLk<\$o͕5.7P|jQ&צXs$">7Pn+'^[-Ǟ={"''wuw #N8LӁfnvZLor౶i&^Z Dk֬AS[w9DV!O{x7|_VVԲu"(wԹSNܖuuiJK={ /N9&8cyn VýsyE@RЧfO=\.αui ZEiir:ZXcrP)88W30ɛ;"BZpHh:V:]f}edڤh4FnCtW;.,?UK}}mSk)6-qV?M˜84T09hF#qe@~r}( cNeee͞R@TT>"rw))#INLBC]Qh8V>)@Wd01'$#*6xg1A@aZY ,Z$zeZ^lO-6b>'6^\~.#}h9s&z=}ئ7A>|}˟[viZж-0>W;++kf\FlNDNΚ5 'OVo (\-(Hl[^BB~LfJ0u@Y_Yɨ{C֝}LI||y󔋱)^޽{wHUl(s rlʰl@ CxbŹmzPJe9YkRV6lJgڻzG)(kk2TҾL;JC:OTrr2Znwycƌq [Y㏵mjׯ+<\nΝ+3ڥB喖mbooY^RSSl2$&&"T Z;gz> 1@Cd2TjbHxEEyr)"G^U-e, cny>,KG^[L7L=nFGuӑehf\#c!&"/rRҗu~nv2Zvhr3= //uQMgsc[) 9\JJ^3m|W!2{])𽶿TVVbrGȄPZ"B2:_ |50zt3Fe҄r4??Yk/=q+. NGnVRyTWW׹I֘KJJ ++alo0@CDt,;Brrrm);f]^G7p7hCLL rrrԠ/|r\M{u="" 6, qII ^cpbٸ8cժU 7ǖkΝ;]ts-cDvgffbsUmfjiƴU_P4oذa~ȍmACD-!<co״()E^*SGJ2b\i*duш:rSz{Vwll&Mɓŋc„ JJJ=KX 8|o]NTL4I>{;4@Vn g-)??lyh?)ND rssu,GCJ HzKE{ryK<^ӡ3h;'"rOACD^Ai褤W> 2mB`0 !!YYY nP2 cǎ^RsILLD||M:k0.V0w\TVV:}a*q gy b̘1t˺4[vHHH@yy9>sl3xǰzjo6͛`]RRᲳGRUXω4DD6jL; иW՗D'--Vt2VrT&"2;'0@CD8iS8cZ3Dt] x| +Vc̙@?5P${o={6 &P3KRRRYYYHLLd_:t@mڇI JmN!&M ҡ189{S w}}}q%dddY}Ra4-5ɚݻaaav-#_Ӏ>آeh 94DDԜExxYSL{ǭSg gӧVrr2Znwyػw/ "", f\hd!IsLuH._ ??T&"jn b u& gOkm2uF9n4 ,RJ},ej3ѣ_~,+W_az2ZSeْBUYYpf'F)\TTT@?F@ijK$;v wqGœ\ǦMԔnN`G˳Veܶh"TVVӔwuҥjp9w}N w0Z4DD)QV5{ܲe F>w_;#1c`-?\uM}@JJ |||R **-dukwΝ׈yz=Уݶ2e"r7 `t.YKΠLcÀ<)Y>s;iL;k8Xv0gS\SZ#YtXJOO7{yˎ3&u3TJAW H)KG%%@V<5K,es-֪vD!!u(oӀgj*lYw/Qϣz [ 9=x W62̗/_t2`f9]b̘ڎtÁ~ Ə?HZnLe lA'Orn&Wgq#--ӐZr^N1r2_K۶ms1@CDD,;',Sl]9EYQP>Gnge:N81b$ 'GN/$"Lq"""`涱,WndeeDIU "ĠC{[jUp]v3f X|9tR;w>>>(..9r999/q 7_~8w:w"s,Np̡ۖ$ FѣGcaҤIoF/_Fii)Ν;Fw̙[nG"** ڵ|P'e\x?3\o/TW˓_mLl:OL˖٪XzGSNŅ |ruרQ`4q7"446l@jj*~:hB$Z +WļylGDDWy"""roh4{Qkhժ\ee%ڶm|iii߿?R޽q9//>>>8ַ,Ρ\wJJ {[lAPPz"]E}&r4y&v!C7 Θ1c#o߾X""j hȹG%bPi(bMw诳M:w ,_R/RQt d1`00vXѣ1cΜ9[o3&Ls=g1%<Qd""w'"""r>}j3.9,HtL;tu,On?l (@dd$>3h׮N:u W^SC򹆈ȍq 9a \.]AZk$yDD-8)| 5U} ddyy\ϼy ȫ5&=PVbp<4DDDԬ;6MR6ʀ͛W^N?Gx)I@Y_/\"y~+ 9g4DDDԼh '3+4{ߟUM!:;`kiMDa!"" !4j`^F!WC޼sˎ$NhÇx4 Aݤ$ yf)NDDD䒴ZX|r2z% ??ggJP틞)Nt<DG2R9`[^.8b<4DDD,;d?+)~ȿo*&Fӱt:,vhQQ4ǡǠmxEDT8j( isE+io5kcmV@&|5j`\Wyiˁt8ںҩSu#E(8k;,_.osn` 5yi#h5%g>wJ$Ҕ !:GPX B""jפ#"""O6!F#үyD^,-- B4 ]4DDD$a얝㉈N ())a!٨@Dd'hI, Z-zDD-""""gA5zAD  Ŗ-[XDMaÆ E@DDDDD&""j!"""1k,g"j`@NN A!"""#"r,hQFh4@D1@CDDDdB$̟? ID!pi0-"""" ~~~,zqm"""" ޽{YD4 W:#"rE@DDDT/;D&HMM0@CDDDd#K/qm"$aٲeDDX Y^r8RDyy9 I8 SL$I ]bp 5Hŋ3`IDD !B`ڵN]v`+`ԨQ{ȉ!"""Xp!$IB^^~g y2@@@ ȉ8 N<͛7#77ADdACDDDd!77cAf!5he!ۿ?zɂ "jLq""""bРA0hx?̙3ς "jlqY!I$IBqq1 Y6Q3"""믿ƨQX5%3DD͇#hj,i4Kv^CA Q3""""k$I5 g_xx#н{FE megg#..ҵc""""IP@\0t({qkc?[ǎ˖"r\eHLZG4J4hT{4 :ɱ}{Dr/OD28 y7%dfʏ}J&)Ir'<rQKby5kÇygr bcԩL}"ZGwB'fd@I4c(/OԤs5(r \e1@CDDDhN1_}ղI cL-(;;ZMDD-)NDDD]4OyfZ3z"QK6QXDDDUZL֘ GsP ZݻADԒMy%(s]6=-- ?Ξ=49)'~84z= 1@CDDDAlۗ-[Fnn.gu|0Nl$ =z`a0ACDDDZPF/4 Q;&W=t5R+pim""4DDD.];w}A^]bŊ}F!k+5 """|B~~vNBF}9sݻw{tѶKPYY9zXb„ , ""4DDD$ ?)vmѾ kW;_S ,"" y>')wk+b]O||<YDD."""|CC$l޼~s`*lڴ ,"" y'|O Iz=87T,"SB̝;7ILDDD^lA#seeeM2`p@jg}`:IIIj%"4DDDy]fh[\L3h8xMtyyy h;(A\3~'&KKKMDD.""".WFr 9]$5"" y8~\(KZ#G>}65" b`}܈% sPm`#ɴZ-˂ "r& RSV~l46g逌 9!YDD."""n&Ɂz[ee@Z 18C*//Æ cA(Ipv""""9 #I+ٻ]@Nrs]'jg𴆿$q&""4DDDDr6(#.UoڶӦFnUNeLh4"33KkzS#hSc>,^luZyyyܹ~u`\N,999,""7 Q ?t9Զm۠XDDn"""l]Kwe*++@DNmQRҝY!!!wDD<!"""r;v,pN 0, ""7  ٰa:V\Vpm"""""cǎņ X d#" """rș 60݉DT5GDD""""t'j Il2g""r/ ݻR4DDDD.L?Ǿ}B ÇYDDn""""tR3m%ŋ#"rc 8It;U1115jxDDn""""7t'*//,""7 aYJKKDD""""7t' ""r_,""""9shw|ڵhݺ5[ns ܅<$8- !8x` V|`/)>>IBapu !YDDr]""""Ç#771118|0Φ5 -F|:t(4sEܹ3"##YDD""""OiؙXlIM @ӱ`]x)ODAuhҥK/=,ܘ יDe0믿2FDA8^ͥ pi2C8亶m aȍ !Wo@)iN${meee< SzwqgplB@ӱ#PQQwK/>gЦY(effb\]0@CDDDyN!l6 > M4޹rb؝;Z-r}_Dsoj ddg rrrXDDz""""`IKfM/n-7+ &0<4DDDDӲZ~Ra꓃hZ 7l^]$,[L<4DDDD&2[;v@BBJJJp%+:t(~mu>>>(((@LL Ν>_XX?'OBr,ZȶϞ ? @D%!"""r'@LLWk A0dt< ;{[nū r &OiӦ㈈Z (Hǣ};ZM`>|AD%$V""""aMӦM ЫW/?~}_5OSN+|||p%wCQQ`8u~WҀ8!'2Kp'""""/r!9wu:u>$I$Iڵ+N:e0x'ЧOh4B`lgPVͪbpȋ0@CDDDNF;6qj ۶m`P555Aee%Ν;nݺ?>>~!bccqAۿömAs}h"""DDDDdN`ڴ&wO<>>s oߎzuAAA8}4fϞ#FcǎVX~=BBB+iii\Z q )(h_|Eݻ7ۇ gϞo'СCشinF6l}r`77n?7ْ%\Fm۶ ' &"""rzsfϖQapIYDD^#hŒ%@V<޽[=Rsg F#ۇbű)88,""/ jmeFFbccY( L8sy98oäs$i0v8ɥ K.x.} 4pFD@$Ii b B6m !i&mZ IDATڵkggH,q$IRH- EU+**rIR%N$I3xSF ^K$Ii4$Is~;x)S}0|rjٗ$)'I$SҞT Tcdٗ$@$I15fktĉ-o$UI$՛\~5r 0$4$I7g7-**" CIA h$IT K.x >;wnz%I] I֫W/tʕ+}%IUrI$ei̙3=zt?Wn$}K$I H;u d٢Yf3ÿ9$IͰaXpaƷ[nI4$I: .$77l_oY7o$~8F$IY+ CfΜѣ3=@ SmIґjbH$)[A@.]ҥD=o$s&/${#h$IHh4?OVphҤI^x/$C h$IP亊;nKK$b3?|W_{S89u!!SSRRªU__M`Ѣ׵j]=V]cGFͥMUb裯ܡ@{WC)I$IRn>'TXO=G5XS0!u\80EA LdgAApp@3o[%v>TC.I $IR[r&0tf$#6>#\Y}/lj{?͝;%K0m45kW_%|9L#GBN0f?'!&>{/Cii揨4[nI~gOݎp9?N^3֮M^Ij@ h$I@p))M{P )7F0]COϘo;N= k+ ]]w!ЦM ҭ3Y 6&z商5$I 3RF(|9y1rziBq1r,/ >TV6Al!U$I2Pj%(Q"DXti1BB)o7bT`*DaƌdPuQ*)(H:9O_pAfa۶ՉO%O?ݸ™O}DRV2$I1bQtďټy3ϧaϪ!oe駟yj-[Xz56lO>! CVZU+*kh299/5Q*'Z{ ֬Y{Wvh<{vX<3Yղe7WVV_|Qi3Vih;N>9/IYÀF$ňO>xT1c={_&0?̹ҥK[ngy戟kΝnݚz߿z rU%?]k{w?e„ A@L63{A$aܸqlx .੧b_ێ7vi<|[b…hZъ[iT[Uj?gW_}ł xO/9k`XRa̘aYjoX`A:(ȑ#7#ܹ379y ~zLtԉ~;j_[J5-!c4$I$ `j0 iժ9.=zHַŲe˸K9묳ҷӇx<ΠA+ ]!C .8hnC%;s5rʫ@C@:Ӽysf͚geb%s?SrEQVVĉi׮]ݳg_~9OϞ=ٻwAaՁ@7M6Un'pBkyጤIz]5F 8nj;m̚5ܹ3,ݻ:t(3n;d|\Aaƍ0`j?m۶=chjS5K `L<;D"رc[@f)V$[Ti'Of͚5tܙugUI.KS1ӱcGV\IYn#GH9X6W+\IR1$IGәN/z6oucǎ\3]w?`۶m޽ &pWп7oN>}88ٱcAf͚vm 2ݻw^:tiG}./mH]7c zʦMkiڴi%$$ '=k, ÐAѭ[7^y>3FQjЩdڴ4lX26"\s ]vt֍VZqmuVv͞={[сWu ٓ={l2^y&M}=JTyk$ }ҳd$e d->I(AO/-]SSfڵߟD"e_aD".qJ}vz葾WXA-8S!;IsWk 矇؏Ν;Yt)$Kі.]J~*wu3o_x+W{l޼ 6н{*{mXlwhڴi-[zj{бTع7Ϛe% h$I2㤌y̫GVʜ27 aL58yp}y59IN,ITߟ}]ULaJFdy?@2)vyjGk'DŠS'FҾ 2IYǀF$B l |)FsC(!!T; $Ggn]f?>ٞ-kop曍gΜd8ɠpFRX$I9RH"D%Fd3X#520OIc$I q#J$'m(O4җs>p& #KC*q6LH3pPPp~H*l]&>pFR8F$)C?eӘLnv+d1FBBGAY=w ނJKa9.?r۲Ɍ>R$IRbUA3|3k HN[sN,$(JϯfIj$IPYe=2B 3M3p p2'gVV*X'9죏9^`Žp*%PT{={|*Q}7K˷wCGHj$Ij L3JaL=&6E{9x51O1~d )/:O=Mĉб#,]>H.B <W_5|s;  U~\X6y9FÀF$Ht8Y>jť 3g2j(l`]~RuT:KC U,5ҏ!T8I$5GtV\ SN9SS@sU^_)pȲpodلa|j,vo#$U~u$IJ4矧UV jxGA@QQ]vW^D"VꋓK$q6b1;$F;F"IʀF$Iu*e˖3$ۥ˜I a$IRkٲ%x;aȶmۈF&d7`@#I:  FvH=Æ s$$e I$3apBCzRPP`8#IʀF$IL_x)SqQѺ7uJeM |tmI$՗"ƌUmΦɎ0$IRp'I$՛1cP\\L.]իkC"2Wj,;$Iѣy';w.=jAII O>$F\I#h$IT>  ÐtJ#h$ITRL\veTC׮]9s&G6,I$eTsoR\\̐!ChӦaa1}tV^m%I>*N$I4Im3qŤLkS6%I:%N$I83xɃ+JEnڵ}#I $I2Zaa!hm۶D gmF4^zY&I $I2^<gذa$G 6,]fxJ/I %I83f H$;52cƌGTs4$I]vxnݺQRRhJJJJ֭x.A I$eTʕ+Yz1+y ðRXDaRVVʕ++$ǹ̶$IUqq1^nOwy,]N3HPRRBYYGŖ΀F$IYoذa,\0 aJ5H$ϠA6Nηz'|Q3X$IpB dAPF'̑S_HIjA#IsrT9ap,v Q836lEfH$((( ??뫺hAE]P%/'瑈eMHH$(,,JA UByr@XD@^^?]4$)H$AJ oxy ПLe*;pWI8Z2'#dB\F5I$5("ќ4SJi!$M}YX΄:ә`+53<=g@#I*Y!\bg[ȑjoːF&v$IM|)J/Y:pl 7R F01qd$5@4$I !!O>'$$B䨖$e] Il0m% $d3$I w#h$Iɮ:^uy0$.Wunں/ (tIK$)cPkLu]^D"A~ͥSNA@QQM۶m?~<999UZZʃ>Hii)999?mwuӟz|;p 5ޏ+=FH#h$I4NZfs9\r%\tEڵ?L2իWs}yqp ?g,Z-ZCq 7OҡC~mn&^xoyfCNN/mڴo߾?d'i#IY4$I8PḱeG}eСaHnn.fk??Сs! /.]0k,O~Ю];(-[dl۶>}sN?xnGm 8 4IbN,IEĈ GҤ’D";p73iҤtz?{t82boλя~ݻk-I;e$e1I$eL|G;޽{Cf͚1a:t@ǎ9(,,_~||9d͚5X!C\-Z}|g_ۦ+CңG~Z_ӏ~< h$IqNM|_%\¥^[ou];կ~ڵkYv- |xW[l}|&z 6кumСCyybtޝ{}vg$e1I$exaCx≼|'{޽MrsұcG>S~CT%\رcѣ ,`ƍn_t)]v=6|$ ֯_O&52(tIi۶-۷o' C~_3pUWf~ꫯrM7qYgq5ЧO^{5vɣ>w]^xZhoKnݸ˹پ};^zi#>y{$Iǖ8I$)f4_'pBGt҅B G+oҤI,Z!Cpdz}v>|8{_~ݻH~y޽;^{-}'t%%%ѣҊQHݻYfSN9& N$A| k{?I$R#g"Dj4M񀥻X#'Iґ1^$IR }Q=iāaL}3GK h$I:щU"JlCTQ6\GtA tIaH$IG#IR]1$I$Ig$I$Iyv4IENDB`