mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
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:
parent
e61e81eb57
commit
a44349dfdf
22 changed files with 315 additions and 258 deletions
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
@ -175,6 +175,7 @@ jobs:
|
|||
run: |
|
||||
cd target/release
|
||||
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
|
||||
|
||||
- name: Pre-release (mac)
|
||||
|
@ -184,6 +185,7 @@ jobs:
|
|||
run: |
|
||||
cd target/release
|
||||
zip -r deno-x86_64-apple-darwin.zip deno
|
||||
zip -r denort-x86_64-apple-darwin.zip denort
|
||||
|
||||
- name: Pre-release (windows)
|
||||
if: |
|
||||
|
@ -191,6 +193,7 @@ jobs:
|
|||
matrix.kind == 'test_release'
|
||||
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/denort.exe -DestinationPath target/release/denort-x86_64-pc-windows-msvc.zip
|
||||
|
||||
- name: Upload canary (unix)
|
||||
if: |
|
||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1314,6 +1314,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
67
cli/flags.rs
67
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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
24
cli/main.rs
24
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);
|
||||
}
|
||||
|
|
32
cli/main_runtime.rs
Normal file
32
cli/main_runtime.rs
Normal 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))
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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(())
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
/// 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(())
|
||||
}
|
||||
|
||||
/// 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);
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
146
cli/tools/standalone.rs
Normal file
146
cli/tools/standalone.rs
Normal 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,
|
||||
})
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use deno_core::error::AnyError;
|
|||
use deno_core::url;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashSet;
|
||||
use std::env::current_dir;
|
||||
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 allow_env: bool,
|
||||
pub allow_hrtime: bool,
|
||||
|
|
|
@ -106,6 +106,15 @@ pub fn deno_exe_path() -> PathBuf {
|
|||
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 {
|
||||
let mut exe = tool.to_string();
|
||||
exe.push_str(if cfg!(windows) { ".exe" } else { "" });
|
||||
|
|
Loading…
Reference in a new issue