1#!/usr/bin/env python3
2#
3# Copyright (c) 2018, Nordic Semiconductor ASA
4# Copyright (c) 2017-2022, Intel Corporation
5#
6# SPDX-License-Identifier: Apache-2.0
7
8# Very quick script to move docs from different places into the doc directory
9# to fix the website and external links
10
11import argparse
12import errno
13import filecmp
14import fnmatch
15import os
16import re
17import shutil
18import sys
19
20# directives to parse for included files
21DIRECTIVES = ["figure","include","image","literalinclude", "graphviz"]
22
23ZEPHYR_BASE = "../"
24ZEPHYR_BUILD = None
25
26def copy_if_different(src, dst):
27    # Copies 'src' as 'dst', but only if dst does not exist or if itx contents
28    # differ from src.This avoids unnecessary # timestamp updates, which
29    # trigger documentation rebuilds.
30    if os.path.exists(dst) and filecmp.cmp(src, dst):
31        return
32    shutil.copyfile(src, dst)
33
34def get_files(all, dest, dir):
35    matches = []
36    for root, dirnames, filenames in os.walk('%s/%s' %(ZEPHYR_BASE, dir)):
37        if ZEPHYR_BUILD:
38            if os.path.normpath(root).startswith(os.path.normpath(ZEPHYR_BUILD)):
39                # Build folder, skip it
40                continue
41
42        for filename in fnmatch.filter(filenames, '*' if all else '*.rst'):
43            matches.append(os.path.join(root, filename))
44    for file in matches:
45        frel = file.replace(ZEPHYR_BASE,"").strip("/")
46        dir=os.path.dirname(frel)
47        if not os.path.exists(os.path.join(dest, dir)):
48            os.makedirs(os.path.join(dest, dir))
49
50        copy_if_different(file, os.path.join(dest, frel))
51
52        # Inspect only .rst files for directives referencing other files
53        # we'll need to copy (as configured in the DIRECTIVES variable)
54        if not fnmatch.fnmatch(file, "*.rst"):
55            continue
56
57        try:
58            with open(file, encoding="utf-8") as f:
59                content = f.readlines()
60
61            content = [x.strip() for x in content]
62            directives = "|".join(DIRECTIVES)
63            pattern = re.compile("\s*\.\.\s+(%s)::\s+(.*)" %directives)
64            for l in content:
65                m = pattern.match(l)
66                if m:
67                    inf = m.group(2)
68                    ind = os.path.dirname(inf)
69                    if not os.path.exists(os.path.join(dest, dir, ind)):
70                        os.makedirs(os.path.join(dest, dir, ind))
71
72                    src = os.path.join(ZEPHYR_BASE, dir, inf)
73                    dst = os.path.join(dest, dir, inf)
74                    try:
75                        copy_if_different(src, dst)
76
77                    except FileNotFoundError:
78                        sys.stderr.write("File not found: %s\n  reference by %s\n" % (inf, file))
79
80        except UnicodeDecodeError as e:
81            sys.stderr.write(
82                "Malformed {} in {}\n"
83                "  Context: {}\n"
84                "  Problematic data: {}\n"
85                "  Reason: {}\n".format(
86                    e.encoding, file,
87                    e.object[max(e.start - 40, 0):e.end + 40],
88                    e.object[e.start:e.end],
89                    e.reason))
90
91        f.close()
92
93def main():
94
95    parser = argparse.ArgumentParser(description='Recursively copy .rst files '
96                                     'from the origin folder(s) to the '
97                                     'destination folder, plus files referenced '
98                                     'in those .rst files by a configurable '
99                                     'list of directives: {}.'.format(DIRECTIVES))
100
101    parser.add_argument('-a', '--all', action='store_true', help='Copy all files '
102                        '(recursively) in the specified source folder(s).')
103    parser.add_argument('dest', nargs=1)
104    parser.add_argument('src', nargs='+')
105    args = parser.parse_args()
106
107    dest = args.dest[0]
108
109    for d in args.src:
110        get_files(args.all, dest, d)
111
112if __name__ == "__main__":
113    main()
114