Home | History | Annotate | Download | only in examples
      1 #!/usr/bin/python -u
      2 #
      3 # Indexes the examples and build an XML description
      4 #
      5 import string
      6 import glob
      7 import sys
      8 try:
      9     import libxml2
     10 except:
     11     sys.exit(1)
     12 sys.path.insert(0, "..")
     13 from apibuild import CParser, escape
     14 
     15 examples = []
     16 extras = ['examples.xsl', 'index.html', 'index.py']
     17 tests = []
     18 sections = {}
     19 symbols = {}
     20 api_dict = None
     21 api_doc = None
     22 
     23 def load_api():
     24     global api_dict
     25     global api_doc
     26 
     27     if api_dict != None:
     28         return
     29     api_dict = {}
     30     try:
     31         print "loading ../libxml2-api.xml"
     32         api_doc = libxml2.parseFile("../libxml2-api.xml")
     33     except:
     34         print "failed to parse ../libxml2-api.xml"
     35 	sys.exit(1)
     36 
     37 def find_symbol(name):
     38     global api_dict
     39     global api_doc
     40 
     41     if api_doc == None:
     42         load_api()
     43 
     44     if name == None:
     45         return
     46     if api_dict.has_key(name):
     47         return api_dict[name]
     48     ctxt = api_doc.xpathNewContext()
     49     res = ctxt.xpathEval("/api/symbols/*[@name = '%s']" % (name))
     50     if type(res) == type([]) and len(res) >= 1:
     51         if len(res) > 1:
     52 	    print "Found %d references to %s in the API" % (len(res), name)
     53 	node = res[0]
     54 	typ = node.name
     55 	file = node.xpathEval("string(@file)")
     56 	info = node.xpathEval("string(info)")
     57     else:
     58         print "Reference %s not found in the API" % (name)
     59 	return None
     60     ret = (typ, file, info)
     61     api_dict[name] = ret
     62     return ret
     63 
     64 def parse_top_comment(filename, comment):
     65     res = {}
     66     lines = string.split(comment, "\n")
     67     item = None
     68     for line in lines:
     69         while line != "" and (line[0] == ' ' or line[0] == '\t'):
     70 	    line = line[1:]
     71         while line != "" and line[0] == '*':
     72 	    line = line[1:]
     73         while line != "" and (line[0] == ' ' or line[0] == '\t'):
     74 	    line = line[1:]
     75 	try:
     76 	    (it, line) = string.split(line, ":", 1)
     77 	    item = it
     78 	    while line != "" and (line[0] == ' ' or line[0] == '\t'):
     79 		line = line[1:]
     80 	    if res.has_key(item):
     81 	        res[item] = res[item] + " " + line
     82 	    else:
     83 		res[item] = line
     84 	except:
     85 	    if item != None:
     86 	        if res.has_key(item):
     87 		    res[item] = res[item] + " " + line
     88 		else:
     89 		    res[item] = line
     90     return res
     91 
     92 def parse(filename, output):
     93     global symbols
     94     global sections
     95 
     96     parser = CParser(filename)
     97     parser.collect_references()
     98     idx = parser.parse()
     99     info = parse_top_comment(filename, parser.top_comment)
    100     output.write("  <example filename='%s'>\n" % filename)
    101     try:
    102         synopsis = info['synopsis']
    103 	output.write("    <synopsis>%s</synopsis>\n" % escape(synopsis));
    104     except:
    105         print "Example %s lacks a synopsis description" % (filename)
    106     try:
    107         purpose = info['purpose']
    108 	output.write("    <purpose>%s</purpose>\n" % escape(purpose));
    109     except:
    110         print "Example %s lacks a purpose description" % (filename)
    111     try:
    112         usage = info['usage']
    113 	output.write("    <usage>%s</usage>\n" % escape(usage));
    114     except:
    115         print "Example %s lacks an usage description" % (filename)
    116     try:
    117         test = info['test']
    118 	output.write("    <test>%s</test>\n" % escape(test));
    119 	progname=filename[0:-2]
    120 	command=string.replace(test, progname, './' + progname, 1)
    121 	tests.append(command)
    122     except:
    123         pass
    124     try:
    125         author = info['author']
    126 	output.write("    <author>%s</author>\n" % escape(author));
    127     except:
    128         print "Example %s lacks an author description" % (filename)
    129     try:
    130         copy = info['copy']
    131 	output.write("    <copy>%s</copy>\n" % escape(copy));
    132     except:
    133         print "Example %s lacks a copyright description" % (filename)
    134     try:
    135         section = info['section']
    136 	output.write("    <section>%s</section>\n" % escape(section));
    137 	if sections.has_key(section):
    138 	    sections[section].append(filename)
    139 	else:
    140 	    sections[section] = [filename]
    141     except:
    142         print "Example %s lacks a section description" % (filename)
    143     for topic in info.keys():
    144         if topic != "purpose" and topic != "usage" and \
    145 	   topic != "author" and topic != "copy" and \
    146 	   topic != "section" and topic != "synopsis" and topic != "test":
    147 	    str = info[topic]
    148 	    output.write("    <extra topic='%s'>%s</extra>\n" % (
    149 	                 escape(topic), escape(str)))
    150     output.write("    <includes>\n")
    151     for include in idx.includes.keys():
    152         if include.find("libxml") != -1:
    153 	    output.write("      <include>%s</include>\n" % (escape(include)))
    154     output.write("    </includes>\n")
    155     output.write("    <uses>\n")
    156     for ref in idx.references.keys():
    157         id = idx.references[ref]
    158 	name = id.get_name()
    159 	line = id.get_lineno()
    160 	if symbols.has_key(name):
    161 	    sinfo = symbols[name]
    162 	    refs = sinfo[0]
    163 	    # gather at most 5 references per symbols
    164 	    if refs > 5:
    165 	        continue
    166 	    sinfo.append(filename)
    167 	    sinfo[0] = refs + 1
    168 	else:
    169 	    symbols[name] = [1, filename]
    170 	info = find_symbol(name)
    171 	if info != None:
    172 	    type = info[0]
    173 	    file = info[1]
    174 	    output.write("      <%s line='%d' file='%s' name='%s'/>\n" % (type,
    175 	                 line, file, name))
    176 	else:
    177 	    type = id.get_type()
    178 	    output.write("      <%s line='%d' name='%s'/>\n" % (type,
    179 	                 line, name))
    180 
    181     output.write("    </uses>\n")
    182     output.write("  </example>\n")
    183 
    184     return idx
    185 
    186 def dump_symbols(output):
    187     global symbols
    188 
    189     output.write("  <symbols>\n")
    190     keys = symbols.keys()
    191     keys.sort()
    192     for symbol in keys:
    193         output.write("    <symbol name='%s'>\n" % (symbol))
    194 	info = symbols[symbol]
    195 	i = 1
    196 	while i < len(info):
    197 	    output.write("      <ref filename='%s'/>\n" % (info[i]))
    198 	    i = i + 1
    199         output.write("    </symbol>\n")
    200     output.write("  </symbols>\n")
    201 
    202 def dump_sections(output):
    203     global sections
    204 
    205     output.write("  <sections>\n")
    206     keys = sections.keys()
    207     keys.sort()
    208     for section in keys:
    209         output.write("    <section name='%s'>\n" % (section))
    210 	info = sections[section]
    211 	i = 0
    212 	while i < len(info):
    213 	    output.write("      <example filename='%s'/>\n" % (info[i]))
    214 	    i = i + 1
    215         output.write("    </section>\n")
    216     output.write("  </sections>\n")
    217 
    218 def dump_Makefile():
    219     for file in glob.glob('*.xml'):
    220         extras.append(file)
    221     for file in glob.glob('*.res'):
    222         extras.append(file)
    223     Makefile="""##
    224 ## This file is auto-generated by index.py
    225 ## DO NOT EDIT !!!
    226 ##
    227 
    228 AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(srcdir)/include
    229 AM_CFLAGS = $(THREAD_CFLAGS) $(Z_CFLAGS)
    230 LDADD = $(RDL_LIBS) $(STATIC_BINARIES) $(top_builddir)/libxml2.la $(THREAD_LIBS) $(Z_LIBS) $(ICONV_LIBS) -lm $(WIN32_EXTRA_LIBADD)
    231 
    232 CLEANFILES = *.tmp
    233 
    234 if REBUILD_DOCS
    235 rebuild: examples.xml index.html
    236 .PHONY: rebuild
    237 
    238 examples.xml: index.py $(noinst_PROGRAMS:=.c)
    239 	cd $(srcdir) && $(PYTHON) index.py
    240 	$(MAKE) Makefile
    241 
    242 index.html: examples.xml examples.xsl
    243 	cd $(srcdir) && xsltproc examples.xsl examples.xml && echo "Rebuilt web page"
    244 	-cd $(srcdir) && xmllint --valid --noout index.html
    245 endif
    246 
    247 install-data-local: 
    248 	$(MKDIR_P) $(DESTDIR)$(HTML_DIR)
    249 	-$(INSTALL) -m 0644 $(srcdir)/*.html $(srcdir)/*.c $(srcdir)/*.xml $(srcdir)/*.xsl $(srcdir)/*.res $(DESTDIR)$(HTML_DIR)
    250 
    251 clean-local:
    252 	test -f Makefile.am || rm -f test?.xml
    253 
    254 """
    255     examples.sort()
    256     extras.sort()
    257     tests.sort()
    258     EXTRA_DIST=""
    259     for extra in extras:
    260         EXTRA_DIST = EXTRA_DIST + " \\\n\t" + extra
    261     Makefile = Makefile + "EXTRA_DIST =%s\n\n" % (EXTRA_DIST)
    262     noinst_PROGRAMS=""
    263     for example in examples:
    264         noinst_PROGRAMS = noinst_PROGRAMS + " \\\n\t" + example
    265     Makefile = Makefile + "noinst_PROGRAMS =%s\n\n" % (noinst_PROGRAMS)
    266     for example in examples:
    267         Makefile = Makefile + "%s_SOURCES = %s.c\n\n" % (example, example)
    268     Makefile = Makefile + "valgrind: \n\t$(MAKE) CHECKER='valgrind' tests\n\n"
    269     Makefile = Makefile + "tests: $(noinst_PROGRAMS)\n"
    270     Makefile = Makefile + "\ttest -f Makefile.am || test -f test1.xml || $(LN_S) $(srcdir)/test?.xml .\n"
    271     Makefile = Makefile + "\t@(echo '## examples regression tests')\n"
    272     Makefile = Makefile + "\t@(echo > .memdump)\n"
    273     for test in tests:
    274         Makefile = Makefile + "\t$(CHECKER) %s\n" % (test)
    275         Makefile = Makefile + '\t@grep "MORY ALLO" .memdump | grep -v "MEMORY ALLOCATED : 0" ; exit 0\n'
    276     try:
    277 	old = open("Makefile.am", "r").read()
    278 	if old != Makefile:
    279 	    n = open("Makefile.am", "w").write(Makefile)
    280 	    print "Updated Makefile.am"
    281     except:
    282         print "Failed to read or save Makefile.am"
    283 #    #
    284 #    # Autogenerate the .cvsignore too ... DEPRECATED
    285 #    #
    286 #    ignore = """.memdump
    287 #Makefile.in
    288 #Makefile
    289 #"""
    290 #    for example in examples:
    291 #        ignore = ignore + "%s\n" % (example)
    292 #    try:
    293 #	old = open(".cvsignore", "r").read()
    294 #	if old != ignore:
    295 #	    n = open(".cvsignore", "w").write(ignore)
    296 #	    print "Updated .cvsignore"
    297 #    except:
    298 #        print "Failed to read or save .cvsignore"
    299 
    300 if __name__ == "__main__":
    301     load_api()
    302     output = open("examples.xml", "w")
    303     output.write("<examples>\n")
    304 
    305     for file in glob.glob('*.c'):
    306 	parse(file, output)
    307 	examples.append(file[:-2])
    308 
    309     dump_symbols(output)
    310     dump_sections(output)
    311     output.write("</examples>\n")
    312     output.close()
    313     dump_Makefile()
    314 
    315