summary refs log tree commit diff
path: root/pkgs/development/interpreters/python/wrap.sh
blob: f10ba003432bef8ef95520aa742ec957c5fba1a1 (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
# Wrapper around wrapPythonProgramsIn, below. The $pythonPath
# variable is passed in from the buildPythonPackage function.
wrapPythonPrograms() {
    wrapPythonProgramsIn "$out/bin" "$out $pythonPath"
}

# Builds environment variables like PYTHONPATH and PATH walking through closure
# of dependencies.
buildPythonPath() {
    local pythonPath="$1"
    local path

    # Create an empty table of python paths (see doc on _addToPythonPath
    # for how this is used). Build up the program_PATH and program_PYTHONPATH
    # variables.
    declare -A pythonPathsSeen=()
    program_PYTHONPATH=
    program_PATH=
    pythonPathsSeen["@pythonHost@"]=1
    addToSearchPath program_PATH @pythonHost@/bin
    for path in $pythonPath; do
        _addToPythonPath $path
    done
}

# Patches a Python script so that it has correct libraries path and executable
# name.
patchPythonScript() {
    local f="$1"

    # The magicalSedExpression will invoke a "$(basename "$f")", so
    # if you change $f to something else, be sure to also change it
    # in pkgs/top-level/python-packages.nix!
    # It also uses $program_PYTHONPATH.
    sed -i "$f" -re '@magicalSedExpression@'
}

# Transforms any binaries generated by the setup.py script, replacing them
# with an executable shell script which will set some environment variables
# and then call into the original binary (which has been given a .wrapped
# suffix).
wrapPythonProgramsIn() {
    local dir="$1"
    local pythonPath="$2"
    local f

    buildPythonPath "$pythonPath"

    # Find all regular files in the output directory that are executable.
    if [ -d "$dir" ]; then
        find "$dir" -type f -perm -0100 -print0 | while read -d "" f; do
            # Rewrite "#! .../env python" to "#! /nix/store/.../python".
            # Strip suffix, like "3" or "2.7m" -- we don't have any choice on which
            # Python to use besides one with this hook anyway.
            if head -n1 "$f" | grep -q '#!.*/env.*\(python\|pypy\)'; then
                sed -i "$f" -e "1 s^.*/env[ ]*\(python\|pypy\)[^ ]*^#!@executable@^"
            fi

            if head -n1 "$f" | grep -q '#!.*'; then
                # Cross-compilation hack: ensure shebangs are for the host
                echo "Rewriting $(head -n 1 $f) to #!@pythonHost@"
                sed -i "$f" -e "1 s^#!@python@^#!@pythonHost@^"
            fi

            # catch /python and /.python-wrapped
            if head -n1 "$f" | grep -q '/\.\?\(python\|pypy\)'; then
                # dont wrap EGG-INFO scripts since they are called from python
                if echo "$f" | grep -qv EGG-INFO/scripts; then
                    echo "wrapping \`$f'..."
                    patchPythonScript "$f"
                    # wrapProgram creates the executable shell script described
                    # above. The script will set PYTHONPATH and PATH variables.!
                    # (see pkgs/build-support/setup-hooks/make-wrapper.sh)
                    local -a wrap_args=("$f"
                                    --prefix PATH ':' "$program_PATH"
                                    )

                    if [ -z "$permitUserSite" ]; then
                        wrap_args+=(--set PYTHONNOUSERSITE "true")
                    fi

                    # Add any additional arguments provided by makeWrapperArgs
                    # argument to buildPythonPackage.
                    # We need to support both the case when makeWrapperArgs
                    # is an array and a IFS-separated string.
                    # TODO: remove the string branch when __structuredAttrs are used.
                    if [[ "${makeWrapperArgs+defined}" == "defined" && "$(declare -p makeWrapperArgs)" =~ ^'declare -a makeWrapperArgs=' ]]; then
                        local -a user_args=("${makeWrapperArgs[@]}")
                    else
                        local -a user_args="(${makeWrapperArgs:-})"
                    fi

                    local -a wrapProgramArgs=("${wrap_args[@]}" "${user_args[@]}")
                    wrapProgram "${wrapProgramArgs[@]}"
                fi
            fi
        done
    fi
}

# Adds the lib and bin directories to the PYTHONPATH and PATH variables,
# respectively. Recurses on any paths declared in
# `propagated-build-inputs`, while avoiding duplicating paths by
# flagging the directories it has visited in `pythonPathsSeen`.
_addToPythonPath() {
    local dir="$1"
    # Stop if we've already visited here.
    if [ -n "${pythonPathsSeen[$dir]}" ]; then return; fi
    pythonPathsSeen[$dir]=1
    # addToSearchPath is defined in stdenv/generic/setup.sh. It will have
    # the effect of calling `export program_X=$dir/...:$program_X`.
    addToSearchPath program_PYTHONPATH $dir/@sitePackages@
    addToSearchPath program_PATH $dir/bin

    # Inspect the propagated inputs (if they exist) and recur on them.
    local prop="$dir/nix-support/propagated-build-inputs"
    if [ -e $prop ]; then
        local new_path
        for new_path in $(cat $prop); do
            _addToPythonPath $new_path
        done
    fi
}

createBuildInputsPth() {
    local category="$1"
    local inputs="$2"
    if [ foo"$inputs" != foo ]; then
        for x in $inputs; do
            if $(echo -n $x |grep -q python-recursive-pth-loader); then
                continue
            fi
            if test -d "$x"/@sitePackages@; then
                echo $x/@sitePackages@ \
                    >> "$out"/@sitePackages@/${name}-nix-python-$category.pth
            fi
        done
    fi
}