X-Git-Url: http://rtime.felk.cvut.cz/gitweb/omk.git/blobdiff_plain/b01389db001da0ccd012c4a9318ea09791b8bf1a..391f17adc34366084f58ff79ba125e1e1b67b811:/omkbuild.py diff --git a/omkbuild.py b/omkbuild.py index e88b294..f409563 100755 --- a/omkbuild.py +++ b/omkbuild.py @@ -1,85 +1,389 @@ #!/usr/bin/env python +""" +This script has two main functions: + + 1) Combine several snippets to form Makefile.rules file + 2) Split Makefile.rules file to the originnal snippets + +Snippet syntax: + + snippet ::= legal? documentation rules + + legal ::= comment* empty-comment empty-comment + documentation := comment* + rules ::= text + + comment ::= '#' text '\n' + empty-comment ::= '#' '\n' + text ::= [^#] ... '\n' + + Makefile.rules policies: + + * All the parts of the Makefile.rules are ordered in the same order + as they are in snippets i.e. copyrights, documentations and rules. + + * On the first line of each part of the Makefile.rules, there is + special mark of the form: + + #OMK:[@] + + This mark is used for splitting modified Makefile.rules back to + the original snippets. If starts with __, it + is ignored during splitting. + + * Toplevel snippet has name in the forb Makefile.rules.* and no + other (included) snippet has such name. +""" from optparse import OptionParser import os +import os.path import sys +import string +import re -class Snippet: +rulesDir = "rules" +snippetsDir = "snippets" - def __init__(self, fname): - self.name = "" - self.legal = [] - self.doc = [] - self.code = [] - self.read(fname) +class LineList(list): + """List of text lines""" + def getDiff(self, other): + s = '' + for i in range(len(self)): + if i >= len(other): + s += (" Line %d differs!\n" % i) + s += " -"+self[i].rstrip() + "\n" + s += " +\n" + break + if self[i] != other[i]: + s += (" Line %d differs!\n" % i) + s += " -"+self[i].rstrip() + "\n" + s += " +"+other[i].rstrip() + "\n" + break + return s + + def __str__(self): + s = '' + for l in self: s += l + return s - def read(self, fname): + def loadFromFile(self, fname): + """Loads itself from file.""" + try: + f = open(fname, "r") + except IOError: + sys.stderr.write("Cannot open %s\n" % fname) + sys.exit(1) + + self.extend(f.readlines()) + f.close + +class Snippet: + def __init__(self, fname = None, name = ""): + """Initializes the snippet and if fname is given, reads it + from file""" + self.name = name + self.legal = LineList() + self.doc = LineList() + self.code = LineList() + if fname: self.loadFromFile(fname) + + def loadFromFile(self, fname): + """Loads snippet from file.""" self.name = fname f = open(fname, "r") - current = self.legal + + self.readLines(f.readlines()) + + f.close + + def addCodeLine(self, line): + self.code.append(line) + + def readLines(self, lines): + """Parses the snippet given as a list and stores it in itself.""" + currentPart = self.legal counter = 0 - for line in f: - if current == self.legal: + + for line in lines: + if currentPart == self.legal: if line.strip() == "#": counter += 1 else: counter = 0 - if counter == 2: current = self.doc - if line[0] != "#": current = self.code - - current.append(line) + if line[0] != "#": currentPart = self.code + + currentPart.append(line) + + if counter == 2: + currentPart = self.doc + counter = 0 + if not self.doc: self.doc = self.legal - self.legal = [] - - f.close + self.legal = LineList() + + def asLinesList(self): + lines = LineList() + for type in ['legal', 'doc', 'code']: + for line in self.__dict__[type]: + lines.append(line) + return lines def __str__(self): - s = "Snippet: %s\n" % self.name - s += " Legal: %d lines\n" % len(self.legal) - s += " Doc: %d lines\n" % len(self.doc) - s += " Code: %d lines\n" % len(self.code) + return str(self.asLinesList()) + + def __repr__(self): + s = "" % self.name return s + def __cmp__(self, other): + ret = cmp(self.name, other.name) + if ret != 0: return ret + ret = cmp(self.legal, other.legal) + if ret != 0: return ret + ret = cmp(self.doc, other.doc) + if ret != 0: return ret + ret = cmp(self.code, other.code) + return ret + + def __getitem__(self, key): + return { + 'legal': self.legal, + 'doc' : self.doc, + 'code' : self.code + }[key] + + def getDiff(self, other): + return self.asLinesList().getDiff(other.asLinesList()) + +class Snippets: + """Collection of snippets, where snippets can be accessed + individually by name (like dictionary) or sequentionaly in the + order they were added.""" + def __init__(self): + self._snippets = dict() + self._order = list() + + def __iadd__(self, snippet): + assert isinstance(snippet, Snippet) + self._snippets[snippet.name] = snippet + self._order += [snippet] + return self + + def __getitem__(self, key): + return self._snippets[key] + + def __contains__(self, item): + return item in self._snippets + + def __iter__(self): + return iter(self._order) + + def __cmp__(self, other): + return cmp(self._snippets, other._snippets) + + def loadFromFiles(self, fnames): + """Reads the snippets from several files and adds them to itself.""" + for fn in fnames: + self += Snippet(fn) + + def loadFromDict(self, snipDict): + """Adds snippets to itself from dictionary of LineLists.""" + for s in snipDict: + snip = Snippet() + snip.name = s + snip.readLines(snipDict[s]) + self += snip + + def getDiff(self, other): + assert isinstance(other, Snippets) + s = '' + for snip in self: + if (snip.name[0:2] == '__'): continue + if (snip != other[snip.name]): + s += "Snippet %s:\n" % snip.name + s += snip.getDiff(other[snip.name]) + return s + +# Include directoves matching this r.e. will be replaced by this script +reInclude = re.compile("^include ([^ ]*) #omkbuild") + +class MakefileRules(LineList): + def __init__(self): + self.snippets = Snippets() + self.rules = LineList() + + def _includeSnippets(self, filename, baseFileName="", onlyLoadSnippets=False): + """Recursively traverses snippets according to include + directives. If onlyLoadSnippets is True, self.rules is not + modified ...""" + + if onlyLoadSnippets: + if filename in self.snippets: + sys.stderr.write("Error: Snippet included more than once\n") + # This is not allowed becouse it would cause problems + # during spliting + sys.exit(1) + self.snippets += Snippet(filename) + + lines = self.snippets[filename]['code'] + + addMarker = 1 # The first line of the snippet should be marked + for line in lines: + match = reInclude.match(line) + if match: + # Include other snippet + self._includeSnippets(match.group(1).strip(),\ + filename, + onlyLoadSnippets) + addMarker = 2 # The next line after include should be marked + else: + # Add this line to rules + if addMarker: + if addMarker==1: + line = string.rstrip(line).ljust(80)+" #OMK:%s@%s\n"%(filename,baseFileName) + elif addMarker==2: + line = string.rstrip(line).ljust(80)+" #OMK:%s\n"%(filename) + addMarker = 0 + if not onlyLoadSnippets: + self.rules += [line] + + def combineFrom(self, topLevelSnippet, onlyCheck=False): + """Produces self.rules from the topLevelSnippet and all + snippets included directly or indirectly from it.""" + self.rules = LineList() + + if not onlyCheck: + self._includeSnippets(topLevelSnippet, onlyLoadSnippets=True) + + # Append legal and doc parts + for type in ['legal', 'doc']: + for snip in self.snippets: + lines = snip[type] + if len(lines) == 0: continue + firstLine = string.rstrip(lines[0]) + self.rules += [firstLine.ljust(80)+" #OMK:%s\n"%snip.name] + self.rules += lines[1:] + #self.rules += ['a'] # test error + + # Append code parts + self._includeSnippets(topLevelSnippet) + + + def split(self): + """Split self.rules to the original snippets in self.snippets.""" + self.snippets = Snippets() + snipDict = self._getSnipDicts() + self.snippets.loadFromDict(snipDict) + + def _getSnipDicts(self): + """Split self.rules to the original snippets, which are + returened as dictionary of LineLists.""" + snipBegin = re.compile("^(.*)#OMK:([^@]*)(?:@(.*))?\n$") + snipDict = dict() + currentLinesList = None + + for line in self.rules: + match = snipBegin.match(line) + if match: + line = match.group(1).rstrip() + "\n" + snipName = match.group(2) + includedFrom = match.group(3) + if includedFrom: + if not includedFrom in snipDict: snipDict[includedFrom] = LineList() + snipDict[includedFrom].append("include %s #omkbuild\n" % snipName); + + if not snipName in snipDict: + snipDict[snipName] = LineList() + currentLinesList = snipDict[snipName] + + if currentLinesList != None: + currentLinesList.append(line); + + return snipDict + def parseCommandLine(): parser = OptionParser(usage = """ - %prog [-o FILE] snippet1 snippet2 ... build Makefile.rules from snippets - %prog --split Makfile.rules + %prog [-o FILE] top-level-snippet build Makefile.rules from the top-level-snippet and included ones + %prog [-o - ] -s Makfile.rules """) parser.add_option("-s", "--split", action="store", dest="split", default=False, metavar="RULES", help="Split given Makefile.rules to the original snippets") parser.add_option("-o", "--output", action="store", dest="output", default=False, metavar="RULES", - help="Split given Makefile.rules to the original snippets") + help="Write Makefile.rules to file RULES") (options, args) = parser.parse_args() + if len(args) > 1: + parser.print_help() + sys.exit(1) return options, args -def splitRules(rules): - pass +def buildRules(topLevelSnippet, output): + rules = MakefileRules() + rules.combineFrom(topLevelSnippet) -def buildRules(snippets, output): + rulesCheck = MakefileRules() + rulesCheck.rules = rules.rules + rulesCheck.split() + + if rules.snippets != rulesCheck.snippets: + sys.stderr.write("Consistency error:\n") + diff = rules.snippets.getDiff(rulesCheck.snippets) + sys.stderr.write(diff) + sys.exit(1) + if output: - f = open(output, "w+") - else: - f = sys.stdout + try: os.makedirs(os.path.dirname(output)) + except: pass + f = open(output,"w+") + else: f = sys.stdout + f.writelines(rules.rules) + f.close() - parts = [] +def splitRules(rulesFN, output): + rules = MakefileRules() + rules.rules.loadFromFile(rulesFN) + rules.split() + + rulesCheck = MakefileRules() + rulesCheck.snippets = rules.snippets + + topLevelSnippet = None + for snip in rules.snippets: + if snip.name.startswith("Makefile.rules."): + topLevelSnippet = snip.name + if not topLevelSnippet: + sys.stderr.write("No toplevel snippet (Makefile.rules.*) found\n") + sys.exit(1) - for snip in snippets: - parts.append(Snippet(snip)) + rulesCheck.combineFrom(topLevelSnippet, onlyCheck=True) - for type in ['legal', 'doc', 'code']: - for snip in parts: - f.writelines(snip.__dict__[type]) - f.close() + # The comparsion is not that simple. The order of rules might be + # different. FIXME: Is this still true? +# if rules.rules != rulesCheck.rules: +# sys.stderr.write("Consistency error:\n") +# diff = rules.rules.getDiff(rulesCheck.rules) +# sys.stderr.write(diff) +# sys.exit(1) + + for snip in rules.snippets: + if snip.name[0:2] == "__": + continue + print snip.name + f = None + if output == "-": f = sys.stdout + else: f = open(snip.name, "w+") + f.writelines(snip.asLinesList()) + f.close() def main(): (options, args) = parseCommandLine() if options.split: - splitRules(options.split) + splitRules(options.split, options.output) else: - buildRules(args, options.output) + buildRules(args[0], options.output) -if __name__ == "__main__": main() +if __name__ == "__main__": main()