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>
This commit is contained in:
Luca Casonato 2021-01-08 03:08:51 +01:00 committed by GitHub
parent e61e81eb57
commit a44349dfdf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 315 additions and 258 deletions

View file

@ -175,6 +175,7 @@ jobs:
run: | run: |
cd target/release cd target/release
zip -r deno-x86_64-unknown-linux-gnu.zip deno zip -r deno-x86_64-unknown-linux-gnu.zip deno
zip -r denort-x86_64-unknown-linux-gnu.zip denort
./deno types > lib.deno.d.ts ./deno types > lib.deno.d.ts
- name: Pre-release (mac) - name: Pre-release (mac)
@ -184,6 +185,7 @@ jobs:
run: | run: |
cd target/release cd target/release
zip -r deno-x86_64-apple-darwin.zip deno zip -r deno-x86_64-apple-darwin.zip deno
zip -r denort-x86_64-apple-darwin.zip denort
- name: Pre-release (windows) - name: Pre-release (windows)
if: | if: |
@ -191,6 +193,7 @@ jobs:
matrix.kind == 'test_release' matrix.kind == 'test_release'
run: | run: |
Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-x86_64-pc-windows-msvc.zip Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-x86_64-pc-windows-msvc.zip
Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-x86_64-pc-windows-msvc.zip
- name: Upload canary (unix) - name: Upload canary (unix)
if: | if: |

1
Cargo.lock generated
View file

@ -1314,6 +1314,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [ dependencies = [
"cfg-if 0.1.10", "cfg-if 0.1.10",
"serde",
] ]
[[package]] [[package]]

View file

@ -14,6 +14,11 @@ default-run = "deno"
name = "deno" name = "deno"
path = "main.rs" path = "main.rs"
[[bin]]
name = "denort"
path = "main_runtime.rs"
[[bench]] [[bench]]
name = "deno_bench" name = "deno_bench"
harness = false harness = false
@ -51,7 +56,7 @@ indexmap = "1.6.0"
jsonc-parser = "0.14.0" jsonc-parser = "0.14.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
libc = "0.2.77" libc = "0.2.77"
log = "0.4.11" log = { version = "0.4.11", features = ["serde"] }
lspower = "0.1.0" lspower = "0.1.0"
notify = "5.0.0-pre.3" notify = "5.0.0-pre.3"
percent-encoding = "2.1.0" percent-encoding = "2.1.0"

View file

