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