| # 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<value>[^']*)'$") |
| inh_link_flags_re = re.compile(b"inherited_linker_flags='(?P<value>[^']*)'$") |
| |
| #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"(?P<usrlib>usr/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 |
| @return: (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<usrlib>", 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 |