diff options
author | Luca Casonato <lucacasonato@yahoo.com> | 2021-01-08 03:08:51 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-08 03:08:51 +0100 |
commit | a44349dfdfecacdd4ccd343a984b05abb728bf88 (patch) | |
tree | 690b5047cc54d62ce27013ebb199e9bdc7de0937 /cli | |
parent | e61e81eb57351782862aa50775ce4348f10b1856 (diff) |
feat: denort binary (#9041)
This commit adds new binary target called "denort".
It is a "lite" version of "deno" binary that can only execute
code embedded inside the binary itself.
Co-authored-by: Bartek IwaĆczuk <biwanczuk@gmail.com>
Diffstat (limited to 'cli')
-rw-r--r-- | cli/Cargo.toml | 7 | ||||
-rw-r--r-- | cli/bench/main.rs | 6 | ||||
-rw-r--r-- | cli/colors.rs | 3 | ||||
-rw-r--r-- | cli/file_fetcher.rs | 2 | ||||
-rw-r--r-- | cli/flags.rs | 67 | ||||
-rw-r--r-- | cli/http_util.rs | 14 | ||||
-rw-r--r-- | cli/info.rs | 4 | ||||
-rw-r--r-- | cli/lsp/sources.rs | 2 | ||||
-rw-r--r-- | cli/main.rs | 24 | ||||
-rw-r--r-- | cli/main_runtime.rs | 32 | ||||
-rw-r--r-- | cli/module_graph.rs | 2 | ||||
-rw-r--r-- | cli/program_state.rs | 4 | ||||
-rw-r--r-- | cli/standalone.rs | 224 | ||||
-rw-r--r-- | cli/tests/integration_tests.rs | 11 | ||||
-rw-r--r-- | cli/tools/mod.rs | 1 | ||||
-rw-r--r-- | cli/tools/standalone.rs | 146 | ||||
-rw-r--r-- | cli/tools/upgrade.rs | 2 | ||||
-rw-r--r-- | cli/version.rs | 6 |
18 files changed, 300 insertions, 257 deletions
diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 99760023a..bea5e87a5 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -14,6 +14,11 @@ default-run = "deno" name = "deno" path = "main.rs" +[[bin]] +name = "denort" +path = "main_runtime.rs" + + [[bench]] name = "deno_bench" harness = false @@ -51,7 +56,7 @@ indexmap = "1.6.0" jsonc-parser = "0.14.0" lazy_static = "1.4.0" libc = "0.2.77" -log = "0.4.11" +log = { version = "0.4.11", features = ["serde"] } lspower = "0.1.0" notify = "5.0.0-pre.3" percent-encoding = "2.1.0" diff --git a/cli/bench/main.rs b/cli/bench/main.rs index ea693fb3a..d796237d1 100644 --- a/cli/bench/main.rs +++ b/cli/bench/main.rs @@ -202,6 +202,12 @@ fn get_binary_sizes(target_dir: &PathBuf) -> Result<Value> { Value::Number(Number::from(test_util::deno_exe_path().metadata()?.len())), ); + // add up size for denort + sizes.insert( + "denort".to_string(), + Value::Number(Number::from(test_util::denort_exe_path().metadata()?.len())), + ); + // add up size for everything in target/release/deps/libswc* let swc_size = rlib_size(&target_dir, "libswc"); println!("swc {} bytes", swc_size); diff --git a/cli/colors.rs b/cli/colors.rs index 93f252716..97813a72c 100644 --- a/cli/colors.rs +++ b/cli/colors.rs @@ -1,5 +1,8 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +// allow(dead_code) because denort does not use this. +#![allow(dead_code)] + use regex::Regex; use std::env; use std::fmt; diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 5a31ee6cc..632b713a0 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -4,10 +4,10 @@ use crate::colors; use crate::http_cache::HttpCache; use crate::http_util::create_http_client; use crate::http_util::fetch_once; -use crate::http_util::get_user_agent; use crate::http_util::FetchOnceResult; use crate::media_type::MediaType; use crate::text_encoding; +use crate::version::get_user_agent; use deno_runtime::permissions::Permissions; use deno_core::error::custom_error; diff --git a/cli/flags.rs b/cli/flags.rs index 6b4e5aa5f..7e48c8428 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -6,15 +6,11 @@ use clap::Arg; use clap::ArgMatches; use clap::ArgSettings; use clap::SubCommand; -use deno_core::serde::de; use deno_core::serde::Deserialize; -use deno_core::serde::Deserializer; use deno_core::serde::Serialize; -use deno_core::serde::Serializer; use deno_core::url::Url; use deno_runtime::permissions::PermissionsOptions; use log::Level; -use std::fmt; use std::net::SocketAddr; use std::path::PathBuf; use std::str::FromStr; @@ -100,66 +96,7 @@ impl Default for DenoSubcommand { } } -fn deserialize_maybe_log_level<'de, D>(d: D) -> Result<Option<Level>, D::Error> -where - D: Deserializer<'de>, -{ - struct OptionalLogLevelVisitor; - impl<'de> de::Visitor<'de> for OptionalLogLevelVisitor { - type Value = Option<Level>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "null or a valid log level string") - } - - fn visit_none<E>(self) -> Result<Self::Value, E> - where - E: de::Error, - { - Ok(None) - } - - fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error> - where - D: de::Deserializer<'de>, - { - struct LogLevelVisitor; - impl<'de> de::Visitor<'de> for LogLevelVisitor { - type Value = Level; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a valid log level string") - } - - fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> - where - E: de::Error, - { - Level::from_str(s).map_err(|_| { - de::Error::invalid_value(de::Unexpected::Str(s), &self) - }) - } - } - Ok(Some(d.deserialize_str(LogLevelVisitor)?)) - } - } - d.deserialize_option(OptionalLogLevelVisitor) -} - -fn serialize_maybe_log_level<S>( - maybe_level: &Option<Level>, - s: S, -) -> Result<S::Ok, S::Error> -where - S: Serializer, -{ - match maybe_level { - None => s.serialize_none(), - Some(level) => s.serialize_str(&level.to_string()), - } -} - -#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Default)] pub struct Flags { /// Vector of CLI arguments - these are user script arguments, all Deno /// specific flags are removed. @@ -185,8 +122,6 @@ pub struct Flags { pub inspect_brk: Option<SocketAddr>, pub lock: Option<PathBuf>, pub lock_write: bool, - #[serde(deserialize_with = "deserialize_maybe_log_level")] - #[serde(serialize_with = "serialize_maybe_log_level")] pub log_level: Option<Level>, pub no_check: bool, pub no_prompts: bool, diff --git a/cli/http_util.rs b/cli/http_util.rs index df2a7d3a2..a27928055 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -1,6 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -use crate::version; use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::url::Url; @@ -15,10 +14,6 @@ use deno_runtime::deno_fetch::reqwest::Client; use deno_runtime::deno_fetch::reqwest::StatusCode; use std::collections::HashMap; -pub fn get_user_agent() -> String { - format!("Deno/{}", version::deno()) -} - /// Create new instance of async reqwest::Client. This client supports /// proxies and doesn't follow redirects. pub fn create_http_client( @@ -155,6 +150,7 @@ pub async fn fetch_once( #[cfg(test)] mod tests { use super::*; + use crate::version; use std::fs::read; fn create_test_client(ca_data: Option<Vec<u8>>) -> Client { @@ -313,7 +309,7 @@ mod tests { Url::parse("https://localhost:5545/cli/tests/fixture.json").unwrap(); let client = create_http_client( - get_user_agent(), + version::get_user_agent(), Some( read( test_util::root_path() @@ -345,7 +341,7 @@ mod tests { ) .unwrap(); let client = create_http_client( - get_user_agent(), + version::get_user_agent(), Some( read( test_util::root_path() @@ -376,7 +372,7 @@ mod tests { let _http_server_guard = test_util::http_server(); let url = Url::parse("https://localhost:5545/etag_script.ts").unwrap(); let client = create_http_client( - get_user_agent(), + version::get_user_agent(), Some( read( test_util::root_path() @@ -416,7 +412,7 @@ mod tests { ) .unwrap(); let client = create_http_client( - get_user_agent(), + version::get_user_agent(), Some( read( test_util::root_path() diff --git a/cli/info.rs b/cli/info.rs index d2ea4e7e6..ededcc024 100644 --- a/cli/info.rs +++ b/cli/info.rs @@ -2,8 +2,8 @@ use crate::colors; use crate::media_type::serialize_media_type; -use crate::MediaType; -use crate::ModuleSpecifier; +use crate::media_type::MediaType; +use deno_core::ModuleSpecifier; use serde::Serialize; use serde::Serializer; diff --git a/cli/lsp/sources.rs b/cli/lsp/sources.rs index 5ef16a876..7d1fd56f3 100644 --- a/cli/lsp/sources.rs +++ b/cli/lsp/sources.rs @@ -14,7 +14,7 @@ use crate::module_graph::GraphBuilder; use crate::program_state::ProgramState; use crate::specifier_handler::FetchHandler; use crate::text_encoding; -use crate::Permissions; +use deno_runtime::permissions::Permissions; use deno_core::error::AnyError; use deno_core::serde_json; diff --git a/cli/main.rs b/cli/main.rs index 066991624..a3b8f3a6d 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -55,7 +55,6 @@ use crate::program_state::exit_unstable; use crate::program_state::ProgramState; use crate::source_maps::apply_source_map; use crate::specifier_handler::FetchHandler; -use crate::standalone::create_standalone_binary; use crate::tools::installer::infer_name_from_url; use deno_core::error::generic_error; use deno_core::error::AnyError; @@ -116,7 +115,7 @@ fn create_web_worker_callback( .map_or(false, |l| l == log::Level::Debug), unstable: program_state.flags.unstable, ca_data: program_state.ca_data.clone(), - user_agent: http_util::get_user_agent(), + user_agent: version::get_user_agent(), seed: program_state.flags.seed, module_loader, create_web_worker_cb, @@ -192,7 +191,7 @@ pub fn create_main_worker( .map_or(false, |l| l == log::Level::Debug), unstable: program_state.flags.unstable, ca_data: program_state.ca_data.clone(), - user_agent: http_util::get_user_agent(), + user_agent: version::get_user_agent(), seed: program_state.flags.seed, js_error_create_fn: Some(js_error_create_fn), create_web_worker_cb, @@ -307,7 +306,8 @@ async fn compile_command( let debug = flags.log_level == Some(log::Level::Debug); - let run_flags = standalone::compile_to_runtime_flags(flags.clone(), args)?; + let run_flags = + tools::standalone::compile_to_runtime_flags(flags.clone(), args)?; let module_specifier = ModuleSpecifier::resolve_url_or_path(&source_file)?; let program_state = ProgramState::new(flags.clone())?; @@ -337,7 +337,12 @@ async fn compile_command( colors::green("Compile"), module_specifier.to_string() ); - create_standalone_binary(bundle_str, run_flags, output.clone()).await?; + tools::standalone::create_standalone_binary( + bundle_str, + run_flags, + output.clone(), + ) + .await?; info!("{} {}", colors::green("Emit"), output.display()); @@ -1244,7 +1249,14 @@ pub fn main() { colors::enable_ansi(); // For Windows 10 let args: Vec<String> = env::args().collect(); - if let Err(err) = standalone::try_run_standalone_binary(args.clone()) { + let standalone_res = match standalone::extract_standalone(args.clone()) { + Ok(Some((metadata, bundle))) => { + tokio_util::run_basic(standalone::run(bundle, metadata)) + } + Ok(None) => Ok(()), + Err(err) => Err(err), + }; + if let Err(err) = standalone_res { eprintln!("{}: {}", colors::red_bold("error"), err.to_string()); std::process::exit(1); } diff --git a/cli/main_runtime.rs b/cli/main_runtime.rs new file mode 100644 index 000000000..12cb4329d --- /dev/null +++ b/cli/main_runtime.rs @@ -0,0 +1,32 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +#![deny(warnings)] + +#[macro_use] +extern crate lazy_static; + +mod colors; +mod standalone; +mod tokio_util; +mod version; + +use deno_core::error::anyhow; +use deno_core::error::AnyError; +use std::env; + +pub fn main() { + #[cfg(windows)] + colors::enable_ansi(); // For Windows 10 + + let args: Vec<String> = env::args().collect(); + if let Err(err) = run(args) { + eprintln!("{}: {}", colors::red_bold("error"), err.to_string()); + std::process::exit(1); + } +} + +fn run(args: Vec<String>) -> Result<(), AnyError> { + let (metadata, bundle) = standalone::extract_standalone(args)? + .ok_or_else(|| anyhow!("This executable is used internally by 'deno compile', it is not meant to be invoked directly."))?; + tokio_util::run_basic(standalone::run(bundle, metadata)) +} diff --git a/cli/module_graph.rs b/cli/module_graph.rs index 686c1bd0e..f2e693724 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -25,7 +25,7 @@ use crate::tsc; use crate::tsc_config::IgnoredCompilerOptions; use crate::tsc_config::TsConfig; use crate::version; -use crate::AnyError; +use deno_core::error::AnyError; use deno_core::error::anyhow; use deno_core::error::custom_error; diff --git a/cli/program_state.rs b/cli/program_state.rs index 896f4d7b4..ad44d8e9b 100644 --- a/cli/program_state.rs +++ b/cli/program_state.rs @@ -5,7 +5,6 @@ use crate::file_fetcher::CacheSetting; use crate::file_fetcher::FileFetcher; use crate::flags; use crate::http_cache; -use crate::http_util; use crate::import_map::ImportMap; use crate::lockfile::Lockfile; use crate::module_graph::CheckOptions; @@ -14,6 +13,7 @@ use crate::module_graph::TranspileOptions; use crate::module_graph::TypeLib; use crate::source_maps::SourceMapGetter; use crate::specifier_handler::FetchHandler; +use crate::version; use deno_runtime::inspector::InspectorServer; use deno_runtime::permissions::Permissions; @@ -106,7 +106,7 @@ impl ProgramState { let maybe_inspector_server = match maybe_inspect_host { Some(host) => Some(Arc::new(InspectorServer::new( host, - http_util::get_user_agent(), + version::get_user_agent(), ))), None => None, }; diff --git a/cli/standalone.rs b/cli/standalone.rs index 8d95d30f2..a4b55f081 100644 --- a/cli/standalone.rs +++ b/cli/standalone.rs @@ -1,9 +1,7 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + use crate::colors; -use crate::flags::DenoSubcommand; -use crate::flags::Flags; -use crate::tokio_util; use crate::version; -use deno_core::error::bail; use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::error::Context; @@ -11,35 +9,41 @@ use deno_core::futures::FutureExt; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; use deno_core::serde_json; +use deno_core::url::Url; use deno_core::v8_set_flags; use deno_core::ModuleLoader; use deno_core::ModuleSpecifier; use deno_core::OpState; use deno_runtime::permissions::Permissions; +use deno_runtime::permissions::PermissionsOptions; use deno_runtime::worker::MainWorker; use deno_runtime::worker::WorkerOptions; +use log::Level; use std::cell::RefCell; use std::convert::TryInto; use std::env::current_exe; -use std::fs::read; use std::fs::File; use std::io::Read; use std::io::Seek; use std::io::SeekFrom; -use std::io::Write; use std::iter::once; -use std::path::PathBuf; use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; #[derive(Deserialize, Serialize)] -struct Metadata { - flags: Flags, - ca_data: Option<Vec<u8>>, +pub struct Metadata { + pub argv: Vec<String>, + pub unstable: bool, + pub seed: Option<u64>, + pub permissions: PermissionsOptions, + pub location: Option<Url>, + pub v8_flags: Vec<String>, + pub log_level: Option<Level>, + pub ca_data: Option<Vec<u8>>, } -const MAGIC_TRAILER: &[u8; 8] = b"d3n0l4nd"; +pub const MAGIC_TRAILER: &[u8; 8] = b"d3n0l4nd"; /// This function will try to run this binary as a standalone binary /// produced by `deno compile`. It determines if this is a stanalone @@ -50,7 +54,9 @@ const MAGIC_TRAILER: &[u8; 8] = b"d3n0l4nd"; /// These are dereferenced, and the bundle is executed under the configuration /// specified by the metadata. If no magic trailer is present, this function /// exits with `Ok(())`. -pub fn try_run_standalone_binary(args: Vec<String>) -> Result<(), AnyError> { +pub fn extract_standalone( + args: Vec<String>, +) -> Result<Option<(Metadata, String)>, AnyError> { let current_exe_path = current_exe()?; let mut current_exe = File::open(current_exe_path)?; @@ -58,31 +64,27 @@ pub fn try_run_standalone_binary(args: Vec<String>) -> Result<(), AnyError> { let mut trailer = [0; 24]; current_exe.read_exact(&mut trailer)?; let (magic_trailer, rest) = trailer.split_at(8); - if magic_trailer == MAGIC_TRAILER { - let (bundle_pos, rest) = rest.split_at(8); - let metadata_pos = rest; - let bundle_pos = u64_from_bytes(bundle_pos)?; - let metadata_pos = u64_from_bytes(metadata_pos)?; - let bundle_len = metadata_pos - bundle_pos; - let metadata_len = trailer_pos - metadata_pos; - current_exe.seek(SeekFrom::Start(bundle_pos))?; - - let bundle = read_string_slice(&mut current_exe, bundle_pos, bundle_len) - .context("Failed to read source bundle from the current executable")?; - let metadata = - read_string_slice(&mut current_exe, metadata_pos, metadata_len) - .context("Failed to read metadata from the current executable")?; - - let mut metadata: Metadata = serde_json::from_str(&metadata).unwrap(); - metadata.flags.argv.append(&mut args[1..].to_vec()); - if let Err(err) = tokio_util::run_basic(run(bundle, metadata)) { - eprintln!("{}: {}", colors::red_bold("error"), err.to_string()); - std::process::exit(1); - } - std::process::exit(0); - } else { - Ok(()) + if magic_trailer != MAGIC_TRAILER { + return Ok(None); } + + let (bundle_pos, rest) = rest.split_at(8); + let metadata_pos = rest; + let bundle_pos = u64_from_bytes(bundle_pos)?; + let metadata_pos = u64_from_bytes(metadata_pos)?; + let bundle_len = metadata_pos - bundle_pos; + let metadata_len = trailer_pos - metadata_pos; + current_exe.seek(SeekFrom::Start(bundle_pos))?; + + let bundle = read_string_slice(&mut current_exe, bundle_pos, bundle_len) + .context("Failed to read source bundle from the current executable")?; + let metadata = + read_string_slice(&mut current_exe, metadata_pos, metadata_len) + .context("Failed to read metadata from the current executable")?; + + let mut metadata: Metadata = serde_json::from_str(&metadata).unwrap(); + metadata.argv.append(&mut args[1..].to_vec()); + Ok(Some((metadata, bundle))) } fn u64_from_bytes(arr: &[u8]) -> Result<u64, AnyError> { @@ -149,10 +151,12 @@ impl ModuleLoader for EmbeddedModuleLoader { } } -async fn run(source_code: String, metadata: Metadata) -> Result<(), AnyError> { - let Metadata { flags, ca_data } = metadata; +pub async fn run( + source_code: String, + metadata: Metadata, +) -> Result<(), AnyError> { let main_module = ModuleSpecifier::resolve_url(SPECIFIER)?; - let permissions = Permissions::from_options(&flags.clone().into()); + let permissions = Permissions::from_options(&metadata.permissions); let module_loader = Rc::new(EmbeddedModuleLoader(source_code)); let create_web_worker_cb = Arc::new(|_| { todo!("Worker are currently not supported in standalone binaries"); @@ -161,18 +165,18 @@ async fn run(source_code: String, metadata: Metadata) -> Result<(), AnyError> { // Keep in sync with `main.rs`. v8_set_flags( once("UNUSED_BUT_NECESSARY_ARG0".to_owned()) - .chain(flags.v8_flags.iter().cloned()) + .chain(metadata.v8_flags.iter().cloned()) .collect::<Vec<_>>(), ); - // TODO(nayeemrmn): Unify this Flags -> WorkerOptions mapping with `deno run`. + let options = WorkerOptions { apply_source_maps: false, - args: flags.argv, - debug_flag: flags.log_level.map_or(false, |l| l == log::Level::Debug), - user_agent: crate::http_util::get_user_agent(), - unstable: flags.unstable, - ca_data, - seed: flags.seed, + args: metadata.argv, + debug_flag: metadata.log_level.map_or(false, |l| l == log::Level::Debug), + user_agent: version::get_user_agent(), + unstable: metadata.unstable, + ca_data: metadata.ca_data, + seed: metadata.seed, js_error_create_fn: None, create_web_worker_cb, attach_inspector: false, @@ -182,8 +186,8 @@ async fn run(source_code: String, metadata: Metadata) -> Result<(), AnyError> { runtime_version: version::deno(), ts_version: version::TYPESCRIPT.to_string(), no_color: !colors::use_color(), - get_error_class_fn: Some(&crate::errors::get_error_class_name), - location: flags.location, + get_error_class_fn: Some(&get_error_class_name), + location: metadata.location, }; let mut worker = MainWorker::from_options(main_module.clone(), permissions, &options); @@ -192,125 +196,11 @@ async fn run(source_code: String, metadata: Metadata) -> Result<(), AnyError> { worker.execute("window.dispatchEvent(new Event('load'))")?; worker.run_event_loop().await?; worker.execute("window.dispatchEvent(new Event('unload'))")?; - Ok(()) -} - -/// This functions creates a standalone deno binary by appending a bundle -/// and magic trailer to the currently executing binary. -pub async fn create_standalone_binary( - source_code: String, - flags: Flags, - output: PathBuf, -) -> Result<(), AnyError> { - let mut source_code = source_code.as_bytes().to_vec(); - let ca_data = match &flags.ca_file { - Some(ca_file) => Some(read(ca_file)?), - None => None, - }; - let metadata = Metadata { flags, ca_data }; - let mut metadata = serde_json::to_string(&metadata)?.as_bytes().to_vec(); - let original_binary_path = std::env::current_exe()?; - let mut original_bin = tokio::fs::read(original_binary_path).await?; - - let bundle_pos = original_bin.len(); - let metadata_pos = bundle_pos + source_code.len(); - let mut trailer = MAGIC_TRAILER.to_vec(); - trailer.write_all(&bundle_pos.to_be_bytes())?; - trailer.write_all(&metadata_pos.to_be_bytes())?; - - let mut final_bin = - Vec::with_capacity(original_bin.len() + source_code.len() + trailer.len()); - final_bin.append(&mut original_bin); - final_bin.append(&mut source_code); - final_bin.append(&mut metadata); - final_bin.append(&mut trailer); - - let output = - if cfg!(windows) && output.extension().unwrap_or_default() != "exe" { - PathBuf::from(output.display().to_string() + ".exe") - } else { - output - }; - - if output.exists() { - // If the output is a directory, throw error - if output.is_dir() { - bail!("Could not compile: {:?} is a directory.", &output); - } - - // Make sure we don't overwrite any file not created by Deno compiler. - // Check for magic trailer in last 24 bytes. - let mut has_trailer = false; - let mut output_file = File::open(&output)?; - // This seek may fail because the file is too small to possibly be - // `deno compile` output. - if output_file.seek(SeekFrom::End(-24)).is_ok() { - let mut trailer = [0; 24]; - output_file.read_exact(&mut trailer)?; - let (magic_trailer, _) = trailer.split_at(8); - has_trailer = magic_trailer == MAGIC_TRAILER; - } - if !has_trailer { - bail!("Could not compile: cannot overwrite {:?}.", &output); - } - } - tokio::fs::write(&output, final_bin).await?; - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - let perms = std::fs::Permissions::from_mode(0o777); - tokio::fs::set_permissions(output, perms).await?; - } - - Ok(()) + std::process::exit(0); } -/// Transform the flags passed to `deno compile` to flags that would be used at -/// runtime, as if `deno run` were used. -/// - Flags that affect module resolution, loading, type checking, etc. aren't -/// applicable at runtime so are set to their defaults like `false`. -/// - Other flags are inherited. -pub fn compile_to_runtime_flags( - flags: Flags, - baked_args: Vec<String>, -) -> Result<Flags, AnyError> { - // IMPORTANT: Don't abbreviate any of this to `..flags` or - // `..Default::default()`. That forces us to explicitly consider how any - // change to `Flags` should be reflected here. - Ok(Flags { - argv: baked_args, - subcommand: DenoSubcommand::Run { - script: "placeholder".to_string(), - }, - allow_env: flags.allow_env, - allow_hrtime: flags.allow_hrtime, - allow_net: flags.allow_net, - allow_plugin: flags.allow_plugin, - allow_read: flags.allow_read, - allow_run: flags.allow_run, - allow_write: flags.allow_write, - cache_blocklist: vec![], - ca_file: flags.ca_file, - cached_only: false, - config_path: None, - coverage_dir: flags.coverage_dir, - ignore: vec![], - import_map_path: None, - inspect: None, - inspect_brk: None, - location: flags.location, - lock: None, - lock_write: false, - log_level: flags.log_level, - no_check: false, - no_prompts: flags.no_prompts, - no_remote: false, - reload: false, - repl: false, - seed: flags.seed, - unstable: flags.unstable, - v8_flags: flags.v8_flags, - version: false, - watch: false, +fn get_error_class_name(e: &AnyError) -> &'static str { + deno_runtime::errors::get_error_class_name(e).unwrap_or_else(|| { + panic!("Error '{}' contains boxed error of unknown type", e); }) } diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index aabeb1f77..20e391171 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -5036,6 +5036,17 @@ fn standalone_runtime_flags() { .contains("PermissionDenied: write access")); } +#[test] +fn denort_direct_use_error() { + let status = Command::new(util::denort_exe_path()) + .current_dir(util::root_path()) + .spawn() + .unwrap() + .wait() + .unwrap(); + assert!(!status.success()); +} + fn concat_bundle( files: Vec<(PathBuf, String)>, bundle_path: &Path, diff --git a/cli/tools/mod.rs b/cli/tools/mod.rs index be76968fb..4489b7479 100644 --- a/cli/tools/mod.rs +++ b/cli/tools/mod.rs @@ -5,5 +5,6 @@ pub mod fmt; pub mod installer; pub mod lint; pub mod repl; +pub mod standalone; pub mod test_runner; pub mod upgrade; diff --git a/cli/tools/standalone.rs b/cli/tools/standalone.rs new file mode 100644 index 000000000..112169756 --- /dev/null +++ b/cli/tools/standalone.rs @@ -0,0 +1,146 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +use crate::flags::DenoSubcommand; +use crate::flags::Flags; +use deno_core::error::bail; +use deno_core::error::AnyError; +use deno_core::serde_json; +use std::fs::read; +use std::fs::File; +use std::io::Read; +use std::io::Seek; +use std::io::SeekFrom; +use std::io::Write; +use std::path::PathBuf; + +use crate::standalone::Metadata; +use crate::standalone::MAGIC_TRAILER; + +/// This functions creates a standalone deno binary by appending a bundle +/// and magic trailer to the currently executing binary. +pub async fn create_standalone_binary( + source_code: String, + flags: Flags, + output: PathBuf, +) -> Result<(), AnyError> { + let mut source_code = source_code.as_bytes().to_vec(); + let ca_data = match &flags.ca_file { + Some(ca_file) => Some(read(ca_file)?), + None => None, + }; + let metadata = Metadata { + argv: flags.argv.clone(), + unstable: flags.unstable, + seed: flags.seed, + location: flags.location.clone(), + permissions: flags.clone().into(), + v8_flags: flags.v8_flags.clone(), + log_level: flags.log_level, + ca_data, + }; + let mut metadata = serde_json::to_string(&metadata)?.as_bytes().to_vec(); + let original_binary_path = std::env::current_exe()?; + let mut original_bin = tokio::fs::read(original_binary_path).await?; + + let bundle_pos = original_bin.len(); + let metadata_pos = bundle_pos + source_code.len(); + let mut trailer = MAGIC_TRAILER.to_vec(); + trailer.write_all(&bundle_pos.to_be_bytes())?; + trailer.write_all(&metadata_pos.to_be_bytes())?; + + let mut final_bin = + Vec::with_capacity(original_bin.len() + source_code.len() + trailer.len()); + final_bin.append(&mut original_bin); + final_bin.append(&mut source_code); + final_bin.append(&mut metadata); + final_bin.append(&mut trailer); + + let output = + if cfg!(windows) && output.extension().unwrap_or_default() != "exe" { + PathBuf::from(output.display().to_string() + ".exe") + } else { + output + }; + + if output.exists() { + // If the output is a directory, throw error + if output.is_dir() { + bail!("Could not compile: {:?} is a directory.", &output); + } + + // Make sure we don't overwrite any file not created by Deno compiler. + // Check for magic trailer in last 24 bytes. + let mut has_trailer = false; + let mut output_file = File::open(&output)?; + // This seek may fail because the file is too small to possibly be + // `deno compile` output. + if output_file.seek(SeekFrom::End(-24)).is_ok() { + let mut trailer = [0; 24]; + output_file.read_exact(&mut trailer)?; + let (magic_trailer, _) = trailer.split_at(8); + has_trailer = magic_trailer == MAGIC_TRAILER; + } + if !has_trailer { + bail!("Could not compile: cannot overwrite {:?}.", &output); + } + } + tokio::fs::write(&output, final_bin).await?; + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let perms = std::fs::Permissions::from_mode(0o777); + tokio::fs::set_permissions(output, perms).await?; + } + + Ok(()) +} + +/// Transform the flags passed to `deno compile` to flags that would be used at +/// runtime, as if `deno run` were used. +/// - Flags that affect module resolution, loading, type checking, etc. aren't +/// applicable at runtime so are set to their defaults like `false`. +/// - Other flags are inherited. +pub fn compile_to_runtime_flags( + flags: Flags, + baked_args: Vec<String>, +) -> Result<Flags, AnyError> { + // IMPORTANT: Don't abbreviate any of this to `..flags` or + // `..Default::default()`. That forces us to explicitly consider how any + // change to `Flags` should be reflected here. + Ok(Flags { + argv: baked_args, + subcommand: DenoSubcommand::Run { + script: "placeholder".to_string(), + }, + allow_env: flags.allow_env, + allow_hrtime: flags.allow_hrtime, + allow_net: flags.allow_net, + allow_plugin: flags.allow_plugin, + allow_read: flags.allow_read, + allow_run: flags.allow_run, + allow_write: flags.allow_write, + cache_blocklist: vec![], + ca_file: flags.ca_file, + cached_only: false, + config_path: None, + coverage_dir: flags.coverage_dir, + ignore: vec![], + import_map_path: None, + inspect: None, + inspect_brk: None, + location: flags.location, + lock: None, + lock_write: false, + log_level: flags.log_level, + no_check: false, + no_prompts: flags.no_prompts, + no_remote: false, + reload: false, + repl: false, + seed: flags.seed, + unstable: flags.unstable, + v8_flags: flags.v8_flags, + version: false, + watch: false, + }) +} diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs index da26b3159..a404f75f5 100644 --- a/cli/tools/upgrade.rs +++ b/cli/tools/upgrade.rs @@ -2,7 +2,7 @@ //! This module provides feature to upgrade deno executable -use crate::AnyError; +use deno_core::error::AnyError; use deno_runtime::deno_fetch::reqwest; use deno_runtime::deno_fetch::reqwest::Client; use semver_parser::version::parse as semver_parse; diff --git a/cli/version.rs b/cli/version.rs index 49cb34f1d..3c2f6a995 100644 --- a/cli/version.rs +++ b/cli/version.rs @@ -10,6 +10,12 @@ pub fn deno() -> String { }) } +// allow(dead_code) because denort does not use this. +#[allow(dead_code)] pub fn is_canary() -> bool { option_env!("DENO_CANARY").is_some() } + +pub fn get_user_agent() -> String { + format!("Deno/{}", deno()) +} |