Merge branch 'bergkvist/make-c-wrapper' into make-c-wrapper
This commit is contained in:
commit
df13841609
2 changed files with 122 additions and 43 deletions
|
@ -796,7 +796,7 @@ A setup-hook very similar to `makeWrapper`, only it creates a tiny _compiled_ wr
|
||||||
|
|
||||||
Compiled wrappers generated by `makeBinaryWrapper` can be inspected with `less <path-to-wrapper>` - by scrolling past the binary data you should be able to see the C code that generated the executable and there see the environment variables that were injected into the wrapper.
|
Compiled wrappers generated by `makeBinaryWrapper` can be inspected with `less <path-to-wrapper>` - by scrolling past the binary data you should be able to see the C code that generated the executable and there see the environment variables that were injected into the wrapper.
|
||||||
|
|
||||||
Similarly to `wrapProgram`, the `makeBinaryWrapper` setup-hook provides a `wrapProgramBinary` with similar command line arguments.
|
Similarly to `wrapProgram`, the `makeBinaryWrapper` setup-hook provides a `binaryWrapProgram` with similar command line arguments.
|
||||||
|
|
||||||
### `substitute` \<infile\> \<outfile\> \<subs\> {#fun-substitute}
|
### `substitute` \<infile\> \<outfile\> \<subs\> {#fun-substitute}
|
||||||
|
|
||||||
|
@ -875,7 +875,7 @@ Convenience function for `makeWrapper` that replaces `<\executable\>` with a wra
|
||||||
|
|
||||||
If you will apply it multiple times, it will overwrite the wrapper file and you will end up with double wrapping, which should be avoided.
|
If you will apply it multiple times, it will overwrite the wrapper file and you will end up with double wrapping, which should be avoided.
|
||||||
|
|
||||||
### `wrapProgramBinary` \<executable\> \<makeBinaryWrapperArgs\> {#fun-wrapProgramBinary}
|
### `binaryWrapProgram` \<executable\> \<makeBinaryWrapperArgs\> {#fun-binaryWrapProgram}
|
||||||
|
|
||||||
Convenience function for `makeBinaryWrapper` that replaces `<\executable\>` with a wrapper that executes the original program. It takes all the same arguments as `makeBinaryWrapper`, except for `--argv0`.
|
Convenience function for `makeBinaryWrapper` that replaces `<\executable\>` with a wrapper that executes the original program. It takes all the same arguments as `makeBinaryWrapper`, except for `--argv0`.
|
||||||
|
|
||||||
|
|
|
@ -51,14 +51,14 @@ wrapProgram() {
|
||||||
makeWrapper "$hidden" "$prog" --inherit-argv0 "${@:2}"
|
makeWrapper "$hidden" "$prog" --inherit-argv0 "${@:2}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Generate source code for the wrapper in such a way that the wrapper source code
|
# Generate source code for the wrapper in such a way that the wrapper inputs
|
||||||
# will still be readable even after compilation
|
# will still be readable even after compilation
|
||||||
# makeDocumentedCWrapper EXECUTABLE ARGS
|
# makeDocumentedCWrapper EXECUTABLE ARGS
|
||||||
# ARGS: same as makeWrapper
|
# ARGS: same as makeWrapper
|
||||||
makeDocumentedCWrapper() {
|
makeDocumentedCWrapper() {
|
||||||
local src docs
|
local src docs
|
||||||
src=$(makeCWrapper "$@")
|
src=$(makeCWrapper "$@")
|
||||||
docs=$(documentationString "$src")
|
docs=$(docstring "$@")
|
||||||
printf '%s\n\n' "$src"
|
printf '%s\n\n' "$src"
|
||||||
printf '%s\n' "$docs"
|
printf '%s\n' "$docs"
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ makeDocumentedCWrapper() {
|
||||||
# makeCWrapper EXECUTABLE ARGS
|
# makeCWrapper EXECUTABLE ARGS
|
||||||
# ARGS: same as makeWrapper
|
# ARGS: same as makeWrapper
|
||||||
makeCWrapper() {
|
makeCWrapper() {
|
||||||
local argv0 inherit_argv0 n params cmd main flagsBefore flags executable params length
|
local argv0 inherit_argv0 n params cmd main flagsBefore flags executable length
|
||||||
local uses_prefix uses_suffix uses_assert uses_assert_success uses_stdio uses_asprintf
|
local uses_prefix uses_suffix uses_assert uses_assert_success uses_stdio uses_asprintf
|
||||||
executable=$(escapeStringLiteral "$1")
|
executable=$(escapeStringLiteral "$1")
|
||||||
params=("$@")
|
params=("$@")
|
||||||
|
@ -76,82 +76,82 @@ makeCWrapper() {
|
||||||
case $p in
|
case $p in
|
||||||
--set)
|
--set)
|
||||||
cmd=$(setEnv "${params[n + 1]}" "${params[n + 2]}")
|
cmd=$(setEnv "${params[n + 1]}" "${params[n + 2]}")
|
||||||
main="$main $cmd"$'\n'
|
main="$main$cmd"$'\n'
|
||||||
n=$((n + 2))
|
n=$((n + 2))
|
||||||
[ $n -ge "$length" ] && main="$main #error makeCWrapper: $p takes 2 arguments"$'\n'
|
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 2 arguments"$'\n'
|
||||||
;;
|
;;
|
||||||
--set-default)
|
--set-default)
|
||||||
cmd=$(setDefaultEnv "${params[n + 1]}" "${params[n + 2]}")
|
cmd=$(setDefaultEnv "${params[n + 1]}" "${params[n + 2]}")
|
||||||
main="$main $cmd"$'\n'
|
main="$main$cmd"$'\n'
|
||||||
uses_stdio=1
|
uses_stdio=1
|
||||||
uses_assert_success=1
|
uses_assert_success=1
|
||||||
n=$((n + 2))
|
n=$((n + 2))
|
||||||
[ $n -ge "$length" ] && main="$main #error makeCWrapper: $p takes 2 arguments"$'\n'
|
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 2 arguments"$'\n'
|
||||||
;;
|
;;
|
||||||
--unset)
|
--unset)
|
||||||
cmd=$(unsetEnv "${params[n + 1]}")
|
cmd=$(unsetEnv "${params[n + 1]}")
|
||||||
main="$main $cmd"$'\n'
|
main="$main$cmd"$'\n'
|
||||||
uses_stdio=1
|
uses_stdio=1
|
||||||
uses_assert_success=1
|
uses_assert_success=1
|
||||||
n=$((n + 1))
|
n=$((n + 1))
|
||||||
[ $n -ge "$length" ] && main="$main #error makeCWrapper: $p takes 1 argument"$'\n'
|
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
|
||||||
;;
|
;;
|
||||||
--prefix)
|
--prefix)
|
||||||
cmd=$(setEnvPrefix "${params[n + 1]}" "${params[n + 2]}" "${params[n + 3]}")
|
cmd=$(setEnvPrefix "${params[n + 1]}" "${params[n + 2]}" "${params[n + 3]}")
|
||||||
main="$main $cmd"$'\n'
|
main="$main$cmd"$'\n'
|
||||||
uses_prefix=1
|
uses_prefix=1
|
||||||
uses_asprintf=1
|
uses_asprintf=1
|
||||||
uses_stdio=1
|
uses_stdio=1
|
||||||
uses_assert_success=1
|
uses_assert_success=1
|
||||||
uses_assert=1
|
uses_assert=1
|
||||||
n=$((n + 3))
|
n=$((n + 3))
|
||||||
[ $n -ge "$length" ] && main="$main #error makeCWrapper: $p takes 3 arguments"$'\n'
|
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 3 arguments"$'\n'
|
||||||
;;
|
;;
|
||||||
--suffix)
|
--suffix)
|
||||||
cmd=$(setEnvSuffix "${params[n + 1]}" "${params[n + 2]}" "${params[n + 3]}")
|
cmd=$(setEnvSuffix "${params[n + 1]}" "${params[n + 2]}" "${params[n + 3]}")
|
||||||
main="$main $cmd"$'\n'
|
main="$main$cmd"$'\n'
|
||||||
uses_suffix=1
|
uses_suffix=1
|
||||||
uses_asprintf=1
|
uses_asprintf=1
|
||||||
uses_stdio=1
|
uses_stdio=1
|
||||||
uses_assert_success=1
|
uses_assert_success=1
|
||||||
uses_assert=1
|
uses_assert=1
|
||||||
n=$((n + 3))
|
n=$((n + 3))
|
||||||
[ $n -ge "$length" ] && main="$main #error makeCWrapper: $p takes 3 arguments"$'\n'
|
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 3 arguments"$'\n'
|
||||||
;;
|
;;
|
||||||
--chdir)
|
--chdir)
|
||||||
cmd=$(changeDir "${params[n + 1]}")
|
cmd=$(changeDir "${params[n + 1]}")
|
||||||
main="$main $cmd"$'\n'
|
main="$main$cmd"$'\n'
|
||||||
uses_stdio=1
|
uses_stdio=1
|
||||||
uses_assert_success=1
|
uses_assert_success=1
|
||||||
n=$((n + 1))
|
n=$((n + 1))
|
||||||
[ $n -ge "$length" ] && main="$main #error makeCWrapper: $p takes 1 argument"$'\n'
|
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
|
||||||
;;
|
;;
|
||||||
--add-flags)
|
--add-flags)
|
||||||
flags="${params[n + 1]}"
|
flags="${params[n + 1]}"
|
||||||
flagsBefore="$flagsBefore $flags"
|
flagsBefore="$flagsBefore $flags"
|
||||||
uses_assert=1
|
uses_assert=1
|
||||||
n=$((n + 1))
|
n=$((n + 1))
|
||||||
[ $n -ge "$length" ] && main="$main #error makeCWrapper: $p takes 1 argument"$'\n'
|
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
|
||||||
;;
|
;;
|
||||||
--argv0)
|
--argv0)
|
||||||
argv0=$(escapeStringLiteral "${params[n + 1]}")
|
argv0=$(escapeStringLiteral "${params[n + 1]}")
|
||||||
inherit_argv0=
|
inherit_argv0=
|
||||||
n=$((n + 1))
|
n=$((n + 1))
|
||||||
[ $n -ge "$length" ] && main="$main #error makeCWrapper: $p takes 1 argument"$'\n'
|
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
|
||||||
;;
|
;;
|
||||||
--inherit-argv0)
|
--inherit-argv0)
|
||||||
# Whichever comes last of --argv0 and --inherit-argv0 wins
|
# Whichever comes last of --argv0 and --inherit-argv0 wins
|
||||||
inherit_argv0=1
|
inherit_argv0=1
|
||||||
;;
|
;;
|
||||||
*) # Using an error macro, we will make sure the compiler gives an understandable error message
|
*) # Using an error macro, we will make sure the compiler gives an understandable error message
|
||||||
main="$main #error makeCWrapper: Unknown argument ${p}"$'\n'
|
main="$main#error makeCWrapper: Unknown argument ${p}"$'\n'
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
[ -z "$flagsBefore" ] || main="$main"${main:+$'\n'}$(addFlags $flagsBefore)$'\n'$'\n'
|
[ -z "$flagsBefore" ] || main="$main"${main:+$'\n'}$(addFlags $flagsBefore)$'\n'$'\n'
|
||||||
[ -z "$inherit_argv0" ] && main="$main argv[0] = \"${argv0:-${executable}}\";"$'\n'
|
[ -z "$inherit_argv0" ] && main="${main}argv[0] = \"${argv0:-${executable}}\";"$'\n'
|
||||||
main="$main return execv(\"${executable}\", argv);"$'\n'
|
main="${main}return execv(\"${executable}\", argv);"$'\n'
|
||||||
|
|
||||||
[ -z "$uses_asprintf" ] || printf '%s\n' "#define _GNU_SOURCE /* See feature_test_macros(7) */"
|
[ -z "$uses_asprintf" ] || printf '%s\n' "#define _GNU_SOURCE /* See feature_test_macros(7) */"
|
||||||
printf '%s\n' "#include <unistd.h>"
|
printf '%s\n' "#include <unistd.h>"
|
||||||
|
@ -162,8 +162,8 @@ makeCWrapper() {
|
||||||
[ -z "$uses_prefix" ] || printf '\n%s\n' "$(setEnvPrefixFn)"
|
[ -z "$uses_prefix" ] || printf '\n%s\n' "$(setEnvPrefixFn)"
|
||||||
[ -z "$uses_suffix" ] || printf '\n%s\n' "$(setEnvSuffixFn)"
|
[ -z "$uses_suffix" ] || printf '\n%s\n' "$(setEnvSuffixFn)"
|
||||||
printf '\n%s' "int main(int argc, char **argv) {"
|
printf '\n%s' "int main(int argc, char **argv) {"
|
||||||
printf '\n%s' "$main"
|
printf '\n%s' "$(indent4 "$main")"
|
||||||
printf '%s\n' "}"
|
printf '\n%s\n' "}"
|
||||||
}
|
}
|
||||||
|
|
||||||
addFlags() {
|
addFlags() {
|
||||||
|
@ -172,17 +172,17 @@ addFlags() {
|
||||||
flags=("$@")
|
flags=("$@")
|
||||||
for ((n = 0; n < ${#flags[*]}; n += 1)); do
|
for ((n = 0; n < ${#flags[*]}; n += 1)); do
|
||||||
flag=$(escapeStringLiteral "${flags[$n]}")
|
flag=$(escapeStringLiteral "${flags[$n]}")
|
||||||
result="$result ${var}[$((n+1))] = \"$flag\";"$'\n'
|
result="$result${var}[$((n+1))] = \"$flag\";"$'\n'
|
||||||
done
|
done
|
||||||
printf ' %s\n' "char **$var = calloc($((n+1)) + argc, sizeof(*$var));"
|
printf '%s\n' "char **$var = calloc($((n+1)) + argc, sizeof(*$var));"
|
||||||
printf ' %s\n' "assert($var != NULL);"
|
printf '%s\n' "assert($var != NULL);"
|
||||||
printf ' %s\n' "${var}[0] = argv[0];"
|
printf '%s\n' "${var}[0] = argv[0];"
|
||||||
printf '%s' "$result"
|
printf '%s' "$result"
|
||||||
printf ' %s\n' "for (int i = 1; i < argc; ++i) {"
|
printf '%s\n' "for (int i = 1; i < argc; ++i) {"
|
||||||
printf ' %s\n' " ${var}[$n + i] = argv[i];"
|
printf '%s\n' " ${var}[$n + i] = argv[i];"
|
||||||
printf ' %s\n' "}"
|
printf '%s\n' "}"
|
||||||
printf ' %s\n' "${var}[$n + argc] = NULL;"
|
printf '%s\n' "${var}[$n + argc] = NULL;"
|
||||||
printf ' %s\n' "argv = $var;"
|
printf '%s\n' "argv = $var;"
|
||||||
}
|
}
|
||||||
|
|
||||||
# chdir DIR
|
# chdir DIR
|
||||||
|
@ -238,14 +238,6 @@ unsetEnv() {
|
||||||
assertValidEnvName "$1"
|
assertValidEnvName "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Put the entire source code into const char* SOURCE_CODE to make it readable after compilation.
|
|
||||||
# documentationString SOURCE_CODE
|
|
||||||
documentationString() {
|
|
||||||
local docs
|
|
||||||
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.
|
# Makes it safe to insert STRING within quotes in a C String Literal.
|
||||||
# escapeStringLiteral STRING
|
# escapeStringLiteral STRING
|
||||||
escapeStringLiteral() {
|
escapeStringLiteral() {
|
||||||
|
@ -257,10 +249,16 @@ escapeStringLiteral() {
|
||||||
printf '%s' "$result"
|
printf '%s' "$result"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Indents every non-empty line by 4 spaces. To avoid trailing whitespace, we don't indent empty lines
|
||||||
|
# indent4 TEXT_BLOCK
|
||||||
|
indent4() {
|
||||||
|
printf '%s' "$1" | awk '{ if ($0 != "") { print " "$0 } else { print $0 }}'
|
||||||
|
}
|
||||||
|
|
||||||
assertValidEnvName() {
|
assertValidEnvName() {
|
||||||
case "$1" in
|
case "$1" in
|
||||||
*=*) printf '\n%s\n' " #error Illegal environment variable name \`$1\` (cannot contain \`=\`)";;
|
*=*) printf '\n%s\n' "#error Illegal environment variable name \`$1\` (cannot contain \`=\`)";;
|
||||||
"") printf '\n%s\n' " #error Environment variable name can't be empty.";;
|
"") printf '\n%s\n' "#error Environment variable name can't be empty.";;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,3 +293,84 @@ void set_env_suffix(char *env, char *sep, char *suffix) {
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Embed a C string which shows up as readable text in the compiled binary wrapper
|
||||||
|
# documentationString ARGS
|
||||||
|
docstring() {
|
||||||
|
printf '%s' "const char * DOCSTRING = \"$(escapeStringLiteral "
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------------
|
||||||
|
# The C-code for this binary wrapper has been generated using the following command:
|
||||||
|
|
||||||
|
|
||||||
|
makeCWrapper $(formatArgs "$@")
|
||||||
|
|
||||||
|
|
||||||
|
# (Use \`nix-shell -p makeBinaryWrapper\` to get access to makeCWrapper in your shell)
|
||||||
|
# ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
")\";"
|
||||||
|
}
|
||||||
|
|
||||||
|
# formatArgs EXECUTABLE ARGS
|
||||||
|
formatArgs() {
|
||||||
|
printf '%s' "$1"
|
||||||
|
shift
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--set)
|
||||||
|
formatArgsLine 2 "$@"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--set-default)
|
||||||
|
formatArgsLine 2 "$@"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--unset)
|
||||||
|
formatArgsLine 1 "$@"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
--prefix)
|
||||||
|
formatArgsLine 3 "$@"
|
||||||
|
shift 3
|
||||||
|
;;
|
||||||
|
--suffix)
|
||||||
|
formatArgsLine 3 "$@"
|
||||||
|
shift 3
|
||||||
|
;;
|
||||||
|
--chdir)
|
||||||
|
formatArgsLine 1 "$@"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
--add-flags)
|
||||||
|
formatArgsLine 1 "$@"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
--argv0)
|
||||||
|
formatArgsLine 1 "$@"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
--inherit-argv0)
|
||||||
|
formatArgsLine 0 "$@"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
printf '%s\n' ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# formatArgsLine ARG_COUNT ARGS
|
||||||
|
formatArgsLine() {
|
||||||
|
local ARG_COUNT LENGTH
|
||||||
|
ARG_COUNT=$1
|
||||||
|
LENGTH=$#
|
||||||
|
shift
|
||||||
|
printf '%s' $' \\\n '"$1"
|
||||||
|
shift
|
||||||
|
while [ "$ARG_COUNT" -gt $((LENGTH - $# - 2)) ]; do
|
||||||
|
printf ' %s' "${1@Q}"
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue