Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: deno compile #8539

Merged
merged 17 commits into from
Nov 30, 2020
82 changes: 82 additions & 0 deletions cli/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ pub enum DenoSubcommand {
source_file: String,
out_file: Option<PathBuf>,
},
Compile {
source_file: String,
out_file: String,
},
Completions {
buf: Box<[u8]>,
},
Expand Down Expand Up @@ -291,6 +295,8 @@ pub fn flags_from_vec_safe(args: Vec<String>) -> clap::Result<Flags> {
doc_parse(&mut flags, m);
} else if let Some(m) = matches.subcommand_matches("lint") {
lint_parse(&mut flags, m);
} else if let Some(m) = matches.subcommand_matches("compile") {
compile_parse(&mut flags, m);
} else {
repl_parse(&mut flags, &matches);
}
Expand Down Expand Up @@ -340,6 +346,7 @@ If the flag is set, restrict these messages to errors.",
)
.subcommand(bundle_subcommand())
.subcommand(cache_subcommand())
.subcommand(compile_subcommand())
.subcommand(completions_subcommand())
.subcommand(doc_subcommand())
.subcommand(eval_subcommand())
Expand Down Expand Up @@ -407,6 +414,18 @@ fn install_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
};
}

fn compile_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
compile_args_parse(flags, matches);

let source_file = matches.value_of("source_file").unwrap().to_string();
let out_file = matches.value_of("out_file").unwrap().to_string();

flags.subcommand = DenoSubcommand::Compile {
source_file,
out_file,
};
}

fn bundle_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
compile_args_parse(flags, matches);

Expand Down Expand Up @@ -789,6 +808,24 @@ The installation root is determined, in order of precedence:
These must be added to the path manually if required.")
}

fn compile_subcommand<'a, 'b>() -> App<'a, 'b> {
compile_args(SubCommand::with_name("compile"))
.arg(
Arg::with_name("source_file")
.takes_value(true)
.required(true),
)
.arg(Arg::with_name("out_file").takes_value(true).required(true))
lucacasonato marked this conversation as resolved.
Show resolved Hide resolved
.about("Compile the script into a self contained executable")
.long_about(
"Compiles the given script into a self contained executable.
deno compile --unstable https://deno.land/std/http/file_server.ts file_server
deno compile --unstable https://deno.land/std/examples/colors.ts colors

Cross compiling binaries for different platforms is not currently possible.",
)
}

fn bundle_subcommand<'a, 'b>() -> App<'a, 'b> {
compile_args(SubCommand::with_name("bundle"))
.arg(
Expand Down Expand Up @@ -3144,4 +3181,49 @@ mod tests {
}
);
}

#[test]
fn compile() {
let r = flags_from_vec_safe(svec![
"deno",
"compile",
"https://deno.land/std/examples/colors.ts",
"colors"
]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Compile {
source_file: "https://deno.land/std/examples/colors.ts".to_string(),
out_file: "colors".to_string()
},
..Flags::default()
}
);
}

#[test]
fn compile_with_flags() {
#[rustfmt::skip]
let r = flags_from_vec_safe(svec!["deno", "compile", "--unstable", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "https://deno.land/std/examples/colors.ts", "colors"]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Compile {
source_file: "https://deno.land/std/examples/colors.ts".to_string(),
out_file: "colors".to_string()
},
unstable: true,
import_map_path: Some("import_map.json".to_string()),
no_remote: true,
config_path: Some("tsconfig.json".to_string()),
no_check: true,
reload: true,
lock: Some(PathBuf::from("lock.json")),
lock_write: true,
ca_file: Some("example.crt".to_string()),
..Flags::default()
}
);
}
}
175 changes: 123 additions & 52 deletions cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ mod resolve_addr;
mod signal;
mod source_maps;
mod specifier_handler;
mod standalone;
mod text_encoding;
mod tokio_util;
mod tools;
Expand All @@ -55,6 +56,7 @@ use crate::media_type::MediaType;
use crate::permissions::Permissions;
use crate::program_state::ProgramState;
use crate::specifier_handler::FetchHandler;
use crate::standalone::create_standalone_binary;
use crate::worker::MainWorker;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
Expand Down Expand Up @@ -149,6 +151,44 @@ fn get_types(unstable: bool) -> String {
types
}

async fn compile_command(
flags: Flags,
source_file: String,
out_file: String,
) -> Result<(), AnyError> {
if !flags.unstable {
exit_unstable("compile");
}

let debug = flags.log_level == Some(log::Level::Debug);

let module_specifier = ModuleSpecifier::resolve_url_or_path(&source_file)?;
let program_state = ProgramState::new(flags.clone())?;

let module_graph = create_module_graph_and_maybe_check(
module_specifier.clone(),
program_state.clone(),
debug,
)
.await?;

info!(
"{} {}",
colors::green("Bundle"),
module_specifier.to_string()
);
let bundle_str = bundle_module_graph(module_graph, flags, debug)?;

info!(
"{} {}",
colors::green("Compile"),
module_specifier.to_string()
);
create_standalone_binary(bundle_str.as_bytes().to_vec(), out_file).await?;

Ok(())
}

