blob: 11001036386007833a33e96606461cae910a8f4e [file] [log] [blame]
# Copyright 2010-2017 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(br"usr/lib[^/]*/pkgconfig/\.\./\.\.")
pkgconfig_sub2 = re.compile(br"(?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(br"\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