1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
|
# Copyright 2010-2011 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
__all__ = (
'LocationsManager',
)
import io
import warnings
from portage import os, eapi_is_supported, _encodings, _unicode_encode
from portage.const import CUSTOM_PROFILE_PATH, GLOBAL_CONFIG_PATH, \
PROFILE_PATH, USER_CONFIG_PATH
from portage.exception import DirectoryNotFound, ParseError
from portage.localization import _
from portage.util import ensure_dirs, grabfile, \
normalize_path, shlex_split, writemsg
from portage.repository.config import parse_layout_conf
_PORTAGE1_DIRECTORIES = frozenset([
'package.mask', 'package.provided',
'package.use', 'package.use.mask', 'package.use.force',
'use.mask', 'use.force'])
class LocationsManager(object):
def __init__(self, config_root=None, eprefix=None, config_profile_path=None, local_config=True, \
target_root=None):
self.user_profile_dir = None
self._local_repo_conf_path = None
self.eprefix = eprefix
self.config_root = config_root
self.target_root = target_root
self._user_config = local_config
if self.eprefix is None:
self.eprefix = ""
if self.config_root is None:
self.config_root = self.eprefix + os.sep
self.config_root = normalize_path(os.path.abspath(
self.config_root)).rstrip(os.path.sep) + os.path.sep
self._check_var_directory("PORTAGE_CONFIGROOT", self.config_root)
self.abs_user_config = os.path.join(self.config_root, USER_CONFIG_PATH)
self.config_profile_path = config_profile_path
def load_profiles(self, known_repository_paths):
known_repos = set(os.path.realpath(x) for x in known_repository_paths)
# force a trailing '/' for ease of doing startswith checks
known_repos = tuple((x + '/', parse_layout_conf(x)[0])
for x in known_repos)
if self.config_profile_path is None:
self.config_profile_path = \
os.path.join(self.config_root, PROFILE_PATH)
if os.path.isdir(self.config_profile_path):
self.profile_path = self.config_profile_path
else:
self.config_profile_path = \
os.path.join(self.abs_user_config, 'make.profile')
if os.path.isdir(self.config_profile_path):
self.profile_path = self.config_profile_path
else:
self.profile_path = None
else:
# NOTE: repoman may pass in an empty string
# here, in order to create an empty profile
# for checking dependencies of packages with
# empty KEYWORDS.
self.profile_path = self.config_profile_path
# The symlink might not exist or might not be a symlink.
self.profiles = []
self.profiles_complex = []
if self.profile_path:
try:
self._addProfile(os.path.realpath(self.profile_path),
known_repos)
except ParseError as e:
writemsg(_("!!! Unable to parse profile: '%s'\n") % \
self.profile_path, noiselevel=-1)
writemsg("!!! ParseError: %s\n" % str(e), noiselevel=-1)
self.profiles = []
if self._user_config and self.profiles:
custom_prof = os.path.join(
self.config_root, CUSTOM_PROFILE_PATH)
if os.path.exists(custom_prof):
self.user_profile_dir = custom_prof
self.profiles.append(custom_prof)
self.profiles_complex.append((custom_prof, True))
del custom_prof
self.profiles = tuple(self.profiles)
self.profiles_complex = tuple(self.profiles_complex)
def _check_var_directory(self, varname, var):
if not os.path.isdir(var):
writemsg(_("!!! Error: %s='%s' is not a directory. "
"Please correct this.\n") % (varname, var),
noiselevel=-1)
raise DirectoryNotFound(var)
def _addProfile(self, currentPath, known_repos):
current_abs_path = os.path.abspath(currentPath)
allow_directories = True
repo_loc = None
compat_mode = False
intersecting_repos = [x for x in known_repos if current_abs_path.startswith(x[0])]
if intersecting_repos:
# protect against nested repositories. Insane configuration, but the longest
# path will be the correct one.
repo_loc, layout_data = max(intersecting_repos, key=lambda x:len(x[0]))
allow_directories = any(x.startswith("portage-1")
for x in layout_data['profile-formats'])
compat_mode = layout_data['profile-formats'] == ('portage-1-compat',)
if compat_mode:
offenders = _PORTAGE1_DIRECTORIES.intersection(os.listdir(currentPath))
offenders = sorted(x for x in offenders
if os.path.isdir(os.path.join(currentPath, x)))
if offenders:
warnings.warn((_("Profile '%(profile_path)s' in repository "
"'%(repo_name)s' is implicitly using 'portage-1' profile format, but "
"the repository profiles are not marked as that format. This will break "
"in the future. Please either convert the following paths "
"to files, or add\nprofile-formats = portage-1\nto the "
"repositories layout.conf. Files: '%(files)s'\n")
% dict(profile_path=currentPath, repo_name=repo_loc,
files=', '.join(offenders))),
)
parentsFile = os.path.join(currentPath, "parent")
eapi_file = os.path.join(currentPath, "eapi")
f = None
try:
f = io.open(_unicode_encode(eapi_file,
encoding=_encodings['fs'], errors='strict'),
mode='r', encoding=_encodings['content'], errors='replace')
eapi = f.readline().strip()
except IOError:
pass
else:
if not eapi_is_supported(eapi):
raise ParseError(_(
"Profile contains unsupported "
"EAPI '%s': '%s'") % \
(eapi, os.path.realpath(eapi_file),))
finally:
if f is not None:
f.close()
if os.path.exists(parentsFile):
parents = grabfile(parentsFile)
if not parents:
raise ParseError(
_("Empty parent file: '%s'") % parentsFile)
for parentPath in parents:
abs_parent = parentPath[:1] == os.sep
parentPath = normalize_path(os.path.join(
currentPath, parentPath))
if abs_parent or repo_loc is None or \
not parentPath.startswith(repo_loc):
# It seems that this parent may point outside
# of the current repo, so realpath it.
parentPath = os.path.realpath(parentPath)
if os.path.exists(parentPath):
self._addProfile(parentPath, known_repos)
else:
raise ParseError(
_("Parent '%s' not found: '%s'") % \
(parentPath, parentsFile))
self.profiles.append(currentPath)
self.profiles_complex.append((currentPath, allow_directories))
def set_root_override(self, root_overwrite=None):
# Allow ROOT setting to come from make.conf if it's not overridden
# by the constructor argument (from the calling environment).
if self.target_root is None and root_overwrite is not None:
self.target_root = root_overwrite
if not self.target_root.strip():
self.target_root = None
if self.target_root is None:
self.target_root = "/"
self.target_root = normalize_path(os.path.abspath(
self.target_root)).rstrip(os.path.sep) + os.path.sep
ensure_dirs(self.target_root)
self._check_var_directory("ROOT", self.target_root)
self.eroot = self.target_root.rstrip(os.sep) + self.eprefix + os.sep
# make.globals should not be relative to config_root
# because it only contains constants. However, if EPREFIX
# is set then there are two possible scenarios:
# 1) If $ROOT == "/" then make.globals should be
# relative to EPREFIX.
# 2) If $ROOT != "/" then the correct location of
# make.globals needs to be specified in the constructor
# parameters, since it's a property of the host system
# (and the current config represents the target system).
self.global_config_path = GLOBAL_CONFIG_PATH
if self.eprefix:
if self.target_root == "/":
# case (1) above
self.global_config_path = os.path.join(self.eprefix,
GLOBAL_CONFIG_PATH.lstrip(os.sep))
else:
# case (2) above
# For now, just assume make.globals is relative
# to EPREFIX.
# TODO: Pass in more info to the constructor,
# so we know the host system configuration.
self.global_config_path = os.path.join(self.eprefix,
GLOBAL_CONFIG_PATH.lstrip(os.sep))
def set_port_dirs(self, portdir, portdir_overlay):
self.portdir = portdir
self.portdir_overlay = portdir_overlay
if self.portdir_overlay is None:
self.portdir_overlay = ""
self.overlay_profiles = []
for ov in shlex_split(self.portdir_overlay):
ov = normalize_path(ov)
profiles_dir = os.path.join(ov, "profiles")
if os.path.isdir(profiles_dir):
self.overlay_profiles.append(profiles_dir)
self.profile_locations = [os.path.join(portdir, "profiles")] + self.overlay_profiles
self.profile_and_user_locations = self.profile_locations[:]
if self._user_config:
self.profile_and_user_locations.append(self.abs_user_config)
self.profile_locations = tuple(self.profile_locations)
self.profile_and_user_locations = tuple(self.profile_and_user_locations)
|