1 |
Generate soname dependency metadata for binary and installed packages, |
2 |
in the form of PROVIDES and REQUIRES metadata. It is useful to generate |
3 |
PROVIDES and REQUIRES metadata now, so that it will be available |
4 |
when dependency resolver support is added in the future. Note that |
5 |
slot-operator dependencies will not be able to serve as a substitute |
6 |
for soname dependencies for the forseeable future, because system |
7 |
dependencies are frequently unspecified (according to Gentoo policy). |
8 |
|
9 |
The PROVIDES/REQUIRES system is very similar to the automatic Requires |
10 |
and Provides system which is supported by RPM. The PROVIDES/REQUIRES |
11 |
metadata is generated automatically from the ELF files that are |
12 |
installed by a package. The PROVIDES/REQUIRES syntax is described in |
13 |
the /var/db/pkg section of the portage(5) man page. REQUIRES_EXCLUDE |
14 |
and PROVIDES_EXCLUDE ebuild variables allow for filtering of the |
15 |
sonames that are saved in REQUIRES and PROVIDES (see the ebuild(5) man |
16 |
page for details). |
17 |
|
18 |
The /var/db/pkg NEEDED.ELF.2 format now includes an additional field |
19 |
which indicates the multilib category, as discussed in bug #534206. The |
20 |
multilib category is used to categorize the sonames that are listed in |
21 |
PROVIDES/REQUIRES metadata, since sonames need to be resolved |
22 |
separately for each multilib category. The complete list of supported |
23 |
multilib categories is documented in the comments of the |
24 |
portage.dep.soname.multilib_category module. |
25 |
|
26 |
X-Gentoo-Bug: 282639 |
27 |
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=282639 |
28 |
--- |
29 |
This is mostly the same as "[PATCH v2] Generate soname dependency |
30 |
metadata (282639)" sent to the list earlier. The only changes are: |
31 |
* Added e_type attribute to the ELFHeader class, so that it can be used |
32 |
to distinguish ET_REL, ET_EXEC, ET_DYN, and ET_CORE types. |
33 |
* Enabled unicode_literals in more files. |
34 |
|
35 |
|
36 |
bin/ebuild.sh | 2 +- |
37 |
bin/phase-functions.sh | 2 +- |
38 |
man/ebuild.5 | 12 +++ |
39 |
man/portage.5 | 25 +++++ |
40 |
pym/_emerge/Package.py | 3 +- |
41 |
pym/portage/dbapi/bintree.py | 5 +- |
42 |
pym/portage/dbapi/vartree.py | 1 + |
43 |
pym/portage/dep/soname/__init__.py | 2 + |
44 |
pym/portage/dep/soname/multilib_category.py | 114 +++++++++++++++++++++++ |
45 |
pym/portage/package/ebuild/doebuild.py | 86 +++++++++++++++-- |
46 |
pym/portage/util/_dyn_libs/LinkageMapELF.py | 61 +++++++++--- |
47 |
pym/portage/util/_dyn_libs/NeededEntry.py | 82 +++++++++++++++++ |
48 |
pym/portage/util/_dyn_libs/soname_deps.py | 138 ++++++++++++++++++++++++++++ |
49 |
pym/portage/util/elf/__init__.py | 2 + |
50 |
pym/portage/util/elf/constants.py | 45 +++++++++ |
51 |
pym/portage/util/elf/header.py | 65 +++++++++++++ |
52 |
pym/portage/util/endian/__init__.py | 2 + |
53 |
pym/portage/util/endian/decode.py | 48 ++++++++++ |
54 |
18 files changed, 668 insertions(+), 27 deletions(-) |
55 |
create mode 100644 pym/portage/dep/soname/__init__.py |
56 |
create mode 100644 pym/portage/dep/soname/multilib_category.py |
57 |
create mode 100644 pym/portage/util/_dyn_libs/NeededEntry.py |
58 |
create mode 100644 pym/portage/util/_dyn_libs/soname_deps.py |
59 |
create mode 100644 pym/portage/util/elf/__init__.py |
60 |
create mode 100644 pym/portage/util/elf/constants.py |
61 |
create mode 100644 pym/portage/util/elf/header.py |
62 |
create mode 100644 pym/portage/util/endian/__init__.py |
63 |
create mode 100644 pym/portage/util/endian/decode.py |
64 |
|
65 |
diff --git a/bin/ebuild.sh b/bin/ebuild.sh |
66 |
index e6f9cb9..b6b3723 100755 |
67 |
--- a/bin/ebuild.sh |
68 |
+++ b/bin/ebuild.sh |
69 |
@@ -578,7 +578,7 @@ if ! has "$EBUILD_PHASE" clean cleanrm ; then |
70 |
# interaction begins. |
71 |
unset EAPI DEPEND RDEPEND PDEPEND HDEPEND INHERITED IUSE REQUIRED_USE \ |
72 |
ECLASS E_IUSE E_REQUIRED_USE E_DEPEND E_RDEPEND E_PDEPEND \ |
73 |
- E_HDEPEND |
74 |
+ E_HDEPEND PROVIDES_EXCLUDE REQUIRES_EXCLUDE |
75 |
|
76 |
if [[ $PORTAGE_DEBUG != 1 || ${-/x/} != $- ]] ; then |
77 |
source "$EBUILD" || die "error sourcing ebuild" |
78 |
diff --git a/bin/phase-functions.sh b/bin/phase-functions.sh |
79 |
index aec86fd..def2080 100644 |
80 |
--- a/bin/phase-functions.sh |
81 |
+++ b/bin/phase-functions.sh |
82 |
@@ -580,7 +580,7 @@ __dyn_install() { |
83 |
for f in ASFLAGS CBUILD CC CFLAGS CHOST CTARGET CXX \ |
84 |
CXXFLAGS EXTRA_ECONF EXTRA_EINSTALL EXTRA_MAKE \ |
85 |
LDFLAGS LIBCFLAGS LIBCXXFLAGS QA_CONFIGURE_OPTIONS \ |
86 |
- QA_DESKTOP_FILE ; do |
87 |
+ QA_DESKTOP_FILE PROVIDES_EXCLUDE REQUIRES_EXCLUDE ; do |
88 |
x=$(echo -n ${!f}) |
89 |
[[ -n $x ]] && echo "$x" > $f |
90 |
done |
91 |
diff --git a/man/ebuild.5 b/man/ebuild.5 |
92 |
index 3f35180..95d0453 100644 |
93 |
--- a/man/ebuild.5 |
94 |
+++ b/man/ebuild.5 |
95 |
@@ -480,6 +480,12 @@ source source\-build which is scheduled for merge |
96 |
.TE |
97 |
.RE |
98 |
.TP |
99 |
+.B PROVIDES_EXCLUDE\fR = \fI[space delimited list of fnmatch patterns]\fR |
100 |
+Sonames and file paths matched by these fnmatch patterns will be |
101 |
+excluded during genertion of \fBPROVIDES\fR metadata (see |
102 |
+\fBportage\fR(5)). Patterns are delimited by whitespace, and it is |
103 |
+possible to create patterns containing quoted whitespace. |
104 |
+.TP |
105 |
.B PORTAGE_LOG_FILE |
106 |
Contains the path of the build log. If \fBPORT_LOGDIR\fR variable is unset then |
107 |
PORTAGE_LOG_FILE=\fI"${T}/build.log"\fR. |
108 |
@@ -506,6 +512,12 @@ to the package version(s) being replaced. Typically, this variable will |
109 |
not contain more than one version, but according to PMS it can contain |
110 |
more. |
111 |
.TP |
112 |
+.B REQUIRES_EXCLUDE\fR = \fI[space delimited list of fnmatch patterns]\fR |
113 |
+Sonames and file paths matched by these fnmatch patterns will be |
114 |
+excluded during generation of \fBREQUIRES\fR metadata (see |
115 |
+\fBportage\fR(5)). Patterns are delimited by whitespace, and it is |
116 |
+possible to create patterns containing quoted whitespace. |
117 |
+.TP |
118 |
.B ROOT\fR = \fI"/" |
119 |
Contains the path that portage should use as the root of the live filesystem. |
120 |
When packages wish to make changes to the live filesystem, they should do so in |
121 |
diff --git a/man/portage.5 b/man/portage.5 |
122 |
index 8bebd32..cec4e2f 100644 |
123 |
--- a/man/portage.5 |
124 |
+++ b/man/portage.5 |
125 |
@@ -1479,6 +1479,31 @@ can be changed quickly. Generally though there is one file per environment |
126 |
variable that "matters" (like CFLAGS) with the contents stored inside of it. |
127 |
Another common file is the CONTENTS file which lists the path and hashes of |
128 |
all objects that the package installed onto your system. |
129 |
+.TP |
130 |
+.BR PROVIDES |
131 |
+Contains information about the sonames that a package provides, which is |
132 |
+automatically generated from the files that it installs. The sonames |
133 |
+may have been filtered by the \fBPROVIDES_EXCLUDE\fR \fBebuild\fR(5) |
134 |
+variable. A multilib category, followed by a colon, always preceeds a |
135 |
+list of one or more sonames. |
136 |
+ |
137 |
+.I Example: |
138 |
+.nf |
139 |
+x86_32: libcom_err.so.2 libss.so.2 x86_64: libcom_err.so.2 libss.so.2 |
140 |
+.fi |
141 |
+.TP |
142 |
+.BR REQUIRES |
143 |
+Contains information about the sonames that a package requires, which is |
144 |
+automatically generated from the files that it installs. The sonames |
145 |
+may have been filtered by the \fBREQUIRES_EXCLUDE\fR \fBebuild\fR(5) |
146 |
+variable. Any sonames that a package provides are automatically excluded |
147 |
+from \fBREQUIRES\fR. A multilib category, followed by a colon, always |
148 |
+preceeds a list of one or more sonames. |
149 |
+ |
150 |
+.I Example: |
151 |
+.nf |
152 |
+x86_32: ld-linux.so.2 libc.so.6 x86_64: ld-linux-x86-64.so.2 libc.so.6 |
153 |
+.fi |
154 |
.RE |
155 |
.TP |
156 |
.BR /var/lib/portage/ |
157 |
diff --git a/pym/_emerge/Package.py b/pym/_emerge/Package.py |
158 |
index 8612e8b..518dbf6 100644 |
159 |
--- a/pym/_emerge/Package.py |
160 |
+++ b/pym/_emerge/Package.py |
161 |
@@ -43,7 +43,8 @@ class Package(Task): |
162 |
"HDEPEND", "INHERITED", "IUSE", "KEYWORDS", |
163 |
"LICENSE", "PDEPEND", "PROVIDE", "RDEPEND", |
164 |
"repository", "PROPERTIES", "RESTRICT", "SLOT", "USE", |
165 |
- "_mtime_", "DEFINED_PHASES", "REQUIRED_USE"] |
166 |
+ "_mtime_", "DEFINED_PHASES", "REQUIRED_USE", "PROVIDES", |
167 |
+ "REQUIRES"] |
168 |
|
169 |
_dep_keys = ('DEPEND', 'HDEPEND', 'PDEPEND', 'RDEPEND') |
170 |
_buildtime_keys = ('DEPEND', 'HDEPEND') |
171 |
diff --git a/pym/portage/dbapi/bintree.py b/pym/portage/dbapi/bintree.py |
172 |
index 1156b66..583e208 100644 |
173 |
--- a/pym/portage/dbapi/bintree.py |
174 |
+++ b/pym/portage/dbapi/bintree.py |
175 |
@@ -81,7 +81,8 @@ class bindbapi(fakedbapi): |
176 |
["BUILD_TIME", "CHOST", "DEPEND", "EAPI", |
177 |
"HDEPEND", "IUSE", "KEYWORDS", |
178 |
"LICENSE", "PDEPEND", "PROPERTIES", "PROVIDE", |
179 |
- "RDEPEND", "repository", "RESTRICT", "SLOT", "USE", "DEFINED_PHASES" |
180 |
+ "RDEPEND", "repository", "RESTRICT", "SLOT", "USE", |
181 |
+ "DEFINED_PHASES", "PROVIDES", "REQUIRES" |
182 |
]) |
183 |
self._aux_cache_slot_dict = slot_dict_class(self._aux_cache_keys) |
184 |
self._aux_cache = {} |
185 |
@@ -322,7 +323,7 @@ class binarytree(object): |
186 |
["BUILD_TIME", "CHOST", "DEPEND", "DESCRIPTION", "EAPI", |
187 |
"HDEPEND", "IUSE", "KEYWORDS", "LICENSE", "PDEPEND", "PROPERTIES", |
188 |
"PROVIDE", "RESTRICT", "RDEPEND", "repository", "SLOT", "USE", "DEFINED_PHASES", |
189 |
- "BASE_URI"] |
190 |
+ "BASE_URI", "PROVIDES", "REQUIRES"] |
191 |
self._pkgindex_aux_keys = list(self._pkgindex_aux_keys) |
192 |
self._pkgindex_use_evaluated_keys = \ |
193 |
("DEPEND", "HDEPEND", "LICENSE", "RDEPEND", |
194 |
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py |
195 |
index 2d4d32d..cf31c8e 100644 |
196 |
--- a/pym/portage/dbapi/vartree.py |
197 |
+++ b/pym/portage/dbapi/vartree.py |
198 |
@@ -176,6 +176,7 @@ class vardbapi(dbapi): |
199 |
"EAPI", "HDEPEND", "HOMEPAGE", "IUSE", "KEYWORDS", |
200 |
"LICENSE", "PDEPEND", "PROPERTIES", "PROVIDE", "RDEPEND", |
201 |
"repository", "RESTRICT" , "SLOT", "USE", "DEFINED_PHASES", |
202 |
+ "PROVIDES", "REQUIRES" |
203 |
]) |
204 |
self._aux_cache_obj = None |
205 |
self._aux_cache_filename = os.path.join(self._eroot, |
206 |
diff --git a/pym/portage/dep/soname/__init__.py b/pym/portage/dep/soname/__init__.py |
207 |
new file mode 100644 |
208 |
index 0000000..4725d33 |
209 |
--- /dev/null |
210 |
+++ b/pym/portage/dep/soname/__init__.py |
211 |
@@ -0,0 +1,2 @@ |
212 |
+# Copyright 2015 Gentoo Foundation |
213 |
+# Distributed under the terms of the GNU General Public License v2 |
214 |
diff --git a/pym/portage/dep/soname/multilib_category.py b/pym/portage/dep/soname/multilib_category.py |
215 |
new file mode 100644 |
216 |
index 0000000..30dbe1d |
217 |
--- /dev/null |
218 |
+++ b/pym/portage/dep/soname/multilib_category.py |
219 |
@@ -0,0 +1,114 @@ |
220 |
+# Copyright 2015 Gentoo Foundation |
221 |
+# Distributed under the terms of the GNU General Public License v2 |
222 |
+# |
223 |
+# Compute a multilib category, as discussed here: |
224 |
+# |
225 |
+# https://bugs.gentoo.org/show_bug.cgi?id=534206 |
226 |
+# |
227 |
+# Supported categories: |
228 |
+# |
229 |
+# alpha_{32,64} |
230 |
+# arm_{32,64} |
231 |
+# hppa_{32,64} |
232 |
+# ia_{32,64} |
233 |
+# m68k_{32,64} |
234 |
+# mips_{eabi32,eabi64,n32,n64,o32,o64} |
235 |
+# ppc_{32,64} |
236 |
+# s390_{32,64} |
237 |
+# sh_{32,64} |
238 |
+# sparc_{32,64} |
239 |
+# x86_{32,64,x32} |
240 |
+# |
241 |
+# NOTES: |
242 |
+# |
243 |
+# * The ABIs referenced by some of the above *_32 and *_64 categories |
244 |
+# may be imaginary, but they are listed anyway, since the goal is to |
245 |
+# establish a naming convention that is as consistent and uniform as |
246 |
+# possible. |
247 |
+# |
248 |
+# * The Elf header's e_ident[EI_OSABI] byte is completely ignored, |
249 |
+# since OS-independence is one of the goals. The assumption is that, |
250 |
+# for given installation, we are only interested in tracking multilib |
251 |
+# ABIs for a single OS. |
252 |
+ |
253 |
+from __future__ import unicode_literals |
254 |
+ |
255 |
+from portage.util.elf.constants import ( |
256 |
+ EF_MIPS_ABI, EF_MIPS_ABI2, ELFCLASS32, ELFCLASS64, |
257 |
+ EM_386, EM_68K, EM_AARCH64, EM_ALPHA, EM_ARM, EM_IA_64, EM_MIPS, |
258 |
+ EM_PARISC, EM_PPC, EM_PPC64, EM_S390, EM_SH, EM_SPARC, |
259 |
+ EM_SPARC32PLUS, EM_SPARCV9, EM_X86_64, E_MIPS_ABI_EABI32, |
260 |
+ E_MIPS_ABI_EABI64, E_MIPS_ABI_O32, E_MIPS_ABI_O64) |
261 |
+ |
262 |
+_machine_prefix_map = { |
263 |
+ EM_386: "x86", |
264 |
+ EM_68K: "m68k", |
265 |
+ EM_AARCH64: "arm", |
266 |
+ EM_ALPHA: "alpha", |
267 |
+ EM_ARM: "arm", |
268 |
+ EM_IA_64: "ia", |
269 |
+ EM_MIPS: "mips", |
270 |
+ EM_PARISC: "hppa", |
271 |
+ EM_PPC: "ppc", |
272 |
+ EM_PPC64: "ppc", |
273 |
+ EM_S390: "s390", |
274 |
+ EM_SH: "sh", |
275 |
+ EM_SPARC: "sparc", |
276 |
+ EM_SPARC32PLUS: "sparc", |
277 |
+ EM_SPARCV9: "sparc", |
278 |
+ EM_X86_64: "x86", |
279 |
+} |
280 |
+ |
281 |
+_mips_abi_map = { |
282 |
+ E_MIPS_ABI_EABI32: "eabi32", |
283 |
+ E_MIPS_ABI_EABI64: "eabi64", |
284 |
+ E_MIPS_ABI_O32: "o32", |
285 |
+ E_MIPS_ABI_O64: "o64", |
286 |
+} |
287 |
+ |
288 |
+def _compute_suffix_mips(elf_header): |
289 |
+ |
290 |
+ name = None |
291 |
+ mips_abi = elf_header.e_flags & EF_MIPS_ABI |
292 |
+ |
293 |
+ if mips_abi: |
294 |
+ name = _mips_abi_map.get(mips_abi) |
295 |
+ elif elf_header.e_flags & EF_MIPS_ABI2: |
296 |
+ name = "n32" |
297 |
+ elif elf_header.ei_class == ELFCLASS64: |
298 |
+ name = "n64" |
299 |
+ |
300 |
+ return name |
301 |
+ |
302 |
+def compute_multilib_category(elf_header): |
303 |
+ """ |
304 |
+ Compute a multilib category from an ELF header. |
305 |
+ |
306 |
+ @param elf_header: an ELFHeader instance |
307 |
+ @type elf_header: ELFHeader |
308 |
+ @rtype: str |
309 |
+ @return: A multilib category, or None if elf_header does not fit |
310 |
+ into a recognized category |
311 |
+ """ |
312 |
+ category = None |
313 |
+ if elf_header.e_machine is not None: |
314 |
+ |
315 |
+ prefix = _machine_prefix_map.get(elf_header.e_machine) |
316 |
+ suffix = None |
317 |
+ |
318 |
+ if prefix == "mips": |
319 |
+ suffix = _compute_suffix_mips(elf_header) |
320 |
+ elif elf_header.ei_class == ELFCLASS64: |
321 |
+ suffix = "64" |
322 |
+ elif elf_header.ei_class == ELFCLASS32: |
323 |
+ if elf_header.e_machine == EM_X86_64: |
324 |
+ suffix = "x32" |
325 |
+ else: |
326 |
+ suffix = "32" |
327 |
+ |
328 |
+ if prefix is None or suffix is None: |
329 |
+ category = None |
330 |
+ else: |
331 |
+ category = "%s_%s" % (prefix, suffix) |
332 |
+ |
333 |
+ return category |
334 |
diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py |
335 |
index 0ac0166..a5970d5 100644 |
336 |
--- a/pym/portage/package/ebuild/doebuild.py |
337 |
+++ b/pym/portage/package/ebuild/doebuild.py |
338 |
@@ -33,7 +33,11 @@ portage.proxy.lazyimport.lazyimport(globals(), |
339 |
'portage.package.ebuild._ipc.QueryCommand:QueryCommand', |
340 |
'portage.dep._slot_operator:evaluate_slot_operator_equal_deps', |
341 |
'portage.package.ebuild._spawn_nofetch:spawn_nofetch', |
342 |
+ 'portage.util.elf.header:ELFHeader', |
343 |
+ 'portage.dep.soname.multilib_category:compute_multilib_category', |
344 |
'portage.util._desktop_entry:validate_desktop_entry', |
345 |
+ 'portage.util._dyn_libs.NeededEntry:NeededEntry', |
346 |
+ 'portage.util._dyn_libs.soname_deps:SonameDepsProcessor', |
347 |
'portage.util._async.SchedulerInterface:SchedulerInterface', |
348 |
'portage.util._eventloop.EventLoop:EventLoop', |
349 |
'portage.util._eventloop.global_event_loop:global_event_loop', |
350 |
@@ -57,9 +61,9 @@ from portage.eapi import eapi_exports_KV, eapi_exports_merge_type, \ |
351 |
eapi_has_pkg_pretend, _get_eapi_attrs |
352 |
from portage.elog import elog_process, _preload_elog_modules |
353 |
from portage.elog.messages import eerror, eqawarn |
354 |
-from portage.exception import DigestException, FileNotFound, \ |
355 |
- IncorrectParameter, InvalidDependString, PermissionDenied, \ |
356 |
- UnsupportedAPIException |
357 |
+from portage.exception import (DigestException, FileNotFound, |
358 |
+ IncorrectParameter, InvalidData, InvalidDependString, |
359 |
+ PermissionDenied, UnsupportedAPIException) |
360 |
from portage.localization import _ |
361 |
from portage.output import colormap |
362 |
from portage.package.ebuild.prepare_build_dirs import prepare_build_dirs |
363 |
@@ -77,6 +81,11 @@ from _emerge.EbuildSpawnProcess import EbuildSpawnProcess |
364 |
from _emerge.Package import Package |
365 |
from _emerge.RootConfig import RootConfig |
366 |
|
367 |
+if sys.hexversion >= 0x3000000: |
368 |
+ _unicode = str |
369 |
+else: |
370 |
+ _unicode = unicode |
371 |
+ |
372 |
_unsandboxed_phases = frozenset([ |
373 |
"clean", "cleanrm", "config", |
374 |
"help", "info", "postinst", |
375 |
@@ -2261,21 +2270,64 @@ def _post_src_install_soname_symlinks(mysettings, out): |
376 |
is_libdir_cache[obj_parent] = rval |
377 |
return rval |
378 |
|
379 |
+ build_info_dir = os.path.join( |
380 |
+ mysettings['PORTAGE_BUILDDIR'], 'build-info') |
381 |
+ try: |
382 |
+ with io.open(_unicode_encode(os.path.join(build_info_dir, |
383 |
+ "PROVIDES_EXCLUDE"), encoding=_encodings['fs'], |
384 |
+ errors='strict'), mode='r', encoding=_encodings['repo.content'], |
385 |
+ errors='replace') as f: |
386 |
+ provides_exclude = f.read() |
387 |
+ except IOError as e: |
388 |
+ if e.errno not in (errno.ENOENT, errno.ESTALE): |
389 |
+ raise |
390 |
+ provides_exclude = "" |
391 |
+ |
392 |
+ try: |
393 |
+ with io.open(_unicode_encode(os.path.join(build_info_dir, |
394 |
+ "REQUIRES_EXCLUDE"), encoding=_encodings['fs'], |
395 |
+ errors='strict'), mode='r', encoding=_encodings['repo.content'], |
396 |
+ errors='replace') as f: |
397 |
+ requires_exclude = f.read() |
398 |
+ except IOError as e: |
399 |
+ if e.errno not in (errno.ENOENT, errno.ESTALE): |
400 |
+ raise |
401 |
+ requires_exclude = "" |
402 |
+ |
403 |
missing_symlinks = [] |
404 |
+ soname_deps = SonameDepsProcessor( |
405 |
+ provides_exclude, requires_exclude) |
406 |
+ |
407 |
+ # Parse NEEDED.ELF.2 like LinkageMapELF.rebuild() does, and |
408 |
+ # rewrite it to include multilib categories. |
409 |
+ needed_file = portage.util.atomic_ofstream(needed_filename, |
410 |
+ encoding=_encodings["repo.content"], errors="strict") |
411 |
|
412 |
- # Parse NEEDED.ELF.2 like LinkageMapELF.rebuild() does. |
413 |
for l in lines: |
414 |
l = l.rstrip("\n") |
415 |
if not l: |
416 |
continue |
417 |
- fields = l.split(";") |
418 |
- if len(fields) < 5: |
419 |
- portage.util.writemsg_level(_("\nWrong number of fields " \ |
420 |
- "in %s: %s\n\n") % (needed_filename, l), |
421 |
+ try: |
422 |
+ entry = NeededEntry.parse(needed_filename, l) |
423 |
+ except InvalidData as e: |
424 |
+ portage.util.writemsg_level("\n%s\n\n" % (e,), |
425 |
level=logging.ERROR, noiselevel=-1) |
426 |
continue |
427 |
|
428 |
- obj, soname = fields[1:3] |
429 |
+ filename = os.path.join(image_dir, |
430 |
+ entry.filename.lstrip(os.sep)) |
431 |
+ with open(_unicode_encode(filename, encoding=_encodings['fs'], |
432 |
+ errors='strict'), 'rb') as f: |
433 |
+ elf_header = ELFHeader.read(f) |
434 |
+ |
435 |
+ # Compute the multilib category and write it back to the file. |
436 |
+ entry.multilib_category = compute_multilib_category(elf_header) |
437 |
+ needed_file.write(_unicode(entry)) |
438 |
+ |
439 |
+ soname_deps.add(entry) |
440 |
+ obj = entry.filename |
441 |
+ soname = entry.soname |
442 |
+ |
443 |
if not soname: |
444 |
continue |
445 |
if not is_libdir(os.path.dirname(obj)): |
446 |
@@ -2295,6 +2347,22 @@ def _post_src_install_soname_symlinks(mysettings, out): |
447 |
|
448 |
missing_symlinks.append((obj, soname)) |
449 |
|
450 |
+ needed_file.close() |
451 |
+ |
452 |
+ if soname_deps.requires is not None: |
453 |
+ with io.open(_unicode_encode(os.path.join(build_info_dir, |
454 |
+ 'REQUIRES'), encoding=_encodings['fs'], errors='strict'), |
455 |
+ mode='w', encoding=_encodings['repo.content'], |
456 |
+ errors='strict') as f: |
457 |
+ f.write(soname_deps.requires) |
458 |
+ |
459 |
+ if soname_deps.provides is not None: |
460 |
+ with io.open(_unicode_encode(os.path.join(build_info_dir, |
461 |
+ 'PROVIDES'), encoding=_encodings['fs'], errors='strict'), |
462 |
+ mode='w', encoding=_encodings['repo.content'], |
463 |
+ errors='strict') as f: |
464 |
+ f.write(soname_deps.provides) |
465 |
+ |
466 |
if not missing_symlinks: |
467 |
return |
468 |
|
469 |
diff --git a/pym/portage/util/_dyn_libs/LinkageMapELF.py b/pym/portage/util/_dyn_libs/LinkageMapELF.py |
470 |
index 3920f94..c44666a 100644 |
471 |
--- a/pym/portage/util/_dyn_libs/LinkageMapELF.py |
472 |
+++ b/pym/portage/util/_dyn_libs/LinkageMapELF.py |
473 |
@@ -11,12 +11,37 @@ from portage import _os_merge |
474 |
from portage import _unicode_decode |
475 |
from portage import _unicode_encode |
476 |
from portage.cache.mappings import slot_dict_class |
477 |
-from portage.exception import CommandNotFound |
478 |
+from portage.exception import CommandNotFound, InvalidData |
479 |
from portage.localization import _ |
480 |
from portage.util import getlibpaths |
481 |
from portage.util import grabfile |
482 |
from portage.util import normalize_path |
483 |
+from portage.util import varexpand |
484 |
from portage.util import writemsg_level |
485 |
+from portage.util._dyn_libs.NeededEntry import NeededEntry |
486 |
+ |
487 |
+# Map ELF e_machine values from NEEDED.ELF.2 to approximate multilib |
488 |
+# categories. This approximation will produce incorrect results on x32 |
489 |
+# and mips systems, but the result is not worse than using the raw |
490 |
+# e_machine value which was used by earlier versions of portage. |
491 |
+_approx_multilib_categories = { |
492 |
+ "386": "x86_32", |
493 |
+ "68K": "m68k_32", |
494 |
+ "AARCH64": "arm_64", |
495 |
+ "ALPHA": "alpha_64", |
496 |
+ "ARM": "arm_32", |
497 |
+ "IA_64": "ia_64", |
498 |
+ "MIPS": "mips_o32", |
499 |
+ "PARISC": "hppa_64", |
500 |
+ "PPC": "ppc_32", |
501 |
+ "PPC64": "ppc_64", |
502 |
+ "S390": "s390_64", |
503 |
+ "SH": "sh_32", |
504 |
+ "SPARC": "sparc_32", |
505 |
+ "SPARC32PLUS": "sparc_32", |
506 |
+ "SPARCV9": "sparc_64", |
507 |
+ "X86_64": "x86_64", |
508 |
+} |
509 |
|
510 |
class LinkageMapELF(object): |
511 |
|
512 |
@@ -294,21 +319,31 @@ class LinkageMapELF(object): |
513 |
"in %s: %s\n\n") % (location, l), |
514 |
level=logging.ERROR, noiselevel=-1) |
515 |
continue |
516 |
- fields = l.split(";") |
517 |
- if len(fields) < 5: |
518 |
- writemsg_level(_("\nWrong number of fields " \ |
519 |
- "in %s: %s\n\n") % (location, l), |
520 |
+ try: |
521 |
+ entry = NeededEntry.parse(location, l) |
522 |
+ except InvalidData as e: |
523 |
+ writemsg_level("\n%s\n\n" % (e,), |
524 |
level=logging.ERROR, noiselevel=-1) |
525 |
continue |
526 |
- arch = fields[0] |
527 |
- obj = fields[1] |
528 |
- soname = fields[2] |
529 |
- path = frozenset(normalize_path(x) \ |
530 |
- for x in filter(None, fields[3].replace( |
531 |
- "${ORIGIN}", os.path.dirname(obj)).replace( |
532 |
- "$ORIGIN", os.path.dirname(obj)).split(":"))) |
533 |
+ |
534 |
+ # If NEEDED.ELF.2 contains the new multilib category field, |
535 |
+ # then use that for categorization. Otherwise, if a mapping |
536 |
+ # exists, map e_machine (entry.arch) to an approximate |
537 |
+ # multilib category. If all else fails, use e_machine, just |
538 |
+ # as older versions of portage did. |
539 |
+ arch = entry.multilib_category |
540 |
+ if arch is None: |
541 |
+ arch = _approx_multilib_categories.get( |
542 |
+ entry.arch, entry.arch) |
543 |
+ |
544 |
+ obj = entry.filename |
545 |
+ soname = entry.soname |
546 |
+ expand = {"ORIGIN": os.path.dirname(entry.filename)} |
547 |
+ path = frozenset(normalize_path(varexpand(x, expand)) |
548 |
+ for x in entry.runpaths) |
549 |
path = frozensets.setdefault(path, path) |
550 |
- needed = frozenset(x for x in fields[4].split(",") if x) |
551 |
+ needed = frozenset(entry.needed) |
552 |
+ |
553 |
needed = frozensets.setdefault(needed, needed) |
554 |
|
555 |
obj_key = self._obj_key(obj) |
556 |
diff --git a/pym/portage/util/_dyn_libs/NeededEntry.py b/pym/portage/util/_dyn_libs/NeededEntry.py |
557 |
new file mode 100644 |
558 |
index 0000000..c52cfce |
559 |
--- /dev/null |
560 |
+++ b/pym/portage/util/_dyn_libs/NeededEntry.py |
561 |
@@ -0,0 +1,82 @@ |
562 |
+# Copyright 2015 Gentoo Foundation |
563 |
+# Distributed under the terms of the GNU General Public License v2 |
564 |
+ |
565 |
+from __future__ import unicode_literals |
566 |
+ |
567 |
+import sys |
568 |
+ |
569 |
+from portage import _encodings, _unicode_encode |
570 |
+from portage.exception import InvalidData |
571 |
+from portage.localization import _ |
572 |
+ |
573 |
+class NeededEntry(object): |
574 |
+ """ |
575 |
+ Represents one entry (line) from a NEEDED.ELF.2 file. The entry |
576 |
+ must have 5 or more semicolon-delimited fields in order to be |
577 |
+ considered valid. The sixth field is optional, corresponding |
578 |
+ to the multilib category. The multilib_category attribute is |
579 |
+ None if the corresponding field is either empty or missing. |
580 |
+ """ |
581 |
+ |
582 |
+ __slots__ = ("arch", "filename", "multilib_category", "needed", |
583 |
+ "runpaths", "soname") |
584 |
+ |
585 |
+ _MIN_FIELDS = 5 |
586 |
+ _MULTILIB_CAT_INDEX = 5 |
587 |
+ |
588 |
+ @classmethod |
589 |
+ def parse(cls, filename, line): |
590 |
+ """ |
591 |
+ Parse a NEEDED.ELF.2 entry. Raises InvalidData if necessary. |
592 |
+ |
593 |
+ @param filename: file name for use in exception messages |
594 |
+ @type filename: str |
595 |
+ @param line: a single line of text from a NEEDED.ELF.2 file, |
596 |
+ without a trailing newline |
597 |
+ @type line: str |
598 |
+ @rtype: NeededEntry |
599 |
+ @return: A new NeededEntry instance containing data from line |
600 |
+ """ |
601 |
+ fields = line.split(";") |
602 |
+ if len(fields) < cls._MIN_FIELDS: |
603 |
+ raise InvalidData(_("Wrong number of fields " |
604 |
+ "in %s: %s\n\n") % (filename, line)) |
605 |
+ |
606 |
+ obj = cls() |
607 |
+ # Extra fields may exist (for future extensions). |
608 |
+ if (len(fields) > cls._MULTILIB_CAT_INDEX and |
609 |
+ fields[cls._MULTILIB_CAT_INDEX]): |
610 |
+ obj.multilib_category = fields[cls._MULTILIB_CAT_INDEX] |
611 |
+ else: |
612 |
+ obj.multilib_category = None |
613 |
+ |
614 |
+ del fields[cls._MIN_FIELDS:] |
615 |
+ obj.arch, obj.filename, obj.soname, rpaths, needed = fields |
616 |
+ obj.runpaths = tuple(filter(None, rpaths.split(":"))) |
617 |
+ obj.needed = tuple(filter(None, needed.split(","))) |
618 |
+ |
619 |
+ return obj |
620 |
+ |
621 |
+ def __str__(self): |
622 |
+ """ |
623 |
+ Format this entry for writing to a NEEDED.ELF.2 file. |
624 |
+ """ |
625 |
+ return ";".join([ |
626 |
+ self.arch, |
627 |
+ self.filename, |
628 |
+ self.soname, |
629 |
+ ":".join(self.runpaths), |
630 |
+ ",".join(self.needed), |
631 |
+ (self.multilib_category if self.multilib_category |
632 |
+ is not None else "") |
633 |
+ ]) + "\n" |
634 |
+ |
635 |
+ if sys.hexversion < 0x3000000: |
636 |
+ |
637 |
+ __unicode__ = __str__ |
638 |
+ |
639 |
+ def __str__(self): |
640 |
+ return _unicode_encode(self.__unicode__(), |
641 |
+ encoding=_encodings['content']) |
642 |
+ |
643 |
+ __str__.__doc__ = __unicode__.__doc__ |
644 |
diff --git a/pym/portage/util/_dyn_libs/soname_deps.py b/pym/portage/util/_dyn_libs/soname_deps.py |
645 |
new file mode 100644 |
646 |
index 0000000..a7d5954 |
647 |
--- /dev/null |
648 |
+++ b/pym/portage/util/_dyn_libs/soname_deps.py |
649 |
@@ -0,0 +1,138 @@ |
650 |
+# Copyright 2015 Gentoo Foundation |
651 |
+# Distributed under the terms of the GNU General Public License v2 |
652 |
+ |
653 |
+from __future__ import unicode_literals |
654 |
+ |
655 |
+import fnmatch |
656 |
+from itertools import chain |
657 |
+import os |
658 |
+import re |
659 |
+ |
660 |
+from portage.util import shlex_split |
661 |
+ |
662 |
+class SonameDepsProcessor(object): |
663 |
+ """ |
664 |
+ Processes NEEDED.ELF.2 entries for one package, in order to generate |
665 |
+ REQUIRES and PROVIDES metadata. |
666 |
+ |
667 |
+ Any sonames provided by the package will automatically be filtered |
668 |
+ from the generated REQUIRES values. |
669 |
+ """ |
670 |
+ |
671 |
+ def __init__(self, provides_exclude, requires_exclude): |
672 |
+ """ |
673 |
+ @param provides_exclude: PROVIDES_EXCLUDE value |
674 |
+ @type provides_exclude: str |
675 |
+ @param requires_exclude: REQUIRES_EXCLUDE value |
676 |
+ @type requires_exclude: str |
677 |
+ """ |
678 |
+ self._provides_exclude = self._exclude_pattern(provides_exclude) |
679 |
+ self._requires_exclude = self._exclude_pattern(requires_exclude) |
680 |
+ self._requires_map = {} |
681 |
+ self._provides_map = {} |
682 |
+ self._provides_unfiltered = {} |
683 |
+ self._provides = None |
684 |
+ self._requires = None |
685 |
+ self._intersected = False |
686 |
+ |
687 |
+ @staticmethod |
688 |
+ def _exclude_pattern(s): |
689 |
+ # shlex_split enables quoted whitespace inside patterns |
690 |
+ if s: |
691 |
+ pat = re.compile("|".join( |
692 |
+ fnmatch.translate(x.lstrip(os.sep)) |
693 |
+ for x in shlex_split(s))) |
694 |
+ else: |
695 |
+ pat = None |
696 |
+ return pat |
697 |
+ |
698 |
+ def add(self, entry): |
699 |
+ """ |
700 |
+ Add one NEEDED.ELF.2 entry, for inclusion in the generated |
701 |
+ REQUIRES and PROVIDES values. |
702 |
+ |
703 |
+ @param entry: NEEDED.ELF.2 entry |
704 |
+ @type entry: NeededEntry |
705 |
+ """ |
706 |
+ |
707 |
+ multilib_cat = entry.multilib_category |
708 |
+ if multilib_cat is None: |
709 |
+ # This usage is invalid. The caller must ensure that |
710 |
+ # the multilib category data is supplied here. |
711 |
+ raise AssertionError( |
712 |
+ "Missing multilib category data: %s" % entry.filename) |
713 |
+ |
714 |
+ if entry.needed and ( |
715 |
+ self._requires_exclude is None or |
716 |
+ self._requires_exclude.match( |
717 |
+ entry.filename.lstrip(os.sep)) is None): |
718 |
+ for x in entry.needed: |
719 |
+ if (self._requires_exclude is None or |
720 |
+ self._requires_exclude.match(x) is None): |
721 |
+ self._requires_map.setdefault( |
722 |
+ multilib_cat, set()).add(x) |
723 |
+ |
724 |
+ if entry.soname: |
725 |
+ self._provides_unfiltered.setdefault( |
726 |
+ multilib_cat, set()).add(entry.soname) |
727 |
+ |
728 |
+ if entry.soname and ( |
729 |
+ self._provides_exclude is None or |
730 |
+ (self._provides_exclude.match( |
731 |
+ entry.filename.lstrip(os.sep)) is None and |
732 |
+ self._provides_exclude.match(entry.soname) is None)): |
733 |
+ self._provides_map.setdefault( |
734 |
+ multilib_cat, set()).add(entry.soname) |
735 |
+ |
736 |
+ def _intersect(self): |
737 |
+ requires_map = self._requires_map |
738 |
+ provides_map = self._provides_map |
739 |
+ provides_unfiltered = self._provides_unfiltered |
740 |
+ |
741 |
+ for multilib_cat in set(chain(requires_map, provides_map)): |
742 |
+ requires_map.setdefault(multilib_cat, set()) |
743 |
+ provides_map.setdefault(multilib_cat, set()) |
744 |
+ provides_unfiltered.setdefault(multilib_cat, set()) |
745 |
+ for soname in list(requires_map[multilib_cat]): |
746 |
+ if soname in provides_unfiltered[multilib_cat]: |
747 |
+ requires_map[multilib_cat].remove(soname) |
748 |
+ |
749 |
+ provides_data = [] |
750 |
+ for multilib_cat in sorted(provides_map): |
751 |
+ if provides_map[multilib_cat]: |
752 |
+ provides_data.append(multilib_cat + ":") |
753 |
+ provides_data.extend(sorted(provides_map[multilib_cat])) |
754 |
+ |
755 |
+ if provides_data: |
756 |
+ self._provides = " ".join(provides_data) + "\n" |
757 |
+ |
758 |
+ requires_data = [] |
759 |
+ for multilib_cat in sorted(requires_map): |
760 |
+ if requires_map[multilib_cat]: |
761 |
+ requires_data.append(multilib_cat + ":") |
762 |
+ requires_data.extend(sorted(requires_map[multilib_cat])) |
763 |
+ |
764 |
+ if requires_data: |
765 |
+ self._requires = " ".join(requires_data) + "\n" |
766 |
+ |
767 |
+ self._intersected = True |
768 |
+ |
769 |
+ @property |
770 |
+ def provides(self): |
771 |
+ """ |
772 |
+ @rtype: str |
773 |
+ @return: PROVIDES value generated from NEEDED.ELF.2 entries |
774 |
+ """ |
775 |
+ if not self._intersected: |
776 |
+ self._intersect() |
777 |
+ return self._provides |
778 |
+ |
779 |
+ @property |
780 |
+ def requires(self): |
781 |
+ """ |
782 |
+ @rtype: str |
783 |
+ @return: REQUIRES value generated from NEEDED.ELF.2 entries |
784 |
+ """ |
785 |
+ if not self._intersected: |
786 |
+ self._intersect() |
787 |
+ return self._requires |
788 |
diff --git a/pym/portage/util/elf/__init__.py b/pym/portage/util/elf/__init__.py |
789 |
new file mode 100644 |
790 |
index 0000000..4725d33 |
791 |
--- /dev/null |
792 |
+++ b/pym/portage/util/elf/__init__.py |
793 |
@@ -0,0 +1,2 @@ |
794 |
+# Copyright 2015 Gentoo Foundation |
795 |
+# Distributed under the terms of the GNU General Public License v2 |
796 |
diff --git a/pym/portage/util/elf/constants.py b/pym/portage/util/elf/constants.py |
797 |
new file mode 100644 |
798 |
index 0000000..f687fdb |
799 |
--- /dev/null |
800 |
+++ b/pym/portage/util/elf/constants.py |
801 |
@@ -0,0 +1,45 @@ |
802 |
+# Copyright 2015 Gentoo Foundation |
803 |
+# Distributed under the terms of the GNU General Public License v2 |
804 |
+# |
805 |
+# These constants are available from elfutils: |
806 |
+# https://git.fedorahosted.org/cgit/elfutils.git/tree/libelf/elf.h |
807 |
+ |
808 |
+EI_CLASS = 4 |
809 |
+ELFCLASS32 = 1 |
810 |
+ELFCLASS64 = 2 |
811 |
+ |
812 |
+EI_DATA = 5 |
813 |
+ELFDATA2LSB = 1 |
814 |
+ELFDATA2MSB = 2 |
815 |
+ |
816 |
+E_TYPE = 16 |
817 |
+ET_REL = 1 |
818 |
+ET_EXEC = 2 |
819 |
+ET_DYN = 3 |
820 |
+ET_CORE = 4 |
821 |
+ |
822 |
+E_MACHINE = 18 |
823 |
+EM_SPARC = 2 |
824 |
+EM_386 = 3 |
825 |
+EM_68K = 4 |
826 |
+EM_MIPS = 8 |
827 |
+EM_PARISC = 15 |
828 |
+EM_SPARC32PLUS = 18 |
829 |
+EM_PPC = 20 |
830 |
+EM_PPC64 = 21 |
831 |
+EM_S390 = 22 |
832 |
+EM_ARM = 40 |
833 |
+EM_SH = 42 |
834 |
+EM_SPARCV9 = 43 |
835 |
+EM_IA_64 = 50 |
836 |
+EM_X86_64 = 62 |
837 |
+EM_AARCH64 = 183 |
838 |
+EM_ALPHA = 0x9026 |
839 |
+ |
840 |
+E_ENTRY = 24 |
841 |
+EF_MIPS_ABI = 0x0000f000 |
842 |
+EF_MIPS_ABI2 = 0x00000020 |
843 |
+E_MIPS_ABI_O32 = 0x00001000 |
844 |
+E_MIPS_ABI_O64 = 0x00002000 |
845 |
+E_MIPS_ABI_EABI32 = 0x00003000 |
846 |
+E_MIPS_ABI_EABI64 = 0x00004000 |
847 |
diff --git a/pym/portage/util/elf/header.py b/pym/portage/util/elf/header.py |
848 |
new file mode 100644 |
849 |
index 0000000..3d23074 |
850 |
--- /dev/null |
851 |
+++ b/pym/portage/util/elf/header.py |
852 |
@@ -0,0 +1,65 @@ |
853 |
+# Copyright 2015 Gentoo Foundation |
854 |
+# Distributed under the terms of the GNU General Public License v2 |
855 |
+ |
856 |
+from portage.util.endian.decode import (decode_uint16_le, |
857 |
+ decode_uint32_le, decode_uint16_be, decode_uint32_be) |
858 |
+from portage.util.elf.constants import (E_ENTRY, E_MACHINE, E_TYPE, |
859 |
+ EI_CLASS, ELFCLASS32, ELFCLASS64, ELFDATA2LSB, ELFDATA2MSB) |
860 |
+ |
861 |
+class ELFHeader(object): |
862 |
+ |
863 |
+ __slots__ = ('e_flags', 'e_machine', 'e_type', 'ei_class', |
864 |
+ 'ei_data') |
865 |
+ |
866 |
+ @classmethod |
867 |
+ def read(cls, f): |
868 |
+ """ |
869 |
+ @param f: an open ELF file |
870 |
+ @type f: file |
871 |
+ @rtype: ELFHeader |
872 |
+ @return: A new ELFHeader instance containing data from f |
873 |
+ """ |
874 |
+ f.seek(EI_CLASS) |
875 |
+ ei_class = ord(f.read(1)) |
876 |
+ ei_data = ord(f.read(1)) |
877 |
+ |
878 |
+ if ei_class == ELFCLASS32: |
879 |
+ width = 32 |
880 |
+ elif ei_class == ELFCLASS64: |
881 |
+ width = 64 |
882 |
+ else: |
883 |
+ width = None |
884 |
+ |
885 |
+ if ei_data == ELFDATA2LSB: |
886 |
+ uint16 = decode_uint16_le |
887 |
+ uint32 = decode_uint32_le |
888 |
+ elif ei_data == ELFDATA2MSB: |
889 |
+ uint16 = decode_uint16_be |
890 |
+ uint32 = decode_uint32_be |
891 |
+ else: |
892 |
+ uint16 = None |
893 |
+ uint32 = None |
894 |
+ |
895 |
+ if width is None or uint16 is None: |
896 |
+ e_flags = None |
897 |
+ e_machine = None |
898 |
+ e_type = None |
899 |
+ else: |
900 |
+ f.seek(E_TYPE) |
901 |
+ e_type = uint16(f.read(2)) |
902 |
+ f.seek(E_MACHINE) |
903 |
+ e_machine = uint16(f.read(2)) |
904 |
+ |
905 |
+ # E_ENTRY + 3 * sizeof(uintN) |
906 |
+ e_flags_offset = E_ENTRY + 3 * width // 8 |
907 |
+ f.seek(e_flags_offset) |
908 |
+ e_flags = uint32(f.read(4)) |
909 |
+ |
910 |
+ obj = cls() |
911 |
+ obj.e_flags = e_flags |
912 |
+ obj.e_machine = e_machine |
913 |
+ obj.e_type = e_type |
914 |
+ obj.ei_class = ei_class |
915 |
+ obj.ei_data = ei_data |
916 |
+ |
917 |
+ return obj |
918 |
diff --git a/pym/portage/util/endian/__init__.py b/pym/portage/util/endian/__init__.py |
919 |
new file mode 100644 |
920 |
index 0000000..4725d33 |
921 |
--- /dev/null |
922 |
+++ b/pym/portage/util/endian/__init__.py |
923 |
@@ -0,0 +1,2 @@ |
924 |
+# Copyright 2015 Gentoo Foundation |
925 |
+# Distributed under the terms of the GNU General Public License v2 |
926 |
diff --git a/pym/portage/util/endian/decode.py b/pym/portage/util/endian/decode.py |
927 |
new file mode 100644 |
928 |
index 0000000..9833b53 |
929 |
--- /dev/null |
930 |
+++ b/pym/portage/util/endian/decode.py |
931 |
@@ -0,0 +1,48 @@ |
932 |
+# Copyright 2015 Gentoo Foundation |
933 |
+# Distributed under the terms of the GNU General Public License v2 |
934 |
+ |
935 |
+import struct |
936 |
+ |
937 |
+def decode_uint16_be(data): |
938 |
+ """ |
939 |
+ Decode an unsigned 16-bit integer with big-endian encoding. |
940 |
+ |
941 |
+ @param data: string of bytes of length 2 |
942 |
+ @type data: bytes |
943 |
+ @rtype: int |
944 |
+ @return: unsigned integer value of the decoded data |
945 |
+ """ |
946 |
+ return struct.unpack_from(">H", data)[0] |
947 |
+ |
948 |
+def decode_uint16_le(data): |
949 |
+ """ |
950 |
+ Decode an unsigned 16-bit integer with little-endian encoding. |
951 |
+ |
952 |
+ @param data: string of bytes of length 2 |
953 |
+ @type data: bytes |
954 |
+ @rtype: int |
955 |
+ @return: unsigned integer value of the decoded data |
956 |
+ """ |
957 |
+ return struct.unpack_from("<H", data)[0] |
958 |
+ |
959 |
+def decode_uint32_be(data): |
960 |
+ """ |
961 |
+ Decode an unsigned 32-bit integer with big-endian encoding. |
962 |
+ |
963 |
+ @param data: string of bytes of length 4 |
964 |
+ @type data: bytes |
965 |
+ @rtype: int |
966 |
+ @return: unsigned integer value of the decoded data |
967 |
+ """ |
968 |
+ return struct.unpack_from(">I", data)[0] |
969 |
+ |
970 |
+def decode_uint32_le(data): |
971 |
+ """ |
972 |
+ Decode an unsigned 32-bit integer with little-endian encoding. |
973 |
+ |
974 |
+ @param data: string of bytes of length 4 |
975 |
+ @type data: bytes |
976 |
+ @rtype: int |
977 |
+ @return: unsigned integer value of the decoded data |
978 |
+ """ |
979 |
+ return struct.unpack_from("<I", data)[0] |
980 |
-- |
981 |
2.0.5 |