aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS8
-rw-r--r--Makefile.am8
-rw-r--r--README.md9
-rw-r--r--acinclude.m483
-rwxr-xr-xautogen.sh16
-rw-r--r--bin/Makefile.in84
-rwxr-xr-xbin/ebuild-helpers/dohtml4
-rwxr-xr-xbin/ebuild-helpers/emake3
-rwxr-xr-xbin/ebuild-pyhelper4
-rwxr-xr-xbin/emerge-webrsync14
-rw-r--r--bin/install-qa-check.d/05prefix12
-rw-r--r--bin/isolated-functions.sh31
-rwxr-xr-xbin/misc-functions.sh181
-rw-r--r--bin/phase-functions.sh7
-rwxr-xr-x[-rw-r--r--]bin/save-ebuild-env.sh4
-rw-r--r--cnf/Makefile.in42
-rw-r--r--cnf/make.conf.example8
-rw-r--r--cnf/make.globals30
-rw-r--r--cnf/repos.conf6
-rw-r--r--configure.ac128
-rw-r--r--lib/Makefile.in41
-rw-r--r--lib/_emerge/EbuildPhase.py19
-rw-r--r--lib/_emerge/Package.py2
-rw-r--r--lib/_emerge/depgraph.py12
-rw-r--r--lib/_emerge/emergelog.py5
-rw-r--r--lib/portage/__init__.py4
-rw-r--r--lib/portage/const.py21
-rw-r--r--lib/portage/const_autotool.py21
-rw-r--r--lib/portage/data.py71
-rw-r--r--lib/portage/dbapi/bintree.py8
-rw-r--r--lib/portage/dbapi/vartree.py25
-rw-r--r--lib/portage/getbinpkg.py8
-rw-r--r--lib/portage/meson.build1
-rw-r--r--lib/portage/package/ebuild/_config/special_env_vars.py18
-rw-r--r--lib/portage/package/ebuild/config.py11
-rw-r--r--lib/portage/package/ebuild/doebuild.py106
-rw-r--r--lib/portage/package/ebuild/fetch.py5
-rw-r--r--lib/portage/process.py20
-rw-r--r--lib/portage/tests/lazyimport/test_lazy_import_portage_baseline.py2
-rw-r--r--lib/portage/tests/resolver/ResolverPlayground.py2
-rw-r--r--lib/portage/util/__init__.py14
-rw-r--r--lib/portage/util/_dyn_libs/LinkageMapMachO.py773
-rw-r--r--lib/portage/util/_dyn_libs/meson.build1
-rw-r--r--lib/portage/util/_info_files.py11
-rw-r--r--lib/portage/util/env_update.py3
-rw-r--r--lib/portage/util/hooks.py9
-rw-r--r--man/Makefile.am17
-rw-r--r--man/ebuild.56
-rw-r--r--man/emerge.17
-rw-r--r--man/make.conf.56
-rw-r--r--subst-install.in87
-rwxr-xr-xtarball.sh54
52 files changed, 1986 insertions, 86 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 000000000..62b0edcbd
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,8 @@
+In addition to the the long list of regular portage contributors, these
+folks (in no particular order) have put their hard work into this.
+
+Kito Dietrich <kito at ???>
+Emanuele Giaquinta <emanuele.giaquinta at gmail.com>
+Fabian Groffen <grobian at gentoo.org>
+Brian Harring <ferringb at gmail.com>
+Michael Haubenwallner <haubi at gentoo.org>
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 000000000..9bcfa00e7
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,8 @@
+SHELL = @PORTAGE_BASH@
+
+SUBDIRS = man bin lib cnf
+
+AUTOMAKE_OPTIONS = dist-bzip2 no-dist-gzip
+
+MAINTAINERCLEANFILES = \
+ Makefile.in config.guess config.sub configure ltmain.sh aclocal.m4
diff --git a/README.md b/README.md
index 481d12bac..5699b1b8b 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,3 @@
-[![CI](https://github.com/gentoo/portage/actions/workflows/ci.yml/badge.svg)](https://github.com/gentoo/portage/actions/workflows/ci.yml)
-
About Portage
=============
@@ -8,6 +6,13 @@ Package Manager Specification Project (PMS) standardises and documents
the behaviour of Portage so that ebuild repositories can be used by
other package managers.
+This is the prefix branch of portage, a branch that deals with portage
+setup as packagemanager for a given offset in the filesystem running
+with user privileges.
+
+If you are not looking for something Gentoo Prefix like, then this
+is not the right place.
+
Contributing
============
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644
index 000000000..1d9ccf55f
--- /dev/null
+++ b/acinclude.m4
@@ -0,0 +1,83 @@
+dnl acinclude.m4 generated automatically by ac-archive's acinclude 0.5.63
+
+dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+dnl PARTICULAR PURPOSE.
+
+dnl ______ ______
+
+dnl GENTOO_PATH_PYTHON([minimum-version], [path])
+dnl author: Fabian Groffen <grobian a gentoo.org>
+AC_DEFUN([GENTOO_PATH_PYTHON],
+[
+ AC_PATH_PROG([PREFIX_PORTAGE_PYTHON], [python], no, $2)
+
+ dnl is is there at all?
+ if test "$PREFIX_PORTAGE_PYTHON" = "no" ; then
+ AC_MSG_ERROR([no python found in your path])
+ fi
+
+ dnl is it the version we want?
+ ver=`$PREFIX_PORTAGE_PYTHON -c 'import sys; print(sys.version.split(" ")[[0]])'`
+ AC_MSG_CHECKING([whether $PREFIX_PORTAGE_PYTHON $ver >= $1])
+ cmp=`$PREFIX_PORTAGE_PYTHON -c 'import sys; print(sys.version.split(" ")[[0]] >= "$1")'`
+ if test "$cmp" = "True" ; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_ERROR([need at least version $1 of python])
+ fi
+])
+
+dnl GENTOO_PATH_XCU_ID([path])
+dnl author: Fabian Groffen <grobian a gentoo.org>
+dnl based on the original work by
+dnl Michael Haubenwallner <mhaubi at users dot sourceforge dot net>
+AC_DEFUN([GENTOO_PATH_XCU_ID],
+[
+ AC_PATH_PROG([XCU_ID], [id], no, $1)
+
+ dnl does it support all the bells and whistles we need?
+ AC_MSG_CHECKING([whether $XCU_ID is good enough])
+ for a in '' '-u' '-g' ; do
+ if ! "$XCU_ID" $a >/dev/null 2>&1 ; then
+ XCU_ID=no
+ break
+ fi
+ done
+ if test "$XCU_ID" != "no" ; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_ERROR([$XCU_ID doesn't understand $a])
+ fi
+])dnl
+
+dnl GENTOO_PATH_GNUPROG([variable], [prog-to-check-for], [path])
+dnl author: Fabian Groffen <grobian a gentoo.org>
+AC_DEFUN([GENTOO_PATH_GNUPROG],
+[
+ AC_PATH_PROG([$1], [$2], no, $3)
+
+ dnl is is there at all?
+ tool="`eval echo \$$1`"
+ if test "$tool" = "no" ; then
+ AC_MSG_ERROR([$1 was not found in your path])
+ fi
+
+ dnl is it a GNU version?
+ AC_MSG_CHECKING([whether $tool is GNU $2])
+ ver=`$tool --version 2>/dev/null | head -n 1`
+ case $ver in
+ *GNU*)
+ AC_MSG_RESULT([yes])
+ ;;
+ *)
+ AC_MSG_ERROR([no])
+ ;;
+ esac
+])
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 000000000..463753c45
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env sh
+
+die() {
+ echo "!!! $*" > /dev/stderr
+ exit -1
+}
+
+#autoheader || { echo "failed autoheader"; exit 1; };
+aclocal || die "failed aclocal"
+[ "`type -t glibtoolize`" = "file" ] && alias libtoolize=glibtoolize
+libtoolize --automake -c -f || die "failed libtoolize"
+autoconf || die "failed autoconf"
+touch ChangeLog
+automake -a -c || die "failed automake"
+
+echo "finished"
diff --git a/bin/Makefile.in b/bin/Makefile.in
new file mode 100644
index 000000000..e6dc6724f
--- /dev/null
+++ b/bin/Makefile.in
@@ -0,0 +1,84 @@
+SHELL = @PORTAGE_BASH@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+sysconfdir = @sysconfdir@
+libdir = @libdir@
+
+srcdir = @srcdir@
+top_builddir = @top_builddir@
+
+portageuser = @portageuser@
+portagegroup = @portagegroup@
+
+PORTAGE_BIN = @PORTAGE_BASE@/bin
+LN_S = @LN_S@
+INSTALL = @INSTALL@
+INSTALL_subst = $(top_builddir)/subst-install
+
+usr_binprogs = \
+ ebuild \
+ egencache \
+ emerge \
+ emerge-webrsync \
+ emirrordist \
+ portageq \
+ quickpkg
+
+usr_sbinprogs = \
+ archive-conf \
+ dispatch-conf \
+ emaint \
+ env-update \
+ etc-update \
+ fixpackages \
+ regenworld
+
+hprefixify_progs = \
+ etc-update
+
+all:
+
+install:
+ $(INSTALL) -d -m 755 -o "$(portageuser)" -g "$(portagegroup)" $(DESTDIR)$(PORTAGE_BIN)
+ ( cd "$(srcdir)" && find . -type d ) | while read f ; do \
+ files=( ) ; \
+ shopt -s nullglob ; \
+ for t in "$(srcdir)/$${f}"/* ; do \
+ [[ -d $${t} ]] && continue ; \
+ [[ $${t} == */Makefile* ]] && continue ; \
+ files=( "$${files[@]}" "$${t}" ) ; \
+ done ; \
+ $(INSTALL) -d -m 755 \
+ -o "$(portageuser)" -g "$(portagegroup)" \
+ "$(DESTDIR)$(PORTAGE_BIN)/$${f}" && \
+ [[ $${files[0]} ]] || continue ; \
+ $(INSTALL_subst) -m 755 \
+ -o "$(portageuser)" -g "$(portagegroup)" \
+ -t "$(DESTDIR)$(PORTAGE_BIN)/$${f}" \
+ "$${files[@]}" ; \
+ done ; \
+ for f in $(hprefixify_progs) ; do \
+ $(INSTALL_subst) --hprefixify -m 755 \
+ -o "$(portageuser)" -g "$(portagegroup)" \
+ -t "$(DESTDIR)$(PORTAGE_BIN)" \
+ "$(srcdir)/$${f}" ; \
+ done
+ $(INSTALL) -d -m 755 -o "$(portageuser)" -g "$(portagegroup)" $(DESTDIR)$(prefix)/bin
+ cd $(DESTDIR)$(prefix)/bin \
+ ; for p in $(usr_binprogs) \
+ ; do test -f $(DESTDIR)$(PORTAGE_BIN)/$${p} \
+ || { echo "$(DESTDIR)$(PORTAGE_BIN)/$${p} does not exist" ; exit 1 ; } \
+ ; rm -f $(DESTDIR)$(prefix)/bin/$${p} \
+ ; $(LN_S) ../lib/portage/bin/$${p} $${p} || exit 1 \
+ ; done
+ $(INSTALL) -d -m 755 -o "$(portageuser)" -g "$(portagegroup)" $(DESTDIR)$(prefix)/sbin
+ cd $(DESTDIR)$(prefix)/sbin \
+ ; for p in $(usr_sbinprogs) \
+ ; do test -f $(DESTDIR)$(PORTAGE_BIN)/$${p} \
+ || { echo "$(DESTDIR)$(PORTAGE_BIN)/$${p} does not exist" ; exit 1 ; } \
+ ; rm -f $(DESTDIR)$(prefix)/sbin/$${p} \
+ ; $(LN_S) ../lib/portage/bin/$${p} $${p} || exit 1 \
+ ; done
+
+.PHONY: all install
diff --git a/bin/ebuild-helpers/dohtml b/bin/ebuild-helpers/dohtml
index 4d4efd496..b1636d6e8 100755
--- a/bin/ebuild-helpers/dohtml
+++ b/bin/ebuild-helpers/dohtml
@@ -16,8 +16,10 @@ fi
# Use safe cwd, avoiding unsafe import for bug #469338.
export __PORTAGE_HELPER_CWD=${PWD}
cd "${PORTAGE_PYM_PATH}" || die
+# BEGIN PREFIX LOCAL: use Prefix Python fallback
PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \
- "${PORTAGE_PYTHON:-/usr/bin/python}" "${PORTAGE_BIN_PATH}/dohtml.py" "$@"
+ "${PORTAGE_PYTHON:-@PREFIX_PORTAGE_PYTHON@}" "${PORTAGE_BIN_PATH}/dohtml.py" "$@"
+# END PREFIX LOCAL
ret=$?
# Restore cwd for display by __helpers_die
diff --git a/bin/ebuild-helpers/emake b/bin/ebuild-helpers/emake
index 3d8b31eee..21da85845 100755
--- a/bin/ebuild-helpers/emake
+++ b/bin/ebuild-helpers/emake
@@ -12,7 +12,8 @@
source "${PORTAGE_BIN_PATH}"/isolated-functions.sh || exit 1
cmd=(
- ${MAKE:-make} ${MAKEOPTS} "$@" ${EXTRA_EMAKE}
+ # PREFIX LOCAL: force SHELL to be set (don't use possibly ancient /bin/sh)
+ ${MAKE:-make} SHELL="${BASH:-/bin/bash}" ${MAKEOPTS} "$@" ${EXTRA_EMAKE}
)
if [[ ${PORTAGE_QUIET} != 1 ]] ; then
diff --git a/bin/ebuild-pyhelper b/bin/ebuild-pyhelper
index 901277c96..afed4d4d5 100755
--- a/bin/ebuild-pyhelper
+++ b/bin/ebuild-pyhelper
@@ -13,8 +13,10 @@ fi
cd "${PORTAGE_PYM_PATH}" || exit 1
for path in "${PORTAGE_BIN_PATH}/${0##*/}"{.py,}; do
if [[ -x "${path}" ]]; then
+ # BEGIN PREFIX LOCAL: use Prefix Python fallback
PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \
- exec "${PORTAGE_PYTHON:-/usr/bin/python}" "${path}" "$@"
+ exec "${PORTAGE_PYTHON:-@PREFIX_PORTAGE_PYTHON@}" "${path}" "$@"
+ # END PREFIX LOCAL
fi
done
echo "File not found: ${path}" >&2
diff --git a/bin/emerge-webrsync b/bin/emerge-webrsync
index 99da05543..9959b6a5a 100755
--- a/bin/emerge-webrsync
+++ b/bin/emerge-webrsync
@@ -78,14 +78,22 @@ emerge=$(PATH="${BASH_SOURCE[0]%/*}:${PATH}" type -P emerge)
portageq=$(PATH="${BASH_SOURCE[0]%/*}:${PATH}" type -P portageq)
[[ -n ${portageq} ]] || die "could not find 'portageq'; aborting"
+# PREFIX LOCAL: retrieve PORTAGE_USER/PORTAGE_GROUP
eval "$("${portageq}" envvar -v DISTDIR EPREFIX FEATURES \
FETCHCOMMAND GENTOO_MIRRORS \
PORTAGE_BIN_PATH PORTAGE_CONFIGROOT PORTAGE_GPG_DIR \
PORTAGE_NICENESS PORTAGE_REPOSITORIES PORTAGE_RSYNC_EXTRA_OPTS \
PORTAGE_RSYNC_OPTS PORTAGE_TEMP_GPG_DIR PORTAGE_TMPDIR \
- USERLAND http_proxy https_proxy ftp_proxy)"
+ USERLAND http_proxy ftp_proxy \
+ USERLAND http_proxy https_proxy ftp_proxy
+ PORTAGE_USER PORTAGE_GROUP)"
export http_proxy https_proxy ftp_proxy
+# PREFIX LOCAL: use Prefix servers, just because we want this and infra
+# can't support us yet
+GENTOO_MIRRORS="http://rsync.prefix.bitzolder.nl"
+# END PREFIX LOCAL
+
source "${PORTAGE_BIN_PATH}"/isolated-functions.sh || exit 1
repo_name=gentoo
@@ -410,7 +418,9 @@ sync_local() {
[[ ${PORTAGE_QUIET} -eq 1 ]] || einfo "Syncing local repository ..."
- local ownership="portage:portage"
+ # PREFIX LOCAL: use PORTAGE_USER and PORTAGE_GROUP
+ local ownership="${PORTAGE_USER:-portage}:${PORTAGE_GROUP:-portage}"
+ # END PREFIX LOCAL
if has usersync ${FEATURES} ; then
case "${USERLAND}" in
BSD)
diff --git a/bin/install-qa-check.d/05prefix b/bin/install-qa-check.d/05prefix
index edbd6fab2..229daff6a 100644
--- a/bin/install-qa-check.d/05prefix
+++ b/bin/install-qa-check.d/05prefix
@@ -34,9 +34,15 @@ install_qa_check_prefix() {
# check shebangs, bug #282539
rm -f "${T}"/non-prefix-shebangs-errs
local WHITELIST=" /usr/bin/env "
+ # BEGIN PREFIX LOCAL: pull canonicalize call out of loop
+ # shebang can be an absolutised path, bug #342929
+ local eprefix=$(canonicalize ${EPREFIX})
+ # END PREFIX LOCAL
# this is hell expensive, but how else?
+ # PREFIX LOCAL: use grep -I to skip binary files, allow POSIX space
+ # between # and !
find "${ED}" -executable \! -type d -print0 \
- | xargs -0 grep -H -n -m1 "^#!" \
+ | xargs -0 grep -H -n -m1 -I '^# \?!' \
| while read f ;
do
local fn=${f%%:*}
@@ -49,9 +55,7 @@ install_qa_check_prefix() {
line=( ${line#"#!"} )
IFS=${oldIFS}
[[ ${WHITELIST} == *" ${line[0]} "* ]] && continue
- local fp=${fn#${D}} ; fp=/${fp%/*}
- # line[0] can be an absolutised path, bug #342929
- local eprefix=$(canonicalize ${EPREFIX})
+ local fp=${fn#${D}/} ; fp=/${fp%/*}
local rf=${fn}
# in case we deal with a symlink, make sure we don't replace it
# with a real file (sed -i does that)
diff --git a/bin/isolated-functions.sh b/bin/isolated-functions.sh
index 06be030fb..47e18cc6b 100644
--- a/bin/isolated-functions.sh
+++ b/bin/isolated-functions.sh
@@ -450,16 +450,24 @@ else
fi
-if [[ -z ${USERLAND} ]] ; then
- case $(uname -s) in
- *BSD|DragonFly)
- export USERLAND="BSD"
- ;;
- *)
- export USERLAND="GNU"
- ;;
- esac
-fi
+# BEGIN PREFIX LOCAL
+# In Prefix every platform has USERLAND=GNU, even FreeBSD. Since I
+# don't know how to reliably "figure out" we are in a Prefix instance of
+# portage here, I for now disable this check, and hardcode it to GNU.
+# Somehow it appears stange to me that this code is in this file,
+# non-ebuilds/eclasses should never rely on USERLAND and XARGS, don't they?
+#if [[ -z ${USERLAND} ]] ; then
+# case $(uname -s) in
+# *BSD|DragonFly)
+# export USERLAND="BSD"
+# ;;
+# *)
+# export USERLAND="GNU"
+# ;;
+# esac
+#fi
+[[ -z ${USERLAND} ]] && USERLAND="GNU"
+# END PREFIX LOCAL
if [[ -z ${XARGS} ]] ; then
case ${USERLAND} in
@@ -658,7 +666,8 @@ debug-print() {
printf '%s\n' "${@}" >> "${T}/eclass-debug.log"
# Let the portage user own/write to this file
- chgrp "${PORTAGE_GRPNAME:-portage}" "${T}/eclass-debug.log"
+ # PREFIX LOCAL: fallback to configured group
+ chgrp "${PORTAGE_GRPNAME:-${PORTAGE_GROUP}}" "${T}/eclass-debug.log"
chmod g+w "${T}/eclass-debug.log"
fi
}
diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh
index 4ce3acbfd..47ee72917 100755
--- a/bin/misc-functions.sh
+++ b/bin/misc-functions.sh
@@ -162,6 +162,31 @@ install_qa_check() {
mtree -U -e -p "${ED}" -k flags < "${T}/bsdflags.mtree" &> /dev/null
fi
+ # PREFIX LOCAL:
+ # anything outside the prefix should be caught by the Prefix QA
+ # check, so if there's nothing in ED, we skip searching for QA
+ # checks there, the specific QA funcs can hence rely on ED existing
+ if [[ -d ${ED} ]] ; then
+ case ${CHOST} in
+ *-darwin*)
+ # Mach-O platforms (NeXT, Darwin, OSX/macOS)
+ install_qa_check_macho
+ ;;
+ *)
+ # because this is the majority: ELF platforms (Linux,
+ # Solaris, *BSD, IRIX, etc.)
+ install_qa_check_elf
+ ;;
+ esac
+ fi
+
+ # this is basically here such that the diff with trunk remains just
+ # offsetted and not out of order
+ install_qa_check_misc
+ # END PREFIX LOCAL
+}
+
+install_qa_check_elf() {
# Create NEEDED.ELF.2 regardless of RESTRICT=binchecks, since this info is
# too useful not to have (it's required for things like preserve-libs), and
# it's tempting for ebuild authors to set RESTRICT=binchecks for packages
@@ -240,7 +265,9 @@ install_qa_check() {
fi
fi
fi
+}
+install_qa_check_misc() {
# If binpkg-dostrip is enabled, apply stripping before creating
# the binary package.
# Note: disabling it won't help with packages calling prepstrip directly.
@@ -257,6 +284,152 @@ install_qa_check() {
fi
}
+install_qa_check_macho() {
+ if ! has binchecks ${RESTRICT} ; then
+ # on Darwin, dynamic libraries are called .dylibs instead of
+ # .sos. In addition the version component is before the
+ # extension, not after it. Check for this, and *only* warn
+ # about it. Some packages do ship .so files on Darwin and make
+ # it work (ugly!).
+ rm -f "${T}/mach-o.check"
+ find ${ED%/} -name "*.so" -or -name "*.so.*" | \
+ while read i ; do
+ [[ $(file $i) == *"Mach-O"* ]] && \
+ echo "${i#${D}}" >> "${T}/mach-o.check"
+ done
+ if [[ -f ${T}/mach-o.check ]] ; then
+ f=$(< "${T}/mach-o.check")
+ __vecho -ne '\a\n'
+ eqawarn "QA Notice: Found .so dynamic libraries on Darwin:"
+ eqawarn " ${f//$'\n'/\n }"
+ fi
+ rm -f "${T}/mach-o.check"
+
+ # The naming for dynamic libraries is different on Darwin; the
+ # version component is before the extention, instead of after
+ # it, as with .sos. Again, make this a warning only.
+ rm -f "${T}/mach-o.check"
+ find ${ED%/} -name "*.dylib.*" | \
+ while read i ; do
+ echo "${i#${D}}" >> "${T}/mach-o.check"
+ done
+ if [[ -f "${T}/mach-o.check" ]] ; then
+ f=$(< "${T}/mach-o.check")
+ __vecho -ne '\a\n'
+ eqawarn "QA Notice: Found wrongly named dynamic libraries on Darwin:"
+ eqawarn " ${f// /\n }"
+ fi
+ rm -f "${T}/mach-o.check"
+ fi
+
+ install_name_is_relative() {
+ case $1 in
+ "@executable_path/"*) return 0 ;;
+ "@loader_path"/*) return 0 ;;
+ "@rpath/"*) return 0 ;;
+ *) return 1 ;;
+ esac
+ }
+
+ # While we generate the NEEDED files, check that we don't get kernel
+ # traps at runtime because of broken install_names on Darwin.
+ rm -f "${T}"/.install_name_check_failed
+ scanmacho -qyRF '%a;%p;%S;%n' "${D}" | { while IFS= read l ; do
+ arch=${l%%;*}; l=${l#*;}
+ obj="/${l%%;*}"; l=${l#*;}
+ install_name=${l%%;*}; l=${l#*;}
+ needed=${l%%;*}; l=${l#*;}
+
+ ignore=
+ qa_var="QA_IGNORE_INSTALL_NAME_FILES_${ARCH/-/_}"
+ eval "[[ -n \${!qa_var} ]] &&
+ QA_IGNORE_INSTALL_NAME_FILES=(\"\${${qa_var}[@]}\")"
+
+ if [[ ${#QA_IGNORE_INSTALL_NAME_FILES[@]} -gt 1 ]] ; then
+ for x in "${QA_IGNORE_INSTALL_NAME_FILES[@]}" ; do
+ [[ ${obj##*/} == ${x} ]] && \
+ ignore=true
+ done
+ else
+ local shopts=$-
+ set -o noglob
+ for x in ${QA_IGNORE_INSTALL_NAME_FILES} ; do
+ [[ ${obj##*/} == ${x} ]] && \
+ ignore=true
+ done
+ set +o noglob
+ set -${shopts}
+ fi
+
+ # See if the self-reference install_name points to an existing
+ # and to be installed file. This usually is a symlink for the
+ # major version.
+ if install_name_is_relative ${install_name} ; then
+ # try to locate the library in the installed image
+ local inpath=${install_name#@*/}
+ local libl
+ for libl in $(find "${ED}" -name "${inpath##*/}") ; do
+ if [[ ${libl} == */${inpath} ]] ; then
+ install_name=/${libl#${D}}
+ break
+ fi
+ done
+ fi
+ if [[ ! -e ${D}${install_name} ]] ; then
+ eqawarn "QA Notice: invalid self-reference install_name ${install_name} in ${obj}"
+ # remember we are in an implicit subshell, that's
+ # why we touch a file here ... ideally we should be
+ # able to die correctly/nicely here
+ [[ -z ${ignore} ]] && touch "${T}"/.install_name_check_failed
+ fi
+
+ # this is ugly, paths with spaces won't work
+ for lib in ${needed//,/ } ; do
+ if [[ ${lib} == ${D}* ]] ; then
+ eqawarn "QA Notice: install_name references \${D}: ${lib} in ${obj}"
+ [[ -z ${ignore} ]] && touch "${T}"/.install_name_check_failed
+ elif [[ ${lib} == ${S}* ]] ; then
+ eqawarn "QA Notice: install_name references \${S}: ${lib} in ${obj}"
+ [[ -z ${ignore} ]] && touch "${T}"/.install_name_check_failed
+ elif ! install_name_is_relative ${lib} ; then
+ local isok=no
+ if [[ -e ${lib} || -e ${D}${lib} ]] ; then
+ isok=yes # yay, we're ok
+ elif [[ -e "${EROOT}"/MacOSX.sdk ]] ; then
+ # trigger SDK mode, at least since Big Sur (11.0)
+ # there are no libraries in /usr/lib any more, but
+ # there are references too it (some library cache is
+ # in place), yet we can validate it sort of is sane
+ # by looking at the SDK metacaches, TAPI-files, .tbd
+ # text versions of libraries, so just look there
+ local tbd=${lib%.*}.tbd
+ if [[ -e ${EROOT}/MacOSX.sdk/${lib%.*}.tbd ]] ; then
+ isok=yes # it's in the SDK, so ok
+ elif [[ -e ${EROOT}/MacOSX.sdk/${lib}.tbd ]] ; then
+ isok=yes # this happens in case of Framework refs
+ fi
+ fi
+ if [[ ${isok} == no ]] ; then
+ eqawarn "QA Notice: invalid reference to ${lib} in ${obj}"
+ [[ -z ${ignore} ]] && \
+ touch "${T}"/.install_name_check_failed
+ fi
+ fi
+ done
+
+ # backwards compatibility
+ echo "${obj} ${needed}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED
+ # what we use
+ echo "${arch};${obj};${install_name};${needed}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED.MACHO.3
+ done }
+ if [[ -f ${T}/.install_name_check_failed ]] ; then
+ # secret switch "allow_broken_install_names" to get
+ # around this and install broken crap (not a good idea)
+ has allow_broken_install_names ${FEATURES} || \
+ die "invalid install_name found, your application or library will crash at runtime"
+ fi
+}
+
__dyn_instprep() {
if [[ -e ${PORTAGE_BUILDDIR}/.instprepped ]] ; then
__vecho ">>> It appears that '${PF}' is already instprepped; skipping."
@@ -529,9 +702,11 @@ __dyn_package() {
${PORTAGE_COMPRESSION_COMMAND} > "${PORTAGE_BINPKG_TMPFILE}"
assert "failed to pack binary package: '${PORTAGE_BINPKG_TMPFILE}'"
+ # BEGIN PREFIX LOCAL: use PREFIX_PORTAGE_PYTHON fallback
PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \
- "${PORTAGE_PYTHON:-/usr/bin/python}" "${PORTAGE_BIN_PATH}"/xpak-helper.py recompose \
+ "${PORTAGE_PYTHON:-@PREFIX_PORTAGE_PYTHON@}" "${PORTAGE_BIN_PATH}"/xpak-helper.py recompose \
"${PORTAGE_BINPKG_TMPFILE}" "${PORTAGE_BUILDDIR}/build-info"
+ # END PREFIX LOCAL
if [[ $? -ne 0 ]]; then
rm -f "${PORTAGE_BINPKG_TMPFILE}"
die "Failed to append metadata to the tbz2 file"
@@ -550,9 +725,11 @@ __dyn_package() {
__vecho ">>> Done."
elif [[ "${BINPKG_FORMAT}" == "gpkg" ]]; then
+ # BEGIN PREFIX LOCAL: use PREFIX_PORTAGE_PYTHON fallback
PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \
- "${PORTAGE_PYTHON:-/usr/bin/python}" "${PORTAGE_BIN_PATH}"/gpkg-helper.py compress \
+ "${PORTAGE_PYTHON:-@PREFIX_PORTAGE_PYTHON@}" "${PORTAGE_BIN_PATH}"/gpkg-helper.py compress \
"${PF}${BUILD_ID:+-${BUILD_ID}}" "${PORTAGE_BINPKG_TMPFILE}" "${PORTAGE_BUILDDIR}/build-info" "${D}"
+ # END PREFIX LOCAL
if [[ $? -ne 0 ]]; then
rm -f "${PORTAGE_BINPKG_TMPFILE}"
die "Failed to create binpkg file"
diff --git a/bin/phase-functions.sh b/bin/phase-functions.sh
index ebcf5f242..31b02cedf 100644
--- a/bin/phase-functions.sh
+++ b/bin/phase-functions.sh
@@ -146,7 +146,8 @@ __filter_readonly_variables() {
fi
fi
- "${PORTAGE_PYTHON:-/usr/bin/python}" "${PORTAGE_BIN_PATH}"/filter-bash-environment.py "${filtered_vars}" || die "filter-bash-environment.py failed"
+ # PREFIX LOCAL: use Prefix Python fallback
+ "${PORTAGE_PYTHON:-@PREFIX_PORTAGE_PYTHON@}" "${PORTAGE_BIN_PATH}"/filter-bash-environment.py "${filtered_vars}" || die "filter-bash-environment.py failed"
}
# @FUNCTION: __preprocess_ebuild_env
@@ -1110,8 +1111,8 @@ __ebuild_main() {
__save_ebuild_env | __filter_readonly_variables \
--filter-features > "${T}/environment"
assert "__save_ebuild_env failed"
-
- chgrp "${PORTAGE_GRPNAME:-portage}" "${T}/environment"
+ # PREFIX LOCAL: use configured group
+ chgrp "${PORTAGE_GRPNAME:-${PORTAGE_GROUP}}" "${T}/environment"
chmod g+w "${T}/environment"
fi
diff --git a/bin/save-ebuild-env.sh b/bin/save-ebuild-env.sh
index 3a2560aab..c3c83c91d 100644..100755
--- a/bin/save-ebuild-env.sh
+++ b/bin/save-ebuild-env.sh
@@ -119,6 +119,10 @@ __save_ebuild_env() {
# user config variables
unset DOC_SYMLINKS_DIR INSTALL_MASK PKG_INSTALL_MASK
+ # PREFIX LOCAL: Prefix additions
+ unset EXTRA_PATH PORTAGE_GROUP PORTAGE_USER
+ # END PREFIX LOCAL
+
declare -p
declare -fp
if [[ ${BASH_VERSINFO[0]} == 3 ]]; then
diff --git a/cnf/Makefile.in b/cnf/Makefile.in
new file mode 100644
index 000000000..64d2fa7a7
--- /dev/null
+++ b/cnf/Makefile.in
@@ -0,0 +1,42 @@
+SHELL = @PORTAGE_BASH@
+
+prefix = @prefix@
+sysconfdir = @sysconfdir@
+datadir = @datadir@
+
+srcdir = @srcdir@
+top_builddir = @top_builddir@
+PORTAGE_CONF = $(datadir)/portage/config
+
+portageuser = @portageuser@
+portagegroup = @portagegroup@
+
+INSTALL = @INSTALL@
+INSTALL_subst = ${top_builddir}/subst-install --hprefixify
+LN_S = @LN_S@
+
+all:
+
+install:
+ $(INSTALL) -d -m 755 -o "$(portageuser)" -g "$(portagegroup)" $(DESTDIR)$(PORTAGE_CONF)
+ $(INSTALL_subst) \
+ -o "$(portageuser)" -g "$(portagegroup)" \
+ -t "$(DESTDIR)$(PORTAGE_CONF)" \
+ "$(srcdir)"/make.globals "$(srcdir)"/repos.conf
+ $(INSTALL) -d -m 755 -o "$(portageuser)" -g "$(portagegroup)" $(DESTDIR)$(PORTAGE_CONF)/sets
+ $(INSTALL_subst) \
+ -o "$(portageuser)" -g "$(portagegroup)" \
+ -t "$(DESTDIR)$(PORTAGE_CONF)/sets" \
+ "$(srcdir)"/sets/portage.conf
+ $(INSTALL_subst) \
+ -o "$(portageuser)" -g "$(portagegroup)" \
+ "$(srcdir)"/make.conf.example "$(DESTDIR)$(PORTAGE_CONF)"/make.conf.example
+ $(INSTALL) -d -m 755 -o "$(portageuser)" -g "$(portagegroup)" $(DESTDIR)$(sysconfdir)
+ $(INSTALL_subst) \
+ -o "$(portageuser)" -g "$(portagegroup)" \
+ -t "$(DESTDIR)$(sysconfdir)" \
+ "$(srcdir)"/dispatch-conf.conf \
+ "$(srcdir)"/etc-update.conf
+ ( cd $(DESTDIR)$(sysconfdir) && rm -f make.globals && $(LN_S) $(DESTDIR)$(PORTAGE_CONF)/make.globals )
+
+.PHONY: all install
diff --git a/cnf/make.conf.example b/cnf/make.conf.example
index 4375665f4..502f0641a 100644
--- a/cnf/make.conf.example
+++ b/cnf/make.conf.example
@@ -157,8 +157,8 @@
#RESUMECOMMAND="wget -c -t 3 -T 60 --passive-ftp --limit-rate=200k -O \"\${DISTDIR}/\${FILE}\" \"\${URI}\""
#
# Lukemftp (BSD ftp):
-#FETCHCOMMAND="/usr/bin/lukemftp -s -a -o \"\${DISTDIR}/\${FILE}\" \"\${URI}\""
-#RESUMECOMMAND="/usr/bin/lukemftp -s -a -R -o \"\${DISTDIR}/\${FILE}\" \"\${URI}\""
+#FETCHCOMMAND="lukemftp -s -a -o \"\${DISTDIR}/\${FILE}\" \"\${URI}\""
+#RESUMECOMMAND="lukemftp -s -a -R -o \"\${DISTDIR}/\${FILE}\" \"\${URI}\""
#
# Portage uses GENTOO_MIRRORS to specify mirrors to use for source retrieval.
# The list is a space separated list which is read left to right. If you use
@@ -246,6 +246,10 @@
# Instructions for setting up a local rsync server are available here:
# https://wiki.gentoo.org/wiki/Local_Mirror
#
+# For Gentoo Prefix, use the following URL:
+#
+# Default: "rsync://rsync.prefix.bitzolder.nl/gentoo-portage-prefix
+#
#SYNC="rsync://rsync.gentoo.org/gentoo-portage"
#
# PORTAGE_RSYNC_RETRIES sets the number of times portage will attempt to retrieve
diff --git a/cnf/make.globals b/cnf/make.globals
index 2bb7a6559..a5931a177 100644
--- a/cnf/make.globals
+++ b/cnf/make.globals
@@ -130,12 +130,24 @@ PORTAGE_SYNC_STALE="30"
PORTAGE_LOGDIR_CLEAN="find \"\${PORTAGE_LOGDIR}\" -type f ! -name \"summary.log*\" -mtime +7 -delete"
# Minimal CONFIG_PROTECT
+# NOTE: in Prefix, these are NOT prefixed on purpose, because the
+# profiles define them too
CONFIG_PROTECT="/etc"
CONFIG_PROTECT_MASK="/etc/env.d"
# Disable auto-use
USE_ORDER="env:pkg:conf:defaults:pkginternal:features:repo:env.d"
+# PREFIX LOCAL: additional vars set during install
+# Default portage user/group
+PORTAGE_USER='@portageuser@'
+PORTAGE_GROUP='@portagegroup@'
+PORTAGE_ROOT_USER='@rootuser@'
+
+# Default ownership of installed files.
+PORTAGE_INST_UID="@rootuid@"
+PORTAGE_INST_GID="@rootgid@"
+
# Mode bits for ${WORKDIR} (see ebuild.5).
PORTAGE_WORKDIR_MODE="0700"
@@ -143,9 +155,9 @@ PORTAGE_WORKDIR_MODE="0700"
PORTAGE_ELOG_CLASSES="log warn error"
PORTAGE_ELOG_SYSTEM="save_summary:log,warn,error,qa echo"
-PORTAGE_ELOG_MAILURI="root"
+PORTAGE_ELOG_MAILURI="@rootuser@"
PORTAGE_ELOG_MAILSUBJECT="[portage] ebuild log for \${PACKAGE} on \${HOST}"
-PORTAGE_ELOG_MAILFROM="portage@localhost"
+PORTAGE_ELOG_MAILFROM="@portageuser@@localhost"
# Signing command used by egencache
PORTAGE_GPG_SIGNING_COMMAND="gpg --sign --digest-algo SHA256 --clearsign --yes --default-key \"\${PORTAGE_GPG_KEY}\" --homedir \"\${PORTAGE_GPG_DIR}\" \"\${FILE}\""
@@ -163,6 +175,20 @@ PORTAGE_XATTR_EXCLUDE="btrfs.* security.evm security.ima
security.selinux system.nfs4_acl user.apache_handler
user.Beagle.* user.dublincore.* user.mime_encoding user.xdg.*"
+# Writeable paths for Mac OS X seatbelt sandbox
+#
+# If path ends in a slash (/), access will recursively be allowed to directory
+# contents (using a regex), not the directory itself. Without a slash, access
+# to the directory or file itself will be allowed (using a literal), so it can
+# be created, removed and changed. If both is needed, the directory needs to be
+# given twice, once with and once without the slash. Obviously this only makes
+# sense for directories, not files.
+#
+# An empty value for either variable will disable all restrictions on the
+# corresponding operation.
+MACOSSANDBOX_PATHS="/dev/fd/ /private/tmp/ /private/var/tmp/ @@PORTAGE_BUILDDIR@@/ @@PORTAGE_ACTUAL_DISTDIR@@/"
+MACOSSANDBOX_PATHS_CONTENT_ONLY="/dev/null /dev/dtracehelper /dev/tty /private/var/run/syslog"
+
# *****************************
# ** DO NOT EDIT THIS FILE **
# ***************************************************
diff --git a/cnf/repos.conf b/cnf/repos.conf
index f16fd352e..3924461c9 100644
--- a/cnf/repos.conf
+++ b/cnf/repos.conf
@@ -1,10 +1,10 @@
[DEFAULT]
-main-repo = gentoo
+main-repo = gentoo_prefix
-[gentoo]
+[gentoo_prefix]
location = /var/db/repos/gentoo
sync-type = rsync
-sync-uri = rsync://rsync.gentoo.org/gentoo-portage
+sync-uri = rsync://rsync.prefix.bitzolder.nl/gentoo-portage-prefix
auto-sync = yes
sync-rsync-verify-jobs = 1
sync-rsync-verify-metamanifest = yes
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 000000000..12b669efb
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,128 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT([portage-prefix],[@version@],[prefix@gentoo.org])
+
+AC_PREREQ([2.71])
+
+case "${prefix}" in
+ '') AC_MSG_ERROR([bad value ${prefix} for --prefix, must not be empty]) ;;
+ */) AC_MSG_ERROR([bad value ${prefix} for --prefix, must not end with '/']) ;;
+ /*|NONE) ;;
+ *) AC_MSG_ERROR([bad value ${prefix} for --prefix, must start with /]) ;;
+esac
+
+AC_CANONICAL_BUILD
+AC_CANONICAL_HOST
+AC_CANONICAL_TARGET
+
+AM_INIT_AUTOMAKE
+
+dnl Checks for programs.
+dnl store cflags prior, otherwise it's not propagated.
+if test "x$CFLAGS" != "x"
+then
+ CFLAGS=$CFLAGS
+fi
+
+AC_PREFIX_DEFAULT([/usr])
+
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_EGREP
+
+GENTOO_PATH_XCU_ID()
+GENTOO_PATH_PYTHON([2.7])
+
+AC_PATH_PROG(PORTAGE_RM, [rm], no)
+AC_PATH_PROG(PORTAGE_MV, [mv], no)
+AC_PATH_PROG(PORTAGE_BASENAME, [basename], no)
+AC_PATH_PROG(PORTAGE_DIRNAME, [dirname], no)
+dnl avoid bash internal variable messing up things here
+GENTOO_PATH_GNUPROG(PORTAGE_BASH, [bash])
+GENTOO_PATH_GNUPROG(PORTAGE_SED, [sed])
+GENTOO_PATH_GNUPROG(PORTAGE_WGET, [wget])
+GENTOO_PATH_GNUPROG(PORTAGE_FIND, [find])
+GENTOO_PATH_GNUPROG(PORTAGE_XARGS, [xargs])
+GENTOO_PATH_GNUPROG(PORTAGE_GREP, [grep])
+
+AC_ARG_WITH(portage-user,
+AS_HELP_STRING([--with-portage-user=myuser],[use user 'myuser' as portage owner (default portage)]),
+[case "${withval}" in
+ ""|yes) AC_MSG_ERROR(bad value ${withval} for --with-portage-user);;
+ *) portageuser="${withval}";;
+esac],
+[portageuser="portage"])
+
+AC_ARG_WITH(portage-group,
+AS_HELP_STRING([--with-portage-group=mygroup],[use group 'mygroup' as portage users group (default portage)]),
+[case "${withval}" in
+ ""|yes) AC_MSG_ERROR(bad value ${withval} for --with-portage-group);;
+ *) portagegroup="${withval}";;
+esac],
+[portagegroup="portage"])
+
+AC_ARG_WITH(root-user,
+AS_HELP_STRING([--with-root-user=myuser],[uses 'myuser' as owner of installed files (default is portage-user)]),
+[case "${withval}" in
+ ""|yes) AC_MSG_ERROR(bad value ${withval} for --with-root-user);;
+ *) rootuser="${withval}";;
+esac],
+[rootuser="${portageuser}"])
+
+AC_MSG_CHECKING([for user id of ${rootuser}])
+dnl grab uid of rootuser
+rootuid=`${XCU_ID} -u "${rootuser}"`
+if test "x`echo ${rootuid} | ${EGREP} '^[[0-9]]+$'`" != "x"
+then
+ AC_MSG_RESULT([${rootuid}])
+else
+ AC_MSG_ERROR([error finding the user id of ${rootuser}])
+fi
+AC_MSG_CHECKING([for group id of ${rootuser}])
+rootgid=`${XCU_ID} -g "${rootuser}"`
+if test "x`echo ${rootgid} | ${EGREP} '^[[0-9]]+$'`" != "x"
+then
+ AC_MSG_RESULT([${rootgid}])
+else
+ AC_MSG_ERROR([error finding the group id of ${rootuser}])
+fi
+
+AC_ARG_WITH(offset-prefix,
+AS_HELP_STRING([--with-offset-prefix],[specify the installation prefix for all packages, defaults to an empty string]),
+ [PORTAGE_EPREFIX=$withval],
+ [PORTAGE_EPREFIX=''])
+
+if test "x$PORTAGE_EPREFIX" != "x"
+then
+ PORTAGE_EPREFIX=`${PREFIX_PORTAGE_PYTHON} -c "import os; print(os.path.normpath('$PORTAGE_EPREFIX'))"`
+fi
+
+AC_SUBST(portageuser)
+AC_SUBST(portagegroup)
+AC_SUBST(rootuser)
+AC_SUBST(rootuid)
+AC_SUBST(rootgid)
+AC_SUBST(PORTAGE_EPREFIX)
+AC_SUBST(PORTAGE_BASE,['${exec_prefix}/lib/portage'])
+
+AC_SUBST(PORTAGE_RM)
+AC_SUBST(PORTAGE_MV)
+AC_SUBST(PORTAGE_BASENAME)
+AC_SUBST(PORTAGE_DIRNAME)
+AC_SUBST(PORTAGE_BASH)
+AC_SUBST(PORTAGE_SED)
+AC_SUBST(PORTAGE_WGET)
+AC_SUBST(PORTAGE_FIND)
+AC_SUBST(PORTAGE_XARGS)
+AC_SUBST(PORTAGE_GREP)
+
+AC_CONFIG_FILES([subst-install], [chmod +x subst-install])
+AC_CONFIG_FILES([
+ Makefile
+ man/Makefile
+ bin/Makefile
+ lib/Makefile
+ cnf/Makefile
+])
+
+AC_OUTPUT
diff --git a/lib/Makefile.in b/lib/Makefile.in
new file mode 100644
index 000000000..7d4193cc9
--- /dev/null
+++ b/lib/Makefile.in
@@ -0,0 +1,41 @@
+SHELL = @PORTAGE_BASH@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+sysconfdir = @sysconfdir@
+libdir = @libdir@
+PYTHON = @PREFIX_PORTAGE_PYTHON@
+
+srcdir=@srcdir@
+top_builddir=@top_builddir@
+
+portageuser = @portageuser@
+portagegroup = @portagegroup@
+
+PORTAGE_PYM = @PORTAGE_BASE@/lib
+INSTALL = @INSTALL@
+INSTALL_subst = ${top_builddir}/subst-install
+
+all:
+
+install:
+ $(INSTALL) -d -m 755 -o "$(portageuser)" -g "$(portagegroup)" $(DESTDIR)$(PORTAGE_PYM)
+ ( cd "$(srcdir)" && find * -type d ) | while read f ; do \
+ files=( ) ; \
+ shopt -s nullglob ; \
+ for t in "$(srcdir)/$${f}"/* ; do \
+ [[ -d $${t} ]] && continue ; \
+ [[ $${t} == */Makefile* ]] && continue ; \
+ files=( "$${files[@]}" "$${t}" ) ; \
+ done ; \
+ $(INSTALL) -d -m 755 \
+ -o "$(portageuser)" -g "$(portagegroup)" \
+ "$(DESTDIR)$(PORTAGE_PYM)/$${f}" && \
+ [[ $${files[0]} ]] || continue ; \
+ $(INSTALL_subst) \
+ -o "$(portageuser)" -g "$(portagegroup)" \
+ -t "$(DESTDIR)$(PORTAGE_PYM)/$${f}" \
+ "$${files[@]}" \
+ ; done
+
+.PHONY: all install
diff --git a/lib/_emerge/EbuildPhase.py b/lib/_emerge/EbuildPhase.py
index b47280343..9dbd79fd1 100644
--- a/lib/_emerge/EbuildPhase.py
+++ b/lib/_emerge/EbuildPhase.py
@@ -32,6 +32,8 @@ from portage.util.futures import asyncio
from portage.util.futures.executor.fork import ForkExecutor
from portage.exception import InvalidBinaryPackageFormat
from portage.const import SUPPORTED_GENTOO_BINPKG_FORMATS
+# PREFIX LOCAL
+from portage.const import EPREFIX
try:
from portage.xml.metadata import MetaDataXML
@@ -636,6 +638,23 @@ class _PostPhaseCommands(CompositeTask):
os.path.join(self.settings["PORTAGE_BUILDDIR"], "build-info"), all_provides
)
+ # BEGIN PREFIX LOCAL
+ if EPREFIX != "" and unresolved:
+ # in prefix, consider the host libs for any unresolved libs,
+ # so we kill warnings about missing libc.so.1, etc.
+ for obj, libs in list(unresolved):
+ unresolved.remove((obj, libs))
+ libs=list(libs)
+ for lib in list(libs):
+ for path in ['/lib64', '/lib/64', '/lib', \
+ '/usr/lib64', '/usr/lib/64', '/usr/lib']:
+ if os.path.exists(os.path.join(path, lib)):
+ libs.remove(lib)
+ break
+ if len(libs) > 0:
+ unresolved.append((obj, tuple(libs)))
+ # END PREFIX LOCAL
+
if unresolved:
unresolved.sort()
qa_msg = ["QA Notice: Unresolved soname dependencies:"]
diff --git a/lib/_emerge/Package.py b/lib/_emerge/Package.py
index 79011380d..7d13897ba 100644
--- a/lib/_emerge/Package.py
+++ b/lib/_emerge/Package.py
@@ -86,6 +86,8 @@ class Package(Task):
"SLOT",
"USE",
"_mtime_",
+ # PREFIX LOCAL
+ "EPREFIX",
]
_dep_keys = ("BDEPEND", "DEPEND", "IDEPEND", "PDEPEND", "RDEPEND")
diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py
index 6853ec491..63e7ad1ee 100644
--- a/lib/_emerge/depgraph.py
+++ b/lib/_emerge/depgraph.py
@@ -12207,6 +12207,18 @@ def _get_masking_status(pkg, pkgsettings, root_config, myrepo=None, use=None):
if not pkgsettings._accept_chost(pkg.cpv, pkg._metadata):
mreasons.append(_MaskReason("CHOST", f"CHOST: {pkg._metadata['CHOST']}"))
+ # BEGIN PREFIX LOCAL: check EPREFIX
+ eprefix = pkgsettings["EPREFIX"]
+ if len(eprefix.rstrip('/')) > 0 and pkg.built and not pkg.installed:
+ if not "EPREFIX" in pkg._metadata:
+ mreasons.append(_MaskReason("EPREFIX",
+ "missing EPREFIX"))
+ elif len(pkg._metadata["EPREFIX"].strip()) < len(eprefix):
+ mreasons.append(_MaskReason("EPREFIX",
+ "EPREFIX: '%s' too small" % \
+ pkg._metadata["EPREFIX"]))
+ # END PREFIX LOCAL
+
if pkg.invalid:
for msgs in pkg.invalid.values():
for msg in msgs:
diff --git a/lib/_emerge/emergelog.py b/lib/_emerge/emergelog.py
index c3c42d8e6..3d4452e68 100644
--- a/lib/_emerge/emergelog.py
+++ b/lib/_emerge/emergelog.py
@@ -9,12 +9,15 @@ from portage import _unicode_decode
from portage import _unicode_encode
from portage.data import secpass
from portage.output import xtermTitle
+# PREFIX LOCAL
+from portage.const import EPREFIX
# We disable emergelog by default, since it's called from
# dblink.merge() and we don't want that to trigger log writes
# unless it's really called via emerge.
_disable = True
-_emerge_log_dir = "/var/log"
+# PREFIX LOCAL: log inside Prefix
+_emerge_log_dir = EPREFIX + '/var/log'
def emergelog(xterm_titles, mystr, short_msg=None):
diff --git a/lib/portage/__init__.py b/lib/portage/__init__.py
index aa81bdb4c..f097b653c 100644
--- a/lib/portage/__init__.py
+++ b/lib/portage/__init__.py
@@ -163,6 +163,10 @@ try:
MISC_SH_BINARY,
REPO_NAME_LOC,
REPO_NAME_FILE,
+ # BEGIN PREFIX LOCAL
+ EPREFIX,
+ rootuid,
+ # END PREFIX LOCAL
)
except ImportError as e:
diff --git a/lib/portage/const.py b/lib/portage/const.py
index 2154213b7..1909199ef 100644
--- a/lib/portage/const.py
+++ b/lib/portage/const.py
@@ -2,6 +2,13 @@
# Copyright 1998-2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
+# BEGIN PREFIX LOCAL
+# ===========================================================================
+# autotool supplied constants.
+# ===========================================================================
+from portage.const_autotool import *
+# END PREFIX LOCAL
+
import os
from portage import installation
@@ -104,6 +111,20 @@ BASH_BINARY = f"{BINARY_PREFIX}/bin/bash"
MOVE_BINARY = f"{BINARY_PREFIX}/bin/mv"
PRELINK_BINARY = f"{BINARY_PREFIX}/usr/sbin/prelink"
+# BEGIN PREFIX LOCAL: macOS sandbox
+MACOSSANDBOX_BINARY = "/usr/bin/sandbox-exec"
+MACOSSANDBOX_PROFILE = '''(version 1)
+(allow default)
+(deny file-write*)
+(allow file-write* file-write-setugid
+@@MACOSSANDBOX_PATHS@@)
+(allow file-write-data
+@@MACOSSANDBOX_PATHS_CONTENT_ONLY@@)'''
+
+PORTAGE_GROUPNAME = portagegroup
+PORTAGE_USERNAME = portageuser
+# END PREFIX LOCAL
+
INVALID_ENV_FILE = "/etc/spork/is/not/valid/profile.env"
MERGING_IDENTIFIER = "-MERGING-"
REPO_NAME_FILE = "repo_name"
diff --git a/lib/portage/const_autotool.py b/lib/portage/const_autotool.py
new file mode 100644
index 000000000..ee540319c
--- /dev/null
+++ b/lib/portage/const_autotool.py
@@ -0,0 +1,21 @@
+# Copyright: 2005-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+# all vars that are to wind up in portage_const must have their name listed in __all__
+
+__all__ = ["EPREFIX", "SYSCONFDIR", "PORTAGE_BASE",
+ "portageuser", "portagegroup", "rootuser", "rootuid", "rootgid",
+ "PORTAGE_BASH", "PORTAGE_MV"]
+
+EPREFIX = "@PORTAGE_EPREFIX@"
+SYSCONFDIR = "@sysconfdir@"
+PORTAGE_BASE = "@PORTAGE_BASE@"
+
+portagegroup = "@portagegroup@"
+portageuser = "@portageuser@"
+rootuser = "@rootuser@"
+rootuid = @rootuid@
+rootgid = @rootgid@
+
+PORTAGE_BASH = "@PORTAGE_BASH@"
+PORTAGE_MV = "@PORTAGE_MV@"
diff --git a/lib/portage/data.py b/lib/portage/data.py
index 4b9b74c7b..e57e672c3 100644
--- a/lib/portage/data.py
+++ b/lib/portage/data.py
@@ -6,6 +6,8 @@ import grp
import os
import platform
import pwd
+# PREFIX LOCAL
+from portage.const import PORTAGE_GROUPNAME, PORTAGE_USERNAME, EPREFIX
import portage
from portage.localization import _
@@ -20,7 +22,8 @@ portage.proxy.lazyimport.lazyimport(
ostype = platform.system()
userland = "GNU"
-if ostype == "DragonFly" or ostype.endswith("BSD"):
+# PREFIX LOCAL: Prefix always has USERLAND=GNU
+if EPREFIX == "" and (ostype == "DragonFly" or ostype.endswith("BSD")):
userland = "BSD"
lchown = getattr(os, "lchown", None)
@@ -166,44 +169,42 @@ def _get_global(k):
try:
portage_uid = pwd.getpwnam(_get_global("_portage_username")).pw_uid
except KeyError:
- keyerror = True
- portage_uid = 0
+ # PREFIX LOCAL: some sysadmins are insane, bug #344307
+ username = _get_global("_portage_username")
+ if username.isdigit():
+ portage_uid = int(username)
+ else:
+ keyerror = True
+ portage_uid = 0
+ # END PREFIX LOCAL
try:
portage_gid = grp.getgrnam(_get_global("_portage_grpname")).gr_gid
except KeyError:
- keyerror = True
- portage_gid = 0
+ # PREFIX LOCAL: some sysadmins are insane, bug #344307
+ grpname = _get_global("_portage_grpname")
+ if grpname.isdigit():
+ portage_gid = int(grpname)
+ else:
+ keyerror = True
+ portage_gid = 0
+ # END PREFIX LOCAL
# Suppress this error message if both PORTAGE_GRPNAME and
# PORTAGE_USERNAME are set to "root", for things like
# Android (see bug #454060).
- if keyerror and not (
- _get_global("_portage_username") == "root"
- and _get_global("_portage_grpname") == "root"
- ):
- writemsg(
- colorize("BAD", _("portage: 'portage' user or group missing.")) + "\n",
- noiselevel=-1,
- )
- writemsg(
- _(
- " For the defaults, line 1 goes into passwd, "
- "and 2 into group.\n"
- ),
- noiselevel=-1,
- )
- writemsg(
- colorize(
- "GOOD",
- " portage:x:250:250:portage:/var/tmp/portage:/bin/false",
- )
- + "\n",
- noiselevel=-1,
- )
- writemsg(
- colorize("GOOD", " portage::250:portage") + "\n", noiselevel=-1
- )
+ if keyerror and not (_get_global('_portage_username') == "root" and
+ _get_global('_portage_grpname') == "root"):
+ # PREFIX LOCAL: we need to fix this one day to distinguish prefix vs non-prefix
+ writemsg(colorize("BAD",
+ _("portage: '%s' user or '%s' group missing." % (_get_global('_portage_username'), _get_global('_portage_grpname')))) + "\n", noiselevel=-1)
+ writemsg(colorize("BAD",
+ _(" In Prefix Portage this is quite dramatic")) + "\n", noiselevel=-1)
+ writemsg(colorize("BAD",
+ _(" since it means you have thrown away yourself.")) + "\n", noiselevel=-1)
+ writemsg(colorize("BAD",
+ _(" Re-add yourself or re-bootstrap Gentoo Prefix.")) + "\n", noiselevel=-1)
+ # END PREFIX LOCAL
portage_group_warning()
globals()["portage_gid"] = portage_gid
@@ -331,13 +332,17 @@ def _init(settings):
# from grp.getgrnam() with PyPy
native_string = platform.python_implementation() == "PyPy"
- v = settings.get("PORTAGE_GRPNAME", "portage")
+ # PREFIX LOCAL: use var iso hardwired 'portage'
+ v = settings.get("PORTAGE_GRPNAME", PORTAGE_GROUPNAME)
+ # END PREFIX LOCAL
if native_string:
v = portage._native_string(v)
globals()["_portage_grpname"] = v
_initialized_globals.add("_portage_grpname")
- v = settings.get("PORTAGE_USERNAME", "portage")
+ # PREFIX LOCAL: use var iso hardwired 'portage'
+ v = settings.get("PORTAGE_USERNAME", PORTAGE_USERNAME)
+ # END PREFIX LOCAL
if native_string:
v = portage._native_string(v)
globals()["_portage_username"] = v
diff --git a/lib/portage/dbapi/bintree.py b/lib/portage/dbapi/bintree.py
index f4251b47d..005681adc 100644
--- a/lib/portage/dbapi/bintree.py
+++ b/lib/portage/dbapi/bintree.py
@@ -132,6 +132,8 @@ class bindbapi(fakedbapi):
"SLOT",
"USE",
"_mtime_",
+ # PREFIX LOCAL
+ "EPREFIX",
}
self._aux_cache = {}
self._aux_cache_slot_dict_cache = None
@@ -563,6 +565,8 @@ class binarytree:
"SIZE",
"SLOT",
"USE",
+ # PREFIX LOCAL
+ "EPREFIX",
]
self._pkgindex_use_evaluated_keys = (
"BDEPEND",
@@ -592,6 +596,8 @@ class binarytree:
"USE_EXPAND_HIDDEN",
"USE_EXPAND_IMPLICIT",
"USE_EXPAND_UNPREFIXED",
+ # PREFIX LOCAL
+ "EPREFIX",
}
self._pkgindex_default_pkg_data = {
"BDEPEND": "",
@@ -615,6 +621,8 @@ class binarytree:
"USE": "",
}
self._pkgindex_inherited_keys = ["CHOST", "repository"]
+ # PREFIX LOCAL
+ self._pkgindex_inherited_keys += ["EPREFIX"]
# Populate the header with appropriate defaults.
self._pkgindex_default_header_data = {
diff --git a/lib/portage/dbapi/vartree.py b/lib/portage/dbapi/vartree.py
index c6b45ba42..05e16b569 100644
--- a/lib/portage/dbapi/vartree.py
+++ b/lib/portage/dbapi/vartree.py
@@ -38,6 +38,9 @@ portage.proxy.lazyimport.lazyimport(
"portage.util._xattr:xattr",
"portage.util._dyn_libs.PreservedLibsRegistry:PreservedLibsRegistry",
"portage.util._dyn_libs.LinkageMapELF:LinkageMapELF@LinkageMap",
+ # BEGIN PREFIX LOCAL
+ 'portage.util._dyn_libs.LinkageMapMachO:LinkageMapMachO',
+ # END PREFIX LOCAL
"portage.util._dyn_libs.NeededEntry:NeededEntry",
"portage.util._async.SchedulerInterface:SchedulerInterface",
"portage.util._eventloop.global_event_loop:global_event_loop",
@@ -56,6 +59,8 @@ from portage.const import (
PRIVATE_PATH,
VDB_PATH,
SUPPORTED_GENTOO_BINPKG_FORMATS,
+ # PREFIX LOCAL
+ EPREFIX,
)
from portage.dbapi import dbapi
from portage.exception import (
@@ -232,7 +237,17 @@ class vardbapi(dbapi):
settings["ROOT"],
os.path.join(self._eroot, PRIVATE_PATH, "preserved_libs_registry"),
)
- self._linkmap = LinkageMap(self)
+
+ # BEGIN PREFIX LOCAL: set linkagemap to the platform specific
+ # provider
+ chost = self.settings.get('CHOST')
+ if not chost:
+ chost = 'lunix?' # this happens when profiles are not available
+ if chost.find('darwin') >= 0:
+ self._linkmap = LinkageMapMachO(self)
+ else:
+ self._linkmap = LinkageMap(self)
+ # END PREFIX LOCAL
self._owners = self._owners_db(self)
self._cached_counter = None
@@ -3715,7 +3730,13 @@ class dblink:
def path_to_node(path):
node = path_node_map.get(path)
if node is None:
- node = LinkageMap._LibGraphNode(linkmap._obj_key(path))
+ # BEGIN PREFIX LOCAL: use arch specific impl
+ chost = self.settings.get('CHOST')
+ if chost.find('darwin') >= 0:
+ node = LinkageMapMachO._LibGraphNode(linkmap._obj_key(path))
+ else:
+ node = LinkageMap._LibGraphNode(linkmap._obj_key(path))
+ # END PREFIX LOCAL
alt_path_node = lib_graph.get(node)
if alt_path_node is not None:
node = alt_path_node
diff --git a/lib/portage/getbinpkg.py b/lib/portage/getbinpkg.py
index fca44f052..0231908d3 100644
--- a/lib/portage/getbinpkg.py
+++ b/lib/portage/getbinpkg.py
@@ -19,6 +19,8 @@ import socket
import time
import tempfile
import base64
+# PREFIX LOCAL
+from portage.const import CACHE_PATH
import warnings
from html.parser import HTMLParser as html_parser_HTMLParser
@@ -603,11 +605,13 @@ def dir_get_metadata(
if conn:
keepconnection = 0
- cache_path = "/var/cache/edb"
+ # PREFIX LOCAL
+ cache_path = CACHE_PATH
metadatafilename = os.path.join(cache_path, "remote_metadata.pickle")
if not makepickle:
- makepickle = "/var/cache/edb/metadata.idx.most_recent"
+ # PREFIX LOCAL: use CACHE_PATH for EPREFIX
+ makepickle = os.path.join(cache_path, "metadata.idx.most_recent")
try:
conn = create_conn(baseurl, conn)[0]
diff --git a/lib/portage/meson.build b/lib/portage/meson.build
index 06dde8ca7..6a648fd9d 100644
--- a/lib/portage/meson.build
+++ b/lib/portage/meson.build
@@ -21,6 +21,7 @@ py.install_sources(
'binpkg.py',
'checksum.py',
const_py,
+ 'const_autotool.py', # PREFIX_LOCAL
'cvstree.py',
'data.py',
'debug.py',
diff --git a/lib/portage/package/ebuild/_config/special_env_vars.py b/lib/portage/package/ebuild/_config/special_env_vars.py
index 6020029e3..e74a3f2b3 100644
--- a/lib/portage/package/ebuild/_config/special_env_vars.py
+++ b/lib/portage/package/ebuild/_config/special_env_vars.py
@@ -240,6 +240,11 @@ environ_whitelist = frozenset(
"STY",
"WINDOW",
"XAUTHORITY",
+ # BEGIN PREFIX LOCAL
+ "EXTRA_PATH",
+ "PORTAGE_GROUP",
+ "PORTAGE_USER",
+ # END PREFIX LOCAL
)
)
@@ -262,6 +267,19 @@ environ_filter = frozenset(
"INFOPATH",
"MANPATH",
"USER",
+ # BEGIN PREFIX LOCAL
+ "HOST",
+ "GROUP",
+ "LOGNAME",
+ "MAIL",
+ "REMOTEHOST",
+ "SECURITYSESSIONID",
+ "TERMINFO",
+ "TERM_PROGRAM",
+ "TERM_PROGRAM_VERSION",
+ "VENDOR",
+ "__CF_USER_TEXT_ENCODING",
+ # END PREFIX LOCAL
# variables that break bash
"HISTFILE",
"POSIXLY_CORRECT",
diff --git a/lib/portage/package/ebuild/config.py b/lib/portage/package/ebuild/config.py
index bafdc55a0..72670cbe1 100644
--- a/lib/portage/package/ebuild/config.py
+++ b/lib/portage/package/ebuild/config.py
@@ -65,6 +65,8 @@ from portage.exception import InvalidDependString, PortageException
from portage.localization import _
from portage.output import colorize
from portage.process import fakeroot_capable, sandbox_capable
+# PREFIX LOCAL
+from portage.process import macossandbox_capable
from portage.repository.config import (
allow_profile_repo_deps,
load_repository_config,
@@ -1095,8 +1097,15 @@ class config:
}
eroot_or_parent = first_existing(eroot)
- unprivileged = False
+ # PREFIX LOCAL
+ unprivileged = portage.const.EPREFIX != ''
try:
+ # PREFIX LOCAL: inventing UID/GID based on a path is a very
+ # bad idea, it breaks almost everything since group ids
+ # don't have to match, when a user has many
+ # This in particularly breaks the configure-set portage
+ # group and user (in portage/data.py)
+ raise OSError(2, "No such file or directory")
eroot_st = os.stat(eroot_or_parent)
except OSError:
pass
diff --git a/lib/portage/package/ebuild/doebuild.py b/lib/portage/package/ebuild/doebuild.py
index bc51fdff2..7994394bd 100644
--- a/lib/portage/package/ebuild/doebuild.py
+++ b/lib/portage/package/ebuild/doebuild.py
@@ -22,6 +22,8 @@ import time
from typing import Union
import warnings
import zlib
+# PREFIX LOCAL
+import platform
import portage
@@ -67,6 +69,10 @@ from portage.const import (
MISC_SH_BINARY,
PORTAGE_PYM_PACKAGES,
SUPPORTED_GENTOO_BINPKG_FORMATS,
+ # BEGIN PREFIX LOCAL
+ EPREFIX,
+ MACOSSANDBOX_PROFILE,
+ # END PREFIX LOCAL
)
from portage.data import portage_gid, portage_uid, secpass, uid, userpriv_groups
from portage.dbapi.porttree import _parse_uri_map
@@ -331,6 +337,11 @@ def _doebuild_path(settings, eapi=None):
path.append(p)
pathset.add(p)
+ # BEGIN PREFIX LOCAL: append EXTRA_PATH from make.globals
+ extrapath = [x for x in settings.get("EXTRA_PATH", "").split(":") if x]
+ path.extend(extrapath)
+ # END PREFIX LOCAL
+
settings["PATH"] = ":".join(path)
@@ -1698,7 +1709,9 @@ def _spawn_actionmap(settings):
and "nouserpriv" not in restrict
)
- if not portage.process.sandbox_capable:
+ # PREFIX LOCAL: macOS sandbox
+ if not (portage.process.sandbox_capable
+ or portage.process.macossandbox_capable):
nosandbox = True
sesandbox = settings.selinux_enabled() and "sesandbox" in features
@@ -2058,6 +2071,10 @@ def spawn(
user = "root"
elif portage_build_uid == portage_uid:
user = portage.data._portage_username
+ # BEGIN PREFIX LOCAL: accept numeric uid
+ else:
+ user = portage_uid
+ # END PREFIX LOCAL
if user is not None:
mysettings["PORTAGE_BUILD_USER"] = user
@@ -2070,6 +2087,10 @@ def spawn(
group = "root"
elif portage_build_gid == portage_gid:
group = portage.data._portage_grpname
+ # BEGIN PREFIX LOCAL: accept numeric gid
+ else:
+ group = portage_gid
+ # END PREFIX LOCAL
if group is not None:
mysettings["PORTAGE_BUILD_GROUP"] = group
@@ -2081,7 +2102,9 @@ def spawn(
and not fakeroot
)
- if not free and not (fakeroot or portage.process.sandbox_capable):
+ # PREFIX LOCAL: macOS sandbox
+ if not free and not (fakeroot or portage.process.sandbox_capable
+ or portage.process.macossandbox_capable):
free = True
if mysettings.mycpv is not None:
@@ -2099,6 +2122,79 @@ def spawn(
keywords["opt_name"] += " fakeroot"
keywords["fakeroot_state"] = os.path.join(mysettings["T"], "fakeroot.state")
spawn_func = portage.process.spawn_fakeroot
+ # BEGIN PREFIX LOCAL
+ elif "sandbox" in features and platform.system() == 'Darwin':
+ keywords["opt_name"] += " macossandbox"
+ sbprofile = MACOSSANDBOX_PROFILE
+
+ # determine variable names from profile: split
+ # "text@@VARNAME@@moretext@@OTHERVAR@@restoftext" into
+ # ("text", # "VARNAME", "moretext", "OTHERVAR", "restoftext")
+ # and extract variable named by reading every second item.
+ variables = []
+ for line in sbprofile.split("\n"):
+ variables.extend(line.split("@@")[1:-1:2])
+
+ for var in variables:
+ paths = ""
+ if var in mysettings:
+ paths = mysettings[var]
+ else:
+ writemsg("Warning: sandbox profile references variable %s "
+ "which is not set.\nThe rule using it will have no "
+ "effect, which is most likely not the intended "
+ "result.\nPlease check make.conf/make.globals.\n" %
+ var)
+
+ # not set or empty value
+ if not paths:
+ sbprofile = sbprofile.replace("@@%s@@" % var, "")
+ continue
+
+ rules_literal = ""
+ rules_regex = ""
+
+ # FIXME: Allow for quoting inside the variable
+ # to allow paths with spaces in them?
+ for path in paths.split(" "):
+ # do a second round of token
+ # replacements to be able to reference
+ # settings like EPREFIX or
+ # PORTAGE_BUILDDIR.
+ for token in path.split("@@")[1:-1:2]:
+ if token not in mysettings:
+ continue
+
+ path = path.replace("@@%s@@" % token, mysettings[token])
+
+ if "@@" in path:
+ # unreplaced tokens left -
+ # silently ignore path - needed
+ # for PORTAGE_ACTUAL_DISTDIR
+ # which isn't always set
+ pass
+ elif path[-1] == os.sep:
+ # path ends in slash - make it a
+ # regex and allow access
+ # recursively.
+ path = path.replace(r'+', r'\+')
+ path = path.replace(r'*', r'\*')
+ path = path.replace(r'[', r'\[')
+ path = path.replace(r']', r'\]')
+ rules_regex += " #\"^%s\"\n" % path
+ else:
+ rules_literal += " #\"%s\"\n" % path
+
+ rules = ""
+ if rules_literal:
+ rules += " (literal\n" + rules_literal + " )\n"
+ if rules_regex:
+ rules += " (regex\n" + rules_regex + " )\n"
+ sbprofile = sbprofile.replace("@@%s@@" % var, rules)
+
+ keywords["profile"] = sbprofile
+ spawn_func = portage.process.spawn_macossandbox
+ # END PREFIX LOCAL
else:
keywords["opt_name"] += " sandbox"
spawn_func = portage.process.spawn_sandbox
@@ -2231,13 +2327,17 @@ _post_phase_cmds = {
(
{},
[
+ # PREFIX LOCAL
"preinst_sfperms",
"preinst_suid_scan",
"preinst_qa_check",
],
),
),
- "postinst": ["postinst_qa_check"],
+ "postinst": [
+ # PREFIX LOCAL
+ "postinst_qa_check",
+ ],
}
diff --git a/lib/portage/package/ebuild/fetch.py b/lib/portage/package/ebuild/fetch.py
index bfa0c2b27..ca15a6c7d 100644
--- a/lib/portage/package/ebuild/fetch.py
+++ b/lib/portage/package/ebuild/fetch.py
@@ -52,6 +52,8 @@ from portage.checksum import (
checksum_str,
)
from portage.const import BASH_BINARY, CUSTOM_MIRRORS_FILE, GLOBAL_CONFIG_PATH
+# PREFIX LOCAL
+from portage.const import rootgid
from portage.data import portage_gid, portage_uid, userpriv_groups
from portage.exception import (
FileNotFound,
@@ -234,7 +236,8 @@ def _ensure_distdir(settings, distdir):
# to have root's gid. Therefore, use root's gid instead of
# portage's gid to avoid spurious permissions adjustments
# when inside fakeroot.
- dir_gid = 0
+ # PREFIX LOCAL: do not assume root to be 0
+ dir_gid = rootgid
userfetch = portage.data.secpass >= 2 and "userfetch" in settings.features
userpriv = portage.data.secpass >= 2 and "userpriv" in settings.features
diff --git a/lib/portage/process.py b/lib/portage/process.py
index cc9ed7bf7..2365778e6 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -35,6 +35,8 @@ portage.proxy.lazyimport.lazyimport(
)
from portage.const import BASH_BINARY, SANDBOX_BINARY, FAKEROOT_BINARY
+# PREFIX LOCAL
+from portage.const import MACOSSANDBOX_BINARY
from portage.exception import CommandNotFound
from portage.proxy.objectproxy import ObjectProxy
from portage.util._ctypes import find_library, LoadLibrary, ctypes
@@ -114,6 +116,9 @@ fakeroot_capable = os.path.isfile(FAKEROOT_BINARY) and os.access(
FAKEROOT_BINARY, os.X_OK
)
+# PREFIX LOCAL
+macossandbox_capable = (os.path.isfile(MACOSSANDBOX_BINARY) and
+ os.access(MACOSSANDBOX_BINARY, os.X_OK))
def sanitize_fds():
"""
@@ -191,6 +196,21 @@ def spawn_fakeroot(mycommand, fakeroot_state=None, opt_name=None, **keywords):
return spawn(args, opt_name=opt_name, **keywords)
+# BEGIN PREFIX LOCAL
+def spawn_macossandbox(mycommand, profile=None, opt_name=None, **keywords):
+ if not macossandbox_capable:
+ return spawn_bash(mycommand, opt_name=opt_name, **keywords)
+ args=[MACOSSANDBOX_BINARY]
+ if not opt_name:
+ opt_name = os.path.basename(mycommand.split()[0])
+ args.append("-p")
+ args.append(profile)
+ args.append(BASH_BINARY)
+ args.append("-c")
+ args.append(mycommand)
+ return spawn(args, opt_name=opt_name, **keywords)
+# END PREFIX LOCAL
+
_exithandlers = []
diff --git a/lib/portage/tests/lazyimport/test_lazy_import_portage_baseline.py b/lib/portage/tests/lazyimport/test_lazy_import_portage_baseline.py
index cbeba37b5..a52bb4cbb 100644
--- a/lib/portage/tests/lazyimport/test_lazy_import_portage_baseline.py
+++ b/lib/portage/tests/lazyimport/test_lazy_import_portage_baseline.py
@@ -24,6 +24,8 @@ class LazyImportPortageBaselineTestCase(TestCase):
"portage.proxy.lazyimport",
"portage.proxy.objectproxy",
"portage._selinux",
+ # PREFIX LOCAL
+ "portage.const_autotool",
]
)
diff --git a/lib/portage/tests/resolver/ResolverPlayground.py b/lib/portage/tests/resolver/ResolverPlayground.py
index 75c86b615..1cb365449 100644
--- a/lib/portage/tests/resolver/ResolverPlayground.py
+++ b/lib/portage/tests/resolver/ResolverPlayground.py
@@ -371,6 +371,8 @@ class ResolverPlayground:
metadata["CATEGORY"] = cat
metadata["PF"] = pf
metadata["BINPKG_FORMAT"] = binpkg_format
+ # PREFIX LOCAL
+ metadata["EPREFIX"] = self.eprefix
repo_dir = self.pkgdir
category_dir = os.path.join(repo_dir, cat)
diff --git a/lib/portage/util/__init__.py b/lib/portage/util/__init__.py
index 1f8c9e94f..0be05ab03 100644
--- a/lib/portage/util/__init__.py
+++ b/lib/portage/util/__init__.py
@@ -75,6 +75,8 @@ import glob
from typing import Optional, TextIO
import portage
+# PREFIX LOCAL
+from portage.const import EPREFIX
portage.proxy.lazyimport.lazyimport(
globals(),
@@ -2007,11 +2009,17 @@ def getlibpaths(root, env=None):
""" Return a list of paths that are used for library lookups """
if env is None:
env = os.environ
+ # BEGIN PREFIX LOCAL:
+ # For Darwin, match LD_LIBRARY_PATH with DYLD_LIBRARY_PATH.
+ # We don't need any host OS lib paths in Prefix, so just going with
+ # the prefixed one is fine.
# the following is based on the information from ld.so(8)
rval = env.get("LD_LIBRARY_PATH", "").split(":")
- rval.extend(read_ld_so_conf(os.path.join(root, "etc", "ld.so.conf")))
- rval.append("/usr/lib")
- rval.append("/lib")
+ rval.extend(env.get("DYLD_LIBRARY_PATH", "").split(":"))
+ rval.extend(read_ld_so_conf(os.path.join(root, EPREFIX, "etc", "ld.so.conf")))
+ rval.append(f"{EPREFIX}/usr/lib")
+ rval.append(f"{EPREFIX}/lib")
+ # END PREFIX LOCAL
return [normalize_path(x) for x in rval if x]
diff --git a/lib/portage/util/_dyn_libs/LinkageMapMachO.py b/lib/portage/util/_dyn_libs/LinkageMapMachO.py
new file mode 100644
index 000000000..e74f0c5ba
--- /dev/null
+++ b/lib/portage/util/_dyn_libs/LinkageMapMachO.py
@@ -0,0 +1,773 @@
+# Copyright 1998-2019 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+import errno
+import logging
+import subprocess
+
+import portage
+from portage import _encodings
+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.exception import CommandNotFound
+from portage.localization import _
+from portage.util import getlibpaths
+from portage.util import grabfile
+from portage.util import normalize_path
+from portage.util import writemsg_level
+from portage.const import EPREFIX
+
+class LinkageMapMachO(object):
+
+ """Models dynamic linker dependencies."""
+
+ _needed_aux_key = "NEEDED.MACHO.3"
+ _installname_map_class = slot_dict_class(
+ ("consumers", "providers"), prefix="")
+
+ class _obj_properties_class(object):
+
+ __slots__ = ("arch", "needed", "install_name", "alt_paths",
+ "owner",)
+
+ def __init__(self, arch, needed, install_name, alt_paths, owner):
+ self.arch = arch
+ self.needed = needed
+ self.install_name = install_name
+ self.alt_paths = alt_paths
+ self.owner = owner
+
+ def __init__(self, vardbapi):
+ self._dbapi = vardbapi
+ self._root = self._dbapi.settings['ROOT']
+ self._libs = {}
+ self._obj_properties = {}
+ self._obj_key_cache = {}
+ self._path_key_cache = {}
+
+ def _clear_cache(self):
+ self._libs.clear()
+ self._obj_properties.clear()
+ self._obj_key_cache.clear()
+ self._path_key_cache.clear()
+
+ def _path_key(self, path):
+ key = self._path_key_cache.get(path)
+ if key is None:
+ key = self._ObjectKey(path, self._root)
+ self._path_key_cache[path] = key
+ return key
+
+ def _obj_key(self, path):
+ key = self._obj_key_cache.get(path)
+ if key is None:
+ key = self._ObjectKey(path, self._root)
+ self._obj_key_cache[path] = key
+ return key
+
+ class _ObjectKey(object):
+
+ """Helper class used as _obj_properties keys for objects."""
+
+ __slots__ = ("_key",)
+
+ def __init__(self, obj, root):
+ """
+ This takes a path to an object.
+
+ @param object: path to a file
+ @type object: string (example: '/usr/bin/bar')
+
+ """
+ self._key = self._generate_object_key(obj, root)
+
+ def __hash__(self):
+ return hash(self._key)
+
+ def __eq__(self, other):
+ return self._key == other._key
+
+ def _generate_object_key(self, obj, root):
+ """
+ Generate object key for a given object.
+
+ @param object: path to a file
+ @type object: string (example: '/usr/bin/bar')
+ @rtype: 2-tuple of types (long, int) if object exists. string if
+ object does not exist.
+ @return:
+ 1. 2-tuple of object's inode and device from a stat call, if object
+ exists.
+ 2. realpath of object if object does not exist.
+
+ """
+
+ os = _os_merge
+
+ try:
+ _unicode_encode(obj,
+ encoding=_encodings['merge'], errors='strict')
+ except UnicodeEncodeError:
+ # The package appears to have been merged with a
+ # different value of sys.getfilesystemencoding(),
+ # so fall back to utf_8 if appropriate.
+ try:
+ _unicode_encode(obj,
+ encoding=_encodings['fs'], errors='strict')
+ except UnicodeEncodeError:
+ pass
+ else:
+ os = portage.os
+
+ abs_path = os.path.join(root, obj.lstrip(os.sep))
+ try:
+ object_stat = os.stat(abs_path)
+ except OSError:
+ # Use the realpath as the key if the file does not exists on the
+ # filesystem.
+ return os.path.realpath(abs_path)
+ # Return a tuple of the device and inode.
+ return (object_stat.st_dev, object_stat.st_ino)
+
+ def file_exists(self):
+ """
+ Determine if the file for this key exists on the filesystem.
+
+ @rtype: Boolean
+ @return:
+ 1. True if the file exists.
+ 2. False if the file does not exist or is a broken symlink.
+
+ """
+ return isinstance(self._key, tuple)
+
+ class _LibGraphNode(_ObjectKey):
+ __slots__ = ("alt_paths",)
+
+ def __init__(self, key):
+ """
+ Create a _LibGraphNode from an existing _ObjectKey.
+ This re-uses the _key attribute in order to avoid repeating
+ any previous stat calls, which helps to avoid potential race
+ conditions due to inconsistent stat results when the
+ file system is being modified concurrently.
+ """
+ self._key = key._key
+ self.alt_paths = set()
+
+ def __str__(self):
+ return str(sorted(self.alt_paths))
+
+ def rebuild(self, exclude_pkgs=None, include_file=None,
+ preserve_paths=None):
+ """
+ Raises CommandNotFound if there are preserved libs
+ and the scanmacho binary is not available.
+
+ @param exclude_pkgs: A set of packages that should be excluded from
+ the LinkageMap, since they are being unmerged and their NEEDED
+ entries are therefore irrelevant and would only serve to corrupt
+ the LinkageMap.
+ @type exclude_pkgs: set
+ @param include_file: The path of a file containing NEEDED entries for
+ a package which does not exist in the vardbapi yet because it is
+ currently being merged.
+ @type include_file: String
+ @param preserve_paths: Libraries preserved by a package instance that
+ is currently being merged. They need to be explicitly passed to the
+ LinkageMap, since they are not registered in the
+ PreservedLibsRegistry yet.
+ @type preserve_paths: set
+ """
+
+ os = _os_merge
+ root = self._root
+ root_len = len(root) - 1
+ self._clear_cache()
+ libs = self._libs
+ obj_properties = self._obj_properties
+
+ lines = []
+
+ # Data from include_file is processed first so that it
+ # overrides any data from previously installed files.
+ if include_file is not None:
+ for line in grabfile(include_file):
+ lines.append((None, include_file, line))
+
+ aux_keys = [self._needed_aux_key]
+ can_lock = os.access(os.path.dirname(self._dbapi._dbroot), os.W_OK)
+ if can_lock:
+ self._dbapi.lock()
+ try:
+ for cpv in self._dbapi.cpv_all():
+ if exclude_pkgs is not None and cpv in exclude_pkgs:
+ continue
+ needed_file = self._dbapi.getpath(cpv,
+ filename=self._needed_aux_key)
+ for line in self._dbapi.aux_get(cpv, aux_keys)[0].splitlines():
+ lines.append((cpv, needed_file, line))
+ finally:
+ if can_lock:
+ self._dbapi.unlock()
+
+ # have to call scanmacho for preserved libs here as they aren't
+ # registered in NEEDED.MACHO.3 files
+ plibs = {}
+ if preserve_paths is not None:
+ plibs.update((x, None) for x in preserve_paths)
+ if self._dbapi._plib_registry and \
+ self._dbapi._plib_registry.hasEntries():
+ for cpv, items in \
+ self._dbapi._plib_registry.getPreservedLibs().items():
+ if exclude_pkgs is not None and cpv in exclude_pkgs:
+ # These preserved libs will either be unmerged,
+ # rendering them irrelevant, or they will be
+ # preserved in the replacement package and are
+ # already represented via the preserve_paths
+ # parameter.
+ continue
+ plibs.update((x, cpv) for x in items)
+ if plibs:
+ args = [os.path.join(EPREFIX or "/", "usr/bin/scanmacho"), "-qF", "%a;%F;%S;%n"]
+ args.extend(os.path.join(root, x.lstrip("." + os.sep)) \
+ for x in plibs)
+ try:
+ proc = subprocess.Popen(args, stdout=subprocess.PIPE)
+ except EnvironmentError as e:
+ if e.errno != errno.ENOENT:
+ raise
+ raise CommandNotFound(args[0])
+ else:
+ for l in proc.stdout:
+ try:
+ l = _unicode_decode(l,
+ encoding=_encodings['content'], errors='strict')
+ except UnicodeDecodeError:
+ l = _unicode_decode(l,
+ encoding=_encodings['content'], errors='replace')
+ writemsg_level(_("\nError decoding characters " \
+ "returned from scanmacho: %s\n\n") % (l,),
+ level=logging.ERROR, noiselevel=-1)
+ l = l.rstrip("\n")
+ if not l:
+ continue
+ fields = l.split(";")
+ if len(fields) < 4:
+ writemsg_level("\nWrong number of fields " + \
+ "returned from scanmacho: %s\n\n" % (l,),
+ level=logging.ERROR, noiselevel=-1)
+ continue
+ fields[1] = fields[1][root_len:]
+ owner = plibs.pop(fields[1], None)
+ lines.append((owner, "scanmacho", ";".join(fields)))
+ proc.wait()
+ proc.stdout.close()
+
+ if plibs:
+ # Preserved libraries that did not appear in the scanmacho output.
+ # This is known to happen with statically linked libraries.
+ # Generate dummy lines for these, so we can assume that every
+ # preserved library has an entry in self._obj_properties. This
+ # is important in order to prevent findConsumers from raising
+ # an unwanted KeyError.
+ for x, cpv in plibs.items():
+ lines.append((cpv, "plibs", ";".join(['', x, '', '', ''])))
+
+ # Share identical frozenset instances when available,
+ # in order to conserve memory.
+ frozensets = {}
+
+ for owner, location, l in lines:
+ l = l.rstrip("\n")
+ if not l:
+ continue
+ fields = l.split(";")
+ if len(fields) < 4:
+ writemsg_level(_("\nWrong number of fields " \
+ "in %s: %s\n\n") % (location, l),
+ level=logging.ERROR, noiselevel=-1)
+ continue
+ arch = fields[0]
+ obj = fields[1]
+ install_name = os.path.normpath(fields[2])
+ needed = frozenset(x for x in fields[3].split(",") if x)
+ needed = frozensets.setdefault(needed, needed)
+
+ obj_key = self._obj_key(obj)
+ indexed = True
+ myprops = obj_properties.get(obj_key)
+ if myprops is None:
+ indexed = False
+ myprops = self._obj_properties_class(
+ arch, needed, install_name, [], owner)
+ obj_properties[obj_key] = myprops
+ # All object paths are added into the obj_properties tuple.
+ myprops.alt_paths.append(obj)
+
+ # Don't index the same file more that once since only one
+ # set of data can be correct and therefore mixing data
+ # may corrupt the index (include_file overrides previously
+ # installed).
+ if indexed:
+ continue
+
+ arch_map = libs.get(arch)
+ if arch_map is None:
+ arch_map = {}
+ libs[arch] = arch_map
+ if install_name:
+ installname_map = arch_map.get(install_name)
+ if installname_map is None:
+ installname_map = self._installname_map_class(
+ providers=[], consumers=[])
+ arch_map[install_name] = installname_map
+ installname_map.providers.append(obj_key)
+ for needed_installname in needed:
+ installname_map = arch_map.get(needed_installname)
+ if installname_map is None:
+ installname_map = self._installname_map_class(
+ providers=[], consumers=[])
+ arch_map[needed_installname] = installname_map
+ installname_map.consumers.append(obj_key)
+
+ for arch, install_names in libs.items():
+ for install_name_node in install_names.values():
+ install_name_node.providers = tuple(set(install_name_node.providers))
+ install_name_node.consumers = tuple(set(install_name_node.consumers))
+
+ def listBrokenBinaries(self, debug=False):
+ """
+ Find binaries and their needed install_names, which have no providers.
+
+ @param debug: Boolean to enable debug output
+ @type debug: Boolean
+ @rtype: dict (example: {'/usr/bin/foo': set(['/usr/lib/libbar.dylib'])})
+ @return: The return value is an object -> set-of-install_names mapping, where
+ object is a broken binary and the set consists of install_names needed by
+ object that have no corresponding libraries to fulfill the dependency.
+
+ """
+
+ os = _os_merge
+
+ class _LibraryCache(object):
+
+ """
+ Caches properties associated with paths.
+
+ The purpose of this class is to prevent multiple instances of
+ _ObjectKey for the same paths.
+
+ """
+
+ def __init__(cache_self):
+ cache_self.cache = {}
+
+ def get(cache_self, obj):
+ """
+ Caches and returns properties associated with an object.
+
+ @param obj: absolute path (can be symlink)
+ @type obj: string (example: '/usr/lib/libfoo.dylib')
+ @rtype: 4-tuple with types
+ (string or None, string or None, 2-tuple, Boolean)
+ @return: 4-tuple with the following components:
+ 1. arch as a string or None if it does not exist,
+ 2. soname as a string or None if it does not exist,
+ 3. obj_key as 2-tuple,
+ 4. Boolean representing whether the object exists.
+ (example: ('libfoo.1.dylib', (123L, 456L), True))
+
+ """
+ if obj in cache_self.cache:
+ return cache_self.cache[obj]
+ else:
+ obj_key = self._obj_key(obj)
+ # Check that the library exists on the filesystem.
+ if obj_key.file_exists():
+ # Get the install_name from LinkageMapMachO._obj_properties if
+ # it exists. Otherwise, None.
+ obj_props = self._obj_properties.get(obj_key)
+ if obj_props is None:
+ arch = None
+ install_name = None
+ else:
+ arch = obj_props.arch
+ install_name = obj_props.install_name
+ return cache_self.cache.setdefault(obj, \
+ (arch, install_name, obj_key, True))
+ else:
+ return cache_self.cache.setdefault(obj, \
+ (None, None, obj_key, False))
+
+ rValue = {}
+ cache = _LibraryCache()
+ providers = self.listProviders()
+
+ # Iterate over all obj_keys and their providers.
+ for obj_key, install_names in providers.items():
+ obj_props = self._obj_properties[obj_key]
+ arch = obj_props.arch
+ objs = obj_props.alt_paths
+ # Iterate over each needed install_name and the set of
+ # library paths that fulfill the install_name to determine
+ # if the dependency is broken.
+ for install_name, libraries in install_names.items():
+ # validLibraries is used to store libraries, which
+ # satisfy install_name, so if no valid libraries are
+ # found, the install_name is not satisfied for obj_key.
+ # If unsatisfied, objects associated with obj_key must
+ # be emerged.
+ validLibrary = set() # for compat with LinkageMap
+ cachedArch, cachedInstallname, cachedKey, cachedExists = \
+ cache.get(install_name)
+ # Check that the this library provides the needed soname. Doing
+ # this, however, will cause consumers of libraries missing
+ # sonames to be unnecessarily emerged. (eg libmix.so)
+ if cachedInstallname == install_name and cachedArch == arch:
+ validLibrary.add(cachedKey)
+ if debug and cachedKey not in \
+ set(map(self._obj_key_cache.get, libraries)):
+ # XXX This is most often due to soname symlinks not in
+ # a library's directory. We could catalog symlinks in
+ # LinkageMap to avoid checking for this edge case here.
+ print(_("Found provider outside of findProviders:"), \
+ install_name, "->", cachedRealpath)
+ if debug and cachedArch == arch and \
+ cachedKey in self._obj_properties:
+ print(_("Broken symlink or missing/bad install_name:"), \
+ install_name, '->', cachedRealpath, \
+ "with install_name", cachedInstallname, "but expecting", install_name)
+ # This conditional checks if there are no libraries to
+ # satisfy the install_name (empty set).
+ if not validLibrary:
+ for obj in objs:
+ rValue.setdefault(obj, set()).add(install_name)
+ # If no valid libraries have been found by this
+ # point, then the install_name does not exist in the
+ # filesystem, but if there are libraries (from the
+ # providers mapping), it is likely that soname
+ # symlinks or the actual libraries are missing or
+ # broken. Thus those libraries are added to rValue
+ # in order to emerge corrupt library packages.
+ for lib in libraries:
+ rValue.setdefault(lib, set()).add(install_name)
+ if debug:
+ if not os.path.isfile(lib):
+ writemsg_level(_("Missing library:") + " %s\n" % (lib,),
+ level=logging.DEBUG,
+ noiselevel=-1)
+ else:
+ writemsg_level(_("Possibly missing symlink:") + \
+ "%s\n" % (os.path.join(os.path.dirname(lib), soname)),
+ level=logging.DEBUG,
+ noiselevel=-1)
+ return rValue
+
+ def listProviders(self):
+ """
+ Find the providers for all object keys in LinkageMap.
+
+ @rtype: dict (example:
+ {(123L, 456L): {'libbar.dylib': set(['/lib/libbar.1.5.dylib'])}})
+ @return: The return value is an object -> providers mapping, where
+ providers is a mapping of install_name -> set-of-library-paths returned
+ from the findProviders method.
+
+ """
+ rValue = {}
+ if not self._libs:
+ self.rebuild()
+ # Iterate over all object keys within LinkageMap.
+ for obj_key in self._obj_properties:
+ rValue.setdefault(obj_key, self.findProviders(obj_key))
+ return rValue
+
+ def isMasterLink(self, obj):
+ """
+ Determine whether an object is a "master" symlink, which means
+ that its basename is the same as the beginning part of the
+ install_name and it lacks the install_name's version component.
+
+ Examples:
+
+ install_name | master symlink name
+ -----------------------------------------------
+ libarchive.2.8.4.dylib | libarchive.dylib
+ (typically the install_name is libarchive.2.dylib)
+
+ @param obj: absolute path to an object
+ @type obj: string (example: '/usr/bin/foo')
+ @rtype: Boolean
+ @return:
+ 1. True if obj is a master link
+ 2. False if obj is not a master link
+
+ """
+ os = _os_merge
+ obj_key = self._obj_key(obj)
+ if obj_key not in self._obj_properties:
+ raise KeyError("%s (%s) not in object list" % (obj_key, obj))
+ basename = os.path.basename(obj)
+ install_name = self._obj_properties[obj_key].install_name
+ return (len(basename) < len(os.path.basename(install_name)) and \
+ basename.endswith(".dylib") and \
+ os.path.basename(install_name).startswith(basename[:-6]))
+
+ def listLibraryObjects(self):
+ """
+ Return a list of library objects.
+
+ Known limitation: library objects lacking an soname are not included.
+
+ @rtype: list of strings
+ @return: list of paths to all providers
+
+ """
+ rValue = []
+ if not self._libs:
+ self.rebuild()
+ for arch_map in self._libs.values():
+ for soname_map in arch_map.values():
+ for obj_key in soname_map.providers:
+ rValue.extend(self._obj_properties[obj_key].alt_paths)
+ return rValue
+
+ def getOwners(self, obj):
+ """
+ Return the package(s) associated with an object. Raises KeyError
+ if the object is unknown. Returns an empty tuple if the owner(s)
+ are unknown.
+
+ NOTE: For preserved libraries, the owner(s) may have been
+ previously uninstalled, but these uninstalled owners can be
+ returned by this method since they are registered in the
+ PreservedLibsRegistry.
+
+ @param obj: absolute path to an object
+ @type obj: string (example: '/usr/bin/bar')
+ @rtype: tuple
+ @return: a tuple of cpv
+ """
+ if not self._libs:
+ self.rebuild()
+ if isinstance(obj, self._ObjectKey):
+ obj_key = obj
+ else:
+ obj_key = self._obj_key_cache.get(obj)
+ if obj_key is None:
+ raise KeyError("%s not in object list" % obj)
+ obj_props = self._obj_properties.get(obj_key)
+ if obj_props is None:
+ raise KeyError("%s not in object list" % obj_key)
+ if obj_props.owner is None:
+ return ()
+ return (obj_props.owner,)
+
+ def getSoname(self, obj):
+ """
+ Return the install_name associated with an object. To match
+ soname behaviour, the leading path is stripped.
+
+ @param obj: absolute path to an object
+ @type obj: string (example: '/usr/bin/bar')
+ @rtype: string
+ @return: install_name basename as a string
+
+ """
+ os = _os_merge
+ if not self._libs:
+ self.rebuild()
+ if isinstance(obj, self._ObjectKey):
+ obj_key = obj
+ if obj_key not in self._obj_properties:
+ raise KeyError("%s not in object list" % obj_key)
+ return os.path.basename(self._obj_properties[obj_key].install_name)
+ if obj not in self._obj_key_cache:
+ raise KeyError("%s not in object list" % obj)
+ return os.path.basename(
+ self._obj_properties[self._obj_key_cache[obj]].install_name)
+
+ def findProviders(self, obj):
+ """
+ Find providers for an object or object key.
+
+ This method may be called with a key from _obj_properties.
+
+ In some cases, not all valid libraries are returned. This may occur when
+ an soname symlink referencing a library is in an object's runpath while
+ the actual library is not. We should consider cataloging symlinks within
+ LinkageMap as this would avoid those cases and would be a better model of
+ library dependencies (since the dynamic linker actually searches for
+ files named with the soname in the runpaths).
+
+ @param obj: absolute path to an object or a key from _obj_properties
+ @type obj: string (example: '/usr/bin/bar') or _ObjectKey
+ @rtype: dict (example: {'libbar.dylib': set(['/lib/libbar.1.5.dylib'])})
+ @return: The return value is a install_name -> set-of-library-paths, where
+ set-of-library-paths satisfy install_name.
+
+ """
+
+ os = _os_merge
+
+ rValue = {}
+
+ if not self._libs:
+ self.rebuild()
+
+ # Determine the obj_key from the arguments.
+ if isinstance(obj, self._ObjectKey):
+ obj_key = obj
+ if obj_key not in self._obj_properties:
+ raise KeyError("%s not in object list" % obj_key)
+ else:
+ obj_key = self._obj_key(obj)
+ if obj_key not in self._obj_properties:
+ raise KeyError("%s (%s) not in object list" % (obj_key, obj))
+
+ obj_props = self._obj_properties[obj_key]
+ arch = obj_props.arch
+ needed = obj_props.needed
+ install_name = obj_props.install_name
+ for install_name in needed:
+ rValue[install_name] = set()
+ if arch not in self._libs or install_name not in self._libs[arch]:
+ continue
+ # For each potential provider of the install_name, add it to
+ # rValue if it exists. (Should be one)
+ for provider_key in self._libs[arch][install_name].providers:
+ providers = self._obj_properties[provider_key].alt_paths
+ for provider in providers:
+ if os.path.exists(provider):
+ rValue[install_name].add(provider)
+ return rValue
+
+ def findConsumers(self, obj, exclude_providers=None, greedy=True):
+ """
+ Find consumers of an object or object key.
+
+ This method may be called with a key from _obj_properties. If this
+ method is going to be called with an object key, to avoid not catching
+ shadowed libraries, do not pass new _ObjectKey instances to this method.
+ Instead pass the obj as a string.
+
+ In some cases, not all consumers are returned. This may occur when
+ an soname symlink referencing a library is in an object's runpath while
+ the actual library is not. For example, this problem is noticeable for
+ binutils since it's libraries are added to the path via symlinks that
+ are gemerated in the /usr/$CHOST/lib/ directory by binutils-config.
+ Failure to recognize consumers of these symlinks makes preserve-libs
+ fail to preserve binutils libs that are needed by these unrecognized
+ consumers.
+
+ Note that library consumption via dlopen (common for kde plugins) is
+ currently undetected. However, it is possible to use the
+ corresponding libtool archive (*.la) files to detect such consumers
+ (revdep-rebuild is able to detect them).
+
+ The exclude_providers argument is useful for determining whether
+ removal of one or more packages will create unsatisfied consumers. When
+ this option is given, consumers are excluded from the results if there
+ is an alternative provider (which is not excluded) of the required
+ soname such that the consumers will remain satisfied if the files
+ owned by exclude_providers are removed.
+
+ @param obj: absolute path to an object or a key from _obj_properties
+ @type obj: string (example: '/usr/bin/bar') or _ObjectKey
+ @param exclude_providers: A collection of callables that each take a
+ single argument referring to the path of a library (example:
+ '/usr/lib/libssl.0.9.8.dylib'), and return True if the library is
+ owned by a provider which is planned for removal.
+ @type exclude_providers: collection
+ @param greedy: If True, then include consumers that are satisfied
+ by alternative providers, otherwise omit them. Default is True.
+ @type greedy: Boolean
+ @rtype: set of strings (example: set(['/bin/foo', '/usr/bin/bar']))
+ @return: The return value is a install_name -> set-of-library-paths, where
+ set-of-library-paths satisfy install_name.
+
+ """
+
+ os = _os_merge
+
+ if not self._libs:
+ self.rebuild()
+
+ # Determine the obj_key and the set of objects matching the arguments.
+ if isinstance(obj, self._ObjectKey):
+ obj_key = obj
+ if obj_key not in self._obj_properties:
+ raise KeyError("%s not in object list" % obj_key)
+ objs = self._obj_properties[obj_key].alt_paths
+ else:
+ objs = set([obj])
+ obj_key = self._obj_key(obj)
+ if obj_key not in self._obj_properties:
+ raise KeyError("%s (%s) not in object list" % (obj_key, obj))
+
+ # If there is another version of this lib with the
+ # same install_name and the install_name symlink points to that
+ # other version, this lib will be shadowed and won't
+ # have any consumers.
+ if not isinstance(obj, self._ObjectKey):
+ install_name = self._obj_properties[obj_key].install_name
+ master_link = os.path.join(self._root,
+ install_name.lstrip(os.path.sep))
+ obj_path = os.path.join(self._root, obj.lstrip(os.sep))
+ try:
+ master_st = os.stat(master_link)
+ obj_st = os.stat(obj_path)
+ except OSError:
+ pass
+ else:
+ if (obj_st.st_dev, obj_st.st_ino) != \
+ (master_st.st_dev, master_st.st_ino):
+ return set()
+
+ obj_props = self._obj_properties[obj_key]
+ arch = obj_props.arch
+ install_name = obj_props.install_name
+
+ install_name_node = None
+ arch_map = self._libs.get(arch)
+ if arch_map is not None:
+ install_name_node = arch_map.get(install_name)
+
+ satisfied_consumer_keys = set()
+ if install_name_node is not None:
+ if exclude_providers is not None and not greedy:
+ relevant_dir_keys = set()
+ for provider_key in install_name_node.providers:
+ if not greedy and provider_key == obj_key:
+ continue
+ provider_objs = self._obj_properties[provider_key].alt_paths
+ for p in provider_objs:
+ provider_excluded = False
+ if exclude_providers is not None:
+ for excluded_provider_isowner in exclude_providers:
+ if excluded_provider_isowner(p):
+ provider_excluded = True
+ break
+ if not provider_excluded:
+ # This provider is not excluded. It will
+ # satisfy a consumer of this install_name.
+ relevant_dir_keys.add(self._path_key(p))
+
+ if relevant_dir_keys:
+ for consumer_key in install_name_node.consumers:
+ satisfied_consumer_keys.add(consumer_key)
+
+ rValue = set()
+ if install_name_node is not None:
+ # For each potential consumer, add it to rValue.
+ for consumer_key in install_name_node.consumers:
+ if consumer_key in satisfied_consumer_keys:
+ continue
+ consumer_props = self._obj_properties[consumer_key]
+ consumer_objs = consumer_props.alt_paths
+ rValue.update(consumer_objs)
+ return rValue
diff --git a/lib/portage/util/_dyn_libs/meson.build b/lib/portage/util/_dyn_libs/meson.build
index f744d2a08..705b51d22 100644
--- a/lib/portage/util/_dyn_libs/meson.build
+++ b/lib/portage/util/_dyn_libs/meson.build
@@ -1,6 +1,7 @@
py.install_sources(
[
'LinkageMapELF.py',
+ 'LinkageMapMachO.py', # PREFIX_LOCAL
'NeededEntry.py',
'PreservedLibsRegistry.py',
'display_preserved_libs.py',
diff --git a/lib/portage/util/_info_files.py b/lib/portage/util/_info_files.py
index 45d674b9b..a5b2b30b4 100644
--- a/lib/portage/util/_info_files.py
+++ b/lib/portage/util/_info_files.py
@@ -9,16 +9,20 @@ import subprocess
import portage
from portage import os
+# PREFIX LOCAL
+from portage.const import EPREFIX
def chk_updated_info_files(root, infodirs, prev_mtimes):
- if os.path.exists("/usr/bin/install-info"):
+ # PREFIX LOCAL
+ if os.path.exists(EPREFIX + "/usr/bin/install-info"):
out = portage.output.EOutput()
regen_infodirs = []
for z in infodirs:
if z == "":
continue
- inforoot = portage.util.normalize_path(root + z)
+ # PREFIX LOCAL
+ inforoot = portage.util.normalize_path(root + EPREFIX + z)
if os.path.isdir(inforoot) and not [
x for x in os.listdir(inforoot) if x.startswith(".keepinfodir")
]:
@@ -75,7 +79,8 @@ def chk_updated_info_files(root, infodirs, prev_mtimes):
try:
proc = subprocess.Popen(
[
- "/usr/bin/install-info",
+ # PREFIX LOCAL
+ f"{EPREFIX}/usr/bin/install-info",
f"--dir-file={os.path.join(inforoot, 'dir')}",
os.path.join(inforoot, x),
],
diff --git a/lib/portage/util/env_update.py b/lib/portage/util/env_update.py
index b19a85325..809712fa8 100644
--- a/lib/portage/util/env_update.py
+++ b/lib/portage/util/env_update.py
@@ -97,7 +97,8 @@ def _env_update(makelinks, target_root, prev_mtimes, contents, env, writemsg_lev
else:
settings = env
- eprefix = settings.get("EPREFIX", "")
+ # PREFIX LOCAL
+ eprefix = settings.get("EPREFIX", portage.const.EPREFIX)
eprefix_lstrip = eprefix.lstrip(os.sep)
eroot = (
normalize_path(os.path.join(target_root, eprefix_lstrip)).rstrip(os.sep)
diff --git a/lib/portage/util/hooks.py b/lib/portage/util/hooks.py
index cbb15f123..c0a37fff7 100644
--- a/lib/portage/util/hooks.py
+++ b/lib/portage/util/hooks.py
@@ -12,11 +12,15 @@ from portage.output import create_color_func
from portage.util import writemsg_level, _recursive_file_list
from warnings import warn
+# PREFIX LOCAL
+from portage.const import EPREFIX
+
bad = create_color_func("BAD")
warn = create_color_func("WARN")
-def get_hooks_from_dir(rel_directory, prefix="/"):
+# PREFIX LOCAL: prefix=EPREFIX
+def get_hooks_from_dir(rel_directory, prefix=EPREFIX):
directory = os.path.join(prefix, portage.USER_CONFIG_PATH, rel_directory)
hooks = OrderedDict()
@@ -39,7 +43,8 @@ def get_hooks_from_dir(rel_directory, prefix="/"):
return hooks
-def perform_hooks(rel_directory, *argv, prefix="/"):
+# PREFIX LOCAL: prefix=EPREFIX
+def perform_hooks(rel_directory, *argv, prefix=EPREFIX):
for filepath, name in get_hooks_from_dir(rel_directory, prefix).items():
hook_command = filepath + " " + " ".join(map(str, argv))
retval = portage.process.spawn(hook_command)
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644
index 000000000..4034c99ba
--- /dev/null
+++ b/man/Makefile.am
@@ -0,0 +1,17 @@
+SHELL = @PORTAGE_BASH@
+
+man_MANS = \
+ color.map.5 \
+ dispatch-conf.1 \
+ ebuild.1 \
+ ebuild.5 \
+ egencache.1 \
+ emaint.1 \
+ emerge.1 \
+ env-update.1 \
+ etc-update.1 \
+ make.conf.5 \
+ portage.5 \
+ quickpkg.1
+
+EXTRA_DIST = $(man_MANS)
diff --git a/man/ebuild.5 b/man/ebuild.5
index a32ba4828..687c4f9d8 100644
--- a/man/ebuild.5
+++ b/man/ebuild.5
@@ -884,6 +884,12 @@ characters.
This variable is intended to be used on files of binary packages which ignore
CFLAGS, CXXFLAGS, FFLAGS, FCFLAGS, and LDFLAGS variables.
.TP
+.B QA_IGNORE_INSTALL_NAME_FILES
+This should contain a list of file names (without path) that should be
+ignored in the install_name check. That is, if these files point to
+something not available in the image directory or live filesystem, these
+files are ignored, albeit being broken.
+.TP
.B QA_MULTILIB_PATHS
This should contain a list of file paths, relative to the image directory, of
files that should be ignored for the multilib\-strict checks.
diff --git a/man/emerge.1 b/man/emerge.1
index 43dc3f26b..609c9ac99 100644
--- a/man/emerge.1
+++ b/man/emerge.1
@@ -1445,6 +1445,12 @@ add this to \fBmake.conf\fR(5):
Tools such as dispatch\-conf, cfg\-update, and etc\-update are also available
to aid in the merging of these files. They provide interactive merging and can
auto\-merge trivial changes.
+.LP
+When an offset prefix (\fBEPREFIX\fR) is active, all paths in
+\fBCONFIG_PROTECT\fR and \fBCONFIG_PROTECT_MASK\fR are prefixed with the
+offset by Portage before they are considered. Hence, these paths never
+contain the offset prefix, and the variables can be defined in
+offset-unaware locations, such as the profiles.
.SH "REPORTING BUGS"
Please report any bugs you encounter through our website:
.LP
@@ -1464,6 +1470,7 @@ Marius Mauch <genone@gentoo.org>
Jason Stubbs <jstubbs@gentoo.org>
Brian Harring <ferringb@gmail.com>
Zac Medico <zmedico@gentoo.org>
+Fabian Groffen <grobian@gentoo.org>
Arfrever Frehtes Taifersar Arahesis <arfrever@apache.org>
.fi
.SH "FILES"
diff --git a/man/make.conf.5 b/man/make.conf.5
index e13f6eec4..cfd843455 100644
--- a/man/make.conf.5
+++ b/man/make.conf.5
@@ -266,6 +266,9 @@ Defaults to "/lib/modules/* *.py[co]".
All files and/or directories that are defined here will have "config file
protection" enabled for them. See the \fBCONFIGURATION FILES\fR section
of \fBemerge\fR(1) for more information.
+Note that if an offset prefix (\fBEPREFIX\fR) is activated, all paths defined
+in \fBCONFIG_PROTECT\fR are prefixed by Portage with the offset before
+they are used.
.TP
\fBCONFIG_PROTECT_MASK\fR = \fI[space delimited list of files and/or \
directories]\fR
@@ -715,6 +718,9 @@ dependencies.
.TP
.B sandbox
Enable sandbox\-ing when running \fBemerge\fR(1) and \fBebuild\fR(1).
+On Mac OS X platforms that have /usr/bin/sandbox-exec available (10.5
+and later), this particular sandbox implementation is used instead of
+sys-apps/sandbox.
.TP
.B sesandbox
Enable SELinux sandbox\-ing. Do not toggle this \fBFEATURE\fR yourself.
diff --git a/subst-install.in b/subst-install.in
new file mode 100644
index 000000000..07576eede
--- /dev/null
+++ b/subst-install.in
@@ -0,0 +1,87 @@
+#!@PORTAGE_BASH@
+
+# for expansion below we need some things to be defined
+prefix="@prefix@"
+exec_prefix="@exec_prefix@"
+
+# For bug #279550 we have to do some nasty trick to make sure that sed
+# doesn't strip the backslash in the replacement value (because it can
+# be a backreference) and hence escape those. Eventually in strings we
+# need to escape the backslash too, such that the single backslash
+# doesn't get lost when considered an invalid escape
+rootuser='@rootuser@'
+portagegroup='@portagegroup@'
+portageuser='@portageuser@'
+rootuser=${rootuser//\\/\\\\}
+portagegroup=${portagegroup//\\/\\\\\\\\}
+portageuser=${portageuser//\\/\\\\\\\\}
+
+# there are many ways to do this all dynamic, but we only care for raw
+# speed here, so let configure fill in this list and be done with it
+at='@'
+sedexp=(
+ -r
+ -e "s,${at}PORTAGE_BASE${at},@PORTAGE_BASE@,g"
+ -e "s,${at}PORTAGE_BASE_PATH${at},@PORTAGE_BASE@,g"
+ -e "s,${at}PORTAGE_BIN_PATH${at},@PORTAGE_BASE@/bin,g"
+ -e "s,${at}PORTAGE_BASH${at},@PORTAGE_BASH@,g"
+ -e "s,${at}PORTAGE_EPREFIX${at},@PORTAGE_EPREFIX@,g"
+ -e "s,${at}PORTAGE_MV${at},@PORTAGE_MV@,g"
+ -e "s,${at}PREFIX_PORTAGE_PYTHON${at},@PREFIX_PORTAGE_PYTHON@,g"
+ -e "s,${at}EPREFIX${at},@PORTAGE_EPREFIX@,g"
+ -e "s,${at}INSTALL_TYPE${at},SYSTEM,g"
+ -e "s,${at}datadir${at},@datadir@,g"
+ -e "s,${at}portagegroup${at},${portagegroup},g"
+ -e "s,${at}portageuser${at},${portageuser},g"
+ -e "s,${at}rootgid${at},@rootgid@,g"
+ -e "s,${at}rootuid${at},@rootuid@,g"
+ -e "s,${at}rootuser${at},${rootuser},g"
+ -e "s,${at}sysconfdir${at},@sysconfdir@,g"
+)
+
+if [[ $1 == --hprefixify ]] ; then
+ shift
+ dirs='/(usr|lib(|[onx]?32|n?64)|etc|bin|sbin|var|opt|run)'
+ sedexp+=(
+ -e 's,([^[:alnum:]}\)\.])'"${dirs}"',\1@PORTAGE_EPREFIX@/\2,g'
+ -e 's,^'"${dirs}"',@PORTAGE_EPREFIX@/\1,'
+ )
+fi
+
+sources=( )
+target=
+args=( "$@" )
+
+while [[ ${#@} != 0 ]] ; do
+ case "$1" in
+ -t)
+ [[ -n ${target} ]] && sources=( "${sources[@]}" "${target##*/}" )
+ shift
+ target=":${1}"
+ ;;
+ -*)
+ shift
+ ;;
+ *)
+ if [[ -z ${target} ]] ; then
+ target="${1}"
+ elif [[ ${target} != ":"* ]] ; then
+ sources=( "${sources[@]}" "${target##*/}" )
+ target="${1}"
+ else
+ sources=( "${sources[@]}" "${1##*/}" )
+ fi
+ ;;
+ esac
+ shift
+done
+
+target=${target#:}
+INSTALL="@INSTALL@"
+echo @INSTALL_DATA@ "${args[@]}"
+if [[ ! -d ${target} ]] ; then
+ # either install will die, or it was just a single file copy
+ @INSTALL_DATA@ "${args[@]}" && sed -i "${sedexp[@]}" "${target}"
+else
+ @INSTALL_DATA@ "${args[@]}" && sed -i "${sedexp[@]}" "${sources[@]/#/${target}/}"
+fi
diff --git a/tarball.sh b/tarball.sh
new file mode 100755
index 000000000..a8f52555d
--- /dev/null
+++ b/tarball.sh
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+
+if [ -z "$1" ]; then
+ echo
+ echo "You need to have the version specified."
+ echo "e.g.: $0 2.0.39-r37"
+ echo
+ exit 0
+fi
+
+export PKG="prefix-portage"
+export TMP="/var/tmp/${PKG}-build.$$"
+export V="$1"
+export DEST="${TMP}/${PKG}-${V}"
+export TARFILE="/var/tmp/${PKG}-${V}.tar.bz2"
+
+# hypothetically it can exist
+rm -Rf "${TMP}"
+
+# create copy of source
+install -d -m0755 "${DEST}"
+rsync -a --exclude='.git' --exclude='.hg' --exclude="repoman/" . "${DEST}"
+
+cd "${DEST}"
+
+# expand version
+sed -i -e '/^VERSION\s*=/s/^.*$/VERSION = "'${V}_prefix'"/' \
+ lib/portage/__init__.py
+sed -i -e "/\<version : /s/'[^']\+'/'${V}-prefix'/" meson.build
+sed -i -e "1s/VERSION/${V}-prefix/" man/{,ru/}*.[15]
+sed -i -e "s/@version@/${V}/" configure.ac
+
+# cleanup cruft
+find -name '*~' | xargs --no-run-if-empty rm -f
+find -name '*.#*' | xargs --no-run-if-empty rm -f
+find -name '*.pyc' | xargs --no-run-if-empty rm -f
+find -name '*.pyo' | xargs --no-run-if-empty rm -f
+find -name '*.orig' | xargs --no-run-if-empty rm -f
+rm -Rf autom4te.cache
+
+# we don't need these (why?)
+rm -f bin/emerge.py bin/{pmake,sandbox}
+
+# generate a configure file
+chmod a+x autogen.sh && ./autogen.sh || { echo "autogen failed!"; exit -1; };
+rm -f autogen.sh tabcheck.py tarball.sh commit
+
+# produce final tarball
+cd "${TMP}"
+tar --numeric-owner -jcf "${TARFILE}" ${PKG}-${V}
+
+cd /
+rm -Rf "${TMP}"
+ls -la "${TARFILE}"