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