async fn info_command(
flags: Flags,
maybe_specifier: Option<String>,
Expand Down Expand Up @@ -299,6 +339,73 @@ async fn eval_command(
Ok(())
}

async fn create_module_graph_and_maybe_check(
module_specifier: ModuleSpecifier,
program_state: Arc<ProgramState>,
debug: bool,
) -> Result<module_graph::Graph, AnyError> {
let handler = Rc::new(RefCell::new(FetchHandler::new(
&program_state,
// when bundling, dynamic imports are only access for their type safety,
// therefore we will allow the graph to access any module.
Permissions::allow_all(),
)?));
let mut builder = module_graph::GraphBuilder::new(
handler,
program_state.maybe_import_map.clone(),
program_state.lockfile.clone(),
);
builder.add(&module_specifier, false).await?;
let module_graph = builder.get_graph();

if !program_state.flags.no_check {
// TODO(@kitsonk) support bundling for workers
let lib = if program_state.flags.unstable {
module_graph::TypeLib::UnstableDenoWindow
} else {
module_graph::TypeLib::DenoWindow
};
let result_info =
module_graph.clone().check(module_graph::CheckOptions {
debug,
emit: false,
lib,
maybe_config_path: program_state.flags.config_path.clone(),
reload: program_state.flags.reload,
})?;

debug!("{}", result_info.stats);
if let Some(ignored_options) = result_info.maybe_ignored_options {
eprintln!("{}", ignored_options);
}
if !result_info.diagnostics.is_empty() {
return Err(generic_error(result_info.diagnostics.to_string()));
}
}

Ok(module_graph)
}

fn bundle_module_graph(
module_graph: module_graph::Graph,
flags: Flags,
debug: bool,
) -> Result<String, AnyError> {
let (bundle, stats, maybe_ignored_options) =
module_graph.bundle(module_graph::BundleOptions {
debug,
maybe_config_path: flags.config_path,
})?;
match maybe_ignored_options {
Some(ignored_options) if flags.no_check => {
eprintln!("{}", ignored_options);
}
_ => {}
}
debug!("{}", stats);
Ok(bundle)
}

async fn bundle_command(
flags: Flags,
source_file: String,
Expand All @@ -323,44 +430,12 @@ async fn bundle_command(
module_specifier.to_string()
);

let handler = Rc::new(RefCell::new(FetchHandler::new(
&program_state,
// when bundling, dynamic imports are only access for their type safety,
// therefore we will allow the graph to access any module.
Permissions::allow_all(),
)?));
let mut builder = module_graph::GraphBuilder::new(
handler,
program_state.maybe_import_map.clone(),
program_state.lockfile.clone(),
);
builder.add(&module_specifier, false).await?;
let module_graph = builder.get_graph();

if !flags.no_check {
// TODO(@kitsonk) support bundling for workers
let lib = if flags.unstable {
module_graph::TypeLib::UnstableDenoWindow
} else {
module_graph::TypeLib::DenoWindow
};
let result_info =
module_graph.clone().check(module_graph::CheckOptions {
debug,
emit: false,
lib,
maybe_config_path: flags.config_path.clone(),
reload: flags.reload,
})?;

debug!("{}", result_info.stats);
if let Some(ignored_options) = result_info.maybe_ignored_options {
eprintln!("{}", ignored_options);
}
if !result_info.diagnostics.is_empty() {
return Err(generic_error(result_info.diagnostics.to_string()));
}
}
let module_graph = create_module_graph_and_maybe_check(
module_specifier,
program_state.clone(),
debug,
)
.await?;

let mut paths_to_watch: Vec<PathBuf> = module_graph
.get_modules()
Expand Down Expand Up @@ -392,19 +467,7 @@ async fn bundle_command(
let flags = flags.clone();
let out_file = out_file.clone();
async move {
let (output, stats, maybe_ignored_options) =
module_graph.bundle(module_graph::BundleOptions {
debug,
maybe_config_path: flags.config_path,
})?;

match maybe_ignored_options {
Some(ignored_options) if flags.no_check => {
eprintln!("{}", ignored_options);
}
_ => {}
}
debug!("{}", stats);
let output = bundle_module_graph(module_graph, flags, debug)?;

debug!(">>>>> bundle END");

Expand Down Expand Up @@ -898,6 +961,10 @@ fn get_subcommand(
DenoSubcommand::Cache { files } => {
cache_command(flags, files).boxed_local()
}
DenoSubcommand::Compile {
source_file,
out_file,
} => compile_command(flags, source_file, out_file).boxed_local(),
DenoSubcommand::Fmt {
check,
files,
Expand Down Expand Up @@ -967,8 +1034,12 @@ pub fn main() {
colors::enable_ansi(); // For Windows 10

let args: Vec<String> = env::args().collect();
let flags = flags::flags_from_vec(args);
if let Err(err) = standalone::try_run_standalone_binary(args.clone()) {
eprintln!("{}: {}", colors::red_bold("error"), err.to_string());
std::process::exit(1);
}

let flags = flags::flags_from_vec(args);
if let Some(ref v8_flags) = flags.v8_flags {
init_v8_flags(v8_flags);
}
Expand Down
Loading