# 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. set -u # Skip setup hook if we're neither a build-time dep, nor, temporarily, doing a # native compile. # # TODO(@Ericson2314): No native exception [[ -z ${crossConfig-} ]] || (( "$hostOffset" < 0 )) || return 0 # 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 `depHostOffset` describes how the host platform 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 $depHostOffset in -1) local role='BUILD_' ;; 0) local role='' ;; 1) local role='TARGET_' ;; *) echo "cc-wrapper: Error: Cannot be used with $depHostOffset-offset deps" >2; return 1 ;; esac if [[ -d "$1/include" ]]; then export NIX_${role}CFLAGS_COMPILE+=" ${ccIncludeFlag:--isystem} $1/include" fi if [[ -d "$1/Library/Frameworks" ]]; then 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. case $targetOffset in -1) export NIX_CC_WRAPPER_@infixSalt@_TARGET_BUILD=1 role_pre='BUILD_' role_post='_FOR_BUILD' ;; 0) export NIX_CC_WRAPPER_@infixSalt@_TARGET_HOST=1 role_pre='' role_post='' ;; 1) export NIX_CC_WRAPPER_@infixSalt@_TARGET_TARGET=1 role_pre='TARGET_' role_post='_FOR_TARGET' ;; *) echo "cc-wrapper: used as improper sort of dependency" >2; return 1 ;; esac # We use the `targetOffset` to choose the right env hook to accumulate the right # sort of deps (those with that offset). addEnvHooks "$targetOffset" ccWrapper_addCVars # Note 1: these come *after* $out in the PATH (see setup.sh). # Note 2: phase separation makes this look useless to shellcheck. # shellcheck disable=SC2157 if [ -n "@cc@" ]; then addToSearchPath _PATH @cc@/bin fi # shellcheck disable=SC2157 if [ -n "@libc_bin@" ]; then addToSearchPath _PATH @libc_bin@/bin fi # shellcheck disable=SC2157 if [ -n "@coreutils_bin@" ]; then addToSearchPath _PATH @coreutils_bin@/bin fi # Export tool environment variables so various build systems use the right ones. export NIX_${role_pre}CC=@out@ export ${role_pre}CC=@named_cc@ export ${role_pre}CXX=@named_cxx@ export CC${role_post}=@named_cc@ export CXX${role_post}=@named_cxx@ # No local scope in sourced file unset -v role_pre role_post set +u