1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-23 15:49:44 -05:00

feat: denort binary for deno compile (#22205)

This introduces the `denort` binary - a slim version of deno without
tooling. The binary is used as the default for `deno compile`.

Improves `deno compile` final size by ~2.5x (141 MB -> 61 MB) on Linux
x86_64.
This commit is contained in:
Divy Srivastava 2024-02-13 21:52:30 +05:30 committed by GitHub
parent 492a9fbb91
commit a68eb3fcc3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 453 additions and 254 deletions

View file

@ -704,6 +704,7 @@ const ci = {
run: [
"cd target/release",
"zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno",
"zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort",
"./deno types > lib.deno.d.ts",
].join("\n"),
},
@ -728,6 +729,7 @@ const ci = {
"--entitlements-xml-file=cli/entitlements.plist",
"cd target/release",
"zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno",
"zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort",
]
.join("\n"),
},
@ -740,8 +742,10 @@ const ci = {
"github.repository == 'denoland/deno'",
].join("\n"),
shell: "pwsh",
run:
run: [
"Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip",
"Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip",
].join("\n"),
},
{
name: "Upload canary to dl.deno.land",
@ -942,6 +946,7 @@ const ci = {
run: [
'du -hd1 "./target/${{ matrix.profile }}"',
'du -ha "./target/${{ matrix.profile }}/deno"',
'du -ha "./target/${{ matrix.profile }}/denort"',
].join("\n"),
},
{
@ -1007,10 +1012,15 @@ const ci = {
with: {
files: [
"target/release/deno-x86_64-pc-windows-msvc.zip",
"target/release/denort-x86_64-pc-windows-msvc.zip",
"target/release/deno-x86_64-unknown-linux-gnu.zip",
"target/release/denort-x86_64-unknown-linux-gnu.zip",
"target/release/deno-x86_64-apple-darwin.zip",
"target/release/denort-x86_64-apple-darwin.zip",
"target/release/deno-aarch64-unknown-linux-gnu.zip",
"target/release/denort-aarch64-unknown-linux-gnu.zip",
"target/release/deno-aarch64-apple-darwin.zip",
"target/release/denort-aarch64-apple-darwin.zip",
"target/release/deno_src.tar.gz",
"target/release/lib.deno.d.ts",
].join("\n"),

View file

@ -424,6 +424,7 @@ jobs:
run: |-
cd target/release
zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno
zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort
./deno types > lib.deno.d.ts
- name: Pre-release (mac)
if: |-
@ -439,6 +440,7 @@ jobs:
rcodesign sign target/release/deno --code-signature-flags=runtime --p12-password="$APPLE_CODESIGN_PASSWORD" --p12-file=<(echo $APPLE_CODESIGN_KEY | base64 -d) --entitlements-xml-file=cli/entitlements.plist
cd target/release
zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno
zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort
- name: Pre-release (windows)
if: |-
!(matrix.skip) && (matrix.os == 'windows' &&
@ -446,7 +448,9 @@ jobs:
matrix.profile == 'release' &&
github.repository == 'denoland/deno')
shell: pwsh
run: 'Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip'
run: |-
Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip
Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip
- name: Upload canary to dl.deno.land
if: |-
!(matrix.skip) && (matrix.job == 'test' &&
@ -588,6 +592,7 @@ jobs:
run: |-
du -hd1 "./target/${{ matrix.profile }}"
du -ha "./target/${{ matrix.profile }}/deno"
du -ha "./target/${{ matrix.profile }}/denort"
- name: Worker info
if: '!(matrix.skip) && (matrix.job == ''bench'')'
run: |-
@ -632,10 +637,15 @@ jobs:
with:
files: |-
target/release/deno-x86_64-pc-windows-msvc.zip
target/release/denort-x86_64-pc-windows-msvc.zip
target/release/deno-x86_64-unknown-linux-gnu.zip
target/release/denort-x86_64-unknown-linux-gnu.zip
target/release/deno-x86_64-apple-darwin.zip
target/release/denort-x86_64-apple-darwin.zip
target/release/deno-aarch64-unknown-linux-gnu.zip
target/release/denort-aarch64-unknown-linux-gnu.zip
target/release/deno-aarch64-apple-darwin.zip
target/release/denort-aarch64-apple-darwin.zip
target/release/deno_src.tar.gz
target/release/lib.deno.d.ts
body_path: target/release/release-notes.md

View file

@ -15,6 +15,11 @@ name = "deno"
path = "main.rs"
doc = false
[[bin]]
name = "denort"
path = "mainrt.rs"
doc = false
[[test]]
name = "integration"
path = "integration_tests_runner.rs"

View file

@ -42,6 +42,8 @@ use crate::resolver::NpmModuleLoader;
use crate::resolver::SloppyImportsResolver;
use crate::standalone::DenoCompileBinaryWriter;
use crate::tools::check::TypeChecker;
use crate::tools::coverage::CoverageCollector;
use crate::tools::run::hmr::HmrRunner;
use crate::util::file_watcher::WatcherCommunicator;
use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::import_map::deno_json_deps;
@ -50,6 +52,7 @@ use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
use crate::worker::CliMainWorkerFactory;
use crate::worker::CliMainWorkerOptions;
use std::path::PathBuf;
use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
@ -766,7 +769,6 @@ impl CliFactory {
)),
self.root_cert_store_provider().clone(),
self.fs().clone(),
Some(self.emitter()?.clone()),
maybe_file_watcher_communicator,
self.maybe_inspector_server().clone(),
self.maybe_lockfile().clone(),
@ -783,6 +785,32 @@ impl CliFactory {
fn create_cli_main_worker_options(
&self,
) -> Result<CliMainWorkerOptions, AnyError> {
let create_hmr_runner = if self.options.has_hmr() {
let watcher_communicator = self.watcher_communicator.clone().unwrap();
let emitter = self.emitter()?.clone();
let fn_: crate::worker::CreateHmrRunnerCb = Box::new(move |session| {
Box::new(HmrRunner::new(
emitter.clone(),
session,
watcher_communicator.clone(),
))
});
Some(fn_)
} else {
None
};
let create_coverage_collector =
if let Some(coverage_dir) = self.options.coverage_dir() {
let coverage_dir = PathBuf::from(coverage_dir);
let fn_: crate::worker::CreateCoverageCollectorCb =
Box::new(move |session| {
Box::new(CoverageCollector::new(coverage_dir.clone(), session))
});
Some(fn_)
} else {
None
};
Ok(CliMainWorkerOptions {
argv: self.options.argv().clone(),
// This optimization is only available for "run" subcommand
@ -814,6 +842,8 @@ impl CliFactory {
.clone(),
unstable: self.options.legacy_unstable_flag(),
maybe_root_package_json_deps: self.options.maybe_package_json_deps(),
create_hmr_runner,
create_coverage_collector,
})
}
}

82
cli/mainrt.rs Normal file
View file

@ -0,0 +1,82 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// Allow unused code warnings because we share
// code between the two bin targets.
#![allow(dead_code)]
#![allow(unused_imports)]
mod standalone;
mod args;
mod auth_tokens;
mod cache;
mod emit;
mod errors;
mod file_fetcher;
mod http_util;
mod js;
mod node;
mod npm;
mod resolver;
mod util;
mod version;
mod worker;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::error::JsError;
use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics;
pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS;
use deno_terminal::colors;
use std::env;
use std::env::current_exe;
use crate::args::Flags;
pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) {
eprintln!(
"Unstable API '{api_name}'. The `--unstable-{}` flag must be provided.",
feature
);
std::process::exit(70);
}
fn exit_with_message(message: &str, code: i32) -> ! {
eprintln!(
"{}: {}",
colors::red_bold("error"),
message.trim_start_matches("error: ")
);
std::process::exit(code);
}
fn unwrap_or_exit<T>(result: Result<T, AnyError>) -> T {
match result {
Ok(value) => value,
Err(error) => {
let mut error_string = format!("{error:?}");
if let Some(e) = error.downcast_ref::<JsError>() {
error_string = format_js_error(e);
}
exit_with_message(&error_string, 1);
}
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let future = async move {
let current_exe_path = current_exe().unwrap();
match standalone::extract_standalone(&current_exe_path, args).await {
Ok(Some((metadata, eszip))) => standalone::run(eszip, metadata).await,
Ok(None) => Err(generic_error("No archive found.")),
Err(err) => Err(err),
}
};
unwrap_or_exit(create_and_run_current_thread_with_maybe_metrics(future));
}

View file

@ -40,7 +40,7 @@ use std::sync::Arc;
use crate::args::package_json::PackageJsonDeps;
use crate::args::JsxImportSourceConfig;
use crate::args::PackageJsonDepsProvider;
use crate::graph_util::format_range_with_colors;
use crate::colors;
use crate::node::CliNodeCodeTranslator;
use crate::npm::ByonmCliNpmResolver;
use crate::npm::CliNpmResolver;
@ -48,6 +48,15 @@ use crate::npm::InnerCliNpmResolverRef;
use crate::util::path::specifier_to_file_path;
use crate::util::sync::AtomicFlag;
pub fn format_range_with_colors(range: &deno_graph::Range) -> String {
format!(
"{}:{}:{}",
colors::cyan(range.specifier.as_str()),
colors::yellow(&(range.start.line + 1).to_string()),
colors::yellow(&(range.start.character + 1).to_string())
)
}
pub struct ModuleCodeStringSource {
pub code: ModuleCodeString,
pub found_url: ModuleSpecifier,

View file

@ -2,12 +2,14 @@
use std::collections::BTreeMap;
use std::env::current_exe;
use std::fs;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail;
@ -337,6 +339,71 @@ fn u64_from_bytes(arr: &[u8]) -> Result<u64, AnyError> {
Ok(u64::from_be_bytes(*fixed_arr))
}
pub fn unpack_into_dir(
exe_name: &str,
archive_name: &str,
archive_data: Vec<u8>,
is_windows: bool,
temp_dir: &tempfile::TempDir,
) -> Result<PathBuf, AnyError> {
let temp_dir_path = temp_dir.path();
let exe_ext = if is_windows { "exe" } else { "" };
let archive_path = temp_dir_path.join(exe_name).with_extension("zip");
let exe_path = temp_dir_path.join(exe_name).with_extension(exe_ext);
assert!(!exe_path.exists());
let archive_ext = Path::new(archive_name)
.extension()
.and_then(|ext| ext.to_str())
.unwrap();
let unpack_status = match archive_ext {
"zip" if cfg!(windows) => {
fs::write(&archive_path, &archive_data)?;
Command::new("tar.exe")
.arg("xf")
.arg(&archive_path)
.arg("-C")
.arg(temp_dir_path)
.spawn()
.map_err(|err| {
if err.kind() == std::io::ErrorKind::NotFound {
std::io::Error::new(
std::io::ErrorKind::NotFound,
"`tar.exe` was not found in your PATH",
)
} else {
err
}
})?
.wait()?
}
"zip" => {
fs::write(&archive_path, &archive_data)?;
Command::new("unzip")
.current_dir(temp_dir_path)
.arg(&archive_path)
.spawn()
.map_err(|err| {
if err.kind() == std::io::ErrorKind::NotFound {
std::io::Error::new(
std::io::ErrorKind::NotFound,
"`unzip` was not found in your PATH, please install `unzip`",
)
} else {
err
}
})?
.wait()?
}
ext => bail!("Unsupported archive type: '{ext}'"),
};
if !unpack_status.success() {
bail!("Failed to unpack archive.");
}
assert!(exe_path.exists());
fs::remove_file(&archive_path)?;
Ok(exe_path)
}
pub struct DenoCompileBinaryWriter<'a> {
file_fetcher: &'a FileFetcher,
client: &'a HttpClient,
@ -404,13 +471,16 @@ impl<'a> DenoCompileBinaryWriter<'a> {
&self,
compile_flags: &CompileFlags,
) -> Result<Vec<u8>, AnyError> {
if compile_flags.target.is_none() {
let path = std::env::current_exe()?;
// Used for testing.
//
// Phase 2 of the 'min sized' deno compile RFC talks
// about adding this as a flag.
if let Some(path) = std::env::var_os("DENORT_BIN") {
return Ok(std::fs::read(path)?);
}
let target = compile_flags.resolve_target();
let binary_name = format!("deno-{target}.zip");
let binary_name = format!("denort-{target}.zip");
let binary_path_suffix = if crate::version::is_canary() {
format!("canary/{}/{}", crate::version::GIT_COMMIT_HASH, binary_name)
@ -429,7 +499,9 @@ impl<'a> DenoCompileBinaryWriter<'a> {
let archive_data = std::fs::read(binary_path)?;
let temp_dir = tempfile::TempDir::new()?;
let base_binary_path = crate::tools::upgrade::unpack_into_dir(
let base_binary_path = unpack_into_dir(
"denort",
&binary_name,
archive_data,
target.contains("windows"),
&temp_dir,

View file

@ -55,7 +55,7 @@ use import_map::parse_from_json;
use std::rc::Rc;
use std::sync::Arc;
mod binary;
pub mod binary;
mod file_system;
mod virtual_fs;
@ -520,7 +520,6 @@ pub async fn run(
None,
None,
None,
None,
feature_checker,
CliMainWorkerOptions {
argv: metadata.argv,
@ -548,6 +547,8 @@ pub async fn run(
.unsafely_ignore_certificate_errors,
unstable: metadata.unstable_config.legacy_flag_enabled,
maybe_root_package_json_deps: package_json_deps_provider.deps().cloned(),
create_hmr_runner: None,
create_coverage_collector: None,
},
None,
// TODO(bartlomieju): temporarily disabled

View file

@ -46,6 +46,56 @@ pub struct CoverageCollector {
session: LocalInspectorSession,
}
#[async_trait::async_trait(?Send)]
impl crate::worker::CoverageCollector for CoverageCollector {
async fn start_collecting(&mut self) -> Result<(), AnyError> {
self.enable_debugger().await?;
self.enable_profiler().await?;
self
.start_precise_coverage(cdp::StartPreciseCoverageArgs {
call_count: true,
detailed: true,
allow_triggered_updates: false,
})
.await?;
Ok(())
}
async fn stop_collecting(&mut self) -> Result<(), AnyError> {
fs::create_dir_all(&self.dir)?;
let script_coverages = self.take_precise_coverage().await?.result;
for script_coverage in script_coverages {
// Filter out internal JS files from being included in coverage reports
if script_coverage.url.starts_with("ext:")
|| script_coverage.url.starts_with("[ext:")
{
continue;
}
let filename = format!("{}.json", Uuid::new_v4());
let filepath = self.dir.join(filename);
let mut out = BufWriter::new(File::create(&filepath)?);
let coverage = serde_json::to_string(&script_coverage)?;
let formatted_coverage =
format_json(&filepath, &coverage, &Default::default())
.ok()
.flatten()
.unwrap_or(coverage);
out.write_all(formatted_coverage.as_bytes())?;
out.flush()?;
}
self.disable_debugger().await?;
self.disable_profiler().await?;
Ok(())
}
}
impl CoverageCollector {
pub fn new(dir: PathBuf, session: LocalInspectorSession) -> Self {
Self { dir, session }
@ -109,53 +159,6 @@ impl CoverageCollector {
Ok(return_object)
}
pub async fn start_collecting(&mut self) -> Result<(), AnyError> {
self.enable_debugger().await?;
self.enable_profiler().await?;
self
.start_precise_coverage(cdp::StartPreciseCoverageArgs {
call_count: true,
detailed: true,
allow_triggered_updates: false,
})
.await?;
Ok(())
}
pub async fn stop_collecting(&mut self) -> Result<(), AnyError> {
fs::create_dir_all(&self.dir)?;
let script_coverages = self.take_precise_coverage().await?.result;
for script_coverage in script_coverages {
// Filter out internal JS files from being included in coverage reports
if script_coverage.url.starts_with("ext:")
|| script_coverage.url.starts_with("[ext:")
{
continue;
}
let filename = format!("{}.json", Uuid::new_v4());
let filepath = self.dir.join(filename);
let mut out = BufWriter::new(File::create(&filepath)?);
let coverage = serde_json::to_string(&script_coverage)?;
let formatted_coverage =
format_json(&filepath, &coverage, &Default::default())
.ok()
.flatten()
.unwrap_or(coverage);
out.write_all(formatted_coverage.as_bytes())?;
out.flush()?;
}
self.disable_debugger().await?;
self.disable_profiler().await?;
Ok(())
}
}
#[derive(Debug, Clone)]

View file

@ -61,105 +61,22 @@ pub struct HmrRunner {
emitter: Arc<Emitter>,
}
impl HmrRunner {
pub fn new(
emitter: Arc<Emitter>,
session: LocalInspectorSession,
watcher_communicator: Arc<WatcherCommunicator>,
) -> Self {
Self {
session,
emitter,
watcher_communicator,
script_ids: HashMap::new(),
}
}
#[async_trait::async_trait(?Send)]
impl crate::worker::HmrRunner for HmrRunner {
// TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
pub async fn start(&mut self) -> Result<(), AnyError> {
async fn start(&mut self) -> Result<(), AnyError> {
self.enable_debugger().await
}
// TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
pub async fn stop(&mut self) -> Result<(), AnyError> {
async fn stop(&mut self) -> Result<(), AnyError> {
self
.watcher_communicator
.change_restart_mode(WatcherRestartMode::Automatic);
self.disable_debugger().await
}
// TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
async fn enable_debugger(&mut self) -> Result<(), AnyError> {
self
.session
.post_message::<()>("Debugger.enable", None)
.await?;
self
.session
.post_message::<()>("Runtime.enable", None)
.await?;
Ok(())
}
// TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
async fn disable_debugger(&mut self) -> Result<(), AnyError> {
self
.session
.post_message::<()>("Debugger.disable", None)
.await?;
self
.session
.post_message::<()>("Runtime.disable", None)
.await?;
Ok(())
}
async fn set_script_source(
&mut self,
script_id: &str,
source: &str,
) -> Result<cdp::SetScriptSourceResponse, AnyError> {
let result = self
.session
.post_message(
"Debugger.setScriptSource",
Some(json!({
"scriptId": script_id,
"scriptSource": source,
"allowTopFrameEditing": true,
})),
)
.await?;
Ok(serde_json::from_value::<cdp::SetScriptSourceResponse>(
result,
)?)
}
async fn dispatch_hmr_event(
&mut self,
script_id: &str,
) -> Result<(), AnyError> {
let expr = format!(
"dispatchEvent(new CustomEvent(\"hmr\", {{ detail: {{ path: \"{}\" }} }}));",
script_id
);
let _result = self
.session
.post_message(
"Runtime.evaluate",
Some(json!({
"expression": expr,
"contextId": Some(1),
})),
)
.await?;
Ok(())
}
pub async fn run(&mut self) -> Result<(), AnyError> {
async fn run(&mut self) -> Result<(), AnyError> {
self
.watcher_communicator
.change_restart_mode(WatcherRestartMode::Manual);
@ -252,3 +169,89 @@ impl HmrRunner {
}
}
}
impl HmrRunner {
pub fn new(
emitter: Arc<Emitter>,
session: LocalInspectorSession,
watcher_communicator: Arc<WatcherCommunicator>,
) -> Self {
Self {
session,
emitter,
watcher_communicator,
script_ids: HashMap::new(),
}
}
// TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
async fn enable_debugger(&mut self) -> Result<(), AnyError> {
self
.session
.post_message::<()>("Debugger.enable", None)
.await?;
self
.session
.post_message::<()>("Runtime.enable", None)
.await?;
Ok(())
}
// TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
async fn disable_debugger(&mut self) -> Result<(), AnyError> {
self
.session
.post_message::<()>("Debugger.disable", None)
.await?;
self
.session
.post_message::<()>("Runtime.disable", None)
.await?;
Ok(())
}
async fn set_script_source(
&mut self,
script_id: &str,
source: &str,
) -> Result<cdp::SetScriptSourceResponse, AnyError> {
let result = self
.session
.post_message(
"Debugger.setScriptSource",
Some(json!({
"scriptId": script_id,
"scriptSource": source,
"allowTopFrameEditing": true,
})),
)
.await?;
Ok(serde_json::from_value::<cdp::SetScriptSourceResponse>(
result,
)?)
}
async fn dispatch_hmr_event(
&mut self,
script_id: &str,
) -> Result<(), AnyError> {
let expr = format!(
"dispatchEvent(new CustomEvent(\"hmr\", {{ detail: {{ path: \"{}\" }} }}));",
script_id
);
let _result = self
.session
.post_message(
"Runtime.evaluate",
Some(json!({
"expression": expr,
"contextId": Some(1),
})),
)
.await?;
Ok(())
}
}

View file

@ -7,6 +7,7 @@ use crate::args::UpgradeFlags;
use crate::colors;
use crate::factory::CliFactory;
use crate::http_util::HttpClient;
use crate::standalone::binary::unpack_into_dir;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
use crate::util::time;
@ -30,11 +31,11 @@ use std::process::Command;
use std::sync::Arc;
use std::time::Duration;
static ARCHIVE_NAME: Lazy<String> =
Lazy::new(|| format!("deno-{}.zip", env!("TARGET")));
const RELEASE_URL: &str = "https://github.com/denoland/deno/releases";
pub static ARCHIVE_NAME: Lazy<String> =
Lazy::new(|| format!("deno-{}.zip", env!("TARGET")));
// How often query server for new version. In hours.
const UPGRADE_CHECK_INTERVAL: i64 = 24;
@ -501,7 +502,13 @@ pub async fn upgrade(
log::info!("Deno is upgrading to version {}", &install_version);
let temp_dir = tempfile::TempDir::new()?;
let new_exe_path = unpack_into_dir(archive_data, cfg!(windows), &temp_dir)?;
let new_exe_path = unpack_into_dir(
"deno",
&ARCHIVE_NAME,
archive_data,
cfg!(windows),
&temp_dir,
)?;
fs::set_permissions(&new_exe_path, permissions)?;
check_exe(&new_exe_path)?;
@ -628,71 +635,6 @@ async fn download_package(
}
}
pub fn unpack_into_dir(
archive_data: Vec<u8>,
is_windows: bool,
temp_dir: &tempfile::TempDir,
) -> Result<PathBuf, AnyError> {
const EXE_NAME: &str = "deno";
let temp_dir_path = temp_dir.path();
let exe_ext = if is_windows { "exe" } else { "" };
let archive_path = temp_dir_path.join(EXE_NAME).with_extension("zip");
let exe_path = temp_dir_path.join(EXE_NAME).with_extension(exe_ext);
assert!(!exe_path.exists());
let archive_ext = Path::new(&*ARCHIVE_NAME)
.extension()
.and_then(|ext| ext.to_str())
.unwrap();
let unpack_status = match archive_ext {
"zip" if cfg!(windows) => {
fs::write(&archive_path, &archive_data)?;
Command::new("tar.exe")
.arg("xf")
.arg(&archive_path)
.arg("-C")
.arg(temp_dir_path)
.spawn()
.map_err(|err| {
if err.kind() == std::io::ErrorKind::NotFound {
std::io::Error::new(
std::io::ErrorKind::NotFound,
"`tar.exe` was not found in your PATH",
)
} else {
err
}
})?
.wait()?
}
"zip" => {
fs::write(&archive_path, &archive_data)?;
Command::new("unzip")
.current_dir(temp_dir_path)
.arg(&archive_path)
.spawn()
.map_err(|err| {
if err.kind() == std::io::ErrorKind::NotFound {
std::io::Error::new(
std::io::ErrorKind::NotFound,
"`unzip` was not found in your PATH, please install `unzip`",
)
} else {
err
}
})?
.wait()?
}
ext => bail!("Unsupported archive type: '{ext}'"),
};
if !unpack_status.success() {
bail!("Failed to unpack archive.");
}
assert!(exe_path.exists());
fs::remove_file(&archive_path)?;
Ok(exe_path)
}
fn replace_exe(from: &Path, to: &Path) -> Result<(), std::io::Error> {
if cfg!(windows) {
// On windows you cannot replace the currently running executable.

View file

@ -49,12 +49,8 @@ use tokio::select;
use crate::args::package_json::PackageJsonDeps;
use crate::args::DenoSubcommand;
use crate::args::StorageKeyResolver;
use crate::emit::Emitter;
use crate::errors;
use crate::npm::CliNpmResolver;
use crate::tools;
use crate::tools::coverage::CoverageCollector;
use crate::tools::run::hmr::HmrRunner;
use crate::util::checksum;
use crate::util::file_watcher::WatcherCommunicator;
use crate::util::file_watcher::WatcherRestartMode;
@ -82,7 +78,29 @@ pub trait HasNodeSpecifierChecker: Send + Sync {
fn has_node_specifier(&self) -> bool;
}
#[derive(Clone)]
#[async_trait::async_trait(?Send)]
pub trait HmrRunner: Send + Sync {
async fn start(&mut self) -> Result<(), AnyError>;
async fn stop(&mut self) -> Result<(), AnyError>;
async fn run(&mut self) -> Result<(), AnyError>;
}
#[async_trait::async_trait(?Send)]
pub trait CoverageCollector: Send + Sync {
async fn start_collecting(&mut self) -> Result<(), AnyError>;
async fn stop_collecting(&mut self) -> Result<(), AnyError>;
}
pub type CreateHmrRunnerCb = Box<
dyn Fn(deno_core::LocalInspectorSession) -> Box<dyn HmrRunner> + Send + Sync,
>;
pub type CreateCoverageCollectorCb = Box<
dyn Fn(deno_core::LocalInspectorSession) -> Box<dyn CoverageCollector>
+ Send
+ Sync,
>;
pub struct CliMainWorkerOptions {
pub argv: Vec<String>,
pub log_level: WorkerLogLevel,
@ -104,6 +122,8 @@ pub struct CliMainWorkerOptions {
pub unstable: bool,
pub skip_op_registration: bool,
pub maybe_root_package_json_deps: Option<PackageJsonDeps>,
pub create_hmr_runner: Option<CreateHmrRunnerCb>,
pub create_coverage_collector: Option<CreateCoverageCollectorCb>,
}
struct SharedWorkerState {
@ -119,7 +139,6 @@ struct SharedWorkerState {
module_loader_factory: Box<dyn ModuleLoaderFactory>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
fs: Arc<dyn deno_fs::FileSystem>,
emitter: Option<Arc<Emitter>>,
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
maybe_inspector_server: Option<Arc<InspectorServer>>,
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
@ -324,42 +343,20 @@ impl CliMainWorker {
self.worker.evaluate_module(id).await
}
pub async fn maybe_setup_coverage_collector(
&mut self,
) -> Result<Option<CoverageCollector>, AnyError> {
if let Some(coverage_dir) = &self.shared.options.coverage_dir {
let session = self.worker.create_inspector_session().await;
let coverage_dir = PathBuf::from(coverage_dir);
let mut coverage_collector =
tools::coverage::CoverageCollector::new(coverage_dir, session);
self
.worker
.js_runtime
.with_event_loop_future(
coverage_collector.start_collecting().boxed_local(),
PollEventLoopOptions::default(),
)
.await?;
Ok(Some(coverage_collector))
} else {
Ok(None)
}
}
pub async fn maybe_setup_hmr_runner(
&mut self,
) -> Result<Option<HmrRunner>, AnyError> {
) -> Result<Option<Box<dyn HmrRunner>>, AnyError> {
if !self.shared.options.hmr {
return Ok(None);
}
let watcher_communicator =
self.shared.maybe_file_watcher_communicator.clone().unwrap();
let emitter = self.shared.emitter.clone().unwrap();
let Some(setup_hmr_runner) = self.shared.options.create_hmr_runner.as_ref()
else {
return Ok(None);
};
let session = self.worker.create_inspector_session().await;
let mut hmr_runner = HmrRunner::new(emitter, session, watcher_communicator);
let mut hmr_runner = setup_hmr_runner(session);
self
.worker
@ -369,10 +366,31 @@ impl CliMainWorker {
PollEventLoopOptions::default(),
)
.await?;
Ok(Some(hmr_runner))
}
pub async fn maybe_setup_coverage_collector(
&mut self,
) -> Result<Option<Box<dyn CoverageCollector>>, AnyError> {
let Some(create_coverage_collector) =
self.shared.options.create_coverage_collector.as_ref()
else {
return Ok(None);
};
let session = self.worker.create_inspector_session().await;
let mut coverage_collector = create_coverage_collector(session);
self
.worker
.js_runtime
.with_event_loop_future(
coverage_collector.start_collecting().boxed_local(),
PollEventLoopOptions::default(),
)
.await?;
Ok(Some(coverage_collector))
}
pub fn execute_script_static(
&mut self,
name: &'static str,
@ -400,7 +418,6 @@ impl CliMainWorkerFactory {
module_loader_factory: Box<dyn ModuleLoaderFactory>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
fs: Arc<dyn deno_fs::FileSystem>,
emitter: Option<Arc<Emitter>>,
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
maybe_inspector_server: Option<Arc<InspectorServer>>,
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
@ -423,7 +440,6 @@ impl CliMainWorkerFactory {
compiled_wasm_module_store: Default::default(),
module_loader_factory,
root_cert_store_provider,
emitter,
fs,
maybe_file_watcher_communicator,
maybe_inspector_server,

View file

@ -20,6 +20,7 @@ use os_pipe::pipe;
use crate::assertions::assert_wildcard_match;
use crate::deno_exe_path;
use crate::denort_exe_path;
use crate::env_vars_for_jsr_tests;
use crate::env_vars_for_npm_tests;
use crate::fs::PathRef;
@ -80,7 +81,7 @@ pub struct TestContextBuilder {
impl TestContextBuilder {
pub fn new() -> Self {
Self::default()
Self::default().add_compile_env_vars()
}
pub fn for_npm() -> Self {
@ -158,6 +159,13 @@ impl TestContextBuilder {
self
}
pub fn add_compile_env_vars(mut self) -> Self {
// The `denort` binary is in the same artifact directory as the `deno` binary.
let denort_bin = denort_exe_path();
self = self.env("DENORT_BIN", denort_bin.to_string());
self
}
pub fn add_jsr_env_vars(mut self) -> Self {
for (key, value) in env_vars_for_jsr_tests() {
self = self.env(key, value);
@ -236,7 +244,7 @@ impl Default for TestContext {
impl TestContext {
pub fn with_http_server() -> Self {
TestContextBuilder::default().use_http_server().build()
TestContextBuilder::new().use_http_server().build()
}
pub fn deno_dir(&self) -> &TempDir {

View file

@ -140,6 +140,14 @@ pub fn deno_exe_path() -> PathRef {
PathRef::new(p)
}
pub fn denort_exe_path() -> PathRef {
let mut p = target_dir().join("denort").to_path_buf();
if cfg!(windows) {
p.set_extension("exe");
}
PathRef::new(p)
}
pub fn prebuilt_tool_path(tool: &str) -> PathRef {
let mut exe = tool.to_string();
exe.push_str(if cfg!(windows) { ".exe" } else { "" });