#!/usr/bin/python -O import sys import os import re import stat sys.path = ["/usr/lib/portage/pym"]+sys.path import portage_contents from portage_contents import PathLookupTable, ContentsHandler, ContentsManager import portage_checksum import output from output import * import portage_util from portage_util import writemsg import popen2 from popen2 import Popen4 def printn(string): writemsg(string) def einfo(string): writemsg(green("* ")+string+"\n") def einfon(string): writemsg(green("* ")+string) def ewarn(string): writemsg(yellow("* ")+string+"\n") def ewarnn(string): writemsg(yellow("* ")+string) def eerror(string): writemsg(red("* ")+string+"\n") def eerrorn(string): writemsg(red("* ")+string) def getoutput(program): process = Popen4(program) if process.wait(): eerror("error calling \""+program+"\"") sys.exit(1) return process.fromchild.readlines() def initPathLookupTable(): einfo("Getting files list from portage's database... please wait.") global plt plt = PathLookupTable("spork") for x in os.listdir("/var/db/pkg"): for y in os.listdir("/var/db/pkg/"+x): c_path = "/var/db/pkg/"+x+"/"+y+"/CONTENTS" if os.path.exists(c_path): plt.addFromFile(c_path, ContentsHandler) def getBestManager(filename): owners = plt.whoProvides(filename) if not len(owners) > 0: ewarn("File "+filename+" not found in the portage's database") return 0 for owner in owners: manager = ContentsManager(owners[0]) # Check if the current file (before change) has the same md5 and mtime reported in the CONTENTS file, it this is right then choose this as the CONTENTS manager. pathstat = os.lstat(filename) mtime = pathstat[stat.ST_MTIME] md5 = portage_checksum.perform_md5(filename, calc_prelink=1) if manager.getMtime(filename) == str(mtime) and manager.getMD5(filename) == str(md5): ewarn("Found a CONTENTS file that owns file "+filename) return manager ewarn("File "+filename+" has different mtime or md5, not updating the CONTENTS file") return 0 def fixlafiles(): # Get our variables from environment if os.environ.has_key("OLDCHOST"): OLDCHOST = os.environ["OLDCHOST"] else: OLDCHOST = "" HAVE_GCC34 = 0 # Add the two default library paths DIRLIST= ["/lib", "/usr/lib"] # Walk /etc/ld.so.conf line for line and get any library paths infile = open("/etc/ld.so.conf") lines = infile.readlines() infile.close() for line in lines: if line[-1] == '\n': line = line[:-1] if not re.match('^\s*#', line): if line == "": continue # Remove any trailing comments line = re.sub('#.*$', '', line) # Remove any trailing spaces line = re.sub('\s+$', '', line) nodes = re.split('[:,\s]', line) # Now add the rest from ld.so.conf for node in nodes: node = re.sub('=.*', '', node) node = re.sub('\/$', '', node) if node == "": continue CHILD = 0 # Drop the directory if its a child directory of # one that was already added ... for directory in DIRLIST: if re.match('^'+directory, node): CHILD = 1 break if CHILD: continue DIRLIST.append(node) # We have no guarantee that ld.so.conf have more library paths than # the default, and its better to fix .la files only in /lib and # /usr/lib than not at all ... # Get line from gcc's output containing CHOST output = getoutput("gcc -v") TMP_CHOST = "" for line in output: if re.match("^Reading specs", line): TMP_CHOST = line break if TMP_CHOST[-1] == '\n': TMP_CHOST = TMP_CHOST[:-1] if TMP_CHOST == "" : # If we fail to get the CHOST, see if we can get the CHOST # portage thinks we are using ... pipe = os.popen("/usr/bin/portageq envvar 'CHOST'") CHOST = pipe.readlines()[0] if CHOST[-1] == '\n': CHOST = CHOST[:-1] if pipe.close: eerror("error calling \"/usr/bin/portageq envvar 'CHOST'\"") sys.exit(1) else: # Check pre gcc-3.4.x versions CHOST = re.sub("^.+lib/gcc-lib/([^/]+)/[0-9]+.+$", "\\1", TMP_CHOST, 1) if CHOST == TMP_CHOST or CHOST == "" : # Check gcc-3.4.x or later CHOST = re.sub("^.+lib/gcc/([^/]+)/[0-9]+.+$", "\\1", TMP_CHOST, 1) if CHOST == TMP_CHOST or CHOST == "" : CHOST = "" else: HAVE_GCC34 = 1 if CHOST == "" : eerror("Could not get gcc's CHOST!") sys.exit(1) if OLDCHOST != "": if OLDCHOST == CHOST: OLDCHOST = "" GCCLIBPREFIX_OLD = "/usr/lib/gcc-lib/" GCCLIBPREFIX_NEW = "/usr/lib/gcc/" if HAVE_GCC34: GCCLIBPREFIX = GCCLIBPREFIX_NEW else: GCCLIBPREFIX = GCCLIBPREFIX_OLD GCCLIB = GCCLIBPREFIX+CHOST if OLDCHOST != "": OLDGCCLIB1 = GCCLIBPREFIX_OLD+OLDCHOST OLDGCCLIB2 = GCCLIBPREFIX_NEW+OLDCHOST # Get current gcc's version pipe = os.popen("gcc -dumpversion") NEWVER = pipe.readlines()[0] if NEWVER[-1] == '\n': NEWVER = NEWVER[:-1] if pipe.close(): eerror("error calling \"gcc -dumpversion\""), sys.exit(1) if NEWVER == "" : eerror("Could not get gcc's version!"), sys.exit(1) for libdir in DIRLIST: # Do nothing if the target dir is gcc's internal library path if re.match(GCCLIBPREFIX_OLD, libdir) or re.match(GCCLIBPREFIX_NEW, libdir) : continue einfo(" Scanning "+libdir+"...") pipe = os.popen("find "+libdir+"/"+" -name '*.la' 2>/dev/null") la_files = pipe.readlines() pipe.close() for la_file in la_files: if la_file[-1] == '\n': la_file = la_file[:-1] # Do nothing if the .la file is located in gcc's internal lib path if re.match(GCCLIBPREFIX_OLD, la_file) or re.match(GCCLIBPREFIX_NEW, la_file): continue OLDVER = 0 VER_CHANGED = 0 CHOST_CHANGED = 0 CHANGED = 0 infile = open(la_file) la_lines = infile.readlines() infile.close() # See if we need to fix the .la file for la_line in la_lines: if la_line[-1] == '\n': la_line = la_line[:-1] if OLDCHOST != "": la_line, n1 = re.subn(OLDGCCLIB1+"([/\s]+)", GCCLIB+"\\1", la_line) la_line, n2 = re.subn(OLDGCCLIB2+"([/\s]+)", GCCLIB+"\\1", la_line) if n1 > 0 or n2 > 0: CHOST_CHANGED = 1 # Search for a version if not already founded in previous lines if not OLDVER: searchobject1 = re.search(GCCLIBPREFIX_OLD+CHOST+"/(?P[0-9]+(\.[0-9]+)*)/([/\s]*)", la_line) searchobject2 = re.search(GCCLIBPREFIX_NEW+CHOST+"/(?P[0-9]+(\.[0-9]+)*)/([/\s]*)", la_line) if searchobject1: OLDVER = str(searchobject1.group('version')) elif searchobject2: OLDVER = str(searchobject2.group('version')) if OLDVER and OLDVER != NEWVER : VER_CHANGED = 1 # Do the actual changes in a second loop, as we can then # verify that CHOST_CHANGED among things is correct ... if CHOST_CHANGED or VER_CHANGED: # get a possible contents manager. manager = getBestManager(la_file) ewarnn(" FIXING: "+la_file+" ... ") printn("[") # Clear the temp file (removing rather than '>foo' is better # out of a security point of view?) outfile = open(la_file+".new","w") for la_line in la_lines: if la_line[-1] == '\n': la_line = la_line[:-1] if OLDCHOST != "": tmpstr = re.sub(OLDGCCLIB1+"([/\s]+)", GCCLIB+"\\1", la_line) tmpstr = re.sub(OLDGCCLIB2+"([/\s]+)", GCCLIB+"\\1", tmpstr) if tmpstr != la_line: printn("c") la_line = tmpstr if CHOST_CHANGED > 0: # We try to be careful about CHOST changes outside # the gcc library path (meaning we cannot match it # via /GCCLIBPREFIX CHOST/) ... # Catch: # # dependency_libs=' -L/usr/CHOST/{bin,lib}' # la_line = re.sub("-L/usr/"+OLDCHOST+"/", "-L/usr/"+CHOST+"/", la_line) # Catch: # # dependency_libs=' -L/usr/lib/gcc-lib/CHOST/VER/../../../../CHOST/lib' # la_line = re.sub("("+GCCLIB+"/[^\s]+)/"+OLDCHOST+"/", "\\1/"+CHOST+"/", la_line) if VER_CHANGED: tmpstr = re.sub(GCCLIBPREFIX_OLD+CHOST+"/"+OLDVER+"([/\s]*)", GCCLIB+"/"+NEWVER+"\\1", la_line) tmpstr = re.sub(GCCLIBPREFIX_NEW+CHOST+"/"+OLDVER+"([/\s]*)", GCCLIB+"/"+NEWVER+"\\1", tmpstr) if tmpstr != la_line : # Catch: # # dependency_libs=' -L/usr/lib/gcc-lib/../../CHOST/lib' # # in cases where we have gcc34 tmpstr = re.sub(GCCLIBPREFIX_OLD+"(../../"+CHOST+"/lib)", GCCLIBPREFIX+"\\1", tmpstr) tmpstr = re.sub(GCCLIBPREFIX_NEW+"(../../"+CHOST+"/lib)", GCCLIBPREFIX+"\\1", tmpstr) printn("v") la_line = tmpstr outfile.write(la_line+"\n") print "]" outfile.close() try: os.rename(la_file+".new", la_file) except OSError, details: eerror ("unable to rename "+la_file+".new into "+la_file+": "+str(details)), sys.exit(1) except IOError, details: eerror ("unable to rename "+la_file+".new into "+la_file+": "+str(details)), sys.exit(1) if manager: ewarn(" Updating "+la_file+" infos in portage's database") manager.updateAllAttributes(la_file) manager.writeFile() initPathLookupTable() fixlafiles()