]> rtime.felk.cvut.cz Git - omk.git/blob - omkbuild.py
Added news to web page
[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 counter == 2: currentPart = self.doc
107             if line[0] != "#": currentPart = self.code
108
109             currentPart.append(line)
110
111         if not self.doc:
112             self.doc = self.legal
113             self.legal = LineList()
114
115     def asLinesList(self):
116         lines = LineList()
117         for type in ['legal', 'doc', 'code']:
118             for line in self.__dict__[type]:
119                 lines.append(line)
120         return lines
121
122     def __str__(self):
123         return str(self.asLinesList())
124
125     def __repr__(self):
126         s = "<Snippet: %s>" % self.name
127         return s
128
129     def __cmp__(self, other):
130         ret = cmp(self.name, other.name)
131         if ret != 0: return ret
132         ret = cmp(self.legal, other.legal)
133         if ret != 0: return ret
134         ret = cmp(self.doc, other.doc)
135         if ret != 0: return ret
136         ret = cmp(self.code, other.code)
137         return ret
138
139     def __getitem__(self, key):
140         return {
141             'legal': self.legal,
142             'doc'  : self.doc,
143             'code' : self.code
144             }[key]
145
146     def getDiff(self, other):
147         return self.asLinesList().getDiff(other.asLinesList())
148
149 class Snippets:
150     """Collection of snippets, where snippets can be accessed
151     individually by name (like dictionary) or sequentionaly in the
152     order they were added."""
153     def __init__(self):
154         self._snippets = dict()
155         self._order = list()
156
157     def __iadd__(self, snippet):
158         assert isinstance(snippet, Snippet)
159         self._snippets[snippet.name] = snippet
160         self._order += [snippet]
161         return self
162
163     def __getitem__(self, key):
164         return self._snippets[key]
165
166     def __contains__(self, item):
167         return item in self._snippets
168
169     def __iter__(self):
170         return iter(self._order)
171
172     def __cmp__(self, other):
173         return cmp(self._snippets, other._snippets)
174
175     def loadFromFiles(self, fnames):
176         """Reads the snippets from several files and adds them to itself."""
177         for fn in fnames:
178             self += Snippet(fn)
179         
180     def loadFromDict(self, snipDict):
181         """Adds snippets to itself from dictionary of LineLists."""
182         for s in snipDict:
183             snip = Snippet()
184             snip.name = s
185             snip.readLines(snipDict[s])
186             self += snip
187
188     def getDiff(self, other):
189         assert isinstance(other, Snippets)
190         s = ''
191         for snip in self:
192             if (snip.name[0:2] == '__'): continue
193             if (snip != other[snip.name]):
194                 s += "Snippet %s:\n" % snip.name
195                 s += snip.getDiff(other[snip.name])
196         return s
197
198 class MakefileRules(LineList):
199     def __init__(self):
200         self.snippets = Snippets()
201         self.rules = LineList()
202
203     def combine(self):
204         """Combine self.snippets and produces self.rules"""
205         self.rules = LineList()
206
207         for type in ['legal', 'doc', 'code']:
208             for snip in self.snippets:
209                 lines = snip[type]
210                 if len(lines) == 0: continue
211                 firstLine = string.rstrip(lines[0])
212                 self.rules += [firstLine.ljust(60)+" #OMK@%s\n"%snip.name]
213                 self.rules += lines[1:]
214                 #self.rules += ['a'] # test error
215
216     def split(self):
217         """Split self.rules to the original snippets in self.snippets."""
218         self.snippets = Snippets()
219         snipDict = self._getSnipDicts()
220         self.snippets.loadFromDict(snipDict)
221
222     def _getSnipDicts(self):
223         """Split self.rules to the original snippets, which are
224         returened as dictionary of LineLists."""
225         snipBegin = re.compile("^(.*)#OMK@(.*)$")
226         snipDict = dict()
227         currentLinesList = None
228
229         for line in self.rules:
230             match = snipBegin.match(line)
231             if match:
232                 line = match.group(1).rstrip() + "\n"
233                 snipName = match.group(2)
234                 if not snipName in snipDict:
235                     snipDict[snipName] = LineList()
236                 currentLinesList = snipDict[snipName]
237             
238             if currentLinesList != None:
239                 currentLinesList.append(line);
240                 
241         return snipDict
242
243
244 def parseCommandLine():
245     parser = OptionParser(usage = """
246     %prog [-o FILE] snippet1 snippet2 ...      build Makefile.rules from snippets
247     %prog [-o - ] -s Makfile.rules
248     """)
249     parser.add_option("-s", "--split",
250                       action="store", dest="split", default=False, metavar="RULES",
251                       help="Split given Makefile.rules to the original snippets")
252     parser.add_option("-o", "--output",
253                       action="store", dest="output", default=False, metavar="RULES",
254                       help="Write Makefile.rules to file RULES")
255     parser.add_option("-a", "--all",
256                       action="store_true", dest="all",
257                       help="Rebuild all rules acording to rulesdef.py")
258     (options, args) = parser.parse_args()
259     return options, args
260
261 def buildRules(fnames, output, name):
262     rules = MakefileRules()
263     snip_rule_type = Snippet(name="__type")
264     snip_rule_type.addCodeLine("OMK_RULES_TYPE=%s\n" % name)
265     rules.snippets += snip_rule_type
266     rules.snippets.loadFromFiles(fnames)
267     rules.combine()
268
269     rulesCheck = MakefileRules()
270     rulesCheck.rules = rules.rules
271     rulesCheck.split()
272
273     if rules.snippets != rulesCheck.snippets:
274         sys.stderr.write("Consistency error:\n")
275         diff = rules.snippets.getDiff(rulesCheck.snippets)
276         sys.stderr.write(diff)
277         sys.exit(1)
278         
279     if output: f = open(output,"w+")
280     else: f = sys.stdout
281     f.writelines(rules.rules)
282     f.close()
283
284 def splitRules(rulesFN, output):
285     rules = MakefileRules()
286     rules.rules.loadFromFile(rulesFN)
287     rules.split()
288
289     rulesCheck = MakefileRules()
290     rulesCheck.snippets = rules.snippets
291     rulesCheck.combine()
292
293     # The comparsion is not that simple. The order of rules might be
294     # different.
295 #     if rules.rules != rulesCheck.rules:
296 #         sys.stderr.write("Consistency error:\n")
297 #         diff = rules.rules.getDiff(rulesCheck.rules)
298 #         sys.stderr.write(diff)
299 #         sys.exit(1)
300
301     for snip in rules.snippets:
302         if snip.name[0:2] == "__":
303             continue
304         print snip.name
305         f = None
306         if output == "-": f = sys.stdout
307         else: f = open(snip.name, "w+")
308         f.writelines(snip.asLinesList())
309         f.close()
310
311 def buildAllRules():
312     import rulesdef
313     os.chdir(snippetsDir)
314     for rules in rulesdef.rules:
315         print 'Building rules: %s' % rules
316         outputDir = os.path.join(sys.path[0], rulesDir, rules)
317         if not os.path.isdir(outputDir): os.makedirs(outputDir)
318         buildRules(rulesdef.rules[rules], os.path.join(outputDir, 'Makefile.rules'), rules)
319
320 def main():
321     (options, args) = parseCommandLine()
322     if options.all:
323         buildAllRules()
324     elif options.split:
325         splitRules(options.split, options.output)
326     else:
327         buildRules(args, options.output)
328
329
330 if __name__ == "__main__": main()