Merge pull request #287609 from reckenrode/darwin-stdenv-improvements

darwin.stdenv: improvements and overrideSDK rewrite
This commit is contained in:
toonn 2024-03-29 18:30:26 +01:00 committed by GitHub
commit 81a44b6680
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 575 additions and 138 deletions

View File

@ -105,6 +105,8 @@ let
# conflicting LLVM modules.
objc4 = stdenv.objc4 or (callPackage ./libobjc.nix { });
sdkRoot = pkgs.callPackage ../apple-sdk/sdkRoot.nix { sdkVersion = "11.0"; };
# questionable aliases
configd = pkgs.darwin.apple_sdk.frameworks.SystemConfiguration;
inherit (pkgs.darwin.apple_sdk.frameworks) IOKit;

View File

@ -352,5 +352,11 @@ in rec {
inherit darwin-stubs;
objc4 = pkgs.darwin.libobjc;
sdkRoot = pkgs.callPackage ./sdkRoot.nix { sdkVersion = "10.12"; };
inherit (pkgs.darwin) Libsystem;
inherit sdk;
}

View File

@ -0,0 +1,68 @@
{
lib,
runCommand,
writeText,
sdkVersion,
}:
let
sdkName = "MacOSX${sdkVersion}";
toolchainName = "com.apple.dt.toolchain.XcodeDefault";
productBuildVer = null;
inherit (lib.generators) toPlist toJSON;
SDKSettings = {
CanonicalName = "macosx${sdkVersion}";
DisplayName = "macOS ${sdkVersion}";
Toolchains = [ toolchainName ];
Version = sdkVersion;
MaximumDeploymentTarget = "${sdkVersion}.99";
isBaseSDK = "YES";
};
SystemVersion =
lib.optionalAttrs (productBuildVer != null) { ProductBuildVersion = productBuildVer; }
// {
ProductName = "macOS";
ProductVersion = sdkVersion;
};
in
runCommand "sdkroot-${sdkVersion}" { } ''
sdk="$out/${sdkName}.sdk"
install -D ${writeText "SDKSettings.plist" (toPlist { } SDKSettings)} "$sdk/SDKSettings.plist"
install -D ${writeText "SDKSettings.json" (toJSON { } SDKSettings)} "$sdk/SDKSettings.json"
install -D ${
writeText "SystemVersion.plist" (toPlist { } SystemVersion)
} "$sdk/System/Library/CoreServices/SystemVersion.plist"
ln -s "$sdk" "$sdk/usr"
install -D '${../../../build-support/setup-hooks/role.bash}' "$out/nix-support/setup-hook"
cat >> "$out/nix-support/setup-hook" <<-hook
#
# See comments in cc-wrapper's setup hook. This works exactly the same way.
#
[[ -z \''${strictDeps-} ]] || (( "\$hostOffset" < 0 )) || return 0
sdkRootHook() {
# See ../../../build-support/setup-hooks/role.bash
local role_post
getHostRoleEnvHook
# Only set the SDK root if one has not been set via this hook or some other means.
if [[ ! \$NIX_CFLAGS_COMPILE =~ isysroot ]]; then
export NIX_CFLAGS_COMPILE\''${role_post}+=' -isysroot $out/${sdkName}.sdk'
fi
}
# See ../../../build-support/setup-hooks/role.bash
getTargetRole
addEnvHooks "\$targetOffset" sdkRootHook
# No local scope in sourced file
unset -v role_post
hook
''

View File

