cc-wrapper: Make hygienic

See the added comments for what exactly has been done.

+189 -15
+75
pkgs/build-support/cc-wrapper/add-flags.sh
··· 1 + # N.B. It may be a surprise that the derivation-specific variables are exported, 2 + # since this is just sourced by the wrapped binaries---the end consumers. This 3 + # is because one wrapper binary may invoke another (e.g. cc invoking ld). In 4 + # that case, it is cheaper/better to not repeat this step and let the forked 5 + # wrapped binary just inherit the work of the forker's wrapper script. 6 + 7 + # Accumulate prefixes for taking in the right input parameters. See setup-hook 8 + # for details. 9 + declare -a role_prefixes=() 10 + if [[ -n "${NIX_CC_WRAPPER_@infixSalt@_TARGET_BUILD:-}" ]]; then 11 + role_prefixes+=(_BUILD) 12 + fi 13 + if [[ -n "${NIX_CC_WRAPPER_@infixSalt@_TARGET_HOST:-}" ]]; then 14 + role_prefixes+=('') 15 + fi 16 + if [[ -n "${NIX_CC_WRAPPER_@infixSalt@_TARGET_TARGET:-}" ]]; then 17 + role_prefixes+=(_TARGET) 18 + fi 19 + 20 + # For each role we serve, we accumulate the input parameters into our own 21 + # cc-wrapper-derivation-specific environment variables. 22 + for pre in "${role_prefixes[@]}"; do 23 + # We need to mangle names for hygiene, but also take parameters/overrides 24 + # from the environment. 25 + slurpUnsalted () { 26 + case "$1" in 27 + CC_WRAPPER_*) 28 + local firstPre=NIX_CC_WRAPPER_ 29 + local varname="${1#CC_WRAPPER_}" 30 + ;; 31 + LD_WRAPPER_*) 32 + local firstPre=NIX_LD_WRAPPER_ 33 + local varname="${1#LD_WRAPPER_}" 34 + ;; 35 + *) 36 + local firstPre=NIX_ 37 + local varname="$1" 38 + ;; 39 + esac 40 + local inputVar="${firstPre}${pre}${varname}" 41 + local outputVar="${firstPre}@infixSalt@_${varname}" 42 + local delimiter='' 43 + if [[ -n "${!outputVar:-}" && -n "${!inputVar:-}" ]]; then 44 + delimiter=' ' 45 + fi 46 + # Easiest to just do this to deal with either the input or (old) output. 47 + set +u 48 + export ${outputVar}+="${delimiter}${!inputVar}" 49 + set -u 50 + } 51 + 52 + slurpUnsalted CC_WRAPPER_START_HOOK 53 + slurpUnsalted CC_WRAPPER_EXEC_HOOK 54 + slurpUnsalted LD_WRAPPER_START_HOOK 55 + slurpUnsalted LD_WRAPPER_EXEC_HOOK 56 + 57 + slurpUnsalted CFLAGS_COMPILE 58 + slurpUnsalted CFLAGS_LINK 59 + slurpUnsalted CXXSTDLIB_COMPILE 60 + slurpUnsalted CXXSTDLIB_LINK 61 + slurpUnsalted DONT_SET_RPATH 62 + slurpUnsalted GNATFLAGS_COMPILE 63 + slurpUnsalted IGNORE_LD_THROUGH_GCC 64 + slurpUnsalted LDFLAGS 65 + slurpUnsalted LDFLAGS_BEFORE 66 + slurpUnsalted LDFLAGS_AFTER 67 + slurpUnsalted LDFLAGS_HARDEN 68 + 69 + slurpUnsalted SET_BUILD_ID 70 + slurpUnsalted DONT_SET_RPATH 71 + slurpUnsalted ENFORCE_NO_NATIVE 72 + done 73 + unset -f slurpUnsalted 74 + 1 75 # `-B@out@/bin' forces cc to use ld-wrapper.sh when calling ld. 2 76 export NIX_@infixSalt@_CFLAGS_COMPILE="-B@out@/bin/ $NIX_@infixSalt@_CFLAGS_COMPILE" 3 77 ··· 34 108 NIX_@infixSalt@_LDFLAGS_BEFORE="$(< @out@/nix-support/libc-ldflags-before) $NIX_@infixSalt@_LDFLAGS_BEFORE" 35 109 fi 36 110 111 + # That way forked processes don't againt extend these environment variables 37 112 export NIX_CC_WRAPPER_@infixSalt@_FLAGS_SET=1
+101 -15
pkgs/build-support/cc-wrapper/setup-hook.sh
··· 1 + # CC Wrapper hygiene 2 + # 3 + # For at least cross compilation, we need to depend on multiple cc-wrappers at 4 + # once---specifically up to one per sort of dependency. This follows from having 5 + # different tools targeting different platforms, and different flags for those 6 + # tools. For example: 7 + # 8 + # # Flags for compiling (whether or not linking) C code for the... 9 + # NIX_BUILD_CFLAGS_COMPILE # ...build platform 10 + # NIX_CFLAGS_COMPILE # ...host platform 11 + # NIX_TARGET_CFLAGS_COMPILE # ...target platform 12 + # 13 + # Notice that these platforms are the 3 *relative* to the package using 14 + # cc-wrapper, not absolute like `x86_64-pc-linux-gnu`. 15 + # 16 + # The simplest solution would be to have separate cc-wrappers per (3 intended 17 + # use-cases * n absolute concrete platforms). For the use-case axis, we would 18 + # @-splice in 'BUILD_' '' 'TARGET_' to use the write environment variables when 19 + # building the cc-wrapper, and likewise prefix the binaries' names so they didn't 20 + # clobber each other on the PATH. But the need for 3x cc-wrappers, along with 21 + # non-standard name prefixes, is annoying and liable to break packages' build 22 + # systems. 23 + # 24 + # Instead, we opt to have just one cc-wrapper per absolute platform. Matching 25 + # convention, the binaries' names can just be prefixed with their target 26 + # platform. On the other hand, that means packages will depend on not just 27 + # multiple cc-wrappers, but the exact same cc-wrapper derivation multiple ways. 28 + # That means the exact same cc-wrapper derivation must be able to avoid 29 + # conflicting with itself, despite the fact that `setup-hook.sh`, the `addCvars` 30 + # function, and `add-flags.sh` are all communicating with each other with 31 + # environment variables. Yuck. 32 + # 33 + # The basic strategy is: 34 + # 35 + # - Everyone exclusively *adds information* to relative-platform-specific 36 + # environment variables, like `NIX_TARGET_CFLAGS_COMPILE`, to communicate 37 + # with the wrapped binaries. 38 + # 39 + # - The wrapped binaries will exclusively *read* cc-wrapper-derivation-specific 40 + # environment variables distinguished with with `infixSalt`, like 41 + # `NIX_@infixSalt@_CFLAGS_COMPILE`. 42 + # 43 + # - `add-flags`, beyond its old task of reading extra flags stuck inside the 44 + # cc-wrapper derivation, will convert the relative-platform-specific 45 + # variables to cc-wrapper-derivation-specific variables. This conversion is 46 + # the only time all but one of the cc-wrapper-derivation-specific variables 47 + # are set. 48 + # 49 + # This ensures the flow of information is exclusive from 50 + # relative-platform-specific variables to cc-wrapper-derivation-specific 51 + # variables. This allows us to support the general case of a many--many relation 52 + # between relative platforms and cc-wrapper derivations. 53 + # 54 + # For more details, read the individual files where the mechanisms used to 55 + # accomplish this will be individually documented. 56 + 57 + 58 + # It's fine that any other cc-wrapper will redefine this. Bash functions close 59 + # over no state, and there's no @-substitutions within, so any redefined 60 + # function is guaranteed to be exactly the same. 1 61 ccWrapper_addCVars () { 62 + # The `depOffset` describes how the platforms of the dependencies are slid 63 + # relative to the depending package. It is brought into scope of the 64 + # environment hook defined as the role of the dependency being applied. 65 + case $depOffset in 66 + -1) local role='BUILD_' ;; 67 + 0) local role='' ;; 68 + 1) local role='TARGET_' ;; 69 + *) echo "cc-wrapper: Error: Cannot be used with $depOffset-offset deps, " >2; 70 + return 1 ;; 71 + esac 72 + 2 73 if [[ -d "$1/include" ]]; then 3 - export NIX_CFLAGS_COMPILE+=" ${ccIncludeFlag:--isystem} $1/include" 74 + export NIX_${role}CFLAGS_COMPILE+=" ${ccIncludeFlag:--isystem} $1/include" 4 75 fi 5 76 6 77 if [[ -d "$1/lib64" && ! -L "$1/lib64" ]]; then 7 - export NIX_LDFLAGS+=" -L$1/lib64" 78 + export NIX_${role}LDFLAGS+=" -L$1/lib64" 8 79 fi 9 80 10 81 if [[ -d "$1/lib" ]]; then 11 - export NIX_LDFLAGS+=" -L$1/lib" 82 + export NIX_${role}LDFLAGS+=" -L$1/lib" 12 83 fi 13 84 14 85 if [[ -d "$1/Library/Frameworks" ]]; then 15 - export NIX_CFLAGS_COMPILE+=" -F$1/Library/Frameworks" 86 + export NIX_${role}CFLAGS_COMPILE+=" -F$1/Library/Frameworks" 16 87 fi 17 88 } 18 89 90 + # Since the same cc-wrapper derivation can be depend on in multiple ways, we 91 + # need to accumulate *each* role (i.e. target platform relative the depending 92 + # derivation) in which the cc-wrapper derivation is used. 93 + # `NIX_CC_WRAPPER_@infixSalt@_TARGET_*` tracks this (needs to be an exported env 94 + # var so can't use fancier data structures). 95 + # 96 + # We also need to worry about what role is being added on *this* invocation of 97 + # setup-hook, which `role` tracks. 98 + if [ -n "${crossConfig:-}" ]; then 99 + export NIX_CC_WRAPPER_@infixSalt@_TARGET_BUILD=1 100 + role="BUILD_" 101 + else 102 + export NIX_CC_WRAPPER_@infixSalt@_TARGET_HOST=1 103 + role="" 104 + fi 105 + 106 + # Eventually the exact sort of env-hook we create will depend on the role. This 107 + # is because based on what relative platform we are targeting, we use different 108 + # dependencies. 19 109 envHooks+=(ccWrapper_addCVars) 20 110 21 111 # Note 1: these come *after* $out in the PATH (see setup.sh). ··· 41 131 addToSearchPath _PATH @coreutils_bin@/bin 42 132 fi 43 133 44 - if [ -z "${crossConfig:-}" ]; then 45 - ENV_PREFIX="" 46 - else 47 - ENV_PREFIX="BUILD_" 48 - fi 134 + # Export tool environment variables so various build systems use the right ones. 49 135 50 - export NIX_${ENV_PREFIX}CC=@out@ 136 + export NIX_${role}CC=@out@ 51 137 52 - export ${ENV_PREFIX}CC=@named_cc@ 53 - export ${ENV_PREFIX}CXX=@named_cxx@ 138 + export ${role}CC=@named_cc@ 139 + export ${role}CXX=@named_cxx@ 54 140 55 141 for CMD in \ 56 142 cpp \ ··· 59 145 if 60 146 PATH=$_PATH type -p "@binPrefix@$CMD" > /dev/null 61 147 then 62 - export "${ENV_PREFIX}$(echo "$CMD" | tr "[:lower:]" "[:upper:]")=@binPrefix@${CMD}"; 148 + export "${role}$(echo "$CMD" | tr "[:lower:]" "[:upper:]")=@binPrefix@${CMD}"; 63 149 fi 64 150 done 65 151 66 - # No local scope available for sourced files 67 - unset ENV_PREFIX 152 + # No local scope in sourced file 153 + unset role
+13
pkgs/stdenv/generic/setup.sh
··· 336 336 337 337 # Set the relevant environment variables to point to the build inputs 338 338 # found above. 339 + # 340 + # These `depOffset`s tell the env hook what sort of dependency 341 + # (ignoring propagatedness) is being passed to the env hook. In a real 342 + # language, we'd append a closure with this information to the 343 + # relevant env hook array, but bash doesn't have closures, so it's 344 + # easier to just pass this in. 345 + 339 346 _addToNativeEnv() { 340 347 local pkg="$1" 348 + if [[ -n "${crossConfig:-}" ]]; then 349 + local -i depOffset=-1 350 + else 351 + local -i depOffset=0 352 + fi 341 353 342 354 # Run the package-specific hooks set by the setup-hook scripts. 343 355 runHook envHook "$pkg" ··· 349 361 350 362 _addToCrossEnv() { 351 363 local pkg="$1" 364 + local -i depOffset=0 352 365 353 366 # Run the package-specific hooks set by the setup-hook scripts. 354 367 runHook crossEnvHook "$pkg"