1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-28 16:20:57 -05:00

fix(compile): make output more deterministic (#25092)

Closes https://github.com/denoland/deno/issues/25084
This commit is contained in:
David Sherret 2024-08-19 12:41:11 -04:00 committed by GitHub
parent a20418e2e8
commit c9edb15f15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 75 additions and 18 deletions

8
Cargo.lock generated
View file

@ -1869,9 +1869,9 @@ dependencies = [
[[package]] [[package]]
name = "deno_npm" name = "deno_npm"
version = "0.22.0" version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01fd279be9f522ebfaabce7ff0f4b069c2c1d737946ba57456b579f9246669e8" checksum = "ceef28152643a021fc3487a3ec93ae83996972c087547d97f425c83e15dca932"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -2778,9 +2778,9 @@ dependencies = [
[[package]] [[package]]
name = "eszip" name = "eszip"
version = "0.74.0" version = "0.75.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a74ab660df48e00a4582f6ca506b3cfc5683ce80e73158c9e4dbf84341bb3aea" checksum = "67ab3253e0e6a99b79ffc6d7a6243ad541f54543768cecd2198b64476a5971f8"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",

View file

@ -72,13 +72,13 @@ deno_emit = "=0.44.0"
deno_graph = { version = "=0.81.2" } deno_graph = { version = "=0.81.2" }
deno_lint = { version = "=0.63.1", features = ["docs"] } deno_lint = { version = "=0.63.1", features = ["docs"] }
deno_lockfile.workspace = true deno_lockfile.workspace = true
deno_npm = "=0.22.0" deno_npm = "=0.23.0"
deno_package_json.workspace = true deno_package_json.workspace = true
deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_semver = "=0.5.10" deno_semver = "=0.5.10"
deno_task_shell = "=0.17.0" deno_task_shell = "=0.17.0"
deno_terminal.workspace = true deno_terminal.workspace = true
eszip = "=0.74.0" eszip = "=0.75.0"
libsui = "0.3.0" libsui = "0.3.0"
napi_sym.workspace = true napi_sym.workspace = true
node_resolver.workspace = true node_resolver.workspace = true

View file

@ -31,6 +31,7 @@ use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics;
pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS;
use deno_terminal::colors; use deno_terminal::colors;
use indexmap::IndexMap;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
@ -73,7 +74,7 @@ fn unwrap_or_exit<T>(result: Result<T, AnyError>) -> T {
} }
} }
fn load_env_vars(env_vars: &HashMap<String, String>) { fn load_env_vars(env_vars: &IndexMap<String, String>) {
env_vars.iter().for_each(|env_var| { env_vars.iter().for_each(|env_var| {
if env::var(env_var.0).is_err() { if env::var(env_var.0).is_err() {
std::env::set_var(env_var.0, env_var.1); std::env::set_var(env_var.0, env_var.1);

View file

@ -100,6 +100,8 @@ pub struct SerializedWorkspaceResolver {
pub pkg_json_resolution: PackageJsonDepResolution, pub pkg_json_resolution: PackageJsonDepResolution,
} }
// Note: Don't use hashmaps/hashsets. Ensure the serialization
// is deterministic.
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct Metadata { pub struct Metadata {
pub argv: Vec<String>, pub argv: Vec<String>,
@ -111,7 +113,7 @@ pub struct Metadata {
pub ca_stores: Option<Vec<String>>, pub ca_stores: Option<Vec<String>>,
pub ca_data: Option<Vec<u8>>, pub ca_data: Option<Vec<u8>>,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>, pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
pub env_vars_from_env_file: HashMap<String, String>, pub env_vars_from_env_file: IndexMap<String, String>,
pub workspace_resolver: SerializedWorkspaceResolver, pub workspace_resolver: SerializedWorkspaceResolver,
pub entrypoint_key: String, pub entrypoint_key: String,
pub node_modules: Option<NodeModules>, pub node_modules: Option<NodeModules>,
@ -667,8 +669,10 @@ impl<'a> DenoCompileBinaryWriter<'a> {
// but also don't make this dependent on the registry url // but also don't make this dependent on the registry url
let root_path = npm_resolver.global_cache_root_folder(); let root_path = npm_resolver.global_cache_root_folder();
let mut builder = VfsBuilder::new(root_path)?; let mut builder = VfsBuilder::new(root_path)?;
for package in npm_resolver.all_system_packages(&self.npm_system_info) let mut packages =
{ npm_resolver.all_system_packages(&self.npm_system_info);
packages.sort_by(|a, b| a.id.cmp(&b.id)); // determinism
for package in packages {
let folder = let folder =
npm_resolver.resolve_pkg_folder_from_pkg_id(&package.id)?; npm_resolver.resolve_pkg_folder_from_pkg_id(&package.id)?;
builder.add_dir_recursive(&folder)?; builder.add_dir_recursive(&folder)?;
@ -729,11 +733,13 @@ impl<'a> DenoCompileBinaryWriter<'a> {
cli_options.workspace().root_dir().to_file_path().unwrap(), cli_options.workspace().root_dir().to_file_path().unwrap(),
); );
while let Some(pending_dir) = pending_dirs.pop_front() { while let Some(pending_dir) = pending_dirs.pop_front() {
let entries = fs::read_dir(&pending_dir).with_context(|| { let mut entries = fs::read_dir(&pending_dir)
.with_context(|| {
format!("Failed reading: {}", pending_dir.display()) format!("Failed reading: {}", pending_dir.display())
})?; })?
.collect::<Result<Vec<_>, _>>()?;
entries.sort_by_cached_key(|entry| entry.file_name()); // determinism
for entry in entries { for entry in entries {
let entry = entry?;
let path = entry.path(); let path = entry.path();
if !path.is_dir() { if !path.is_dir() {
continue; continue;
@ -755,8 +761,8 @@ impl<'a> DenoCompileBinaryWriter<'a> {
/// in the passed environment file. /// in the passed environment file.
fn get_file_env_vars( fn get_file_env_vars(
filename: String, filename: String,
) -> Result<HashMap<String, String>, dotenvy::Error> { ) -> Result<IndexMap<String, String>, dotenvy::Error> {
let mut file_env_vars = HashMap::new(); let mut file_env_vars = IndexMap::new();
for item in dotenvy::from_filename_iter(filename)? { for item in dotenvy::from_filename_iter(filename)? {
let Ok((key, val)) = item else { let Ok((key, val)) = item else {
continue; // this failure will be warned about on load continue; // this failure will be warned about on load

View file

@ -90,8 +90,11 @@ impl VfsBuilder {
let read_dir = std::fs::read_dir(path) let read_dir = std::fs::read_dir(path)
.with_context(|| format!("Reading {}", path.display()))?; .with_context(|| format!("Reading {}", path.display()))?;
for entry in read_dir { let mut dir_entries =
let entry = entry?; read_dir.into_iter().collect::<Result<Vec<_>, _>>()?;
dir_entries.sort_by_cached_key(|entry| entry.file_name()); // determinism
for entry in dir_entries {
let file_type = entry.file_type()?; let file_type = entry.file_type()?;
let path = entry.path(); let path = entry.path();

View file

@ -0,0 +1,28 @@
{
"tempDir": true,
"steps": [{
"if": "unix",
"args": "compile --output main1 main.ts",
"output": "[WILDCARD]"
}, {
"if": "unix",
"args": "compile --output main2 main.ts",
"output": "[WILDCARD]"
}, {
"if": "unix",
"args": "run --allow-read=. assert_equal.ts main1 main2",
"output": "Same\n"
}, {
"if": "windows",
"args": "compile --output main1.exe main.ts",
"output": "[WILDCARD]"
}, {
"if": "windows",
"args": "compile --output main2.exe main.ts",
"output": "[WILDCARD]"
}, {
"if": "windows",
"args": "run --allow-read=. assert_equal.ts main1.exe main2.exe",
"output": "Same\n"
}]
}

View file

@ -0,0 +1,15 @@
const file1 = Deno.readFileSync(Deno.args[0]);
const file2 = Deno.readFileSync(Deno.args[1]);
if (file1.length !== file2.length) {
console.error("File lengths are different");
Deno.exit(1);
}
for (let i = 0; i < file1.length; i++) {
if (file1[i] !== file2[i]) {
console.error("Files are different");
Deno.exit(1);
}
}
console.error("Same");

View file

@ -0,0 +1,4 @@
import "npm:@denotest/esm-basic";
import "npm:@denotest/add";
import "npm:@denotest/subtract";
import "npm:preact";