# Copyright 2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 import os as _os import re from portage import _unicode_decode from portage.exception import InvalidData ######################################################### # This an re-implementaion of dev-util/lafilefixer-0.5. # rewrite_lafile() takes the contents of an lafile as a string # It then parses the dependency_libs and inherited_linker_flags # entries. # We insist on dependency_libs being present. inherited_linker_flags # is optional. # There are strict rules about the syntax imposed by libtool's libltdl. # See 'parse_dotla_file' and 'trim' functions in libltdl/ltdl.c. # Note that duplicated entries of dependency_libs and inherited_linker_flags # are ignored by libtool (last one wins), but we treat it as error (like # lafilefixer does). # What it does: # * Replaces all .la files with absolut paths in dependency_libs with # corresponding -l* and -L* entries # (/usr/lib64/libfoo.la -> -L/usr/lib64 -lfoo) # * Moves various flags (see flag_re below) to inherited_linker_flags, # if such an entry was present. # * Reorders dependency_libs such that all -R* entries precede -L* entries # and these precede all other entries. # * Remove duplicated entries from dependency_libs # * Takes care that no entry to inherited_linker_flags is added that is # already there. ######################################################### #These regexes are used to parse the interesting entries in the la file dep_libs_re = re.compile(b"dependency_libs='(?P[^']*)'$") inh_link_flags_re = re.compile(b"inherited_linker_flags='(?P[^']*)'$") #regexes for replacing stuff in -L entries. #replace 'X11R6/lib' and 'local/lib' with 'lib', no idea what's this about. X11_local_sub = re.compile(b"X11R6/lib|local/lib") #get rid of the '..' pkgconfig_sub1 = re.compile(b"usr/lib[^/]*/pkgconfig/\.\./\.\.") pkgconfig_sub2 = re.compile(b"(?Pusr/lib[^/]*)/pkgconfig/\.\.") #detect flags that should go into inherited_linker_flags instead of dependency_libs flag_re = re.compile(b"-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads") def _parse_lafile_contents(contents): """ Parses 'dependency_libs' and 'inherited_linker_flags' lines. """ dep_libs = None inh_link_flags = None for line in contents.split(b"\n"): m = dep_libs_re.match(line) if m: if dep_libs is not None: raise InvalidData("duplicated dependency_libs entry") dep_libs = m.group("value") continue m = inh_link_flags_re.match(line) if m: if inh_link_flags is not None: raise InvalidData("duplicated inherited_linker_flags entry") inh_link_flags = m.group("value") continue return dep_libs, inh_link_flags def rewrite_lafile(contents): """ Given the contents of an .la file, parse and fix it. This operates with strings of raw bytes (assumed to contain some ascii characters), in order to avoid any potential character encoding issues. Raises 'InvalidData' if the .la file is invalid. @param contents: the contents of a libtool archive file @type contents: bytes @rtype: tuple @returns: (True, fixed_contents) if something needed to be fixed, (False, None) otherwise. """ #Parse the 'dependency_libs' and 'inherited_linker_flags' lines. dep_libs, inh_link_flags = \ _parse_lafile_contents(contents) if dep_libs is None: raise InvalidData("missing or invalid dependency_libs") new_dep_libs = [] new_inh_link_flags = [] librpath = [] libladir = [] if inh_link_flags is not None: new_inh_link_flags = inh_link_flags.split() #Check entries in 'dependency_libs'. for dep_libs_entry in dep_libs.split(): if dep_libs_entry.startswith(b"-l"): #-lfoo, keep it if dep_libs_entry not in new_dep_libs: new_dep_libs.append(dep_libs_entry) elif dep_libs_entry.endswith(b".la"): #Two cases: #1) /usr/lib64/libfoo.la, turn it into -lfoo and append -L/usr/lib64 to libladir #2) libfoo.la, keep it dir, file = _os.path.split(dep_libs_entry) if not dir or not file.startswith(b"lib"): if dep_libs_entry not in new_dep_libs: new_dep_libs.append(dep_libs_entry) else: #/usr/lib64/libfoo.la -> -lfoo lib = b"-l" + file[3:-3] if lib not in new_dep_libs: new_dep_libs.append(lib) #/usr/lib64/libfoo.la -> -L/usr/lib64 ladir = b"-L" + dir if ladir not in libladir: libladir.append(ladir) elif dep_libs_entry.startswith(b"-L"): #Do some replacement magic and store them in 'libladir'. #This allows us to place all -L entries at the beginning #of 'dependency_libs'. ladir = dep_libs_entry ladir = X11_local_sub.sub(b"lib", ladir) ladir = pkgconfig_sub1.sub(b"usr", ladir) ladir = pkgconfig_sub2.sub(b"\g", ladir) if ladir not in libladir: libladir.append(ladir) elif dep_libs_entry.startswith(b"-R"): if dep_libs_entry not in librpath: librpath.append(dep_libs_entry) elif flag_re.match(dep_libs_entry): #All this stuff goes into inh_link_flags, if the la file has such an entry. #If it doesn't, they stay in 'dependency_libs'. if inh_link_flags is not None: if dep_libs_entry not in new_inh_link_flags: new_inh_link_flags.append(dep_libs_entry) else: if dep_libs_entry not in new_dep_libs: new_dep_libs.append(dep_libs_entry) else: raise InvalidData("Error: Unexpected entry '%s' in 'dependency_libs'" \ % _unicode_decode(dep_libs_entry)) #What should 'dependency_libs' and 'inherited_linker_flags' look like? expected_dep_libs = b"" for x in (librpath, libladir, new_dep_libs): if x: expected_dep_libs += b" " + b" ".join(x) expected_inh_link_flags = b"" if new_inh_link_flags: expected_inh_link_flags += b" " + b" ".join(new_inh_link_flags) #Don't touch the file if we don't need to, otherwise put the expected values into #'contents' and write it into the la file. changed = False if dep_libs != expected_dep_libs: contents = contents.replace(b"dependency_libs='" + dep_libs + b"'", \ b"dependency_libs='" + expected_dep_libs + b"'") changed = True if inh_link_flags is not None and expected_inh_link_flags != inh_link_flags: contents = contents.replace(b"inherited_linker_flags='" + inh_link_flags + b"'", \ b"inherited_linker_flags='" + expected_inh_link_flags + b"'") changed = True if changed: return True, contents else: return False, None