summaryrefslogtreecommitdiff
path: root/cli/standalone.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/standalone.rs')
-rw-r--r--cli/standalone.rs224
1 files changed, 57 insertions, 167 deletions
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);
})
}