]> rtime.felk.cvut.cz Git - omk.git/blob - omkbuild.py
Fixes to omkbuild.py
[omk.git] / omkbuild.py
1 #!/usr/bin/env python
2 """
3 This script has two main functions:
4
5   1) Combine several snippets to form Makefile.rules file
6   2) Split Makefile.rules file to the originnal snippets
7
8 Snippet syntax:
9
10   snippet ::= legal? documentation rules
11   
12   legal ::= comment* empty-comment empty-comment
13   documentation := comment*
14   rules ::= text
15   
16   comment ::= '#' text
17   empty-comment ::= '#'
18   text ::= [^#] ...
19
20  Makefile.rules policies:
21
22    * All the parts of the Makefile.rules are ordered in the same order
23      as they are in snippets i.e. copyrights, documentations and rules.
24
25    * On the first line of each part of the Makefile.rules, there is
26      special mart of the form #OMK@<snippet file name><EOL>. This mark
27      is used for splitting Makefile.rules back to the original
28      snippets.
29
30 """
31
32 from optparse import OptionParser
33 import os
34 import sys
35 import string
36 import re
37
38 class LineList(list):
39     """List of text lines"""
40     def getDiff(self, other):
41         s = ''
42         for i in range(len(self)):
43             if self[i] != other[i]:
44                 s += ("  Line %d differs!\n" % i)
45                 s += "  -"+self[i].rstrip() + "\n"
46                 s += "  +"+other[i].rstrip() + "\n"
47                 break
48         return s
49     
50     def __str__(self):
51         s = ''
52         for l in self: s += l
53         return s
54
55     def loadFromFile(self, fname):
56         """Loads itself from file."""
57         f = open(fname, "r")
58         self.extend(f.readlines())
59         f.close
60
61 class Snippet:
62     def __init__(self, fname = None):
63         """Initializes the snippet and if fname is given, reads it
64         from file"""
65         self.name = ""
66         self.legal = LineList()
67         self.doc = LineList()
68         self.code = LineList()
69         if fname: self.loadFromFile(fname)
70         
71     def loadFromFile(self, fname):
72         """Loads snippet from file."""
73         self.name = fname
74         f = open(fname, "r")
75         
76         self.readLines(f.readlines())
77          
78         f.close
79
80     def readLines(self, lines):
81         """Parses the snippet given as a list and stores it in itself."""
82         currentPart = self.legal
83         counter = 0
84
85         for line in lines:
86             if currentPart == self.legal:
87                 if line.strip() == "#": counter += 1
88                 else: counter = 0
89                 if counter == 2: currentPart = self.doc
90             if line[0] != "#": currentPart = self.code
91
92             currentPart.append(line)
93
94         if not self.doc:
95             self.doc = self.legal
96             self.legal = LineList()
97
98     def asLinesList(self):
99         lines = LineList()
100         for type in ['legal', 'doc', 'code']:
101             for line in self.__dict__[type]:
102                 lines.append(line)
103         return lines
104
105     def __str__(self):
106         return str(self.asLinesList())
107
108     def __repr__(self):
109         s = "<Snippet: %s>" % self.name
110         return s
111
112     def __cmp__(self, other):
113         ret = cmp(self.name, other.name)
114         if ret != 0: return ret
115         ret = cmp(self.legal, other.legal)
116         if ret != 0: return ret
117         ret = cmp(self.doc, other.doc)
118         if ret != 0: return ret
119         ret = cmp(self.code, other.code)
120         return ret
121
122     def __getitem__(self, key):
123         return {
124             'legal': self.legal,
125             'doc'  : self.doc,
126             'code' : self.code
127             }[key]
128
129     def getDiff(self, other):
130         return self.asLinesList().getDiff(other.asLinesList())
131
132 class Snippets:
133     """Collection of snippets, where snippets can be accessed
134     individually by name (like dictionary) or sequentionaly in the
135     order they were added."""
136     def __init__(self):
137         self._snippets = dict()
138         self._order = list()
139
140     def __iadd__(self, snippet):
141         assert isinstance(snippet, Snippet)
142         self._snippets[snippet.name] = snippet
143         self._order += [snippet]
144         return self
145
146     def __getitem__(self, key):
147         return self._snippets[key]
148
149     def __contains__(self, item):
150         return item in self._snippets
151
152     def __iter__(self):
153         return iter(self._order)
154
155     def __cmp__(self, other):
156         return cmp(self._snippets, other._snippets)
157
158     def loadFromFiles(self, fnames):
159         """Reads the snippets from several files and adds them to itself."""
160         for fn in fnames:
161             self += Snippet(fn)
162         
163     def loadFromDict(self, snipDict):
164         """Adds snippets to itself from dictionary of LineLists."""
165         for s in snipDict:
166             snip = Snippet()
167             snip.name = s
168             snip.readLines(snipDict[s])
169             self += snip
170
171     def getDiff(self, other):
172         assert isinstance(other, Snippets)
173         s = ''
174         for snip in self:
175             if (snip != other[snip.name]):
176                 s += "Snippet %s:\n" % snip.name
177                 s += snip.getDiff(other[snip.name])
178         return s
179
180 class MakefileRules(LineList):
181     def __init__(self):
182         self.snippets = Snippets()
183         self.rules = LineList()
184
185     def combine(self):
186         """Combine self.snippets and produces self.rules"""
187         self.rules = LineList()
188
189         for type in ['legal', 'doc', 'code']:
190             for snip in self.snippets:
191                 lines = snip[type]
192                 if len(lines) == 0: continue
193                 firstLine = string.rstrip(lines[0])
194                 self.rules += [firstLine.ljust(60)+" #OMK@%s\n"%snip.name]
195                 self.rules += lines[1:]
196                 #self.rules += ['a'] # test error
197
198     def split(self):
199         """Split self.rules to the original snippets in self.snippets."""
200         self.snippets = Snippets()
201         snipDict = self._getSnipDicts()
202         self.snippets.loadFromDict(snipDict)
203
204     def _getSnipDicts(self):
205         """Split self.rules to the original snippets, which are
206         returened as dictionary of LineLists."""
207         snipBegin = re.compile("^(.*)#OMK@(.*)$")
208         snipDict = dict()
209         currentLinesList = None
210
211         for line in self.rules:
212             match = snipBegin.match(line)
213             if match:
214                 line = match.group(1).rstrip() + "\n"
215                 snipName = match.group(2)
216                 if not snipName in snipDict:
217                     snipDict[snipName] = LineList()
218                 currentLinesList = snipDict[snipName]
219
220             currentLinesList.append(line);
221         return snipDict
222
223
224 def parseCommandLine():
225     parser = OptionParser(usage = """
226     %prog [-o FILE] snippet1 snippet2 ...      build Makefile.rules from snippets
227     %prog [-o - ] -s Makfile.rules
228     """)
229     parser.add_option("-s", "--split",
230                       action="store", dest="split", default=False, metavar="RULES",
231                       help="Split given Makefile.rules to the original snippets")
232     parser.add_option("-o", "--output",
233                       action="store", dest="output", default=False, metavar="RULES",
234                       help="Write Makefile.rules to file RULES")
235     (options, args) = parser.parse_args()
236     return options, args
237
238 def buildRules(fnames, output):
239     rules = MakefileRules()
240     rules.snippets.loadFromFiles(fnames)
241     rules.combine()
242
243     rulesCheck = MakefileRules()
244     rulesCheck.rules = rules.rules
245     rulesCheck.split()
246
247     if rules.snippets != rulesCheck.snippets:
248         sys.stderr.write("Consistency error:\n")
249         diff = rules.snippets.getDiff(rulesCheck.snippets)
250         sys.stderr.write(diff)
251         sys.exit(1)
252         
253     if output: f = open(output,"w+")
254     else: f = sys.stdout
255     f.writelines(rules.rules)
256     f.close()
257
258 def splitRules(rulesFN, output):
259     rules = MakefileRules()
260     rules.rules.loadFromFile(rulesFN)
261     rules.split()
262
263     rulesCheck = MakefileRules()
264     rulesCheck.snippets = rules.snippets
265     rulesCheck.combine()
266
267     # The order of rules might be different
268 #     if rules.rules != rulesCheck.rules:
269 #         sys.stderr.write("Consistency error:\n")
270 #         diff = rules.rules.getDiff(rulesCheck.rules)
271 #         sys.stderr.write(diff)
272 #         sys.exit(1)
273
274     for snip in rules.snippets:
275         print snip.__class__
276 #     f = None
277 #     if output == "-": f = sys.stdout
278 #     f.writelines(rules.rules)
279 #     f.close()
280
281
282 def main():
283     (options, args) = parseCommandLine()
284     if options.split:
285         splitRules(options.split, options.output)
286     else:
287         buildRules(args, options.output)
288
289
290 if __name__ == "__main__": main()