documentation := comment*
rules ::= text
- comment ::= '#' text
- empty-comment ::= '#'
- text ::= [^#] ...
+ comment ::= '#' text '\n'
+ empty-comment ::= '#' '\n'
+ text ::= [^#] ... '\n'
Makefile.rules policies:
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 mart of the form #OMK@<snippet file name><EOL>. This mark
- is used for splitting Makefile.rules back to the original
- snippets.
+ special mark of the form:
+ #OMK:<snippet file name>[@<included from snippet>]<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.
+
+ * 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
+rulesDir = "rules"
+snippetsDir = "snippets"
+
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"
def loadFromFile(self, fname):
"""Loads itself from file."""
- f = open(fname, "r")
+ 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):
+ def __init__(self, fname = None, name = ""):
"""Initializes the snippet and if fname is given, reads it
from file"""
- self.name = ""
+ self.name = name
self.legal = LineList()
self.doc = LineList()
self.code = LineList()
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
if currentPart == self.legal:
if line.strip() == "#": counter += 1
else: counter = 0
- if counter == 2: currentPart = self.doc
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 = LineList()
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 combine(self):
- """Combine self.snippets and produces self.rules"""
+ 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()
- for type in ['legal', 'doc', 'code']:
+ 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(60)+" #OMK@%s\n"%snip.name]
+ 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()
def _getSnipDicts(self):
"""Split self.rules to the original snippets, which are
returened as dictionary of LineLists."""
- snipBegin = re.compile("^(.*)#OMK@(.*)$")
+ snipBegin = re.compile("^(.*)#OMK:([^@]*)(?:@(.*))?\n$")
snipDict = dict()
currentLinesList = None
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]
-
- currentLinesList.append(line);
+
+ if currentLinesList != None:
+ currentLinesList.append(line);
+
return snipDict
def parseCommandLine():
parser = OptionParser(usage = """
- %prog [-o FILE] snippet1 snippet2 ... build Makefile.rules from snippets
+ %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="output", default=False, metavar="RULES",
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 buildRules(fnames, output):
+def buildRules(topLevelSnippet, output):
rules = MakefileRules()
- rules.snippets.loadFromFiles(fnames)
- rules.combine()
+ rules.combineFrom(topLevelSnippet)
rulesCheck = MakefileRules()
rulesCheck.rules = rules.rules
sys.stderr.write(diff)
sys.exit(1)
- if output: f = open(output,"w+")
+ if output:
+ try: os.makedirs(os.path.dirname(output))
+ except: pass
+ f = open(output,"w+")
else: f = sys.stdout
f.writelines(rules.rules)
f.close()
rulesCheck = MakefileRules()
rulesCheck.snippets = rules.snippets
- rulesCheck.combine()
- # The order of rules might be different
+ 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)
+
+ rulesCheck.combineFrom(topLevelSnippet, onlyCheck=True)
+
+ # 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.exit(1)
for snip in rules.snippets:
- print snip.__class__
-# f = None
-# if output == "-": f = sys.stdout
-# f.writelines(rules.rules)
-# f.close()
-
+ 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, options.output)
else:
- buildRules(args, options.output)
+ buildRules(args[0], options.output)
if __name__ == "__main__": main()