@ -317,125 +317,17 @@ rec {
# `sdkVersion` can be any of the following:
# * A version string indicating the requested SDK version; or
# * An attrset consisting of either or both of the following fields: darwinSdkVersion and darwinMinVersion.
overrideSDK = stdenv: sdkVersion:
let
inherit (
{ inherit (stdenv.hostPlatform) darwinMinVersion darwinSdkVersion; }
// (if lib.isAttrs sdkVersion then sdkVersion else { darwinSdkVersion = sdkVersion; })
) darwinMinVersion darwinSdkVersion;
sdk = pkgs.darwin."apple_sdk_${lib.replaceStrings [ "." ] [ "_" ] darwinSdkVersion}";
# TODO: Make this unconditional after #229210 has been merged,
# and the 10.12 SDK is updated to follow the new structure.
Libsystem = if darwinSdkVersion == "10.12" then pkgs.darwin.Libsystem else sdk.Libsystem;
replacePropagatedFrameworks = pkg:
let
propagatedInputs = pkg.propagatedBuildInputs;
mappedInputs = map mapPackageToSDK propagatedInputs;
env = {
inherit (pkg) outputs;
# Map old frameworks to new ones and the packages outputs to their original outPaths.
# Also map any packages that have propagated frameworks to their proxy packages using
# the requested SDK version. These mappings are rendered into tab-separated files to be
# parsed and read back with `read`.
dependencies = lib.concatMapStrings (pair: "${pair.fst}\t${pair.snd}\n") (lib.zipLists propagatedInputs mappedInputs);
pkgOutputs = lib.concatMapStrings (output: "${output}\t${(lib.getOutput output pkg).outPath}\n") pkg.outputs;
passAsFile = [ "dependencies" "pkgOutputs" ];
};
in
# Only remap the packages propagated inputs if there are any and if any of them were themselves remapped.
if lib.length propagatedInputs > 0 && propagatedInputs != mappedInputs
then pkgs.runCommand pkg.name env ''
# Iterate over the outputs in the package being replaced to make sure the proxy is
# a fully functional replacement. This is like `symlinkJoin` except for outputs and
# the contents of `nix-support`, which will be customized for the requested SDK.
while IFS=$'\t\n' read -r outputName pkgOutputPath; do
mkdir -p "''${!outputName}"
for targetPath in "$pkgOutputPath"/*; do
targetName=$(basename "$targetPath")
# `nix-support` is special-cased because any propagated inputs need their SDK
# frameworks replaced with those from the requested SDK.
if [ "$targetName" == "nix-support" ]; then
mkdir "''${!outputName}/nix-support"
for file in "$targetPath"/*; do
fileName=$(basename "$file")
if [ "$fileName" == "propagated-build-inputs" ]; then
cp "$file" "''${!outputName}/nix-support/$fileName"
while IFS=$'\t\n' read -r oldFramework newFramework; do
substituteInPlace "''${!outputName}/nix-support/$fileName" \
--replace "$oldFramework" "$newFramework"
done < "$dependenciesPath"
fi
done
else
ln -s "$targetPath" "''${!outputName}/$targetName"
fi
done
done < "$pkgOutputsPath"
''
else pkg;
# Remap a framework from one SDK version to another.
mapPackageToSDK = pkg:
let
name = lib.getName pkg;
framework = lib.removePrefix "apple-framework-" name;
in
/**/ if pkg == null then pkg
else if name != framework then sdk.frameworks."${framework}"
else replacePropagatedFrameworks pkg;
mapRuntimeToSDK = pkg:
# Only remap xcbuild for now, which exports the SDK used to build it.
if pkg != null && lib.isAttrs pkg && lib.getName pkg == "xcodebuild"
then pkg.override { stdenv = overrideSDK stdenv { inherit darwinMinVersion darwinSdkVersion; }; }
else pkg;
mapInputsToSDK = inputs: args:
let
runsAtBuild = lib.flip lib.elem [
"depsBuildBuild"
"depsBuildBuildPropagated"
"nativeBuildInputs"
"propagatedNativeBuildInputs"
"depsBuildTarget"
"depsBuildTargetPropagated"
];
atBuildInputs = lib.filter runsAtBuild inputs;
atRuntimeInputs = lib.subtractLists atBuildInputs inputs;
in
lib.genAttrs atRuntimeInputs (input: map mapPackageToSDK (args."${input}" or [ ]))
// lib.genAttrs atBuildInputs (input: map mapRuntimeToSDK (args."${input}" or [ ]));
mkCC = cc: cc.override {
bintools = cc.bintools.override { libc = Libsystem; };
libc = Libsystem;
};
in
# TODO: make this work across all input types and not just propagatedBuildInputs
stdenv.override (old: {
buildPlatform = old.buildPlatform // { inherit darwinMinVersion darwinSdkVersion; };
hostPlatform = old.hostPlatform // { inherit darwinMinVersion darwinSdkVersion; };
targetPlatform = old.targetPlatform // { inherit darwinMinVersion darwinSdkVersion; };
allowedRequisites = null;
cc = mkCC old.cc;
extraBuildInputs = [sdk.frameworks.CoreFoundation ];
mkDerivationFromStdenv = extendMkDerivationArgs old (mapInputsToSDK [
"buildInputs"
"nativeBuildInputs"
"propagatedNativeBuildInputs"
"propagatedBuildInputs"
]);
});
overrideSDK = import ./darwin/override-sdk.nix {
inherit lib extendMkDerivationArgs;
inherit (pkgs)
stdenvNoCC
pkgsBuildBuild
pkgsBuildHost
pkgsBuildTarget
pkgsHostHost
pkgsHostTarget
pkgsTargetTarget;
};
withDefaultHardeningFlags = defaultHardeningFlags: stdenv: let
bintools = let

View File

@ -135,9 +135,11 @@ let
hostPlatform = localSystem;
targetPlatform = localSystem;
inherit config extraNativeBuildInputs;
inherit config;
extraBuildInputs = [ prevStage.darwin.CF ];
extraNativeBuildInputs = extraNativeBuildInputs
++ [ prevStage.darwin.apple_sdk.sdkRoot ];
preHook = lib.optionalString (!isBuiltByNixpkgsCompiler bash) ''
# Don't patch #!/interpreter because it leads to retained
@ -196,6 +198,7 @@ in
cpio = null;
darwin = {
apple_sdk.sdkRoot = null;
binutils = null;
binutils-unwrapped = null;
cctools = null;
@ -244,7 +247,7 @@ in
version = "boot";
};
binutils = (import ../../build-support/bintools-wrapper) {
binutils = super.wrapBintoolsWith {
name = "bootstrap-stage0-binutils-wrapper";
nativeTools = false;
@ -435,6 +438,10 @@ in
});
darwin = super.darwin.overrideScope (selfDarwin: superDarwin: {
apple_sdk = superDarwin.apple_sdk // {
inherit (prevStage.darwin.apple_sdk) sdkRoot;
};
# Use this stages CF to build configd. Its required but cant be included in the stdenv.
configd = superDarwin.configd.overrideAttrs (old: {
buildInputs = old.buildInputs or [ ] ++ [ self.darwin.CF ];
@ -500,7 +507,7 @@ in
assert lib.all isFromBootstrapFiles (with prevStage; [ coreutils gnugrep ]);
assert lib.all isBuiltByBootstrapFilesCompiler (with prevStage; [
autoconf automake bash binutils-unwrapped bison brotli cmake cpio curl cyrus_sasl db
autoconf automake bash binutils-unwrapped bison brotli cmake cpio cyrus_sasl db
ed expat flex gettext gmp groff icu libedit libffi libiconv libidn2 libkrb5 libssh2
libtool libunistring libxml2 m4 ncurses nghttp2 ninja openldap openssh openssl
patchutils pbzx perl pkg-config.pkg-config python3 python3Minimal scons serf sqlite
@ -527,7 +534,7 @@ in
overrides = self: super: {
inherit (prevStage) ccWrapperStdenv
autoconf automake bash binutils binutils-unwrapped bison brotli cmake cmakeMinimal
coreutils cpio curl cyrus_sasl db ed expat flex gettext gmp gnugrep groff icu
coreutils cpio cyrus_sasl db ed expat flex gettext gmp gnugrep groff icu
libedit libffi libiconv libidn2 libkrb5 libssh2 libtool libunistring libxml2 m4
ncurses nghttp2 ninja openldap openssh openssl patchutils pbzx perl pkg-config
python3Minimal scons sed serf sharutils sqlite subversion texinfo unzip which xz
@ -558,9 +565,13 @@ in
darwin = super.darwin.overrideScope (_: superDarwin: {
inherit (prevStage.darwin)
CF Libsystem binutils-unwrapped cctools cctools-port configd darwin-stubs dyld
CF sdkRoot Libsystem binutils-unwrapped cctools cctools-port configd darwin-stubs dyld
launchd libclosure libdispatch libobjc locale objc4 postLinkSignHook
print-reexports rewrite-tbd signingUtils sigtool;
apple_sdk = superDarwin.apple_sdk // {
inherit (prevStage.darwin.apple_sdk) sdkRoot;
};
});
llvmPackages = super.llvmPackages // (
@ -599,7 +610,7 @@ in
assert lib.all isFromBootstrapFiles (with prevStage; [ coreutils gnugrep ]);
assert lib.all isBuiltByBootstrapFilesCompiler (with prevStage; [
autoconf automake bash binutils-unwrapped bison brotli cmake cpio curl cyrus_sasl db
autoconf automake bash binutils-unwrapped bison brotli cmake cpio cyrus_sasl db
ed expat flex gettext gmp groff icu libedit libffi libiconv libidn2 libkrb5 libssh2
libtool libunistring libxml2 m4 ncurses nghttp2 ninja openldap openssh openssl
patchutils pbzx perl pkg-config.pkg-config python3 python3Minimal scons serf sqlite
@ -627,7 +638,7 @@ in
overrides = self: super: {
inherit (prevStage) ccWrapperStdenv
autoconf automake bash binutils binutils-unwrapped bison brotli cmake cmakeMinimal
cpio curl cyrus_sasl db ed expat flex gettext gmp groff icu libedit libffi libiconv
cpio cyrus_sasl db ed expat flex gettext gmp groff icu libedit libffi libiconv
libidn2 libkrb5 libssh2 libtool libunistring libxml2 m4 ncurses nghttp2 ninja
openldap openssh openssl patchutils pbzx perl pkg-config python3 python3Minimal
scons sed serf sharutils sqlite subversion sysctl texinfo unzip which xz zlib zstd;
@ -638,6 +649,10 @@ in
CF Libsystem configd darwin-stubs dyld launchd libclosure libdispatch libobjc
locale objc4 postLinkSignHook print-reexports rewrite-tbd signingUtils sigtool;
apple_sdk = superDarwin.apple_sdk // {
inherit (prevStage.darwin.apple_sdk) sdkRoot;
};
# Avoid building unnecessary Python dependencies due to building LLVM manpages.
cctools-llvm = superDarwin.cctools-llvm.override { enableManpages = false; };
});
@ -691,7 +706,7 @@ in
]);
assert lib.all isBuiltByBootstrapFilesCompiler (with prevStage; [
brotli curl libffi libiconv libidn2 libkrb5 libssh2 libunistring libxml2 ncurses
brotli libffi libiconv libidn2 libkrb5 libssh2 libunistring libxml2 ncurses
nghttp2 openssl zlib zstd
]);
@ -719,7 +734,7 @@ in
overrides = self: super: {
inherit (prevStage) ccWrapperStdenv
autoconf automake binutils-unwrapped bison brotli cmake cmakeMinimal coreutils
cpio curl cyrus_sasl db ed expat flex gettext gmp gnugrep groff icu libedit libffi
cpio cyrus_sasl db ed expat flex gettext gmp gnugrep groff icu libedit libffi
libiconv libidn2 libkrb5 libssh2 libtool libunistring libxml2 m4 ncurses nghttp2
ninja openbsm openldap openpam openssh openssl patchutils pbzx perl pkg-config
python3 python3Minimal scons serf sqlite subversion sysctl texinfo unzip which xz
@ -735,6 +750,10 @@ in
inherit (prevStage.darwin)
CF binutils-unwrapped cctools configd darwin-stubs launchd libobjc libtapi locale
objc4 print-reexports rewrite-tbd signingUtils sigtool;
apple_sdk = superDarwin.apple_sdk // {
inherit (prevStage.darwin.apple_sdk) sdkRoot;
};
});
llvmPackages = super.llvmPackages // (
@ -773,7 +792,7 @@ in
'';
})
# This stage rebuilds CF and compiler-rt.
# This stage rebuilds CF, compiler-rt, and the sdkRoot derivation.
#
# CF requires:
# - aarch64-darwin: libobjc (due to being apple_sdk.frameworks.CoreFoundation instead of swift-corefoundation)
@ -782,7 +801,7 @@ in
# previous stage2-Libsystem stdenv:
assert lib.all isBuiltByBootstrapFilesCompiler (with prevStage; [
autoconf automake binutils-unwrapped bison brotli cmake cmakeMinimal coreutils
cpio curl cyrus_sasl db ed expat flex gettext gmp gnugrep groff icu libedit libidn2
cpio cyrus_sasl db ed expat flex gettext gmp gnugrep groff icu libedit libidn2
libkrb5 libssh2 libtool libunistring m4 nghttp2 ninja openbsm openldap openpam openssh
openssl patchutils pbzx perl pkg-config.pkg-config python3 python3Minimal scons serf
sqlite subversion sysctl.provider texinfo unzip which xz zstd
@ -818,7 +837,7 @@ in
overrides = self: super: {
inherit (prevStage) ccWrapperStdenv
autoconf automake bash bison brotli cmake cmakeMinimal coreutils cpio curl
autoconf automake bash bison brotli cmake cmakeMinimal coreutils cpio
cyrus_sasl db ed expat flex gettext gmp gnugrep groff libedit libidn2 libkrb5
libssh2 libtool libunistring m4 ncurses nghttp2 ninja openbsm openldap openpam
openssh openssl patchutils pbzx perl pkg-config python3 python3Minimal scons serf
@ -905,7 +924,7 @@ in
(prevStage:
# previous stage2-CF stdenv:
assert lib.all isBuiltByBootstrapFilesCompiler (with prevStage; [
autoconf automake bison brotli cmake cmakeMinimal coreutils cpio curl cyrus_sasl
autoconf automake bison brotli cmake cmakeMinimal coreutils cpio cyrus_sasl
db ed expat flex gettext gmp gnugrep groff libedit libidn2 libkrb5 libssh2 libtool
libunistring m4 ncurses nghttp2 ninja openbsm openldap openpam openssh openssl
patchutils pbzx perl pkg-config.pkg-config python3 python3Minimal scons serf sqlite
@ -942,7 +961,7 @@ in
overrides = self: super: {
inherit (prevStage) ccWrapperStdenv
autoconf automake bash binutils binutils-unwrapped bison brotli cmake cmakeMinimal
coreutils cpio curl cyrus_sasl db ed expat flex gettext gmp gnugrep groff libedit
coreutils cpio cyrus_sasl db ed expat flex gettext gmp gnugrep groff libedit
libidn2 libkrb5 libssh2 libtool libunistring m4 nghttp2 ninja openbsm openldap
openpam openssh openssl patchutils pbzx perl pkg-config python3 python3Minimal scons
sed serf sharutils sqlite subversion sysctl texinfo unzip which xz zstd
@ -958,6 +977,10 @@ in
CF Libsystem binutils binutils-unwrapped cctools cctools-llvm cctools-port configd
darwin-stubs dyld launchd libclosure libdispatch libobjc libtapi locale objc4
postLinkSignHook print-reexports rewrite-tbd signingUtils sigtool;
apple_sdk = superDarwin.apple_sdk // {
inherit (prevStage.darwin.apple_sdk) sdkRoot;
};
});
llvmPackages = super.llvmPackages // (
@ -985,7 +1008,7 @@ in
(prevStage:
# previous stage3 stdenv:
assert lib.all isBuiltByBootstrapFilesCompiler (with prevStage; [
autoconf automake bison brotli cmake cmakeMinimal coreutils cpio curl cyrus_sasl
autoconf automake bison brotli cmake cmakeMinimal coreutils cpio cyrus_sasl
db ed expat flex gettext gmp gnugrep groff libedit libidn2 libkrb5 libssh2 libtool
libunistring m4 nghttp2 ninja openbsm openldap openpam openssh openssl patchutils pbzx
perl pkg-config.pkg-config python3 python3Minimal scons serf sqlite subversion
@ -1036,6 +1059,10 @@ in
# CF dependencies - dont rebuild them.
libobjc objc4;
apple_sdk = superDarwin.apple_sdk // {
inherit (prevStage.darwin.apple_sdk) sdkRoot;
};
signingUtils = superDarwin.signingUtils.override {
inherit (selfDarwin) sigtool;
};
@ -1153,7 +1180,7 @@ in
(prevStage:
# previous stage4 stdenv:
assert lib.all isBuiltByNixpkgsCompiler (with prevStage; [
bash binutils-unwrapped brotli bzip2 cpio curl diffutils ed file findutils gawk
bash binutils-unwrapped brotli bzip2 cpio diffutils ed file findutils gawk
gettext gmp gnugrep gnumake gnused gnutar gzip icu libffi libiconv libidn2 libkrb5
libssh2 libunistring libxml2 libyaml ncurses nghttp2 openbsm openpam openssl patch
pbzx pcre python3Minimal xar xz zlib zstd
@ -1206,7 +1233,7 @@ in
extraNativeBuildInputs = lib.optionals localSystem.isAarch64 [
prevStage.updateAutotoolsGnuConfigScriptsHook
];
] ++ [ prevStage.darwin.apple_sdk.sdkRoot ];
extraBuildInputs = [ prevStage.darwin.CF ];
@ -1293,6 +1320,7 @@ in
dyld
libtapi
locale
apple_sdk.sdkRoot
]
++ lib.optional useAppleSDKLibs [ objc4 ]
++ lib.optionals doSign [ postLinkSignHook sigtool signingUtils ]);
@ -1302,14 +1330,18 @@ in
overrides = self: super: {
inherit (prevStage)
bash binutils brotli bzip2 coreutils cpio curl diffutils ed file findutils gawk
bash binutils brotli bzip2 coreutils cpio diffutils ed file findutils gawk
gettext gmp gnugrep gnumake gnused gnutar gzip icu libffi libiconv libidn2 libssh2
libunistring libxml2 libyaml ncurses nghttp2 openbsm openpam openssl patch pbzx
pcre python3Minimal xar xz zlib zstd;
darwin = super.darwin.overrideScope (_: _: {
darwin = super.darwin.overrideScope (_: superDarwin: {
inherit (prevStage.darwin)
CF ICU Libsystem darwin-stubs dyld locale libobjc libtapi rewrite-tbd xnu;
apple_sdk = superDarwin.apple_sdk // {
inherit (prevStage.darwin.apple_sdk) sdkRoot;
};
} // lib.optionalAttrs (super.stdenv.targetPlatform == localSystem) {
inherit (prevStage.darwin) binutils binutils-unwrapped cctools-llvm cctools-port;
});

View File

@ -0,0 +1,437 @@
# The basic algorithm is to rewrite the propagated inputs of a package and any of its
# own propagated inputs recursively to replace references from the default SDK with
# those from the requested SDK version. This is done across all propagated inputs to
# avoid making assumptions about how those inputs are being used.
#
# For example, packages may propagate target-target dependencies with the expectation that
# they will be just build inputs when the package itself is used as a native build input.
#
# To support this use case and operate without regard to the original package set,
# `overrideSDK` creates a mapping from the default SDK in all package categories to the
# requested SDK. If the lookup fails, it is assumed the package is not part of the SDK.
# Non-SDK packages are processed per the algorithm described above.
{
lib,
stdenvNoCC,
extendMkDerivationArgs,
pkgsBuildBuild,
pkgsBuildHost,
pkgsBuildTarget,
pkgsHostHost,
pkgsHostTarget,
pkgsTargetTarget,
}@args:
let
# Takes a mapping from a package to its replacement and transforms it into a list of
# mappings by output (e.g., a package with three outputs produces a list of size 3).
expandOutputs =
mapping:
map (output: {
name = builtins.unsafeDiscardStringContext (lib.getOutput output mapping.original);
value = lib.getOutput output mapping.replacement;
}) mapping.original.outputs;
# Produces a list of mappings from the default SDK to the new SDK (`sdk`).
# `attr` indicates which SDK path to remap (e.g., `libs` remaps `apple_sdk.libs`).
#
# TODO: Update once the SDKs have been refactored to a common pattern to better handle
# frameworks that are not present in both SDKs. Currently, theyre dropped.
mkMapping =
attr: pkgs: sdk:
lib.foldlAttrs (
mappings: name: pkg:
let
# Avoid evaluation failures due to missing or throwing
# frameworks (such as QuickTime in the 11.0 SDK).
maybeReplacement = builtins.tryEval sdk.${attr}.${name} or { success = false; };
in
if maybeReplacement.success then
mappings
++ expandOutputs {
original = pkg;
replacement = maybeReplacement.value;
}
else
mappings
) [ ] pkgs.darwin.apple_sdk.${attr};
# Produces a list of overrides for the given package set, SDK, and version.
# If you want to manually specify a mapping, this is where you should do it.
mkOverrides =
pkgs: sdk: version:
lib.concatMap expandOutputs [
# Libsystem needs to match the one used by the SDK or weird errors happen.
{
original = pkgs.darwin.apple_sdk.Libsystem;
replacement = sdk.Libsystem;
}
# Make sure darwin.CF is mapped to the correct version for the SDK.
{
original = pkgs.darwin.CF;
replacement = sdk.frameworks.CoreFoundation;
}
# libobjc needs to be handled specially because its not actually in the SDK.
{
original = pkgs.darwin.libobjc;
replacement = sdk.objc4;
}
# Unfortunately, this is not consistent between Darwin SDKs in nixpkgs, so
# try both versions to map between them.
{
original = pkgs.darwin.apple_sdk.sdk or pkgs.darwin.apple_sdk.MacOSX-SDK;
replacement = sdk.sdk or sdk.MacOSX-SDK;
}
# Remap the SDK root. This is used by clang to set the SDK version when
# linking. This behavior is automatic by clang and cant be overriden.
# Otherwise, without the SDK root set, the SDK version will be inferred to
# be the same as the deployment target, which is not usually what you want.
{
original = pkgs.darwin.apple_sdk.sdkRoot;
replacement = sdk.sdkRoot;
}
# Override xcodebuild because it hardcodes the SDK version.
# TODO: Make xcodebuild defer to the SDK root set in the stdenv.
{
original = pkgs.xcodebuild;
replacement = pkgs.xcodebuild.override {
# Do the override manually to avoid an infinite recursion.
stdenv = pkgs.stdenv.override (old: {
buildPlatform = mkPlatform version old.buildPlatform;
hostPlatform = mkPlatform version old.hostPlatform;
targetPlatform = mkPlatform version old.targetPlatform;
allowedRequisites = null;
cc = mkCC sdk.Libsystem old.cc;
});
};
}
];
mkBintools =
Libsystem: bintools:
if bintools ? override then
bintools.override { libc = Libsystem; }
else
let
# `override` isnt available, so bintools has to be rewrapped with the new libc.
# Most of the required arguments can be recovered except for `postLinkSignHook`
# and `signingUtils`, which have to be scrapped from the originals `postFixup`.
# This isnt ideal, but it works.
postFixup = lib.splitString "\n" bintools.postFixup;
postLinkSignHook = lib.pipe postFixup [
(lib.findFirst (lib.hasPrefix "echo 'source") null)
(builtins.match "^echo 'source (.*-post-link-sign-hook)' >> \\$out/nix-support/post-link-hook$")
lib.head
];
signingUtils = lib.pipe postFixup [
(lib.findFirst (lib.hasPrefix "export signingUtils") null)
(builtins.match "^export signingUtils=(.*)$")
lib.head
];
newBintools = pkgsBuildTarget.wrapBintoolsWith {
inherit (bintools) name;
buildPackages = { };
libc = Libsystem;
inherit lib;
coreutils = bintools.coreutils_bin;
gnugrep = bintools.gnugrep_bin;
inherit (bintools) bintools;
inherit postLinkSignHook signingUtils;
};
in
lib.getOutput bintools.outputName newBintools;
mkCC =
Libsystem: cc:
if cc ? override then
cc.override {
bintools = mkBintools Libsystem cc.bintools;
libc = Libsystem;
}
else
builtins.throw "CC has no override: ${cc}";
mkPlatform =
version: platform:
platform
// lib.optionalAttrs platform.isDarwin { inherit (version) darwinMinVersion darwinSdkVersion; };
# Creates a stub package. Unchanged files from the original package are symlinked
# into the package. The contents of `nix-support` are updated to reference any
# replaced packages.
#
# Note: `env` is an attrset containing `outputs` and `dependencies`.
# `dependencies` is a regex passed to sed and must be `passAsFile`.
mkProxyPackage =
name: env:
stdenvNoCC.mkDerivation {
inherit name;
inherit (env) outputs replacements sourceOutputs;
# Take advantage of the fact that replacements and sourceOutputs will be passed
# via JSON and parsed into environment variables.
__structuredAttrs = true;
buildCommand = ''
# Map over the outputs in the package being replaced to make sure the proxy is
# a fully functional replacement. This is like `symlinkJoin` except for
# outputs and the contents of `nix-support`, which will be customized.
function replacePropagatedInputs() {
local sourcePath=$1
local targetPath=$2
mkdir -p "$targetPath"
local sourceFile
for sourceFile in "$sourcePath"/*; do
local fileName=$(basename "$sourceFile")
local targetFile="$targetPath/$fileName"
if [ -d "$sourceFile" ]; then
replacePropagatedInputs "$sourceFile" "$targetPath/$fileName"
# Check to see if any of the files in the folder were replaced.
# Otherwise, replace the folder with a symlink if none were changed.
if [ "$(find -maxdepth 1 "$targetPath/$fileName" -not -type l)" = "" ]; then
rm "$targetPath/$fileName"/*
ln -s "$sourceFile" "$targetPath/$fileName"
fi
else
cp "$sourceFile" "$targetFile"
local original
for original in "''${!replacements[@]}"; do
substituteInPlace "$targetFile" \
--replace-quiet "$original" "''${replacements[$original]}"
done
if cmp -s "$sourceFile" "$targetFile"; then
rm "$targetFile"
ln -s "$sourceFile" "$targetFile"
fi
fi
done
}
local outputName
for outputName in "''${!outputs[@]}"; do
local outPath=''${outputs[$outputName]}
mkdir -p "$outPath"
local sourcePath
for sourcePath in "''${sourceOutputs[$outputName]}"/*; do
sourceName=$(basename "$sourcePath")
# `nix-support` is special-cased because any propagated inputs need their
# SDK frameworks replaced with those from the requested SDK.
if [ "$sourceName" == "nix-support" ]; then
replacePropagatedInputs "$sourcePath" "$outPath/nix-support"
else
ln -s "$sourcePath" "$outPath/$sourceName"
fi
done
done
'';
};
# Gets all propagated inputs in a package. This does not recurse.
getPropagatedInputs =
pkg:
lib.optionals (lib.isDerivation pkg) (
lib.concatMap (input: pkg.${input} or [ ]) [
"depsBuildBuildPropagated"
"propagatedNativeBuildInputs"
"depsBuildTargetPropagated"
"depsHostHostPropagated"
"propagatedBuildInputs"
"depsTargetTargetPropagated"
]
);
# Looks up the replacement for `pkg` in the `newPackages` mapping. If `pkg` is a
# compiler (meaning it has a `libc` attribute), the compiler will be overriden.
getReplacement =
newPackages: pkg:
let
pkgOrCC =
if pkg.libc or null != null then
# Heuristic to determine whether package is a compiler or bintools.
if pkg.wrapperName == "CC_WRAPPER" then
mkCC (getReplacement newPackages pkg.libc) pkg
else
mkBintools (getReplacement newPackages pkg.libc) pkg
else
pkg;
in
if lib.isDerivation pkg then
newPackages.${builtins.unsafeDiscardStringContext pkg} or pkgOrCC
else
pkg;
# Replaces all packages propagated by `pkgs` using the `newPackages` mapping.
# It is assumed that all possible overrides have already been incorporated into
# the mapping. If any propagated packages are replaced, a proxy package will be
# created with references to the old packages replaced in `nix-support`.
replacePropagatedPackages =
newPackages: pkg:
let
propagatedInputs = getPropagatedInputs pkg;
env = {
inherit (pkg) outputs;
replacements = lib.pipe propagatedInputs [
(lib.filter (pkg: pkg != null))
(map (dep: {
name = builtins.unsafeDiscardStringContext dep;
value = getReplacement newPackages dep;
}))
(lib.filter (mapping: mapping.name != mapping.value))
lib.listToAttrs
];
sourceOutputs = lib.genAttrs pkg.outputs (output: lib.getOutput output pkg);
};
in
# Only remap the packages propagated inputs if there are any and if any of them
# had packages remapped (with frameworks or proxy packages).
if propagatedInputs != [ ] && env.replacements != { } then mkProxyPackage pkg.name env else pkg;
# Gets all propagated dependencies in a package in reverse order sorted topologically.
# This takes advantage of the fact that items produced by `operator` are pushed to
# the end of the working set, ensuring that dependencies always appear after their
# parent in the list with leaf nodes at the end.
topologicallyOrderedPropagatedDependencies =
pkgs:
let
mapPackageDeps = lib.flip lib.pipe [
(lib.filter (pkg: pkg != null))
(map (pkg: {
key = builtins.unsafeDiscardStringContext pkg;
package = pkg;
deps = getPropagatedInputs pkg;
}))
];
in
lib.genericClosure {
startSet = mapPackageDeps pkgs;
operator = { deps, ... }: mapPackageDeps deps;
};
# Returns a package mapping based on remapping all propagated packages.
getPackageMapping =
baseMapping: input:
let
dependencies = topologicallyOrderedPropagatedDependencies input;
in
lib.foldr (
pkg: newPackages:
let
replacement = replacePropagatedPackages newPackages pkg.package;
outPath = pkg.key;
in
if pkg.key == null || newPackages ? ${outPath} then
newPackages
else
newPackages // { ${outPath} = replacement; }
) baseMapping dependencies;
overrideSDK =
stdenv: sdkVersion:
let
newVersion = {
inherit (stdenv.hostPlatform) darwinMinVersion darwinSdkVersion;
} // (if lib.isAttrs sdkVersion then sdkVersion else { darwinSdkVersion = sdkVersion; });
inherit (newVersion) darwinMinVersion darwinSdkVersion;
# Used to get an SDK version corresponding to the requested `darwinSdkVersion`.
# TODO: Treat `darwinSdkVersion` as a constraint rather than as an exact version.
resolveSDK = pkgs: pkgs.darwin."apple_sdk_${lib.replaceStrings [ "." ] [ "_" ] darwinSdkVersion}";
# `newSdkPackages` is constructed based on the assumption that SDK packages only
# propagate versioned packages from that SDK -- that they neither propagate
# unversioned SDK packages nor propagate non-SDK packages (such as curl).
#
# Note: `builtins.unsafeDiscardStringContext` is used to allow the path from the
# original package output to be mapped to the replacement. This is safe because
# the value is not persisted anywhere and necessary because store paths are not
# allowed as attrset names otherwise.
baseSdkMapping = lib.pipe args [
(lib.flip removeAttrs [
"lib"
"stdenvNoCC"
"extendMkDerivationArgs"
])
(lib.filterAttrs (_: lib.hasAttr "darwin"))
lib.attrValues
(lib.concatMap (
pkgs:
let
newSDK = resolveSDK pkgs;
frameworks = mkMapping "frameworks" pkgs newSDK;
libs = mkMapping "libs" pkgs newSDK;
overrides = mkOverrides pkgs newSDK newVersion;
in
frameworks ++ libs ++ overrides
))
lib.listToAttrs
];
# Remaps all inputs given to the requested SDK version. The result is an attrset
# that can be passed to `extendMkDerivationArgs`.
mapInputsToSDK =
inputs: args:
lib.pipe inputs [
(lib.filter (input: args ? ${input}))
(lib.flip lib.genAttrs (
inputName:
let
input = args.${inputName};
newPackages = getPackageMapping baseSdkMapping input;
in
map (getReplacement newPackages) input
))
];
in
stdenv.override (
old:
{
buildPlatform = mkPlatform newVersion old.buildPlatform;
hostPlatform = mkPlatform newVersion old.hostPlatform;
targetPlatform = mkPlatform newVersion old.targetPlatform;
}
# Only perform replacements if the SDK version has changed. Changing only the
# deployment target does not require replacing the libc or SDK dependencies.
// lib.optionalAttrs (old.hostPlatform.darwinSdkVersion != darwinSdkVersion) {
allowedRequisites = null;
mkDerivationFromStdenv = extendMkDerivationArgs old (mapInputsToSDK [
"depsBuildBuild"
"nativeBuildInputs"
"depsBuildTarget"
"depsHostHost"
"buildInputs"
"depsTargetTarget"
"depsBuildBuildPropagated"
"propagatedNativeBuildInputs"
"depsBuildTargetPropagated"
"depsHostHostPropagated"
"propagatedBuildInputs"
"depsTargetTargetPropagated"
]);
cc = getReplacement baseSdkMapping old.cc;
extraBuildInputs = map (getReplacement baseSdkMapping) stdenv.extraBuildInputs;
extraNativeBuildInputs = map (getReplacement baseSdkMapping) stdenv.extraNativeBuildInputs;
}
);
in
overrideSDK