summary refs log tree commit diff
path: root/pkgs/build-support/setup-hooks/make-wrapper.sh
blob: 8a38c39efc478e2ecb453c95c77d87c6db79ac59 (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
# Assert that FILE exists and is executable
#
# assertExecutable FILE
assertExecutable() {
    local file="$1"
    [[ -f "$file" && -x "$file" ]] || \
        die "Cannot wrap '$file' because it is not an executable file"
}

# construct an executable file that wraps the actual executable
# makeWrapper EXECUTABLE OUT_PATH ARGS

# ARGS:
# --argv0       NAME    : set the name of the executed process to NAME
#                         (if unset or empty, defaults to EXECUTABLE)
# --inherit-argv0       : the executable inherits argv0 from the wrapper.
#                         (use instead of --argv0 '$0')
# --set         VAR VAL : add VAR with value VAL to the executable's environment
# --set-default VAR VAL : like --set, but only adds VAR if not already set in
#                         the environment
# --unset       VAR     : remove VAR from the environment
# --chdir       DIR     : change working directory (use instead of --run "cd DIR")
# --run         COMMAND : run command before the executable
# --add-flags   FLAGS   : add FLAGS to invocation of executable
# TODO(@ncfavier): --append-flags

# --prefix          ENV SEP VAL   : suffix/prefix ENV with VAL, separated by SEP
# --suffix
# --prefix-each     ENV SEP VALS  : like --prefix, but VALS is a list
# --suffix-each     ENV SEP VALS  : like --suffix, but VALS is a list
# --prefix-contents ENV SEP FILES : like --suffix-each, but contents of FILES
#                                   are read first and used as VALS
# --suffix-contents
makeWrapper() { makeShellWrapper "$@"; }
makeShellWrapper() {
    local original="$1"
    local wrapper="$2"
    local params varName value command separator n fileNames
    local argv0 flagsBefore flags

    assertExecutable "$original"

    # Write wrapper code which adds `value` to the beginning or end of
    # the list variable named by `varName`, depending on the `mode`
    # specified.
    #
    # A value which is already part of the list will not be added
    # again. If this is the case and the `suffix` mode is used, the
    # list won't be touched at all. The `prefix` mode will however
    # move the last matching instance of the value to the beginning
    # of the list. Any remaining duplicates of the value will be left
    # as-is.
    addValue() {
        local mode="$1"       # `prefix` or `suffix` to add to the beginning or end respectively
        local varName="$2"    # name of list variable to add to
        local separator="$3"  # character used to separate elements of list
        local value="$4"      # one value, or multiple values separated by `separator`, to add to list

        # Disable file globbing, since bash will otherwise try to find
        # filenames matching the the value to be prefixed/suffixed if
        # it contains characters considered wildcards, such as `?` and
        # `*`. We want the value as is, except we also want to split
        # it on on the separator; hence we can't quote it.
        local reenableGlob=0
        if [[ ! -o noglob ]]; then
            reenableGlob=1
        fi
        set -o noglob

        if [[ -n "$value" ]]; then
            local old_ifs=$IFS
            IFS=$separator

            if [[ "$mode" == '--prefix'* ]]; then
                # Keep the order of the components as written when
                # prefixing; normally, they would be added in the
                # reverse order.
                local tmp=
                for v in $value; do
                    tmp=$v${tmp:+$separator}$tmp
                done
                value="$tmp"
            fi
            for v in $value; do
                {
                    echo "$varName=\${$varName:+${separator@Q}\$$varName${separator@Q}}"               # add separators on both ends unless empty
                    if [[ "$mode" == '--prefix'* ]]; then                                              # -- in prefix mode --
                        echo "$varName=\${$varName/${separator@Q}${v@Q}${separator@Q}/${separator@Q}}" # remove the first instance of the value (if any)
                        echo "$varName=${v@Q}\$$varName"                                               # prepend the value
                    elif [[ "$mode" == '--suffix'* ]]; then                                            # -- in suffix mode --
                        echo "if [[ \$$varName != *${separator@Q}${v@Q}${separator@Q}* ]]; then"       # if the value isn't already in the list
                        echo "    $varName=\$$varName${v@Q}"                                           # append the value
                        echo "fi"
                    else
                        echo "unknown mode $mode!" 1>&2
                        exit 1
                    fi
                    echo "$varName=\${$varName#${separator@Q}}"                                        # remove leading separator
                    echo "$varName=\${$varName%${separator@Q}}"                                        # remove trailing separator
                    echo "export $varName"
                } >> "$wrapper"
            done
            IFS=$old_ifs
        fi

        if (( reenableGlob )); then
            set +o noglob
        fi
    }

    mkdir -p "$(dirname "$wrapper")"

    echo "#! @shell@ -e" > "$wrapper"

    params=("$@")
    for ((n = 2; n < ${#params[*]}; n += 1)); do
        p="${params[$n]}"

        if [[ "$p" == "--set" ]]; then
            varName="${params[$((n + 1))]}"
            value="${params[$((n + 2))]}"
            n=$((n + 2))
            echo "export $varName=${value@Q}" >> "$wrapper"
        elif [[ "$p" == "--set-default" ]]; then
            varName="${params[$((n + 1))]}"
            value="${params[$((n + 2))]}"
            n=$((n + 2))
            echo "export $varName=\${$varName-${value@Q}}" >> "$wrapper"
        elif [[ "$p" == "--unset" ]]; then
            varName="${params[$((n + 1))]}"
            n=$((n + 1))
            echo "unset $varName" >> "$wrapper"
        elif [[ "$p" == "--chdir" ]]; then
            dir="${params[$((n + 1))]}"
            n=$((n + 1))
            echo "cd ${dir@Q}" >> "$wrapper"
        elif [[ "$p" == "--run" ]]; then
            command="${params[$((n + 1))]}"
            n=$((n + 1))
            echo "$command" >> "$wrapper"
        elif [[ ("$p" == "--suffix") || ("$p" == "--prefix") ]]; then
            varName="${params[$((n + 1))]}"
            separator="${params[$((n + 2))]}"
            value="${params[$((n + 3))]}"
            n=$((n + 3))
            addValue "$p" "$varName" "$separator" "$value"
        elif [[ ("$p" == "--suffix-each") || ("$p" == "--prefix-each") ]]; then
            varName="${params[$((n + 1))]}"
            separator="${params[$((n + 2))]}"
            values="${params[$((n + 3))]}"
            n=$((n + 3))
            for value in $values; do
                addValue "$p" "$varName" "$separator" "$value"
            done
        elif [[ ("$p" == "--suffix-contents") || ("$p" == "--prefix-contents") ]]; then
            varName="${params[$((n + 1))]}"
            separator="${params[$((n + 2))]}"
            fileNames="${params[$((n + 3))]}"
            n=$((n + 3))
            for fileName in $fileNames; do
                contents="$(cat "$fileName")"
                addValue "$p" "$varName" "$separator" "$contents"
            done
        elif [[ "$p" == "--add-flags" ]]; then
            flags="${params[$((n + 1))]}"
            n=$((n + 1))
            flagsBefore="$flagsBefore $flags"
        elif [[ "$p" == "--argv0" ]]; then
            argv0="${params[$((n + 1))]}"
            n=$((n + 1))
        elif [[ "$p" == "--inherit-argv0" ]]; then
            # Whichever comes last of --argv0 and --inherit-argv0 wins
            argv0='$0'
        else
            die "makeWrapper doesn't understand the arg $p"
        fi
    done

    echo exec ${argv0:+-a \"$argv0\"} \""$original"\" \
         "$flagsBefore" '"$@"' >> "$wrapper"

    chmod +x "$wrapper"
}

addSuffix() {
    suffix="$1"
    shift
    for name in "$@"; do
        echo "$name$suffix"
    done
}

filterExisting() {
    for fn in "$@"; do
        if test -e "$fn"; then
            echo "$fn"
        fi
    done
}

# Syntax: wrapProgram <PROGRAM> <MAKE-WRAPPER FLAGS...>
wrapProgram() { wrapProgramShell "$@"; }
wrapProgramShell() {
    local prog="$1"
    local hidden

    assertExecutable "$prog"

    hidden="$(dirname "$prog")/.$(basename "$prog")"-wrapped
    while [ -e "$hidden" ]; do
      hidden="${hidden}_"
    done
    mv "$prog" "$hidden"
    makeWrapper "$hidden" "$prog" --inherit-argv0 "${@:2}"
}