12eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerimport os
22eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerimport os.path
32eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerfrom fnmatch import fnmatch
42eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerimport targz
52eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
62eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##def DoxyfileParse(file_contents):
72eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   """
82eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   Parse a Doxygen source file and return a dictionary of all the values.
92eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   Values will be strings and lists of strings.
102eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   """
112eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   data = {}
122eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
132eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   import shlex
142eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   lex = shlex.shlex(instream = file_contents, posix = True)
152eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   lex.wordchars += "*+./-:"
162eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   lex.whitespace = lex.whitespace.replace("\n", "")
172eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   lex.escape = ""
182eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
192eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   lineno = lex.lineno
202eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   last_backslash_lineno = lineno
212eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   token = lex.get_token()
222eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   key = token   # the first token should be a key
232eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   last_token = ""
242eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   key_token = False
252eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   next_key = False
262eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   new_data = True
272eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
282eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   def append_data(data, key, new_data, token):
292eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      if new_data or len(data[key]) == 0:
302eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         data[key].append(token)
312eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      else:
322eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         data[key][-1] += token
332eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
342eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   while token:
352eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      if token in ['\n']:
362eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         if last_token not in ['\\']:
372eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##            key_token = True
382eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      elif token in ['\\']:
392eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         pass
402eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      elif key_token:
412eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         key = token
422eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         key_token = False
432eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      else:
442eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         if token == "+=":
452eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##            if not data.has_key(key):
462eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##               data[key] = list()
472eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         elif token == "=":
482eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##            data[key] = list()
492eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         else:
502eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##            append_data( data, key, new_data, token )
512eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##            new_data = True
522eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
532eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      last_token = token
542eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      token = lex.get_token()
552eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
562eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      if last_token == '\\' and token != '\n':
572eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         new_data = False
582eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         append_data( data, key, new_data, '\\' )
592eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
602eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   # compress lists of len 1 into single strings
612eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   for (k, v) in data.items():
622eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      if len(v) == 0:
632eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         data.pop(k)
642eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
652eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      # items in the following list will be kept as lists and not converted to strings
662eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      if k in ["INPUT", "FILE_PATTERNS", "EXCLUDE_PATTERNS"]:
672eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         continue
682eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
692eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      if len(v) == 1:
702eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         data[k] = v[0]
712eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
722eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   return data
732eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
742eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##def DoxySourceScan(node, env, path):
752eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   """
762eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   Doxygen Doxyfile source scanner.  This should scan the Doxygen file and add
772eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   any files used to generate docs to the list of source files.
782eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   """
792eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   default_file_patterns = [
802eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      '*.c', '*.cc', '*.cxx', '*.cpp', '*.c++', '*.java', '*.ii', '*.ixx',
812eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      '*.ipp', '*.i++', '*.inl', '*.h', '*.hh ', '*.hxx', '*.hpp', '*.h++',
822eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      '*.idl', '*.odl', '*.cs', '*.php', '*.php3', '*.inc', '*.m', '*.mm',
832eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      '*.py',
842eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   ]
852eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
862eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   default_exclude_patterns = [
872eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      '*~',
882eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   ]
892eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
902eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   sources = []
912eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
922eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   data = DoxyfileParse(node.get_contents())
932eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
942eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   if data.get("RECURSIVE", "NO") == "YES":
952eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      recursive = True
962eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   else:
972eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      recursive = False
982eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
992eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   file_patterns = data.get("FILE_PATTERNS", default_file_patterns)
1002eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   exclude_patterns = data.get("EXCLUDE_PATTERNS", default_exclude_patterns)
1012eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
1022eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   for node in data.get("INPUT", []):
1032eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      if os.path.isfile(node):
1042eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         sources.add(node)
1052eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      elif os.path.isdir(node):
1062eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         if recursive:
1072eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##            for root, dirs, files in os.walk(node):
1082eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##               for f in files:
1092eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##                  filename = os.path.join(root, f)
1102eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
1112eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##                  pattern_check = reduce(lambda x, y: x or bool(fnmatch(filename, y)), file_patterns, False)
1122eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##                  exclude_check = reduce(lambda x, y: x and fnmatch(filename, y), exclude_patterns, True)
1132eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
1142eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##                  if pattern_check and not exclude_check:
1152eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##                     sources.append(filename)
1162eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         else:
1172eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##            for pattern in file_patterns:
1182eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##               sources.extend(glob.glob("/".join([node, pattern])))
1192eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   sources = map( lambda path: env.File(path), sources )
1202eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   return sources
1212eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
1222eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
1232eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##def DoxySourceScanCheck(node, env):
1242eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   """Check if we should scan this file"""
1252eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   return os.path.isfile(node.path)
1262eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
1272eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerdef srcDistEmitter(source, target, env):
1282eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   """Doxygen Doxyfile emitter"""
1292eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   # possible output formats and their default values and output locations
1302eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   output_formats = {
1312eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      "HTML": ("YES", "html"),
1322eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      "LATEX": ("YES", "latex"),
1332eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      "RTF": ("NO", "rtf"),
1342eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      "MAN": ("YES", "man"),
1352eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      "XML": ("NO", "xml"),
1362eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   }
1372eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
1382eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   data = DoxyfileParse(source[0].get_contents())
1392eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
1402eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   targets = []
1412eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   out_dir = data.get("OUTPUT_DIRECTORY", ".")
1422eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
1432eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   # add our output locations
1442eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   for (k, v) in output_formats.items():
1452eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      if data.get("GENERATE_" + k, v[0]) == "YES":
1462eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##         targets.append(env.Dir( os.path.join(out_dir, data.get(k + "_OUTPUT", v[1]))) )
1472eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
1482eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   # don't clobber targets
1492eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   for node in targets:
1502eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      env.Precious(node)
1512eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
1522eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   # set up cleaning stuff
1532eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   for node in targets:
1542eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      env.Clean(node, node)
1552eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##
1562eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   return (targets, source)
1572eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger   return (target,source)
1582eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
1592eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerdef generate(env):
1602eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger   """
1612eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger   Add builders and construction variables for the
1622eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger   SrcDist tool.
1632eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger   """
1642eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   doxyfile_scanner = env.Scanner(
1652eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      DoxySourceScan,
1662eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      "DoxySourceScan",
1672eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##      scan_check = DoxySourceScanCheck,
1682eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##   )
1692eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
1702eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger   if targz.exists(env):
1712eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger      srcdist_builder = targz.makeBuilder( srcDistEmitter )
1722eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
1732eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger      env['BUILDERS']['SrcDist'] = srcdist_builder
1742eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
1752eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerdef exists(env):
1762eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger   """
1772eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger   Make sure srcdist exists.
1782eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger   """
1792eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger   return targz.exists(env)
180