summary refs log tree commit diff
path: root/pkgs/build-support/build-fhs-chrootenv/env.nix
blob: d80e2869e5e6ac899610eb817b02704dc89f74e0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
{ nixpkgs, nixpkgs_i686, system
} :
{ name, pkgs ? [], profile ? ""
, targetPkgs ? null, multiPkgs ? null
, extraBuildCommands ? "", extraBuildCommandsMulti ? ""
}:

assert pkgs       != []   -> targetPkgs == null && multiPkgs == null;
assert targetPkgs != null -> multiPkgs  != null;
assert multiPkgs  != null -> targetPkgs != null;
assert targetPkgs != null -> pkgs       == [];


# HOWTO:
# If pkgs is defined buildFHSEnv will run in legacy mode. This means
# it will build all pkgs contained in pkgs and basePkgs and then just merge
# all of their contents together via buildEnv.
#
# The new way is to define both targetPkgs and multiPkgs. These two are
# functions which get a pkgs environment supplied and should then return a list
# of packages based this environment.
# For example: targetPkgs = pkgs: [ pkgs.nmap ];
#
# All packages (most likely programs) placed in targetPkgs will only be
# installed once--matching the hosts architecture (64bit on x86_64 and 32bit on
# x86). These packages will populate the chroot directory tree.
#
# Packages (most likeley libraries) defined in multiPkgs will be installed once
# on x86 systems and twice on x86_64 systems.
# On x86 they will just be merge with the packages defined in targetPkgs.
# On x86_64 they will be added to targetPkgs and in addition their 32bit
# versions will also be installed. The final directory should look as follows:
# /lib will include 32bit libraries from multiPkgs
# /lib32 will link to /lib
# /lib64 will include 64bit libraries from multiPkgs and targetPkgs
# /x86 will contain a complete 32bit environment composed by multiPkgs

