#!/usr/bin/env python
# A tiny Python script to perform substitutions in the NDK documentation
# .text input files before processing them with Markdown.
#
import re
import argparse
import sys
class Filter:
def __init__(self,pattern,replacement):
self.pattern = re.compile(pattern)
self.replacement = replacement
def process(self, line):
return self.pattern.sub(self.replacement, line)
all_filters = []
all_filter_tests = []
def add_filter(pattern, replacement):
global all_filters
filter = Filter(pattern, replacement)
all_filters.append(filter)
def add_filter_test(input, expected):
global all_filter_tests
all_filter_tests.append((input, expected))
def run_all_tests():
global all_filter_tests
count = 0
failed_tests = []
for input_string, expected in all_filter_tests:
string = input_string
print "Testing: '%s'" % input_string,
for f in all_filters:
string = f.process(string)
if string != expected:
failed_tests.append((input_string, expected, string))
print " KO!"
print " Got : '%s'" % string
print " Expected: '%s'" % expected
else:
print "ok."
count += 1
return count, failed_tests
# Auto-linkify documentation
#
# d/NDK-BUILD
# -> [NDK-BUILD](NDK-BUILD.html)
#
add_filter(r"(^|\s+)d/([^\s.]+)", r"\1[\2](\2.html)")
add_filter_test("d/NDK-BUILD", "[NDK-BUILD](NDK-BUILD.html)")
add_filter_test("aa d/NDK-BUILD", "aa [NDK-BUILD](NDK-BUILD.html)")
add_filter_test("ad/NDK-BUILD", "ad/NDK-BUILD")
add_filter_test("d/NDK-BUILD.", "[NDK-BUILD](NDK-BUILD.html).")
# Auto-linkify documentation
# NDK-BUILD.html
# -> [NDK-BUILD](NDK-BUILD.html)
#
add_filter(r"(^|\s+)([A-Z0-9-]+)\.html", r"\1[\2](\2.html)")
add_filter_test("NDK-BUILD.html", "[NDK-BUILD](NDK-BUILD.html)")
add_filter_test("NDK-BUILD.html.", "[NDK-BUILD](NDK-BUILD.html).")
add_filter_test("aa NDK-BUILD.html", "aa [NDK-BUILD](NDK-BUILD.html)")
add_filter(r"(^|\s+)(\$NDK/docs/|docs/)([A-Z0-9_-]+)\.html", r"\1[\3](\3.html)")
add_filter_test("$NDK/docs/ANDROID-MK.html", "[ANDROID-MK](ANDROID-MK.html)")
add_filter_test("See docs/ANDROID-MK.html.", "See [ANDROID-MK](ANDROID-MK.html).")
# Auto quote script file.
# make-standalone-toolchain.sh
# -> `make-standalone-toolchain.sh`
add_filter(r"(^|\s+)([^\s]+\.sh)", r"\1`\2`")
add_filter_test("make-standalone-toolchain.sh", "`make-standalone-toolchain.sh`")
# Auto-linkify bug entries:
#
# http://b.android.com/<number>
# or http://code.google.com/p/android/issues/detail?id=<number>
# -> [b/<number>](http://b.android.com/<number>)
#
add_filter(
r"http://(code\.google\.com/p/android/issues/detail\?id=|b\.android\.com/)([0-9]+)",
r"[b/\2](http://b.android.com/\2)")
add_filter_test(r"See http://b.android.com/12345", r"See [b/12345](http://b.android.com/12345)")
add_filter_test(r"See http://code.google.com/p/android/issues/detail?id=12345", r"See [b/12345](http://b.android.com/12345)")
# Auto-linkify bug shortcuts like b/1000
#
# b/<number> after space or start of line
# -> [b/<number>](http://b.android.com/<number>)
add_filter(
r"(^|\s+)(b/([0-9]+))",
r"\1[\2](http://b.android.com/\3)")
add_filter_test(r"b/12345", r"[b/12345](http://b.android.com/12345)")
add_filter_test(r"See b/12345.", r"See [b/12345](http://b.android.com/12345).")
add_filter_test(r"[b/12345](....)", r"[b/12345](....)")
# Auto-linkify patch entries.
# https://android-review.googlesource.com/#/c/<number>
# -> [r/<number>](https://android-review.googlesource.com/#/c/<number>)
add_filter(
r"(^|\s+)(https://android-review\.googlesource\.com/\#/c/([0-9]+))",
r"\1[r/\3](\2)")
add_filter_test(r"https://android-review.googlesource.com/#/c/12345", r"[r/12345](https://android-review.googlesource.com/#/c/12345)")
# Auto-linkify anything
# http://www.example.com
# -> <http://www.example.com>
add_filter(r"(^|\s+)((ssh|http|https|ftp)://[^\s]+)", r"\1<\2>")
add_filter_test("http://example.com", "<http://example.com>")
# r/<number> not followed by (...)
# -> [r/<number>](https://android-review.googlesource.com/#/c/<number>)
add_filter(
r"(^|\s+)(r/([0-9]+))",
r"\1[\2](https://android-review.googlesource.com/#/c/\3)")
add_filter_test(
r"r/12345",
r"[r/12345](https://android-review.googlesource.com/#/c/12345)")
# Auto format __ANDROID__, __ARM_ARCH*__, etc..
# __XXX__
# -> `__XXX__`
add_filter(r"(__[A-Z][^\s]*)", r"`\1`")
add_filter_test(r"__ANDROID__", r"`__ANDROID__`")
add_filter_test(r"__ARM_ARCH_5*__", r"`__ARM_ARCH_5*__`")
# Auto-format compiler/linker flags:
# -O2
# -> `-O2`
add_filter(r"(^|\s+)(\-[\w][^\s]+)", r"\1`\2`")
add_filter_test(r"-O2", r"`-O2`")
add_filter_test(r" -fPIC", r" `-fPIC`")
add_filter_test(r" -mfpu=neon xxx", r" `-mfpu=neon` xxx")
add_filter_test(r" -mfpu=vfpd3-d16", r" `-mfpu=vfpd3-d16`")
# Auto-format LOCAL_XXX, APP_XXX and NDK_XXX variables
# as well as assignments.
add_filter(r"(^|\s+)([A-Z_0-9]+=[^\s]+)", r"\1`\2`")
add_filter_test("Use NDK_DEBUG=release", "Use `NDK_DEBUG=release`")
add_filter_test("NDK_HOST_32BIT=1", "`NDK_HOST_32BIT=1`")
add_filter(r"(^|\s+)((APP_|NDK_|LOCAL_)[A-Z0-9_]*)", r"\1`\2`")
add_filter_test("LOCAL_MODULE", "`LOCAL_MODULE`")
# Auto-format __cxa_xxxxx and other prefixes.
#
add_filter(r"(^|\s+)((__cxa_|__dso_|__aeabi_|__atomic_|__sync_)[A-Za-z0-9_]+)", r"\1`\2`")
add_filter_test("__cxa_begin_cleanup", "`__cxa_begin_cleanup`")
add_filter_test("__dso_handle", "`__dso_handle`")
add_filter_test("__aeabi_idiv2", "`__aeabi_idiv2`")
add_filter_test("See __cxa_cleanup.", "See `__cxa_cleanup`.")
re_blockquote = re.compile(r"^ ")
def process(input_file, output_file):
# Process lines, we need to take care or _not_ processing
# block-quoted lines. For our needs, these begin with 8 spaces.
#
in_list = False
margins = [ 0 ]
margin_index = 0
for line in input_file:
do_process = True
if len(line.strip()):
if not re_blockquote.match(line):
for f in all_filters:
line = f.process(line)
output_file.write(line)
def main():
parser = argparse.ArgumentParser(description='''
Perform .text substitution before Markdown processing.''')
parser.add_argument( '-o', '--output',
help="Specify output file, stdout otherwise.",
dest='output',
default=None )
parser.add_argument( '--run-checks',
help="Run internal unit tests.",
action='store_true',
dest='run_checks',
default=False )
parser.add_argument( 'input_file',
help="Input file, stdin if not specified.",
nargs="?",
default=None )
args = parser.parse_args()
if args.run_checks:
count, failed_tests = run_all_tests()
if failed_tests:
sys.stderr.write("ERROR: %d tests out of %d failed:\n" % (len(failed_tests), count))
for failed in failed_tests:
sys.stderr.write(" '%s' -> '%s' (expected '%s')\n" % (failed[0], failed[2], failed[1]))
sys.exit(1)
else:
print "%d tests passed. Congratulations!" % count
sys.exit(0)
if args.input_file:
try:
in_file = open(args.input_file, "rt")
except:
sys.stderr.write("Error: Can't read input file: %s: %s\n" % args.input_file, repr(e))
sys.exit(1)
else:
in_file = sys.stdin
if args.output:
try:
out_file = open(args.output, "wt")
except:
sys.stderr.write("Error: Can't open output file: %s: %s\n" % args.output, repr(e))
sys.exit(1)
else:
out_file = sys.stdout
process(in_file, out_file)
out_file.close()
in_file.close()
sys.exit(0)
if __name__ == '__main__':
main()