Skip to content

Commit

Permalink
stdenvAdapters: add overrideSDK
Browse files Browse the repository at this point in the history
This is a replacement for using `darwin.apple_sdk_<ver>.callPackage`.
Instead of injecting the required packages, it provides a stdenv adapter
that modifies the derivation’s build inputs to use the requested SDK
versions. This modification extends to any build inputs propagated to it
as well. The `callPackage` approach is not deprecated yet, but it is
expected that it will be eventually.

Note that this is an MVP. It should work with most packages, but it only
handles build inputs and also only handles frameworks. Once more SDKs
are added (after NixOS#229210 is merged) and the SDK structure is normalized,
it can be extended to handle any package in the SDK namespace.

Cross-compilation may or may not work. Any cross-related issues can be
addressed after NixOS#256590 is merged.
  • Loading branch information
reckenrode committed Oct 26, 2023
1 parent 08be9a9 commit 646651f
Showing 1 changed file with 99 additions and 0 deletions.
99 changes: 99 additions & 0 deletions pkgs/stdenv/adapters.nix
Expand Up @@ -246,4 +246,103 @@ rec {
env = (args.env or {}) // { NIX_CFLAGS_COMPILE = toString (args.env.NIX_CFLAGS_COMPILE or "") + " ${toString compilerFlags}"; };
});
});

# Overriding the SDK changes the Darwin SDK used to build the package, which:
# * Ensures that the compiler and bintools have the correct Libsystem version; and
# * Replaces any SDK references with those in the SDK corresponding to requested SDK version.
#
# `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}";

isSDKFramework = pkg: lib.hasPrefix "apple-framework-" (lib.getName pkg);

replacePropagatedFrameworks = pkg:
let
propagatedFrameworks = lib.filter isSDKFramework pkg.propagatedBuildInputs;
env = {
inherit (pkg) outputs;
# Map the old frameworks to new and the package’s outputs to their original outPaths.
# The mappings are rendered into tab-separated files to be read back with `read`.
frameworks = lib.concatMapStrings (pkg: "${pkg}\t${mapPackageToSDK pkg}\n") propagatedFrameworks;
pkgOutputs = lib.concatMapStrings (output: "${output}\t${(lib.getOutput output pkg).outPath}\n") pkg.outputs;
passAsFile = [ "frameworks" "pkgOutputs" ];
};
in
if lib.length propagatedFrameworks > 0
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 < "$frameworksPath"
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 isSDKFramework pkg
then sdk.frameworks."${framework}"
else replacePropagatedFrameworks pkg;

mapInputsToSDK = inputs: args:
lib.genAttrs inputs (input: map mapPackageToSDK (args."${input}" or [ ]));

mkCC = cc: cc.override {
bintools = cc.bintools.override { libc = sdk.Libsystem; };
libc = sdk.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"
]);
});
}

0 comments on commit 646651f

Please sign in to comment.