commit:     cb20d3f95bd20b528a14126b3d1c545c4ddb1704
Author:     Michael Haubenwallner <haubi <AT> gentoo <DOT> org>
AuthorDate: Mon May 28 16:40:04 2018 +0000
Commit:     Michael Haubenwallner <haubi <AT> gentoo <DOT> org>
CommitDate: Mon May 28 16:43:59 2018 +0000
URL:        https://gitweb.gentoo.org/repo/proj/prefix.git/commit/?id=cb20d3f9

sys-apps/portage: bump ebuildshell+prefix-chaining patches

Package-Manager: Portage-2.3.24, Repoman-2.3.6

 .../portage/files/portage-2.3.40-ebuildshell.patch | 354 ++++++++
 .../files/portage-2.3.40-prefix-chaining.patch     | 921 +++++++++++++++++++++
 sys-apps/portage/portage-2.3.40.1.ebuild           |   5 +-
 3 files changed, 1277 insertions(+), 3 deletions(-)

diff --git a/sys-apps/portage/files/portage-2.3.40-ebuildshell.patch 
b/sys-apps/portage/files/portage-2.3.40-ebuildshell.patch
new file mode 100644
index 0000000000..167ed3824e
--- /dev/null
+++ b/sys-apps/portage/files/portage-2.3.40-ebuildshell.patch
@@ -0,0 +1,354 @@
+From 9075d30d24af87f69d23ae129dc75e1305cd3aa8 Mon Sep 17 00:00:00 2001
+From: Michael Haubenwallner <[email protected]>
+Date: Wed, 6 Nov 2013 12:40:05 +0100
+Subject: [PATCH 1/2] Add ebuildshell feature, bug#155161.
+
+---
+ bin/ebuild.sh                        | 146 ++++++++++++++++++++++++++++++++++-
+ bin/filter-bash-environment.py       |  55 +++++++++----
+ bin/save-ebuild-env.sh               |   2 +-
+ man/make.conf.5                      |   6 ++
+ pym/_emerge/AbstractEbuildProcess.py |   1 +
+ pym/portage/const.py                 |   1 +
+ 6 files changed, 194 insertions(+), 17 deletions(-)
+
+diff --git a/bin/ebuild.sh b/bin/ebuild.sh
+index f76a48d8e..51ba95cb1 100755
+--- a/bin/ebuild.sh
++++ b/bin/ebuild.sh
+@@ -121,7 +121,7 @@ __qa_source() {
+ __qa_call() {
+       local shopts=$(shopt) OLDIFS="$IFS"
+       local retval
+-      "$@"
++      __call-ebuildshell "$@"
+       retval=$?
+       set +e
+       [[ $shopts != $(shopt) ]] &&
+@@ -547,6 +547,150 @@ if [[ -n ${QA_INTERCEPTORS} ]] ; then
+       unset BIN_PATH BIN BODY FUNC_SRC
+ fi
+ 
++__call-ebuildshell() {
++      if ! has ebuildshell ${FEATURES}; then
++              "$@"
++              return $?
++      fi
++      local __ebuildshell_args=( "$@" )
++      # These are the variables I have seen 'bash -i' maintaining the values 
for:
++      local __ebuildshell_bash_i_vars="__ebuildshell_.*
++              _ BASH_ARGC BASH_ARGV BASH_COMMAND BASH_LINENO BASH_SOURCE
++              BASH_VERSINFO BASH_SUBSHELL BASHOPTS BASHPID COMP_WORDBREAKS
++              DIRSTACK EUID FUNCNAME GROUPS HISTCMD HISTFILE LINENO PIPESTATUS
++              PPID PS1 PS2 PS3 PS4 PWD RANDOM SECONDS SHELLOPTS UID"
++      # Allow recursive ebuildshell, for use in multibuild.eclass and similar:
++      local __ebuildshell_pid=${BASHPID:-$(__bashpid)}
++      local __ebuildshell_tmpf="${T}/ebuildshell.${__ebuildshell_pid}"
++      rm -f "${__ebuildshell_tmpf}."{ebuild,return}-{env,rovars}
++      (
++              cat <<-EOE
++                      # local variables of functions using recursive 
ebuildshell are
++                      # visible to the EXIT trap of that recursive 
ebuildshell.  To
++                      # keep them local, we have to filter them from that 
recursive
++                      # ebuildshell's return-env.  As 'declare -p' is unable 
to tell
++                      # local-ity of variables, we abuse the trace attribute 
for local
++                      # variables to filter them from the return-env.  So we 
need the
++                      # local alias active before declaring any functions.
++                      # On a sidehand, this allows for copy&paste of function 
body
++                      # lines including the local keyword.
++                      alias local='declare -t'
++                      shopt -s expand_aliases
++              EOE
++              (
++                      declare -p
++                      declare -fp
++                      shopt -p
++                      [[ ${BASH_VERSINFO[0]} == 3 ]] && export
++              ) |
++              (
++                      # we need everything but the bash vars after 'env -i'
++                      2>"${__ebuildshell_tmpf}.ebuild-rovars" \
++                      
"${PORTAGE_PYTHON:-/tools/haubi/gentoo/s01en24/usr/bin/python}" \
++                              
"${PORTAGE_BIN_PATH}"/filter-bash-environment.py \
++                                      --report-readonly-variables \
++                                      --preserve-readonly-attribute \
++                                      "${__ebuildshell_bash_i_vars}" \
++                              || die "filter-bash-environment.py failed"
++              )
++              # 'declare -g' is available since bash-4.2,
++              # https://bugs.gentoo.org/show_bug.cgi?id=155161#c35
++              if (( ${BASH_VERSINFO[0]} > 4 )) ||
++                 (( ${BASH_VERSINFO[0]} == 4 && ${BASH_VERSINFO[1]} >= 2 ))
++              then
++                      __ebuildshell_bash42_true=
++                      __ebuildshell_bash42_false='#bash-4.2#'
++              else
++                  __ebuildshell_bash42_true='#bash-4.2#'
++                  __ebuildshell_bash42_false=
++              fi
++              # The already readonly variables, without bash maintained ones:
++              
__ebuildshell_ro_ebuild_vars=$(<"${__ebuildshell_tmpf}.ebuild-rovars")
++              cat <<-EOE
++                      # properly quote the function arguments
++                      $(declare -p __ebuildshell_args)
++                      set -- "\${__ebuildshell_args[@]}"
++                      unset __ebuildshell_args
++                      # be informative about what to do
++                      PS1="EBUILD ${PN} $1 \$ "
++                      type $1
++                      ${__ebuildshell_bash42_false}echo 'warning: preserving 
variables across phases requires bash-4.2'
++                      echo "WANTED: \$@"
++                      echo "or use: \"\\\$@\""
++                      # use bash history, but not the 'user's real one
++                      HISTFILE=~/.bash_history
++                      # but do not use history-expansion with '!',
++                      # for copy&paste of function body lines containing: !
++                      set +H
++                      # this is a debugging shell already
++                      shopt -u extdebug
++                      trap - DEBUG
++                      # at exit, dump the current environment
++                      trap "
++                              unalias local
++                              unset -f __call-ebuildshell
++                              rm -f '${__ebuildshell_tmpf}.return-'*
++                              (
++                                      (
++                                              # declare -p does not tell the 
-g flag,
++                                              # so we add it by aliasing 
declare.
++                                              
${__ebuildshell_bash42_true}echo \"alias declare='declare -g'\"
++                                              declare -p
++                                              
${__ebuildshell_bash42_true}echo \"unalias declare\"
++                                              declare -fp
++                                              shopt -p | grep -v 
'\\(expand_aliases\\|extdebug\\)$'
++                                              $([[ ${BASH_VERSINFO[0]} == 3 
]] && echo export)
++                                      ) |
++                                      (
++                                              # We may have more readonly 
variables now, yet we
++                                              # need to filter variables that 
were readonly before.
++                                              # And filter local variables by 
their trace attribute.
++                                              
2>'${__ebuildshell_tmpf}.return-rovars' \\
++                                              
'${PORTAGE_PYTHON:-/tools/haubi/gentoo/s01en24/usr/bin/python}' \\
++                                                      
'${PORTAGE_BIN_PATH}'/filter-bash-environment.py \\
++                                                              
--report-readonly-variables \\
++                                                              
--preserve-readonly-attribute \\
++                                                              
--filter-traced-variables \\
++                                                              
'${__ebuildshell_bash_i_vars} \
++                                                               
${__ebuildshell_ro_ebuild_vars}' \\
++                                                      || die 
'filter-bash-environment.py failed'
++                                      )
++                              ) > '${__ebuildshell_tmpf}.return-env'
++                              " EXIT
++                      # can do some cleanup right now
++                      rm -f '${__ebuildshell_tmpf}.ebuild-'*
++              EOE
++      ) > "${__ebuildshell_tmpf}.ebuild-env"
++
++      # pre-fill the history with "$@"
++      echo '"$@"' >> ~/.bash_history
++      chown ${PORTAGE_USER:-portage}:${PORTAGE_GROUP:-portage} 
~/.bash_history &>/dev/null
++
++      env -i ${BASH} --rcfile "${__ebuildshell_tmpf}.ebuild-env" -i
++
++      # The environment- and exit-status handling after leaving the 
ebuildshell
++      # prompt is expected to be identical as without the ebuildshell prompt.
++      local __ebuildshell_status=$?
++
++      # We might be in a recursive ebuildshell, but do not want
++      # any aliases being active while sourcing the return-env.
++      local __ebuildshell_orig_aliases=$(alias)
++      unalias -a
++      source "${__ebuildshell_tmpf}.return-env"
++      unalias -a
++      eval "${__ebuildshell_orig_aliases}"
++
++      # Portage has a whitelist of readonly variables: If an ebuild defines
++      # additional readonly variables, their readonly attribute is removed
++      # across ebuild phases.  If we ever want to preserve the readonly
++      # attribute of additional ebuild-defined variables across phases,
++      # when returning from the ebuildshell their names are in
++      # "${__ebuildshell_tmpf}.return-rovars"
++      rm -f "${__ebuildshell_tmpf}."{ebuild,return}-{env,rovars}
++
++      return ${__ebuildshell_status}
++}
++
+ # Subshell/helper die support (must export for the die helper).
+ export EBUILD_MASTER_PID=${BASHPID:-$(__bashpid)}
+ trap 'exit 1' SIGTERM
+diff --git a/bin/filter-bash-environment.py b/bin/filter-bash-environment.py
+index 06cac7214..5590dbfc4 100755
+--- a/bin/filter-bash-environment.py
++++ b/bin/filter-bash-environment.py
+@@ -12,7 +12,8 @@ func_end_re = re.compile(br'^\}$')
+ 
+ var_assign_re = 
re.compile(br'(^|^declare\s+-\S+\s+|^declare\s+|^export\s+)([^=\s]+)=("|\')?.*$')
+ close_quote_re = re.compile(br'(\\"|"|\')\s*$')
+-readonly_re = re.compile(br'^declare\s+-(\S*)r(\S*)\s+')
++readonly_re = re.compile(br'^declare\s+-(\S*)r(\S*)\s+([^=\s]+)')
++trace_re = re.compile(br'^declare\s+-\S*t\S*\s+')
+ # declare without assignment
+ var_declare_re = re.compile(br'^declare(\s+-\S+)?\s+([^=\s]+)\s*$')
+ 
+@@ -27,7 +28,7 @@ def have_end_quote(quote, line):
+       return close_quote_match is not None and \
+               close_quote_match.group(1) == quote
+ 
+-def filter_declare_readonly_opt(line):
++def filter_declare_readonly_opt(line, options):
+       readonly_match = readonly_re.match(line)
+       if readonly_match is not None:
+               declare_opts = b''
+@@ -35,14 +36,19 @@ def filter_declare_readonly_opt(line):
+                       group = readonly_match.group(i)
+                       if group is not None:
+                               declare_opts += group
++              var = readonly_match.group(3)
++              if '--report-readonly-variables' in options:
++                      getattr(sys.stderr, 'buffer', sys.stderr).write(var + 
b'\n')
++              if '--preserve-readonly-attribute' in options:
++                      declare_opts += b'r'
+               if declare_opts:
+                       line = b'declare -' + declare_opts + \
+-                              b' ' + line[readonly_match.end():]
++                              b' ' + var + line[readonly_match.end():]
+               else:
+-                      line = b'declare ' + line[readonly_match.end():]
++                      line = b'declare ' + var + line[readonly_match.end():]
+       return line
+ 
+-def filter_bash_environment(pattern, file_in, file_out):
++def filter_bash_environment(pattern, file_in, file_out, options):
+       # Filter out any instances of the \1 character from variable values
+       # since this character multiplies each time that the environment
+       # is saved (strange bash behavior). This can eventually result in
+@@ -66,6 +72,8 @@ def filter_bash_environment(pattern, file_in, file_out):
+                               quote = var_assign_match.group(3)
+                               filter_this = 
pattern.match(var_assign_match.group(2)) \
+                                       is not None
++                              if not filter_this and 
'--filter-traced-variables' in options:
++                                      filter_this = trace_re.match(line) is 
not None
+                               # Exclude the start quote when searching for 
the end quote,
+                               # to ensure that the start quote is not 
misidentified as the
+                               # end quote (happens if there is a newline 
immediately after
+@@ -75,7 +83,7 @@ def filter_bash_environment(pattern, file_in, file_out):
+                                       multi_line_quote = quote
+                                       multi_line_quote_filter = filter_this
+                               if not filter_this:
+-                                      line = filter_declare_readonly_opt(line)
++                                      line = 
filter_declare_readonly_opt(line, options)
+                                       file_out.write(line.replace(b"\1", b""))
+                               continue
+                       else:
+@@ -84,8 +92,10 @@ def filter_bash_environment(pattern, file_in, file_out):
+                                       # declare without assignment
+                                       filter_this = 
pattern.match(declare_match.group(2)) \
+                                               is not None
++                                      if not filter_this and 
'--filter-traced-variables' in options:
++                                              filter_this = 
trace_re.match(line) is not None
+                                       if not filter_this:
+-                                              line = 
filter_declare_readonly_opt(line)
++                                              line = 
filter_declare_readonly_opt(line, options)
+                                               file_out.write(line)
+                                       continue
+ 
+@@ -122,13 +132,28 @@ if __name__ == "__main__":
+               "while leaving bash function definitions and here-documents " + 
\
+               "intact. The PATTERN is a space separated list of variable 
names" + \
+               " and it supports python regular expression syntax."
+-      usage = "usage: %s PATTERN" % os.path.basename(sys.argv[0])
+-      args = sys.argv[1:]
+-
+-      if '-h' in args or '--help' in args:
+-              sys.stdout.write(usage + "\n")
+-              sys.stdout.flush()
+-              sys.exit(os.EX_OK)
++      usage = "usage: %s [-h|<options>] PATTERN" % 
os.path.basename(sys.argv[0])
++      args = []
++      known_options = {
++              '--report-readonly-variables':
++                      "Write names of readonly variables to stderr.",
++              '--preserve-readonly-attribute':
++                      "Preserve the '-r' flag in 'declare -r'.",
++              '--filter-traced-variables':
++                      "Filter out variables declared with '-t' attribute."
++      }
++      options = {}
++      for arg in sys.argv[1:]:
++              if arg in known_options.keys():
++                      options[arg] = True
++                      continue
++              if '-h' == arg or '--help' == arg:
++                      sys.stdout.write(usage + "\n\nKnown <options>:\n\n")
++                      for option, descr in known_options.items():
++                              sys.stdout.write("  " + option + "\t" + descr + 
"\n")
++                      sys.stdout.flush()
++                      sys.exit(os.EX_OK)
++              args.append(arg)
+ 
+       if len(args) != 1:
+               sys.stderr.write(usage + "\n")
+@@ -151,5 +176,5 @@ if __name__ == "__main__":
+ 
+       var_pattern = b'^(' + b'|'.join(var_pattern) + b')$'
+       filter_bash_environment(
+-              re.compile(var_pattern), file_in, file_out)
++              re.compile(var_pattern), file_in, file_out, options)
+       file_out.flush()
+diff --git a/bin/save-ebuild-env.sh b/bin/save-ebuild-env.sh
+index bb17382d4..af35a3327 100755
+--- a/bin/save-ebuild-env.sh
++++ b/bin/save-ebuild-env.sh
+@@ -53,7 +53,7 @@ __save_ebuild_env() {
+               einfo einfon ewarn eerror ebegin __eend eend KV_major \
+               KV_minor KV_micro KV_to_int get_KV has \
+               __has_phase_defined_up_to \
+-              hasv hasq __qa_source __qa_call \
++              hasv hasq __qa_source __qa_call __call-ebuildshell \
+               addread addwrite adddeny addpredict __sb_append_var \
+               use usev useq has_version portageq \
+               best_version use_with use_enable register_die_hook \
+diff --git a/man/make.conf.5 b/man/make.conf.5
+index b0c1aa4f2..568f350a0 100644
+--- a/man/make.conf.5
++++ b/man/make.conf.5
+@@ -408,6 +408,12 @@ exist). Also see the related \fIunmerge\-backup\fR 
feature.
+ Use locks to ensure that unsandboxed ebuild phases never execute
+ concurrently. Also see \fIparallel\-install\fR.
+ .TP
++.B ebuildshell
++Drop into an interactive shell for each phase function, meant for
++debugging.  Because the shell would normally be used to execute the
++phase function, commands like src_unpack or epatch are available in the
++interactive shell.  Use `die` to terminate the merge.
++.TP
+ .B fail\-clean
+ Clean up temporary files after a build failure. This is particularly useful
+ if you have \fBPORTAGE_TMPDIR\fR on tmpfs. If this feature is enabled, you
+diff --git a/pym/_emerge/AbstractEbuildProcess.py 
b/pym/_emerge/AbstractEbuildProcess.py
+index 370cac529..a521596e5 100644
+--- a/pym/_emerge/AbstractEbuildProcess.py
++++ b/pym/_emerge/AbstractEbuildProcess.py
+@@ -181,6 +181,7 @@ class AbstractEbuildProcess(SpawnProcess):
+                       self.fd_pipes = {}
+               null_fd = None
+               if 0 not in self.fd_pipes and \
++                      "ebuildshell" not in self.settings.features and \
+                       self.phase not in self._phases_interactive_whitelist 
and \
+                       "interactive" not in self.settings.get("PROPERTIES", 
"").split():
+                       null_fd = os.open('/dev/null', os.O_RDONLY)
+diff --git a/pym/portage/const.py b/pym/portage/const.py
+index 3c23c85ed..d9c57f300 100644
+--- a/pym/portage/const.py
++++ b/pym/portage/const.py
+@@ -161,6 +161,7 @@ SUPPORTED_FEATURES       = frozenset([
+       "distlocks",
+       "downgrade-backup",
+       "ebuild-locks",
++      "ebuildshell",
+       "fail-clean",
+       "fakeroot",
+       "fixlafiles",
+-- 
+2.16.1
+

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
new file mode 100644
index 0000000000..8e0864990d
--- /dev/null
+++ b/sys-apps/portage/files/portage-2.3.40-prefix-chaining.patch
@@ -0,0 +1,921 @@
+From 9c991762d6becb779925d59289eb0324f269ad18 Mon Sep 17 00:00:00 2001
+From: Michael Haubenwallner <[email protected]>
+Date: Thu, 23 Mar 2017 13:52:32 +0100
+Subject: [PATCH 2/2] add prefix-chaining support
+
+---
+ bin/install-qa-check.d/05prefix                    | 30 ++++++-
+ bin/phase-helpers.sh                               | 24 ++++++
+ pym/_emerge/actions.py                             |  6 +-
+ pym/_emerge/depgraph.py                            | 53 +++++++-----
+ pym/_emerge/resolver/output.py                     | 40 ++++++++-
+ pym/portage/_sets/__init__.py                      |  5 ++
+ pym/portage/const.py                               |  6 ++
+ pym/portage/dbapi/vartree.py                       | 34 ++++++--
+ pym/portage/dep/dep_check.py                       | 99 +++++++++++++++++++++-
+ .../package/ebuild/_config/LocationsManager.py     |  3 +
+ pym/portage/package/ebuild/config.py               | 62 ++++++++++++++
+ pym/portage/package/ebuild/doebuild.py             | 24 +++++-
+ pym/portage/package/ebuild/fetch.py                |  4 +
+ pym/portage/sync/controller.py                     | 27 +++---
+ pym/portage/util/_dyn_libs/LinkageMapELF.py        |  4 +-
+ 15 files changed, 373 insertions(+), 48 deletions(-)
+
+diff --git a/bin/install-qa-check.d/05prefix b/bin/install-qa-check.d/05prefix
+index 32561e263..0c1147367 100644
+--- a/bin/install-qa-check.d/05prefix
++++ b/bin/install-qa-check.d/05prefix
+@@ -79,16 +79,42 @@ install_qa_check_prefix() {
+               # unprefixed shebang, is the script directly in $PATH or an init
+               # script?
+               if [[ ":${PATH}:${EPREFIX}/etc/init.d:" == *":${fp}:"* ]] ; then
+-                      if [[ -e ${EROOT}${line[0]} || -e ${ED}${line[0]} ]] ; 
then
++                      
all_epfs="$PORTAGE_READONLY_EPREFIXES:$EPREFIX:$EROOT:$ED"
++                      save_IFS=$IFS
++                      IFS=:
++                      epfs=( $all_epfs )
++                      IFS=$save_IFS
++
++                      found=
++                      for x in "${epfs[@]}"; do
++                              [[ -z "${x}" ]] && continue
++                              check="${x}${line[0]}"
++
++                              # might already contain a prefix
++                              if [[ "${line[0]}" == "${x}"* ]]; then
++                                      check="${line[0]}"
++                              fi
++
++                              if [[ -e ${check} ]]; then
++                                      found="${check}"
++                              fi
++                      done
++
++                      if [[ -n ${found} ]] ; then
+                               # is it unprefixed, but we can just fix it 
because a
+                               # prefixed variant exists
+                               eqawarn "prefixing shebang of ${fn#${D}}"
++
++                              if [[ ${found} == "${ED}"* || ${found} == 
"${EROOT}"* ]]; then
++                                      found="${EPREFIX}${line[0]}"
++                              fi
++
+                               # statement is made idempotent on purpose, 
because
+                               # symlinks may point to the same target, and 
hence the
+                               # same real file may be sedded multiple times 
since we
+                               # read the shebangs in one go upfront for 
performance
+                               # reasons
+-                              sed -i -e '1s:^#! 
\?'"${line[0]}"':#!'"${EPREFIX}"${line[0]}':' "${rf}"
++                              sed -i -e '1s:^#! 
\?'"${line[0]}"':#!'"${found}"':' "${rf}"
+                               continue
+                       else
+                               # this is definitely wrong: script in $PATH and 
invalid shebang
+diff --git a/bin/phase-helpers.sh b/bin/phase-helpers.sh
+index 75d92b407..c32533fb3 100644
+--- a/bin/phase-helpers.sh
++++ b/bin/phase-helpers.sh
+@@ -934,6 +934,10 @@ ___best_version_and_has_version_common() {
+       fi
+       "${cmd[@]}"
+       local retval=$?
++      if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
++              ${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= 
'${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' '${FUNCNAME[1]}' 
'${READONLY_EPREFIX%:*}' '${atom}'"
++              retval=$?
++      fi
+       case "${retval}" in
+               0|1)
+                       return ${retval}
+@@ -1194,6 +1198,10 @@ if ___eapi_has_master_repositories; then
+                       output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" 
master_repositories "${EROOT}" "${repository}")
+               fi
+               retval=$?
++              if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
++                      output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' 
EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' master_repositories 
'${READONLY_EPREFIX%:*}' '${repository}'")
++                      retval=$?
++              fi
+               [[ -n ${output} ]] && echo "${output}"
+               case "${retval}" in
+                       0|1)
+@@ -1225,6 +1233,10 @@ if ___eapi_has_repository_path; then
+                       output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" 
get_repo_path "${EROOT}" "${repository}")
+               fi
+               retval=$?
++              if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
++                      output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' 
EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' get_repo_path 
'${READONLY_EPREFIX%:*}' '${repository}'")
++                      retval=$?
++              fi
+               [[ -n ${output} ]] && echo "${output}"
+               case "${retval}" in
+                       0|1)
+@@ -1255,6 +1267,10 @@ if ___eapi_has_available_eclasses; then
+                       output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" 
available_eclasses "${EROOT}" "${repository}")
+               fi
+               retval=$?
++              if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
++                      output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' 
EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' available_eclasses 
'${READONLY_EPREFIX%:*}' '${repository}'")
++                      retval=$?
++              fi
+               [[ -n ${output} ]] && echo "${output}"
+               case "${retval}" in
+                       0|1)
+@@ -1285,6 +1301,10 @@ if ___eapi_has_eclass_path; then
+               else
+                       output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" 
eclass_path "${EROOT}" "${repository}" "${eclass}")
+               fi
++              if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
++                      output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' 
EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' eclass_path 
'${READONLY_EPREFIX%:*}' '${repository}' '${eclass}'")
++                      retval=$?
++              fi
+               retval=$?
+               [[ -n ${output} ]] && echo "${output}"
+               case "${retval}" in
+@@ -1316,6 +1336,10 @@ if ___eapi_has_license_path; then
+               else
+                       output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" 
license_path "${EROOT}" "${repository}" "${license}")
+               fi
++              if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then
++                      output=(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' 
EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' license_path 
'${READONLY_EPREFIX%:*}' '${repository}' '${license}'")
++                      retval=$?
++              fi
+               retval=$?
+               [[ -n ${output} ]] && echo "${output}"
+               case "${retval}" in
+diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py
+index 432fc57e3..764462fc5 100644
+--- a/pym/_emerge/actions.py
++++ b/pym/_emerge/actions.py
+@@ -39,7 +39,7 @@ from portage import os
+ from portage import shutil
+ from portage import eapi_is_supported, _encodings, _unicode_decode
+ from portage.cache.cache_errors import CacheError
+-from portage.const import EPREFIX
++from portage.const import EPREFIX, BPREFIX
+ from portage.const import GLOBAL_CONFIG_PATH, VCS_DIRS, 
_DEPCLEAN_LIB_CHECK_DEFAULT
+ from portage.const import SUPPORTED_BINPKG_FORMATS, TIMESTAMP_FORMAT
+ from portage.dbapi.dep_expand import dep_expand
+@@ -65,6 +65,7 @@ from portage.util.SlotObject import SlotObject
+ from portage.util._async.run_main_scheduler import run_main_scheduler
+ from portage.util._async.SchedulerInterface import SchedulerInterface
+ from portage.util._eventloop.global_event_loop import global_event_loop
++from portage.util._path import exists_raise_eaccess
+ from portage._global_updates import _global_updates
+ from portage.sync.old_tree_timestamp import old_tree_timestamp_warn
+ from portage.localization import _
+@@ -2672,6 +2673,9 @@ def missing_sets_warning(root_config, missing_sets):
+       if portage.const.EPREFIX:
+               global_config_path = os.path.join(portage.const.EPREFIX,
+                               portage.const.GLOBAL_CONFIG_PATH.lstrip(os.sep))
++              if not exists_raise_eaccess(global_config_path) and 
portage.const.BPREFIX:
++                      global_config_path = os.path.join(portage.const.BPREFIX,
++                    portage.const.GLOBAL_CONFIG_PATH.lstrip(os.sep))
+       msg.append("        This usually means that '%s'" % \
+               (os.path.join(global_config_path, "sets/portage.conf"),))
+       msg.append("        is missing or corrupt.")
+diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
+index f7bac69f9..a6eb0d3d4 100644
+--- a/pym/_emerge/depgraph.py
++++ b/pym/_emerge/depgraph.py
+@@ -3355,19 +3355,19 @@ class depgraph(object):
+               # _dep_disjunctive_stack first, so that choices for build-time
+               # deps influence choices for run-time deps (bug 639346).
+               deps = (
+-                      (myroot, edepend["RDEPEND"],
++                      (myroot, "RDEPEND",
+                               self._priority(runtime=True)),
+-                      (myroot, edepend["PDEPEND"],
++                      (myroot, "PDEPEND",
+                               self._priority(runtime_post=True)),
+-                      (depend_root, edepend["DEPEND"],
++                      (depend_root, "DEPEND",
+                               self._priority(buildtime=True,
+                               optional=(pkg.built or ignore_depend_deps),
+                               ignored=ignore_depend_deps)),
+-                      (self._frozen_config._running_root.root, 
edepend["HDEPEND"],
++                      (self._frozen_config._running_root.root, "HDEPEND",
+                               self._priority(buildtime=True,
+                               optional=(pkg.built or ignore_hdepend_deps),
+                               ignored=ignore_hdepend_deps)),
+-                      (self._frozen_config._running_root.root, 
edepend["BDEPEND"],
++                      (self._frozen_config._running_root.root, "BDEPEND",
+                               self._priority(buildtime=True,
+                               optional=(pkg.built or ignore_bdepend_deps),
+                               ignored=ignore_bdepend_deps)),
+@@ -3375,7 +3375,8 @@ class depgraph(object):
+ 
+               debug = "--debug" in self._frozen_config.myopts
+ 
+-              for dep_root, dep_string, dep_priority in deps:
++              for dep_root, dep_type, dep_priority in deps:
++                              dep_string = edepend[dep_type]
+                               if not dep_string:
+                                       continue
+                               if debug:
+@@ -3413,7 +3414,7 @@ class depgraph(object):
+ 
+                               try:
+                                       dep_string = 
list(self._queue_disjunctive_deps(
+-                                              pkg, dep_root, dep_priority, 
dep_string))
++                                              pkg, dep_root, dep_priority, 
dep_string, dep_type))
+                               except portage.exception.InvalidDependString as 
e:
+                                       if pkg.installed:
+                                               
self._dynamic_config._masked_installed.add(pkg)
+@@ -3428,14 +3429,14 @@ class depgraph(object):
+ 
+                               if not self._add_pkg_dep_string(
+                                       pkg, dep_root, dep_priority, dep_string,
+-                                      allow_unsatisfied):
++                                      allow_unsatisfied, dep_type):
+                                       return 0
+ 
+               self._dynamic_config._traversed_pkg_deps.add(pkg)
+               return 1
+ 
+       def _add_pkg_dep_string(self, pkg, dep_root, dep_priority, dep_string,
+-              allow_unsatisfied):
++              allow_unsatisfied, dep_type=None):
+               _autounmask_backup = self._dynamic_config._autounmask
+               if dep_priority.optional or dep_priority.ignored:
+                       # Temporarily disable autounmask for deps that
+@@ -3444,7 +3445,7 @@ class depgraph(object):
+               try:
+                       return self._wrapped_add_pkg_dep_string(
+                               pkg, dep_root, dep_priority, dep_string,
+-                              allow_unsatisfied)
++                              allow_unsatisfied, dep_type)
+               finally:
+                       self._dynamic_config._autounmask = _autounmask_backup
+ 
+@@ -3480,7 +3481,7 @@ class depgraph(object):
+                       not slot_operator_rebuild
+ 
+       def _wrapped_add_pkg_dep_string(self, pkg, dep_root, dep_priority,
+-              dep_string, allow_unsatisfied):
++              dep_string, allow_unsatisfied, dep_type=None):
+               if isinstance(pkg.depth, int):
+                       depth = pkg.depth + 1
+               else:
+@@ -3504,7 +3505,7 @@ class depgraph(object):
+               try:
+                       selected_atoms = self._select_atoms(dep_root,
+                               dep_string, myuse=self._pkg_use_enabled(pkg), 
parent=pkg,
+-                              strict=strict, priority=dep_priority)
++                              strict=strict, priority=dep_priority, 
dep_type=dep_type)
+               except portage.exception.InvalidDependString:
+                       if pkg.installed:
+                               self._dynamic_config._masked_installed.add(pkg)
+@@ -3811,7 +3812,7 @@ class depgraph(object):
+                                       child_pkgs.sort()
+                               yield (atom, child_pkgs[-1])
+ 
+-      def _queue_disjunctive_deps(self, pkg, dep_root, dep_priority, 
dep_struct):
++      def _queue_disjunctive_deps(self, pkg, dep_root, dep_priority, 
dep_struct, dep_type=None):
+               """
+               Queue disjunctive (virtual and ||) deps in 
self._dynamic_config._dep_disjunctive_stack.
+               Yields non-disjunctive deps. Raises InvalidDependString when
+@@ -3820,33 +3821,33 @@ class depgraph(object):
+               for x in dep_struct:
+                       if isinstance(x, list):
+                               if x and x[0] == "||":
+-                                      self._queue_disjunction(pkg, dep_root, 
dep_priority, [x])
++                                      self._queue_disjunction(pkg, dep_root, 
dep_priority, [x], dep_type)
+                               else:
+                                       for y in self._queue_disjunctive_deps(
+-                                              pkg, dep_root, dep_priority, x):
++                                              pkg, dep_root, dep_priority, x, 
dep_type):
+                                               yield y
+                       else:
+                               # Note: Eventually this will check for 
PROPERTIES=virtual
+                               # or whatever other metadata gets implemented 
for this
+                               # purpose.
+                               if x.cp.startswith('virtual/'):
+-                                      self._queue_disjunction(pkg, dep_root, 
dep_priority, [x])
++                                      self._queue_disjunction(pkg, dep_root, 
dep_priority, [x], dep_type)
+                               else:
+                                       yield x
+ 
+-      def _queue_disjunction(self, pkg, dep_root, dep_priority, dep_struct):
++      def _queue_disjunction(self, pkg, dep_root, dep_priority, dep_struct, 
dep_type=None):
+               self._dynamic_config._dep_disjunctive_stack.append(
+-                      (pkg, dep_root, dep_priority, dep_struct))
++                      (pkg, dep_root, dep_priority, dep_struct, dep_type))
+ 
+       def _pop_disjunction(self, allow_unsatisfied):
+               """
+               Pop one disjunctive dep from 
self._dynamic_config._dep_disjunctive_stack, and use it to
+               populate self._dynamic_config._dep_stack.
+               """
+-              pkg, dep_root, dep_priority, dep_struct = \
++              pkg, dep_root, dep_priority, dep_struct, dep_type = \
+                       self._dynamic_config._dep_disjunctive_stack.pop()
+               if not self._add_pkg_dep_string(
+-                      pkg, dep_root, dep_priority, dep_struct, 
allow_unsatisfied):
++                      pkg, dep_root, dep_priority, dep_struct, 
allow_unsatisfied, dep_type):
+                       return 0
+               return 1
+ 
+@@ -4699,7 +4700,7 @@ class depgraph(object):
+               return self._select_atoms_highest_available(*pargs, **kwargs)
+ 
+       def _select_atoms_highest_available(self, root, depstring,
+-              myuse=None, parent=None, strict=True, trees=None, 
priority=None):
++              myuse=None, parent=None, strict=True, trees=None, 
priority=None, dep_type=None):
+               """This will raise InvalidDependString if necessary. If trees is
+               None then self._dynamic_config._filtered_trees is used."""
+ 
+@@ -4722,6 +4723,13 @@ class depgraph(object):
+               pkgsettings = self._frozen_config.pkgsettings[root]
+               if trees is None:
+                       trees = self._dynamic_config._filtered_trees
++
++              # this one is needed to guarantee good readonly root
++              # resolution display in the merge list. required since
++              # parent (below) can be None
++              trees[root]["disp_parent"] = parent
++
++
+               mytrees = trees[root]
+               atom_graph = digraph()
+               if True:
+@@ -4753,7 +4761,7 @@ class depgraph(object):
+ 
+                               mycheck = portage.dep_check(depstring, None,
+                                       pkgsettings, myuse=myuse,
+-                                      myroot=root, trees=trees)
++                                      myroot=root, trees=trees, 
dep_type=dep_type)
+                       finally:
+                               # restore state
+                               self._dynamic_config._autounmask = 
_autounmask_backup
+@@ -4829,6 +4837,7 @@ class depgraph(object):
+                                                       continue
+                                               node_stack.append((child_node, 
node, child_atom))
+ 
++              trees[root].pop("disp_parent")
+               return selected_atoms
+ 
+       def _expand_virt_from_graph(self, root, atom):
+diff --git a/pym/_emerge/resolver/output.py b/pym/_emerge/resolver/output.py
+index 24340576c..4a1741f3a 100644
+--- a/pym/_emerge/resolver/output.py
++++ b/pym/_emerge/resolver/output.py
+@@ -22,11 +22,12 @@ from portage.localization import localized_size
+ from portage.package.ebuild.config import _get_feature_flags
+ from portage.package.ebuild._spawn_nofetch import spawn_nofetch
+ from portage.output import ( blue, colorize, create_color_func,
+-      darkblue, darkgreen, green, nc_len, teal)
++      darkblue, darkgreen, green, nc_len, teal, yellow, turquoise)
+ bad = create_color_func("BAD")
+ from portage._sets.base import InternalPackageSet
+ from portage.util import writemsg_stdout
+ from portage.versions import best, cpv_getversion
++from portage.dep.dep_check import ro_selected
+ 
+ from _emerge.Blocker import Blocker
+ from _emerge.create_world_atom import create_world_atom
+@@ -563,6 +564,42 @@ class Display(object):
+                       writemsg_stdout("%s\n" % (pkg,), noiselevel=-1)
+               return
+ 
++      def print_readonly_prefix(self):
++              """Performs the actual output printing for the readonly prefix
++              information stuff
++              """
++              out = sys.stdout
++
++        # print readonly selected packages
++              if len(ro_selected) > 0:
++                      out.write("\n%s\n\n" % (darkgreen("Packages resolved 
from readonly installations:")))
++
++              ro_mismatch_warning = False
++              ro_dupcheck = []
++              for x in ro_selected:
++                      tmp_type = x["type"].replace("END","")
++                      while len(tmp_type) < 4:
++                              tmp_type += " "
++                      if x["parent"] and str(x["atom"]) not in ro_dupcheck:
++                              out.write("[%s %s] %s %s %s (%s by %s)" % 
(teal("readonly"),
++                                      green(tmp_type), 
green(str(x["matches"][0])), yellow("from"),
++                                      blue(x["ro_root"]), 
turquoise(str(x["atom"])), green(x["parent"].cpv)))
++
++                              ro_dupcheck.append(str(x["atom"]))
++
++                              if x["host_mismatch"]:
++                                      ro_mismatch_warning = True
++                                      out.write(" %s\n" % (red("**")))
++                              else:
++                                      out.write("\n")
++
++              if ro_mismatch_warning:
++                      out.write("\n%s:" % (red("**")))
++                      out.write(yellow(" WARNING: packages marked with ** 
have been resolved as a\n"))
++                      out.write(yellow("    runtime dependency, but the CHOST 
variable for the parent\n"))
++                      out.write(yellow("    and dependency package don't 
match. This could cause link\n"))
++                      out.write(yellow("    errors. It is recommended to use 
RDEPEND READONLY_EPREFIX's\n"))
++                      out.write(yellow("    only with matching CHOST portage 
instances.\n"))
+ 
+       def print_verbose(self, show_repos):
+               """Prints the verbose output to std_out
+@@ -913,6 +950,7 @@ class Display(object):
+               show_repos = self.quiet_repo_display and repoadd_set and 
repoadd_set != set(["0"])
+ 
+               # now finally print out the messages
++              self.print_readonly_prefix()
+               self.print_messages(show_repos)
+               self.print_blockers()
+               if self.conf.verbosity == 3:
+diff --git a/pym/portage/_sets/__init__.py b/pym/portage/_sets/__init__.py
+index 2c9bf9715..6a2784207 100644
+--- a/pym/portage/_sets/__init__.py
++++ b/pym/portage/_sets/__init__.py
+@@ -21,6 +21,7 @@ from portage.const import _ENABLE_SET_CONFIG
+ from portage.exception import PackageSetNotFound
+ from portage.localization import _
+ from portage.util import writemsg_level
++from portage.util._path import exists_raise_eaccess
+ from portage.util.configparser import (SafeConfigParser,
+       NoOptionError, ParsingError, read_configs)
+ 
+@@ -281,6 +282,10 @@ def load_default_config(settings, trees):
+       if portage.const.EPREFIX:
+               global_config_path = os.path.join(portage.const.EPREFIX,
+                       GLOBAL_CONFIG_PATH.lstrip(os.sep))
++              if not exists_raise_eaccess(global_config_path) and 
portage.const.BPREFIX:
++                      global_config_path = os.path.join(portage.const.BPREFIX,
++                              GLOBAL_CONFIG_PATH.lstrip(os.sep))
++
+       vcs_dirs = [_unicode_encode(x, encoding=_encodings['fs']) for x in 
VCS_DIRS]
+       def _getfiles():
+               for path, dirs, files in 
os.walk(os.path.join(global_config_path, "sets")):
+diff --git a/pym/portage/const.py b/pym/portage/const.py
+index d9c57f300..a3d927c3b 100644
+--- a/pym/portage/const.py
++++ b/pym/portage/const.py
+@@ -190,6 +190,7 @@ SUPPORTED_FEATURES       = frozenset([
+       "notitles",
+       "parallel-fetch",
+       "parallel-install",
++      "prefix-chaining",
+       "prelink-checksums",
+       "preserve-libs",
+       "protect-owned",
+@@ -241,6 +242,11 @@ MANIFEST2_IDENTIFIERS    = ("AUX", "MISC", "DIST", 
"EBUILD")
+ #EPREFIX = ""
+ # END PREFIX LOCAL
+ 
++BPREFIX = EPREFIX
++
++# --prefix commandline arg always rules, ends up in os.environ["EPREFIX"]
++if "EPREFIX" in os.environ:
++    os.environ["PORTAGE_OVERRIDE_EPREFIX"] = os.environ["EPREFIX"]
+ # pick up EPREFIX from the environment if set
+ if "PORTAGE_OVERRIDE_EPREFIX" in os.environ:
+       EPREFIX = os.environ["PORTAGE_OVERRIDE_EPREFIX"]
+diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
+index 77a72b5b1..f20c6763e 100644
+--- a/pym/portage/dbapi/vartree.py
++++ b/pym/portage/dbapi/vartree.py
+@@ -196,8 +196,19 @@ class vardbapi(dbapi):
+               self._counter_path = os.path.join(self._eroot,
+                       CACHE_PATH, "counter")
+ 
+-              self._plib_registry = PreservedLibsRegistry(settings["ROOT"],
+-                      os.path.join(self._eroot, PRIVATE_PATH, 
"preserved_libs_registry"))
++              plibreg_path = os.path.join(self._eroot, PRIVATE_PATH, 
"preserved_libs_registry")
++
++              if vartree:
++                      self._kill_eprefix = vartree._kill_eprefix
++              else:
++                      self._kill_eprefix = False
++
++              if self._kill_eprefix:
++                      self._aux_cache_filename = 
self._aux_cache_filename.replace(EPREFIX, "")
++                      self._counter_path = 
self._counter_path.replace(EPREFIX, "")
++                      plibreg_path = plibreg_path.replace(EPREFIX, "")
++
++              self._plib_registry = PreservedLibsRegistry(settings["ROOT"], 
plibreg_path)
+               self._linkmap = LinkageMap(self)
+               chost = self.settings.get('CHOST')
+               if not chost:
+@@ -238,6 +249,9 @@ class vardbapi(dbapi):
+               # This is an optimized hotspot, so don't use unicode-wrapped
+               # os module and don't use os.path.join().
+               rValue = self._eroot + VDB_PATH + _os.sep + mykey
++              if self._kill_eprefix:
++                      rValue = rValue.replace(EPREFIX, "")
++
+               if filename is not None:
+                       # If filename is always relative, we can do just
+                       # rValue += _os.sep + filename
+@@ -502,6 +516,9 @@ class vardbapi(dbapi):
+               returnme = []
+               basepath = os.path.join(self._eroot, VDB_PATH) + os.path.sep
+ 
++              if self._kill_eprefix:
++                      basepath = os.path.join(self.root, 
basepath.replace(EPREFIX, ""))
++
+               if use_cache:
+                       from portage import listdir
+               else:
+@@ -598,11 +615,17 @@ class vardbapi(dbapi):
+                               del self.matchcache[mycat]
+                       return list(self._iter_match(mydep,
+                               self.cp_list(mydep.cp, use_cache=use_cache)))
++
++              _tmp_path = os.path.join(self._eroot, VDB_PATH, mycat)
++
++              if self._kill_eprefix:
++                      _tmp_path = _tmp_path.replace(EPREFIX, "")
++
+               try:
+                       if sys.hexversion >= 0x3030000:
+-                              curmtime = os.stat(os.path.join(self._eroot, 
VDB_PATH, mycat)).st_mtime_ns
++                              curmtime = os.stat(_tmp_path).st_mtime_ns
+                       else:
+-                              curmtime = os.stat(os.path.join(self._eroot, 
VDB_PATH, mycat)).st_mtime
++                              curmtime = os.stat(_tmp_path).st_mtime
+               except (IOError, OSError):
+                       curmtime=0
+ 
+@@ -1450,7 +1473,7 @@ class vardbapi(dbapi):
+ class vartree(object):
+       "this tree will scan a var/db/pkg database located at root (passed to 
init)"
+       def __init__(self, root=None, virtual=DeprecationWarning, 
categories=None,
+-              settings=None):
++              settings=None, kill_eprefix=None):
+ 
+               if settings is None:
+                       settings = portage.settings
+@@ -1468,6 +1491,7 @@ class vartree(object):
+                               " constructor is unused",
+                               DeprecationWarning, stacklevel=2)
+ 
++              self._kill_eprefix = kill_eprefix
+               self.settings = settings
+               self.dbapi = vardbapi(settings=settings, vartree=self)
+               self.populated = 1
+diff --git a/pym/portage/dep/dep_check.py b/pym/portage/dep/dep_check.py
+index 2896e2389..c700a3651 100644
+--- a/pym/portage/dep/dep_check.py
++++ b/pym/portage/dep/dep_check.py
+@@ -298,6 +298,95 @@ class _dep_choice(SlotObject):
+       __slots__ = ('atoms', 'slot_map', 'cp_map', 'all_available',
+               'all_installed_slots', 'new_slot_count')
+ 
++ro_trees={}
++ro_vartrees={}
++ro_selected=[]
++
++def dep_match_readonly_roots(settings, atom, dep_type, parent=None):
++   if len(ro_trees) < len(settings.readonly_prefixes):
++       # MDUFT: create additional vartrees for every readonly root here.
++       # the ro_vartrees instances are created below as they are needed to
++       # avoid reading vartrees of portage instances which aren't required
++       # while resolving this dependencies.
++       for type in ("DEPEND","RDEPEND", "PDEPEND"):
++           ro_trees[type] = []
++
++           for ro_root, ro_dep_types in settings.readonly_prefixes.items():
++               if type in ro_dep_types:
++                   ro_trees[type].append(ro_root)
++
++   if len(ro_trees) == 0:
++       return []
++
++   matches = []
++
++   for ro_root in ro_trees[dep_type]:
++       if not ro_root in ro_vartrees:
++           # target_root=ro_root ok? or should it be the real target_root?
++           _tmp_settings = portage.config(config_root=ro_root, 
target_root=ro_root,
++               config_incrementals=portage.const.INCREMENTALS)
++
++           ro_vartrees[ro_root] = portage.vartree(root=ro_root,
++               categories=_tmp_settings.categories,
++               settings=_tmp_settings, kill_eprefix=True)
++
++       ro_matches = ro_vartrees[ro_root].dbapi.match(atom)
++
++       if ro_matches:
++           ro_host_mismatch = False
++           if dep_type is "RDEPEND":
++               # we need to assure binary compatability, so it needs to be
++               # the same CHOST! But how? for now i cannot do anything...
++               if parent and parent.metadata["CHOST"] != 
ro_vartrees[ro_root].settings.get("CHOST", ""):
++                   # provocate a big fat warning in the list of external 
packages.
++                   ro_host_mismatch = True
++               pass
++
++           matches.append({ "ro_root": ro_root, "atom": atom, "matches": 
ro_matches,
++               "type": dep_type, "parent": parent, "host_mismatch": 
ro_host_mismatch })
++
++   return matches
++
++def dep_wordreduce_readonly(reduced, unreduced, settings, dep_type, parent):
++   for mypos, token in enumerate(unreduced):
++       # recurse if it's a list.
++       if isinstance(reduced[mypos], list):
++           reduced[mypos] = dep_wordreduce_readonly(reduced[mypos],
++               unreduced[mypos], settings, dep_type, parent)
++
++       # do nothing if it's satisfied already.
++       elif not reduced[mypos]:
++           ro_matches = dep_match_readonly_roots(settings, unreduced[mypos], 
dep_type, parent)
++
++           if ro_matches:
++               # TODO: select a match if there are more than one?
++               # for now, the first match is taken...
++               ro_selected.append(ro_matches[0])
++               reduced[mypos] = True
++
++   return reduced
++
++# this may be better placed somewhere else, but i put it here for now, to
++# keep all functions in the patch on one big heap.
++def readonly_pathmatch_any(settings, path):
++   path = path.lstrip('/')
++   # first try locally, and match that if it exists.
++   if os.path.exists(os.path.join(EPREFIX,path)):
++       return os.path.join(EPREFIX,path)
++
++   # after that try all readonly roots where DEPEND is allowed. this makes
++   # sure that executing binaries is possible from there.
++   for ro_root, ro_deps in settings.readonly_roots.items():
++       if "DEPEND" in ro_deps:
++           print(" --- checking %s --- " % (os.path.join(ro_root,path)))
++           if os.path.exists(os.path.join(ro_root,path)):
++               return os.path.join(ro_root,path)
++           break
++
++   # as a fallback make the string the same as it was originally.
++   # even though this path doesn't exist.
++   return os.path.join(EPREFIX,path)
++
+ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
+       minimize_slots=False):
+       """
+@@ -725,7 +814,7 @@ def dep_zapdeps(unreduced, reduced, myroot, 
use_binaries=0, trees=None,
+       assert(False) # This point should not be reachable
+ 
+ def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, 
myuse=None,
+-      use_cache=1, use_binaries=0, myroot=None, trees=None):
++      use_cache=1, use_binaries=0, myroot=None, trees=None, dep_type=None):
+       """
+       Takes a depend string, parses it, and selects atoms.
+       The myroot parameter is unused (use mysettings['EROOT'] instead).
+@@ -829,6 +918,14 @@ def dep_check(depstring, mydbapi, mysettings, use="yes", 
mode=None, myuse=None,
+       writemsg("mysplit:  %s\n" % (mysplit), 1)
+       writemsg("mysplit2: %s\n" % (mysplit2), 1)
+ 
++      if dep_type is not None:
++              mysplit2=dep_wordreduce_readonly(unreduced=mysplit[:],
++                              reduced=mysplit2, settings=mysettings,
++                              dep_type=dep_type, 
parent=trees[myroot].get("disp_parent"))
++
++              writemsg("\n", 1)
++              writemsg("mysplit2 after readonly reduce: %s\n" % (mysplit2), 1)
++
+       selected_atoms = dep_zapdeps(mysplit, mysplit2, myroot,
+               use_binaries=use_binaries, trees=trees, minimize_slots=dnf)
+ 
+diff --git a/pym/portage/package/ebuild/_config/LocationsManager.py 
b/pym/portage/package/ebuild/_config/LocationsManager.py
+index f7d7209ff..e37e5b1a9 100644
+--- a/pym/portage/package/ebuild/_config/LocationsManager.py
++++ b/pym/portage/package/ebuild/_config/LocationsManager.py
+@@ -326,6 +326,9 @@ class LocationsManager(object):
+               if portage.const.EPREFIX:
+                       self.global_config_path = 
os.path.join(portage.const.EPREFIX,
+                               GLOBAL_CONFIG_PATH.lstrip(os.sep))
++                      if not exists_raise_eaccess(self.global_config_path) 
and portage.const.BPREFIX:
++                              self.global_config_path = 
os.path.join(portage.const.BPREFIX,
++                                      GLOBAL_CONFIG_PATH.lstrip(os.sep))
+ 
+       def set_port_dirs(self, portdir, portdir_overlay):
+               self.portdir = portdir
+diff --git a/pym/portage/package/ebuild/config.py 
b/pym/portage/package/ebuild/config.py
+index 059aa83ce..3bf6049e8 100644
+--- a/pym/portage/package/ebuild/config.py
++++ b/pym/portage/package/ebuild/config.py
+@@ -309,6 +309,7 @@ class config(object):
+                       self.features = features_set(self)
+                       self.features._features = 
copy.deepcopy(clone.features._features)
+                       self._features_overrides = 
copy.deepcopy(clone._features_overrides)
++                      self.readonly_prefixes = 
copy.deepcopy(clone.readonly_prefixes)
+ 
+                       #Strictly speaking _license_manager is not immutable. 
Users need to ensure that
+                       #extract_global_changes() is called right after 
__init__ (if at all).
+@@ -969,6 +970,63 @@ class config(object):
+ 
+                       self._validate_commands()
+ 
++                      # expand READONLY_EPREFIX to a list of all readonly 
portage instances
++                      # all the way down to the last one. beware that ATM a 
deeper instance
++                      # in the chain can provide more than the toplevel! this 
means that
++                      # if you only inherit DEPENDS from one instance, that 
instance may
++                      # inherit RDEPENDs from another one, making the 
top-level instance
++                      # inherit RDEPENDs from there too - even if the 
intermediate prefix
++                      # does not do this.
++                      self.readonly_prefixes = {}
++                      ro_cfg_root = config_root
++                      ro_widest_depset = set(['DEPEND', 'RDEPEND', 'PDEPEND'])
++
++                      while ro_cfg_root:
++                              ro_make_conf_paths = [
++                                      os.path.join(ro_cfg_root, 'etc', 
'make.conf'),
++                                      os.path.join(ro_cfg_root, 
MAKE_CONF_FILE)
++                              ]
++                              try:
++                                      if 
os.path.samefile(*ro_make_conf_paths):
++                                              ro_make_conf_paths.pop()
++                              except OSError:
++                                      pass
++
++                              ro_cfg_root = None
++                              for ro_make_conf in ro_make_conf_paths:
++                                      if not os.path.exists(ro_make_conf):
++                                              continue
++
++                                      ro_cfg = getconfig(ro_make_conf, 
tolerant=True, allow_sourcing=True)
++                                      if not "READONLY_EPREFIX" in ro_cfg:
++                                              continue
++
++                                      if not 
ro_cfg["READONLY_EPREFIX"].find(":"):
++                                              raise 
portage.exception.InvalidReadonlyERoot("ERROR: malformed READONLY_EPREFIX in 
%s" % (ro_make_conf))
++
++                                      if ro_cfg_root is not None:
++                                              raise 
portage.exception.InvalidReadonlyERoot("ERROR: duplicate READONLY_EPREFIX in %s 
and %s" % tuple(ro_make_conf_paths))
++
++                                      (ro_cfg_root,ro_cfg_root_deps) = 
ro_cfg["READONLY_EPREFIX"].rsplit(":",1)
++
++                                      if not os.path.exists(ro_cfg_root):
++                                              raise 
portage.exception.InvalidReadonlyERoot("ERROR: malformed READONLY_EPREFIX in 
%s: %s does not exist!" % (ro_make_conf, ro_cfg_root))
++
++                                      if os.path.samefile(ro_cfg_root, 
config_root):
++                                              raise 
portage.exception.InvalidReadonlyERoot("ERROR: cannot add this instance (%s) as 
READONLY_EPREFIX in %s." % (ro_cfg_root, ro_make_conf))
++
++                                      if ro_cfg_root in 
self.readonly_prefixes:
++                                              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]))
++
++                                      # intersect the widest depset with the 
current one to strip down
++                                      # the allowed dependency resolution to 
not be wider than the
++                                      # next higher one. this way we can 
prevent for a given prefix
++                                      # to resolve RDEPENDs from a prefix 
with a different CHOST that
++                                      # is a few levels deeper in the chain.
++                                      ro_widest_depset = 
set(ro_cfg_root_deps.split(",")) & ro_widest_depset
++                                      self.readonly_prefixes[ro_cfg_root] = 
ro_widest_depset
++                              pass
++
+                       for k in self._case_insensitive_vars:
+                               if k in self:
+                                       self[k] = self[k].lower()
+@@ -2771,6 +2829,10 @@ class config(object):
+               if not (src_phase and eapi_attrs.broot):
+                       mydict.pop("BROOT", None)
+ 
++              # populate with PORTAGE_READONLY_EPREFIXES
++              if self.readonly_prefixes and len(self.readonly_prefixes) > 0:
++                      mydict["PORTAGE_READONLY_EPREFIXES"] = 
':'.join(self.readonly_prefixes)
++
+               # Prefix variables are supported beginning with EAPI 3, or when
+               # force-prefix is in FEATURES, since older EAPIs would 
otherwise be
+               # useless with prefix configurations. This brings compatibility 
with
+diff --git a/pym/portage/package/ebuild/doebuild.py 
b/pym/portage/package/ebuild/doebuild.py
+index f8b784d6b..a6548a43b 100644
+--- a/pym/portage/package/ebuild/doebuild.py
++++ b/pym/portage/package/ebuild/doebuild.py
+@@ -52,6 +52,7 @@ from portage import bsd_chflags, \
+       unmerge, _encodings, _os_merge, \
+       _shell_quote, _unicode_decode, _unicode_encode
+ from portage.const import EBUILD_SH_ENV_FILE, EBUILD_SH_ENV_DIR, \
++    GLOBAL_CONFIG_PATH, \
+       EBUILD_SH_BINARY, INVALID_ENV_FILE, MISC_SH_BINARY, 
PORTAGE_PYM_PACKAGES, EPREFIX, MACOSSANDBOX_PROFILE
+ from portage.data import portage_gid, portage_uid, secpass, \
+       uid, userpriv_groups
+@@ -73,6 +74,7 @@ from portage.package.ebuild.prepare_build_dirs import 
prepare_build_dirs
+ from portage.process import find_binary
+ from portage.util import ( apply_recursive_permissions,
+       apply_secpass_permissions,
++      getconfig,
+       noiselimit,
+       shlex_split,
+       varexpand,
+@@ -80,6 +82,7 @@ from portage.util import ( apply_recursive_permissions,
+       writemsg_stdout,
+       write_atomic
+       )
++from portage.util._path import exists_raise_eaccess
+ from portage.util.cpuinfo import get_cpu_count
+ from portage.util.lafilefixer import rewrite_lafile
+ from portage.util.compression_probe import _compressors
+@@ -243,8 +246,27 @@ def _doebuild_path(settings, eapi=None):
+ 
+       for x in portage_bin_path:
+               path.append(os.path.join(x, "ebuild-helpers"))
++
++      # PREFIX CHAINING: append default path for all prefixes involved
++      pfxs = [ eprefix ]
++      pfxs.extend(settings.readonly_prefixes)
++      for prefix in pfxs:
++              global_config_path = os.path.join(prefix, 
GLOBAL_CONFIG_PATH.lstrip(os.sep))
++              make_globals_path = os.path.join(global_config_path, 
"make.globals")
++              if exists_raise_eaccess(make_globals_path):
++                      expand_map = { "EPREFIX": prefix }
++                      pxcfg = getconfig(make_globals_path, True, expand_map)
++                      pxdefp = [x for x in pxcfg.get("DEFAULT_PATH", 
"").split(":") if x]
++                      for x in pxdefp:
++                              if x.startswith(prefix) and not x in path:
++                                      path.append(x)
++              else:
++                      pxdefs = [prefix + "/usr/sbin", prefix + "/usr/bin", 
prefix + "/sbin", prefix + "/bin"]
++                      path.extend(pxdefs)
++      # END PREFIX CHAINING
++
+       path.extend(prerootpath)
+-      path.extend(defaultpath)
++      # path.extend(defaultpath) # PREFIX CHAINING appends the default path 
for involved prefixes above
+       path.extend(rootpath)
+       path.extend(extrapath)
+       # END PREFIX LOCAL
+diff --git a/pym/portage/package/ebuild/fetch.py 
b/pym/portage/package/ebuild/fetch.py
+index 265d0c9fc..2ec6ff472 100644
+--- a/pym/portage/package/ebuild/fetch.py
++++ b/pym/portage/package/ebuild/fetch.py
+@@ -43,6 +43,7 @@ from portage.output import colorize, EOutput
+ from portage.util import apply_recursive_permissions, \
+       apply_secpass_permissions, ensure_dirs, grabdict, shlex_split, \
+       varexpand, writemsg, writemsg_level, writemsg_stdout
++from portage.util._path import exists_raise_eaccess
+ from portage.process import spawn
+ 
+ _userpriv_spawn_kwargs = (
+@@ -874,6 +875,9 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0,
+                               global_config_path = GLOBAL_CONFIG_PATH
+                               if portage.const.EPREFIX:
+                                       global_config_path = 
os.path.join(portage.const.EPREFIX,
++                                              
GLOBAL_CONFIG_PATH.lstrip(os.sep))
++                                      if not 
exists_raise_eaccess(global_config_path) and portage.const.BPREFIX:
++                                              global_config_path = 
os.path.join(portage.const.BPREFIX,
+                                                       
GLOBAL_CONFIG_PATH.lstrip(os.sep))
+ 
+                               missing_file_param = False
+diff --git a/pym/portage/sync/controller.py b/pym/portage/sync/controller.py
+index 3bccf6f74..cacd63797 100644
+--- a/pym/portage/sync/controller.py
++++ b/pym/portage/sync/controller.py
+@@ -94,19 +94,20 @@ class SyncManager(object):
+               self.module_controller = portage.sync.module_controller
+               self.module_names = self.module_controller.module_names
+               self.hooks = {}
+-              for _dir in ["repo.postsync.d", "postsync.d"]:
+-                      postsync_dir = 
os.path.join(self.settings["PORTAGE_CONFIGROOT"],
+-                              portage.USER_CONFIG_PATH, _dir)
+-                      hooks = OrderedDict()
+-                      for filepath in util._recursive_file_list(postsync_dir):
+-                              name = 
filepath.split(postsync_dir)[1].lstrip(os.sep)
+-                              if os.access(filepath, os.X_OK):
+-                                      hooks[filepath] = name
+-                              else:
+-                                      writemsg_level(" %s %s hook: '%s' is 
not executable\n"
+-                                              % (warn("*"), _dir, 
_unicode_decode(name),),
+-                                              level=logging.WARN, 
noiselevel=2)
+-                      self.hooks[_dir] = hooks
++              for _confroot in [self.settings["PORTAGE_CONFIGROOT"], 
portage.const.BPREFIX]:
++                      for _dir in ["repo.postsync.d", "postsync.d"]:
++                                      postsync_dir = os.path.join(_confroot,
++                                              portage.USER_CONFIG_PATH, _dir)
++                                      hooks = OrderedDict()
++                                      for filepath in 
util._recursive_file_list(postsync_dir):
++                                              name = 
filepath.split(postsync_dir)[1].lstrip(os.sep)
++                                              if os.access(filepath, os.X_OK):
++                                                      hooks[filepath] = name
++                                              else:
++                                                      writemsg_level(" %s %s 
hook: '%s' is not executable\n"
++                                                              % (warn("*"), 
_dir, _unicode_decode(name),),
++                                                              
level=logging.WARN, noiselevel=2)
++                                      self.hooks[_dir] = hooks
+ 
+       def __getattr__(self, name):
+               if name == 'async':
+diff --git a/pym/portage/util/_dyn_libs/LinkageMapELF.py 
b/pym/portage/util/_dyn_libs/LinkageMapELF.py
+index a063621c1..968fbd339 100644
+--- a/pym/portage/util/_dyn_libs/LinkageMapELF.py
++++ b/pym/portage/util/_dyn_libs/LinkageMapELF.py
+@@ -12,7 +12,7 @@ from portage import _os_merge
+ from portage import _unicode_decode
+ from portage import _unicode_encode
+ from portage.cache.mappings import slot_dict_class
+-from portage.const import EPREFIX
++from portage.const import BPREFIX
+ from portage.dep.soname.multilib_category import compute_multilib_category
+ from portage.exception import CommandNotFound, InvalidData
+ from portage.localization import _
+@@ -268,7 +268,7 @@ class LinkageMapELF(object):
+                                       continue
+                               plibs.update((x, cpv) for x in items)
+               if plibs:
+-                      args = [os.path.join(EPREFIX or "/", 
"usr/bin/scanelf"), "-qF", "%a;%F;%S;%r;%n"]
++                      args = [os.path.join(BPREFIX or "/", 
"usr/bin/scanelf"), "-qF", "%a;%F;%S;%r;%n"]
+                       args.extend(os.path.join(root, x.lstrip("." + os.sep)) \
+                               for x in plibs)
+                       try:
+-- 
+2.16.1
+

diff --git a/sys-apps/portage/portage-2.3.40.1.ebuild 
b/sys-apps/portage/portage-2.3.40.1.ebuild
index bf7393b3ac..0bea1d9f01 100644
--- a/sys-apps/portage/portage-2.3.40.1.ebuild
+++ b/sys-apps/portage/portage-2.3.40.1.ebuild
@@ -91,10 +91,9 @@ pkg_setup() {
 python_prepare_all() {
        distutils-r1_python_prepare_all
 
-       # fails to apply
-       #epatch "${FILESDIR}"/${PN}-2.3.10-ebuildshell.patch # 155161
+       epatch "${FILESDIR}"/${PN}-2.3.40-ebuildshell.patch # 155161
        use prefix-chaining &&
-               epatch "${FILESDIR}"/${PN}-2.3.18-prefix-chaining.patch
+               epatch "${FILESDIR}"/${PN}-2.3.40-prefix-chaining.patch
 
        if use native-extensions; then
                printf "[build_ext]\nportage-ext-modules=true\n" >> \

Reply via email to