diff --git a/pkgs/build-support/setup-hooks/make-binary-wrapper.sh b/pkgs/build-support/setup-hooks/make-binary-wrapper.sh new file mode 100755 index 000000000000..b77aca60a01d --- /dev/null +++ b/pkgs/build-support/setup-hooks/make-binary-wrapper.sh @@ -0,0 +1,108 @@ +# Generate a binary executable wrapper for wrapping an executable. +# The binary is compiled from generated C-code using gcc. +# makeBinaryWrapper EXECUTABLE OUT_PATH ARGS + +# ARGS: +# --argv0 NAME : set name of executed process to NAME +# (defaults to EXECUTABLE) +# --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 +# +# To troubleshoot a binary wrapper after you compiled it, +# use the `strings` command or open the binary file in a text editor. +makeBinaryWrapper() { + makeDocumentedCWrapper "$1" "${@:3}" | gcc -x c -o "$2" - +} + +# Generate source code for the wrapper in such a way that the wrapper source code +# will still be readable even after compilation +# makeDocumentedCWrapper EXECUTABLE ARGS +# ARGS: same as makeBinaryWrapper +makeDocumentedCWrapper() { + local src=$(makeCWrapper "$@") + local docs=$(documentationString "$src") + printf "%s\n" "$src" + printf "\n%s\n" "$docs" +} + +# makeCWrapper EXECUTABLE ARGS +# ARGS: same as makeBinaryWrapper +makeCWrapper() { + local argv0 n params cmd + local executable=$(escapeStringLiteral "$1") + local params=("$@") + + printf "%s\n" "#include " + printf "%s\n" "#include " + printf "\n%s\n" "int main(int argc, char **argv) {" + + for ((n = 1; n < ${#params[*]}; n += 1)); do + p="${params[$n]}" + if [[ "$p" == "--set" ]]; then + cmd=$(setEnv "${params[$((n + 1))]}" "${params[$((n + 2))]}") + n=$((n + 2)) + printf "%s\n" " $cmd" + elif [[ "$p" == "--set-default" ]]; then + cmd=$(setDefaultEnv "${params[$((n + 1))]}" "${params[$((n + 2))]}") + n=$((n + 2)) + printf "%s\n" " $cmd" + elif [[ "$p" == "--unset" ]]; then + cmd=$(unsetEnv "${params[$((n + 1))]}") + printf "%s\n" " $cmd" + n=$((n + 1)) + elif [[ "$p" == "--argv0" ]]; then + argv0=$(escapeStringLiteral "${params[$((n + 1))]}") + n=$((n + 1)) + else + # Using an error macro, we will make sure the compiler gives an understandable error message + printf "%s\n" " #error makeCWrapper did not understand argument ${p}" + fi + done + + printf "%s\n" " argv[0] = \"${argv0:-${executable}}\";" + printf "%s\n" " return execv(\"${executable}\", argv);" + printf "%s\n" "}" +} + +# setEnv KEY VALUE +setEnv() { + local key=$(escapeStringLiteral "$1") + local value=$(escapeStringLiteral "$2") + printf "%s" "putenv(\"${key}=${value}\");" +} + +# setDefaultEnv KEY VALUE +setDefaultEnv() { + local key=$(escapeStringLiteral "$1") + local value=$(escapeStringLiteral "$2") + printf "%s" "setenv(\"$key\", \"$value\", 0);" +} + +# unsetEnv KEY +unsetEnv() { + local key=$(escapeStringLiteral "$1") + printf "%s" "unsetenv(\"$key\");" +} + +# Put the entire source code into const char* SOURCE_CODE to make it readable after compilation. +# documentationString SOURCE_CODE +documentationString() { + local docs=$(escapeStringLiteral $'\n----------\n// This binary wrapper was compiled from the following generated C-code:\n'"$1"$'\n----------\n') + printf "%s" "const char * SOURCE_CODE = \"$docs\";" +} + +# Makes it safe to insert STRING within quotes in a C String Literal. +# escapeStringLiteral STRING +escapeStringLiteral() { + local result + result=${1//$'\\'/$'\\\\'} + result=${result//\"/'\"'} + result=${result//$'\n'/"\n"} + result=${result//$'\r'/"\r"} + printf "%s" "$result" +} + +makeBinaryWrapper "$@" diff --git a/pkgs/build-support/setup-hooks/make-c-wrapper.sh b/pkgs/build-support/setup-hooks/make-c-wrapper.sh deleted file mode 100644 index 55afe9fcf2a2..000000000000 --- a/pkgs/build-support/setup-hooks/make-c-wrapper.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/bash -# make-c-wrapper.sh EXECUTABLE ARGS -# -# ARGS: -# --argv0 NAME : set name of executed process to NAME -# (defaults to EXECUTABLE) -# --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 -# -# To debug a binary wrapper after you compiled it, use the `strings` command - -escape_string_literal() { - # We need to make sure that special characters are escaped - # before trying to create C string literals - result=${1//$'\\'/$'\\\\'} - result=${result//\"/'\"'} - result=${result//$'\n'/"\n"} - result=${result//$'\r'/"\r"} -} - -escape_string_literal "$1" -executable="${result}" -args=("$@") - -printf "%s\n" "#include " -printf "%s\n" "#include " -printf "\n%s\n" "int main(int argc, char **argv) {" -for ((n = 1; n < ${#args[*]}; n += 1)); do - p="${args[$n]}" - if [[ "$p" == "--set" ]]; then - escape_string_literal "${args[$((n + 1))]}" - key="${result}" - escape_string_literal "${args[$((n + 2))]}" - value="${result}" - n=$((n + 2)) - printf "%s\n" " putenv(\"${key}=${value}\");" - docs="${docs:+$docs$'\n'}putenv(\"${key}=${value}\");" - elif [[ "$p" == "--set-default" ]]; then - escape_string_literal "${args[$((n + 1))]}" - key="${result}" - escape_string_literal "${args[$((n + 2))]}" - value="${result}" - n=$((n + 2)) - printf "%s\n" " setenv(\"${key}\", \"${value}\", 0);" - docs="${docs:+$docs$'\n'}setenv(\"${key}=${value}\", 0);" - elif [[ "$p" == "--unset" ]]; then - escape_string_literal "${args[$((n + 1))]}" - key="${result}" - printf "%s\n" " unsetenv(\"$key\");" - docs="${docs:+$docs$'\n'}unsetenv(\"${key}\");" - n=$((n + 1)) - elif [[ "$p" == "--argv0" ]]; then - escape_string_literal "${args[$((n + 1))]}" - argv0="${result}" - n=$((n + 1)) - else - # Using an error macro, we will make sure the compiler gives an understandable error message - printf "%s\n" " #error make-c-wrapper.sh did not understand argument ${p}" - fi -done -printf "%s\n" " argv[0] = \"${argv0:-${executable}}\";" -printf "%s\n" " return execv(\"${executable}\", argv);" -printf "%s\n" "}" - -docs="${docs:+$docs$'\n'}argv[0] = \"${argv0:-${executable}}\";" -docs="${docs:+$docs$'\n'}execv(\"${executable}\", argv);" -docs="----------"$'\n'"This binary wrapper (created from generated C-code) is configured with the following settings:${docs:+$'\n'$docs}" -escape_string_literal "$docs" -docs=$result -printf "\n%s\n" "const char* DOCS = \"$docs\";"