diff options
-rw-r--r-- | RULES | 2 | ||||
-rwxr-xr-x | rule_checker.py | 231 |
2 files changed, 214 insertions, 19 deletions
@@ -94,3 +94,5 @@ We do not have any semantics for building blocks with such relations. When we have definitions for such semantics and have them implemented in TIC, we can allow then. + +5.3. A block must NOT have any files included diff --git a/rule_checker.py b/rule_checker.py index abf4212..529b3e8 100755 --- a/rule_checker.py +++ b/rule_checker.py @@ -18,17 +18,77 @@ from __future__ import print_function import re import os -import os.path import sys +import collections + +Block = collections.namedtuple('Block', 'name level parent children description files') +blocks = {} def report(file, lc, code): print(file + ":Line " + str(lc) + " |"+code) + return 0 + +def ruleCheckInterBlock(): + global blocks + error = 0 + warning = 0 + root_suggested = {} + file = "packaging/building-blocks.spec" + + try: + f = open(file) + except: + error += 1 + print("ERROR: cannot find packaging/building-blocks.spec") + return (1, 0) + for line in f: + if re.search(r'^\s*Suggests:\s*%{name}-root-[a-zA-Z0-9_]', line): + n = re.sub(r'^\s*Suggests:\s*%{name}-root-', r'', line) + n = re.sub(r'\s*', r'', n) + n = re.sub(r'\n', r'', n) + root_suggested[n] = 1 + f.close() + + for n in blocks: + # Orphan check + if blocks[n].level == 0: + if not n in root_suggested: + error += 1 + print("ERROR: Orphaned root block. Add Suggests: %{name}-root-"+n+" at building-blocks.spec") + else: # subX + p = blocks[n].parent + if not p in blocks: + error += 1 + print("ERROR: Orphaned sub block. The block "+n+" requires a parent block "+p+".") + else: + found = 0 + # check if p has n as its child + for c in blocks[p].children: + if c == n: + found = 1 + break + if found == 0: + error += 1 + print("ERROR: Orphaned sub block. The block "+n+" is not registered at the parent block "+p+" although "+p+" exists.") + + # TODO: Which item? + + + + return (error, warning) def ruleCheckInc(file): + global blocks + + print("Checking "+file) + error = 0 warning = 0 lc = 0 + files = 0 # Start checking if %files section have files (error if exists) + lastpkg = '' + try: f = open("packaging/"+file, 'r') except: @@ -37,86 +97,215 @@ def ruleCheckInc(file): return (0, 1) for line in f: lc += 1 + + if (files == 1): + if re.search(r'^\s*(%package)|(%build)|(%description)|(%prep)|(%clean)|(%install)|(%post)|(%pre)', line): + files = 0 + else: + if re.search(r'^\s*[^#\s]+', line) and \ + not re.search(r'^\s*%include', line): + error += 1 + print("ERROR: RULE 5.3 a block must not have a file included") + report(file, lc, line) + continue + + if re.search(r'^\s*((Suggests)|(Requires))', line, re.IGNORECASE): + if not re.search(r'^\s*((Suggests)|(Requires)):', line): + error += 1 + print("ERROR: Use case sensitive put : directly after the keyword") + report(file, lc, line) + continue + + if len(lastpkg) < 1: + error += 1 + print("ERROR: You should add Suggests:/Requires: after %package before other sections") + report(file, lc, line) + continue + else: + c = re.sub(r'^\s*((Suggests)|(Requires)):\s*%{name}-sub[12]-', r'', line) + c = re.sub(r'\s*', r'', c) + c = re.sub(r'\n', r'', c) + + cs = blocks[n].children + cs.append(c) + blocks[n]._replace(children = cs) + print("Children added to "+n+" of "+c) + # RULE 5.1 - if re.search('^\s*BuildRequires', line, re.IGNORECASE): + if re.search(r'^\s*BuildRequires', line, re.IGNORECASE): error += 1 print("ERROR: RULE 5.1 .inc file cannot have BuildRequires tags") + report(file, lc, line) continue # Prevent: https://github.com/rpm-software-management/rpm/issues/158 - if re.search('^#.*[^%]%[^%]', line) and not re.search('^#!', line): + if re.search(r'^#.*[^%]%[^%]', line) and not re.search('^#!', line): error += 1 print("ERROR: unless it is shebang, you must not have rpm macro in a # comment. They are expanded and multiline macro will do unexpected effects.") report(file, lc, line) continue # RULE 5.2 - if re.search('^\s*Recommends', line, re.IGNORECASE) or \ - re.search('^\s*Provides', line, re.IGNORECASE) or \ - re.search('^\s*Enhances', line, re.IGNORECASE) or \ - re.search('^\s*Supplements', line, re.IGNORECASE): + if re.search(r'^\s*Recommends', line, re.IGNORECASE) or \ + re.search(r'^\s*Provides', line, re.IGNORECASE) or \ + re.search(r'^\s*Enhances', line, re.IGNORECASE) or \ + re.search(r'^\s*Supplements', line, re.IGNORECASE): error += 1 print("ERROR: RULE 5.2 .inc file cannot have unsupported relations") report(file, lc, line) continue # RULE 1-1 - if re.search('^\s*%package\s*-n', line, re.IGNORECASE): + if re.search(r'^\s*%package\s*-n', line, re.IGNORECASE): error += 1 print("ERROR: RULE 1.1 to ensure 1.1, do not use -n option in package name") report(file, lc, line) continue # Implicit / General Rule - if re.search('^\s*%package\s', line, re.IGNORECASE) and not re.search('^\s*%package\s', line): + if re.search(r'^\s*%package\s', line, re.IGNORECASE) and \ + not re.search(r'^\s*%package\s', line): error += 1 print('ERROR: (General) Please use %package, not '+re.search('^%package')) report(file, lc, line) continue # RULE 1-3 / 1-4 - if re.search('^\s*%package', line) and not re.search('^\s*%package\s*(root)|(sub1)|(sub2)', line): + if re.search(r'^\s*%package', line) and \ + not re.search(r'^\s*%package\s*((root)|(sub1)|(sub2))', line): error +=1 print("ERROR: RULE 1.3 the send prefix should be root, sub1, or sub2.") report(file, lc, line) continue # RULE 1-9 for root block (1-5) - if re.search('^\s*%package\s*root', line) and \ - not re.search('^\s*%package\s*root-[a-zA-Z0-9_]+\s*$', line): + if re.search(r'^\s*%package\s*root', line) and \ + not re.search(r'^\s*%package\s*root-[a-zA-Z0-9_]+\s*$', line): error += 1 print("ERROR: RULE 1.9 not met with root (RULE 1.5)") report(file, lc, line) continue # RULE 1-9 for sub1 block (1-6) - if re.search('^\s*%package\s*sub1', line) and \ - not re.search('^\s*%package\s*sub1-[a-zA-Z0-9_]+-[a-zA-Z0-9_]+\s*$', line): + if re.search(r'^\s*%package\s*sub1', line) and \ + not re.search(r'^\s*%package\s*sub1-[a-zA-Z0-9_]+-[a-zA-Z0-9_]+\s*$', line): error += 1 print("ERROR: RULE 1.9 not met with sub1 (RULE 1.6)") report(file, lc, line) continue # RULE 1-9 for sub2 block (1-7) - if re.search('^\s*%package\s*sub2', line) and \ - not re.search('^\s*%package\s*sub2-[a-zA-Z0-9_]+-[a-zA-Z0-9_]+-[a-zA-Z0-9_]+\s*$', line): + if re.search(r'^\s*%package\s*sub2', line) and \ + not re.search(r'^\s*%package\s*sub2-[a-zA-Z0-9_]+-[a-zA-Z0-9_]+-[a-zA-Z0-9_]+\s*$', line): error += 1 print("ERROR: RULE 1.9 not met with sub1 (RULE 1.7)") report(file, lc, line) continue + # Adding a new package. Register to the data structure for context-aware check + if re.search(r'^\s*%package\s*((root)|(sub1)|(sub2))-', line): + # remove tag & prefixes + n = re.sub(r'^\s*%package\s*((root)|(sub1)|(sub2))-', r'', line) + # remove tailing whitespaces + n = re.sub(r'\s*', r'', n) + # remove trailing \n + n = re.sub(r'\n', r'', n) + if len(n) > 0: + l = 0 + p = '' - + if re.search(r'^\s*%package\s*root-', line): + l = 0 + p = '' + elif re.search(r'^\s*%package\s*sub1-', line): + l = 1 + p = re.sub(r'^([a-zA-Z0-9_]+)-.*', r'\1', n) + elif re.search(r'^\s*%package\s*sub2-', line): + l = 2 + p = re.sub(r'^([a-zA-Z0-9_]+-[a-zA-Z0-9_]+)-.*', r'\1', n) + p = re.sub(r'\n', r'', p) # remove trailing \n - f.close() - + print("Block: "+n+", level = "+str(l)+", parent = ["+p+"]") + lastpkg = n + + newBlock = Block(name=n, level=l, parent=p, children=[], description=0, files=0) + blocks[n] = newBlock + else: + error += 1 + print("ERROR: Package name too short") + report(file, lc, line) + continue + + # Check for %description entry + if re.search(r'^\s*%description\s+', line): + lastpkg = '' + + # remove tag + n = re.sub(r'^\s*%description\s+', r'', line) + # prefixes + n = re.sub(r'^((root)|(sub1)|(sub2))-', r'', n) + #remove trailing whitespaces / \n + n = re.sub(r'\s*', r'', n) + n = re.sub(r'\n', r'', n) + + if n in blocks: + if blocks[n].description == 1: + error += 1 + print("ERROR: (General) duplicated %description") + report(file, lc, line) + continue + else: + blocks[n]._replace(description = 1) + else: + error += 1 + print("ERROR: (General) %description appears before %package or in another file") + report(file, lc, line) + continue + + # Check for %files entry + if re.search(r'^\s*%files\s+', line): + files = 1 + lastpkg = '' + # remove tag + n = re.sub(r'^\s*%files\s+', r'', line) + # prefixes + n = re.sub(r'^((root)|(sub1)|(sub2))-', r'', n) + #remove trailing whitespaces / \n + n = re.sub(r'\s*', r'', n) + n = re.sub(r'\n', r'', n) + + if n in blocks: + if blocks[n].files == 1: + error += 1 + print("ERROR: (General) duplicated %files") + report(file, lc, line) + continue + else: + blocks[n]._replace(files = 1) + else: + error += 1 + print("ERROR: (General) %files appears before %package or in another file") + report(file, lc, line) + continue + + # Check for not allowed sections + if re.search(r'^\s*(%post)|(%pre)|(%build)|(%clean)|(%install)', line): + error += 1 + print("ERROR: It is not allowed to add such sections in each domain.") + report(file, lc, line) + continue + + + f.close() return (error, warning) def main(): + global blocks + dirs = os.listdir("packaging/") error = 0 warning = 0 @@ -128,6 +317,10 @@ def main(): error += result[0] warning += result[1] + result = ruleCheckInterBlock() + error += result[0] + warning += result[1] + print('Error: '+str(error)) print('Warning: '+str(warning)) |