nixpkgs/pkgs/build-support/build-fhsenv-bubblewrap/buildFHSEnv.nix
Alyssa Ross 00a28d0ed9
buildFHSEnv: add base paths to passthru
It's useful to be able to introspect all packages which are available
in the fhsenv.  I've renamed basePkgs and baseMultiPkgs to be
consistent with the naming scheme used for the bits that were
previously public — names ending in "Pkgs" are for functions, and
names ending in "Paths" are the results of those functions.
2023-09-24 08:43:20 +00:00

263 lines
8.3 KiB
Nix

{ lib
, stdenv
, runCommandLocal
, buildEnv
, writeText
, writeShellScriptBin
, pkgs
, pkgsi686Linux
}:
{ name ? null
, profile ? ""
, targetPkgs ? pkgs: []
, multiPkgs ? pkgs: []
, multiArch ? false # Whether to include 32bit packages
, extraBuildCommands ? ""
, extraBuildCommandsMulti ? ""
, extraOutputsToInstall ? []
} @ args:
# HOWTO:
# All packages (most likely programs) returned from targetPkgs will only be
# installed once--matching the host's architecture (64bit on x86_64 and 32bit on
# x86).
#
# Packages (most likely libraries) returned from multiPkgs are installed
# once on x86 systems and twice on x86_64 systems.
# On x86 they are merged with packages from targetPkgs.
# On x86_64 they are added to targetPkgs and in addition their 32bit
# versions are also installed. The final directory structure looks as
# follows:
# /lib32 will include 32bit libraries from multiPkgs
# /lib64 will include 64bit libraries from multiPkgs and targetPkgs
# /lib will link to /lib32
let
inherit (stdenv) is64bit;
# "use of glibc_multi is only supported on x86_64-linux"
isMultiBuild = multiArch && stdenv.system == "x86_64-linux";
isTargetBuild = !isMultiBuild;
# list of packages (usually programs) which are only be installed for the
# host's architecture
targetPaths = targetPkgs pkgs ++ (if multiPkgs == null then [] else multiPkgs pkgs);
# list of packages which are installed for both x86 and x86_64 on x86_64
# systems
multiPaths = multiPkgs pkgsi686Linux;
# base packages of the chroot
# these match the host's architecture, glibc_multi is used for multilib
# builds. glibcLocales must be before glibc or glibc_multi as otherwiese
# the wrong LOCALE_ARCHIVE will be used where only C.UTF-8 is available.
baseTargetPaths = with pkgs; [
glibcLocales
(if isMultiBuild then glibc_multi else glibc)
(toString gcc.cc.lib)
bashInteractiveFHS
coreutils
less
shadow
su
gawk
diffutils
findutils
gnused
gnugrep
gnutar
gzip
bzip2
xz
];
baseMultiPaths = with pkgsi686Linux; [
(toString gcc.cc.lib)
];
ldconfig = writeShellScriptBin "ldconfig" ''
# due to a glibc bug, 64-bit ldconfig complains about patchelf'd 32-bit libraries, so we're using 32-bit ldconfig
exec ${if stdenv.system == "x86_64-linux" then pkgsi686Linux.glibc.bin else pkgs.glibc.bin}/bin/ldconfig -f /etc/ld.so.conf -C /etc/ld.so.cache "$@"
'';
etcProfile = writeText "profile" ''
export PS1='${name}-chrootenv:\u@\h:\w\$ '
export LOCALE_ARCHIVE='/usr/lib/locale/locale-archive'
export LD_LIBRARY_PATH="/run/opengl-driver/lib:/run/opengl-driver-32/lib:/usr/lib:/usr/lib32''${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH"
export PATH="/run/wrappers/bin:/usr/bin:/usr/sbin:$PATH"
export TZDIR='/etc/zoneinfo'
# XDG_DATA_DIRS is used by pressure-vessel (steam proton) and vulkan loaders to find the corresponding icd
export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}/run/opengl-driver/share:/run/opengl-driver-32/share
# Following XDG spec [1], XDG_DATA_DIRS should default to "/usr/local/share:/usr/share".
# In nix, it is commonly set without containing these values, so we add them as fallback.
#
# [1] <https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html>
case ":$XDG_DATA_DIRS:" in
*:/usr/local/share:*) ;;
*) export XDG_DATA_DIRS="$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}/usr/local/share" ;;
esac
case ":$XDG_DATA_DIRS:" in
*:/usr/share:*) ;;
*) export XDG_DATA_DIRS="$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}/usr/share" ;;
esac
# Force compilers and other tools to look in default search paths
unset NIX_ENFORCE_PURITY
export NIX_BINTOOLS_WRAPPER_TARGET_HOST_${stdenv.cc.suffixSalt}=1
export NIX_CC_WRAPPER_TARGET_HOST_${stdenv.cc.suffixSalt}=1
export NIX_CFLAGS_COMPILE='-idirafter /usr/include'
export NIX_CFLAGS_LINK='-L/usr/lib -L/usr/lib32'
export NIX_LDFLAGS='-L/usr/lib -L/usr/lib32'
export PKG_CONFIG_PATH=/usr/lib/pkgconfig
export ACLOCAL_PATH=/usr/share/aclocal
${profile}
'';
# Compose /etc for the chroot environment
etcPkg = runCommandLocal "${name}-chrootenv-etc" { } ''
mkdir -p $out/etc
pushd $out/etc
# environment variables
ln -s ${etcProfile} profile
# symlink /etc/mtab -> /proc/mounts (compat for old userspace progs)
ln -s /proc/mounts mtab
'';
# Composes a /usr-like directory structure
staticUsrProfileTarget = buildEnv {
name = "${name}-usr-target";
# ldconfig wrapper must come first so it overrides the original ldconfig
paths = [ etcPkg ldconfig ] ++ baseTargetPaths ++ targetPaths;
extraOutputsToInstall = [ "out" "lib" "bin" ] ++ extraOutputsToInstall;
ignoreCollisions = true;
postBuild = ''
if [[ -d $out/share/gsettings-schemas/ ]]; then
# Recreate the standard schemas directory if its a symlink to make it writable
if [[ -L $out/share/glib-2.0 ]]; then
target=$(readlink $out/share/glib-2.0)
rm $out/share/glib-2.0
mkdir $out/share/glib-2.0
ln -fs $target/* $out/share/glib-2.0
fi
if [[ -L $out/share/glib-2.0/schemas ]]; then
target=$(readlink $out/share/glib-2.0/schemas)
rm $out/share/glib-2.0/schemas
mkdir $out/share/glib-2.0/schemas
ln -fs $target/* $out/share/glib-2.0/schemas
fi
mkdir -p $out/share/glib-2.0/schemas
for d in $out/share/gsettings-schemas/*; do
# Force symlink, in case there are duplicates
ln -fs $d/glib-2.0/schemas/*.xml $out/share/glib-2.0/schemas
ln -fs $d/glib-2.0/schemas/*.gschema.override $out/share/glib-2.0/schemas
done
# and compile them
${pkgs.glib.dev}/bin/glib-compile-schemas $out/share/glib-2.0/schemas
fi
'';
};
staticUsrProfileMulti = buildEnv {
name = "${name}-usr-multi";
paths = baseMultiPaths ++ multiPaths;
extraOutputsToInstall = [ "out" "lib" ] ++ extraOutputsToInstall;
ignoreCollisions = true;
};
# setup library paths only for the targeted architecture
setupLibDirsTarget = ''
# link content of targetPaths
cp -rsHf ${staticUsrProfileTarget}/lib lib
ln -s lib lib${if is64bit then "64" else "32"}
'';
# setup /lib, /lib32 and /lib64
setupLibDirsMulti = ''
mkdir -m0755 lib32
mkdir -m0755 lib64
ln -s lib64 lib
# copy glibc stuff
cp -rsHf ${staticUsrProfileTarget}/lib/32/* lib32/
chmod u+w -R lib32/
# copy content of multiPaths (32bit libs)
if [ -d ${staticUsrProfileMulti}/lib ]; then
cp -rsHf ${staticUsrProfileMulti}/lib/* lib32/
chmod u+w -R lib32/
fi
# copy content of targetPaths (64bit libs)
cp -rsHf ${staticUsrProfileTarget}/lib/* lib64/
chmod u+w -R lib64/
# symlink 32-bit ld-linux.so
ln -Ls ${staticUsrProfileTarget}/lib/32/ld-linux.so.2 lib/
'';
setupLibDirs = if isTargetBuild
then setupLibDirsTarget
else setupLibDirsMulti;
# the target profile is the actual profile that will be used for the chroot
setupTargetProfile = ''
mkdir -m0755 usr
pushd usr
${setupLibDirs}
'' + lib.optionalString isMultiBuild ''
if [ -d "${staticUsrProfileMulti}/share" ]; then
cp -rLf ${staticUsrProfileMulti}/share share
fi
'' + ''
if [ -d "${staticUsrProfileTarget}/share" ]; then
if [ -d share ]; then
chmod -R 755 share
cp -rLTf ${staticUsrProfileTarget}/share share
else
cp -rsHf ${staticUsrProfileTarget}/share share
fi
fi
for i in bin sbin include; do
if [ -d "${staticUsrProfileTarget}/$i" ]; then
cp -rsHf "${staticUsrProfileTarget}/$i" "$i"
fi
done
cd ..
for i in var etc opt; do
if [ -d "${staticUsrProfileTarget}/$i" ]; then
cp -rsHf "${staticUsrProfileTarget}/$i" "$i"
fi
done
for i in usr/{bin,sbin,lib,lib32,lib64}; do
if [ -d "$i" ]; then
ln -s "$i"
fi
done
popd
'';
in runCommandLocal "${name}-fhs" {
passthru = {
inherit args baseTargetPaths targetPaths baseMultiPaths multiPaths ldconfig;
};
} ''
mkdir -p $out
pushd $out
${setupTargetProfile}
${extraBuildCommands}
${lib.optionalString isMultiBuild extraBuildCommandsMulti}
''