From affce30bc41659bbfd60646db94fde37152f20c6 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Mon, 18 Dec 2017 12:28:42 +0200 Subject: Add: ql-ikiwiki-preprocess --- ql-ikiwiki-preprocess | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100755 ql-ikiwiki-preprocess diff --git a/ql-ikiwiki-preprocess b/ql-ikiwiki-preprocess new file mode 100755 index 0000000..2803043 --- /dev/null +++ b/ql-ikiwiki-preprocess @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +# Copyright 2017 QvarnLabs Ab +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# =*= License: GPL-3+ =*= + + +import os +import shutil +import subprocess +import sys +import tempfile + +import cliapp + + +def write_temp(content, suffix, tgtdir): + fd, filename = tempfile.mkstemp(dir=tgtdir, suffix=suffix) + os.close(fd) + with open(filename, 'w') as f: + f.write(content) + return filename + + +def get_uml_title(lines): + prefix = 'title ' + for line in lines: + line = line.strip() + if line.startswith(prefix): + return line[len(prefix):] + return 'title' + + +def format_uml(filename): + subprocess.check_call(['plantuml', '-tpng', '-output', '.', filename]) + + +def remove_prefixes(lines): + prefix = ' ' * 4 + result = [] + for line in lines: + if line.startswith(prefix): + line = line[len(prefix):] + result.append(line) + return ''.join(result) + + +def process_uml(lines, tgtdir): + title = get_uml_title(lines) + uml_text = remove_prefixes(lines) + filename = write_temp(uml_text, '.uml', tgtdir) + format_uml(filename) + prefix, _ = os.path.splitext(filename) + image = prefix + '.png' + relative = make_relative(os.path.abspath(tgtdir), image) + include = '[[!img {}]]\n'.format(relative) + return [include] + + +def format_roadmap(text): + input = text.encode('utf-8') + output = cliapp.runcmd(['projgraph'], feed_stdin=input) + output_text = output.decode('utf-8') + output_lines = output_text.splitlines() + return '\n'.join(output_lines[1:-1]) + + +def process_roadmap(lines, tgtdir): + roadmap_text = remove_prefixes(lines[1:-1]) + include = format_roadmap(roadmap_text) + return ['[[!graph src="""\n', include, '"""]]\n'] + + +def read_lines(filename): + with open(filename) as f: + return f.readlines() + + +def write_lines(filename, lines): + with open(filename, 'w') as f: + f.write(''.join(lines)) + + +def line_matches(line, patterns): + prefix = ' ' * 4 + for i, pattern in enumerate(patterns): + if line in [pattern, prefix + pattern]: + return i + return None + + +def find_match(lines, patterns): + for i, line in enumerate(lines): + j = line_matches(line, patterns) + if j is not None: + return i, j + else: + return None, None + + +def find_section(lines, sections): + starters = [x for x, _, _ in sections] + enders = [x for _, x, _ in sections] + funcs = [x for _, _, x in sections] + + first, section = find_match(lines, starters) + if first is not None: + last, _ = find_match(lines, enders[section:section+1]) + if last is not None: + return first, last+1, funcs[section] + return 0, len(lines), None + + +def preprocess_lines(lines, tgtdir): + sections = [ + ('@startuml\n', '@enduml\n', process_uml), + ('@startroadmap\n', '@endroadmap\n', process_roadmap), + ] + + result = [] + while lines: + start, after, func = find_section(lines, sections) + result.extend(lines[:start]) + section = lines[start:after] + if func: + include = func(section, tgtdir) + result.extend(include) + else: + result.extend(section) + lines = lines[after:] + return result + + +def preprocess_mdwn(src, tgt): + lines = read_lines(src) + tgtdir = os.path.dirname(tgt) + lines = preprocess_lines(lines, tgtdir) + write_lines(tgt, lines) + shutil.copystat(src, tgt) + + +def copy_file(src, tgt): + shutil.copy(src, tgt) + shutil.copystat(src, tgt) + + +def preprocess_file(src, tgt): + if src.endswith('.mdwn'): + preprocess_mdwn(src, tgt) + else: + copy_file(src, tgt) + + +def copy_dir(src, tgt): + os.makedirs(tgt) + return lambda: shutil.copystat(src, tgt) + + +def acceptable(basename): + return basename != '.git' + + +def make_relative(dirname, pathname): + prefix = dirname + if not prefix.endswith('/'): + prefix += '/' + + assert pathname == dirname or pathname.startswith(prefix) + if pathname == dirname: + return '.' + else: + return pathname[len(prefix):] + + +def find_all(root): + for dirpath, subdirs, filenames in os.walk(root): + yield dirpath + for x in subdirs[:]: + if not acceptable(x): + subdirs.remove(x) + for basename in filenames: + yield os.path.join(dirpath, basename) + + +def find_relative(dirname): + for pathname in find_all(dirname): + yield make_relative(dirname, pathname) + + +def make_absolute(root, relative): + return os.path.join(root, relative) + + +def preprocess(srcdir, tgtdir): + preprocessors = [ + (os.path.isfile, preprocess_file), + (os.path.isdir, copy_dir), + ] + + assert os.path.isdir(srcdir) + assert not os.path.exists(tgtdir) + todo = [] + for pathname in find_relative(srcdir): + src = make_absolute(srcdir, pathname) + tgt = make_absolute(tgtdir, pathname) + + for func, callback in preprocessors: + if func(src): + do = callback(src, tgt) + if do is not None: + todo.append(do) + + for do in todo: + do() + + +def main(): + srcdir = sys.argv[1] + tgtdir = sys.argv[2] + preprocess(srcdir, tgtdir) + + +if __name__ == '__main__': + main() -- cgit v1.2.1