Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 
      3 # A tiny Python script to perform substitutions in the NDK documentation
      4 # .text input files before processing them with Markdown.
      5 #
      6 
      7 import re
      8 import argparse
      9 import sys
     10 
     11 class Filter:
     12   def __init__(self,pattern,replacement):
     13     self.pattern = re.compile(pattern)
     14     self.replacement = replacement
     15 
     16   def process(self, line):
     17     return self.pattern.sub(self.replacement, line)
     18 
     19 all_filters = []
     20 all_filter_tests = []
     21 
     22 def add_filter(pattern, replacement):
     23   global all_filters
     24   filter = Filter(pattern, replacement)
     25   all_filters.append(filter)
     26 
     27 def add_filter_test(input, expected):
     28   global all_filter_tests
     29   all_filter_tests.append((input, expected))
     30 
     31 def run_all_tests():
     32   global all_filter_tests
     33   count = 0
     34   failed_tests = []
     35   for input_string, expected in all_filter_tests:
     36     string = input_string
     37     print "Testing: '%s'" % input_string,
     38     for f in all_filters:
     39       string = f.process(string)
     40     if string != expected:
     41       failed_tests.append((input_string, expected, string))
     42       print "  KO!"
     43       print "  Got     : '%s'" % string
     44       print "  Expected: '%s'" % expected
     45     else:
     46       print "ok."
     47     count += 1
     48 
     49   return count, failed_tests
     50 
     51 # Auto-linkify documentation
     52 #
     53 #     d/NDK-BUILD
     54 # -> [NDK-BUILD](NDK-BUILD.html)
     55 #
     56 add_filter(r"(^|\s+)d/([^\s.]+)", r"\1[\2](\2.html)")
     57 
     58 add_filter_test("d/NDK-BUILD", "[NDK-BUILD](NDK-BUILD.html)")
     59 add_filter_test("aa d/NDK-BUILD", "aa [NDK-BUILD](NDK-BUILD.html)")
     60 add_filter_test("ad/NDK-BUILD", "ad/NDK-BUILD")
     61 add_filter_test("d/NDK-BUILD.", "[NDK-BUILD](NDK-BUILD.html).")
     62 
     63 # Auto-linkify documentation
     64 #    NDK-BUILD.html
     65 # -> [NDK-BUILD](NDK-BUILD.html)
     66 #
     67 add_filter(r"(^|\s+)([A-Z0-9-]+)\.html", r"\1[\2](\2.html)")
     68 add_filter_test("NDK-BUILD.html", "[NDK-BUILD](NDK-BUILD.html)")
     69 add_filter_test("NDK-BUILD.html.", "[NDK-BUILD](NDK-BUILD.html).")
     70 add_filter_test("aa NDK-BUILD.html", "aa [NDK-BUILD](NDK-BUILD.html)")
     71 
     72 add_filter(r"(^|\s+)(\$NDK/docs/|docs/)([A-Z0-9_-]+)\.html", r"\1[\3](\3.html)")
     73 add_filter_test("$NDK/docs/ANDROID-MK.html", "[ANDROID-MK](ANDROID-MK.html)")
     74 add_filter_test("See docs/ANDROID-MK.html.", "See [ANDROID-MK](ANDROID-MK.html).")
     75 
     76 # Auto quote script file.
     77 #    make-standalone-toolchain.sh
     78 # -> `make-standalone-toolchain.sh`
     79 add_filter(r"(^|\s+)([^\s]+\.sh)", r"\1`\2`")
     80 add_filter_test("make-standalone-toolchain.sh", "`make-standalone-toolchain.sh`")
     81 
     82 # Auto-linkify bug entries:
     83 #
     84 #    http://b.android.com/<number>
     85 # or http://code.google.com/p/android/issues/detail?id=<number>
     86 # -> [b/<number>](http://b.android.com/<number>)
     87 #
     88 add_filter(
     89   r"http://(code\.google\.com/p/android/issues/detail\?id=|b\.android\.com/)([0-9]+)",
     90   r"[b/\2](http://b.android.com/\2)")
     91 add_filter_test(r"See http://b.android.com/12345", r"See [b/12345](http://b.android.com/12345)")
     92 add_filter_test(r"See http://code.google.com/p/android/issues/detail?id=12345", r"See [b/12345](http://b.android.com/12345)")
     93 
     94 # Auto-linkify bug shortcuts like b/1000
     95 #
     96 #    b/<number> after space or start of line
     97 # -> [b/<number>](http://b.android.com/<number>)
     98 add_filter(
     99   r"(^|\s+)(b/([0-9]+))",
    100   r"\1[\2](http://b.android.com/\3)")
    101 add_filter_test(r"b/12345", r"[b/12345](http://b.android.com/12345)")
    102 add_filter_test(r"See b/12345.", r"See [b/12345](http://b.android.com/12345).")
    103 add_filter_test(r"[b/12345](....)", r"[b/12345](....)")
    104 
    105 # Auto-linkify patch entries.
    106 #    https://android-review.googlesource.com/#/c/<number>
    107 # -> [r/<number>](https://android-review.googlesource.com/#/c/<number>)
    108 add_filter(
    109   r"(^|\s+)(https://android-review\.googlesource\.com/\#/c/([0-9]+))",
    110   r"\1[r/\3](\2)")
    111 add_filter_test(r"https://android-review.googlesource.com/#/c/12345", r"[r/12345](https://android-review.googlesource.com/#/c/12345)")
    112 
    113 # Auto-linkify anything
    114 #    http://www.example.com
    115 # -> <http://www.example.com>
    116 add_filter(r"(^|\s+)((ssh|http|https|ftp)://[^\s]+)", r"\1<\2>")
    117 add_filter_test("http://example.com", "<http://example.com>")
    118 
    119 
    120 #    r/<number> not followed by (...)
    121 # -> [r/<number>](https://android-review.googlesource.com/#/c/<number>)
    122 add_filter(
    123   r"(^|\s+)(r/([0-9]+))",
    124   r"\1[\2](https://android-review.googlesource.com/#/c/\3)")
    125 add_filter_test(
    126   r"r/12345",
    127   r"[r/12345](https://android-review.googlesource.com/#/c/12345)")
    128 
    129 # Auto format __ANDROID__, __ARM_ARCH*__, etc..
    130 #    __XXX__
    131 # -> `__XXX__`
    132 add_filter(r"(__[A-Z][^\s]*)", r"`\1`")
    133 add_filter_test(r"__ANDROID__", r"`__ANDROID__`")
    134 add_filter_test(r"__ARM_ARCH_5*__", r"`__ARM_ARCH_5*__`")
    135 
    136 # Auto-format compiler/linker flags:
    137 #    -O2
    138 # -> `-O2`
    139 add_filter(r"(^|\s+)(\-[\w][^\s]+)", r"\1`\2`")
    140 add_filter_test(r"-O2", r"`-O2`")
    141 add_filter_test(r" -fPIC", r" `-fPIC`")
    142 add_filter_test(r" -mfpu=neon xxx", r" `-mfpu=neon` xxx")
    143 add_filter_test(r" -mfpu=vfpd3-d16", r" `-mfpu=vfpd3-d16`")
    144 
    145 # Auto-format LOCAL_XXX, APP_XXX and NDK_XXX variables
    146 # as well as assignments.
    147 add_filter(r"(^|\s+)([A-Z_0-9]+=[^\s]+)", r"\1`\2`")
    148 add_filter_test("Use NDK_DEBUG=release", "Use `NDK_DEBUG=release`")
    149 add_filter_test("NDK_HOST_32BIT=1", "`NDK_HOST_32BIT=1`")
    150 
    151 add_filter(r"(^|\s+)((APP_|NDK_|LOCAL_)[A-Z0-9_]*)", r"\1`\2`")
    152 add_filter_test("LOCAL_MODULE", "`LOCAL_MODULE`")
    153 
    154 # Auto-format __cxa_xxxxx and other prefixes.
    155 #
    156 add_filter(r"(^|\s+)((__cxa_|__dso_|__aeabi_|__atomic_|__sync_)[A-Za-z0-9_]+)", r"\1`\2`")
    157 add_filter_test("__cxa_begin_cleanup", "`__cxa_begin_cleanup`")
    158 add_filter_test("__dso_handle", "`__dso_handle`")
    159 add_filter_test("__aeabi_idiv2", "`__aeabi_idiv2`")
    160 add_filter_test("See __cxa_cleanup.", "See `__cxa_cleanup`.")
    161 
    162 re_blockquote = re.compile(r"^        ")
    163 
    164 def process(input_file, output_file):
    165   # Process lines, we need to take care or _not_ processing
    166   # block-quoted lines. For our needs, these begin with 8 spaces.
    167   #
    168   in_list = False
    169   margins = [ 0 ]
    170   margin_index = 0
    171   for line in input_file:
    172     do_process = True
    173     if len(line.strip()):
    174       if not re_blockquote.match(line):
    175         for f in all_filters:
    176           line = f.process(line)
    177     output_file.write(line)
    178 
    179 def main():
    180     parser = argparse.ArgumentParser(description='''
    181     Perform .text substitution before Markdown processing.''')
    182 
    183     parser.add_argument( '-o', '--output',
    184                          help="Specify output file, stdout otherwise.",
    185                          dest='output',
    186                          default=None )
    187 
    188     parser.add_argument( '--run-checks',
    189                          help="Run internal unit tests.",
    190                          action='store_true',
    191                          dest='run_checks',
    192                          default=False )
    193 
    194     parser.add_argument( 'input_file',
    195                          help="Input file, stdin if not specified.",
    196                          nargs="?",
    197                          default=None )
    198 
    199     args = parser.parse_args()
    200 
    201     if args.run_checks:
    202       count, failed_tests = run_all_tests()
    203       if failed_tests:
    204         sys.stderr.write("ERROR: %d tests out of %d failed:\n" % (len(failed_tests), count))
    205         for failed in failed_tests:
    206           sys.stderr.write("  '%s' -> '%s' (expected '%s')\n" % (failed[0], failed[2], failed[1]))
    207         sys.exit(1)
    208       else:
    209         print "%d tests passed. Congratulations!" % count
    210         sys.exit(0)
    211 
    212     if args.input_file:
    213       try:
    214         in_file = open(args.input_file, "rt")
    215       except:
    216         sys.stderr.write("Error: Can't read input file: %s: %s\n" % args.input_file, repr(e))
    217         sys.exit(1)
    218     else:
    219       in_file = sys.stdin
    220 
    221     if args.output:
    222       try:
    223         out_file = open(args.output, "wt")
    224       except:
    225         sys.stderr.write("Error: Can't open output file: %s: %s\n" % args.output, repr(e))
    226         sys.exit(1)
    227     else:
    228       out_file = sys.stdout
    229 
    230     process(in_file, out_file)
    231 
    232     out_file.close()
    233     in_file.close()
    234     sys.exit(0)
    235 
    236 if __name__ == '__main__':
    237     main()
    238 
    239