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 |
PATCH v2 includes the following changes: |
30 |
* no relative imports |
31 |
* NeededEntry.__str__ now uses ";".join |
32 |
* portage.util.elf.constants refers to elf.h from elfutils |
33 |
* EM_ALPHA uses correct 0x9026 value (tested with gentoo stage3) |
34 |
* portage.util.endian.decode uses struct.unpack_from |
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 | 112 +++++++++++++++++++++++ |
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 | 136 ++++++++++++++++++++++++++++ |
49 |
pym/portage/util/elf/__init__.py | 2 + |
50 |
pym/portage/util/elf/constants.py | 39 ++++++++ |
51 |
pym/portage/util/elf/header.py | 62 +++++++++++++ |
52 |
pym/portage/util/endian/__init__.py | 2 + |
53 |
pym/portage/util/endian/decode.py | 48 ++++++++++ |
54 |
18 files changed, 655 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 b587264..c2cbe4b 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 |
@@ -501,6 +507,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 189561c..bf159fd 100644 |
123 |
--- a/man/portage.5 |
124 |
+++ b/man/portage.5 |
125 |
@@ -1443,6 +1443,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..cce1ad5 |
217 |
--- /dev/null |
218 |
+++ b/pym/portage/dep/soname/multilib_category.py |
219 |
@@ -0,0 +1,112 @@ |
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 portage.util.elf.constants import ( |
254 |
+ EF_MIPS_ABI, EF_MIPS_ABI2, ELFCLASS32, ELFCLASS64, |
255 |
+ EM_386, EM_68K, EM_AARCH64, EM_ALPHA, EM_ARM, EM_IA_64, EM_MIPS, |
256 |
+ EM_PARISC, EM_PPC, EM_PPC64, EM_S390, EM_SH, EM_SPARC, |
257 |
+ EM_SPARC32PLUS, EM_SPARCV9, EM_X86_64, E_MIPS_ABI_EABI32, |
258 |
+ E_MIPS_ABI_EABI64, E_MIPS_ABI_O32, E_MIPS_ABI_O64) |
259 |
+ |
260 |
+_machine_prefix_map = { |
261 |
+ EM_386: "x86", |
262 |
+ EM_68K: "m68k", |
263 |
+ EM_AARCH64: "arm", |
264 |
+ EM_ALPHA: "alpha", |
265 |
+ EM_ARM: "arm", |
266 |
+ EM_IA_64: "ia", |
267 |
+ EM_MIPS: "mips", |
268 |
+ EM_PARISC: "hppa", |
269 |
+ EM_PPC: "ppc", |
270 |
+ EM_PPC64: "ppc", |
271 |
+ EM_S390: "s390", |
272 |
+ EM_SH: "sh", |
273 |
+ EM_SPARC: "sparc", |
274 |
+ EM_SPARC32PLUS: "sparc", |
275 |
+ EM_SPARCV9: "sparc", |
276 |
+ EM_X86_64: "x86", |
277 |
+} |
278 |
+ |
279 |
+_mips_abi_map = { |
280 |
+ E_MIPS_ABI_EABI32: "eabi32", |
281 |
+ E_MIPS_ABI_EABI64: "eabi64", |
282 |
+ E_MIPS_ABI_O32: "o32", |
283 |
+ E_MIPS_ABI_O64: "o64", |
284 |
+} |
285 |
+ |
286 |
+def _compute_suffix_mips(elf_header): |
287 |
+ |
288 |
+ name = None |
289 |
+ mips_abi = elf_header.e_flags & EF_MIPS_ABI |
290 |
+ |
291 |
+ if mips_abi: |
292 |
+ name = _mips_abi_map.get(mips_abi) |
293 |
+ elif elf_header.e_flags & EF_MIPS_ABI2: |
294 |
+ name = "n32" |
295 |
+ elif elf_header.ei_class == ELFCLASS64: |
296 |
+ name = "n64" |
297 |
+ |
298 |
+ return name |
299 |
+ |
300 |
+def compute_multilib_category(elf_header): |
301 |
+ """ |
302 |
+ Compute a multilib category from an ELF header. |
303 |
+ |
304 |
+ @param elf_header: an ELFHeader instance |
305 |
+ @type elf_header: ELFHeader |
306 |
+ @rtype: str |
307 |
+ @return: A multilib category, or None if elf_header does not fit |
308 |
+ into a recognized category |
309 |
+ """ |
310 |
+ category = None |
311 |
+ if elf_header.e_machine is not None: |
312 |
+ |
313 |
+ prefix = _machine_prefix_map.get(elf_header.e_machine) |
314 |
+ suffix = None |
315 |
+ |
316 |
+ if prefix == "mips": |
317 |
+ suffix = _compute_suffix_mips(elf_header) |
318 |
+ elif elf_header.ei_class == ELFCLASS64: |
319 |
+ suffix = "64" |
320 |
+ elif elf_header.ei_class == ELFCLASS32: |
321 |
+ if elf_header.e_machine == EM_X86_64: |
322 |
+ suffix = "x32" |
323 |
+ else: |
324 |
+ suffix = "32" |
325 |
+ |
326 |
+ if prefix is None or suffix is None: |
327 |
+ category = None |
328 |
+ else: |
329 |
+ category = "%s_%s" % (prefix, suffix) |
330 |
+ |
331 |
+ return category |
332 |
diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py |
333 |
index 5dadb7f..e6fbada 100644 |
334 |
--- a/pym/portage/package/ebuild/doebuild.py |
335 |
+++ b/pym/portage/package/ebuild/doebuild.py |
336 |
@@ -33,7 +33,11 @@ portage.proxy.lazyimport.lazyimport(globals(), |
337 |
'portage.package.ebuild._ipc.QueryCommand:QueryCommand', |
338 |
'portage.dep._slot_operator:evaluate_slot_operator_equal_deps', |
339 |
'portage.package.ebuild._spawn_nofetch:spawn_nofetch', |
340 |
+ 'portage.util.elf.header:ELFHeader', |
341 |
+ 'portage.dep.soname.multilib_category:compute_multilib_category', |
342 |
'portage.util._desktop_entry:validate_desktop_entry', |
343 |
+ 'portage.util._dyn_libs.NeededEntry:NeededEntry', |
344 |
+ 'portage.util._dyn_libs.soname_deps:SonameDepsProcessor', |
345 |
'portage.util._async.SchedulerInterface:SchedulerInterface', |
346 |
'portage.util._eventloop.EventLoop:EventLoop', |
347 |
'portage.util._eventloop.global_event_loop:global_event_loop', |
348 |
@@ -57,9 +61,9 @@ from portage.eapi import eapi_exports_KV, eapi_exports_merge_type, \ |
349 |
eapi_has_pkg_pretend, _get_eapi_attrs |
350 |
from portage.elog import elog_process, _preload_elog_modules |
351 |
from portage.elog.messages import eerror, eqawarn |
352 |
-from portage.exception import DigestException, FileNotFound, \ |
353 |
- IncorrectParameter, InvalidDependString, PermissionDenied, \ |
354 |
- UnsupportedAPIException |
355 |
+from portage.exception import (DigestException, FileNotFound, |
356 |
+ IncorrectParameter, InvalidData, InvalidDependString, |
357 |
+ PermissionDenied, UnsupportedAPIException) |
358 |
from portage.localization import _ |
359 |
from portage.output import colormap |
360 |
from portage.package.ebuild.prepare_build_dirs import prepare_build_dirs |
361 |
@@ -76,6 +80,11 @@ from _emerge.EbuildSpawnProcess import EbuildSpawnProcess |
362 |
from _emerge.Package import Package |
363 |
from _emerge.RootConfig import RootConfig |
364 |
|
365 |
+if sys.hexversion >= 0x3000000: |
366 |
+ _unicode = str |
367 |
+else: |
368 |
+ _unicode = unicode |
369 |
+ |
370 |
_unsandboxed_phases = frozenset([ |
371 |
"clean", "cleanrm", "config", |
372 |
"help", "info", "postinst", |
373 |
@@ -2250,21 +2259,64 @@ def _post_src_install_soname_symlinks(mysettings, out): |
374 |
is_libdir_cache[obj_parent] = rval |
375 |
return rval |
376 |
|
377 |
+ build_info_dir = os.path.join( |
378 |
+ mysettings['PORTAGE_BUILDDIR'], 'build-info') |
379 |
+ try: |
380 |
+ with io.open(_unicode_encode(os.path.join(build_info_dir, |
381 |
+ "PROVIDES_EXCLUDE"), encoding=_encodings['fs'], |
382 |
+ errors='strict'), mode='r', encoding=_encodings['repo.content'], |
383 |
+ errors='replace') as f: |
384 |
+ provides_exclude = f.read() |
385 |
+ except IOError as e: |
386 |
+ if e.errno not in (errno.ENOENT, errno.ESTALE): |
387 |
+ raise |
388 |
+ provides_exclude = "" |
389 |
+ |
390 |
+ try: |
391 |
+ with io.open(_unicode_encode(os.path.join(build_info_dir, |
392 |
+ "REQUIRES_EXCLUDE"), encoding=_encodings['fs'], |
393 |
+ errors='strict'), mode='r', encoding=_encodings['repo.content'], |
394 |
+ errors='replace') as f: |
395 |
+ requires_exclude = f.read() |
396 |
+ except IOError as e: |
397 |
+ if e.errno not in (errno.ENOENT, errno.ESTALE): |
398 |
+ raise |
399 |
+ requires_exclude = "" |
400 |
+ |
401 |
missing_symlinks = [] |
402 |
+ soname_deps = SonameDepsProcessor( |
403 |
+ provides_exclude, requires_exclude) |
404 |
+ |
405 |
+ # Parse NEEDED.ELF.2 like LinkageMapELF.rebuild() does, and |
406 |
+ # rewrite it to include multilib categories. |
407 |
+ needed_file = portage.util.atomic_ofstream(needed_filename, |
408 |
+ encoding=_encodings["repo.content"], errors="strict") |
409 |
|
410 |
- # Parse NEEDED.ELF.2 like LinkageMapELF.rebuild() does. |
411 |
for l in lines: |
412 |
l = l.rstrip("\n") |
413 |
if not l: |
414 |
continue |
415 |
- fields = l.split(";") |
416 |
- if len(fields) < 5: |
417 |
- portage.util.writemsg_level(_("\nWrong number of fields " \ |
418 |
- "in %s: %s\n\n") % (needed_filename, l), |
419 |
+ try: |
420 |
+ entry = NeededEntry.parse(needed_filename, l) |
421 |
+ except InvalidData as e: |
422 |
+ portage.util.writemsg_level("\n%s\n\n" % (e,), |
423 |
level=logging.ERROR, noiselevel=-1) |
424 |
continue |
425 |
|
426 |
- obj, soname = fields[1:3] |
427 |
+ filename = os.path.join(image_dir, |
428 |
+ entry.filename.lstrip(os.sep)) |
429 |
+ with open(_unicode_encode(filename, encoding=_encodings['fs'], |
430 |
+ errors='strict'), 'rb') as f: |
431 |
+ elf_header = ELFHeader.read(f) |
432 |
+ |
433 |
+ # Compute the multilib category and write it back to the file. |
434 |
+ entry.multilib_category = compute_multilib_category(elf_header) |
435 |
+ needed_file.write(_unicode(entry)) |
436 |
+ |
437 |
+ soname_deps.add(entry) |
438 |
+ obj = entry.filename |
439 |
+ soname = entry.soname |
440 |
+ |
441 |
if not soname: |
442 |
continue |
443 |
if not is_libdir(os.path.dirname(obj)): |
444 |
@@ -2284,6 +2336,22 @@ def _post_src_install_soname_symlinks(mysettings, out): |
445 |
|
446 |
missing_symlinks.append((obj, soname)) |
447 |
|
448 |
+ needed_file.close() |
449 |
+ |
450 |
+ if soname_deps.requires is not None: |
451 |
+ with io.open(_unicode_encode(os.path.join(build_info_dir, |
452 |
+ 'REQUIRES'), encoding=_encodings['fs'], errors='strict'), |
453 |
+ mode='w', encoding=_encodings['repo.content'], |
454 |
+ errors='strict') as f: |
455 |
+ f.write(soname_deps.requires) |
456 |
+ |
457 |
+ if soname_deps.provides is not None: |
458 |
+ with io.open(_unicode_encode(os.path.join(build_info_dir, |
459 |
+ 'PROVIDES'), encoding=_encodings['fs'], errors='strict'), |
460 |
+ mode='w', encoding=_encodings['repo.content'], |
461 |
+ errors='strict') as f: |
462 |
+ f.write(soname_deps.provides) |
463 |
+ |
464 |
if not missing_symlinks: |
465 |
return |
466 |
|
467 |
diff --git a/pym/portage/util/_dyn_libs/LinkageMapELF.py b/pym/portage/util/_dyn_libs/LinkageMapELF.py |
468 |
index 3920f94..c44666a 100644 |
469 |
--- a/pym/portage/util/_dyn_libs/LinkageMapELF.py |
470 |
+++ b/pym/portage/util/_dyn_libs/LinkageMapELF.py |
471 |
@@ -11,12 +11,37 @@ from portage import _os_merge |
472 |
from portage import _unicode_decode |
473 |
from portage import _unicode_encode |
474 |
from portage.cache.mappings import slot_dict_class |
475 |
-from portage.exception import CommandNotFound |
476 |
+from portage.exception import CommandNotFound, InvalidData |
477 |
from portage.localization import _ |
478 |
from portage.util import getlibpaths |
479 |
from portage.util import grabfile |
480 |
from portage.util import normalize_path |
481 |
+from portage.util import varexpand |
482 |
from portage.util import writemsg_level |
483 |
+from portage.util._dyn_libs.NeededEntry import NeededEntry |
484 |
+ |
485 |
+# Map ELF e_machine values from NEEDED.ELF.2 to approximate multilib |
486 |
+# categories. This approximation will produce incorrect results on x32 |
487 |
+# and mips systems, but the result is not worse than using the raw |
488 |
+# e_machine value which was used by earlier versions of portage. |
489 |
+_approx_multilib_categories = { |
490 |
+ "386": "x86_32", |
491 |
+ "68K": "m68k_32", |
492 |
+ "AARCH64": "arm_64", |
493 |
+ "ALPHA": "alpha_64", |
494 |
+ "ARM": "arm_32", |
495 |
+ "IA_64": "ia_64", |
496 |
+ "MIPS": "mips_o32", |
497 |
+ "PARISC": "hppa_64", |
498 |
+ "PPC": "ppc_32", |
499 |
+ "PPC64": "ppc_64", |
500 |
+ "S390": "s390_64", |
501 |
+ "SH": "sh_32", |
502 |
+ "SPARC": "sparc_32", |
503 |
+ "SPARC32PLUS": "sparc_32", |
504 |
+ "SPARCV9": "sparc_64", |
505 |
+ "X86_64": "x86_64", |
506 |
+} |
507 |
|
508 |
class LinkageMapELF(object): |
509 |
|
510 |
@@ -294,21 +319,31 @@ class LinkageMapELF(object): |
511 |
"in %s: %s\n\n") % (location, l), |
512 |
level=logging.ERROR, noiselevel=-1) |
513 |
continue |
514 |
- fields = l.split(";") |
515 |
- if len(fields) < 5: |
516 |
- writemsg_level(_("\nWrong number of fields " \ |
517 |
- "in %s: %s\n\n") % (location, l), |
518 |
+ try: |
519 |
+ entry = NeededEntry.parse(location, l) |
520 |
+ except InvalidData as e: |
521 |
+ writemsg_level("\n%s\n\n" % (e,), |
522 |
level=logging.ERROR, noiselevel=-1) |
523 |
continue |
524 |
- arch = fields[0] |
525 |
- obj = fields[1] |
526 |
- soname = fields[2] |
527 |
- path = frozenset(normalize_path(x) \ |
528 |
- for x in filter(None, fields[3].replace( |
529 |
- "${ORIGIN}", os.path.dirname(obj)).replace( |
530 |
- "$ORIGIN", os.path.dirname(obj)).split(":"))) |
531 |
+ |
532 |
+ # If NEEDED.ELF.2 contains the new multilib category field, |
533 |
+ # then use that for categorization. Otherwise, if a mapping |
534 |
+ # exists, map e_machine (entry.arch) to an approximate |
535 |
+ # multilib category. If all else fails, use e_machine, just |
536 |
+ # as older versions of portage did. |
537 |
+ arch = entry.multilib_category |
538 |
+ if arch is None: |
539 |
+ arch = _approx_multilib_categories.get( |
540 |
+ entry.arch, entry.arch) |
541 |
+ |
542 |
+ obj = entry.filename |
543 |
+ soname = entry.soname |
544 |
+ expand = {"ORIGIN": os.path.dirname(entry.filename)} |
545 |
+ path = frozenset(normalize_path(varexpand(x, expand)) |
546 |
+ for x in entry.runpaths) |
547 |
path = frozensets.setdefault(path, path) |
548 |
- needed = frozenset(x for x in fields[4].split(",") if x) |
549 |
+ needed = frozenset(entry.needed) |
550 |
+ |
551 |
needed = frozensets.setdefault(needed, needed) |
552 |
|
553 |
obj_key = self._obj_key(obj) |
554 |
diff --git a/pym/portage/util/_dyn_libs/NeededEntry.py b/pym/portage/util/_dyn_libs/NeededEntry.py |
555 |
new file mode 100644 |
556 |
index 0000000..c52cfce |
557 |
--- /dev/null |
558 |
+++ b/pym/portage/util/_dyn_libs/NeededEntry.py |
559 |
@@ -0,0 +1,82 @@ |
560 |
+# Copyright 2015 Gentoo Foundation |
561 |
+# Distributed under the terms of the GNU General Public License v2 |
562 |
+ |
563 |
+from __future__ import unicode_literals |
564 |
+ |
565 |
+import sys |
566 |
+ |
567 |
+from portage import _encodings, _unicode_encode |
568 |
+from portage.exception import InvalidData |
569 |
+from portage.localization import _ |
570 |
+ |
571 |
+class NeededEntry(object): |
572 |
+ """ |
573 |
+ Represents one entry (line) from a NEEDED.ELF.2 file. The entry |
574 |
+ must have 5 or more semicolon-delimited fields in order to be |
575 |
+ considered valid. The sixth field is optional, corresponding |
576 |
+ to the multilib category. The multilib_category attribute is |
577 |
+ None if the corresponding field is either empty or missing. |
578 |
+ """ |
579 |
+ |
580 |
+ __slots__ = ("arch", "filename", "multilib_category", "needed", |
581 |
+ "runpaths", "soname") |
582 |
+ |
583 |
+ _MIN_FIELDS = 5 |
584 |
+ _MULTILIB_CAT_INDEX = 5 |
585 |
+ |
586 |
+ @classmethod |
587 |
+ def parse(cls, filename, line): |
588 |
+ """ |
589 |
+ Parse a NEEDED.ELF.2 entry. Raises InvalidData if necessary. |
590 |
+ |
591 |
+ @param filename: file name for use in exception messages |
592 |
+ @type filename: str |
593 |
+ @param line: a single line of text from a NEEDED.ELF.2 file, |
594 |
+ without a trailing newline |
595 |
+ @type line: str |
596 |
+ @rtype: NeededEntry |
597 |
+ @return: A new NeededEntry instance containing data from line |
598 |
+ """ |
599 |
+ fields = line.split(";") |
600 |
+ if len(fields) < cls._MIN_FIELDS: |
601 |
+ raise InvalidData(_("Wrong number of fields " |
602 |
+ "in %s: %s\n\n") % (filename, line)) |
603 |
+ |
604 |
+ obj = cls() |
605 |
+ # Extra fields may exist (for future extensions). |
606 |
+ if (len(fields) > cls._MULTILIB_CAT_INDEX and |
607 |
+ fields[cls._MULTILIB_CAT_INDEX]): |
608 |
+ obj.multilib_category = fields[cls._MULTILIB_CAT_INDEX] |
609 |
+ else: |
610 |
+ obj.multilib_category = None |
611 |
+ |
612 |
+ del fields[cls._MIN_FIELDS:] |
613 |
+ obj.arch, obj.filename, obj.soname, rpaths, needed = fields |
614 |
+ obj.runpaths = tuple(filter(None, rpaths.split(":"))) |
615 |
+ obj.needed = tuple(filter(None, needed.split(","))) |
616 |
+ |
617 |
+ return obj |
618 |
+ |
619 |
+ def __str__(self): |
620 |
+ """ |
621 |
+ Format this entry for writing to a NEEDED.ELF.2 file. |
622 |
+ """ |
623 |
+ return ";".join([ |
624 |
+ self.arch, |
625 |
+ self.filename, |
626 |
+ self.soname, |
627 |
+ ":".join(self.runpaths), |
628 |
+ ",".join(self.needed), |
629 |
+ (self.multilib_category if self.multilib_category |
630 |
+ is not None else "") |
631 |
+ ]) + "\n" |
632 |
+ |
633 |
+ if sys.hexversion < 0x3000000: |
634 |
+ |
635 |
+ __unicode__ = __str__ |
636 |
+ |
637 |
+ def __str__(self): |
638 |
+ return _unicode_encode(self.__unicode__(), |
639 |
+ encoding=_encodings['content']) |
640 |
+ |
641 |
+ __str__.__doc__ = __unicode__.__doc__ |
642 |
diff --git a/pym/portage/util/_dyn_libs/soname_deps.py b/pym/portage/util/_dyn_libs/soname_deps.py |
643 |
new file mode 100644 |
644 |
index 0000000..24813e7 |
645 |
--- /dev/null |
646 |
+++ b/pym/portage/util/_dyn_libs/soname_deps.py |
647 |
@@ -0,0 +1,136 @@ |
648 |
+# Copyright 2015 Gentoo Foundation |
649 |
+# Distributed under the terms of the GNU General Public License v2 |
650 |
+ |
651 |
+import fnmatch |
652 |
+from itertools import chain |
653 |
+import os |
654 |
+import re |
655 |
+ |
656 |
+from portage.util import shlex_split |
657 |
+ |
658 |
+class SonameDepsProcessor(object): |
659 |
+ """ |
660 |
+ Processes NEEDED.ELF.2 entries for one package, in order to generate |
661 |
+ REQUIRES and PROVIDES metadata. |
662 |
+ |
663 |
+ Any sonames provided by the package will automatically be filtered |
664 |
+ from the generated REQUIRES values. |
665 |
+ """ |
666 |
+ |
667 |
+ def __init__(self, provides_exclude, requires_exclude): |
668 |
+ """ |
669 |
+ @param provides_exclude: PROVIDES_EXCLUDE value |
670 |
+ @type provides_exclude: str |
671 |
+ @param requires_exclude: REQUIRES_EXCLUDE value |
672 |
+ @type requires_exclude: str |
673 |
+ """ |
674 |
+ self._provides_exclude = self._exclude_pattern(provides_exclude) |
675 |
+ self._requires_exclude = self._exclude_pattern(requires_exclude) |
676 |
+ self._requires_map = {} |
677 |
+ self._provides_map = {} |
678 |
+ self._provides_unfiltered = {} |
679 |
+ self._provides = None |
680 |
+ self._requires = None |
681 |
+ self._intersected = False |
682 |
+ |
683 |
+ @staticmethod |
684 |
+ def _exclude_pattern(s): |
685 |
+ # shlex_split enables quoted whitespace inside patterns |
686 |
+ if s: |
687 |
+ pat = re.compile("|".join( |
688 |
+ fnmatch.translate(x.lstrip(os.sep)) |
689 |
+ for x in shlex_split(s))) |
690 |
+ else: |
691 |
+ pat = None |
692 |
+ return pat |
693 |
+ |
694 |
+ def add(self, entry): |
695 |
+ """ |
696 |
+ Add one NEEDED.ELF.2 entry, for inclusion in the generated |
697 |
+ REQUIRES and PROVIDES values. |
698 |
+ |
699 |
+ @param entry: NEEDED.ELF.2 entry |
700 |
+ @type entry: NeededEntry |
701 |
+ """ |
702 |
+ |
703 |
+ multilib_cat = entry.multilib_category |
704 |
+ if multilib_cat is None: |
705 |
+ # This usage is invalid. The caller must ensure that |
706 |
+ # the multilib category data is supplied here. |
707 |
+ raise AssertionError( |
708 |
+ "Missing multilib category data: %s" % entry.filename) |
709 |
+ |
710 |
+ if entry.needed and ( |
711 |
+ self._requires_exclude is None or |
712 |
+ self._requires_exclude.match( |
713 |
+ entry.filename.lstrip(os.sep)) is None): |
714 |
+ for x in entry.needed: |
715 |
+ if (self._requires_exclude is None or |
716 |
+ self._requires_exclude.match(x) is None): |
717 |
+ self._requires_map.setdefault( |
718 |
+ multilib_cat, set()).add(x) |
719 |
+ |
720 |
+ if entry.soname: |
721 |
+ self._provides_unfiltered.setdefault( |
722 |
+ multilib_cat, set()).add(entry.soname) |
723 |
+ |
724 |
+ if entry.soname and ( |
725 |
+ self._provides_exclude is None or |
726 |
+ (self._provides_exclude.match( |
727 |
+ entry.filename.lstrip(os.sep)) is None and |
728 |
+ self._provides_exclude.match(entry.soname) is None)): |
729 |
+ self._provides_map.setdefault( |
730 |
+ multilib_cat, set()).add(entry.soname) |
731 |
+ |
732 |
+ def _intersect(self): |
733 |
+ requires_map = self._requires_map |
734 |
+ provides_map = self._provides_map |
735 |
+ provides_unfiltered = self._provides_unfiltered |
736 |
+ |
737 |
+ for multilib_cat in set(chain(requires_map, provides_map)): |
738 |
+ requires_map.setdefault(multilib_cat, set()) |
739 |
+ provides_map.setdefault(multilib_cat, set()) |
740 |
+ provides_unfiltered.setdefault(multilib_cat, set()) |
741 |
+ for soname in list(requires_map[multilib_cat]): |
742 |
+ if soname in provides_unfiltered[multilib_cat]: |
743 |
+ requires_map[multilib_cat].remove(soname) |
744 |
+ |
745 |
+ provides_data = [] |
746 |
+ for multilib_cat in sorted(provides_map): |
747 |
+ if provides_map[multilib_cat]: |
748 |
+ provides_data.append(multilib_cat + ":") |
749 |
+ provides_data.extend(sorted(provides_map[multilib_cat])) |
750 |
+ |
751 |
+ if provides_data: |
752 |
+ self._provides = " ".join(provides_data) + "\n" |
753 |
+ |
754 |
+ requires_data = [] |
755 |
+ for multilib_cat in sorted(requires_map): |
756 |
+ if requires_map[multilib_cat]: |
757 |
+ requires_data.append(multilib_cat + ":") |
758 |
+ requires_data.extend(sorted(requires_map[multilib_cat])) |
759 |
+ |
760 |
+ if requires_data: |
761 |
+ self._requires = " ".join(requires_data) + "\n" |
762 |
+ |
763 |
+ self._intersected = True |
764 |
+ |
765 |
+ @property |
766 |
+ def provides(self): |
767 |
+ """ |
768 |
+ @rtype: str |
769 |
+ @return: PROVIDES value generated from NEEDED.ELF.2 entries |
770 |
+ """ |
771 |
+ if not self._intersected: |
772 |
+ self._intersect() |
773 |
+ return self._provides |
774 |
+ |
775 |
+ @property |
776 |
+ def requires(self): |
777 |
+ """ |
778 |
+ @rtype: str |
779 |
+ @return: REQUIRES value generated from NEEDED.ELF.2 entries |
780 |
+ """ |
781 |
+ if not self._intersected: |
782 |
+ self._intersect() |
783 |
+ return self._requires |
784 |
diff --git a/pym/portage/util/elf/__init__.py b/pym/portage/util/elf/__init__.py |
785 |
new file mode 100644 |
786 |
index 0000000..4725d33 |
787 |
--- /dev/null |
788 |
+++ b/pym/portage/util/elf/__init__.py |
789 |
@@ -0,0 +1,2 @@ |
790 |
+# Copyright 2015 Gentoo Foundation |
791 |
+# Distributed under the terms of the GNU General Public License v2 |
792 |
diff --git a/pym/portage/util/elf/constants.py b/pym/portage/util/elf/constants.py |
793 |
new file mode 100644 |
794 |
index 0000000..2415c7b |
795 |
--- /dev/null |
796 |
+++ b/pym/portage/util/elf/constants.py |
797 |
@@ -0,0 +1,39 @@ |
798 |
+# Copyright 2015 Gentoo Foundation |
799 |
+# Distributed under the terms of the GNU General Public License v2 |
800 |
+# |
801 |
+# These constants are available from elfutils: |
802 |
+# https://git.fedorahosted.org/cgit/elfutils.git/tree/libelf/elf.h |
803 |
+ |
804 |
+EI_CLASS = 4 |
805 |
+ELFCLASS32 = 1 |
806 |
+ELFCLASS64 = 2 |
807 |
+ |
808 |
+EI_DATA = 5 |
809 |
+ELFDATA2LSB = 1 |
810 |
+ELFDATA2MSB = 2 |
811 |
+ |
812 |
+E_MACHINE = 18 |
813 |
+EM_SPARC = 2 |
814 |
+EM_386 = 3 |
815 |
+EM_68K = 4 |
816 |
+EM_MIPS = 8 |
817 |
+EM_PARISC = 15 |
818 |
+EM_SPARC32PLUS = 18 |
819 |
+EM_PPC = 20 |
820 |
+EM_PPC64 = 21 |
821 |
+EM_S390 = 22 |
822 |
+EM_ARM = 40 |
823 |
+EM_SH = 42 |
824 |
+EM_SPARCV9 = 43 |
825 |
+EM_IA_64 = 50 |
826 |
+EM_X86_64 = 62 |
827 |
+EM_AARCH64 = 183 |
828 |
+EM_ALPHA = 0x9026 |
829 |
+ |
830 |
+E_ENTRY = 24 |
831 |
+EF_MIPS_ABI = 0x0000f000 |
832 |
+EF_MIPS_ABI2 = 0x00000020 |
833 |
+E_MIPS_ABI_O32 = 0x00001000 |
834 |
+E_MIPS_ABI_O64 = 0x00002000 |
835 |
+E_MIPS_ABI_EABI32 = 0x00003000 |
836 |
+E_MIPS_ABI_EABI64 = 0x00004000 |
837 |
diff --git a/pym/portage/util/elf/header.py b/pym/portage/util/elf/header.py |
838 |
new file mode 100644 |
839 |
index 0000000..67729de |
840 |
--- /dev/null |
841 |
+++ b/pym/portage/util/elf/header.py |
842 |
@@ -0,0 +1,62 @@ |
843 |
+# Copyright 2015 Gentoo Foundation |
844 |
+# Distributed under the terms of the GNU General Public License v2 |
845 |
+ |
846 |
+import collections |
847 |
+ |
848 |
+from portage.util.endian.decode import (decode_uint16_le, |
849 |
+ decode_uint32_le, decode_uint16_be, decode_uint32_be) |
850 |
+from portage.util.elf.constants import (E_ENTRY, E_MACHINE, EI_CLASS, |
851 |
+ ELFCLASS32, ELFCLASS64, ELFDATA2LSB, ELFDATA2MSB) |
852 |
+ |
853 |
+class ELFHeader(object): |
854 |
+ |
855 |
+ __slots__ = ('e_flags', 'e_machine', 'ei_class', 'ei_data') |
856 |
+ |
857 |
+ @classmethod |
858 |
+ def read(cls, f): |
859 |
+ """ |
860 |
+ @param f: an open ELF file |
861 |
+ @type f: file |
862 |
+ @rtype: ELFHeader |
863 |
+ @return: A new ELFHeader instance containing data from f |
864 |
+ """ |
865 |
+ f.seek(EI_CLASS) |
866 |
+ ei_class = ord(f.read(1)) |
867 |
+ ei_data = ord(f.read(1)) |
868 |
+ |
869 |
+ if ei_class == ELFCLASS32: |
870 |
+ width = 32 |
871 |
+ elif ei_class == ELFCLASS64: |
872 |
+ width = 64 |
873 |
+ else: |
874 |
+ width = None |
875 |
+ |
876 |
+ if ei_data == ELFDATA2LSB: |
877 |
+ uint16 = decode_uint16_le |
878 |
+ uint32 = decode_uint32_le |
879 |
+ elif ei_data == ELFDATA2MSB: |
880 |
+ uint16 = decode_uint16_be |
881 |
+ uint32 = decode_uint32_be |
882 |
+ else: |
883 |
+ uint16 = None |
884 |
+ uint32 = None |
885 |
+ |
886 |
+ if width is None or uint16 is None: |
887 |
+ e_machine = None |
888 |
+ e_flags = None |
889 |
+ else: |
890 |
+ f.seek(E_MACHINE) |
891 |
+ e_machine = uint16(f.read(2)) |
892 |
+ |
893 |
+ # E_ENTRY + 3 * sizeof(uintN) |
894 |
+ e_flags_offset = E_ENTRY + 3 * width // 8 |
895 |
+ f.seek(e_flags_offset) |
896 |
+ e_flags = uint32(f.read(4)) |
897 |
+ |
898 |
+ obj = cls() |
899 |
+ obj.e_flags = e_flags |
900 |
+ obj.e_machine = e_machine |
901 |
+ obj.ei_class = ei_class |
902 |
+ obj.ei_data = ei_data |
903 |
+ |
904 |
+ return obj |
905 |
diff --git a/pym/portage/util/endian/__init__.py b/pym/portage/util/endian/__init__.py |
906 |
new file mode 100644 |
907 |
index 0000000..4725d33 |
908 |
--- /dev/null |
909 |
+++ b/pym/portage/util/endian/__init__.py |
910 |
@@ -0,0 +1,2 @@ |
911 |
+# Copyright 2015 Gentoo Foundation |
912 |
+# Distributed under the terms of the GNU General Public License v2 |
913 |
diff --git a/pym/portage/util/endian/decode.py b/pym/portage/util/endian/decode.py |
914 |
new file mode 100644 |
915 |
index 0000000..9833b53 |
916 |
--- /dev/null |
917 |
+++ b/pym/portage/util/endian/decode.py |
918 |
@@ -0,0 +1,48 @@ |
919 |
+# Copyright 2015 Gentoo Foundation |
920 |
+# Distributed under the terms of the GNU General Public License v2 |
921 |
+ |
922 |
+import struct |
923 |
+ |
924 |
+def decode_uint16_be(data): |
925 |
+ """ |
926 |
+ Decode an unsigned 16-bit integer with big-endian encoding. |
927 |
+ |
928 |
+ @param data: string of bytes of length 2 |
929 |
+ @type data: bytes |
930 |
+ @rtype: int |
931 |
+ @return: unsigned integer value of the decoded data |
932 |
+ """ |
933 |
+ return struct.unpack_from(">H", data)[0] |
934 |
+ |
935 |
+def decode_uint16_le(data): |
936 |
+ """ |
937 |
+ Decode an unsigned 16-bit integer with little-endian encoding. |
938 |
+ |
939 |
+ @param data: string of bytes of length 2 |
940 |
+ @type data: bytes |
941 |
+ @rtype: int |
942 |
+ @return: unsigned integer value of the decoded data |
943 |
+ """ |
944 |
+ return struct.unpack_from("<H", data)[0] |
945 |
+ |
946 |
+def decode_uint32_be(data): |
947 |
+ """ |
948 |
+ Decode an unsigned 32-bit integer with big-endian encoding. |
949 |
+ |
950 |
+ @param data: string of bytes of length 4 |
951 |
+ @type data: bytes |
952 |
+ @rtype: int |
953 |
+ @return: unsigned integer value of the decoded data |
954 |
+ """ |
955 |
+ return struct.unpack_from(">I", data)[0] |
956 |
+ |
957 |
+def decode_uint32_le(data): |
958 |
+ """ |
959 |
+ Decode an unsigned 32-bit integer with little-endian encoding. |
960 |
+ |
961 |
+ @param data: string of bytes of length 4 |
962 |
+ @type data: bytes |
963 |
+ @rtype: int |
964 |
+ @return: unsigned integer value of the decoded data |
965 |
+ """ |
966 |
+ return struct.unpack_from("<I", data)[0] |
967 |
-- |
968 |
2.0.5 |