Support isolated builds with a combined cache

This commit is contained in:
Stéphan Kochen
2024-02-11 13:27:49 +01:00
parent ab15ad6804
commit 56e3459543
4 changed files with 41 additions and 24 deletions
+7 -1
View File
@@ -91,7 +91,6 @@ jobs:
- name: Test isolated builds - name: Test isolated builds
run: | run: |
# Matches example in ISOLATED_BUILDS.md # Matches example in ISOLATED_BUILDS.md
echo 'individualNixPackaging: true' >> .yarnrc.yml
echo 'isolatedNixBuilds: ["sqlite3"]' >> .yarnrc.yml echo 'isolatedNixBuilds: ["sqlite3"]' >> .yarnrc.yml
cat > default.nix << EOF cat > default.nix << EOF
{ pkgs ? import <nixpkgs> { } }: { pkgs ? import <nixpkgs> { } }:
@@ -107,6 +106,13 @@ jobs:
yarn add sqlite3 yarn add sqlite3
nix-build
- name: Test individual packaging
run: |
echo 'individualNixPackaging: true' >> .yarnrc.yml
yarn
# This delete tests refetching a package with Nix. # This delete tests refetching a package with Nix.
nix-store --delete /nix/store/*--yarnpkg-core-npm-* nix-store --delete /nix/store/*--yarnpkg-core-npm-*
-3
View File
@@ -9,12 +9,9 @@ As an example, to create an isolated build of sqlite3, add the following to
your `.yarnrc.yml`: your `.yarnrc.yml`:
```yml ```yml
individualNixPackaging: true
isolatedNixBuilds: ["sqlite3"] isolatedNixBuilds: ["sqlite3"]
``` ```
(`individualNixPackaging` is required to use `isolatedNixBuilds`.)
In your Nix expression, separate options can be set to override attributes of In your Nix expression, separate options can be set to override attributes of
these derivations, which is often necessary to provide build inputs. For these derivations, which is often necessary to provide build inputs. For
sqlite3, you'd do the following in your `default.nix`: sqlite3, you'd do the following in your `default.nix`:
+22 -17
View File
@@ -52,13 +52,6 @@ export default async (
return; return;
} }
// Config validation.
const isolatedBuilds = configuration.get(`isolatedNixBuilds`);
const individualDrvs = configuration.get(`individualNixPackaging`);
if (isolatedBuilds.length > 0 && !individualDrvs) {
throw Error(`isolatedNixBuilds requires individualNixPackaging to be set`);
}
// Determine relative paths for Nix path literals. // Determine relative paths for Nix path literals.
const nixExprPath = configuration.get(`nixExprPath`); const nixExprPath = configuration.get(`nixExprPath`);
@@ -157,6 +150,7 @@ export default async (
hash: string; hash: string;
} }
const cacheEntries = new Map<string, CacheEntry>(); const cacheEntries = new Map<string, CacheEntry>();
const individualDrvs = configuration.get(`individualNixPackaging`);
let cacheEntriesCode = ""; let cacheEntriesCode = "";
let combinedHash = ""; let combinedHash = "";
if (individualDrvs) { if (individualDrvs) {
@@ -217,6 +211,7 @@ export default async (
} }
// Generate Nix code for isolated builds. // Generate Nix code for isolated builds.
const isolatedBuilds = configuration.get(`isolatedNixBuilds`);
let isolatedPackages = new Set<Package>(); let isolatedPackages = new Set<Package>();
let isolatedIntegration = []; let isolatedIntegration = [];
let isolatedCode = []; let isolatedCode = [];
@@ -332,19 +327,29 @@ export default async (
if (!isolatedPackages.has(devirtPkg)) { if (!isolatedPackages.has(devirtPkg)) {
isolatedPackages.add(devirtPkg); isolatedPackages.add(devirtPkg);
const locators = [...collectTree(pkg)] const args = [
.sort() `pname = ${json(pkg.name)};`,
.map((v) => `${json(v)}\n`) `version = ${json(pkg.version)};`,
.join(``); `reference = ${json(devirtPkg.reference)};`,
];
// If packaging deps individually, depend on just
// the deps used during this isolated build.
if (individualDrvs) {
const locators = [...collectTree(pkg)]
.sort()
.map((v) => `${json(v)}\n`)
.join(``);
if (locators) {
args.push(`locators = [\n${locators}];`);
}
}
const overrideArg = `override${upperCamelize(pkg.name)}Attrs`; const overrideArg = `override${upperCamelize(pkg.name)}Attrs`;
isolatedCode.push( isolatedCode.push(
`${isolatedProp} = optionalOverride (args.${overrideArg} or null) (mkIsolatedBuild { ${[ `${isolatedProp} = optionalOverride (args.${overrideArg} or null) (mkIsolatedBuild { ${args.join(
`pname = ${json(pkg.name)};`, ` `,
`version = ${json(pkg.version)};`, )} });`,
`reference = ${json(devirtPkg.reference)};`,
`locators = [\n${locators}];`,
].join(` `)} });`,
); );
} }
+12 -3
View File
@@ -24,6 +24,8 @@ let
drvCommon = { drvCommon = {
# Make sure the build uses the right Node.js version everywhere. # Make sure the build uses the right Node.js version everywhere.
buildInputs = [ nodejs yarn ]; buildInputs = [ nodejs yarn ];
# All dependencies should already be cached.
yarn_enable_network = "0";
# Tell node-gyp to use the provided Node.js headers for native code builds. # Tell node-gyp to use the provided Node.js headers for native code builds.
npm_config_nodedir = nodejs; npm_config_nodedir = nodejs;
}; };
@@ -80,14 +82,16 @@ let
#@@ ENDIF INDIVIDUAL_DRVS #@@ ENDIF INDIVIDUAL_DRVS
#@@ IF NEED_ISOLATED_BUILD_SUPPRORT #@@ IF NEED_ISOLATED_BUILD_SUPPRORT
#@@ IF INDIVIDUAL_DRVS
# Create a shell snippet to copy dependencies from a list of locators. # Create a shell snippet to copy dependencies from a list of locators.
mkCacheBuilderForLocators = let mkCacheBuilderForLocators = let
pickCacheDrvs = map (locator: cacheDrvs.${locator}); pickCacheDrvs = map (locator: cacheDrvs.${locator});
in locators: in locators:
mkCacheBuilderForDrvs (pickCacheDrvs locators); mkCacheBuilderForDrvs (pickCacheDrvs locators);
#@@ ENDIF INDIVIDUAL_DRVS
# Create a derivation that builds a node-pre-gyp module in isolation. # Create a derivation that builds a module in isolation.
mkIsolatedBuild = { pname, version, reference, locators }: stdenv.mkDerivation (drvCommon // { mkIsolatedBuild = { pname, version, reference, locators ? [] }: stdenv.mkDerivation (drvCommon // {
inherit pname version; inherit pname version;
dontUnpack = true; dontUnpack = true;
@@ -98,16 +102,21 @@ let
buildPhase = '' buildPhase = ''
mkdir -p .yarn/cache mkdir -p .yarn/cache
#@@ IF COMBINED_DRV
cp --reflink=auto --recursive ${cacheDrv}/* .yarn/cache/
#@@ ENDIF COMBINED_DRV
#@@ IF INDIVIDUAL_DRVS
pushd .yarn/cache > /dev/null pushd .yarn/cache > /dev/null
source ${mkCacheBuilderForLocators locators} source ${mkCacheBuilderForLocators locators}
popd > /dev/null popd > /dev/null
#@@ ENDIF INDIVIDUAL_DRVS
echo '{ "dependencies": { "${pname}": "${reference}" } }' > package.json echo '{ "dependencies": { "${pname}": "${reference}" } }' > package.json
install -m 0600 ${lockfile} ./yarn.lock install -m 0600 ${lockfile} ./yarn.lock
export yarn_global_folder="$TMP" export yarn_global_folder="$TMP"
export yarn_enable_global_cache=false export yarn_enable_global_cache=false
export yarn_enable_immutable_installs=false export yarn_enable_immutable_installs=false
yarn --immutable-cache yarn
''; '';
installPhase = '' installPhase = ''