12 , targetPkgs ? pkgs: []
13 , multiPkgs ? pkgs: []
14 , multiArch ? false # Whether to include 32bit packages
15 , includeClosures ? false # Whether to include closures of all packages
16 , nativeBuildInputs ? []
17 , extraBuildCommands ? ""
18 , extraBuildCommandsMulti ? ""
19 , extraOutputsToInstall ? []
20 , ... # for name, or pname+version
24 # All packages (most likely programs) returned from targetPkgs will only be
25 # installed once--matching the host's architecture (64bit on x86_64 and 32bit on
28 # Packages (most likely libraries) returned from multiPkgs are installed
29 # once on x86 systems and twice on x86_64 systems.
30 # On x86 they are merged with packages from targetPkgs.
31 # On x86_64 they are added to targetPkgs and in addition their 32bit
32 # versions are also installed. The final directory structure looks as
34 # /lib32 will include 32bit libraries from multiPkgs
35 # /lib64 will include 64bit libraries from multiPkgs and targetPkgs
36 # /lib will link to /lib32
39 inherit (stdenv.hostPlatform) is64bit;
41 name = if (args ? pname && args ? version)
42 then "${args.pname}-${args.version}"
45 # "use of glibc_multi is only supported on x86_64-linux"
46 isMultiBuild = multiArch && stdenv.system == "x86_64-linux";
47 isTargetBuild = !isMultiBuild;
49 # list of packages (usually programs) which match the host's architecture
50 # (which includes stuff from multiPkgs)
51 targetPaths = targetPkgs pkgs ++ (if multiPkgs == null then [] else multiPkgs pkgs);
53 # list of packages which are for x86 (only multiPkgs, only for x86_64 hosts)
54 multiPaths = multiPkgs pkgsi686Linux;
56 # base packages of the fhsenv
57 # these match the host's architecture, glibc_multi is used for multilib
58 # builds. glibcLocales must be before glibc or glibc_multi as otherwiese
59 # the wrong LOCALE_ARCHIVE will be used where only C.UTF-8 is available.
60 baseTargetPaths = with pkgs; [
62 (if isMultiBuild then glibc_multi else glibc)
79 baseMultiPaths = with pkgsi686Linux; [
83 ldconfig = writeShellScriptBin "ldconfig" ''
84 # due to a glibc bug, 64-bit ldconfig complains about patchelf'd 32-bit libraries, so we use 32-bit ldconfig when we have them
85 exec ${if isMultiBuild then pkgsi686Linux.glibc.bin else pkgs.glibc.bin}/bin/ldconfig -f /etc/ld.so.conf -C /etc/ld.so.cache "$@"
88 etcProfile = writeText "profile" ''
89 export PS1='${name}-fhsenv:\u@\h:\w\$ '
90 export LOCALE_ARCHIVE='/usr/lib/locale/locale-archive'
91 export PATH="/run/wrappers/bin:/usr/bin:/usr/sbin:$PATH"
92 export TZDIR='/etc/zoneinfo'
94 # XDG_DATA_DIRS is used by pressure-vessel (steam proton) and vulkan loaders to find the corresponding icd
95 export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}/run/opengl-driver/share:/run/opengl-driver-32/share
97 # Following XDG spec [1], XDG_DATA_DIRS should default to "/usr/local/share:/usr/share".
98 # In nix, it is commonly set without containing these values, so we add them as fallback.
100 # [1] <https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html>
101 case ":$XDG_DATA_DIRS:" in
102 *:/usr/local/share:*) ;;
103 *) export XDG_DATA_DIRS="$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}/usr/local/share" ;;
105 case ":$XDG_DATA_DIRS:" in
107 *) export XDG_DATA_DIRS="$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}/usr/share" ;;
110 # Force compilers and other tools to look in default search paths
111 unset NIX_ENFORCE_PURITY
112 export NIX_BINTOOLS_WRAPPER_TARGET_HOST_${stdenv.cc.suffixSalt}=1
113 export NIX_CC_WRAPPER_TARGET_HOST_${stdenv.cc.suffixSalt}=1
114 export NIX_CFLAGS_COMPILE='-idirafter /usr/include'
115 export NIX_CFLAGS_LINK='-L/usr/lib -L/usr/lib32'
116 export NIX_LDFLAGS='-L/usr/lib -L/usr/lib32'
117 export PKG_CONFIG_PATH=/usr/lib/pkgconfig
118 export ACLOCAL_PATH=/usr/share/aclocal
120 # GStreamer searches for plugins relative to its real binary's location
121 # https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/bd97973ce0f2c5495bcda5cccd4f7ef7dcb7febc
122 export GST_PLUGIN_SYSTEM_PATH_1_0=/usr/lib/gstreamer-1.0:/usr/lib32/gstreamer-1.0
127 # Compose /etc for the fhs environment
128 etcPkg = runCommandLocal "${name}-fhs-etc" { } ''
132 # environment variables
133 ln -s ${etcProfile} profile
135 # symlink /etc/mtab -> /proc/mounts (compat for old userspace progs)
136 ln -s /proc/mounts mtab
139 # Composes a /usr-like directory structure
140 staticUsrProfileTarget = buildEnv {
141 name = "${name}-usr-target";
142 # ldconfig wrapper must come first so it overrides the original ldconfig
143 paths = [ etcPkg ldconfig ] ++ baseTargetPaths ++ targetPaths;
144 extraOutputsToInstall = [ "out" "lib" "bin" ] ++ extraOutputsToInstall;
145 ignoreCollisions = true;
147 if [[ -d $out/share/gsettings-schemas/ ]]; then
148 # Recreate the standard schemas directory if its a symlink to make it writable
149 if [[ -L $out/share/glib-2.0 ]]; then
150 target=$(readlink $out/share/glib-2.0)
151 rm $out/share/glib-2.0
152 mkdir $out/share/glib-2.0
153 ln -fsr $target/* $out/share/glib-2.0
156 if [[ -L $out/share/glib-2.0/schemas ]]; then
157 target=$(readlink $out/share/glib-2.0/schemas)
158 rm $out/share/glib-2.0/schemas
159 mkdir $out/share/glib-2.0/schemas
160 ln -fsr $target/* $out/share/glib-2.0/schemas
163 mkdir -p $out/share/glib-2.0/schemas
165 for d in $out/share/gsettings-schemas/*; do
166 # Force symlink, in case there are duplicates
167 ln -fsr $d/glib-2.0/schemas/*.xml $out/share/glib-2.0/schemas
168 ln -fsr $d/glib-2.0/schemas/*.gschema.override $out/share/glib-2.0/schemas
172 ${pkgs.glib.dev}/bin/glib-compile-schemas $out/share/glib-2.0/schemas
175 inherit includeClosures;
178 staticUsrProfileMulti = buildEnv {
179 name = "${name}-usr-multi";
180 paths = baseMultiPaths ++ multiPaths;
181 extraOutputsToInstall = [ "out" "lib" ] ++ extraOutputsToInstall;
182 ignoreCollisions = true;
183 inherit includeClosures;
186 # setup library paths only for the targeted architecture
187 setupLibDirsTarget = ''
188 # link content of targetPaths
189 cp -rsHf ${staticUsrProfileTarget}/lib lib
190 ln -s lib lib${if is64bit then "64" else "32"}
193 # setup /lib, /lib32 and /lib64
194 setupLibDirsMulti = ''
200 cp -rsHf ${staticUsrProfileTarget}/lib/32/* lib32/
203 # copy content of multiPaths (32bit libs)
204 if [ -d ${staticUsrProfileMulti}/lib ]; then
205 cp -rsHf ${staticUsrProfileMulti}/lib/* lib32/
209 # copy content of targetPaths (64bit libs)
210 cp -rsHf ${staticUsrProfileTarget}/lib/* lib64/
213 # symlink 32-bit ld-linux.so
214 ln -Lsf ${staticUsrProfileTarget}/lib/32/ld-linux.so.2 lib/
217 setupLibDirs = if isTargetBuild
218 then setupLibDirsTarget
219 else setupLibDirsMulti;
221 # the target profile is the actual profile that will be used for the fhs
222 setupTargetProfile = ''
228 '' + lib.optionalString isMultiBuild ''
229 if [ -d "${staticUsrProfileMulti}/share" ]; then
230 cp -rLf ${staticUsrProfileMulti}/share share
233 if [ -d "${staticUsrProfileTarget}/share" ]; then
234 if [ -d share ]; then
236 cp -rLTf ${staticUsrProfileTarget}/share share
238 cp -rsHf ${staticUsrProfileTarget}/share share
241 for i in bin sbin include; do
242 if [ -d "${staticUsrProfileTarget}/$i" ]; then
243 cp -rsHf "${staticUsrProfileTarget}/$i" "$i"
249 if [ -d "${staticUsrProfileTarget}/$i" ]; then
250 cp -rsHf "${staticUsrProfileTarget}/$i" "$i"
253 for i in usr/{bin,sbin,lib,lib32,lib64}; do
262 in runCommandLocal "${name}-fhs" {
263 inherit nativeBuildInputs;
265 inherit args baseTargetPaths targetPaths baseMultiPaths ldconfig isMultiBuild;
271 ${setupTargetProfile}
272 ${extraBuildCommands}
273 ${lib.optionalString isMultiBuild extraBuildCommandsMulti}