File: FileGenerator.py

package info (click to toggle)
codequery 0.26.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 9,332 kB
  • sloc: cpp: 106,043; xml: 16,576; python: 4,187; perl: 244; makefile: 11
file content (221 lines) | stat: -rw-r--r-- 8,786 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#!/usr/bin/env python3
# FileGenerator.py - implemented 2013 by Neil Hodgson neilh@scintilla.org
# Released to the public domain.

# Generate or regenerate source files based on comments in those files.
# May be modified in-place or a template may be generated into a complete file.
# Requires Python 2.5 or later
# The files are copied to a string apart from sections between a
# ++Autogenerated comment and a --Autogenerated comment which is
# generated by the CopyWithInsertion function. After the whole string is
# instantiated, it is compared with the target file and if different the file
# is rewritten.

from __future__ import with_statement

import codecs, os, re, string, sys

lineEnd = "\r\n" if sys.platform == "win32" else "\n"

def UpdateFile(filename, updated):
    """ If the file contents are different to updated then copy updated into the
    file else leave alone so Mercurial and make don't treat it as modified. """
    newOrChanged = "Changed"
    try:
        with codecs.open(filename, "r", "utf-8") as infile:
            original = infile.read()
        if updated == original:
            # Same as before so don't write
            return
        os.unlink(filename)
    except IOError: # File is not there yet
        newOrChanged = "New"
    with codecs.open(filename, "w", "utf-8") as outfile:
        outfile.write(updated)
    print("%s %s" % (newOrChanged, filename))

# Automatically generated sections contain start and end comments,
# a definition line and the results.
# The results are replaced by regenerating based on the definition line.
# The definition line is a comment prefix followed by "**".
# If there is a digit after the ** then this indicates which list to use
# and the digit and next character are not part of the definition
# Backslash is used as an escape within the definition line.
# The part between \( and \) is repeated for each item in the list.
# \* is replaced by each list item. \t, and \n are tab and newline.
# If there is no definition line than the first list is copied verbatim.
# If retainDefs then the comments controlling generation are copied.
def CopyWithInsertion(input, commentPrefix, retainDefs, lists):
    copying = 1
    generated = False
    listid = 0
    output = []
    for line in input.splitlines(0):
        isStartGenerated = line.lstrip().startswith(commentPrefix + "++Autogenerated")
        if copying and not isStartGenerated:
            output.append(line)
        if isStartGenerated:
            if retainDefs:
                output.append(line)
            copying = 0
            generated = False
        elif not copying and not generated:
            # Generating
            if line.startswith(commentPrefix + "**"):
                # Pattern to transform input data
                if retainDefs:
                    output.append(line)
                definition = line[len(commentPrefix + "**"):]
                if (commentPrefix == "<!--") and (" -->" in definition):
                    definition = definition.replace(" -->", "")
                listid = 0
                if definition[0] in string.digits:
                    listid = int(definition[:1])
                    definition = definition[2:]
                # Hide double slashes as a control character
                definition = definition.replace("\\\\", "\001")
                # Do some normal C style transforms
                definition = definition.replace("\\n", "\n")
                definition = definition.replace("\\t", "\t")
                # Get the doubled backslashes back as single backslashes
                definition = definition.replace("\001", "\\")
                startRepeat = definition.find("\\(")
                endRepeat = definition.find("\\)")
                intro = definition[:startRepeat]
                out = ""
                if intro.endswith("\n"):
                    pos = 0
                else:
                    pos = len(intro)
                out += intro
                middle = definition[startRepeat+2:endRepeat]
                for i in lists[listid]:
                    item = middle.replace("\\*", i)
                    if pos and (pos + len(item) >= 80):
                        out += "\\\n"
                        pos = 0
                    out += item
                    pos += len(item)
                    if item.endswith("\n"):
                        pos = 0
                outro = definition[endRepeat+2:]
                out += outro
                out = out.replace("\n", lineEnd) # correct EOLs in generated content
                output.append(out)
            else:
                # Simple form with no rule to transform input
                output.extend(lists[0])
            generated = True
        if line.lstrip().startswith(commentPrefix + "--Autogenerated") or \
            line.lstrip().startswith(commentPrefix + "~~Autogenerated"):
            copying = 1
            if retainDefs:
                output.append(line)
    output = [line.rstrip(" \t") for line in output] # trim trailing whitespace
    return lineEnd.join(output) + lineEnd

