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