3 This script has two main functions:
5 1) Combine several snippets to form Makefile.rules file
6 2) Split Makefile.rules file to the originnal snippets
10 snippet ::= legal? documentation rules
12 legal ::= comment* empty-comment empty-comment
13 documentation := comment*
16 comment ::= '#' text '\n'
17 empty-comment ::= '#' '\n'
18 text ::= [^#] ... '\n'
20 Makefile.rules policies:
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.
25 * On the first line of each part of the Makefile.rules, there is
26 special mark of the form:
28 #OMK:<snippet file name>[@<included from snippet>]<EOL>
30 This mark is used for splitting modified Makefile.rules back to
31 the original snippets. If <snippet file name> starts with __, it
32 is ignored during splitting.
34 * Toplevel snippet has name in the forb Makefile.rules.* and no
35 other (included) snippet has such name.
38 from optparse import OptionParser
46 snippetsDir = "snippets"
49 """List of text lines"""
50 def getDiff(self, other):
52 for i in range(len(self)):
54 s += (" Line %d differs!\n" % i)
55 s += " -"+self[i].rstrip() + "\n"
58 if self[i] != other[i]:
59 s += (" Line %d differs!\n" % i)
60 s += " -"+self[i].rstrip() + "\n"
61 s += " +"+other[i].rstrip() + "\n"
70 def loadFromFile(self, fname):
71 """Loads itself from file."""
75 sys.stderr.write("Cannot open %s\n" % fname)
78 self.extend(f.readlines())
82 def __init__(self, fname = None, name = ""):
83 """Initializes the snippet and if fname is given, reads it
86 self.legal = LineList()
88 self.code = LineList()
89 if fname: self.loadFromFile(fname)
91 def loadFromFile(self, fname):
92 """Loads snippet from file."""
96 self.readLines(f.readlines())
100 def addCodeLine(self, line):
101 self.code.append(line)
103 def readLines(self, lines):
104 """Parses the snippet given as a list and stores it in itself."""
105 currentPart = self.legal
109 if currentPart == self.legal:
110 if line.strip() == "#": counter += 1
112 if line[0] != "#": currentPart = self.code
114 currentPart.append(line)
117 currentPart = self.doc
122 self.doc = self.legal
123 self.legal = LineList()
125 def asLinesList(self):
127 for type in ['legal', 'doc', 'code']:
128 for line in self.__dict__[type]:
133 return str(self.asLinesList())
136 s = "<Snippet: %s>" % self.name
139 def __cmp__(self, other):
140 ret = cmp(self.name, other.name)
141 if ret != 0: return ret
142 ret = cmp(self.legal, other.legal)
143 if ret != 0: return ret
144 ret = cmp(self.doc, other.doc)
145 if ret != 0: return ret
146 ret = cmp(self.code, other.code)
149 def __getitem__(self, key):
156 def getDiff(self, other):
157 return self.asLinesList().getDiff(other.asLinesList())
160 """Collection of snippets, where snippets can be accessed
161 individually by name (like dictionary) or sequentionaly in the
162 order they were added."""
164 self._snippets = dict()
167 def __iadd__(self, snippet):
168 assert isinstance(snippet, Snippet)
169 self._snippets[snippet.name] = snippet
170 self._order += [snippet]
173 def __getitem__(self, key):
174 return self._snippets[key]
176 def __contains__(self, item):
177 return item in self._snippets
180 return iter(self._order)
182 def __cmp__(self, other):
183 return cmp(self._snippets, other._snippets)
185 def loadFromFiles(self, fnames):
186 """Reads the snippets from several files and adds them to itself."""
190 def loadFromDict(self, snipDict):
191 """Adds snippets to itself from dictionary of LineLists."""
195 snip.readLines(snipDict[s])
198 def getDiff(self, other):
199 assert isinstance(other, Snippets)
202 if (snip.name[0:2] == '__'): continue
203 if (snip != other[snip.name]):
204 s += "Snippet %s:\n" % snip.name
205 s += snip.getDiff(other[snip.name])
208 # Include directoves matching this r.e. will be replaced by this script
209 reInclude = re.compile("^include ([^ ]*) #omkbuild")
211 class MakefileRules(LineList):
213 self.snippets = Snippets()
214 self.rules = LineList()
216 def _includeSnippets(self, filename, baseFileName="", onlyLoadSnippets=False):
217 """Recursively traverses snippets according to include
218 directives. If onlyLoadSnippets is True, self.rules is not
222 if filename in self.snippets:
223 sys.stderr.write("Error: Snippet included more than once\n")
224 # This is not allowed becouse it would cause problems
227 self.snippets += Snippet(filename)
229 lines = self.snippets[filename]['code']
231 addMarker = 1 # The first line of the snippet should be marked
233 match = reInclude.match(line)
235 # Include other snippet
236 self._includeSnippets(match.group(1).strip(),\
239 addMarker = 2 # The next line after include should be marked
241 # Add this line to rules
244 line = string.rstrip(line).ljust(80)+" #OMK:%s@%s\n"%(filename,baseFileName)
246 line = string.rstrip(line).ljust(80)+" #OMK:%s\n"%(filename)
248 if not onlyLoadSnippets:
251 def combineFrom(self, topLevelSnippet, onlyCheck=False):
252 """Produces self.rules from the topLevelSnippet and all
253 snippets included directly or indirectly from it."""
254 self.rules = LineList()
257 self._includeSnippets(topLevelSnippet, onlyLoadSnippets=True)
259 # Append legal and doc parts
260 for type in ['legal', 'doc']:
261 for snip in self.snippets:
263 if len(lines) == 0: continue
264 firstLine = string.rstrip(lines[0])
265 self.rules += [firstLine.ljust(80)+" #OMK:%s\n"%snip.name]
266 self.rules += lines[1:]
267 #self.rules += ['a'] # test error
270 self._includeSnippets(topLevelSnippet)
274 """Split self.rules to the original snippets in self.snippets."""
275 self.snippets = Snippets()
276 snipDict = self._getSnipDicts()
277 self.snippets.loadFromDict(snipDict)
279 def _getSnipDicts(self):
280 """Split self.rules to the original snippets, which are
281 returened as dictionary of LineLists."""
282 snipBegin = re.compile("^(.*)#OMK:([^@]*)(?:@(.*))?\n$")
284 currentLinesList = None
286 for line in self.rules:
287 match = snipBegin.match(line)
289 line = match.group(1).rstrip() + "\n"
290 snipName = match.group(2)
291 includedFrom = match.group(3)
293 if not includedFrom in snipDict: snipDict[includedFrom] = LineList()
294 snipDict[includedFrom].append("include %s #omkbuild\n" % snipName);
296 if not snipName in snipDict:
297 snipDict[snipName] = LineList()
298 currentLinesList = snipDict[snipName]
300 if currentLinesList != None:
301 currentLinesList.append(line);
306 def parseCommandLine():
307 parser = OptionParser(usage = """
308 %prog [-o FILE] top-level-snippet build Makefile.rules from the top-level-snippet and included ones
309 %prog [-o - ] -s Makfile.rules
311 parser.add_option("-s", "--split",
312 action="store", dest="split", default=False, metavar="RULES",
313 help="Split given Makefile.rules to the original snippets")
314 parser.add_option("-o", "--output",
315 action="store", dest="output", default=False, metavar="RULES",
316 help="Write Makefile.rules to file RULES")
317 (options, args) = parser.parse_args()
323 def buildRules(topLevelSnippet, output):
324 rules = MakefileRules()
325 rules.combineFrom(topLevelSnippet)
327 rulesCheck = MakefileRules()
328 rulesCheck.rules = rules.rules
331 if rules.snippets != rulesCheck.snippets:
332 sys.stderr.write("Consistency error:\n")
333 diff = rules.snippets.getDiff(rulesCheck.snippets)
334 sys.stderr.write(diff)
338 try: os.makedirs(os.path.dirname(output))
340 f = open(output,"w+")
342 f.writelines(rules.rules)
345 def splitRules(rulesFN, output):
346 rules = MakefileRules()
347 rules.rules.loadFromFile(rulesFN)
350 rulesCheck = MakefileRules()
351 rulesCheck.snippets = rules.snippets
353 topLevelSnippet = None
354 for snip in rules.snippets:
355 if snip.name.startswith("Makefile.rules."):
356 topLevelSnippet = snip.name
357 if not topLevelSnippet:
358 sys.stderr.write("No toplevel snippet (Makefile.rules.*) found\n")
361 rulesCheck.combineFrom(topLevelSnippet, onlyCheck=True)
363 # The comparsion is not that simple. The order of rules might be
364 # different. FIXME: Is this still true?
365 # if rules.rules != rulesCheck.rules:
366 # sys.stderr.write("Consistency error:\n")
367 # diff = rules.rules.getDiff(rulesCheck.rules)
368 # sys.stderr.write(diff)
371 for snip in rules.snippets:
372 if snip.name[0:2] == "__":
376 if output == "-": f = sys.stdout
377 else: f = open(snip.name, "w+")
378 f.writelines(snip.asLinesList())
382 (options, args) = parseCommandLine()
384 splitRules(options.split, options.output)
386 buildRules(args[0], options.output)
389 if __name__ == "__main__": main()