1 |
Add support for an eapi-x flag in the metadata/layout.conf |
2 |
profile-formats field, where x must be a supported EAPI. This may be |
3 |
used to set the default EAPI for profile directories that do not |
4 |
contain an eapi file. For example, setting "profile-formats = eapi-5" |
5 |
will cause all profiles that do not contain an eapi file to default to |
6 |
EAPI 5 instead of EAPI 0. |
7 |
|
8 |
This is convenient for organizations that maintain their own profiles |
9 |
in separate repositories from Gentoo, since they typically want to use |
10 |
EAPI 5 for all of their profiles, and this allows them to avoid having |
11 |
separate eapi files in each directory of their profiles. |
12 |
|
13 |
The default EAPI setting is stored in the RepoConfig.eapi attribute, |
14 |
which is used as the "default" parameter for all |
15 |
read_corresponding_eapi_file calls. Each profile node in |
16 |
LocationsManager.profiles_complex now has an eapi attribute which |
17 |
serves to cache the computed EAPI for that node, allowing redundant |
18 |
read_corresponding_eapi_file calls to be eliminated. As a result, |
19 |
functions such as grabfile_package now have eapi and eapi_default |
20 |
parameters, where eapi is used to pass in a cached EAPI, and |
21 |
eapi_default is used to pass in a default for |
22 |
read_corresponding_eapi_file to use when a cached EAPI is not |
23 |
available. For /etc/portage/profile, the EAPI is considered to have a |
24 |
default value of None, which means that atoms from any supported EAPI |
25 |
are allowed. |
26 |
|
27 |
X-Gentoo-Bug: 532670 |
28 |
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=532670 |
29 |
--- |
30 |
PATCH v2 fixes some eapi_default parameters for correct behavior with |
31 |
/etc/portage/profile where eapi_default should be None. |
32 |
|
33 |
man/portage.5 | 9 +- |
34 |
pym/portage/_sets/ProfilePackageSet.py | 3 +- |
35 |
pym/portage/_sets/profiles.py | 27 +++-- |
36 |
.../package/ebuild/_config/KeywordsManager.py | 6 +- |
37 |
.../package/ebuild/_config/LocationsManager.py | 31 ++++-- |
38 |
pym/portage/package/ebuild/_config/MaskManager.py | 12 +- |
39 |
pym/portage/package/ebuild/_config/UseManager.py | 88 ++++++++++++--- |
40 |
pym/portage/package/ebuild/config.py | 9 +- |
41 |
pym/portage/repository/config.py | 47 ++++++-- |
42 |
.../tests/resolver/test_profile_default_eapi.py | 123 +++++++++++++++++++++ |
43 |
pym/portage/util/__init__.py | 11 +- |
44 |
11 files changed, 305 insertions(+), 61 deletions(-) |
45 |
create mode 100644 pym/portage/tests/resolver/test_profile_default_eapi.py |
46 |
|
47 |
diff --git a/man/portage.5 b/man/portage.5 |
48 |
index 88cf3bb..2ed4501 100644 |
49 |
--- a/man/portage.5 |
50 |
+++ b/man/portage.5 |
51 |
@@ -254,6 +254,10 @@ use.stable.force, package.use.stable.mask and |
52 |
package.use.stable.force. These files behave similarly to |
53 |
previously supported USE configuration files, except that they |
54 |
only influence packages that are merged due to a stable keyword. |
55 |
+ |
56 |
+If the eapi file does not exist, then the \fBEAPI\fR defaults to |
57 |
+\fI0\fR unless the default has been overridden by a profile-formats |
58 |
+setting inside \fImetadata/layout.conf\fR of the containing repository. |
59 |
.TP |
60 |
.BR make.defaults |
61 |
The profile default settings for Portage. The general format is described |
62 |
@@ -1114,7 +1118,7 @@ The default setting for repoman's --echangelog option. |
63 |
The cache formats supported in the metadata tree. There is the old "pms" format |
64 |
and the newer/faster "md5-dict" format. Default is to detect dirs. |
65 |
.TP |
66 |
-.BR profile\-formats " = [pms|portage-1|portage-2|profile-bashrcs|profile-set]" |
67 |
+.BR profile\-formats " = [pms|portage-1|portage-2|profile-bashrcs|profile-set|eapi-x]" |
68 |
Control functionality available to profiles in this repo such as which files |
69 |
may be dirs, or the syntax available in parent files. Use "portage-2" if you're |
70 |
unsure. The default is "portage-1-compat" mode which is meant to be compatible |
71 |
@@ -1123,6 +1127,9 @@ Setting profile-bashrcs will enable the per-profile bashrc mechanism |
72 |
\fBpackage.bashrc\fR. Setting profile-set enables support for using the |
73 |
profile \fBpackages\fR file to add atoms to the @profile package set. |
74 |
See the profile \fBpackages\fR section for more information. |
75 |
+Setting eapi-x, where \fIx\fR must be a specific supported \fBEAPI\fR, |
76 |
+causes a profile to be interpreted with the specified \fBEAPI\fR unless |
77 |
+the profile has an \fIeapi\fR file specifying a different \fBEAPI\fR. |
78 |
.RE |
79 |
.RE |
80 |
|
81 |
diff --git a/pym/portage/_sets/ProfilePackageSet.py b/pym/portage/_sets/ProfilePackageSet.py |
82 |
index c2f5fee..2fcafb6 100644 |
83 |
--- a/pym/portage/_sets/ProfilePackageSet.py |
84 |
+++ b/pym/portage/_sets/ProfilePackageSet.py |
85 |
@@ -23,7 +23,8 @@ class ProfilePackageSet(PackageSet): |
86 |
def load(self): |
87 |
self._setAtoms(x for x in stack_lists( |
88 |
[grabfile_package(os.path.join(y.location, "packages"), |
89 |
- verify_eapi=True) for y in self._profiles |
90 |
+ verify_eapi=True, eapi=y.eapi, eapi_default=None) |
91 |
+ for y in self._profiles |
92 |
if "profile-set" in y.profile_formats], |
93 |
incremental=1) if x[:1] != "*") |
94 |
|
95 |
diff --git a/pym/portage/_sets/profiles.py b/pym/portage/_sets/profiles.py |
96 |
index 39a2968..ccb3432 100644 |
97 |
--- a/pym/portage/_sets/profiles.py |
98 |
+++ b/pym/portage/_sets/profiles.py |
99 |
@@ -1,4 +1,4 @@ |
100 |
-# Copyright 2007 Gentoo Foundation |
101 |
+# Copyright 2007-2014 Gentoo Foundation |
102 |
# Distributed under the terms of the GNU General Public License v2 |
103 |
|
104 |
import logging |
105 |
@@ -14,15 +14,15 @@ __all__ = ["PackagesSystemSet"] |
106 |
class PackagesSystemSet(PackageSet): |
107 |
_operations = ["merge"] |
108 |
|
109 |
- def __init__(self, profile_paths, debug=False): |
110 |
+ def __init__(self, profiles, debug=False): |
111 |
super(PackagesSystemSet, self).__init__() |
112 |
- self._profile_paths = profile_paths |
113 |
+ self._profiles = profiles |
114 |
self._debug = debug |
115 |
- if profile_paths: |
116 |
- description = self._profile_paths[-1] |
117 |
- if description == "/etc/portage/profile" and \ |
118 |
- len(self._profile_paths) > 1: |
119 |
- description = self._profile_paths[-2] |
120 |
+ if profiles: |
121 |
+ desc_profile = profiles[-1] |
122 |
+ if desc_profile.user_config and len(profiles) > 1: |
123 |
+ desc_profile = profiles[-2] |
124 |
+ description = desc_profile.location |
125 |
else: |
126 |
description = None |
127 |
self.description = "System packages for profile %s" % description |
128 |
@@ -30,10 +30,12 @@ class PackagesSystemSet(PackageSet): |
129 |
def load(self): |
130 |
debug = self._debug |
131 |
if debug: |
132 |
- writemsg_level("\nPackagesSystemSet: profile paths: %s\n" % \ |
133 |
- (self._profile_paths,), level=logging.DEBUG, noiselevel=-1) |
134 |
+ writemsg_level("\nPackagesSystemSet: profiles: %s\n" % |
135 |
+ (self._profiles,), level=logging.DEBUG, noiselevel=-1) |
136 |
|
137 |
- mylist = [grabfile_package(os.path.join(x, "packages"), verify_eapi=True) for x in self._profile_paths] |
138 |
+ mylist = [grabfile_package(os.path.join(x.location, "packages"), |
139 |
+ verify_eapi=True, eapi=x.eapi, eapi_default=None) |
140 |
+ for x in self._profiles] |
141 |
|
142 |
if debug: |
143 |
writemsg_level("\nPackagesSystemSet: raw packages: %s\n" % \ |
144 |
@@ -49,5 +51,6 @@ class PackagesSystemSet(PackageSet): |
145 |
|
146 |
def singleBuilder(self, options, settings, trees): |
147 |
debug = get_boolean(options, "debug", False) |
148 |
- return PackagesSystemSet(settings.profiles, debug=debug) |
149 |
+ return PackagesSystemSet( |
150 |
+ settings._locations_manager.profiles_complex, debug=debug) |
151 |
singleBuilder = classmethod(singleBuilder) |
152 |
diff --git a/pym/portage/package/ebuild/_config/KeywordsManager.py b/pym/portage/package/ebuild/_config/KeywordsManager.py |
153 |
index af606f1..e1a8e2b 100644 |
154 |
--- a/pym/portage/package/ebuild/_config/KeywordsManager.py |
155 |
+++ b/pym/portage/package/ebuild/_config/KeywordsManager.py |
156 |
@@ -1,4 +1,4 @@ |
157 |
-# Copyright 2010-2012 Gentoo Foundation |
158 |
+# Copyright 2010-2014 Gentoo Foundation |
159 |
# Distributed under the terms of the GNU General Public License v2 |
160 |
|
161 |
__all__ = ( |
162 |
@@ -22,7 +22,7 @@ class KeywordsManager(object): |
163 |
rawpkeywords = [grabdict_package( |
164 |
os.path.join(x.location, "package.keywords"), |
165 |
recursive=x.portage1_directories, |
166 |
- verify_eapi=True) \ |
167 |
+ verify_eapi=True, eapi=x.eapi, eapi_default=None) |
168 |
for x in profiles] |
169 |
for pkeyworddict in rawpkeywords: |
170 |
if not pkeyworddict: |
171 |
@@ -38,7 +38,7 @@ class KeywordsManager(object): |
172 |
raw_p_accept_keywords = [grabdict_package( |
173 |
os.path.join(x.location, "package.accept_keywords"), |
174 |
recursive=x.portage1_directories, |
175 |
- verify_eapi=True) \ |
176 |
+ verify_eapi=True, eapi=x.eapi, eapi_default=None) |
177 |
for x in profiles] |
178 |
for d in raw_p_accept_keywords: |
179 |
if not d: |
180 |
diff --git a/pym/portage/package/ebuild/_config/LocationsManager.py b/pym/portage/package/ebuild/_config/LocationsManager.py |
181 |
index 6641092..1fc7e45 100644 |
182 |
--- a/pym/portage/package/ebuild/_config/LocationsManager.py |
183 |
+++ b/pym/portage/package/ebuild/_config/LocationsManager.py |
184 |
@@ -19,7 +19,7 @@ from portage.eapi import eapi_allows_directories_on_profile_level_and_repository |
185 |
from portage.exception import DirectoryNotFound, ParseError |
186 |
from portage.localization import _ |
187 |
from portage.util import ensure_dirs, grabfile, \ |
188 |
- normalize_path, shlex_split, writemsg |
189 |
+ normalize_path, read_corresponding_eapi_file, shlex_split, writemsg |
190 |
from portage.util._path import exists_raise_eaccess, isdir_raise_eaccess |
191 |
from portage.repository.config import parse_layout_conf, \ |
192 |
_portage1_profiles_allow_directories |
193 |
@@ -31,7 +31,7 @@ _PORTAGE1_DIRECTORIES = frozenset([ |
194 |
'use.mask', 'use.force']) |
195 |
|
196 |
_profile_node = collections.namedtuple('_profile_node', |
197 |
- 'location portage1_directories user_config profile_formats') |
198 |
+ 'location portage1_directories user_config profile_formats eapi') |
199 |
|
200 |
_allow_parent_colon = frozenset( |
201 |
["portage-2"]) |
202 |
@@ -129,11 +129,16 @@ class LocationsManager(object): |
203 |
custom_prof = os.path.join( |
204 |
self.config_root, CUSTOM_PROFILE_PATH) |
205 |
if os.path.exists(custom_prof): |
206 |
+ # For read_corresponding_eapi_file, specify default=None |
207 |
+ # in order to allow things like wildcard atoms when |
208 |
+ # is no explicit EAPI setting. |
209 |
self.user_profile_dir = custom_prof |
210 |
self.profiles.append(custom_prof) |
211 |
self.profiles_complex.append( |
212 |
_profile_node(custom_prof, True, True, |
213 |
- ('profile-bashrcs', 'profile-set'))) |
214 |
+ ('profile-bashrcs', 'profile-set'), |
215 |
+ read_corresponding_eapi_file( |
216 |
+ custom_prof + os.sep, default=None))) |
217 |
del custom_prof |
218 |
|
219 |
self.profiles = tuple(self.profiles) |
220 |
@@ -153,9 +158,21 @@ class LocationsManager(object): |
221 |
repo_loc = None |
222 |
compat_mode = False |
223 |
current_formats = () |
224 |
+ eapi = None |
225 |
+ |
226 |
+ intersecting_repos = [x for x in known_repos |
227 |
+ if current_abs_path.startswith(x[0])] |
228 |
+ if intersecting_repos: |
229 |
+ # Handle nested repositories. The longest path |
230 |
+ # will be the correct one. |
231 |
+ repo_loc, layout_data = max(intersecting_repos, |
232 |
+ key=lambda x:len(x[0])) |
233 |
+ eapi = [x for x in layout_data['profile-formats'] |
234 |
+ if x.startswith("eapi-")] |
235 |
+ eapi = eapi and eapi[0][len("eapi-"):] |
236 |
|
237 |
eapi_file = os.path.join(currentPath, "eapi") |
238 |
- eapi = "0" |
239 |
+ eapi = eapi or "0" |
240 |
f = None |
241 |
try: |
242 |
f = io.open(_unicode_encode(eapi_file, |
243 |
@@ -174,11 +191,7 @@ class LocationsManager(object): |
244 |
if f is not None: |
245 |
f.close() |
246 |
|
247 |
- intersecting_repos = [x for x in known_repos if current_abs_path.startswith(x[0])] |
248 |
if intersecting_repos: |
249 |
- # protect against nested repositories. Insane configuration, but the longest |
250 |
- # path will be the correct one. |
251 |
- repo_loc, layout_data = max(intersecting_repos, key=lambda x:len(x[0])) |
252 |
allow_directories = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \ |
253 |
any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats']) |
254 |
compat_mode = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \ |
255 |
@@ -238,7 +251,7 @@ class LocationsManager(object): |
256 |
self.profiles.append(currentPath) |
257 |
self.profiles_complex.append( |
258 |
_profile_node(currentPath, allow_directories, False, |
259 |
- current_formats)) |
260 |
+ current_formats, eapi)) |
261 |
|
262 |
def _expand_parent_colon(self, parentsFile, parentPath, |
263 |
repo_loc, repositories): |
264 |
diff --git a/pym/portage/package/ebuild/_config/MaskManager.py b/pym/portage/package/ebuild/_config/MaskManager.py |
265 |
index 0f060c9..55c8c7a 100644 |
266 |
--- a/pym/portage/package/ebuild/_config/MaskManager.py |
267 |
+++ b/pym/portage/package/ebuild/_config/MaskManager.py |
268 |
@@ -39,7 +39,8 @@ class MaskManager(object): |
269 |
path = os.path.join(loc, 'profiles', 'package.mask') |
270 |
pmask_cache[loc] = grabfile_package(path, |
271 |
recursive=repo_config.portage1_profiles, |
272 |
- remember_source_file=True, verify_eapi=True) |
273 |
+ remember_source_file=True, verify_eapi=True, |
274 |
+ eapi_default=repo_config.eapi) |
275 |
if repo_config.portage1_profiles_compat and os.path.isdir(path): |
276 |
warnings.warn(_("Repository '%(repo_name)s' is implicitly using " |
277 |
"'portage-1' profile format in its profiles/package.mask, but " |
278 |
@@ -105,7 +106,8 @@ class MaskManager(object): |
279 |
if not repo.portage1_profiles: |
280 |
continue |
281 |
repo_lines = grabfile_package(os.path.join(repo.location, "profiles", "package.unmask"), \ |
282 |
- recursive=1, remember_source_file=True, verify_eapi=True) |
283 |
+ recursive=1, remember_source_file=True, |
284 |
+ verify_eapi=True, eapi_default=repo.eapi) |
285 |
lines = stack_lists([repo_lines], incremental=1, \ |
286 |
remember_source_file=True, warn_for_unmatched_removal=True, |
287 |
strict_warn_for_unmatched_removal=strict_umatched_removal) |
288 |
@@ -119,12 +121,14 @@ class MaskManager(object): |
289 |
profile_pkgmasklines.append(grabfile_package( |
290 |
os.path.join(x.location, "package.mask"), |
291 |
recursive=x.portage1_directories, |
292 |
- remember_source_file=True, verify_eapi=True)) |
293 |
+ remember_source_file=True, verify_eapi=True, |
294 |
+ eapi=x.eapi, eapi_default=None)) |
295 |
if x.portage1_directories: |
296 |
profile_pkgunmasklines.append(grabfile_package( |
297 |
os.path.join(x.location, "package.unmask"), |
298 |
recursive=x.portage1_directories, |
299 |
- remember_source_file=True, verify_eapi=True)) |
300 |
+ remember_source_file=True, verify_eapi=True, |
301 |
+ eapi=x.eapi, eapi_default=None)) |
302 |
profile_pkgmasklines = stack_lists(profile_pkgmasklines, incremental=1, \ |
303 |
remember_source_file=True, warn_for_unmatched_removal=True, |
304 |
strict_warn_for_unmatched_removal=strict_umatched_removal) |
305 |
diff --git a/pym/portage/package/ebuild/_config/UseManager.py b/pym/portage/package/ebuild/_config/UseManager.py |
306 |
index 1c8c60e..f869908 100644 |
307 |
--- a/pym/portage/package/ebuild/_config/UseManager.py |
308 |
+++ b/pym/portage/package/ebuild/_config/UseManager.py |
309 |
@@ -107,10 +107,32 @@ class UseManager(object): |
310 |
|
311 |
self.repositories = repositories |
312 |
|
313 |
- def _parse_file_to_tuple(self, file_name, recursive=True, eapi_filter=None): |
314 |
+ def _parse_file_to_tuple(self, file_name, recursive=True, |
315 |
+ eapi_filter=None, eapi=None, eapi_default="0"): |
316 |
+ """ |
317 |
+ @param file_name: input file name |
318 |
+ @type file_name: str |
319 |
+ @param recursive: triggers recursion if the input file is a |
320 |
+ directory |
321 |
+ @type recursive: bool |
322 |
+ @param eapi_filter: a function that accepts a single eapi |
323 |
+ argument, and returns true if the the current file type |
324 |
+ is supported by the given EAPI |
325 |
+ @type eapi_filter: callable |
326 |
+ @param eapi: the EAPI of the current profile node, which allows |
327 |
+ a call to read_corresponding_eapi_file to be skipped |
328 |
+ @type eapi: str |
329 |
+ @param eapi_default: the default EAPI which applies if the |
330 |
+ current profile node does not define a local EAPI |
331 |
+ @type eapi_default: str |
332 |
+ @rtype: tuple |
333 |
+ @return: collection of use flags |
334 |
+ """ |
335 |
ret = [] |
336 |
lines = grabfile(file_name, recursive=recursive) |
337 |
- eapi = read_corresponding_eapi_file(file_name) |
338 |
+ if eapi is None: |
339 |
+ eapi = read_corresponding_eapi_file( |
340 |
+ file_name, default=eapi_default) |
341 |
if eapi_filter is not None and not eapi_filter(eapi): |
342 |
if lines: |
343 |
writemsg(_("--- EAPI '%s' does not support '%s': '%s'\n") % |
344 |
@@ -131,19 +153,46 @@ class UseManager(object): |
345 |
return tuple(ret) |
346 |
|
347 |
def _parse_file_to_dict(self, file_name, juststrings=False, recursive=True, |
348 |
- eapi_filter=None, user_config=False): |
349 |
+ eapi_filter=None, user_config=False, eapi=None, eapi_default="0"): |
350 |
+ """ |
351 |
+ @param file_name: input file name |
352 |
+ @type file_name: str |
353 |
+ @param juststrings: store dict values as space-delimited strings |
354 |
+ instead of tuples |
355 |
+ @type juststrings: bool |
356 |
+ @param recursive: triggers recursion if the input file is a |
357 |
+ directory |
358 |
+ @type recursive: bool |
359 |
+ @param eapi_filter: a function that accepts a single eapi |
360 |
+ argument, and returns true if the the current file type |
361 |
+ is supported by the given EAPI |
362 |
+ @type eapi_filter: callable |
363 |
+ @param user_config: current file is part of the local |
364 |
+ configuration (not repository content) |
365 |
+ @type user_config: bool |
366 |
+ @param eapi: the EAPI of the current profile node, which allows |
367 |
+ a call to read_corresponding_eapi_file to be skipped |
368 |
+ @type eapi: str |
369 |
+ @param eapi_default: the default EAPI which applies if the |
370 |
+ current profile node does not define a local EAPI |
371 |
+ @type eapi_default: str |
372 |
+ @rtype: tuple |
373 |
+ @return: collection of use flags |
374 |
+ """ |
375 |
ret = {} |
376 |
location_dict = {} |
377 |
- eapi = read_corresponding_eapi_file(file_name, default=None) |
378 |
- if eapi is None and not user_config: |
379 |
- eapi = "0" |
380 |
if eapi is None: |
381 |
+ eapi = read_corresponding_eapi_file(file_name, |
382 |
+ default=eapi_default) |
383 |
+ extended_syntax = eapi is None and user_config |
384 |
+ if extended_syntax: |
385 |
ret = ExtendedAtomDict(dict) |
386 |
else: |
387 |
ret = {} |
388 |
file_dict = grabdict_package(file_name, recursive=recursive, |
389 |
- allow_wildcard=(eapi is None), allow_repo=(eapi is None), |
390 |
- verify_eapi=(eapi is not None)) |
391 |
+ allow_wildcard=extended_syntax, allow_repo=extended_syntax, |
392 |
+ verify_eapi=(not extended_syntax), eapi=eapi, |
393 |
+ eapi_default=eapi_default) |
394 |
if eapi is not None and eapi_filter is not None and not eapi_filter(eapi): |
395 |
if file_dict: |
396 |
writemsg(_("--- EAPI '%s' does not support '%s': '%s'\n") % |
397 |
@@ -185,35 +234,41 @@ class UseManager(object): |
398 |
def _parse_repository_files_to_dict_of_tuples(self, file_name, repositories, eapi_filter=None): |
399 |
ret = {} |
400 |
for repo in repositories.repos_with_profiles(): |
401 |
- ret[repo.name] = self._parse_file_to_tuple(os.path.join(repo.location, "profiles", file_name), eapi_filter=eapi_filter) |
402 |
+ ret[repo.name] = self._parse_file_to_tuple( |
403 |
+ os.path.join(repo.location, "profiles", file_name), |
404 |
+ eapi_filter=eapi_filter, eapi_default=repo.eapi) |
405 |
return ret |
406 |
|
407 |
def _parse_repository_files_to_dict_of_dicts(self, file_name, repositories, eapi_filter=None): |
408 |
ret = {} |
409 |
for repo in repositories.repos_with_profiles(): |
410 |
- ret[repo.name] = self._parse_file_to_dict(os.path.join(repo.location, "profiles", file_name), eapi_filter=eapi_filter) |
411 |
+ ret[repo.name] = self._parse_file_to_dict( |
412 |
+ os.path.join(repo.location, "profiles", file_name), |
413 |
+ eapi_filter=eapi_filter, eapi_default=repo.eapi) |
414 |
return ret |
415 |
|
416 |
def _parse_profile_files_to_tuple_of_tuples(self, file_name, locations, |
417 |
eapi_filter=None): |
418 |
return tuple(self._parse_file_to_tuple( |
419 |
os.path.join(profile.location, file_name), |
420 |
- recursive=profile.portage1_directories, eapi_filter=eapi_filter) |
421 |
- for profile in locations) |
422 |
+ recursive=profile.portage1_directories, |
423 |
+ eapi_filter=eapi_filter, eapi=profile.eapi, |
424 |
+ eapi_default=None) for profile in locations) |
425 |
|
426 |
def _parse_profile_files_to_tuple_of_dicts(self, file_name, locations, |
427 |
juststrings=False, eapi_filter=None): |
428 |
return tuple(self._parse_file_to_dict( |
429 |
os.path.join(profile.location, file_name), juststrings, |
430 |
recursive=profile.portage1_directories, eapi_filter=eapi_filter, |
431 |
- user_config=profile.user_config) |
432 |
- for profile in locations) |
433 |
+ user_config=profile.user_config, eapi=profile.eapi, |
434 |
+ eapi_default=None) for profile in locations) |
435 |
|
436 |
def _parse_repository_usealiases(self, repositories): |
437 |
ret = {} |
438 |
for repo in repositories.repos_with_profiles(): |
439 |
file_name = os.path.join(repo.location, "profiles", "use.aliases") |
440 |
- eapi = read_corresponding_eapi_file(file_name) |
441 |
+ eapi = read_corresponding_eapi_file( |
442 |
+ file_name, default=repo.eapi) |
443 |
useflag_re = _get_useflag_re(eapi) |
444 |
raw_file_dict = grabdict(file_name, recursive=True) |
445 |
file_dict = {} |
446 |
@@ -238,7 +293,8 @@ class UseManager(object): |
447 |
ret = {} |
448 |
for repo in repositories.repos_with_profiles(): |
449 |
file_name = os.path.join(repo.location, "profiles", "package.use.aliases") |
450 |
- eapi = read_corresponding_eapi_file(file_name) |
451 |
+ eapi = read_corresponding_eapi_file( |
452 |
+ file_name, default=repo.eapi) |
453 |
useflag_re = _get_useflag_re(eapi) |
454 |
lines = grabfile(file_name, recursive=True) |
455 |
file_dict = {} |
456 |
diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py |
457 |
index 65de93e..b7dd9ea 100644 |
458 |
--- a/pym/portage/package/ebuild/config.py |
459 |
+++ b/pym/portage/package/ebuild/config.py |
460 |
@@ -564,8 +564,10 @@ class config(object): |
461 |
self.user_profile_dir = locations_manager.user_profile_dir |
462 |
|
463 |
try: |
464 |
- packages_list = [grabfile_package(os.path.join(x, "packages"), |
465 |
- verify_eapi=True) for x in self.profiles] |
466 |
+ packages_list = [grabfile_package( |
467 |
+ os.path.join(x.location, "packages"), |
468 |
+ verify_eapi=True, eapi=x.eapi, eapi_default=None) |
469 |
+ for x in profiles_complex] |
470 |
except IOError as e: |
471 |
if e.errno == IsADirectory.errno: |
472 |
raise IsADirectory(os.path.join(self.profile_path, |
473 |
@@ -758,7 +760,8 @@ class config(object): |
474 |
portage.dep.ExtendedAtomDict(dict) |
475 |
bashrc = grabdict_package(os.path.join(profile.location, |
476 |
"package.bashrc"), recursive=1, allow_wildcard=True, |
477 |
- allow_repo=True, verify_eapi=False) |
478 |
+ allow_repo=True, verify_eapi=True, |
479 |
+ eapi=profile.eapi, eapi_default=None) |
480 |
if not bashrc: |
481 |
continue |
482 |
|
483 |
diff --git a/pym/portage/repository/config.py b/pym/portage/repository/config.py |
484 |
index 9096d73..abc8756 100644 |
485 |
--- a/pym/portage/repository/config.py |
486 |
+++ b/pym/portage/repository/config.py |
487 |
@@ -190,11 +190,9 @@ class RepoConfig(object): |
488 |
location = None |
489 |
self.location = location |
490 |
|
491 |
- eapi = None |
492 |
missing = True |
493 |
self.name = name |
494 |
if self.location is not None: |
495 |
- eapi = read_corresponding_eapi_file(os.path.join(self.location, REPO_NAME_LOC)) |
496 |
self.name, missing = self._read_valid_repo_name(self.location) |
497 |
if missing: |
498 |
# The name from repos.conf has to be used here for |
499 |
@@ -208,7 +206,7 @@ class RepoConfig(object): |
500 |
elif name == "DEFAULT": |
501 |
missing = False |
502 |
|
503 |
- self.eapi = eapi |
504 |
+ self.eapi = None |
505 |
self.missing_repo_name = missing |
506 |
# sign_commit is disabled by default, since it requires Git >=1.7.9, |
507 |
# and key_id configured by `git config user.signingkey key_id` |
508 |
@@ -258,10 +256,26 @@ class RepoConfig(object): |
509 |
'sign-commit', 'sign-manifest', 'thin-manifest', 'update-changelog'): |
510 |
setattr(self, value.lower().replace("-", "_"), layout_data[value]) |
511 |
|
512 |
- self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \ |
513 |
- any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats']) |
514 |
- self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \ |
515 |
- layout_data['profile-formats'] == ('portage-1-compat',) |
516 |
+ # If profile-formats specifies a default EAPI, then set |
517 |
+ # self.eapi to that, otherwise set it to "0" as specified |
518 |
+ # by PMS. |
519 |
+ eapi = [x for x in layout_data['profile-formats'] |
520 |
+ if x.startswith("eapi-")] |
521 |
+ eapi = eapi and eapi[0][len("eapi-"):] |
522 |
+ self.eapi = eapi or "0" |
523 |
+ |
524 |
+ profile_root_eapi = read_corresponding_eapi_file( |
525 |
+ os.path.join(self.location, REPO_NAME_LOC)) |
526 |
+ |
527 |
+ self.portage1_profiles = ((eapi is not None and |
528 |
+ eapi_allows_directories_on_profile_level_and_repository_level(eapi)) or |
529 |
+ eapi_allows_directories_on_profile_level_and_repository_level(profile_root_eapi) or |
530 |
+ any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats'])) |
531 |
+ self.portage1_profiles_compat = ( |
532 |
+ layout_data['profile-formats'] == ('portage-1-compat',) and |
533 |
+ not (eapi is not None and |
534 |
+ eapi_allows_directories_on_profile_level_and_repository_level(eapi)) and |
535 |
+ not eapi_allows_directories_on_profile_level_and_repository_level(profile_root_eapi)) |
536 |
|
537 |
self._eapis_banned = frozenset(layout_data['eapis-banned']) |
538 |
self._eapis_deprecated = frozenset(layout_data['eapis-deprecated']) |
539 |
@@ -1073,6 +1087,14 @@ def parse_layout_conf(repo_location, repo_name=None): |
540 |
else: |
541 |
raw_formats = set(raw_formats.split()) |
542 |
unknown = raw_formats.difference(_valid_profile_formats) |
543 |
+ eapi_formats = tuple(x for x in unknown |
544 |
+ if x.startswith("eapi-") and |
545 |
+ portage.eapi_is_supported(x[len("eapi-"):])) |
546 |
+ raw_formats.intersection_update(_valid_profile_formats) |
547 |
+ raw_formats.update(eapi_formats) |
548 |
+ raw_formats = tuple(raw_formats) |
549 |
+ unknown.difference_update(eapi_formats) |
550 |
+ |
551 |
if unknown: |
552 |
repo_name = _get_repo_name(repo_location, cached=repo_name) |
553 |
warnings.warn((_("Repository named '%(repo_name)s' has unsupported " |
554 |
@@ -1082,7 +1104,16 @@ def parse_layout_conf(repo_location, repo_name=None): |
555 |
layout_filename=layout_filename, |
556 |
unknown_fmts=" ".join(unknown))), |
557 |
DeprecationWarning) |
558 |
- raw_formats = tuple(raw_formats.intersection(_valid_profile_formats)) |
559 |
+ |
560 |
+ if len(eapi_formats) > 1: |
561 |
+ warnings.warn((_("Repository named '%(repo_name)s' has " |
562 |
+ "multiple default eapi settings ('profile-formats = " |
563 |
+ "%(eapi_fmts)s' setting in '%(layout_filename)s") % |
564 |
+ dict(repo_name=repo_name or 'unspecified', |
565 |
+ layout_filename=layout_filename, |
566 |
+ eapi_fmts=" ".join(eapi_formats))), |
567 |
+ SyntaxWarning) |
568 |
+ |
569 |
data['profile-formats'] = raw_formats |
570 |
|
571 |
return data, layout_errors |
572 |
diff --git a/pym/portage/tests/resolver/test_profile_default_eapi.py b/pym/portage/tests/resolver/test_profile_default_eapi.py |
573 |
new file mode 100644 |
574 |
index 0000000..4b4b9d9 |
575 |
--- /dev/null |
576 |
+++ b/pym/portage/tests/resolver/test_profile_default_eapi.py |
577 |
@@ -0,0 +1,123 @@ |
578 |
+# Copyright 2014 Gentoo Foundation |
579 |
+# Distributed under the terms of the GNU General Public License v2 |
580 |
+ |
581 |
+from __future__ import unicode_literals |
582 |
+ |
583 |
+import io |
584 |
+ |
585 |
+from portage import os, _encodings |
586 |
+from portage.const import USER_CONFIG_PATH |
587 |
+from portage.tests import TestCase |
588 |
+from portage.tests.resolver.ResolverPlayground import ResolverPlayground |
589 |
+from portage.dep import ExtendedAtomDict |
590 |
+from portage.util import ensure_dirs |
591 |
+ |
592 |
+class ProfileDefaultEAPITestCase(TestCase): |
593 |
+ |
594 |
+ def testProfileDefaultEAPI(self): |
595 |
+ |
596 |
+ repo_configs = { |
597 |
+ "test_repo": { |
598 |
+ "layout.conf": ("profile-formats = eapi-5",), |
599 |
+ } |
600 |
+ } |
601 |
+ |
602 |
+ profiles = ( |
603 |
+ ( |
604 |
+ "", |
605 |
+ { |
606 |
+ "package.mask": ("sys-libs/A:1",), |
607 |
+ "package.use": ("sys-libs/A:1 flag",) |
608 |
+ } |
609 |
+ ), |
610 |
+ ( |
611 |
+ "default/linux", |
612 |
+ { |
613 |
+ "package.mask": ("sys-libs/B:1",), |
614 |
+ "package.use": ("sys-libs/B:1 flag",), |
615 |
+ "package.keywords": ("sys-libs/B:1 x86",) |
616 |
+ } |
617 |
+ ), |
618 |
+ ( |
619 |
+ "default/linux/x86", |
620 |
+ { |
621 |
+ "package.mask": ("sys-libs/C:1",), |
622 |
+ "package.use": ("sys-libs/C:1 flag",), |
623 |
+ "package.keywords": ("sys-libs/C:1 x86",), |
624 |
+ "parent": ("..",) |
625 |
+ } |
626 |
+ ), |
627 |
+ ) |
628 |
+ |
629 |
+ user_profile = { |
630 |
+ "package.mask": ("sys-libs/D:1",), |
631 |
+ "package.use": ("sys-libs/D:1 flag",), |
632 |
+ "package.keywords": ("sys-libs/D:1 x86",), |
633 |
+ } |
634 |
+ |
635 |
+ test_cases = ( |
636 |
+ (lambda x: x._mask_manager._pmaskdict, { |
637 |
+ "sys-libs/A": ("sys-libs/A:1::test_repo",), |
638 |
+ "sys-libs/B": ("sys-libs/B:1",), |
639 |
+ "sys-libs/C": ("sys-libs/C:1",), |
640 |
+ "sys-libs/D": ("sys-libs/D:1",), |
641 |
+ }), |
642 |
+ (lambda x: x._use_manager._repo_puse_dict, { |
643 |
+ "test_repo": { |
644 |
+ "sys-libs/A": { |
645 |
+ "sys-libs/A:1": ("flag",) |
646 |
+ } |
647 |
+ } |
648 |
+ }), |
649 |
+ (lambda x: x._use_manager._pkgprofileuse, ( |
650 |
+ {"sys-libs/B": {"sys-libs/B:1": "flag"}}, |
651 |
+ {"sys-libs/C": {"sys-libs/C:1": "flag"}}, |
652 |
+ {}, |
653 |
+ {"sys-libs/D": {"sys-libs/D:1": "flag"}}, |
654 |
+ )), |
655 |
+ (lambda x: x._keywords_manager._pkeywords_list, ( |
656 |
+ {"sys-libs/B": {"sys-libs/B:1": ["x86"]}}, |
657 |
+ {"sys-libs/C": {"sys-libs/C:1": ["x86"]}}, |
658 |
+ {"sys-libs/D": {"sys-libs/D:1": ["x86"]}}, |
659 |
+ ) |
660 |
+ ) |
661 |
+ ) |
662 |
+ |
663 |
+ playground = ResolverPlayground(debug=False, |
664 |
+ repo_configs=repo_configs) |
665 |
+ try: |
666 |
+ repo_dir = (playground.settings.repositories. |
667 |
+ get_location_for_name("test_repo")) |
668 |
+ profile_root = os.path.join(repo_dir, "profiles") |
669 |
+ profile_info = [(os.path.join(profile_root, p), data) |
670 |
+ for p, data in profiles] |
671 |
+ profile_info.append((os.path.join(playground.eroot, |
672 |
+ USER_CONFIG_PATH, "profile"), user_profile)) |
673 |
+ |
674 |
+ for prof_path, data in profile_info: |
675 |
+ ensure_dirs(prof_path) |
676 |
+ for k, v in data.items(): |
677 |
+ with io.open(os.path.join(prof_path, k), mode="w", |
678 |
+ encoding=_encodings["repo.content"]) as f: |
679 |
+ for line in v: |
680 |
+ f.write("%s\n" % line) |
681 |
+ |
682 |
+ # The config must be reloaded in order to account |
683 |
+ # for the above profile customizations. |
684 |
+ playground.reload_config() |
685 |
+ |
686 |
+ for fn, expected in test_cases: |
687 |
+ result = self._translate_result(fn(playground.settings)) |
688 |
+ self.assertEqual(result, expected) |
689 |
+ |
690 |
+ finally: |
691 |
+ playground.cleanup() |
692 |
+ |
693 |
+ |
694 |
+ @staticmethod |
695 |
+ def _translate_result(result): |
696 |
+ if isinstance(result, ExtendedAtomDict): |
697 |
+ result = dict(result.items()) |
698 |
+ elif isinstance(result, tuple): |
699 |
+ result = tuple(dict(x.items()) for x in result) |
700 |
+ return result |
701 |
diff --git a/pym/portage/util/__init__.py b/pym/portage/util/__init__.py |
702 |
index d0cca5b..61fe787 100644 |
703 |
--- a/pym/portage/util/__init__.py |
704 |
+++ b/pym/portage/util/__init__.py |
705 |
@@ -425,7 +425,7 @@ def read_corresponding_eapi_file(filename, default="0"): |
706 |
return eapi |
707 |
|
708 |
def grabdict_package(myfilename, juststrings=0, recursive=0, allow_wildcard=False, allow_repo=False, |
709 |
- verify_eapi=False, eapi=None): |
710 |
+ verify_eapi=False, eapi=None, eapi_default="0"): |
711 |
""" Does the same thing as grabdict except it validates keys |
712 |
with isvalidatom()""" |
713 |
|
714 |
@@ -441,7 +441,8 @@ def grabdict_package(myfilename, juststrings=0, recursive=0, allow_wildcard=Fals |
715 |
if not d: |
716 |
continue |
717 |
if verify_eapi and eapi is None: |
718 |
- eapi = read_corresponding_eapi_file(myfilename) |
719 |
+ eapi = read_corresponding_eapi_file( |
720 |
+ myfilename, default=eapi_default) |
721 |
|
722 |
for k, v in d.items(): |
723 |
try: |
724 |
@@ -460,13 +461,15 @@ def grabdict_package(myfilename, juststrings=0, recursive=0, allow_wildcard=Fals |
725 |
return atoms |
726 |
|
727 |
def grabfile_package(myfilename, compatlevel=0, recursive=0, allow_wildcard=False, allow_repo=False, |
728 |
- remember_source_file=False, verify_eapi=False, eapi=None): |
729 |
+ remember_source_file=False, verify_eapi=False, eapi=None, |
730 |
+ eapi_default="0"): |
731 |
|
732 |
pkgs=grabfile(myfilename, compatlevel, recursive=recursive, remember_source_file=True) |
733 |
if not pkgs: |
734 |
return pkgs |
735 |
if verify_eapi and eapi is None: |
736 |
- eapi = read_corresponding_eapi_file(myfilename) |
737 |
+ eapi = read_corresponding_eapi_file( |
738 |
+ myfilename, default=eapi_default) |
739 |
mybasename = os.path.basename(myfilename) |
740 |
atoms = [] |
741 |
for pkg, source_file in pkgs: |
742 |
-- |
743 |
2.0.4 |