RULE: rule checker can check more rules 45/119345/1
authorMyungJoo Ham <myungjoo.ham@samsung.com>
Thu, 16 Mar 2017 11:05:25 +0000 (20:05 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Thu, 16 Mar 2017 11:05:25 +0000 (20:05 +0900)
- Orphaned blocks
- Added rules

Change-Id: Ifa3ec976502960a491072e24e7f07e16794d60e6
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
RULES
rule_checker.py

diff --git a/RULES b/RULES
index 226aaad..c04476b 100644 (file)
--- a/RULES
+++ b/RULES
@@ -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
index abf4212..529b3e8 100755 (executable)
 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))