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
45 snippetsDir = "snippets"
48 """List of text lines"""
49 def getDiff(self, other):
51 for i in range(len(self)):
53 s += (" Line %d differs!\n" % i)
54 s += " -"+self[i].rstrip() + "\n"
57 if self[i] != other[i]:
58 s += (" Line %d differs!\n" % i)
59 s += " -"+self[i].rstrip() + "\n"
60 s += " +"+other[i].rstrip() + "\n"
69 def loadFromFile(self, fname):
70 """Loads itself from file."""
74 sys.stderr.write("Cannot open %s\n" % fname)
77 self.extend(f.readlines())
81 def __init__(self, fname = None, name = ""):
82 """Initializes the snippet and if fname is given, reads it
85 self.legal = LineList()
87 self.code = LineList()
88 if fname: self.loadFromFile(fname)
90 def loadFromFile(self, fname):
91 """Loads snippet from file."""
95 self.readLines(f.readlines())
99 def addCodeLine(self, line):
100 self.code.append(line)
102 def readLines(self, lines):
103 """Parses the snippet given as a list and stores it in itself."""
104 currentPart = self.legal
108 if currentPart == self.legal:
109 if line.strip() == "#": counter += 1
111 if line[0] != "#": currentPart = self.code
113 currentPart.append(line)
116 currentPart = self.doc
121 self.doc = self.legal
122 self.legal = LineList()
124 def asLinesList(self):
126 for type in ['legal', 'doc', 'code']:
127 for line in self.__dict__[type]:
132 return str(self.asLinesList())
135 s = "<Snippet: %s>" % self.name
138 def __eq__(self, other):
140 self.name == other.name and \
141 self.legal == other.legal and \
142 self.doc == other.doc and \
143 self.code == other.code
145 def __ne__(self, other):
146 return not self == other
148 def __getitem__(self, key):
155 def getDiff(self, other):
156 return self.asLinesList().getDiff(other.asLinesList())
159 """Collection of snippets, where snippets can be accessed
160 individually by name (like dictionary) or sequentionaly in the
161 order they were added."""
163 self._snippets = dict()
166 def __iadd__(self, snippet):
167 assert isinstance(snippet, Snippet)
168 self._snippets[snippet.name] = snippet
169 self._order += [snippet]
172 def __getitem__(self, key):
173 return self._snippets[key]
175 def __contains__(self, item):
176 return item in self._snippets
179 return iter(self._order)
181 def __eq__(self, other):
182 return self._snippets == other._snippets
184 def __ne__(self, other):
185 return not self == other
187 def loadFromFiles(self, fnames):
188 """Reads the snippets from several files and adds them to itself."""
192 def loadFromDict(self, snipDict):
193 """Adds snippets to itself from dictionary of LineLists."""
197 snip.readLines(snipDict[s])
200 def getDiff(self, other):
201 assert isinstance(other, Snippets)
204 if (snip.name[0:2] == '__'): continue
205 if (snip != other[snip.name]):
206 s += "Snippet %s:\n" % snip.name
207 s += snip.getDiff(other[snip.name])
210 # Include directoves matching this r.e. will be replaced by this script
211 reInclude = re.compile("^include ([^ ]*) #omkbuild")
213 class MakefileRules(LineList):
215 self.snippets = Snippets()
216 self.rules = LineList()
218 def _includeSnippets(self, filename, baseFileName="", onlyLoadSnippets=False):
219 """Recursively traverses snippets according to include
220 directives. If onlyLoadSnippets is True, self.rules is not
224 if filename in self.snippets:
225 sys.stderr.write("Error: Snippet included more than once\n")
226 # This is not allowed becouse it would cause problems
229 self.snippets += Snippet(filename)
231 lines = self.snippets[filename]['code']
233 addMarker = 1 # The first line of the snippet should be marked
235 match = reInclude.match(line)
237 # Include other snippet
238 self._includeSnippets(match.group(1).strip(),\
241 addMarker = 2 # The next line after include should be marked
243 # Add this line to rules
246 line = line.rstrip().ljust(80)+" #OMK:%s@%s\n"%(filename,baseFileName)
248 line = line.rstrip().ljust(80)+" #OMK:%s\n"%(filename)
250 if not onlyLoadSnippets:
253 def combineFrom(self, topLevelSnippet, onlyCheck=False):
254 """Produces self.rules from the topLevelSnippet and all
255 snippets included directly or indirectly from it."""
256 self.rules = LineList()
259 self._includeSnippets(topLevelSnippet, onlyLoadSnippets=True)
261 # Append legal and doc parts
262 for type in ['legal', 'doc']:
263 for snip in self.snippets:
265 if len(lines) == 0: continue
266 firstLine = lines[0].rstrip()
267 self.rules += [firstLine.ljust(80)+" #OMK:%s\n"%snip.name]
268 self.rules += lines[1:]
269 #self.rules += ['a'] # test error
272 self._includeSnippets(topLevelSnippet)
276 """Split self.rules to the original snippets in self.snippets."""
277 self.snippets = Snippets()
278 snipDict = self._getSnipDicts()
279 self.snippets.loadFromDict(snipDict)
281 def _getSnipDicts(self):
282 """Split self.rules to the original snippets, which are
283 returened as dictionary of LineLists."""
284 snipBegin = re.compile("^(.*)#OMK:([^@]*)(?:@(.*))?\n$")
286 currentLinesList = None
288 for line in self.rules:
289 match = snipBegin.match(line)
291 line = match.group(1).rstrip() + "\n"
292 snipName = match.group(2)
293 includedFrom = match.group(3)
295 if not includedFrom in snipDict: snipDict[includedFrom] = LineList()
296 snipDict[includedFrom].append("include %s #omkbuild\n" % snipName);
298 if not snipName in snipDict:
299 snipDict[snipName] = LineList()
300 currentLinesList = snipDict[snipName]
302 if currentLinesList != None:
303 currentLinesList.append(line);
308 def parseCommandLine():
309 parser = OptionParser(usage = """
310 %prog [-o FILE] top-level-snippet build Makefile.rules from the top-level-snippet and included ones
311 %prog [-o - ] -s Makfile.rules
313 parser.add_option("-s", "--split",
314 action="store", dest="split", default=False, metavar="RULES",
315 help="Split given Makefile.rules to the original snippets")
316 parser.add_option("-o", "--output",
317 action="store", dest="output", default=False, metavar="RULES",
318 help="Write Makefile.rules to file RULES")
319 (options, args) = parser.parse_args()
325 def buildRules(topLevelSnippet, output):
326 rules = MakefileRules()
327 rules.combineFrom(topLevelSnippet)
329 rulesCheck = MakefileRules()
330 rulesCheck.rules = rules.rules
333 if rules.snippets != rulesCheck.snippets:
334 sys.stderr.write("Consistency error:\n")
335 diff = rules.snippets.getDiff(rulesCheck.snippets)
336 sys.stderr.write(diff)
340 try: os.makedirs(os.path.dirname(output))
342 f = open(output,"w+")
344 f.writelines(rules.rules)
347 def splitRules(rulesFN, output):
348 rules = MakefileRules()
349 rules.rules.loadFromFile(rulesFN)
352 rulesCheck = MakefileRules()
353 rulesCheck.snippets = rules.snippets
355 topLevelSnippet = None
356 for snip in rules.snippets:
357 if snip.name.startswith("Makefile.rules."):
358 topLevelSnippet = snip.name
359 if not topLevelSnippet:
360 sys.stderr.write("No toplevel snippet (Makefile.rules.*) found\n")
363 rulesCheck.combineFrom(topLevelSnippet, onlyCheck=True)
365 # The comparsion is not that simple. The order of rules might be
366 # different. FIXME: Is this still true?
367 # if rules.rules != rulesCheck.rules:
368 # sys.stderr.write("Consistency error:\n")
369 # diff = rules.rules.getDiff(rulesCheck.rules)
370 # sys.stderr.write(diff)
373 for snip in rules.snippets:
374 if snip.name[0:2] == "__":
378 if output == "-": f = sys.stdout
379 else: f = open(snip.name, "w+")
380 f.writelines(snip.asLinesList())
384 (options, args) = parseCommandLine()
386 splitRules(options.split, options.output)
388 buildRules(args[0], options.output)
391 if __name__ == "__main__": main()