def GenerateFile(inpath, outpath, commentPrefix, retainDefs, *lists):
    """Generate 'outpath' from 'inpath'.
    """

    try:
        with codecs.open(inpath, "r", "UTF-8") as infile:
            original = infile.read()
        updated = CopyWithInsertion(original, commentPrefix,
            retainDefs, lists)
        UpdateFile(outpath, updated)
    except IOError:
        print("Can not open %s" % inpath)

def Generate(inpath, outpath, commentPrefix, *lists):
    """Generate 'outpath' from 'inpath'.
    """
    GenerateFile(inpath, outpath, commentPrefix, inpath == outpath, *lists)

def Regenerate(filename, commentPrefix, *lists):
    """Regenerate the given file.
    """
    Generate(filename, filename, commentPrefix, *lists)

def UpdateLineInPlistFile(path, key, value):
    """Replace a single string value preceded by 'key' in an XML plist file.
    """
    lines = []
    keyCurrent = ""
    with codecs.open(path, "rb", "utf-8") as f:
        for l in f.readlines():
            ls = l.strip()
            if ls.startswith("<key>"):
                keyCurrent = ls.replace("<key>", "").replace("</key>", "")
            elif ls.startswith("<string>"):
                if keyCurrent == key:
                    start, tag, rest = l.partition("<string>")
                    val, etag, end = rest.partition("</string>")
                    l = start + tag + value + etag + end
            lines.append(l)
    contents = "".join(lines)
    UpdateFile(path, contents)

def UpdateLineInFile(path, linePrefix, lineReplace):
    lines = []
    updated = False
    with codecs.open(path, "r", "utf-8") as f:
        for l in f.readlines():
            l = l.rstrip()
            if not updated and l.startswith(linePrefix):
                lines.append(lineReplace)
                updated = True
            else:
                lines.append(l)
    contents = lineEnd.join(lines) + lineEnd
    UpdateFile(path, contents)

def ReadFileAsList(path):
    """Read all the lnes in the file and return as a list of strings without line ends.
    """
    with codecs.open(path, "rU", "utf-8") as f:
        return [l.rstrip('\n') for l in f]

def UpdateFileFromLines(path, lines, lineEndToUse):
    """Join the lines with the lineEndToUse then update file if the result is different.
    """
    contents = lineEndToUse.join(lines) + lineEndToUse
    UpdateFile(path, contents)

def FindSectionInList(lines, markers):
    """Find a section defined by an initial start marker, an optional secondary
    marker and an end marker.
    The section is between the secondary/initial start and the end.
    Report as a slice object so the section can be extracted or replaced.
    Raises an exception if the markers can't be found.
    """
    start = -1
    end = -1
    state = 0
    for i, l in enumerate(lines):
        if markers[0] in l:
            if markers[1]:
                state = 1
            else:
                start = i+1
                state = 2
        elif state == 1:
            if markers[1] in l:
                start = i+1
                state = 2
        elif state == 2:
            if markers[2] in l:
                end = i
                state = 3
    # Check that section was found
    if start == -1:
        raise Exception("Could not find start marker(s) |" + markers[0] + "|" + markers[1] + "|")
    if end == -1:
        raise Exception("Could not find end marker " + markers[2])
    return slice(start, end)

def ReplaceREInFile(path, match, replace, count=0):
    with codecs.open(path, "r", "utf-8") as f:
        contents = f.read()
    contents = re.sub(match, replace, contents, count)
    UpdateFile(path, contents)