]> rtime.felk.cvut.cz Git - omk.git/blobdiff - omkbuild.py
Added news to web page
[omk.git] / omkbuild.py
index e88b29466010a8281fafd43cf26dd8779983fabb..facccc21d39e42edc433f298a85e9480ef6c5cb5 100755 (executable)
 #!/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@<snippet file name><EOL>. This mark
+     is used for splitting modified Makefile.rules back to the
+     original snippets. If <snippet file name> starts with __, it is
+     ignored during splitting.
+"""
 
 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 counter == 2: currentPart = self.doc
+            if line[0] != "#": currentPart = self.code
+
+            currentPart.append(line)
 
         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 = "<Snippet: %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
 
+class MakefileRules(LineList):
+    def __init__(self):
+        self.snippets = Snippets()
+        self.rules = LineList()
+
+    def combine(self):
+        """Combine self.snippets and produces self.rules"""
+        self.rules = LineList()
+
+        for type in ['legal', 'doc', 'code']:
+            for snip in self.snippets:
+                lines = snip[type]
+                if len(lines) == 0: continue
+                firstLine = string.rstrip(lines[0])
+                self.rules += [firstLine.ljust(60)+" #OMK@%s\n"%snip.name]
+                self.rules += lines[1:]
+                #self.rules += ['a'] # test error
+
+    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@(.*)$")
+        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)
+                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 - ] -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")
+    parser.add_option("-a", "--all",
+                      action="store_true", dest="all",
+                      help="Rebuild all rules acording to rulesdef.py")
     (options, args) = parser.parse_args()
     return options, args
 
-def splitRules(rules):
-    pass
+def buildRules(fnames, output, name):
+    rules = MakefileRules()
+    snip_rule_type = Snippet(name="__type")
+    snip_rule_type.addCodeLine("OMK_RULES_TYPE=%s\n" % name)
+    rules.snippets += snip_rule_type
+    rules.snippets.loadFromFiles(fnames)
+    rules.combine()
 
-def buildRules(snippets, output):
-    if output:
-        f = open(output, "w+")
-    else:
-        f = sys.stdout
+    rulesCheck = MakefileRules()
+    rulesCheck.rules = rules.rules
+    rulesCheck.split()
 
-    parts = []
+    if rules.snippets != rulesCheck.snippets:
+        sys.stderr.write("Consistency error:\n")
+        diff = rules.snippets.getDiff(rulesCheck.snippets)
+        sys.stderr.write(diff)
+        sys.exit(1)
         
-    for snip in snippets:
-        parts.append(Snippet(snip))
-
-    for type in ['legal', 'doc', 'code']:
-        for snip in parts:
-            f.writelines(snip.__dict__[type])
+    if output: f = open(output,"w+")
+    else: f = sys.stdout
+    f.writelines(rules.rules)
     f.close()
 
+def splitRules(rulesFN, output):
+    rules = MakefileRules()
+    rules.rules.loadFromFile(rulesFN)
+    rules.split()
+
+    rulesCheck = MakefileRules()
+    rulesCheck.snippets = rules.snippets
+    rulesCheck.combine()
+
+    # The comparsion is not that simple. The order of rules might be
+    # different.
+#     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 buildAllRules():
+    import rulesdef
+    os.chdir(snippetsDir)
+    for rules in rulesdef.rules:
+        print 'Building rules: %s' % rules
+        outputDir = os.path.join(sys.path[0], rulesDir, rules)
+        if not os.path.isdir(outputDir): os.makedirs(outputDir)
+        buildRules(rulesdef.rules[rules], os.path.join(outputDir, 'Makefile.rules'), rules)
+
 def main():
     (options, args) = parseCommandLine()
-    if options.split:
-        splitRules(options.split)
+    if options.all:
+        buildAllRules()
+    elif options.split:
+        splitRules(options.split, options.output)
     else:
         buildRules(args, options.output)
 
 
-if __name__ == "__main__":    main()
+if __name__ == "__main__": main()