let
  is64Bit       = system == "x86_64-linux";
  # enable multi builds on x86_64 hosts if pakgs_target/multi are defined
  isMultiBuild  = is64Bit && targetPkgs != null;
  isTargetBuild = !isMultiBuild;

  # list of packages (usually programs) which will only be installed for the
  # hosts architecture
  targetPaths = if targetPkgs == null
                  then pkgs
                  else targetPkgs nixpkgs ++ multiPkgs nixpkgs;

  # list of pckages which should be build for both x86 and x86_64 on x86_64
  # systems
  multiPaths = if isMultiBuild
                  then multiPkgs nixpkgs_i686
                  else [];

  # base packages of the chroot
  # these match the hosts architecture, gcc/glibc_multi will be choosen
  # on multi builds
  chosenGcc = if isMultiBuild then nixpkgs.gcc_multi else nixpkgs.gcc;
  basePkgs = with nixpkgs;
    [ (if isMultiBuild then glibc_multi else glibc)
      chosenGcc
      bashInteractive coreutils less shadow su
      gawk diffutils findutils gnused gnugrep
      gnutar gzip bzip2 xz glibcLocales
    ];

  # Compose a global profile for the chroot environment
  profilePkg = nixpkgs.stdenv.mkDerivation {
    name         = "${name}-chrootenv-profile";
    buildCommand = ''
      mkdir -p $out/etc
      cat >> $out/etc/profile << "EOF"
      export PS1='${name}-chrootenv:\u@\h:\w\$ '
      export LOCALE_ARCHIVE='/usr/lib${if is64Bit then "64" else ""}/locale/locale-archive'
      export LD_LIBRARY_PATH=/run/opengl-driver/lib:/run/opengl-driver-32/lib:/lib:/lib32:/lib64
      export PATH='/bin:/sbin'
      ${profile}
      EOF
    '';
  };

  # Composes a /usr like directory structure
  staticUsrProfileTarget = nixpkgs.buildEnv {
    name = "system-profile-target";
    paths = basePkgs ++ [ profilePkg ] ++ targetPaths;
    ignoreCollisions = true;
  };

  staticUsrProfileMulti = nixpkgs.buildEnv {
    name = "system-profile-multi";
    paths = multiPaths;
    ignoreCollisions = true;
  };

  linkProfile = profile: ''
    for i in ${profile}/{bin,sbin,share,var}; do
        if [ -x "$i" ]
        then
            ln -s "$i"
        fi
    done
  '';

  # the target profile is the actual profile that will be used for the chroot
  setupTargetProfile = ''
    ${linkProfile staticUsrProfileTarget}
    ${setupLibDirs}

    mkdir -m0755 usr
    cd usr
    ${linkProfile staticUsrProfileTarget}
    ${setupLibDirs}
    cd ..
  '';

  # this will happen on x86_64 host:
  # /x86         -> links to the whole profile defined by multiPaths
  # /lib, /lib32 -> links to 32bit binaries
  # /lib64       -> links to 64bit binaries
  # /usr/lib*    -> same as above
  setupMultiProfile = if isTargetBuild then "" else ''
    mkdir -m0755 x86
    cd x86
    ${linkProfile staticUsrProfileMulti}
    cd ..
  '';

  setupLibDirs = if isTargetBuild then setupLibDirs_target
                                  else setupLibDirs_multi;

  # setup library paths only for the targeted architecture
  setupLibDirs_target = ''
    mkdir -m0755 lib

    # copy content of targetPaths
    cp -rsf ${staticUsrProfileTarget}/lib/* lib/ && chmod u+w -R lib/
  '';

  # setup /lib, /lib32 and /lib64
  setupLibDirs_multi = ''
    mkdir -m0755 lib
    mkdir -m0755 lib64
    ln -s lib lib32

    # copy glibc stuff
    cp -rsf ${staticUsrProfileTarget}/lib/32/* lib/

    # copy content of multiPaths (32bit libs)
    [ -d ${staticUsrProfileMulti}/lib ] && cp -rsf ${staticUsrProfileMulti}/lib/* lib/ && chmod u+w -R lib/

    # copy content of targetPaths (64bit libs)
    cp -rsf ${staticUsrProfileTarget}/lib/* lib64/ && chmod u+w -R lib64/

    # most 64bit only libs put their stuff into /lib
    # some pkgs (like gcc_multi) put 32bit libs into and /lib 64bit libs into /lib64
    # by overwriting these we will hopefully catch all these cases
    # in the end /lib should only contain 32bit and /lib64 only 64bit libs
    cp -rsf ${staticUsrProfileTarget}/lib64/* lib64/ && chmod u+w -R lib64/

    # copy gcc libs (and may overwrite exitsting wrongly placed libs)
    cp -rsf ${chosenGcc.cc}/lib/*   lib/
    cp -rsf ${chosenGcc.cc}/lib64/* lib64/
  '';

  setupEtc = ''
    mkdir -m0755 etc

    # copy profile content
    cp -rsf ${staticUsrProfileTarget}/etc/* etc/ && chmod u+w -R etc/
    [ -d ${staticUsrProfileMulti}/etc ] && cp -rsf ${staticUsrProfileMulti}/etc/* etc/ && chmod u+w -R etc/

    # compatibility with NixOS
    ln -s /host-etc/static etc/static

    # symlink some NSS stuff
    ln -s /host-etc/passwd etc/passwd
    ln -s /host-etc/group etc/group
    ln -s /host-etc/shadow etc/shadow
    ln -s /host-etc/hosts etc/hosts
    ln -s /host-etc/resolv.conf etc/resolv.conf
    ln -s /host-etc/nsswitch.conf etc/nsswitch.conf

    # symlink other core stuff
    ln -s /host-etc/localtime etc/localtime
    ln -s /host-etc/machine-id etc/machine-id

    # symlink PAM stuff
    rm -rf etc/pam.d
    ln -s /host-etc/pam.d etc/pam.d

    # symlink fonts stuff
    rm -rf etc/fonts
    ln -s /host-etc/fonts etc/fonts

    # symlink ALSA stuff
    ln -s /host-etc/asound.conf etc/asound.conf
  '';

in nixpkgs.stdenv.mkDerivation {
  name         = "${name}-fhs";
  buildCommand = ''
    mkdir -p $out
    cd $out
    ${setupTargetProfile}
    ${setupMultiProfile}
    ${setupEtc}
    cd $out
    ${extraBuildCommands}
    cd $out
    ${if isMultiBuild then extraBuildCommandsMulti else ""}
  '';
  preferLocalBuild = true;
  passthru = {
    pname = name;
  };
}