@ -202,6 +202,12 @@ fn get_binary_sizes(target_dir: &PathBuf) -> Result<Value> {
Value::Number(Number::from(test_util::deno_exe_path().metadata()?.len())), 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* // add up size for everything in target/release/deps/libswc*
let swc_size = rlib_size(&target_dir, "libswc"); let swc_size = rlib_size(&target_dir, "libswc");
println!("swc {} bytes", swc_size); println!("swc {} bytes", swc_size);

View file

@ -1,5 +1,8 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // 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 regex::Regex;
use std::env; use std::env;
use std::fmt; use std::fmt;

View file

@ -4,10 +4,10 @@ use crate::colors;
use crate::http_cache::HttpCache; use crate::http_cache::HttpCache;
use crate::http_util::create_http_client; use crate::http_util::create_http_client;
use crate::http_util::fetch_once; use crate::http_util::fetch_once;
use crate::http_util::get_user_agent;
use crate::http_util::FetchOnceResult; use crate::http_util::FetchOnceResult;
use crate::media_type::MediaType; use crate::media_type::MediaType;
use crate::text_encoding; use crate::text_encoding;
use crate::version::get_user_agent;
use deno_runtime::permissions::Permissions; use deno_runtime::permissions::Permissions;
use deno_core::error::custom_error; use deno_core::error::custom_error;

View file

@ -6,15 +6,11 @@ use clap::Arg;
use clap::ArgMatches; use clap::ArgMatches;
use clap::ArgSettings; use clap::ArgSettings;
use clap::SubCommand; use clap::SubCommand;
use deno_core::serde::de;
use deno_core::serde::Deserialize; use deno_core::serde::Deserialize;
use deno_core::serde::Deserializer;
use deno_core::serde::Serialize; use deno_core::serde::Serialize;
use deno_core::serde::Serializer;
use deno_core::url::Url; use deno_core::url::Url;
use deno_runtime::permissions::PermissionsOptions; use deno_runtime::permissions::PermissionsOptions;
use log::Level; use log::Level;
use std::fmt;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; 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> #[derive(Clone, Debug, PartialEq, Default)]
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)]
pub struct Flags { pub struct Flags {
/// Vector of CLI arguments - these are user script arguments, all Deno /// Vector of CLI arguments - these are user script arguments, all Deno
/// specific flags are removed. /// specific flags are removed.
@ -185,8 +122,6 @@ pub struct Flags {
pub inspect_brk: Option<SocketAddr>, pub inspect_brk: Option<SocketAddr>,
pub lock: Option<PathBuf>, pub lock: Option<PathBuf>,
pub lock_write: bool, pub lock_write: bool,
#[serde(deserialize_with = "deserialize_maybe_log_level")]
#[serde(serialize_with = "serialize_maybe_log_level")]
pub log_level: Option<Level>, pub log_level: Option<Level>,
pub no_check: bool, pub no_check: bool,
pub no_prompts: bool, pub no_prompts: bool,

View file

@ -1,6 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::version;
use deno_core::error::generic_error; use deno_core::error::generic_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::url::Url; use deno_core::url::Url;
@ -15,10 +14,6 @@ use deno_runtime::deno_fetch::reqwest::Client;
use deno_runtime::deno_fetch::reqwest::StatusCode; use deno_runtime::deno_fetch::reqwest::StatusCode;
use std::collections::HashMap; use std::collections::HashMap;
pub fn get_user_agent() -> String {
format!("Deno/{}", version::deno())
}
/// Create new instance of async reqwest::Client. This client supports /// Create new instance of async reqwest::Client. This client supports
/// proxies and doesn't follow redirects. /// proxies and doesn't follow redirects.
pub fn create_http_client( pub fn create_http_client(
@ -155,6 +150,7 @@ pub async fn fetch_once(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::version;
use std::fs::read; use std::fs::read;
fn create_test_client(ca_data: Option<Vec<u8>>) -> Client { 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(); Url::parse("https://localhost:5545/cli/tests/fixture.json").unwrap();
let client = create_http_client( let client = create_http_client(
get_user_agent(), version::get_user_agent(),
Some( Some(
read( read(
test_util::root_path() test_util::root_path()
@ -345,7 +341,7 @@ mod tests {
) )
.unwrap(); .unwrap();
let client = create_http_client( let client = create_http_client(
get_user_agent(), version::get_user_agent(),
Some( Some(
read( read(
test_util::root_path() test_util::root_path()
@ -376,7 +372,7 @@ mod tests {
let _http_server_guard = test_util::http_server(); let _http_server_guard = test_util::http_server();
let url = Url::parse("https://localhost:5545/etag_script.ts").unwrap(); let url = Url::parse("https://localhost:5545/etag_script.ts").unwrap();
let client = create_http_client( let client = create_http_client(
get_user_agent(), version::get_user_agent(),
Some( Some(
read( read(
test_util::root_path() test_util::root_path()
@ -416,7 +412,7 @@ mod tests {
) )
.unwrap(); .unwrap();
let client = create_http_client( let client = create_http_client(
get_user_agent(), version::get_user_agent(),
Some( Some(
read( read(
test_util::root_path() test_util::root_path()

View file

@ -2,8 +2,8 @@
use crate::colors; use crate::colors;
use crate::media_type::serialize_media_type; use crate::media_type::serialize_media_type;
use crate::MediaType; use crate::media_type::MediaType;
use crate::ModuleSpecifier; use deno_core::ModuleSpecifier;
use serde::Serialize; use serde::Serialize;
use serde::Serializer; use serde::Serializer;

View file

@ -14,7 +14,7 @@ use crate::module_graph::GraphBuilder;
use crate::program_state::ProgramState; use crate::program_state::ProgramState;
use crate::specifier_handler::FetchHandler; use crate::specifier_handler::FetchHandler;
use crate::text_encoding; use crate::text_encoding;
use crate::Permissions; use deno_runtime::permissions::Permissions;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde_json; use deno_core::serde_json;

View file

@ -55,7 +55,6 @@ use crate::program_state::exit_unstable;
use crate::program_state::ProgramState; use crate::program_state::ProgramState;
use crate::source_maps::apply_source_map; use crate::source_maps::apply_source_map;
use crate::specifier_handler::FetchHandler; use crate::specifier_handler::FetchHandler;
use crate::standalone::create_standalone_binary;
use crate::tools::installer::infer_name_from_url; use crate::tools::installer::infer_name_from_url;
use deno_core::error::generic_error; use deno_core::error::generic_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
@ -116,7 +115,7 @@ fn create_web_worker_callback(
.map_or(false, |l| l == log::Level::Debug), .map_or(false, |l| l == log::Level::Debug),
unstable: program_state.flags.unstable, unstable: program_state.flags.unstable,
ca_data: program_state.ca_data.clone(), 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, seed: program_state.flags.seed,
module_loader, module_loader,
create_web_worker_cb, create_web_worker_cb,
@ -192,7 +191,7 @@ pub fn create_main_worker(
.map_or(false, |l| l == log::Level::Debug), .map_or(false, |l| l == log::Level::Debug),
unstable: program_state.flags.unstable, unstable: program_state.flags.unstable,
ca_data: program_state.ca_data.clone(), 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, seed: program_state.flags.seed,
js_error_create_fn: Some(js_error_create_fn), js_error_create_fn: Some(js_error_create_fn),
create_web_worker_cb, create_web_worker_cb,
@ -307,7 +306,8 @@ async fn compile_command(
let debug = flags.log_level == Some(log::Level::Debug); 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 module_specifier = ModuleSpecifier::resolve_url_or_path(&source_file)?;
let program_state = ProgramState::new(flags.clone())?; let program_state = ProgramState::new(flags.clone())?;
@ -337,7 +337,12 @@ async fn compile_command(
colors::green("Compile"), colors::green("Compile"),
module_specifier.to_string() 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()); info!("{} {}", colors::green("Emit"), output.display());
@ -1244,7 +1249,14 @@ pub fn main() {
colors::enable_ansi(); // For Windows 10 colors::enable_ansi(); // For Windows 10
let args: Vec<String> = env::args().collect(); 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()); eprintln!("{}: {}", colors::red_bold("error"), err.to_string());
std::process::exit(1); std::process::exit(1);
} }

32
cli/main_runtime.rs Normal file
View file

@ -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))
}

View file

@ -25,7 +25,7 @@ use crate::tsc;
use crate::tsc_config::IgnoredCompilerOptions; use crate::tsc_config::IgnoredCompilerOptions;
use crate::tsc_config::TsConfig; use crate::tsc_config::TsConfig;
use crate::version; use crate::version;
use crate::AnyError; use deno_core::error::AnyError;
use deno_core::error::anyhow; use deno_core::error::anyhow;
use deno_core::error::custom_error; use deno_core::error::custom_error;

View file

@ -5,7 +5,6 @@ use crate::file_fetcher::CacheSetting;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
use crate::flags; use crate::flags;
use crate::http_cache; use crate::http_cache;
use crate::http_util;
use crate::import_map::ImportMap; use crate::import_map::ImportMap;
use crate::lockfile::Lockfile; use crate::lockfile::Lockfile;
use crate::module_graph::CheckOptions; use crate::module_graph::CheckOptions;
@ -14,6 +13,7 @@ use crate::module_graph::TranspileOptions;
use crate::module_graph::TypeLib; use crate::module_graph::TypeLib;
use crate::source_maps::SourceMapGetter; use crate::source_maps::SourceMapGetter;
use crate::specifier_handler::FetchHandler; use crate::specifier_handler::FetchHandler;
use crate::version;
use deno_runtime::inspector::InspectorServer; use deno_runtime::inspector::InspectorServer;
use deno_runtime::permissions::Permissions; use deno_runtime::permissions::Permissions;
@ -106,7 +106,7 @@ impl ProgramState {
let maybe_inspector_server = match maybe_inspect_host { let maybe_inspector_server = match maybe_inspect_host {
Some(host) => Some(Arc::new(InspectorServer::new( Some(host) => Some(Arc::new(InspectorServer::new(
host, host,
http_util::get_user_agent(), version::get_user_agent(),
))), ))),
None => None, None => None,
}; };

View file

@ -1,9 +1,7 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::colors; use crate::colors;
use crate::flags::DenoSubcommand;
use crate::flags::Flags;
use crate::tokio_util;
use crate::version; use crate::version;
use deno_core::error::bail;
use deno_core::error::type_error; use deno_core::error::type_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::error::Context; use deno_core::error::Context;
@ -11,35 +9,41 @@ use deno_core::futures::FutureExt;
use deno_core::serde::Deserialize; use deno_core::serde::Deserialize;
use deno_core::serde::Serialize; use deno_core::serde::Serialize;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::url::Url;
use deno_core::v8_set_flags; use deno_core::v8_set_flags;
use deno_core::ModuleLoader; use deno_core::ModuleLoader;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_core::OpState; use deno_core::OpState;
use deno_runtime::permissions::Permissions; use deno_runtime::permissions::Permissions;
use deno_runtime::permissions::PermissionsOptions;
use deno_runtime::worker::MainWorker; use deno_runtime::worker::MainWorker;
use deno_runtime::worker::WorkerOptions; use deno_runtime::worker::WorkerOptions;
use log::Level;
use std::cell::RefCell; use std::cell::RefCell;
use std::convert::TryInto; use std::convert::TryInto;
use std::env::current_exe; use std::env::current_exe;
use std::fs::read;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::io::Seek; use std::io::Seek;
use std::io::SeekFrom; use std::io::SeekFrom;
use std::io::Write;
use std::iter::once; use std::iter::once;
use std::path::PathBuf;
use std::pin::Pin; use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
struct Metadata { pub struct Metadata {
flags: Flags, pub argv: Vec<String>,
ca_data: Option<Vec<u8>>, 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 /// This function will try to run this binary as a standalone binary
/// produced by `deno compile`. It determines if this is a stanalone /// 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 /// These are dereferenced, and the bundle is executed under the configuration
/// specified by the metadata. If no magic trailer is present, this function /// specified by the metadata. If no magic trailer is present, this function
/// exits with `Ok(())`. /// 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 current_exe_path = current_exe()?;
let mut current_exe = File::open(current_exe_path)?; 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]; let mut trailer = [0; 24];
current_exe.read_exact(&mut trailer)?; current_exe.read_exact(&mut trailer)?;
let (magic_trailer, rest) = trailer.split_at(8); let (magic_trailer, rest) = trailer.split_at(8);
if magic_trailer == MAGIC_TRAILER { if magic_trailer != MAGIC_TRAILER {
let (bundle_pos, rest) = rest.split_at(8); return Ok(None);
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(())
} }
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> { 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> { pub async fn run(
let Metadata { flags, ca_data } = metadata; source_code: String,
metadata: Metadata,
) -> Result<(), AnyError> {
let main_module = ModuleSpecifier::resolve_url(SPECIFIER)?; 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 module_loader = Rc::new(EmbeddedModuleLoader(source_code));
let create_web_worker_cb = Arc::new(|_| { let create_web_worker_cb = Arc::new(|_| {
todo!("Worker are currently not supported in standalone binaries"); 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`. // Keep in sync with `main.rs`.
v8_set_flags( v8_set_flags(
once("UNUSED_BUT_NECESSARY_ARG0".to_owned()) once("UNUSED_BUT_NECESSARY_ARG0".to_owned())
.chain(flags.v8_flags.iter().cloned()) .chain(metadata.v8_flags.iter().cloned())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
// TODO(nayeemrmn): Unify this Flags -> WorkerOptions mapping with `deno run`.
let options = WorkerOptions { let options = WorkerOptions {
apply_source_maps: false, apply_source_maps: false,
args: flags.argv, args: metadata.argv,
debug_flag: flags.log_level.map_or(false, |l| l == log::Level::Debug), debug_flag: metadata.log_level.map_or(false, |l| l == log::Level::Debug),
user_agent: crate::http_util::get_user_agent(), user_agent: version::get_user_agent(),
unstable: flags.unstable, unstable: metadata.unstable,
ca_data, ca_data: metadata.ca_data,
seed: flags.seed, seed: metadata.seed,
js_error_create_fn: None, js_error_create_fn: None,
create_web_worker_cb, create_web_worker_cb,
attach_inspector: false, attach_inspector: false,
@ -182,8 +186,8 @@ async fn run(source_code: String, metadata: Metadata) -> Result<(), AnyError> {
runtime_version: version::deno(), runtime_version: version::deno(),
ts_version: version::TYPESCRIPT.to_string(), ts_version: version::TYPESCRIPT.to_string(),
no_color: !colors::use_color(), no_color: !colors::use_color(),
get_error_class_fn: Some(&crate::errors::get_error_class_name), get_error_class_fn: Some(&get_error_class_name),
location: flags.location, location: metadata.location,
}; };
let mut worker = let mut worker =
MainWorker::from_options(main_module.clone(), permissions, &options); 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.execute("window.dispatchEvent(new Event('load'))")?;
worker.run_event_loop().await?; worker.run_event_loop().await?;
worker.execute("window.dispatchEvent(new Event('unload'))")?; worker.execute("window.dispatchEvent(new Event('unload'))")?;
Ok(()) std::process::exit(0);
} }
/// This functions creates a standalone deno binary by appending a bundle fn get_error_class_name(e: &AnyError) -> &'static str {
/// and magic trailer to the currently executing binary. deno_runtime::errors::get_error_class_name(e).unwrap_or_else(|| {
pub async fn create_standalone_binary( panic!("Error '{}' contains boxed error of unknown type", e);
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(())
}
/// 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,
}) })
} }

View file

@ -5036,6 +5036,17 @@ fn standalone_runtime_flags() {
.contains("PermissionDenied: write access")); .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( fn concat_bundle(
files: Vec<(PathBuf, String)>, files: Vec<(PathBuf, String)>,
bundle_path: &Path, bundle_path: &Path,

View file

@ -5,5 +5,6 @@ pub mod fmt;
pub mod installer; pub mod installer;
pub mod lint; pub mod lint;
pub mod repl; pub mod repl;
pub mod standalone;
pub mod test_runner; pub mod test_runner;
pub mod upgrade; pub mod upgrade;

146
cli/tools/standalone.rs Normal file
View file

@ -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,
})
}

View file

@ -2,7 +2,7 @@
//! This module provides feature to upgrade deno executable //! 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;
use deno_runtime::deno_fetch::reqwest::Client; use deno_runtime::deno_fetch::reqwest::Client;
use semver_parser::version::parse as semver_parse; use semver_parser::version::parse as semver_parse;

View file

@ -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 { pub fn is_canary() -> bool {
option_env!("DENO_CANARY").is_some() option_env!("DENO_CANARY").is_some()
} }
pub fn get_user_agent() -> String {
format!("Deno/{}", deno())
}

View file

@ -8,6 +8,7 @@ use deno_core::error::AnyError;
use deno_core::url; use deno_core::url;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize;
use std::collections::HashSet; use std::collections::HashSet;
use std::env::current_dir; use std::env::current_dir;
use std::fmt; use std::fmt;
@ -88,7 +89,7 @@ pub fn resolve_fs_allowlist(allow: &Option<Vec<PathBuf>>) -> HashSet<PathBuf> {
} }
} }
#[derive(Clone, Debug, PartialEq, Default)] #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
pub struct PermissionsOptions { pub struct PermissionsOptions {
pub allow_env: bool, pub allow_env: bool,
pub allow_hrtime: bool, pub allow_hrtime: bool,

View file

@ -106,6 +106,15 @@ pub fn deno_exe_path() -> PathBuf {
p p
} }
pub fn denort_exe_path() -> PathBuf {
// Something like /Users/rld/src/deno/target/debug/deps/denort
let mut p = target_dir().join("denort");
if cfg!(windows) {
p.set_extension("exe");
}
p
}
pub fn prebuilt_tool_path(tool: &str) -> PathBuf { pub fn prebuilt_tool_path(tool: &str) -> PathBuf {
let mut exe = tool.to_string(); let mut exe = tool.to_string();
exe.push_str(if cfg!(windows) { ".exe" } else { "" }); exe.push_str(if cfg!(windows) { ".exe" } else { "" });