Gentoo Archives: gentoo-commits

From: Michael Haubenwallner <haubi@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] repo/proj/prefix:master commit in: sys-apps/portage/files/, sys-apps/portage/
Date: Mon, 28 May 2018 16:44:30
Message-Id: 1527525839.cb20d3f95bd20b528a14126b3d1c545c4ddb1704.haubi@gentoo
1 commit: cb20d3f95bd20b528a14126b3d1c545c4ddb1704
2 Author: Michael Haubenwallner <haubi <AT> gentoo <DOT> org>
3 AuthorDate: Mon May 28 16:40:04 2018 +0000
4 Commit: Michael Haubenwallner <haubi <AT> gentoo <DOT> org>
5 CommitDate: Mon May 28 16:43:59 2018 +0000
6 URL: https://gitweb.gentoo.org/repo/proj/prefix.git/commit/?id=cb20d3f9
7
8 sys-apps/portage: bump ebuildshell+prefix-chaining patches
9
10 Package-Manager: Portage-2.3.24, Repoman-2.3.6
11
12 .../portage/files/portage-2.3.40-ebuildshell.patch | 354 ++++++++
13 .../files/portage-2.3.40-prefix-chaining.patch | 921 +++++++++++++++++++++
14 sys-apps/portage/portage-2.3.40.1.ebuild | 5 +-
15 3 files changed, 1277 insertions(+), 3 deletions(-)
16
17 diff --git a/sys-apps/portage/files/portage-2.3.40-ebuildshell.patch b/sys-apps/portage/files/portage-2.3.40-ebuildshell.patch
18 new file mode 100644
19 index 0000000000..167ed3824e
20 --- /dev/null
21 +++ b/sys-apps/portage/files/portage-2.3.40-ebuildshell.patch
22 @@ -0,0 +1,354 @@
23 +From 9075d30d24af87f69d23ae129dc75e1305cd3aa8 Mon Sep 17 00:00:00 2001
24 +From: Michael Haubenwallner <michael.haubenwallner@×××××××.at>
25 +Date: Wed, 6 Nov 2013 12:40:05 +0100
26 +Subject: [PATCH 1/2] Add ebuildshell feature, bug#155161.
27 +
28 +---
29 + bin/ebuild.sh | 146 ++++++++++++++++++++++++++++++++++-
30 + bin/filter-bash-environment.py | 55 +++++++++----
31 + bin/save-ebuild-env.sh | 2 +-
32 + man/make.conf.5 | 6 ++
33 + pym/_emerge/AbstractEbuildProcess.py | 1 +
34 + pym/portage/const.py | 1 +
35 + 6 files changed, 194 insertions(+), 17 deletions(-)
36 +
37 +diff --git a/bin/ebuild.sh b/bin/ebuild.sh
38 +index f76a48d8e..51ba95cb1 100755
39 +--- a/bin/ebuild.sh
40 ++++ b/bin/ebuild.sh
41 +@@ -121,7 +121,7 @@ __qa_source() {
42 + __qa_call() {
43 + local shopts=$(shopt) OLDIFS="$IFS"
44 + local retval
45 +- "$@"
46 ++ __call-ebuildshell "$@"
47 + retval=$?
48 + set +e
49 + [[ $shopts != $(shopt) ]] &&
50 +@@ -547,6 +547,150 @@ if [[ -n ${QA_INTERCEPTORS} ]] ; then
51 + unset BIN_PATH BIN BODY FUNC_SRC
52 + fi
53 +
54 ++__call-ebuildshell() {
55 ++ if ! has ebuildshell ${FEATURES}; then
56 ++ "$@"
57 ++ return $?
58 ++ fi
59 ++ local __ebuildshell_args=( "$@" )
60 ++ # These are the variables I have seen 'bash -i' maintaining the values for:
61 ++ local __ebuildshell_bash_i_vars="__ebuildshell_.*
62 ++ _ BASH_ARGC BASH_ARGV BASH_COMMAND BASH_LINENO BASH_SOURCE
63 ++ BASH_VERSINFO BASH_SUBSHELL BASHOPTS BASHPID COMP_WORDBREAKS
64 ++ DIRSTACK EUID FUNCNAME GROUPS HISTCMD HISTFILE LINENO PIPESTATUS
65 ++ PPID PS1 PS2 PS3 PS4 PWD RANDOM SECONDS SHELLOPTS UID"
66 ++ # Allow recursive ebuildshell, for use in multibuild.eclass and similar:
67 ++ local __ebuildshell_pid=${BASHPID:-$(__bashpid)}
68 ++ local __ebuildshell_tmpf="${T}/ebuildshell.${__ebuildshell_pid}"
69 ++ rm -f "${__ebuildshell_tmpf}."{ebuild,return}-{env,rovars}
70 ++ (
71 ++ cat <<-EOE
72 ++ # local variables of functions using recursive ebuildshell are
73 ++ # visible to the EXIT trap of that recursive ebuildshell. To
74 ++ # keep them local, we have to filter them from that recursive
75 ++ # ebuildshell's return-env. As 'declare -p' is unable to tell
76 ++ # local-ity of variables, we abuse the trace attribute for local
77 ++ # variables to filter them from the return-env. So we need the
78 ++ # local alias active before declaring any functions.
79 ++ # On a sidehand, this allows for copy&paste of function body
80 ++ # lines including the local keyword.
81 ++ alias local='declare -t'
82 ++ shopt -s expand_aliases
83 ++ EOE
84 ++ (
85 ++ declare -p
86 ++ declare -fp
87 ++ shopt -p
88 ++ [[ ${BASH_VERSINFO[0]} == 3 ]] && export
89 ++ ) |
90 ++ (
91 ++ # we need everything but the bash vars after 'env -i'
92 ++ 2>"${__ebuildshell_tmpf}.ebuild-rovars" \
93 ++ "${PORTAGE_PYTHON:-/tools/haubi/gentoo/s01en24/usr/bin/python}" \
94 ++ "${PORTAGE_BIN_PATH}"/filter-bash-environment.py \
95 ++ --report-readonly-variables \
96 ++ --preserve-readonly-attribute \
97 ++ "${__ebuildshell_bash_i_vars}" \
98 ++ || die "filter-bash-environment.py failed"
99 ++ )
100 ++ # 'declare -g' is available since bash-4.2,
101 ++ # https://bugs.gentoo.org/show_bug.cgi?id=155161#c35
102 ++ if (( ${BASH_VERSINFO[0]} > 4 )) ||
103 ++ (( ${BASH_VERSINFO[0]} == 4 && ${BASH_VERSINFO[1]} >= 2 ))
104 ++ then
105 ++ __ebuildshell_bash42_true=
106 ++ __ebuildshell_bash42_false='#bash-4.2#'
107 ++ else
108 ++ __ebuildshell_bash42_true='#bash-4.2#'
109 ++ __ebuildshell_bash42_false=
110 ++ fi
111 ++ # The already readonly variables, without bash maintained ones:
112 ++ __ebuildshell_ro_ebuild_vars=$(<"${__ebuildshell_tmpf}.ebuild-rovars")
113 ++ cat <<-EOE
114 ++ # properly quote the function arguments
115 ++ $(declare -p __ebuildshell_args)
116 ++ set -- "\${__ebuildshell_args[@]}"
117 ++ unset __ebuildshell_args
118 ++ # be informative about what to do
119 ++ PS1="EBUILD ${PN} $1 \$ "
120 ++ type $1
121 ++ ${__ebuildshell_bash42_false}echo 'warning: preserving variables across phases requires bash-4.2'
122 ++ echo "WANTED: \$@"
123 ++ echo "or use: \"\\\$@\""
124 ++ # use bash history, but not the 'user's real one
125 ++ HISTFILE=~/.bash_history
126 ++ # but do not use history-expansion with '!',
127 ++ # for copy&paste of function body lines containing: !
128 ++ set +H
129 ++ # this is a debugging shell already
130 ++ shopt -u extdebug
131 ++ trap - DEBUG
132 ++ # at exit, dump the current environment
133 ++ trap "
134 ++ unalias local
135 ++ unset -f __call-ebuildshell
136 ++ rm -f '${__ebuildshell_tmpf}.return-'*
137 ++ (
138 ++ (
139 ++ # declare -p does not tell the -g flag,
140 ++ # so we add it by aliasing declare.
141 ++ ${__ebuildshell_bash42_true}echo \"alias declare='declare -g'\"
142 ++ declare -p
143 ++ ${__ebuildshell_bash42_true}echo \"unalias declare\"
144 ++ declare -fp
145 ++ shopt -p | grep -v '\\(expand_aliases\\|extdebug\\)$'
146 ++ $([[ ${BASH_VERSINFO[0]} == 3 ]] && echo export)
147 ++ ) |
148 ++ (
149 ++ # We may have more readonly variables now, yet we
150 ++ # need to filter variables that were readonly before.
151 ++ # And filter local variables by their trace attribute.
152 ++ 2>'${__ebuildshell_tmpf}.return-rovars' \\
153 ++ '${PORTAGE_PYTHON:-/tools/haubi/gentoo/s01en24/usr/bin/python}' \\
154 ++ '${PORTAGE_BIN_PATH}'/filter-bash-environment.py \\
155 ++ --report-readonly-variables \\
156 ++ --preserve-readonly-attribute \\
157 ++ --filter-traced-variables \\
158 ++ '${__ebuildshell_bash_i_vars} \
159 ++ ${__ebuildshell_ro_ebuild_vars}' \\
160 ++ || die 'filter-bash-environment.py failed'
161 ++ )
162 ++ ) > '${__ebuildshell_tmpf}.return-env'
163 ++ " EXIT
164 ++ # can do some cleanup right now
165 ++ rm -f '${__ebuildshell_tmpf}.ebuild-'*
166 ++ EOE
167 ++ ) > "${__ebuildshell_tmpf}.ebuild-env"
168 ++
169 ++ # pre-fill the history with "$@"
170 ++ echo '"$@"' >> ~/.bash_history
171 ++ chown ${PORTAGE_USER:-portage}:${PORTAGE_GROUP:-portage} ~/.bash_history &>/dev/null
172 ++
173 ++ env -i ${BASH} --rcfile "${__ebuildshell_tmpf}.ebuild-env" -i
174 ++
175 ++ # The environment- and exit-status handling after leaving the ebuildshell
176 ++ # prompt is expected to be identical as without the ebuildshell prompt.
177 ++ local __ebuildshell_status=$?
178 ++
179 ++ # We might be in a recursive ebuildshell, but do not want
180 ++ # any aliases being active while sourcing the return-env.
181 ++ local __ebuildshell_orig_aliases=$(alias)
182 ++ unalias -a
183 ++ source "${__ebuildshell_tmpf}.return-env"
184 ++ unalias -a
185 ++ eval "${__ebuildshell_orig_aliases}"
186 ++
187 ++ # Portage has a whitelist of readonly variables: If an ebuild defines
188 ++ # additional readonly variables, their readonly attribute is removed
189 ++ # across ebuild phases. If we ever want to preserve the readonly
190 ++ # attribute of additional ebuild-defined variables across phases,
191 ++ # when returning from the ebuildshell their names are in
192 ++ # "${__ebuildshell_tmpf}.return-rovars"
193 ++ rm -f "${__ebuildshell_tmpf}."{ebuild,return}-{env,rovars}
194 ++
195 ++ return ${__ebuildshell_status}
196 ++}
197 ++
198 + # Subshell/helper die support (must export for the die helper).
199 + export EBUILD_MASTER_PID=${BASHPID:-$(__bashpid)}
200 + trap 'exit 1' SIGTERM
201 +diff --git a/bin/filter-bash-environment.py b/bin/filter-bash-environment.py
202 +index 06cac7214..5590dbfc4 100755
203 +--- a/bin/filter-bash-environment.py
204 ++++ b/bin/filter-bash-environment.py
205 +@@ -12,7 +12,8 @@ func_end_re = re.compile(br'^\}$')
206 +
207 + var_assign_re = re.compile(br'(^|^declare\s+-\S+\s+|^declare\s+|^export\s+)([^=\s]+)=("|\')?.*$')
208 + close_quote_re = re.compile(br'(\\"|"|\')\s*$')
209 +-readonly_re = re.compile(br'^declare\s+-(\S*)r(\S*)\s+')
210 ++readonly_re = re.compile(br'^declare\s+-(\S*)r(\S*)\s+([^=\s]+)')
211 ++trace_re = re.compile(br'^declare\s+-\S*t\S*\s+')
212 + # declare without assignment
213 + var_declare_re = re.compile(br'^declare(\s+-\S+)?\s+([^=\s]+)\s*$')
214 +
215 +@@ -27,7 +28,7 @@ def have_end_quote(quote, line):
216 + return close_quote_match is not None and \
217 + close_quote_match.group(1) == quote
218 +
219 +-def filter_declare_readonly_opt(line):
220 ++def filter_declare_readonly_opt(line, options):
221 + readonly_match = readonly_re.match(line)
222 + if readonly_match is not None:
223 + declare_opts = b''
224 +@@ -35,14 +36,19 @@ def filter_declare_readonly_opt(line):
225 + group = readonly_match.group(i)
226 + if group is not None:
227 + declare_opts += group
228 ++ var = readonly_match.group(3)
229 ++ if '--report-readonly-variables' in options:
230 ++ getattr(sys.stderr, 'buffer', sys.stderr).write(var + b'\n')
231 ++ if '--preserve-readonly-attribute' in options:
232 ++ declare_opts += b'r'
233 + if declare_opts:
234 + line = b'declare -' + declare_opts + \
235 +- b' ' + line[readonly_match.end():]
236 ++ b' ' + var + line[readonly_match.end():]
237 + else:
238 +- line = b'declare ' + line[readonly_match.end():]
239 ++ line = b'declare ' + var + line[readonly_match.end():]
240 + return line
241 +
242 +-def filter_bash_environment(pattern, file_in, file_out):
243 ++def filter_bash_environment(pattern, file_in, file_out, options):
244 + # Filter out any instances of the \1 character from variable values
245 + # since this character multiplies each time that the environment
246 + # is saved (strange bash behavior). This can eventually result in
247 +@@ -66,6 +72,8 @@ def filter_bash_environment(pattern, file_in, file_out):
248 + quote = var_assign_match.group(3)
249 + filter_this = pattern.match(var_assign_match.group(2)) \
250 + is not None
251 ++ if not filter_this and '--filter-traced-variables' in options:
252 ++ filter_this = trace_re.match(line) is not None
253 + # Exclude the start quote when searching for the end quote,
254 + # to ensure that the start quote is not misidentified as the
255 + # end quote (happens if there is a newline immediately after
256 +@@ -75,7 +83,7 @@ def filter_bash_environment(pattern, file_in, file_out):
257 + multi_line_quote = quote
258 + multi_line_quote_filter = filter_this
259 + if not filter_this:
260 +- line = filter_declare_readonly_opt(line)
261 ++ line = filter_declare_readonly_opt(line, options)
262 + file_out.write(line.replace(b"\1", b""))
263 + continue
264 + else:
265 +@@ -84,8 +92,10 @@ def filter_bash_environment(pattern, file_in, file_out):
266 + # declare without assignment
267 + filter_this = pattern.match(declare_match.group(2)) \
268 + is not None
269 ++ if not filter_this and '--filter-traced-variables' in options:
270 ++ filter_this = trace_re.match(line) is not None
271 + if not filter_this:
272 +- line = filter_declare_readonly_opt(line)
273 ++ line = filter_declare_readonly_opt(line, options)
274 + file_out.write(line)
275 + continue
276 +
277 +@@ -122,13 +132,28 @@ if __name__ == "__main__":
278 + "while leaving bash function definitions and here-documents " + \
279 + "intact. The PATTERN is a space separated list of variable names" + \
280 + " and it supports python regular expression syntax."
281 +- usage = "usage: %s PATTERN" % os.path.basename(sys.argv[0])
282 +- args = sys.argv[1:]
283 +-
284 +- if '-h' in args or '--help' in args:
285 +- sys.stdout.write(usage + "\n")
286 +- sys.stdout.flush()
287 +- sys.exit(os.EX_OK)
288 ++ usage = "usage: %s [-h|<options>] PATTERN" % os.path.basename(sys.argv[0])
289 ++ args = []
290 ++ known_options = {
291 ++ '--report-readonly-variables':
292 ++ "Write names of readonly variables to stderr.",
293 ++ '--preserve-readonly-attribute':
294 ++ "Preserve the '-r' flag in 'declare -r'.",
295 ++ '--filter-traced-variables':
296 ++ "Filter out variables declared with '-t' attribute."
297 ++ }
298 ++ options = {}
299 ++ for arg in sys.argv[1:]:
300 ++ if arg in known_options.keys():
301 ++ options[arg] = True
302 ++ continue
303 ++ if '-h' == arg or '--help' == arg:
304 ++ sys.stdout.write(usage + "\n\nKnown <options>:\n\n")
305 ++ for option, descr in known_options.items():
306 ++ sys.stdout.write(" " + option + "\t" + descr + "\n")
307 ++ sys.stdout.flush()
308 ++ sys.exit(os.EX_OK)
309 ++ args.append(arg)
310 +
311 + if len(args) != 1:
312 + sys.stderr.write(usage + "\n")
313 +@@ -151,5 +176,5 @@ if __name__ == "__main__":
314 +
315 + var_pattern = b'^(' + b'|'.join(var_pattern) + b')$'
316 + filter_bash_environment(
317 +- re.compile(var_pattern), file_in, file_out)
318 ++ re.compile(var_pattern), file_in, file_out, options)
319 + file_out.flush()
320 +diff --git a/bin/save-ebuild-env.sh b/bin/save-ebuild-env.sh
321 +index bb17382d4..af35a3327 100755
322 +--- a/bin/save-ebuild-env.sh
323 ++++ b/bin/save-ebuild-env.sh
324 +@@ -53,7 +53,7 @@ __save_ebuild_env() {
325 + einfo einfon ewarn eerror ebegin __eend eend KV_major \
326 + KV_minor KV_micro KV_to_int get_KV has \
327 + __has_phase_defined_up_to \
328 +- hasv hasq __qa_source __qa_call \
329 ++ hasv hasq __qa_source __qa_call __call-ebuildshell \
330 + addread addwrite adddeny addpredict __sb_append_var \
331 + use usev useq has_version portageq \
332 + best_version use_with use_enable register_die_hook \
333 +diff --git a/man/make.conf.5 b/man/make.conf.5
334 +index b0c1aa4f2..568f350a0 100644
335 +--- a/man/make.conf.5
336 ++++ b/man/make.conf.5
337 +@@ -408,6 +408,12 @@ exist). Also see the related \fIunmerge\-backup\fR feature.
338 + Use locks to ensure that unsandboxed ebuild phases never execute
339 + concurrently. Also see \fIparallel\-install\fR.
340 + .TP
341 ++.B ebuildshell
342 ++Drop into an interactive shell for each phase function, meant for
343 ++debugging. Because the shell would normally be used to execute the
344 ++phase function, commands like src_unpack or epatch are available in the
345 ++interactive shell. Use `die` to terminate the merge.
346 ++.TP
347 + .B fail\-clean
348 + Clean up temporary files after a build failure. This is particularly useful
349 + if you have \fBPORTAGE_TMPDIR\fR on tmpfs. If this feature is enabled, you
350 +diff --git a/pym/_emerge/AbstractEbuildProcess.py b/pym/_emerge/AbstractEbuildProcess.py
351 +index 370cac529..a521596e5 100644
352 +--- a/pym/_emerge/AbstractEbuildProcess.py
353 ++++ b/pym/_emerge/AbstractEbuildProcess.py
354 +@@ -181,6 +181,7 @@ class AbstractEbuildProcess(SpawnProcess):
355 + self.fd_pipes = {}
356 + null_fd = None
357 + if 0 not in self.fd_pipes and \
358 ++ "ebuildshell" not in self.settings.features and \
359 + self.phase not in self._phases_interactive_whitelist and \
360 + "interactive" not in self.settings.get("PROPERTIES", "").split():
361 + null_fd = os.open('/dev/null', os.O_RDONLY)
362 +diff --git a/pym/portage/const.py b/pym/portage/const.py
363 +index 3c23c85ed..d9c57f300 100644
364 +--- a/pym/portage/const.py
365 ++++ b/pym/portage/const.py
366 +@@ -161,6 +161,7 @@ SUPPORTED_FEATURES = frozenset([
367 + "distlocks",
368 + "downgrade-backup",
369 + "ebuild-locks",
370 ++ "ebuildshell",
371 + "fail-clean",
372 + "fakeroot",
373 + "fixlafiles",
374 +--
375 +2.16.1
376 +
377
378 diff --git a/sys-apps/portage/files/portage-2.3.40-prefix-chaining.patch b/sys-apps/portage/files/portage-2.3.40-prefix-chaining.patch
379 new file mode 100644
380 index 0000000000..8e0864990d
381 --- /dev/null
382 +++ b/sys-apps/portage/files/portage-2.3.40-prefix-chaining.patch
383 @@ -0,0 +1,921 @@
384 +From 9c991762d6becb779925d59289eb0324f269ad18 Mon Sep 17 00:00:00 2001
385 +From: Michael Haubenwallner <haubi@g.o>
386 +Date: Thu, 23 Mar 2017 13:52:32 +0100
387 +Subject: [PATCH 2/2] add prefix-chaining support
388 +
389 +---
390 + bin/install-qa-check.d/05prefix | 30 ++++++-
391 + bin/phase-helpers.sh | 24 ++++++
392 + pym/_emerge/actions.py | 6 +-
393 + pym/_emerge/depgraph.py | 53 +++++++-----
394 + pym/_emerge/resolver/output.py | 40 ++++++++-
395 + pym/portage/_sets/__init__.py | 5 ++
396 + pym/portage/const.py | 6 ++
397 + pym/portage/dbapi/vartree.py | 34 ++++++--
398 + pym/portage/dep/dep_check.py | 99 +++++++++++++++++++++-
399 + .../package/ebuild/_config/LocationsManager.py | 3 +
400 + pym/portage/package/ebuild/config.py | 62 ++++++++++++++
401 + pym/portage/package/ebuild/doebuild.py | 24 +++++-
402 + pym/portage/package/ebuild/fetch.py | 4 +
403 + pym/portage/sync/controller.py | 27 +++---
404 + pym/portage/util/_dyn_libs/LinkageMapELF.py | 4 +-
405 + 15 files changed, 373 insertions(+), 48 deletions(-)
406 +
407 +diff --git a/bin/install-qa-check.d/05prefix b/bin/install-qa-check.d/05prefix
408 +index 32561e263..0c1147367 100644
409 +--- a/bin/install-qa-check.d/05prefix
410 ++++ b/bin/install-qa-check.d/05prefix
411 +@@ -79,16 +79,42 @@ install_qa_check_prefix() {
412 + # unprefixed shebang, is the script directly in $PATH or an init
413 + # script?
414 + if [[ ":${PATH}:${EPREFIX}/etc/init.d:" == *":${fp}:"* ]] ; then
415 +- if [[ -e ${EROOT}${line[0]} || -e ${ED}${line[0]} ]] ; then
416 ++ all_epfs="$PORTAGE_READONLY_EPREFIXES:$EPREFIX:$EROOT:$ED"
417 ++ save_IFS=$IFS
418 ++ IFS=:
419 ++ epfs=( $all_epfs )
420 ++ IFS=$save_IFS
421 ++
422 ++ found=
423 ++ for x in "${epfs[@]}"; do
424 ++ [[ -z "${x}" ]] && continue
425 ++ check="${x}${line[0]}"
426 ++
427 ++ # might already contain a prefix
428 ++ if [[ "${line[0]}" == "${x}"* ]]; then
429 ++ check="${line[0]}"
430 ++ fi
431 ++
432 ++ if [[ -e ${check} ]]; then
433 ++ found="${check}"
434 ++ fi
435 ++ done
436 ++
437 ++ if [[ -n ${found} ]] ; then
438 + # is it unprefixed, but we can just fix it because a
439 + # prefixed variant exists
440 + eqawarn "prefixing shebang of ${fn#${D}}"
441 ++
442 ++ if [[ ${found} == "${ED}"* || ${found} == "${EROOT}"* ]]; then
443 ++ found="${EPREFIX}${line[0]}"
444 ++ fi
445 ++
446 + # statement is made idempotent on purpose, because
447 + # symlinks may point to the same target, and hence the
448 + # same real file may be sedded multiple times since we
449 + # read the shebangs in one go upfront for performance
450 + # reasons
451 +- sed -i -e '1s:^#! \?'"${line[0]}"':#!'"${EPREFIX}"${line[0]}':' "${rf}"
452 ++ sed -i -e '1s:^#! \?'"${line[0]}"':#!'"${found}"':' "${rf}"
453 + continue
454 + else
455 + # this is definitely wrong: script in $PATH and invalid shebang
456 +diff --git a/bin/phase-helpers.sh b/bin/phase-helpers.sh
457 +index 75d92b407..c32533fb3 100644
458 +--- a/bin/phase-helpers.sh
459 ++++ b/bin/phase-helpers.sh
460 +@@ -934,6 +934,10 @@ ___best_version_and_has_version_common() {
461 + fi
462 + "${cmd[@]}"
463 + local retval=$?
464 ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
465 ++ ${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' '${FUNCNAME[1]}' '${READONLY_EPREFIX%:*}' '${atom}'"
466 ++ retval=$?
467 ++ fi
468 + case "${retval}" in
469 + 0|1)
470 + return ${retval}
471 +@@ -1194,6 +1198,10 @@ if ___eapi_has_master_repositories; then
472 + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" master_repositories "${EROOT}" "${repository}")
473 + fi
474 + retval=$?
475 ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
476 ++ output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' master_repositories '${READONLY_EPREFIX%:*}' '${repository}'")
477 ++ retval=$?
478 ++ fi
479 + [[ -n ${output} ]] && echo "${output}"
480 + case "${retval}" in
481 + 0|1)
482 +@@ -1225,6 +1233,10 @@ if ___eapi_has_repository_path; then
483 + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" get_repo_path "${EROOT}" "${repository}")
484 + fi
485 + retval=$?
486 ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
487 ++ output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' get_repo_path '${READONLY_EPREFIX%:*}' '${repository}'")
488 ++ retval=$?
489 ++ fi
490 + [[ -n ${output} ]] && echo "${output}"
491 + case "${retval}" in
492 + 0|1)
493 +@@ -1255,6 +1267,10 @@ if ___eapi_has_available_eclasses; then
494 + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" available_eclasses "${EROOT}" "${repository}")
495 + fi
496 + retval=$?
497 ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
498 ++ output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' available_eclasses '${READONLY_EPREFIX%:*}' '${repository}'")
499 ++ retval=$?
500 ++ fi
501 + [[ -n ${output} ]] && echo "${output}"
502 + case "${retval}" in
503 + 0|1)
504 +@@ -1285,6 +1301,10 @@ if ___eapi_has_eclass_path; then
505 + else
506 + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" eclass_path "${EROOT}" "${repository}" "${eclass}")
507 + fi
508 ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
509 ++ output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' eclass_path '${READONLY_EPREFIX%:*}' '${repository}' '${eclass}'")
510 ++ retval=$?
511 ++ fi
512 + retval=$?
513 + [[ -n ${output} ]] && echo "${output}"
514 + case "${retval}" in
515 +@@ -1316,6 +1336,10 @@ if ___eapi_has_license_path; then
516 + else
517 + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" license_path "${EROOT}" "${repository}" "${license}")
518 + fi
519 ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
520 ++ output=(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' license_path '${READONLY_EPREFIX%:*}' '${repository}' '${license}'")
521 ++ retval=$?
522 ++ fi
523 + retval=$?
524 + [[ -n ${output} ]] && echo "${output}"
525 + case "${retval}" in
526 +diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py
527 +index 432fc57e3..764462fc5 100644
528 +--- a/pym/_emerge/actions.py
529 ++++ b/pym/_emerge/actions.py
530 +@@ -39,7 +39,7 @@ from portage import os
531 + from portage import shutil
532 + from portage import eapi_is_supported, _encodings, _unicode_decode
533 + from portage.cache.cache_errors import CacheError
534 +-from portage.const import EPREFIX
535 ++from portage.const import EPREFIX, BPREFIX
536 + from portage.const import GLOBAL_CONFIG_PATH, VCS_DIRS, _DEPCLEAN_LIB_CHECK_DEFAULT
537 + from portage.const import SUPPORTED_BINPKG_FORMATS, TIMESTAMP_FORMAT
538 + from portage.dbapi.dep_expand import dep_expand
539 +@@ -65,6 +65,7 @@ from portage.util.SlotObject import SlotObject
540 + from portage.util._async.run_main_scheduler import run_main_scheduler
541 + from portage.util._async.SchedulerInterface import SchedulerInterface
542 + from portage.util._eventloop.global_event_loop import global_event_loop
543 ++from portage.util._path import exists_raise_eaccess
544 + from portage._global_updates import _global_updates
545 + from portage.sync.old_tree_timestamp import old_tree_timestamp_warn
546 + from portage.localization import _
547 +@@ -2672,6 +2673,9 @@ def missing_sets_warning(root_config, missing_sets):
548 + if portage.const.EPREFIX:
549 + global_config_path = os.path.join(portage.const.EPREFIX,
550 + portage.const.GLOBAL_CONFIG_PATH.lstrip(os.sep))
551 ++ if not exists_raise_eaccess(global_config_path) and portage.const.BPREFIX:
552 ++ global_config_path = os.path.join(portage.const.BPREFIX,
553 ++ portage.const.GLOBAL_CONFIG_PATH.lstrip(os.sep))
554 + msg.append(" This usually means that '%s'" % \
555 + (os.path.join(global_config_path, "sets/portage.conf"),))
556 + msg.append(" is missing or corrupt.")
557 +diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
558 +index f7bac69f9..a6eb0d3d4 100644
559 +--- a/pym/_emerge/depgraph.py
560 ++++ b/pym/_emerge/depgraph.py
561 +@@ -3355,19 +3355,19 @@ class depgraph(object):
562 + # _dep_disjunctive_stack first, so that choices for build-time
563 + # deps influence choices for run-time deps (bug 639346).
564 + deps = (
565 +- (myroot, edepend["RDEPEND"],
566 ++ (myroot, "RDEPEND",
567 + self._priority(runtime=True)),
568 +- (myroot, edepend["PDEPEND"],
569 ++ (myroot, "PDEPEND",
570 + self._priority(runtime_post=True)),
571 +- (depend_root, edepend["DEPEND"],
572 ++ (depend_root, "DEPEND",
573 + self._priority(buildtime=True,
574 + optional=(pkg.built or ignore_depend_deps),
575 + ignored=ignore_depend_deps)),
576 +- (self._frozen_config._running_root.root, edepend["HDEPEND"],
577 ++ (self._frozen_config._running_root.root, "HDEPEND",
578 + self._priority(buildtime=True,
579 + optional=(pkg.built or ignore_hdepend_deps),
580 + ignored=ignore_hdepend_deps)),
581 +- (self._frozen_config._running_root.root, edepend["BDEPEND"],
582 ++ (self._frozen_config._running_root.root, "BDEPEND",
583 + self._priority(buildtime=True,
584 + optional=(pkg.built or ignore_bdepend_deps),
585 + ignored=ignore_bdepend_deps)),
586 +@@ -3375,7 +3375,8 @@ class depgraph(object):
587 +
588 + debug = "--debug" in self._frozen_config.myopts
589 +
590 +- for dep_root, dep_string, dep_priority in deps:
591 ++ for dep_root, dep_type, dep_priority in deps:
592 ++ dep_string = edepend[dep_type]
593 + if not dep_string:
594 + continue
595 + if debug:
596 +@@ -3413,7 +3414,7 @@ class depgraph(object):
597 +
598 + try:
599 + dep_string = list(self._queue_disjunctive_deps(
600 +- pkg, dep_root, dep_priority, dep_string))
601 ++ pkg, dep_root, dep_priority, dep_string, dep_type))
602 + except portage.exception.InvalidDependString as e:
603 + if pkg.installed:
604 + self._dynamic_config._masked_installed.add(pkg)
605 +@@ -3428,14 +3429,14 @@ class depgraph(object):
606 +
607 + if not self._add_pkg_dep_string(
608 + pkg, dep_root, dep_priority, dep_string,
609 +- allow_unsatisfied):
610 ++ allow_unsatisfied, dep_type):
611 + return 0
612 +
613 + self._dynamic_config._traversed_pkg_deps.add(pkg)
614 + return 1
615 +
616 + def _add_pkg_dep_string(self, pkg, dep_root, dep_priority, dep_string,
617 +- allow_unsatisfied):
618 ++ allow_unsatisfied, dep_type=None):
619 + _autounmask_backup = self._dynamic_config._autounmask
620 + if dep_priority.optional or dep_priority.ignored:
621 + # Temporarily disable autounmask for deps that
622 +@@ -3444,7 +3445,7 @@ class depgraph(object):
623 + try:
624 + return self._wrapped_add_pkg_dep_string(
625 + pkg, dep_root, dep_priority, dep_string,
626 +- allow_unsatisfied)
627 ++ allow_unsatisfied, dep_type)
628 + finally:
629 + self._dynamic_config._autounmask = _autounmask_backup
630 +
631 +@@ -3480,7 +3481,7 @@ class depgraph(object):
632 + not slot_operator_rebuild
633 +
634 + def _wrapped_add_pkg_dep_string(self, pkg, dep_root, dep_priority,
635 +- dep_string, allow_unsatisfied):
636 ++ dep_string, allow_unsatisfied, dep_type=None):
637 + if isinstance(pkg.depth, int):
638 + depth = pkg.depth + 1
639 + else:
640 +@@ -3504,7 +3505,7 @@ class depgraph(object):
641 + try:
642 + selected_atoms = self._select_atoms(dep_root,
643 + dep_string, myuse=self._pkg_use_enabled(pkg), parent=pkg,
644 +- strict=strict, priority=dep_priority)
645 ++ strict=strict, priority=dep_priority, dep_type=dep_type)
646 + except portage.exception.InvalidDependString:
647 + if pkg.installed:
648 + self._dynamic_config._masked_installed.add(pkg)
649 +@@ -3811,7 +3812,7 @@ class depgraph(object):
650 + child_pkgs.sort()
651 + yield (atom, child_pkgs[-1])
652 +
653 +- def _queue_disjunctive_deps(self, pkg, dep_root, dep_priority, dep_struct):
654 ++ def _queue_disjunctive_deps(self, pkg, dep_root, dep_priority, dep_struct, dep_type=None):
655 + """
656 + Queue disjunctive (virtual and ||) deps in self._dynamic_config._dep_disjunctive_stack.
657 + Yields non-disjunctive deps. Raises InvalidDependString when
658 +@@ -3820,33 +3821,33 @@ class depgraph(object):
659 + for x in dep_struct:
660 + if isinstance(x, list):
661 + if x and x[0] == "||":
662 +- self._queue_disjunction(pkg, dep_root, dep_priority, [x])
663 ++ self._queue_disjunction(pkg, dep_root, dep_priority, [x], dep_type)
664 + else:
665 + for y in self._queue_disjunctive_deps(
666 +- pkg, dep_root, dep_priority, x):
667 ++ pkg, dep_root, dep_priority, x, dep_type):
668 + yield y
669 + else:
670 + # Note: Eventually this will check for PROPERTIES=virtual
671 + # or whatever other metadata gets implemented for this
672 + # purpose.
673 + if x.cp.startswith('virtual/'):
674 +- self._queue_disjunction(pkg, dep_root, dep_priority, [x])
675 ++ self._queue_disjunction(pkg, dep_root, dep_priority, [x], dep_type)
676 + else:
677 + yield x
678 +
679 +- def _queue_disjunction(self, pkg, dep_root, dep_priority, dep_struct):
680 ++ def _queue_disjunction(self, pkg, dep_root, dep_priority, dep_struct, dep_type=None):
681 + self._dynamic_config._dep_disjunctive_stack.append(
682 +- (pkg, dep_root, dep_priority, dep_struct))
683 ++ (pkg, dep_root, dep_priority, dep_struct, dep_type))
684 +
685 + def _pop_disjunction(self, allow_unsatisfied):
686 + """
687 + Pop one disjunctive dep from self._dynamic_config._dep_disjunctive_stack, and use it to
688 + populate self._dynamic_config._dep_stack.
689 + """
690 +- pkg, dep_root, dep_priority, dep_struct = \
691 ++ pkg, dep_root, dep_priority, dep_struct, dep_type = \
692 + self._dynamic_config._dep_disjunctive_stack.pop()
693 + if not self._add_pkg_dep_string(
694 +- pkg, dep_root, dep_priority, dep_struct, allow_unsatisfied):
695 ++ pkg, dep_root, dep_priority, dep_struct, allow_unsatisfied, dep_type):
696 + return 0
697 + return 1
698 +
699 +@@ -4699,7 +4700,7 @@ class depgraph(object):
700 + return self._select_atoms_highest_available(*pargs, **kwargs)
701 +
702 + def _select_atoms_highest_available(self, root, depstring,
703 +- myuse=None, parent=None, strict=True, trees=None, priority=None):
704 ++ myuse=None, parent=None, strict=True, trees=None, priority=None, dep_type=None):
705 + """This will raise InvalidDependString if necessary. If trees is
706 + None then self._dynamic_config._filtered_trees is used."""
707 +
708 +@@ -4722,6 +4723,13 @@ class depgraph(object):
709 + pkgsettings = self._frozen_config.pkgsettings[root]
710 + if trees is None:
711 + trees = self._dynamic_config._filtered_trees
712 ++
713 ++ # this one is needed to guarantee good readonly root
714 ++ # resolution display in the merge list. required since
715 ++ # parent (below) can be None
716 ++ trees[root]["disp_parent"] = parent
717 ++
718 ++
719 + mytrees = trees[root]
720 + atom_graph = digraph()
721 + if True:
722 +@@ -4753,7 +4761,7 @@ class depgraph(object):
723 +
724 + mycheck = portage.dep_check(depstring, None,
725 + pkgsettings, myuse=myuse,
726 +- myroot=root, trees=trees)
727 ++ myroot=root, trees=trees, dep_type=dep_type)
728 + finally:
729 + # restore state
730 + self._dynamic_config._autounmask = _autounmask_backup
731 +@@ -4829,6 +4837,7 @@ class depgraph(object):
732 + continue
733 + node_stack.append((child_node, node, child_atom))
734 +
735 ++ trees[root].pop("disp_parent")
736 + return selected_atoms
737 +
738 + def _expand_virt_from_graph(self, root, atom):
739 +diff --git a/pym/_emerge/resolver/output.py b/pym/_emerge/resolver/output.py
740 +index 24340576c..4a1741f3a 100644
741 +--- a/pym/_emerge/resolver/output.py
742 ++++ b/pym/_emerge/resolver/output.py
743 +@@ -22,11 +22,12 @@ from portage.localization import localized_size
744 + from portage.package.ebuild.config import _get_feature_flags
745 + from portage.package.ebuild._spawn_nofetch import spawn_nofetch
746 + from portage.output import ( blue, colorize, create_color_func,
747 +- darkblue, darkgreen, green, nc_len, teal)
748 ++ darkblue, darkgreen, green, nc_len, teal, yellow, turquoise)
749 + bad = create_color_func("BAD")
750 + from portage._sets.base import InternalPackageSet
751 + from portage.util import writemsg_stdout
752 + from portage.versions import best, cpv_getversion
753 ++from portage.dep.dep_check import ro_selected
754 +
755 + from _emerge.Blocker import Blocker
756 + from _emerge.create_world_atom import create_world_atom
757 +@@ -563,6 +564,42 @@ class Display(object):
758 + writemsg_stdout("%s\n" % (pkg,), noiselevel=-1)
759 + return
760 +
761 ++ def print_readonly_prefix(self):
762 ++ """Performs the actual output printing for the readonly prefix
763 ++ information stuff
764 ++ """
765 ++ out = sys.stdout
766 ++
767 ++ # print readonly selected packages
768 ++ if len(ro_selected) > 0:
769 ++ out.write("\n%s\n\n" % (darkgreen("Packages resolved from readonly installations:")))
770 ++
771 ++ ro_mismatch_warning = False
772 ++ ro_dupcheck = []
773 ++ for x in ro_selected:
774 ++ tmp_type = x["type"].replace("END","")
775 ++ while len(tmp_type) < 4:
776 ++ tmp_type += " "
777 ++ if x["parent"] and str(x["atom"]) not in ro_dupcheck:
778 ++ out.write("[%s %s] %s %s %s (%s by %s)" % (teal("readonly"),
779 ++ green(tmp_type), green(str(x["matches"][0])), yellow("from"),
780 ++ blue(x["ro_root"]), turquoise(str(x["atom"])), green(x["parent"].cpv)))
781 ++
782 ++ ro_dupcheck.append(str(x["atom"]))
783 ++
784 ++ if x["host_mismatch"]:
785 ++ ro_mismatch_warning = True
786 ++ out.write(" %s\n" % (red("**")))
787 ++ else:
788 ++ out.write("\n")
789 ++
790 ++ if ro_mismatch_warning:
791 ++ out.write("\n%s:" % (red("**")))
792 ++ out.write(yellow(" WARNING: packages marked with ** have been resolved as a\n"))
793 ++ out.write(yellow(" runtime dependency, but the CHOST variable for the parent\n"))
794 ++ out.write(yellow(" and dependency package don't match. This could cause link\n"))
795 ++ out.write(yellow(" errors. It is recommended to use RDEPEND READONLY_EPREFIX's\n"))
796 ++ out.write(yellow(" only with matching CHOST portage instances.\n"))
797 +
798 + def print_verbose(self, show_repos):
799 + """Prints the verbose output to std_out
800 +@@ -913,6 +950,7 @@ class Display(object):
801 + show_repos = self.quiet_repo_display and repoadd_set and repoadd_set != set(["0"])
802 +
803 + # now finally print out the messages
804 ++ self.print_readonly_prefix()
805 + self.print_messages(show_repos)
806 + self.print_blockers()
807 + if self.conf.verbosity == 3:
808 +diff --git a/pym/portage/_sets/__init__.py b/pym/portage/_sets/__init__.py
809 +index 2c9bf9715..6a2784207 100644
810 +--- a/pym/portage/_sets/__init__.py
811 ++++ b/pym/portage/_sets/__init__.py
812 +@@ -21,6 +21,7 @@ from portage.const import _ENABLE_SET_CONFIG
813 + from portage.exception import PackageSetNotFound
814 + from portage.localization import _
815 + from portage.util import writemsg_level
816 ++from portage.util._path import exists_raise_eaccess
817 + from portage.util.configparser import (SafeConfigParser,
818 + NoOptionError, ParsingError, read_configs)
819 +
820 +@@ -281,6 +282,10 @@ def load_default_config(settings, trees):
821 + if portage.const.EPREFIX:
822 + global_config_path = os.path.join(portage.const.EPREFIX,
823 + GLOBAL_CONFIG_PATH.lstrip(os.sep))
824 ++ if not exists_raise_eaccess(global_config_path) and portage.const.BPREFIX:
825 ++ global_config_path = os.path.join(portage.const.BPREFIX,
826 ++ GLOBAL_CONFIG_PATH.lstrip(os.sep))
827 ++
828 + vcs_dirs = [_unicode_encode(x, encoding=_encodings['fs']) for x in VCS_DIRS]
829 + def _getfiles():
830 + for path, dirs, files in os.walk(os.path.join(global_config_path, "sets")):
831 +diff --git a/pym/portage/const.py b/pym/portage/const.py
832 +index d9c57f300..a3d927c3b 100644
833 +--- a/pym/portage/const.py
834 ++++ b/pym/portage/const.py
835 +@@ -190,6 +190,7 @@ SUPPORTED_FEATURES = frozenset([
836 + "notitles",
837 + "parallel-fetch",
838 + "parallel-install",
839 ++ "prefix-chaining",
840 + "prelink-checksums",
841 + "preserve-libs",
842 + "protect-owned",
843 +@@ -241,6 +242,11 @@ MANIFEST2_IDENTIFIERS = ("AUX", "MISC", "DIST", "EBUILD")
844 + #EPREFIX = ""
845 + # END PREFIX LOCAL
846 +
847 ++BPREFIX = EPREFIX
848 ++
849 ++# --prefix commandline arg always rules, ends up in os.environ["EPREFIX"]
850 ++if "EPREFIX" in os.environ:
851 ++ os.environ["PORTAGE_OVERRIDE_EPREFIX"] = os.environ["EPREFIX"]
852 + # pick up EPREFIX from the environment if set
853 + if "PORTAGE_OVERRIDE_EPREFIX" in os.environ:
854 + EPREFIX = os.environ["PORTAGE_OVERRIDE_EPREFIX"]
855 +diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
856 +index 77a72b5b1..f20c6763e 100644
857 +--- a/pym/portage/dbapi/vartree.py
858 ++++ b/pym/portage/dbapi/vartree.py
859 +@@ -196,8 +196,19 @@ class vardbapi(dbapi):
860 + self._counter_path = os.path.join(self._eroot,
861 + CACHE_PATH, "counter")
862 +
863 +- self._plib_registry = PreservedLibsRegistry(settings["ROOT"],
864 +- os.path.join(self._eroot, PRIVATE_PATH, "preserved_libs_registry"))
865 ++ plibreg_path = os.path.join(self._eroot, PRIVATE_PATH, "preserved_libs_registry")
866 ++
867 ++ if vartree:
868 ++ self._kill_eprefix = vartree._kill_eprefix
869 ++ else:
870 ++ self._kill_eprefix = False
871 ++
872 ++ if self._kill_eprefix:
873 ++ self._aux_cache_filename = self._aux_cache_filename.replace(EPREFIX, "")
874 ++ self._counter_path = self._counter_path.replace(EPREFIX, "")
875 ++ plibreg_path = plibreg_path.replace(EPREFIX, "")
876 ++
877 ++ self._plib_registry = PreservedLibsRegistry(settings["ROOT"], plibreg_path)
878 + self._linkmap = LinkageMap(self)
879 + chost = self.settings.get('CHOST')
880 + if not chost:
881 +@@ -238,6 +249,9 @@ class vardbapi(dbapi):
882 + # This is an optimized hotspot, so don't use unicode-wrapped
883 + # os module and don't use os.path.join().
884 + rValue = self._eroot + VDB_PATH + _os.sep + mykey
885 ++ if self._kill_eprefix:
886 ++ rValue = rValue.replace(EPREFIX, "")
887 ++
888 + if filename is not None:
889 + # If filename is always relative, we can do just
890 + # rValue += _os.sep + filename
891 +@@ -502,6 +516,9 @@ class vardbapi(dbapi):
892 + returnme = []
893 + basepath = os.path.join(self._eroot, VDB_PATH) + os.path.sep
894 +
895 ++ if self._kill_eprefix:
896 ++ basepath = os.path.join(self.root, basepath.replace(EPREFIX, ""))
897 ++
898 + if use_cache:
899 + from portage import listdir
900 + else:
901 +@@ -598,11 +615,17 @@ class vardbapi(dbapi):
902 + del self.matchcache[mycat]
903 + return list(self._iter_match(mydep,
904 + self.cp_list(mydep.cp, use_cache=use_cache)))
905 ++
906 ++ _tmp_path = os.path.join(self._eroot, VDB_PATH, mycat)
907 ++
908 ++ if self._kill_eprefix:
909 ++ _tmp_path = _tmp_path.replace(EPREFIX, "")
910 ++
911 + try:
912 + if sys.hexversion >= 0x3030000:
913 +- curmtime = os.stat(os.path.join(self._eroot, VDB_PATH, mycat)).st_mtime_ns
914 ++ curmtime = os.stat(_tmp_path).st_mtime_ns
915 + else:
916 +- curmtime = os.stat(os.path.join(self._eroot, VDB_PATH, mycat)).st_mtime
917 ++ curmtime = os.stat(_tmp_path).st_mtime
918 + except (IOError, OSError):
919 + curmtime=0
920 +
921 +@@ -1450,7 +1473,7 @@ class vardbapi(dbapi):
922 + class vartree(object):
923 + "this tree will scan a var/db/pkg database located at root (passed to init)"
924 + def __init__(self, root=None, virtual=DeprecationWarning, categories=None,
925 +- settings=None):
926 ++ settings=None, kill_eprefix=None):
927 +
928 + if settings is None:
929 + settings = portage.settings
930 +@@ -1468,6 +1491,7 @@ class vartree(object):
931 + " constructor is unused",
932 + DeprecationWarning, stacklevel=2)
933 +
934 ++ self._kill_eprefix = kill_eprefix
935 + self.settings = settings
936 + self.dbapi = vardbapi(settings=settings, vartree=self)
937 + self.populated = 1
938 +diff --git a/pym/portage/dep/dep_check.py b/pym/portage/dep/dep_check.py
939 +index 2896e2389..c700a3651 100644
940 +--- a/pym/portage/dep/dep_check.py
941 ++++ b/pym/portage/dep/dep_check.py
942 +@@ -298,6 +298,95 @@ class _dep_choice(SlotObject):
943 + __slots__ = ('atoms', 'slot_map', 'cp_map', 'all_available',
944 + 'all_installed_slots', 'new_slot_count')
945 +
946 ++ro_trees={}
947 ++ro_vartrees={}
948 ++ro_selected=[]
949 ++
950 ++def dep_match_readonly_roots(settings, atom, dep_type, parent=None):
951 ++ if len(ro_trees) < len(settings.readonly_prefixes):
952 ++ # MDUFT: create additional vartrees for every readonly root here.
953 ++ # the ro_vartrees instances are created below as they are needed to
954 ++ # avoid reading vartrees of portage instances which aren't required
955 ++ # while resolving this dependencies.
956 ++ for type in ("DEPEND","RDEPEND", "PDEPEND"):
957 ++ ro_trees[type] = []
958 ++
959 ++ for ro_root, ro_dep_types in settings.readonly_prefixes.items():
960 ++ if type in ro_dep_types:
961 ++ ro_trees[type].append(ro_root)
962 ++
963 ++ if len(ro_trees) == 0:
964 ++ return []
965 ++
966 ++ matches = []
967 ++
968 ++ for ro_root in ro_trees[dep_type]:
969 ++ if not ro_root in ro_vartrees:
970 ++ # target_root=ro_root ok? or should it be the real target_root?
971 ++ _tmp_settings = portage.config(config_root=ro_root, target_root=ro_root,
972 ++ config_incrementals=portage.const.INCREMENTALS)
973 ++
974 ++ ro_vartrees[ro_root] = portage.vartree(root=ro_root,
975 ++ categories=_tmp_settings.categories,
976 ++ settings=_tmp_settings, kill_eprefix=True)
977 ++
978 ++ ro_matches = ro_vartrees[ro_root].dbapi.match(atom)
979 ++
980 ++ if ro_matches:
981 ++ ro_host_mismatch = False
982 ++ if dep_type is "RDEPEND":
983 ++ # we need to assure binary compatability, so it needs to be
984 ++ # the same CHOST! But how? for now i cannot do anything...
985 ++ if parent and parent.metadata["CHOST"] != ro_vartrees[ro_root].settings.get("CHOST", ""):
986 ++ # provocate a big fat warning in the list of external packages.
987 ++ ro_host_mismatch = True
988 ++ pass
989 ++
990 ++ matches.append({ "ro_root": ro_root, "atom": atom, "matches": ro_matches,
991 ++ "type": dep_type, "parent": parent, "host_mismatch": ro_host_mismatch })
992 ++
993 ++ return matches
994 ++
995 ++def dep_wordreduce_readonly(reduced, unreduced, settings, dep_type, parent):
996 ++ for mypos, token in enumerate(unreduced):
997 ++ # recurse if it's a list.
998 ++ if isinstance(reduced[mypos], list):
999 ++ reduced[mypos] = dep_wordreduce_readonly(reduced[mypos],
1000 ++ unreduced[mypos], settings, dep_type, parent)
1001 ++
1002 ++ # do nothing if it's satisfied already.
1003 ++ elif not reduced[mypos]:
1004 ++ ro_matches = dep_match_readonly_roots(settings, unreduced[mypos], dep_type, parent)
1005 ++
1006 ++ if ro_matches:
1007 ++ # TODO: select a match if there are more than one?
1008 ++ # for now, the first match is taken...
1009 ++ ro_selected.append(ro_matches[0])
1010 ++ reduced[mypos] = True
1011 ++
1012 ++ return reduced
1013 ++
1014 ++# this may be better placed somewhere else, but i put it here for now, to
1015 ++# keep all functions in the patch on one big heap.
1016 ++def readonly_pathmatch_any(settings, path):
1017 ++ path = path.lstrip('/')
1018 ++ # first try locally, and match that if it exists.
1019 ++ if os.path.exists(os.path.join(EPREFIX,path)):
1020 ++ return os.path.join(EPREFIX,path)
1021 ++
1022 ++ # after that try all readonly roots where DEPEND is allowed. this makes
1023 ++ # sure that executing binaries is possible from there.
1024 ++ for ro_root, ro_deps in settings.readonly_roots.items():
1025 ++ if "DEPEND" in ro_deps:
1026 ++ print(" --- checking %s --- " % (os.path.join(ro_root,path)))
1027 ++ if os.path.exists(os.path.join(ro_root,path)):
1028 ++ return os.path.join(ro_root,path)
1029 ++ break
1030 ++
1031 ++ # as a fallback make the string the same as it was originally.
1032 ++ # even though this path doesn't exist.
1033 ++ return os.path.join(EPREFIX,path)
1034 ++
1035 + def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
1036 + minimize_slots=False):
1037 + """
1038 +@@ -725,7 +814,7 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
1039 + assert(False) # This point should not be reachable
1040 +
1041 + def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None,
1042 +- use_cache=1, use_binaries=0, myroot=None, trees=None):
1043 ++ use_cache=1, use_binaries=0, myroot=None, trees=None, dep_type=None):
1044 + """
1045 + Takes a depend string, parses it, and selects atoms.
1046 + The myroot parameter is unused (use mysettings['EROOT'] instead).
1047 +@@ -829,6 +918,14 @@ def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None,
1048 + writemsg("mysplit: %s\n" % (mysplit), 1)
1049 + writemsg("mysplit2: %s\n" % (mysplit2), 1)
1050 +
1051 ++ if dep_type is not None:
1052 ++ mysplit2=dep_wordreduce_readonly(unreduced=mysplit[:],
1053 ++ reduced=mysplit2, settings=mysettings,
1054 ++ dep_type=dep_type, parent=trees[myroot].get("disp_parent"))
1055 ++
1056 ++ writemsg("\n", 1)
1057 ++ writemsg("mysplit2 after readonly reduce: %s\n" % (mysplit2), 1)
1058 ++
1059 + selected_atoms = dep_zapdeps(mysplit, mysplit2, myroot,
1060 + use_binaries=use_binaries, trees=trees, minimize_slots=dnf)
1061 +
1062 +diff --git a/pym/portage/package/ebuild/_config/LocationsManager.py b/pym/portage/package/ebuild/_config/LocationsManager.py
1063 +index f7d7209ff..e37e5b1a9 100644
1064 +--- a/pym/portage/package/ebuild/_config/LocationsManager.py
1065 ++++ b/pym/portage/package/ebuild/_config/LocationsManager.py
1066 +@@ -326,6 +326,9 @@ class LocationsManager(object):
1067 + if portage.const.EPREFIX:
1068 + self.global_config_path = os.path.join(portage.const.EPREFIX,
1069 + GLOBAL_CONFIG_PATH.lstrip(os.sep))
1070 ++ if not exists_raise_eaccess(self.global_config_path) and portage.const.BPREFIX:
1071 ++ self.global_config_path = os.path.join(portage.const.BPREFIX,
1072 ++ GLOBAL_CONFIG_PATH.lstrip(os.sep))
1073 +
1074 + def set_port_dirs(self, portdir, portdir_overlay):
1075 + self.portdir = portdir
1076 +diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py
1077 +index 059aa83ce..3bf6049e8 100644
1078 +--- a/pym/portage/package/ebuild/config.py
1079 ++++ b/pym/portage/package/ebuild/config.py
1080 +@@ -309,6 +309,7 @@ class config(object):
1081 + self.features = features_set(self)
1082 + self.features._features = copy.deepcopy(clone.features._features)
1083 + self._features_overrides = copy.deepcopy(clone._features_overrides)
1084 ++ self.readonly_prefixes = copy.deepcopy(clone.readonly_prefixes)
1085 +
1086 + #Strictly speaking _license_manager is not immutable. Users need to ensure that
1087 + #extract_global_changes() is called right after __init__ (if at all).
1088 +@@ -969,6 +970,63 @@ class config(object):
1089 +
1090 + self._validate_commands()
1091 +
1092 ++ # expand READONLY_EPREFIX to a list of all readonly portage instances
1093 ++ # all the way down to the last one. beware that ATM a deeper instance
1094 ++ # in the chain can provide more than the toplevel! this means that
1095 ++ # if you only inherit DEPENDS from one instance, that instance may
1096 ++ # inherit RDEPENDs from another one, making the top-level instance
1097 ++ # inherit RDEPENDs from there too - even if the intermediate prefix
1098 ++ # does not do this.
1099 ++ self.readonly_prefixes = {}
1100 ++ ro_cfg_root = config_root
1101 ++ ro_widest_depset = set(['DEPEND', 'RDEPEND', 'PDEPEND'])
1102 ++
1103 ++ while ro_cfg_root:
1104 ++ ro_make_conf_paths = [
1105 ++ os.path.join(ro_cfg_root, 'etc', 'make.conf'),
1106 ++ os.path.join(ro_cfg_root, MAKE_CONF_FILE)
1107 ++ ]
1108 ++ try:
1109 ++ if os.path.samefile(*ro_make_conf_paths):
1110 ++ ro_make_conf_paths.pop()
1111 ++ except OSError:
1112 ++ pass
1113 ++
1114 ++ ro_cfg_root = None
1115 ++ for ro_make_conf in ro_make_conf_paths:
1116 ++ if not os.path.exists(ro_make_conf):
1117 ++ continue
1118 ++
1119 ++ ro_cfg = getconfig(ro_make_conf, tolerant=True, allow_sourcing=True)
1120 ++ if not "READONLY_EPREFIX" in ro_cfg:
1121 ++ continue
1122 ++
1123 ++ if not ro_cfg["READONLY_EPREFIX"].find(":"):
1124 ++ raise portage.exception.InvalidReadonlyERoot("ERROR: malformed READONLY_EPREFIX in %s" % (ro_make_conf))
1125 ++
1126 ++ if ro_cfg_root is not None:
1127 ++ raise portage.exception.InvalidReadonlyERoot("ERROR: duplicate READONLY_EPREFIX in %s and %s" % tuple(ro_make_conf_paths))
1128 ++
1129 ++ (ro_cfg_root,ro_cfg_root_deps) = ro_cfg["READONLY_EPREFIX"].rsplit(":",1)
1130 ++
1131 ++ if not os.path.exists(ro_cfg_root):
1132 ++ raise portage.exception.InvalidReadonlyERoot("ERROR: malformed READONLY_EPREFIX in %s: %s does not exist!" % (ro_make_conf, ro_cfg_root))
1133 ++
1134 ++ if os.path.samefile(ro_cfg_root, config_root):
1135 ++ raise portage.exception.InvalidReadonlyERoot("ERROR: cannot add this instance (%s) as READONLY_EPREFIX in %s." % (ro_cfg_root, ro_make_conf))
1136 ++
1137 ++ if ro_cfg_root in self.readonly_prefixes:
1138 ++ raise portage.exception.InvalidReadonlyERoot("ERROR: circular READONLY_EPREFIX's in %s. %s already checked for %s" % (ro_make_conf, ro_cfg_root, self.readonly_prefixes[ro_cfg_root]))
1139 ++
1140 ++ # intersect the widest depset with the current one to strip down
1141 ++ # the allowed dependency resolution to not be wider than the
1142 ++ # next higher one. this way we can prevent for a given prefix
1143 ++ # to resolve RDEPENDs from a prefix with a different CHOST that
1144 ++ # is a few levels deeper in the chain.
1145 ++ ro_widest_depset = set(ro_cfg_root_deps.split(",")) & ro_widest_depset
1146 ++ self.readonly_prefixes[ro_cfg_root] = ro_widest_depset
1147 ++ pass
1148 ++
1149 + for k in self._case_insensitive_vars:
1150 + if k in self:
1151 + self[k] = self[k].lower()
1152 +@@ -2771,6 +2829,10 @@ class config(object):
1153 + if not (src_phase and eapi_attrs.broot):
1154 + mydict.pop("BROOT", None)
1155 +
1156 ++ # populate with PORTAGE_READONLY_EPREFIXES
1157 ++ if self.readonly_prefixes and len(self.readonly_prefixes) > 0:
1158 ++ mydict["PORTAGE_READONLY_EPREFIXES"] = ':'.join(self.readonly_prefixes)
1159 ++
1160 + # Prefix variables are supported beginning with EAPI 3, or when
1161 + # force-prefix is in FEATURES, since older EAPIs would otherwise be
1162 + # useless with prefix configurations. This brings compatibility with
1163 +diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py
1164 +index f8b784d6b..a6548a43b 100644
1165 +--- a/pym/portage/package/ebuild/doebuild.py
1166 ++++ b/pym/portage/package/ebuild/doebuild.py
1167 +@@ -52,6 +52,7 @@ from portage import bsd_chflags, \
1168 + unmerge, _encodings, _os_merge, \
1169 + _shell_quote, _unicode_decode, _unicode_encode
1170 + from portage.const import EBUILD_SH_ENV_FILE, EBUILD_SH_ENV_DIR, \
1171 ++ GLOBAL_CONFIG_PATH, \
1172 + EBUILD_SH_BINARY, INVALID_ENV_FILE, MISC_SH_BINARY, PORTAGE_PYM_PACKAGES, EPREFIX, MACOSSANDBOX_PROFILE
1173 + from portage.data import portage_gid, portage_uid, secpass, \
1174 + uid, userpriv_groups
1175 +@@ -73,6 +74,7 @@ from portage.package.ebuild.prepare_build_dirs import prepare_build_dirs
1176 + from portage.process import find_binary
1177 + from portage.util import ( apply_recursive_permissions,
1178 + apply_secpass_permissions,
1179 ++ getconfig,
1180 + noiselimit,
1181 + shlex_split,
1182 + varexpand,
1183 +@@ -80,6 +82,7 @@ from portage.util import ( apply_recursive_permissions,
1184 + writemsg_stdout,
1185 + write_atomic
1186 + )
1187 ++from portage.util._path import exists_raise_eaccess
1188 + from portage.util.cpuinfo import get_cpu_count
1189 + from portage.util.lafilefixer import rewrite_lafile
1190 + from portage.util.compression_probe import _compressors
1191 +@@ -243,8 +246,27 @@ def _doebuild_path(settings, eapi=None):
1192 +
1193 + for x in portage_bin_path:
1194 + path.append(os.path.join(x, "ebuild-helpers"))
1195 ++
1196 ++ # PREFIX CHAINING: append default path for all prefixes involved
1197 ++ pfxs = [ eprefix ]
1198 ++ pfxs.extend(settings.readonly_prefixes)
1199 ++ for prefix in pfxs:
1200 ++ global_config_path = os.path.join(prefix, GLOBAL_CONFIG_PATH.lstrip(os.sep))
1201 ++ make_globals_path = os.path.join(global_config_path, "make.globals")
1202 ++ if exists_raise_eaccess(make_globals_path):
1203 ++ expand_map = { "EPREFIX": prefix }
1204 ++ pxcfg = getconfig(make_globals_path, True, expand_map)
1205 ++ pxdefp = [x for x in pxcfg.get("DEFAULT_PATH", "").split(":") if x]
1206 ++ for x in pxdefp:
1207 ++ if x.startswith(prefix) and not x in path:
1208 ++ path.append(x)
1209 ++ else:
1210 ++ pxdefs = [prefix + "/usr/sbin", prefix + "/usr/bin", prefix + "/sbin", prefix + "/bin"]
1211 ++ path.extend(pxdefs)
1212 ++ # END PREFIX CHAINING
1213 ++
1214 + path.extend(prerootpath)
1215 +- path.extend(defaultpath)
1216 ++ # path.extend(defaultpath) # PREFIX CHAINING appends the default path for involved prefixes above
1217 + path.extend(rootpath)
1218 + path.extend(extrapath)
1219 + # END PREFIX LOCAL
1220 +diff --git a/pym/portage/package/ebuild/fetch.py b/pym/portage/package/ebuild/fetch.py
1221 +index 265d0c9fc..2ec6ff472 100644
1222 +--- a/pym/portage/package/ebuild/fetch.py
1223 ++++ b/pym/portage/package/ebuild/fetch.py
1224 +@@ -43,6 +43,7 @@ from portage.output import colorize, EOutput
1225 + from portage.util import apply_recursive_permissions, \
1226 + apply_secpass_permissions, ensure_dirs, grabdict, shlex_split, \
1227 + varexpand, writemsg, writemsg_level, writemsg_stdout
1228 ++from portage.util._path import exists_raise_eaccess
1229 + from portage.process import spawn
1230 +
1231 + _userpriv_spawn_kwargs = (
1232 +@@ -874,6 +875,9 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0,
1233 + global_config_path = GLOBAL_CONFIG_PATH
1234 + if portage.const.EPREFIX:
1235 + global_config_path = os.path.join(portage.const.EPREFIX,
1236 ++ GLOBAL_CONFIG_PATH.lstrip(os.sep))
1237 ++ if not exists_raise_eaccess(global_config_path) and portage.const.BPREFIX:
1238 ++ global_config_path = os.path.join(portage.const.BPREFIX,
1239 + GLOBAL_CONFIG_PATH.lstrip(os.sep))
1240 +
1241 + missing_file_param = False
1242 +diff --git a/pym/portage/sync/controller.py b/pym/portage/sync/controller.py
1243 +index 3bccf6f74..cacd63797 100644
1244 +--- a/pym/portage/sync/controller.py
1245 ++++ b/pym/portage/sync/controller.py
1246 +@@ -94,19 +94,20 @@ class SyncManager(object):
1247 + self.module_controller = portage.sync.module_controller
1248 + self.module_names = self.module_controller.module_names
1249 + self.hooks = {}
1250 +- for _dir in ["repo.postsync.d", "postsync.d"]:
1251 +- postsync_dir = os.path.join(self.settings["PORTAGE_CONFIGROOT"],
1252 +- portage.USER_CONFIG_PATH, _dir)
1253 +- hooks = OrderedDict()
1254 +- for filepath in util._recursive_file_list(postsync_dir):
1255 +- name = filepath.split(postsync_dir)[1].lstrip(os.sep)
1256 +- if os.access(filepath, os.X_OK):
1257 +- hooks[filepath] = name
1258 +- else:
1259 +- writemsg_level(" %s %s hook: '%s' is not executable\n"
1260 +- % (warn("*"), _dir, _unicode_decode(name),),
1261 +- level=logging.WARN, noiselevel=2)
1262 +- self.hooks[_dir] = hooks
1263 ++ for _confroot in [self.settings["PORTAGE_CONFIGROOT"], portage.const.BPREFIX]:
1264 ++ for _dir in ["repo.postsync.d", "postsync.d"]:
1265 ++ postsync_dir = os.path.join(_confroot,
1266 ++ portage.USER_CONFIG_PATH, _dir)
1267 ++ hooks = OrderedDict()
1268 ++ for filepath in util._recursive_file_list(postsync_dir):
1269 ++ name = filepath.split(postsync_dir)[1].lstrip(os.sep)
1270 ++ if os.access(filepath, os.X_OK):
1271 ++ hooks[filepath] = name
1272 ++ else:
1273 ++ writemsg_level(" %s %s hook: '%s' is not executable\n"
1274 ++ % (warn("*"), _dir, _unicode_decode(name),),
1275 ++ level=logging.WARN, noiselevel=2)
1276 ++ self.hooks[_dir] = hooks
1277 +
1278 + def __getattr__(self, name):
1279 + if name == 'async':
1280 +diff --git a/pym/portage/util/_dyn_libs/LinkageMapELF.py b/pym/portage/util/_dyn_libs/LinkageMapELF.py
1281 +index a063621c1..968fbd339 100644
1282 +--- a/pym/portage/util/_dyn_libs/LinkageMapELF.py
1283 ++++ b/pym/portage/util/_dyn_libs/LinkageMapELF.py
1284 +@@ -12,7 +12,7 @@ from portage import _os_merge
1285 + from portage import _unicode_decode
1286 + from portage import _unicode_encode
1287 + from portage.cache.mappings import slot_dict_class
1288 +-from portage.const import EPREFIX
1289 ++from portage.const import BPREFIX
1290 + from portage.dep.soname.multilib_category import compute_multilib_category
1291 + from portage.exception import CommandNotFound, InvalidData
1292 + from portage.localization import _
1293 +@@ -268,7 +268,7 @@ class LinkageMapELF(object):
1294 + continue
1295 + plibs.update((x, cpv) for x in items)
1296 + if plibs:
1297 +- args = [os.path.join(EPREFIX or "/", "usr/bin/scanelf"), "-qF", "%a;%F;%S;%r;%n"]
1298 ++ args = [os.path.join(BPREFIX or "/", "usr/bin/scanelf"), "-qF", "%a;%F;%S;%r;%n"]
1299 + args.extend(os.path.join(root, x.lstrip("." + os.sep)) \
1300 + for x in plibs)
1301 + try:
1302 +--
1303 +2.16.1
1304 +
1305
1306 diff --git a/sys-apps/portage/portage-2.3.40.1.ebuild b/sys-apps/portage/portage-2.3.40.1.ebuild
1307 index bf7393b3ac..0bea1d9f01 100644
1308 --- a/sys-apps/portage/portage-2.3.40.1.ebuild
1309 +++ b/sys-apps/portage/portage-2.3.40.1.ebuild
1310 @@ -91,10 +91,9 @@ pkg_setup() {
1311 python_prepare_all() {
1312 distutils-r1_python_prepare_all
1313
1314 - # fails to apply
1315 - #epatch "${FILESDIR}"/${PN}-2.3.10-ebuildshell.patch # 155161
1316 + epatch "${FILESDIR}"/${PN}-2.3.40-ebuildshell.patch # 155161
1317 use prefix-chaining &&
1318 - epatch "${FILESDIR}"/${PN}-2.3.18-prefix-chaining.patch
1319 + epatch "${FILESDIR}"/${PN}-2.3.40-prefix-chaining.patch
1320
1321 if use native-extensions; then
1322 printf "[build_ext]\nportage-ext-modules=true\n" >> \