diff options
author | John Ericson <John.Ericson@Obsidian.Systems> | 2017-07-06 17:19:53 -0400 |
---|---|---|
committer | John Ericson <John.Ericson@Obsidian.Systems> | 2017-08-07 03:05:50 -0400 |
commit | 42f35503b56e293759685e9fb2a66ba75a55c339 (patch) | |
tree | 9fdd4bb2215fd0d1b290527a932371046f7b9fd8 /pkgs/build-support | |
parent | 9f1e009975dc2d58541de435c74a26afe011542a (diff) | |
download | nixlib-42f35503b56e293759685e9fb2a66ba75a55c339.tar nixlib-42f35503b56e293759685e9fb2a66ba75a55c339.tar.gz nixlib-42f35503b56e293759685e9fb2a66ba75a55c339.tar.bz2 nixlib-42f35503b56e293759685e9fb2a66ba75a55c339.tar.lz nixlib-42f35503b56e293759685e9fb2a66ba75a55c339.tar.xz nixlib-42f35503b56e293759685e9fb2a66ba75a55c339.tar.zst nixlib-42f35503b56e293759685e9fb2a66ba75a55c339.zip |
cc-wrapper: Make hygienic
See the added comments for what exactly has been done.
Diffstat (limited to 'pkgs/build-support')
-rw-r--r-- | pkgs/build-support/cc-wrapper/add-flags.sh | 75 | ||||
-rw-r--r-- | pkgs/build-support/cc-wrapper/setup-hook.sh | 116 |
2 files changed, 176 insertions, 15 deletions
diff --git a/pkgs/build-support/cc-wrapper/add-flags.sh b/pkgs/build-support/cc-wrapper/add-flags.sh index 37b3b4cae86d..b3a765d38e38 100644 --- a/pkgs/build-support/cc-wrapper/add-flags.sh +++ b/pkgs/build-support/cc-wrapper/add-flags.sh @@ -1,3 +1,77 @@ +# N.B. It may be a surprise that the derivation-specific variables are exported, +# since this is just sourced by the wrapped binaries---the end consumers. This +# is because one wrapper binary may invoke another (e.g. cc invoking ld). In +# that case, it is cheaper/better to not repeat this step and let the forked +# wrapped binary just inherit the work of the forker's wrapper script. + +# Accumulate prefixes for taking in the right input parameters. See setup-hook +# for details. +declare -a role_prefixes=() +if [[ -n "${NIX_CC_WRAPPER_@infixSalt@_TARGET_BUILD:-}" ]]; then + role_prefixes+=(_BUILD) +fi +if [[ -n "${NIX_CC_WRAPPER_@infixSalt@_TARGET_HOST:-}" ]]; then + role_prefixes+=('') +fi +if [[ -n "${NIX_CC_WRAPPER_@infixSalt@_TARGET_TARGET:-}" ]]; then + role_prefixes+=(_TARGET) +fi + +# For each role we serve, we accumulate the input parameters into our own +# cc-wrapper-derivation-specific environment variables. +for pre in "${role_prefixes[@]}"; do + # We need to mangle names for hygiene, but also take parameters/overrides + # from the environment. + slurpUnsalted () { + case "$1" in + CC_WRAPPER_*) + local firstPre=NIX_CC_WRAPPER_ + local varname="${1#CC_WRAPPER_}" + ;; + LD_WRAPPER_*) + local firstPre=NIX_LD_WRAPPER_ + local varname="${1#LD_WRAPPER_}" + ;; + *) + local firstPre=NIX_ + local varname="$1" + ;; + esac + local inputVar="${firstPre}${pre}${varname}" + local outputVar="${firstPre}@infixSalt@_${varname}" + local delimiter='' + if [[ -n "${!outputVar:-}" && -n "${!inputVar:-}" ]]; then + delimiter=' ' + fi + # Easiest to just do this to deal with either the input or (old) output. + set +u + export ${outputVar}+="${delimiter}${!inputVar}" + set -u + } + + slurpUnsalted CC_WRAPPER_START_HOOK + slurpUnsalted CC_WRAPPER_EXEC_HOOK + slurpUnsalted LD_WRAPPER_START_HOOK + slurpUnsalted LD_WRAPPER_EXEC_HOOK + + slurpUnsalted CFLAGS_COMPILE + slurpUnsalted CFLAGS_LINK + slurpUnsalted CXXSTDLIB_COMPILE + slurpUnsalted CXXSTDLIB_LINK + slurpUnsalted DONT_SET_RPATH + slurpUnsalted GNATFLAGS_COMPILE + slurpUnsalted IGNORE_LD_THROUGH_GCC + slurpUnsalted LDFLAGS + slurpUnsalted LDFLAGS_BEFORE + slurpUnsalted LDFLAGS_AFTER + slurpUnsalted LDFLAGS_HARDEN + + slurpUnsalted SET_BUILD_ID + slurpUnsalted DONT_SET_RPATH + slurpUnsalted ENFORCE_NO_NATIVE +done +unset -f slurpUnsalted + # `-B@out@/bin' forces cc to use ld-wrapper.sh when calling ld. export NIX_@infixSalt@_CFLAGS_COMPILE="-B@out@/bin/ $NIX_@infixSalt@_CFLAGS_COMPILE" @@ -34,4 +108,5 @@ if [ -e @out@/nix-support/libc-ldflags-before ]; then NIX_@infixSalt@_LDFLAGS_BEFORE="$(< @out@/nix-support/libc-ldflags-before) $NIX_@infixSalt@_LDFLAGS_BEFORE" fi +# That way forked processes don't againt extend these environment variables export NIX_CC_WRAPPER_@infixSalt@_FLAGS_SET=1 diff --git a/pkgs/build-support/cc-wrapper/setup-hook.sh b/pkgs/build-support/cc-wrapper/setup-hook.sh index 360a667a3a5b..c6abd6281d26 100644 --- a/pkgs/build-support/cc-wrapper/setup-hook.sh +++ b/pkgs/build-support/cc-wrapper/setup-hook.sh @@ -1,21 +1,111 @@ +# CC Wrapper hygiene +# +# For at least cross compilation, we need to depend on multiple cc-wrappers at +# once---specifically up to one per sort of dependency. This follows from having +# different tools targeting different platforms, and different flags for those +# tools. For example: +# +# # Flags for compiling (whether or not linking) C code for the... +# NIX_BUILD_CFLAGS_COMPILE # ...build platform +# NIX_CFLAGS_COMPILE # ...host platform +# NIX_TARGET_CFLAGS_COMPILE # ...target platform +# +# Notice that these platforms are the 3 *relative* to the package using +# cc-wrapper, not absolute like `x86_64-pc-linux-gnu`. +# +# The simplest solution would be to have separate cc-wrappers per (3 intended +# use-cases * n absolute concrete platforms). For the use-case axis, we would +# @-splice in 'BUILD_' '' 'TARGET_' to use the write environment variables when +# building the cc-wrapper, and likewise prefix the binaries' names so they didn't +# clobber each other on the PATH. But the need for 3x cc-wrappers, along with +# non-standard name prefixes, is annoying and liable to break packages' build +# systems. +# +# Instead, we opt to have just one cc-wrapper per absolute platform. Matching +# convention, the binaries' names can just be prefixed with their target +# platform. On the other hand, that means packages will depend on not just +# multiple cc-wrappers, but the exact same cc-wrapper derivation multiple ways. +# That means the exact same cc-wrapper derivation must be able to avoid +# conflicting with itself, despite the fact that `setup-hook.sh`, the `addCvars` +# function, and `add-flags.sh` are all communicating with each other with +# environment variables. Yuck. +# +# The basic strategy is: +# +# - Everyone exclusively *adds information* to relative-platform-specific +# environment variables, like `NIX_TARGET_CFLAGS_COMPILE`, to communicate +# with the wrapped binaries. +# +# - The wrapped binaries will exclusively *read* cc-wrapper-derivation-specific +# environment variables distinguished with with `infixSalt`, like +# `NIX_@infixSalt@_CFLAGS_COMPILE`. +# +# - `add-flags`, beyond its old task of reading extra flags stuck inside the +# cc-wrapper derivation, will convert the relative-platform-specific +# variables to cc-wrapper-derivation-specific variables. This conversion is +# the only time all but one of the cc-wrapper-derivation-specific variables +# are set. +# +# This ensures the flow of information is exclusive from +# relative-platform-specific variables to cc-wrapper-derivation-specific +# variables. This allows us to support the general case of a many--many relation +# between relative platforms and cc-wrapper derivations. +# +# For more details, read the individual files where the mechanisms used to +# accomplish this will be individually documented. + + +# It's fine that any other cc-wrapper will redefine this. Bash functions close +# over no state, and there's no @-substitutions within, so any redefined +# function is guaranteed to be exactly the same. ccWrapper_addCVars () { + # The `depOffset` describes how the platforms of the dependencies are slid + # relative to the depending package. It is brought into scope of the + # environment hook defined as the role of the dependency being applied. + case $depOffset in + -1) local role='BUILD_' ;; + 0) local role='' ;; + 1) local role='TARGET_' ;; + *) echo "cc-wrapper: Error: Cannot be used with $depOffset-offset deps, " >2; + return 1 ;; + esac + if [[ -d "$1/include" ]]; then - export NIX_CFLAGS_COMPILE+=" ${ccIncludeFlag:--isystem} $1/include" + export NIX_${role}CFLAGS_COMPILE+=" ${ccIncludeFlag:--isystem} $1/include" fi if [[ -d "$1/lib64" && ! -L "$1/lib64" ]]; then - export NIX_LDFLAGS+=" -L$1/lib64" + export NIX_${role}LDFLAGS+=" -L$1/lib64" fi if [[ -d "$1/lib" ]]; then - export NIX_LDFLAGS+=" -L$1/lib" + export NIX_${role}LDFLAGS+=" -L$1/lib" fi if [[ -d "$1/Library/Frameworks" ]]; then - export NIX_CFLAGS_COMPILE+=" -F$1/Library/Frameworks" + export NIX_${role}CFLAGS_COMPILE+=" -F$1/Library/Frameworks" fi } +# Since the same cc-wrapper derivation can be depend on in multiple ways, we +# need to accumulate *each* role (i.e. target platform relative the depending +# derivation) in which the cc-wrapper derivation is used. +# `NIX_CC_WRAPPER_@infixSalt@_TARGET_*` tracks this (needs to be an exported env +# var so can't use fancier data structures). +# +# We also need to worry about what role is being added on *this* invocation of +# setup-hook, which `role` tracks. +if [ -n "${crossConfig:-}" ]; then + export NIX_CC_WRAPPER_@infixSalt@_TARGET_BUILD=1 + role="BUILD_" +else + export NIX_CC_WRAPPER_@infixSalt@_TARGET_HOST=1 + role="" +fi + +# Eventually the exact sort of env-hook we create will depend on the role. This +# is because based on what relative platform we are targeting, we use different +# dependencies. envHooks+=(ccWrapper_addCVars) # Note 1: these come *after* $out in the PATH (see setup.sh). @@ -41,16 +131,12 @@ if [ -n "@coreutils_bin@" ]; then addToSearchPath _PATH @coreutils_bin@/bin fi -if [ -z "${crossConfig:-}" ]; then - ENV_PREFIX="" -else - ENV_PREFIX="BUILD_" -fi +# Export tool environment variables so various build systems use the right ones. -export NIX_${ENV_PREFIX}CC=@out@ +export NIX_${role}CC=@out@ -export ${ENV_PREFIX}CC=@named_cc@ -export ${ENV_PREFIX}CXX=@named_cxx@ +export ${role}CC=@named_cc@ +export ${role}CXX=@named_cxx@ for CMD in \ cpp \ @@ -59,9 +145,9 @@ do if PATH=$_PATH type -p "@binPrefix@$CMD" > /dev/null then - export "${ENV_PREFIX}$(echo "$CMD" | tr "[:lower:]" "[:upper:]")=@binPrefix@${CMD}"; + export "${role}$(echo "$CMD" | tr "[:lower:]" "[:upper:]")=@binPrefix@${CMD}"; fi done -# No local scope available for sourced files -unset ENV_PREFIX +# No local scope in sourced file +unset role |