1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 use driver::session::Session;
14 use driver::{config, PpMode};
15 use driver::{PpmFlowGraph, PpmExpanded, PpmExpandedIdentified, PpmTyped};
16 use driver::{PpmIdentified};
18 use lib::llvm::{ContextRef, ModuleRef};
19 use metadata::common::LinkMeta;
20 use metadata::creader;
21 use metadata::creader::Loader;
23 use middle::cfg::graphviz::LabelledCFG;
24 use middle::{trans, freevars, kind, ty, typeck, lint, reachable};
25 use middle::dependency_format;
27 use util::common::time;
29 use util::nodemap::{NodeSet};
33 use serialize::{json, Encodable};
37 use std::io::MemReader;
40 use syntax::attr::{AttrMetaMethods};
41 use syntax::crateid::CrateId;
42 use syntax::ext::base::CrateLoader;
44 use syntax::parse::token;
45 use syntax::print::{pp, pprust};
48 pub fn host_triple() -> &'static str {
49 // Get the host triple out of the build environment. This ensures that our
50 // idea of the host triple is the same as for the set of libraries we've
51 // actually built. We can't just take LLVM's host triple because they
52 // normalize all ix86 architectures to i386.
54 // Instead of grabbing the host triple (for the current host), we grab (at
55 // compile time) the target triple that this rustc is built with and
56 // calling that (at runtime) the host triple.
57 (option_env!("CFG_COMPILER_HOST_TRIPLE")).
58 expect("CFG_COMPILER_HOST_TRIPLE")
61 pub fn compile_input(sess: Session,
62 cfg: ast::CrateConfig,
64 outdir: &Option<Path>,
65 output: &Option<Path>) {
66 // We need nested scopes here, because the intermediate results can keep
67 // large chunks of memory alive and we want to free them as soon as
68 // possible to keep the peak memory usage low
69 let (outputs, trans, sess) = {
70 let (outputs, expanded_crate, ast_map) = {
71 let krate = phase_1_parse_input(&sess, cfg, input);
72 if stop_after_phase_1(&sess) { return; }
73 let outputs = build_output_filenames(input,
76 krate.attrs.as_slice(),
78 let loader = &mut Loader::new(&sess);
79 let id = link::find_crate_id(krate.attrs.as_slice(),
80 outputs.out_filestem.as_slice());
81 let (expanded_crate, ast_map) =
82 phase_2_configure_and_expand(&sess, loader, krate, &id);
83 (outputs, expanded_crate, ast_map)
85 write_out_deps(&sess, input, &outputs, &expanded_crate);
87 if stop_after_phase_2(&sess) { return; }
89 let analysis = phase_3_run_analysis_passes(sess, &expanded_crate, ast_map);
90 if stop_after_phase_3(&analysis.ty_cx.sess) { return; }
91 let (tcx, trans) = phase_4_translate_to_llvm(expanded_crate,
94 // Discard interned strings as they are no longer required.
95 token::get_ident_interner().clear();
97 (outputs, trans, tcx.sess)
99 phase_5_run_llvm_passes(&sess, &trans, &outputs);
100 if stop_after_phase_5(&sess) { return; }
101 phase_6_link_output(&sess, &trans, &outputs);
105 * The name used for source code that doesn't originate in a file
106 * (e.g. source from stdin or a string)
108 pub fn anon_src() -> String {
112 pub fn source_name(input: &Input) -> String {
114 // FIXME (#9639): This needs to handle non-utf8 paths
115 FileInput(ref ifile) => ifile.as_str().unwrap().to_string(),
116 StrInput(_) => anon_src()
121 /// Load source from file
123 /// The string is the source
128 fn filestem(&self) -> String {
130 FileInput(ref ifile) => ifile.filestem_str().unwrap().to_string(),
131 StrInput(_) => "rust_out".to_string(),
137 pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
139 let krate = time(sess.time_passes(), "parsing", (), |_| {
141 FileInput(ref file) => {
142 parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess)
144 StrInput(ref src) => {
145 parse::parse_crate_from_source_str(anon_src().to_string(),
153 if sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0 {
154 let mut stdout = io::BufferedWriter::new(io::stdout());
155 let mut json = json::PrettyEncoder::new(&mut stdout);
156 // unwrapping so IoError isn't ignored
157 krate.encode(&mut json).unwrap();
160 if sess.show_span() {
161 front::show_span::run(sess, &krate);
167 // For continuing compilation after a parsed crate has been
170 /// Run the "early phases" of the compiler: initial `cfg` processing,
171 /// syntax expansion, secondary `cfg` expansion, synthesis of a test
172 /// harness if one is to be provided and injection of a dependency on the
173 /// standard library and prelude.
174 pub fn phase_2_configure_and_expand(sess: &Session,
175 loader: &mut CrateLoader,
176 mut krate: ast::Crate,
178 -> (ast::Crate, syntax::ast_map::Map) {
179 let time_passes = sess.time_passes();
181 *sess.crate_types.borrow_mut() = collect_crate_types(sess, krate.attrs.as_slice());
183 time(time_passes, "gated feature checking", (), |_|
184 front::feature_gate::check_crate(sess, &krate));
186 krate = time(time_passes, "crate injection", krate, |krate|
187 front::std_inject::maybe_inject_crates_ref(sess, krate));
189 // strip before expansion to allow macros to depend on
190 // configuration variables e.g/ in
192 // #[macro_escape] #[cfg(foo)]
193 // mod bar { macro_rules! baz!(() => {{}}) }
195 // baz! should not use this definition unless foo is enabled.
197 krate = time(time_passes, "configuration 1", krate, |krate|
198 front::config::strip_unconfigured_items(krate));
200 krate = time(time_passes, "expansion", krate, |krate| {
201 // Windows dlls do not have rpaths, so they don't know how to find their
202 // dependencies. It's up to us to tell the system where to find all the
203 // dependent dlls. Note that this uses cfg!(windows) as opposed to
204 // targ_cfg because syntax extensions are always loaded for the host
205 // compiler, not for the target.
207 sess.host_filesearch().add_dylib_search_paths();
209 let cfg = syntax::ext::expand::ExpansionConfig {
211 deriving_hash_type_parameter: sess.features.default_type_params.get(),
212 crate_id: crate_id.clone(),
214 syntax::ext::expand::expand_crate(&sess.parse_sess,
219 // strip again, in case expansion added anything with a #[cfg].
220 krate = time(time_passes, "configuration 2", krate, |krate|
221 front::config::strip_unconfigured_items(krate));
223 krate = time(time_passes, "maybe building test harness", krate, |krate|
224 front::test::modify_for_testing(sess, krate));
226 krate = time(time_passes, "prelude injection", krate, |krate|
227 front::std_inject::maybe_inject_prelude(sess, krate));
229 let (krate, map) = time(time_passes, "assigning node ids and indexing ast", krate, |krate|
230 front::assign_node_ids_and_map::assign_node_ids_and_map(sess, krate));
232 if sess.opts.debugging_opts & config::AST_JSON != 0 {
233 let mut stdout = io::BufferedWriter::new(io::stdout());
234 let mut json = json::PrettyEncoder::new(&mut stdout);
235 // unwrapping so IoError isn't ignored
236 krate.encode(&mut json).unwrap();
242 pub struct CrateAnalysis {
243 pub exp_map2: middle::resolve::ExportMap2,
244 pub exported_items: middle::privacy::ExportedItems,
245 pub public_items: middle::privacy::PublicItems,
247 pub reachable: NodeSet,
250 /// Run the resolution, typechecking, region checking and other
251 /// miscellaneous analysis passes on the crate. Return various
252 /// structures carrying the results of the analysis.
253 pub fn phase_3_run_analysis_passes(sess: Session,
255 ast_map: syntax::ast_map::Map) -> CrateAnalysis {
257 let time_passes = sess.time_passes();
259 time(time_passes, "external crate/lib resolution", (), |_|
260 creader::read_crates(&sess, krate));
262 let lang_items = time(time_passes, "language item collection", (), |_|
263 middle::lang_items::collect_language_items(krate, &sess));
265 let middle::resolve::CrateMap {
268 trait_map: trait_map,
269 external_exports: external_exports,
270 last_private_map: last_private_map
272 time(time_passes, "resolution", (), |_|
273 middle::resolve::resolve_crate(&sess, &lang_items, krate));
275 // Discard MTWT tables that aren't required past resolution.
276 syntax::ext::mtwt::clear_tables();
278 let named_region_map = time(time_passes, "lifetime resolution", (),
279 |_| middle::resolve_lifetime::krate(&sess, krate));
281 time(time_passes, "looking for entry point", (),
282 |_| middle::entry::find_entry_point(&sess, krate, &ast_map));
284 sess.macro_registrar_fn.set(
285 time(time_passes, "looking for macro registrar", (), |_|
286 syntax::ext::registrar::find_macro_registrar(
287 sess.diagnostic(), krate)));
289 let freevars = time(time_passes, "freevar finding", (), |_|
290 freevars::annotate_freevars(&def_map, krate));
292 let region_map = time(time_passes, "region resolution", (), |_|
293 middle::region::resolve_crate(&sess, krate));
295 time(time_passes, "loop checking", (), |_|
296 middle::check_loop::check_crate(&sess, krate));
298 let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map,
299 freevars, region_map, lang_items);
301 // passes are timed inside typeck
302 typeck::check_crate(&ty_cx, trait_map, krate);
304 time(time_passes, "check static items", (), |_|
305 middle::check_static::check_crate(&ty_cx, krate));
307 // These next two const passes can probably be merged
308 time(time_passes, "const marking", (), |_|
309 middle::const_eval::process_crate(krate, &ty_cx));
311 time(time_passes, "const checking", (), |_|
312 middle::check_const::check_crate(krate, &ty_cx));
314 let maps = (external_exports, last_private_map);
315 let (exported_items, public_items) =
316 time(time_passes, "privacy checking", maps, |(a, b)|
317 middle::privacy::check_crate(&ty_cx, &exp_map2, a, b, krate));
319 time(time_passes, "effect checking", (), |_|
320 middle::effect::check_crate(&ty_cx, krate));
322 time(time_passes, "match checking", (), |_|
323 middle::check_match::check_crate(&ty_cx, krate));
325 time(time_passes, "liveness checking", (), |_|
326 middle::liveness::check_crate(&ty_cx, krate));
328 time(time_passes, "borrow checking", (), |_|
329 middle::borrowck::check_crate(&ty_cx, krate));
331 time(time_passes, "kind checking", (), |_|
332 kind::check_crate(&ty_cx, krate));
335 time(time_passes, "reachability checking", (), |_|
336 reachable::find_reachable(&ty_cx, &exported_items));
338 time(time_passes, "death checking", (), |_| {
339 middle::dead::check_crate(&ty_cx,
345 time(time_passes, "lint checking", (), |_|
346 lint::check_crate(&ty_cx, &exported_items, krate));
351 exported_items: exported_items,
352 public_items: public_items,
353 reachable: reachable_map,
357 pub struct CrateTranslation {
358 pub context: ContextRef,
359 pub module: ModuleRef,
360 pub metadata_module: ModuleRef,
362 pub metadata: Vec<u8>,
363 pub reachable: Vec<String>,
364 pub crate_formats: dependency_format::Dependencies,
365 pub no_builtins: bool,
368 /// Run the translation phase to LLVM, after which the AST and analysis can
370 pub fn phase_4_translate_to_llvm(krate: ast::Crate,
371 analysis: CrateAnalysis,
372 outputs: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
373 let time_passes = analysis.ty_cx.sess.time_passes();
375 time(time_passes, "resolving dependency formats", (), |_|
376 dependency_format::calculate(&analysis.ty_cx));
378 // Option dance to work around the lack of stack once closures.
379 time(time_passes, "translation", (krate, analysis), |(krate, analysis)|
380 trans::base::trans_crate(krate, analysis, outputs))
383 /// Run LLVM itself, producing a bitcode file, assembly file or object file
384 /// as a side effect.
385 pub fn phase_5_run_llvm_passes(sess: &Session,
386 trans: &CrateTranslation,
387 outputs: &OutputFilenames) {
388 if sess.opts.cg.no_integrated_as {
389 let output_type = link::OutputTypeAssembly;
391 time(sess.time_passes(), "LLVM passes", (), |_|
392 link::write::run_passes(sess, trans, [output_type], outputs));
394 link::write::run_assembler(sess, outputs);
396 // Remove assembly source, unless --save-temps was specified
397 if !sess.opts.cg.save_temps {
398 fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap();
401 time(sess.time_passes(), "LLVM passes", (), |_|
402 link::write::run_passes(sess,
404 sess.opts.output_types.as_slice(),
409 /// Run the linker on any artifacts that resulted from the LLVM run.
410 /// This should produce either a finished executable or library.
411 pub fn phase_6_link_output(sess: &Session,
412 trans: &CrateTranslation,
413 outputs: &OutputFilenames) {
414 time(sess.time_passes(), "linking", (), |_|
415 link::link_binary(sess,
418 &trans.link.crateid));
421 pub fn stop_after_phase_3(sess: &Session) -> bool {
422 if sess.opts.no_trans {
423 debug!("invoked with --no-trans, returning early from compile_input");
429 pub fn stop_after_phase_1(sess: &Session) -> bool {
430 if sess.opts.parse_only {
431 debug!("invoked with --parse-only, returning early from compile_input");
434 if sess.show_span() {
437 return sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0;
440 pub fn stop_after_phase_2(sess: &Session) -> bool {
441 if sess.opts.no_analysis {
442 debug!("invoked with --no-analysis, returning early from compile_input");
445 return sess.opts.debugging_opts & config::AST_JSON != 0;
448 pub fn stop_after_phase_5(sess: &Session) -> bool {
449 if !sess.opts.output_types.iter().any(|&i| i == link::OutputTypeExe) {
450 debug!("not building executable, returning early from compile_input");
456 fn write_out_deps(sess: &Session,
458 outputs: &OutputFilenames,
459 krate: &ast::Crate) {
460 let id = link::find_crate_id(krate.attrs.as_slice(),
461 outputs.out_filestem.as_slice());
463 let mut out_filenames = Vec::new();
464 for output_type in sess.opts.output_types.iter() {
465 let file = outputs.path(*output_type);
467 link::OutputTypeExe => {
468 for output in sess.crate_types.borrow().iter() {
469 let p = link::filename_for_input(sess, *output, &id, &file);
470 out_filenames.push(p);
473 _ => { out_filenames.push(file); }
477 // Write out dependency rules to the dep-info file if requested with
479 let deps_filename = match sess.opts.write_dependency_info {
480 // Use filename from --dep-file argument if given
481 (true, Some(ref filename)) => filename.clone(),
482 // Use default filename: crate source filename with extension replaced
484 (true, None) => match *input {
485 FileInput(..) => outputs.with_extension("d"),
487 sess.warn("can not write --dep-info without a filename \
488 when compiling stdin.");
496 // Build a list of files used to compile the output and
497 // write Makefile-compatible dependency rules
498 let files: Vec<String> = sess.codemap().files.borrow()
499 .iter().filter(|fmap| fmap.is_real_file())
500 .map(|fmap| fmap.name.to_string())
502 let mut file = try!(io::File::create(&deps_filename));
503 for path in out_filenames.iter() {
504 try!(write!(&mut file as &mut Writer,
505 "{}: {}\n\n", path.display(), files.connect(" ")));
513 sess.fatal(format!("error writing dependencies to `{}`: {}",
514 deps_filename.display(), e).as_slice());
519 struct IdentifiedAnnotation;
521 impl pprust::PpAnn for IdentifiedAnnotation {
523 s: &mut pprust::State,
524 node: pprust::AnnNode) -> io::IoResult<()> {
526 pprust::NodeExpr(_) => s.popen(),
531 s: &mut pprust::State,
532 node: pprust::AnnNode) -> io::IoResult<()> {
534 pprust::NodeItem(item) => {
535 try!(pp::space(&mut s.s));
536 s.synth_comment(item.id.to_str().to_string())
538 pprust::NodeBlock(blk) => {
539 try!(pp::space(&mut s.s));
540 s.synth_comment((format!("block {}", blk.id)).to_string())
542 pprust::NodeExpr(expr) => {
543 try!(pp::space(&mut s.s));
544 try!(s.synth_comment(expr.id.to_str().to_string()));
547 pprust::NodePat(pat) => {
548 try!(pp::space(&mut s.s));
549 s.synth_comment((format!("pat {}", pat.id)).to_string())
555 struct TypedAnnotation {
556 analysis: CrateAnalysis,
559 impl pprust::PpAnn for TypedAnnotation {
561 s: &mut pprust::State,
562 node: pprust::AnnNode) -> io::IoResult<()> {
564 pprust::NodeExpr(_) => s.popen(),
569 s: &mut pprust::State,
570 node: pprust::AnnNode) -> io::IoResult<()> {
571 let tcx = &self.analysis.ty_cx;
573 pprust::NodeExpr(expr) => {
574 try!(pp::space(&mut s.s));
575 try!(pp::word(&mut s.s, "as"));
576 try!(pp::space(&mut s.s));
577 try!(pp::word(&mut s.s,
580 ty::expr_ty(tcx, expr)).as_slice()));
588 pub fn pretty_print_input(sess: Session,
589 cfg: ast::CrateConfig,
592 ofile: Option<Path>) {
593 let krate = phase_1_parse_input(&sess, cfg, input);
594 let id = link::find_crate_id(krate.attrs.as_slice(),
595 input.filestem().as_slice());
597 let (krate, ast_map, is_expanded) = match ppm {
598 PpmExpanded | PpmExpandedIdentified | PpmTyped | PpmFlowGraph(_) => {
599 let loader = &mut Loader::new(&sess);
600 let (krate, ast_map) = phase_2_configure_and_expand(&sess,
604 (krate, Some(ast_map), true)
606 _ => (krate, None, false)
609 let src_name = source_name(input);
610 let src = Vec::from_slice(sess.codemap()
611 .get_filemap(src_name.as_slice())
614 let mut rdr = MemReader::new(src);
616 let out = match ofile {
617 None => box io::stdout() as Box<Writer>,
619 let r = io::File::create(&p);
621 Ok(w) => box w as Box<Writer>,
622 Err(e) => fail!("print-print failed to open {} due to {}",
628 PpmIdentified | PpmExpandedIdentified => {
629 pprust::print_crate(sess.codemap(),
632 src_name.to_string(),
635 &IdentifiedAnnotation,
639 let ast_map = ast_map.expect("--pretty=typed missing ast_map");
640 let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
641 let annotation = TypedAnnotation {
644 pprust::print_crate(annotation.analysis.ty_cx.sess.codemap(),
645 annotation.analysis.ty_cx.sess.diagnostic(),
647 src_name.to_string(),
653 PpmFlowGraph(nodeid) => {
654 let ast_map = ast_map.expect("--pretty flowgraph missing ast_map");
655 let node = ast_map.find(nodeid).unwrap_or_else(|| {
656 sess.fatal(format_strbuf!("--pretty flowgraph couldn't find id: {}",
659 let block = match node {
660 syntax::ast_map::NodeBlock(block) => block,
662 let message = format_strbuf!("--pretty=flowgraph needs block, got {:?}",
665 // point to what was found, if there's an
667 match ast_map.opt_span(nodeid) {
668 Some(sp) => sess.span_fatal(sp, message.as_slice()),
669 None => sess.fatal(message.as_slice())
673 let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
674 print_flowgraph(analysis, block, out)
677 pprust::print_crate(sess.codemap(),
680 src_name.to_string(),
690 fn print_flowgraph<W:io::Writer>(analysis: CrateAnalysis,
691 block: ast::P<ast::Block>,
692 mut out: W) -> io::IoResult<()> {
693 let ty_cx = &analysis.ty_cx;
694 let cfg = cfg::CFG::new(ty_cx, block);
695 let lcfg = LabelledCFG { ast_map: &ty_cx.map,
697 name: format!("block{}", block.id).to_string(), };
698 debug!("cfg: {:?}", cfg);
699 let r = dot::render(&lcfg, &mut out);
700 return expand_err_details(r);
702 fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> {
704 let orig_detail = ioerr.detail.clone();
705 let m = "graphviz::render failed";
707 detail: Some(match orig_detail {
708 None => m.into_string(),
709 Some(d) => format_strbuf!("{}: {}", m, d)
717 pub fn collect_crate_types(session: &Session,
718 attrs: &[ast::Attribute]) -> Vec<config::CrateType> {
719 // Unconditionally collect crate types from attributes to make them used
720 let attr_types: Vec<config::CrateType> = attrs.iter().filter_map(|a| {
721 if a.check_name("crate_type") {
722 match a.value_str() {
723 Some(ref n) if n.equiv(&("rlib")) => {
724 Some(config::CrateTypeRlib)
726 Some(ref n) if n.equiv(&("dylib")) => {
727 Some(config::CrateTypeDylib)
729 Some(ref n) if n.equiv(&("lib")) => {
730 Some(config::default_lib_output())
732 Some(ref n) if n.equiv(&("staticlib")) => {
733 Some(config::CrateTypeStaticlib)
735 Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable),
737 session.add_lint(lint::UnknownCrateType,
740 "invalid `crate_type` \
745 session.add_lint(lint::UnknownCrateType,
748 "`crate_type` requires a \
758 // If we're generating a test executable, then ignore all other output
759 // styles at all other locations
760 if session.opts.test {
761 return vec!(config::CrateTypeExecutable)
764 // Only check command line flags if present. If no types are specified by
765 // command line, then reuse the empty `base` Vec to hold the types that
766 // will be found in crate attributes.
767 let mut base = session.opts.crate_types.clone();
771 base.extend(attr_types.move_iter());
773 base.push(config::CrateTypeExecutable);
775 base.as_mut_slice().sort();
781 pub struct OutputFilenames {
782 pub out_directory: Path,
783 pub out_filestem: String,
784 pub single_output_file: Option<Path>,
787 impl OutputFilenames {
788 pub fn path(&self, flavor: link::OutputType) -> Path {
789 match self.single_output_file {
790 Some(ref path) => return path.clone(),
793 self.temp_path(flavor)
796 pub fn temp_path(&self, flavor: link::OutputType) -> Path {
797 let base = self.out_directory.join(self.out_filestem.as_slice());
799 link::OutputTypeBitcode => base.with_extension("bc"),
800 link::OutputTypeAssembly => base.with_extension("s"),
801 link::OutputTypeLlvmAssembly => base.with_extension("ll"),
802 link::OutputTypeObject => base.with_extension("o"),
803 link::OutputTypeExe => base,
807 pub fn with_extension(&self, extension: &str) -> Path {
808 let stem = self.out_filestem.as_slice();
809 self.out_directory.join(stem).with_extension(extension)
813 pub fn build_output_filenames(input: &Input,
815 ofile: &Option<Path>,
816 attrs: &[ast::Attribute],
821 // "-" as input file will cause the parser to read from stdin so we
822 // have to make up a name
823 // We want to toss everything after the final '.'
824 let dirpath = match *odir {
825 Some(ref d) => d.clone(),
826 None => Path::new(".")
829 let mut stem = input.filestem();
831 // If a crateid is present, we use it as the link name
832 let crateid = attr::find_crateid(attrs);
835 Some(crateid) => stem = crateid.name.to_string(),
838 out_directory: dirpath,
840 single_output_file: None,
844 Some(ref out_file) => {
845 let ofile = if sess.opts.output_types.len() > 1 {
846 sess.warn("ignoring specified output filename because multiple \
847 outputs were requested");
850 Some(out_file.clone())
853 sess.warn("ignoring --out-dir flag due to -o flag.");
856 out_directory: out_file.dir_path(),
857 out_filestem: out_file.filestem_str().unwrap().to_string(),
858 single_output_file: ofile,