summary refs log tree commit diff
path: root/pkgs/development/python-modules/generic/default.nix
blob: 4c9c53aab83424487a66f299b33536d6b876b978 (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
/* This function provides a generic Python package builder.  It is
   intended to work with packages that use `distutils/setuptools'
   (http://pypi.python.org/pypi/setuptools/), which represents a large
   number of Python packages nowadays.  */

{ python, setuptools, unzip, wrapPython, lib, recursivePthLoader, distutils-cfg }:

{ name

# by default prefix `name` e.g. "python3.3-${name}"
, namePrefix ? python.libPrefix + "-"

, buildInputs ? []

# pass extra information to the distutils global configuration (think as global setup.cfg)
, distutilsExtraCfg ? ""

# propagate build dependencies so in case we have A -> B -> C,
# C can import propagated packages by A
, propagatedBuildInputs ? []

# passed to "python setup.py install"
, setupPyInstallFlags ? []

# passed to "python setup.py build"
, setupPyBuildFlags ? []

# enable tests by default
, doCheck ? true

# List of packages that should be added to the PYTHONPATH
# environment variable in programs built by this function.  Packages
# in the standard `propagatedBuildInputs' variable are also added.
# The difference is that `pythonPath' is not propagated to the user
# environment.  This is preferrable for programs because it doesn't
# pollute the user environment.
, pythonPath ? []

# used to disable derivation, useful for specific python versions
, disabled ? false

, meta ? {}

# Execute before shell hook
, preShellHook ? ""

# Execute after shell hook
, postShellHook ? ""

, ... } @ attrs:


# Keep extra attributes from `attrs`, e.g., `patchPhase', etc.
if disabled then throw "${name} not supported for interpreter ${python.executable}" else python.stdenv.mkDerivation (attrs // {
  inherit doCheck;

  name = namePrefix + name;

  buildInputs = [
    python wrapPython setuptools
    (distutils-cfg.override { extraCfg = distutilsExtraCfg; })
  ] ++ buildInputs ++ pythonPath
    ++ (lib.optional (lib.hasSuffix "zip" attrs.src.name or "") unzip);

  propagatedBuildInputs = propagatedBuildInputs ++ [ recursivePthLoader ];

  pythonPath = [ setuptools ] ++ pythonPath;

  configurePhase = attrs.configurePhase or ''
    runHook preConfigure

    # patch python interpreter to write null timestamps when compiling python files
    # with following var we tell python to activate the patch so that python doesn't
    # try to update them when we freeze timestamps in nix store
    export DETERMINISTIC_BUILD=1

    # prepend following line to import setuptools before distutils
    # this way we make sure setuptools monkeypatches distutils commands
    # this way setuptools provides extra helpers such as "python setup.py test"
    sed -i '0,/import distutils/s//import setuptools;import distutils/' setup.py
    sed -i '0,/from distutils/s//import setuptools;from distutils/' setup.py

    runHook postConfigure
  '';

  checkPhase = attrs.checkPhase or ''
      runHook preCheck

      ${python}/bin/${python.executable} setup.py test

      runHook postCheck
  '';

  buildPhase = attrs.buildPhase or ''
    runHook preBuild

    ${python}/bin/${python.executable} setup.py build ${lib.concatStringsSep " " setupPyBuildFlags}

    runHook postBuild
  '';

  installPhase = attrs.installPhase or ''
    runHook preInstall

    mkdir -p "$out/lib/${python.libPrefix}/site-packages"

    export PYTHONPATH="$out/lib/${python.libPrefix}/site-packages:$PYTHONPATH"

    ${python}/bin/${python.executable} setup.py install \
      --install-lib=$out/lib/${python.libPrefix}/site-packages \
      --old-and-unmanageable \
      --prefix="$out" ${lib.concatStringsSep " " setupPyInstallFlags}

    # --install-lib:
    # sometimes packages specify where files should be installed outside the usual
    # python lib prefix, we override that back so all infrastructure (setup hooks)
    # work as expected

    # --old-and-unmanagable:
    # instruct setuptools not to use eggs but fallback to plan package install 
    # this also reduces one .pth file in the chain, but the main reason is to
    # force install process to install only scripts for the package we are
    # installing (otherwise it will install scripts also for dependencies)

    # A pth file might have been generated to load the package from
    # within its own site-packages, rename this package not to
    # collide with others.
    eapth="$out/lib/${python.libPrefix}"/site-packages/easy-install.pth
    if [ -e "$eapth" ]; then
        # move colliding easy_install.pth to specifically named one
        mv "$eapth" $(dirname "$eapth")/${name}.pth
    fi

    # Remove any site.py files generated by easy_install as these
    # cause collisions. If pth files are to be processed a
    # corresponding site.py needs to be included in the PYTHONPATH.
    rm -f "$out/lib/${python.libPrefix}"/site-packages/site.py*

    runHook postInstall
  '';

  postFixup = attrs.postFixup or ''
      wrapPythonPrograms

      # If a user installs a Python package, they probably also wants its
      # dependencies in the user environment profile (only way to find the
      # dependencies is to have them in the PYTHONPATH variable).
      # Allows you to do: $ PYTHONPATH=~/.nix-profile/lib/python2.7/site-packages python
      if test -e $out/nix-support/propagated-build-inputs; then
          ln -s $out/nix-support/propagated-build-inputs $out/nix-support/propagated-user-env-packages
      fi

      # TODO: document
      createBuildInputsPth build-inputs "$buildInputStrings"
      for inputsfile in propagated-build-inputs propagated-native-build-inputs; do
        if test -e $out/nix-support/$inputsfile; then
            createBuildInputsPth $inputsfile "$(cat $out/nix-support/$inputsfile)"
        fi
      done
    '';

  shellHook = attrs.shellHook or ''
    if test -e setup.py; then
       mkdir -p /tmp/$name/lib/${python.libPrefix}/site-packages
       ${preShellHook}
       export PATH="/tmp/$name/bin:$PATH"
       export PYTHONPATH="/tmp/$name/lib/${python.libPrefix}/site-packages:$PYTHONPATH"
       ${python}/bin/${python.executable} setup.py develop --prefix /tmp/$name
       ${postShellHook}
    fi
  '';

  meta = with lib.maintainers; {
    # default to python's platforms
    platforms = python.meta.platforms;
  } // meta // {
    # add extra maintainer(s) to every package
    maintainers = (meta.maintainers or []) ++ [ chaoflow iElectric ];
  };

})