]> rtime.felk.cvut.cz Git - omk.git/blob - omkbuild.py
7f01c3b5064478d5164891bbb52902c6e7095188
[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 '\n'
17   empty-comment ::= '#' '\n'
18   text ::= [^#] ... '\n'
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 mark of the form #OMK@<snippet file name><EOL>. This mark
27      is used for splitting modified Makefile.rules back to the
28      original snippets. If <snippet file name> starts with __, it is
29      ignored during splitting.
30 """
31
32 from optparse import OptionParser
33 import os
34 import os.path
35 import sys
36 import string
37 import re
38
39 rulesDir = "rules"
40 snippetsDir = "snippets"
41
42 class LineList(list):
43     """List of text lines"""
44     def getDiff(self, other):
45         s = ''
46         for i in range(len(self)):
47             if i >= len(other):
48                 s += ("  Line %d differs!\n" % i)
49                 s += "  -"+self[i].rstrip() + "\n"
50                 s += "  +\n"
51                 break
52             if self[i] != other[i]:
53                 s += ("  Line %d differs!\n" % i)
54                 s += "  -"+self[i].rstrip() + "\n"
55                 s += "  +"+other[i].rstrip() + "\n"
56                 break
57         return s
58     
59     def __str__(self):
60         s = ''
61         for l in self: s += l
62         return s
63
64     def loadFromFile(self, fname):
65         """Loads itself from file."""
66         try:
67             f = open(fname, "r")
68         except IOError:
69             sys.stderr.write("Cannot open %s\n" % fname)
70             sys.exit(1)
71             
72         self.extend(f.readlines())
73         f.close
74
75 class Snippet:
76     def __init__(self, fname = None, name = ""):
77         """Initializes the snippet and if fname is given, reads it
78         from file"""
79         self.name = name
80         self.legal = LineList()
81         self.doc = LineList()
82         self.code = LineList()
83         if fname: self.loadFromFile(fname)
84         
85     def loadFromFile(self, fname):
86         """Loads snippet from file."""
87         self.name = fname
88         f = open(fname, "r")
89         
90         self.readLines(f.readlines())
91          
92         f.close
93
94     def addCodeLine(self, line):
95         self.code.append(line)
96
97     def readLines(self, lines):
98         """Parses the snippet given as a list and stores it in itself."""
99         currentPart = self.legal
100         counter = 0
101
102         for line in lines:
103             if currentPart == self.legal:
104                 if line.strip() == "#": counter += 1
105                 else: counter = 0
106             if line[0] != "#": currentPart = self.code
107
108             currentPart.append(line)
109
110             if counter == 2:
111                 currentPart = self.doc
112                 counter = 0
113
114
115         if not self.doc:
116             self.doc = self.legal
117             self.legal = LineList()
118
119     def asLinesList(self):
120         lines = LineList()
121         for type in ['legal', 'doc', 'code']:
122             for line in self.__dict__[type]:
123                 lines.append(line)
124         return lines
125
126     def __str__(self):
127         return str(self.asLinesList())
128
129     def __repr__(self):
130         s = "<Snippet: %s>" % self.name
131         return s
132
133     def __cmp__(self, other):
134         ret = cmp(self.name, other.name)
135         if ret != 0: return ret
136         ret = cmp(self.legal, other.legal)
137         if ret != 0: return ret
138         ret = cmp(self.doc, other.doc)
139         if ret != 0: return ret
140         ret = cmp(self.code, other.code)
141         return ret
142
143     def __getitem__(self, key):
144         return {
145             'legal': self.legal,
146             'doc'  : self.doc,
147             'code' : self.code
148             }[key]
149
150     def getDiff(self, other):
151         return self.asLinesList().getDiff(other.asLinesList())
152
153 class Snippets:
154     """Collection of snippets, where snippets can be accessed
155     individually by name (like dictionary) or sequentionaly in the
156     order they were added."""
157     def __init__(self):
158         self._snippets = dict()
159         self._order = list()
160
161     def __iadd__(self, snippet):
162         assert isinstance(snippet, Snippet)
163         self._snippets[snippet.name] = snippet
164         self._order += [snippet]
165         return self
166
167     def __getitem__(self, key):
168         return self._snippets[key]
169
170     def __contains__(self, item):
171         return item in self._snippets
172
173     def __iter__(self):
174         return iter(self._order)
175
176     def __cmp__(self, other):
177         return cmp(self._snippets, other._snippets)
178
179     def loadFromFiles(self, fnames):
180         """Reads the snippets from several files and adds them to itself."""
181         for fn in fnames:
182             self += Snippet(fn)
183         
184     def loadFromDict(self, snipDict):
185         """Adds snippets to itself from dictionary of LineLists."""
186         for s in snipDict:
187             snip = Snippet()
188             snip.name = s
189             snip.readLines(snipDict[s])
190             self += snip
191
192     def getDiff(self, other):
193         assert isinstance(other, Snippets)
194         s = ''
195         for snip in self:
196             if (snip.name[0:2] == '__'): continue
197             if (snip != other[snip.name]):
198                 s += "Snippet %s:\n" % snip.name
199                 s += snip.getDiff(other[snip.name])
200         return s
201
202 class MakefileRules(LineList):
203     def __init__(self):
204         self.snippets = Snippets()
205         self.rules = LineList()
206
207     def combine(self):
208         """Combine self.snippets and produces self.rules"""
209         self.rules = LineList()
210
211         for type in ['legal', 'doc', 'code']:
212             for snip in self.snippets:
213                 lines = snip[type]
214                 if len(lines) == 0: continue
215                 firstLine = string.rstrip(lines[0])
216                 self.rules += [firstLine.ljust(60)+" #OMK@%s\n"%snip.name]
217                 self.rules += lines[1:]
218                 #self.rules += ['a'] # test error
219
220     def split(self):
221         """Split self.rules to the original snippets in self.snippets."""
222         self.snippets = Snippets()
223         snipDict = self._getSnipDicts()
224         self.snippets.loadFromDict(snipDict)
225
226     def _getSnipDicts(self):
227         """Split self.rules to the original snippets, which are
228         returened as dictionary of LineLists."""
229         snipBegin = re.compile("^(.*)#OMK@(.*)$")
230         snipDict = dict()
231         currentLinesList = None
232
233         for line in self.rules:
234             match = snipBegin.match(line)
235             if match:
236                 line = match.group(1).rstrip() + "\n"
237                 snipName = match.group(2)
238                 if not snipName in snipDict:
239                     snipDict[snipName] = LineList()
240                 currentLinesList = snipDict[snipName]
241             
242             if currentLinesList != None:
243                 currentLinesList.append(line);
244                 
245         return snipDict
246
247
248 def parseCommandLine():
249     parser = OptionParser(usage = """
250     %prog [-o FILE] snippet1 snippet2 ...      build Makefile.rules from snippets
251     %prog [-o - ] -s Makfile.rules
252     """)
253     parser.add_option("-s", "--split",
254                       action="store", dest="split", default=False, metavar="RULES",
255                       help="Split given Makefile.rules to the original snippets")
256     parser.add_option("-o", "--output",
257                       action="store", dest="output", default=False, metavar="RULES",
258                       help="Write Makefile.rules to file RULES")
259     parser.add_option("-a", "--all",
260                       action="store_true", dest="all",
261                       help="Rebuild all rules acording to rulesdef.py")
262     (options, args) = parser.parse_args()
263     return options, args
264
265 def buildRules(fnames, output, name):
266     rules = MakefileRules()
267     snip_rule_type = Snippet(name="__type")
268     snip_rule_type.addCodeLine("OMK_RULES_TYPE=%s\n" % name)
269     rules.snippets += snip_rule_type
270     rules.snippets.loadFromFiles(fnames)
271     rules.combine()
272
273     rulesCheck = MakefileRules()
274     rulesCheck.rules = rules.rules
275     rulesCheck.split()
276
277     if rules.snippets != rulesCheck.snippets:
278         sys.stderr.write("Consistency error:\n")
279         diff = rules.snippets.getDiff(rulesCheck.snippets)
280         sys.stderr.write(diff)
281         sys.exit(1)
282         
283     if output: f = open(output,"w+")
284     else: f = sys.stdout
285     f.writelines(rules.rules)
286     f.close()
287
288 def splitRules(rulesFN, output):
289     rules = MakefileRules()
290     rules.rules.loadFromFile(rulesFN)
291     rules.split()
292
293     rulesCheck = MakefileRules()
294     rulesCheck.snippets = rules.snippets
295     rulesCheck.combine()
296
297     # The comparsion is not that simple. The order of rules might be
298     # different.
299 #     if rules.rules != rulesCheck.rules:
300 #         sys.stderr.write("Consistency error:\n")
301 #         diff = rules.rules.getDiff(rulesCheck.rules)
302 #         sys.stderr.write(diff)
303 #         sys.exit(1)
304
305     for snip in rules.snippets:
306         if snip.name[0:2] == "__":
307             continue
308         print snip.name
309         f = None
310         if output == "-": f = sys.stdout
311         else: f = open(snip.name, "w+")
312         f.writelines(snip.asLinesList())
313         f.close()
314
315 def buildAllRules():
316     import rulesdef
317     os.chdir(snippetsDir)
318     for rules in rulesdef.rules:
319         print 'Building rules: %s' % rules
320         outputDir = os.path.join(sys.path[0], rulesDir, rules)
321         if not os.path.isdir(outputDir): os.makedirs(outputDir)
322         buildRules(rulesdef.rules[rules], os.path.join(outputDir, 'Makefile.rules'), rules)
323
324 def main():
325     (options, args) = parseCommandLine()
326     if options.all:
327         buildAllRules()
328     elif options.split:
329         splitRules(options.split, options.output)
330     else:
331         buildRules(args, options.output)
332
333
334 if __name__ == "__main__": main()