Home | History | Annotate | Download | only in tools
      1 # common python utility routines for the Bionic tool scripts
      2 
      3 import sys, os, commands, string, commands
      4 
      5 # basic debugging trace support
      6 # call D_setlevel to set the verbosity level
      7 # and D(), D2(), D3(), D4() to add traces
      8 #
      9 verbose = 0
     10 
     11 def panic(msg):
     12     sys.stderr.write( find_program_name() + ": error: " )
     13     sys.stderr.write( msg )
     14     sys.exit(1)
     15 
     16 def D(msg):
     17     global verbose
     18     if verbose > 0:
     19         print msg
     20 
     21 def D2(msg):
     22     global verbose
     23     if verbose >= 2:
     24         print msg
     25 
     26 def D3(msg):
     27     global verbose
     28     if verbose >= 3:
     29         print msg
     30 
     31 def D4(msg):
     32     global verbose
     33     if verbose >= 4:
     34         print msg
     35 
     36 def D_setlevel(level):
     37     global verbose
     38     verbose = level
     39 
     40 
     41 #  other stuff
     42 #
     43 #
     44 def find_program_name():
     45     return os.path.basename(sys.argv[0])
     46 
     47 def find_program_dir():
     48     return os.path.dirname(sys.argv[0])
     49 
     50 def find_file_from_upwards(from_path,target_file):
     51     """find a file in the current directory or its parents. if 'from_path' is None,
     52        seach from the current program's directory"""
     53     path = from_path
     54     if path == None:
     55         path = os.path.realpath(sys.argv[0])
     56         path = os.path.dirname(path)
     57         D("this script seems to be located in: %s" % path)
     58 
     59     while 1:
     60         D("probing "+path)
     61         if path == "":
     62             file = target_file
     63         else:
     64             file = path + "/" + target_file
     65 
     66         if os.path.isfile(file):
     67             D("found %s in %s" % (target_file, path))
     68             return file
     69 
     70         if path == "":
     71             return None
     72 
     73         path = os.path.dirname(path)
     74 
     75 def find_bionic_root():
     76     file = find_file_from_upwards(None, "SYSCALLS.TXT")
     77     if file:
     78         return os.path.dirname(file)
     79     else:
     80         return None
     81 
     82 def find_kernel_headers():
     83     """try to find the directory containing the kernel headers for this machine"""
     84     status, version = commands.getstatusoutput( "uname -r" )  # get Linux kernel version
     85     if status != 0:
     86         D("could not execute 'uname -r' command properly")
     87         return None
     88 
     89     # get rid of the "-xenU" suffix that is found in Xen virtual machines
     90     if len(version) > 5 and version[-5:] == "-xenU":
     91         version = version[:-5]
     92 
     93     path = "/usr/src/linux-headers-" + version
     94     D("probing %s for kernel headers" % (path+"/include"))
     95     ret = os.path.isdir( path )
     96     if ret:
     97         D("found kernel headers in: %s" % (path + "/include"))
     98         return path
     99     return None
    100 
    101 
    102 # parser for the SYSCALLS.TXT file
    103 #
    104 class SysCallsTxtParser:
    105     def __init__(self):
    106         self.syscalls = []
    107         self.lineno   = 0
    108 
    109     def E(msg):
    110         print "%d: %s" % (self.lineno, msg)
    111 
    112     def parse_line(self, line):
    113         pos_lparen = line.find('(')
    114         E          = self.E
    115         if pos_lparen < 0:
    116             E("missing left parenthesis in '%s'" % line)
    117             return
    118 
    119         pos_rparen = line.rfind(')')
    120         if pos_rparen < 0 or pos_rparen <= pos_lparen:
    121             E("missing or misplaced right parenthesis in '%s'" % line)
    122             return
    123 
    124         return_type = line[:pos_lparen].strip().split()
    125         if len(return_type) < 2:
    126             E("missing return type in '%s'" % line)
    127             return
    128 
    129         syscall_func = return_type[-1]
    130         return_type  = string.join(return_type[:-1],' ')
    131 
    132         pos_colon = syscall_func.find(':')
    133         if pos_colon < 0:
    134             syscall_name = syscall_func
    135         else:
    136             if pos_colon == 0 or pos_colon+1 >= len(syscall_func):
    137                 E("misplaced colon in '%s'" % line)
    138                 return
    139             syscall_name = syscall_func[pos_colon+1:]
    140             syscall_func = syscall_func[:pos_colon]
    141 
    142         if pos_rparen > pos_lparen+1:
    143             syscall_params = line[pos_lparen+1:pos_rparen].split(',')
    144             params         = string.join(syscall_params,',')
    145         else:
    146             syscall_params = []
    147             params         = "void"
    148 
    149         number = line[pos_rparen+1:].strip()
    150         if number == "stub":
    151             syscall_id  = -1
    152             syscall_id2 = -1
    153         else:
    154             try:
    155                 if number[0] == '#':
    156                     number = number[1:].strip()
    157                 numbers = string.split(number,',')
    158                 syscall_id  = int(numbers[0])
    159                 syscall_id2 = syscall_id
    160                 if len(numbers) > 1:
    161                     syscall_id2 = int(numbers[1])
    162             except:
    163                 E("invalid syscall number in '%s'" % line)
    164                 return
    165 
    166         t = { "id"     : syscall_id,
    167               "id2"    : syscall_id2,
    168               "name"   : syscall_name,
    169               "func"   : syscall_func,
    170               "params" : syscall_params,
    171               "decl"   : "%-15s  %s (%s);" % (return_type, syscall_func, params) }
    172 
    173         self.syscalls.append(t)
    174 
    175     def parse_file(self, file_path):
    176         fp = open(file_path)
    177         for line in fp.xreadlines():
    178             self.lineno += 1
    179             line = line.strip()
    180             if not line: continue
    181             if line[0] == '#': continue
    182             self.parse_line(line)
    183 
    184         fp.close()
    185 
    186 
    187 class Output:
    188     def  __init__(self,out=sys.stdout):
    189         self.out = out
    190 
    191     def write(self,msg):
    192         self.out.write(msg)
    193 
    194     def writeln(self,msg):
    195         self.out.write(msg)
    196         self.out.write("\n")
    197 
    198 class StringOutput:
    199     def __init__(self):
    200         self.line = ""
    201 
    202     def write(self,msg):
    203         self.line += msg
    204         D2("write '%s'" % msg)
    205 
    206     def writeln(self,msg):
    207         self.line += msg + '\n'
    208         D2("write '%s\\n'"% msg)
    209 
    210     def get(self):
    211         return self.line
    212 
    213 
    214 def create_file_path(path):
    215     dirs = []
    216     while 1:
    217         parent = os.path.dirname(path)
    218         #print "parent: %s <- %s" % (parent, path)
    219         if parent == "/" or parent == "":
    220             break
    221         dirs.append(parent)
    222         path = parent
    223 
    224     dirs.reverse()
    225     for dir in dirs:
    226         #print "dir %s" % dir
    227         if os.path.isdir(dir):
    228             continue
    229         os.mkdir(dir)
    230 
    231 def walk_source_files(paths,callback,args,excludes=[]):
    232     """recursively walk a list of paths and files, only keeping the source files in directories"""
    233     for path in paths:
    234         if len(path) > 0 and path[0] == '@':
    235             # this is the name of another file, include it and parse it
    236             path = path[1:]
    237             if os.path.exists(path):
    238                 for line in open(path):
    239                     if len(line) > 0 and line[-1] == '\n':
    240                         line = line[:-1]
    241                     walk_source_files([line],callback,args,excludes)
    242             continue
    243         if not os.path.isdir(path):
    244             callback(path,args)
    245         else:
    246             for root, dirs, files in os.walk(path):
    247                 #print "w-- %s (ex: %s)" % (repr((root,dirs)), repr(excludes))
    248                 if len(excludes):
    249                     for d in dirs[:]:
    250                         if os.path.join(root,d) in excludes:
    251                             dirs.remove(d)
    252                 for f in files:
    253                     r, ext = os.path.splitext(f)
    254                     if ext in [ ".h", ".c", ".cpp", ".S" ]:
    255                         callback( "%s/%s" % (root,f), args )
    256 
    257 def cleanup_dir(path):
    258     """create a directory if needed, and ensure that it is totally empty
    259        by removing any existing content in it"""
    260     if not os.path.exists(path):
    261         os.mkdir(path)
    262     else:
    263         for root, dirs, files in os.walk(path, topdown=False):
    264             if root.endswith("kernel_headers/"):
    265                 # skip 'kernel_headers'
    266                 continue
    267             for name in files:
    268                 os.remove(os.path.join(root, name))
    269             for name in dirs:
    270                 os.rmdir(os.path.join(root, name))
    271 
    272 def update_file( path, newdata ):
    273     """update a file on disk, only if its content has changed"""
    274     if os.path.exists( path ):
    275         try:
    276             f = open( path, "r" )
    277             olddata = f.read()
    278             f.close()
    279         except:
    280             D("update_file: cannot read existing file '%s'" % path)
    281             return 0
    282 
    283         if oldata == newdata:
    284             D2("update_file: no change to file '%s'" % path )
    285             return 0
    286 
    287         update = 1
    288     else:
    289         try:
    290             create_file_path(path)
    291         except:
    292             D("update_file: cannot create path to '%s'" % path)
    293             return 0
    294 
    295     f = open( path, "w" )
    296     f.write( newdata )
    297     f.close()
    298 
    299     return 1
    300 
    301 
    302 class BatchFileUpdater:
    303     """a class used to edit several files at once"""
    304     def __init__(self):
    305         self.old_files = set()
    306         self.new_files = set()
    307         self.new_data  = {}
    308 
    309     def readFile(self,path):
    310         #path = os.path.realpath(path)
    311         if os.path.exists(path):
    312             self.old_files.add(path)
    313 
    314     def readDir(self,path):
    315         #path = os.path.realpath(path)
    316         for root, dirs, files in os.walk(path):
    317             for f in files:
    318                 dst = "%s/%s" % (root,f)
    319                 self.old_files.add(dst)
    320 
    321     def editFile(self,dst,data):
    322         """edit a destination file. if the file is not mapped from a source,
    323            it will be added. return 0 if the file content wasn't changed,
    324            1 if it was edited, or 2 if the file is new"""
    325         #dst = os.path.realpath(dst)
    326         result = 1
    327         if os.path.exists(dst):
    328             f = open(dst, "r")
    329             olddata = f.read()
    330             f.close()
    331             if olddata == data:
    332                 self.old_files.remove(dst)
    333                 return 0
    334         else:
    335             result = 2
    336 
    337         self.new_data[dst] = data
    338         self.new_files.add(dst)
    339         return result
    340 
    341     def getChanges(self):
    342         """determine changes, returns (adds, deletes, edits)"""
    343         adds    = set()
    344         edits   = set()
    345         deletes = set()
    346 
    347         for dst in self.new_files:
    348             if not (dst in self.old_files):
    349                 adds.add(dst)
    350             else:
    351                 edits.add(dst)
    352 
    353         for dst in self.old_files:
    354             if not dst in self.new_files:
    355                 deletes.add(dst)
    356 
    357         return (adds, deletes, edits)
    358 
    359     def _writeFile(self,dst,data=None):
    360         if not os.path.exists(os.path.dirname(dst)):
    361             create_file_path(dst)
    362         if data == None:
    363             data = self.new_data[dst]
    364         f = open(dst, "w")
    365         f.write(self.new_data[dst])
    366         f.close()
    367 
    368     def updateFiles(self):
    369         adds, deletes, edits = self.getChanges()
    370 
    371         for dst in sorted(adds):
    372             self._writeFile(dst)
    373 
    374         for dst in sorted(edits):
    375             self._writeFile(dst)
    376 
    377         for dst in sorted(deletes):
    378             os.remove(dst)
    379 
    380     def updateP4Files(self):
    381         adds, deletes, edits = self.getChanges()
    382 
    383         if len(adds):
    384             files = string.join(sorted(adds)," ")
    385             D( "%d new files will be p4 add-ed" % len(adds) )
    386             for dst in adds:
    387                 self._writeFile(dst)
    388             D2("P4 ADDS: %s" % files)
    389             o = commands.getoutput( "p4 add " + files )
    390             D2( o )
    391 
    392         if len(edits):
    393             files = string.join(sorted(edits)," ")
    394             D( "%d files will be p4 edit-ed" % len(edits) )
    395             D2("P4 EDITS: %s" % files)
    396             o = commands.getoutput( "p4 edit " + files )
    397             D2( o )
    398             for dst in edits:
    399                 self._writeFile(dst)
    400 
    401         if len(deletes):
    402             files = string.join(sorted(deletes)," ")
    403             D( "%d files will be p4 delete-d" % len(deletes) )
    404             D2("P4 DELETES: %s" % files)
    405             o = commands.getoutput( "p4 delete " + files )
    406             D2( o )
    407 
    408     def updateGitFiles(self):
    409         adds, deletes, edits = self.getChanges()
    410 
    411         if adds:
    412             for dst in sorted(adds):
    413                 self._writeFile(dst)
    414             commands.getoutput("git add " + " ".join(adds))
    415 
    416         if deletes:
    417             commands.getoutput("git rm " + " ".join(deletes))
    418 
    419         if edits:
    420             for dst in sorted(edits):
    421                 self._writeFile(dst)
    422             commands.getoutput("git add " + " ".join(edits))
    423