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 /lib64
39 name = if (args ? pname && args ? version)
40 then "${args.pname}-${args.version}"
43 # "use of glibc_multi is only supported on x86_64-linux"
44 isMultiBuild = multiArch && stdenv.system == "x86_64-linux";
46 # list of packages (usually programs) which match the host's architecture
47 # (which includes stuff from multiPkgs)
48 targetPaths = targetPkgs pkgs ++ (if multiPkgs == null then [] else multiPkgs pkgs);
50 # list of packages which are for x86 (only multiPkgs, only for x86_64 hosts)
51 multiPaths = multiPkgs pkgsi686Linux;
53 # base packages of the fhsenv
54 # these match the host's architecture, glibc_multi is used for multilib
55 # builds. glibcLocales must be before glibc or glibc_multi as otherwiese
56 # the wrong LOCALE_ARCHIVE will be used where only C.UTF-8 is available.
57 baseTargetPaths = with pkgs; [
59 (if isMultiBuild then glibc_multi else glibc)
76 baseMultiPaths = with pkgsi686Linux; [
80 ldconfig = writeShellScriptBin "ldconfig" ''
81 # 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
82 exec ${if isMultiBuild then pkgsi686Linux.glibc.bin else pkgs.glibc.bin}/bin/ldconfig -f /etc/ld.so.conf -C /etc/ld.so.cache "$@"
85 etcProfile = pkgs.writeTextFile {
86 name = "${name}-fhsenv-profile";
87 destination = "/etc/profile";
89 export PS1='${name}-fhsenv:\u@\h:\w\$ '
90 export LOCALE_ARCHIVE="''${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/latest>
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
128 ensureGsettingsSchemasIsDirectory = runCommandLocal "fhsenv-ensure-gsettings-schemas-directory" {} ''
129 mkdir -p $out/share/glib-2.0/schemas
130 touch $out/share/glib-2.0/schemas/.keep
133 # Shamelessly stolen (and cleaned up) from original buildEnv.
134 # Should be semantically equivalent, except we also take
135 # a list of default extra outputs that will be installed
136 # for derivations that don't explicitly specify one.
137 # Note that this is not the same as `extraOutputsToInstall`,
138 # as that will apply even to derivations with an output
139 # explicitly specified, so this does change the behavior
140 # very slightly for that particular edge case.
142 pickOutputsOne = outputs: drv:
144 isSpecifiedOutput = drv.outputSpecified or false;
145 outputsToInstall = drv.meta.outputsToInstall or null;
146 pickedOutputs = if isSpecifiedOutput || outputsToInstall == null
148 else map (out: drv.${out} or null) (outputsToInstall ++ outputs);
149 extraOutputs = map (out: drv.${out} or null) extraOutputsToInstall;
150 cleanOutputs = lib.filter (o: o != null) (pickedOutputs ++ extraOutputs);
152 paths = cleanOutputs;
153 priority = drv.meta.priority or lib.meta.defaultPriority;
155 in paths: outputs: map (pickOutputsOne outputs) paths;
160 # ldconfig wrapper must come first so it overrides the original ldconfig
162 # magic package that just creates a directory, to ensure that
163 # the entire directory can't be a symlink, as we will write
164 # compiled schemas to it
165 ensureGsettingsSchemasIsDirectory
166 ] ++ baseTargetPaths ++ targetPaths;
167 in pickOutputs basePaths ["out" "lib" "bin"];
169 paths32 = lib.optionals isMultiBuild (
171 basePaths = baseMultiPaths ++ multiPaths;
172 in pickOutputs basePaths ["out" "lib"]
175 allPaths = paths ++ paths32;
177 rootfs-builder = pkgs.rustPlatform.buildRustPackage {
178 name = "fhs-rootfs-bulder";
179 src = ./rootfs-builder;
180 cargoLock.lockFile = ./rootfs-builder/Cargo.lock;
184 rootfs = pkgs.runCommand "${name}-fhsenv-rootfs" {
185 __structuredAttrs = true;
186 exportReferencesGraph.graph = lib.concatMap (p: p.paths) allPaths;
187 inherit paths paths32 isMultiBuild includeClosures;
188 nativeBuildInputs = [ pkgs.jq ];
190 ${rootfs-builder}/bin/rootfs-builder
192 # create a bunch of symlinks for usrmerge
193 ln -s /usr/bin $out/bin
194 ln -s /usr/sbin $out/sbin
195 ln -s /usr/lib $out/lib
196 ln -s /usr/lib32 $out/lib32
197 ln -s /usr/lib64 $out/lib64
198 ln -s /usr/lib64 $out/usr/lib
200 # symlink 32-bit ld-linux so it's visible in /lib
201 if [ -e $out/usr/lib32/ld-linux.so.2 ]; then
202 ln -s /usr/lib32/ld-linux.so.2 $out/usr/lib64/ld-linux.so.2
205 # symlink /etc/mtab -> /proc/mounts (compat for old userspace progs)
206 ln -s /proc/mounts $out/etc/mtab
208 if [[ -d $out/usr/share/gsettings-schemas/ ]]; then
209 for d in $out/usr/share/gsettings-schemas/*; do
210 # Force symlink, in case there are duplicates
211 ln -fsr $d/glib-2.0/schemas/*.xml $out/usr/share/glib-2.0/schemas
212 ln -fsr $d/glib-2.0/schemas/*.gschema.override $out/usr/share/glib-2.0/schemas
214 ${pkgs.glib.dev}/bin/glib-compile-schemas $out/usr/share/glib-2.0/schemas
217 ${extraBuildCommands}
218 ${lib.optionalString isMultiBuild extraBuildCommandsMulti}