1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-24 16:19:12 -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:
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: |
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
View file

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

View file

@ -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"

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())),
);
// 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);

View file

@ -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;

View file

@ -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;

View file

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

View file

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

View file

@ -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;

View file

@ -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;

View file

@ -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
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::TsConfig;
use crate::version;
use crate::AnyError;
use deno_core::error::AnyError;
use deno_core::error::anyhow;
use deno_core::error::custom_error;

View file

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

View file

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

View file

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

View file

@ -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
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
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;

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 {
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::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,

View file

@ -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 { "" });