Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 91 additions & 7 deletions lib/update-v8/updateCrates.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import semver from 'semver';
import * as TOML from 'smol-toml';
import { promises as fs } from 'node:fs';
import os from 'node:os';
import path from 'node:path';

import { removeDirectory } from './util.js';
Expand All @@ -15,6 +16,7 @@ export default function updateCrates() {
enumerateTemporalDependencies(),
removeExistingCrates(),
vendorCrates(),
fetchPlaceholderCrates(),
buildManifest(),
generateLockfile(),
updateGYP(),
Expand All @@ -25,6 +27,7 @@ export default function updateCrates() {
}

const chromiumCratesDir = 'deps/v8/third_party/rust/chromium_crates_io';
const icu4xDataPatchesDir = 'deps/v8/third_party/rust/icu4x_data/patches';
const nodeCratesDir = 'deps/crates';

function enumerateTemporalDependencies() {
Expand Down Expand Up @@ -78,15 +81,22 @@ function vendorCrates() {
await fs.mkdir(nodeVendor);

const subtasks = [];
const rustVersions = [];
ctx.rustVersions = [];
ctx.placeholderCrates = [];
for (const dir of await fs.readdir(chromiumVendor)) {
const crate = await getCrateInfo(dir);
if (!crate || !ctx.temporalCrates.has(`${crate.name}@${crate.version}`)) continue;
if (crate.name === 'temporal_capi') {
ctx.temporalCAPIDirectory = dir;
}
// Fetch replace_with placeholders from crates.io.
// See https://chromium-review.googlesource.com/c/chromium/src/+/8527739
if (crate.metadata?.gnrt?.is_placeholder) {
ctx.placeholderCrates.push({ dir, name: crate.name, version: crate.version });
continue;
}
if (crate['rust-version']) {
rustVersions.push(crate['rust-version']);
ctx.rustVersions.push(crate['rust-version']);
}
subtasks.push({
title: dir,
Expand All @@ -100,11 +110,6 @@ function vendorCrates() {
});
}

if (rustVersions.length) {
const msrv = rustVersions.map(semver.coerce).reduce((a, b) => (semver.gt(a, b) ? a : b));
ctx.msrv = `${msrv.major}.${msrv.minor}`;
}

return task.newListr(subtasks, { concurrent: ctx.concurrent });

async function getCrateInfo(crate) {
Expand All @@ -121,10 +126,89 @@ function vendorCrates() {
};
}

// This is a stop-gap fix that only works as long as the Chromium doesn't
// patch the data Node.js uses with third_party/rust/icu4x_data/patches/
// or the locale filters. See third_party/rust/icu4x_data/patches/README.md
// for the markers affected.

// Current markers used by Node.js:
// icu_locale_fallback: locale_likely_subtags_language
// locale_parents
// icu_calendar: calendar_japanese_modern
// calendar_preferred
// calendar_week
// TODO(joyeecheung): Instead of fetching the crates from crates.io,
// generate the replacement crates from Chromium's data. e.g.
// For `replace_with = "//third_party/rust/icu4x_data:lib"`
// in gnrt_config.toml, copy the `.rs.data` files that the dependent
// crates use (`impl_<marker>!(Baked)` in their provider.rs) from
// third_party/rust/icu4x_data/src/data/common and generate a data/mod.rs in the
// upstream crate layout.
// See https://github.com/nodejs/node-core-utils/pull/1117
function fetchPlaceholderCrates() {
return {
title: 'Fetch placeholder crates from crates.io',
skip: (ctx) => ctx.placeholderCrates.length === 0 && 'No placeholder crates',
task: async(ctx, task) => {
const nodeVendor = path.join(ctx.nodeDir, nodeCratesDir, 'vendor');

const patches = await listPatches(path.join(ctx.nodeDir, icu4xDataPatchesDir));
if (patches.length) {
task.title += `, ${icu4xDataPatchesDir} affects: ${patches.join(', ')})`;
}

// Vendor a temporary package that depends on the crates to fetch them.
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'node-crates-'));
try {
const manifest = {
package: { name: 'placeholder_crates', version: '0.0.0', edition: '2021' },
dependencies: Object.fromEntries(ctx.placeholderCrates.map(({ name, version }) => (
[name, { version: `=${version}`, 'default-features': false }]
)))
};
await fs.writeFile(path.join(tmpDir, 'Cargo.toml'), TOML.stringify(manifest));
await fs.mkdir(path.join(tmpDir, 'src'));
await fs.writeFile(path.join(tmpDir, 'src/lib.rs'), '');
const vendorDir = path.join(tmpDir, 'vendor');
await forceRunAsync(ctx.cargo, ['vendor', '--quiet', '--versioned-dirs', vendorDir], {
ignoreFailure: false,
spawnArgs: { cwd: tmpDir }
});

for (const { dir, name, version } of ctx.placeholderCrates) {
const dest = path.join(nodeVendor, dir);
await fs.cp(path.join(vendorDir, `${name}-${version}`), dest, { recursive: true });
await fs.writeFile(path.join(dest, '.cargo-checksum.json'), '{"files":{}}\n');
const crate = TOML.parse(await fs.readFile(path.join(dest, 'Cargo.toml'), 'utf8'));
if (crate.package['rust-version']) {
ctx.rustVersions.push(crate.package['rust-version']);
}
}
} finally {
await removeDirectory(tmpDir);
}
}
};
}

async function listPatches(dir) {
try {
return (await fs.readdir(dir)).filter((file) => file.endsWith('.patch')).sort();
} catch {
return [];
}
}

function buildManifest() {
return {
title: 'Build new manifest',
task: async(ctx) => {
if (ctx.rustVersions.length) {
const msrv = ctx.rustVersions.map(semver.coerce)
.reduce((a, b) => (semver.gt(a, b) ? a : b));
ctx.msrv = `${msrv.major}.${msrv.minor}`;
}

const sourceManifest = TOML.parse(
await fs.readFile(path.join(ctx.nodeDir, chromiumCratesDir, 'Cargo.toml'), 'utf8')
);
Expand Down
Loading