summaryrefslogtreecommitdiff
blob: 5a36647d1d1c5174abebe07edfb7fd50b90efd20 (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 1999-2022 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

# @ECLASS: ltprune.eclass
# @MAINTAINER:
# Michał Górny <mgorny@gentoo.org>
# @SUPPORTED_EAPIS: 0 1 2 3 4 5 6
# @BLURB: Smart .la file pruning
# @DEPRECATED: none
# @DESCRIPTION:
# A function to locate and remove unnecessary .la files.
#
# Discouraged. Whenever possible, please use much simpler:
# @CODE
# find "${ED}" -type f -name '*.la' -delete || die
# @CODE

if [[ -z ${_LTPRUNE_ECLASS} ]]; then

case ${EAPI:-0} in
	0|1|2|3|4|5|6)
		;;
	*)
		die "${ECLASS}: banned in EAPI=${EAPI}; use 'find' instead";;
esac

inherit toolchain-funcs

# @FUNCTION: prune_libtool_files
# @USAGE: [--all|--modules]
# @DESCRIPTION:
# Locate unnecessary libtool files (.la) and libtool static archives
# (.a) and remove them from installation image.
#
# By default, .la files are removed whenever the static linkage can
# either be performed using pkg-config or doesn't introduce additional
# flags.
#
# If '--modules' argument is passed, .la files for modules (plugins) are
# removed as well. This is usually useful when the package installs
# plugins and the plugin loader does not use .la files.
#
# If '--all' argument is passed, all .la files are removed without
# performing any heuristic on them. You shouldn't ever use that,
# and instead report a bug in the algorithm instead.
#
# The .a files are only removed whenever corresponding .la files state
# that they should not be linked to, i.e. whenever these files
# correspond to plugins.
#
# Note: if your package installs both static libraries and .pc files
# which use variable substitution for -l flags, you need to add
# pkg-config to your DEPEND.
prune_libtool_files() {
	debug-print-function ${FUNCNAME} "$@"

	local removing_all removing_modules opt
	for opt; do
		case "${opt}" in
			--all)
				removing_all=1
				removing_modules=1
				;;
			--modules)
				removing_modules=1
				;;
			*)
				die "Invalid argument to ${FUNCNAME}(): ${opt}"
		esac
	done

	local f
	local queue=()
	while IFS= read -r -d '' f; do # for all .la files
		local archivefile=${f/%.la/.a}

		# The following check is done by libtool itself.
		# It helps us avoid removing random files which match '*.la',
		# see bug #468380.
		if ! sed -n -e '/^# Generated by .*libtool/q0;4q1' "${f}"; then
			continue
		fi

		[[ ${f} != ${archivefile} ]] || die 'regex sanity check failed'
		local reason= pkgconfig_scanned=
		local snotlink=$(sed -n -e 's:^shouldnotlink=::p' "${f}")

		if [[ ${snotlink} == yes ]]; then

			# Remove static libs we're not supposed to link against.
			if [[ -f ${archivefile} ]]; then
				einfo "Removing unnecessary ${archivefile#${D%/}} (static plugin)"
				queue+=( "${archivefile}" )
			fi

			# The .la file may be used by a module loader, so avoid removing it
			# unless explicitly requested.
			if [[ ${removing_modules} ]]; then
				reason='module'
			fi

		else

			# Remove .la files when:
			# - user explicitly wants us to remove all .la files,
			# - respective static archive doesn't exist,
			# - they are covered by a .pc file already,
			# - they don't provide any new information (no libs & no flags).

			if [[ ${removing_all} ]]; then
				reason='requested'
			elif [[ ! -f ${archivefile} ]]; then
				reason='no static archive'
			elif [[ ! $(sed -nre \
					"s/^(dependency_libs|inherited_linker_flags)='(.*)'$/\2/p" \
					"${f}") ]]; then
				reason='no libs & flags'
			else
				if [[ ! ${pkgconfig_scanned} ]]; then
					# Create a list of all .pc-covered libs.
					local pc_libs=()
					if [[ ! ${removing_all} ]]; then
						local pc
						local tf=${T}/prune-lt-files.pc
						local pkgconf=$(tc-getPKG_CONFIG)

						while IFS= read -r -d '' pc; do # for all .pc files
							local arg libs

							# Use pkg-config if available (and works),
							# fallback to sed.
							if ${pkgconf} --exists "${pc}" &>/dev/null; then
								sed -e '/^Requires:/d' "${pc}" > "${tf}"
								libs=$(${pkgconf} --libs "${tf}")
							else
								libs=$(sed -ne 's/^Libs://p' "${pc}")
							fi

							for arg in ${libs}; do
								if [[ ${arg} == -l* ]]; then
									if [[ ${arg} == '*$*' ]]; then
										eerror "${FUNCNAME}: variable substitution likely failed in ${pc}"
										eerror "(arg: ${arg})"
										eerror "Most likely, you need to add virtual/pkgconfig to DEPEND."
										die "${FUNCNAME}: unsubstituted variable found in .pc"
									fi

									pc_libs+=( lib${arg#-l}.la )
								fi
							done
						done < <(find "${D}" -type f -name '*.pc' -print0)

						rm -f "${tf}"
					fi

					pkgconfig_scanned=1
				fi # pkgconfig_scanned

				has "${f##*/}" "${pc_libs[@]}" && reason='covered by .pc'
			fi # removal due to .pc

		fi # shouldnotlink==no

		if [[ ${reason} ]]; then
			einfo "Removing unnecessary ${f#${D%/}} (${reason})"
			queue+=( "${f}" )
		fi
	done < <(find "${D}" -xtype f -name '*.la' -print0)

	if [[ ${queue[@]} ]]; then
		rm -f "${queue[@]}"
	fi
}

_LTPRUNE_ECLASS=1
fi #_LTPRUNE_ECLASS