aboutsummaryrefslogtreecommitdiff
blob: 90cc435417b166e5ffdf7bac8cfdccfa8072bac3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# Copyright 2010 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

import re

from portage import os
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("dependency_libs='(?P<value>[^']*)'$")
inh_link_flags_re = re.compile("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("X11R6/lib|local/lib")
#get rid of the '..'
pkgconfig_sub1 = re.compile("usr/lib[^/]*/pkgconfig/\.\./\.\.")
pkgconfig_sub2 = re.compile("(?P<usrlib>usr/lib[^/]*)/pkgconfig/\.\.")

#detect flags that should go into inherited_linker_flags instead of dependency_libs
flag_re = re.compile("-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("\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.
	Returns a bool, string tuple. 
	The bool indicates if the file needs fixing.
	The string contains the fixed contents if the file needs fixing.
	Raises 'InvalidData' if the .la file is invalid.
	"""
	#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("-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(".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("lib"):
				if dep_libs_entry not in new_dep_libs:
					new_dep_libs.append(dep_libs_entry)
			else:
				#/usr/lib64/libfoo.la -> -lfoo
				lib = "-l" + file[3:-3]
				if lib not in new_dep_libs:
					new_dep_libs.append(lib)
				#/usr/lib64/libfoo.la -> -L/usr/lib64
				ladir = "-L" + dir
				if ladir not in libladir:
					libladir.append(ladir)

		elif dep_libs_entry.startswith("-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("lib", ladir)
			ladir = pkgconfig_sub1.sub("usr", ladir)
			ladir = pkgconfig_sub2.sub("\g<usrlib>", ladir)
			
			if ladir not in libladir:
				libladir.append(ladir)

		elif dep_libs_entry.startswith("-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'" \
				% dep_libs_entry)

	#What should 'dependency_libs' and 'inherited_linker_flags' look like?
	expected_dep_libs = ""
	for x in (librpath, libladir, new_dep_libs):
		if x:
			expected_dep_libs += " " + " ".join(x)

	expected_inh_link_flags = ""
	if new_inh_link_flags:
		expected_inh_link_flags += " " + " ".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.
	if dep_libs == expected_dep_libs and \
		(inh_link_flags is None or expected_inh_link_flags == inh_link_flags):
		return False, None

	contents = re.sub("dependency_libs='" + dep_libs + "'", \
		"dependency_libs='" + expected_dep_libs + "'" , contents)

	if inh_link_flags is not None:
		contents = re.sub("inherited_linker_flags='" + inh_link_flags + "'", \
			"inherited_linker_flags='" + expected_inh_link_flags + "'" , contents)

	return True, contents