]> git.lizzy.rs Git - rust.git/blob - src/librustc_driver/lib.rs
Rollup merge of 21681 - japaric:no-warn, r=alexcrichton
[rust.git] / src / librustc_driver / lib.rs
1 // Copyright 2014 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.
4 //
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.
10
11 //! The Rust compiler.
12 //!
13 //! # Note
14 //!
15 //! This API is completely unstable and subject to change.
16
17 #![crate_name = "rustc_driver"]
18 #![unstable(feature = "rustc_private")]
19 #![feature(staged_api)]
20 #![staged_api]
21 #![crate_type = "dylib"]
22 #![crate_type = "rlib"]
23 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
24       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
25       html_root_url = "http://doc.rust-lang.org/nightly/")]
26
27 #![allow(unknown_features)]
28 #![feature(quote)]
29 #![feature(slicing_syntax, unsafe_destructor)]
30 #![feature(box_syntax)]
31 #![feature(rustc_diagnostic_macros)]
32 #![allow(unknown_features)] #![feature(int_uint)]
33 #![feature(collections)]
34 #![feature(core)]
35 #![feature(io)]
36 #![feature(libc)]
37 #![feature(os)]
38 #![feature(path)]
39 #![feature(rustc_private)]
40 #![feature(std_misc)]
41 #![feature(unicode)]
42
43 extern crate arena;
44 extern crate flate;
45 extern crate getopts;
46 extern crate graphviz;
47 extern crate libc;
48 extern crate rustc;
49 extern crate rustc_back;
50 extern crate rustc_borrowck;
51 extern crate rustc_privacy;
52 extern crate rustc_resolve;
53 extern crate rustc_trans;
54 extern crate rustc_typeck;
55 extern crate serialize;
56 extern crate "rustc_llvm" as llvm;
57 #[macro_use] extern crate log;
58 #[macro_use] extern crate syntax;
59
60 pub use syntax::diagnostic;
61
62 use driver::CompileController;
63
64 use rustc_resolve as resolve;
65 use rustc_trans::back::link;
66 use rustc_trans::save;
67 use rustc::session::{config, Session, build_session};
68 use rustc::session::config::{Input, PrintRequest, UnstableFeatures};
69 use rustc::lint::Lint;
70 use rustc::lint;
71 use rustc::metadata;
72 use rustc::metadata::creader::CrateOrString::Str;
73 use rustc::util::common::time;
74
75 use std::cmp::Ordering::Equal;
76 use std::old_io;
77 use std::iter::repeat;
78 use std::os;
79 use std::sync::mpsc::channel;
80 use std::thread;
81
82 use rustc::session::early_error;
83
84 use syntax::ast;
85 use syntax::parse;
86 use syntax::diagnostic::Emitter;
87 use syntax::diagnostics;
88
89 #[cfg(test)]
90 pub mod test;
91
92 pub mod driver;
93 pub mod pretty;
94
95 pub fn run(args: Vec<String>) -> int {
96     monitor(move |:| run_compiler(args.as_slice()));
97     0
98 }
99
100 static BUG_REPORT_URL: &'static str =
101     "http://doc.rust-lang.org/complement-bugreport.html";
102
103 fn run_compiler(args: &[String]) {
104     let matches = match handle_options(args.to_vec()) {
105         Some(matches) => matches,
106         None => return
107     };
108
109     let descriptions = diagnostics_registry();
110     match matches.opt_str("explain") {
111         Some(ref code) => {
112             match descriptions.find_description(&code[]) {
113                 Some(ref description) => {
114                     println!("{}", description);
115                 }
116                 None => {
117                     early_error(&format!("no extended information for {}", code)[]);
118                 }
119             }
120             return;
121         },
122         None => ()
123     }
124
125     let sopts = config::build_session_options(&matches);
126     let odir = matches.opt_str("out-dir").map(|o| Path::new(o));
127     let ofile = matches.opt_str("o").map(|o| Path::new(o));
128     let (input, input_file_path) = match matches.free.len() {
129         0u => {
130             if sopts.describe_lints {
131                 let mut ls = lint::LintStore::new();
132                 ls.register_builtin(None);
133                 describe_lints(&ls, false);
134                 return;
135             }
136             let sess = build_session(sopts, None, descriptions);
137             if print_crate_info(&sess, None, &odir, &ofile) {
138                 return;
139             }
140             early_error("no input filename given");
141         }
142         1u => {
143             let ifile = &matches.free[0][];
144             if ifile == "-" {
145                 let contents = old_io::stdin().read_to_end().unwrap();
146                 let src = String::from_utf8(contents).unwrap();
147                 (Input::Str(src), None)
148             } else {
149                 (Input::File(Path::new(ifile)), Some(Path::new(ifile)))
150             }
151         }
152         _ => early_error("multiple input filenames provided")
153     };
154
155     let mut sopts = sopts;
156     sopts.unstable_features = get_unstable_features_setting();
157
158     let mut sess = build_session(sopts, input_file_path, descriptions);
159
160     let cfg = config::build_configuration(&sess);
161     if print_crate_info(&sess, Some(&input), &odir, &ofile) {
162         return
163     }
164
165     let pretty = if sess.opts.debugging_opts.unstable_options {
166         matches.opt_default("pretty", "normal").map(|a| {
167             // stable pretty-print variants only
168             pretty::parse_pretty(&sess, a.as_slice(), false)
169         })
170     } else {
171         None
172     };
173     let pretty = if pretty.is_none() &&
174         sess.unstable_options() {
175             matches.opt_str("xpretty").map(|a| {
176                 // extended with unstable pretty-print variants
177                 pretty::parse_pretty(&sess, a.as_slice(), true)
178             })
179         } else {
180             pretty
181         };
182
183     match pretty.into_iter().next() {
184         Some((ppm, opt_uii)) => {
185             pretty::pretty_print_input(sess, cfg, &input, ppm, opt_uii, ofile);
186             return;
187         }
188         None => {/* continue */ }
189     }
190
191     if sess.unstable_options() {
192         sess.opts.show_span = matches.opt_str("show-span");
193     }
194
195     let r = matches.opt_strs("Z");
196     if r.contains(&("ls".to_string())) {
197         match input {
198             Input::File(ref ifile) => {
199                 let mut stdout = old_io::stdout();
200                 list_metadata(&sess, &(*ifile), &mut stdout).unwrap();
201             }
202             Input::Str(_) => {
203                 early_error("cannot list metadata for stdin");
204             }
205         }
206         return;
207     }
208
209     let plugins = sess.opts.debugging_opts.extra_plugins.clone();
210     let control = build_controller(&sess);
211     driver::compile_input(sess, cfg, &input, &odir, &ofile, Some(plugins), control);
212 }
213
214 fn build_controller<'a>(sess: &Session) -> CompileController<'a> {
215     let mut control = CompileController::basic();
216
217     if sess.opts.parse_only ||
218        sess.opts.show_span.is_some() ||
219        sess.opts.debugging_opts.ast_json_noexpand {
220         control.after_parse.stop = true;
221     }
222
223     if sess.opts.no_analysis || sess.opts.debugging_opts.ast_json {
224         control.after_write_deps.stop = true;
225     }
226
227     if sess.opts.no_trans {
228         control.after_analysis.stop = true;
229     }
230
231     if !sess.opts.output_types.iter().any(|&i| i == config::OutputTypeExe) {
232         control.after_llvm.stop = true;
233     }
234
235     if sess.opts.debugging_opts.save_analysis {
236         control.after_analysis.callback = box |state| {
237             time(state.session.time_passes(), "save analysis", state.krate.unwrap(), |krate|
238                  save::process_crate(state.session,
239                                      krate,
240                                      state.analysis.unwrap(),
241                                      state.out_dir));
242         };
243         control.make_glob_map = resolve::MakeGlobMap::Yes;
244     }
245
246     control
247 }
248
249 pub fn get_unstable_features_setting() -> UnstableFeatures {
250     // Whether this is a feature-staged build, i.e. on the beta or stable channel
251     let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
252     // The secret key needed to get through the rustc build itself by
253     // subverting the unstable features lints
254     let bootstrap_secret_key = option_env!("CFG_BOOTSTRAP_KEY");
255     // The matching key to the above, only known by the build system
256     let bootstrap_provided_key = os::getenv("RUSTC_BOOTSTRAP_KEY");
257     match (disable_unstable_features, bootstrap_secret_key, bootstrap_provided_key) {
258         (_, Some(ref s), Some(ref p)) if s == p => UnstableFeatures::Cheat,
259         (true, _, _) => UnstableFeatures::Disallow,
260         (false, _, _) => UnstableFeatures::Default
261     }
262 }
263
264 /// Returns a version string such as "0.12.0-dev".
265 pub fn release_str() -> Option<&'static str> {
266     option_env!("CFG_RELEASE")
267 }
268
269 /// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built.
270 pub fn commit_hash_str() -> Option<&'static str> {
271     option_env!("CFG_VER_HASH")
272 }
273
274 /// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string.
275 pub fn commit_date_str() -> Option<&'static str> {
276     option_env!("CFG_VER_DATE")
277 }
278
279 /// Prints version information and returns None on success or an error
280 /// message on panic.
281 pub fn version(binary: &str, matches: &getopts::Matches) {
282     let verbose = matches.opt_present("verbose");
283
284     println!("{} {}", binary, option_env!("CFG_VERSION").unwrap_or("unknown version"));
285     if verbose {
286         fn unw(x: Option<&str>) -> &str { x.unwrap_or("unknown") }
287         println!("binary: {}", binary);
288         println!("commit-hash: {}", unw(commit_hash_str()));
289         println!("commit-date: {}", unw(commit_date_str()));
290         println!("host: {}", config::host_triple());
291         println!("release: {}", unw(release_str()));
292     }
293 }
294
295 fn usage(verbose: bool, include_unstable_options: bool) {
296     let groups = if verbose {
297         config::rustc_optgroups()
298     } else {
299         config::rustc_short_optgroups()
300     };
301     let groups : Vec<_> = groups.into_iter()
302         .filter(|x| include_unstable_options || x.is_stable())
303         .map(|x|x.opt_group)
304         .collect();
305     let message = format!("Usage: rustc [OPTIONS] INPUT");
306     let extra_help = if verbose {
307         ""
308     } else {
309         "\n    --help -v           Print the full set of options rustc accepts"
310     };
311     println!("{}\n\
312 Additional help:
313     -C help             Print codegen options
314     -W help             Print 'lint' options and default settings
315     -Z help             Print internal options for debugging rustc{}\n",
316               getopts::usage(message.as_slice(), groups.as_slice()),
317               extra_help);
318 }
319
320 fn describe_lints(lint_store: &lint::LintStore, loaded_plugins: bool) {
321     println!("
322 Available lint options:
323     -W <foo>           Warn about <foo>
324     -A <foo>           Allow <foo>
325     -D <foo>           Deny <foo>
326     -F <foo>           Forbid <foo> (deny, and deny all overrides)
327
328 ");
329
330     fn sort_lints(lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> {
331         let mut lints: Vec<_> = lints.into_iter().map(|(x, _)| x).collect();
332         lints.sort_by(|x: &&Lint, y: &&Lint| {
333             match x.default_level.cmp(&y.default_level) {
334                 // The sort doesn't case-fold but it's doubtful we care.
335                 Equal => x.name.cmp(y.name),
336                 r => r,
337             }
338         });
339         lints
340     }
341
342     fn sort_lint_groups(lints: Vec<(&'static str, Vec<lint::LintId>, bool)>)
343                      -> Vec<(&'static str, Vec<lint::LintId>)> {
344         let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect();
345         lints.sort_by(|&(x, _): &(&'static str, Vec<lint::LintId>),
346                        &(y, _): &(&'static str, Vec<lint::LintId>)| {
347             x.cmp(y)
348         });
349         lints
350     }
351
352     let (plugin, builtin): (Vec<_>, _) = lint_store.get_lints()
353         .iter().cloned().partition(|&(_, p)| p);
354     let plugin = sort_lints(plugin);
355     let builtin = sort_lints(builtin);
356
357     let (plugin_groups, builtin_groups): (Vec<_>, _) = lint_store.get_lint_groups()
358         .iter().cloned().partition(|&(_, _, p)| p);
359     let plugin_groups = sort_lint_groups(plugin_groups);
360     let builtin_groups = sort_lint_groups(builtin_groups);
361
362     let max_name_len = plugin.iter().chain(builtin.iter())
363         .map(|&s| s.name.width(true))
364         .max().unwrap_or(0);
365     let padded = |&: x: &str| {
366         let mut s = repeat(" ").take(max_name_len - x.chars().count())
367                                .collect::<String>();
368         s.push_str(x);
369         s
370     };
371
372     println!("Lint checks provided by rustc:\n");
373     println!("    {}  {:7.7}  {}", padded("name"), "default", "meaning");
374     println!("    {}  {:7.7}  {}", padded("----"), "-------", "-------");
375
376     let print_lints = |&: lints: Vec<&Lint>| {
377         for lint in lints.into_iter() {
378             let name = lint.name_lower().replace("_", "-");
379             println!("    {}  {:7.7}  {}",
380                      padded(&name[]), lint.default_level.as_str(), lint.desc);
381         }
382         println!("\n");
383     };
384
385     print_lints(builtin);
386
387
388
389     let max_name_len = plugin_groups.iter().chain(builtin_groups.iter())
390         .map(|&(s, _)| s.width(true))
391         .max().unwrap_or(0);
392     let padded = |&: x: &str| {
393         let mut s = repeat(" ").take(max_name_len - x.chars().count())
394                                .collect::<String>();
395         s.push_str(x);
396         s
397     };
398
399     println!("Lint groups provided by rustc:\n");
400     println!("    {}  {}", padded("name"), "sub-lints");
401     println!("    {}  {}", padded("----"), "---------");
402
403     let print_lint_groups = |&: lints: Vec<(&'static str, Vec<lint::LintId>)>| {
404         for (name, to) in lints.into_iter() {
405             let name = name.chars().map(|x| x.to_lowercase())
406                            .collect::<String>().replace("_", "-");
407             let desc = to.into_iter().map(|x| x.as_str().replace("_", "-"))
408                          .collect::<Vec<String>>().connect(", ");
409             println!("    {}  {}",
410                      padded(&name[]), desc);
411         }
412         println!("\n");
413     };
414
415     print_lint_groups(builtin_groups);
416
417     match (loaded_plugins, plugin.len(), plugin_groups.len()) {
418         (false, 0, _) | (false, _, 0) => {
419             println!("Compiler plugins can provide additional lints and lint groups. To see a \
420                       listing of these, re-run `rustc -W help` with a crate filename.");
421         }
422         (false, _, _) => panic!("didn't load lint plugins but got them anyway!"),
423         (true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."),
424         (true, l, g) => {
425             if l > 0 {
426                 println!("Lint checks provided by plugins loaded by this crate:\n");
427                 print_lints(plugin);
428             }
429             if g > 0 {
430                 println!("Lint groups provided by plugins loaded by this crate:\n");
431                 print_lint_groups(plugin_groups);
432             }
433         }
434     }
435 }
436
437 fn describe_debug_flags() {
438     println!("\nAvailable debug options:\n");
439     for &(name, _, opt_type_desc, desc) in config::DB_OPTIONS.iter() {
440         let (width, extra) = match opt_type_desc {
441             Some(..) => (21, "=val"),
442             None => (25, "")
443         };
444         println!("    -Z {:>width$}{} -- {}", name.replace("_", "-"),
445                  extra, desc, width=width);
446     }
447 }
448
449 fn describe_codegen_flags() {
450     println!("\nAvailable codegen options:\n");
451     for &(name, _, opt_type_desc, desc) in config::CG_OPTIONS.iter() {
452         let (width, extra) = match opt_type_desc {
453             Some(..) => (21, "=val"),
454             None => (25, "")
455         };
456         println!("    -C {:>width$}{} -- {}", name.replace("_", "-"),
457                  extra, desc, width=width);
458     }
459 }
460
461 /// Process command line options. Emits messages as appropriate. If compilation
462 /// should continue, returns a getopts::Matches object parsed from args, otherwise
463 /// returns None.
464 pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
465     // Throw away the first argument, the name of the binary
466     let _binary = args.remove(0);
467
468     if args.is_empty() {
469         // user did not write `-v` nor `-Z unstable-options`, so do not
470         // include that extra information.
471         usage(false, false);
472         return None;
473     }
474
475     let matches =
476         match getopts::getopts(&args[], &config::optgroups()[]) {
477             Ok(m) => m,
478             Err(f_stable_attempt) => {
479                 // redo option parsing, including unstable options this time,
480                 // in anticipation that the mishandled option was one of the
481                 // unstable ones.
482                 let all_groups : Vec<getopts::OptGroup>
483                     = config::rustc_optgroups().into_iter().map(|x|x.opt_group).collect();
484                 match getopts::getopts(args.as_slice(), all_groups.as_slice()) {
485                     Ok(m_unstable) => {
486                         let r = m_unstable.opt_strs("Z");
487                         let include_unstable_options = r.iter().any(|x| *x == "unstable-options");
488                         if include_unstable_options {
489                             m_unstable
490                         } else {
491                             early_error(f_stable_attempt.to_string().as_slice());
492                         }
493                     }
494                     Err(_) => {
495                         // ignore the error from the unstable attempt; just
496                         // pass the error we got from the first try.
497                         early_error(f_stable_attempt.to_string().as_slice());
498                     }
499                 }
500             }
501         };
502
503     let r = matches.opt_strs("Z");
504     let include_unstable_options = r.iter().any(|x| *x == "unstable-options");
505
506     if matches.opt_present("h") || matches.opt_present("help") {
507         usage(matches.opt_present("verbose"), include_unstable_options);
508         return None;
509     }
510
511     // Don't handle -W help here, because we might first load plugins.
512
513     let r = matches.opt_strs("Z");
514     if r.iter().any(|x| *x == "help") {
515         describe_debug_flags();
516         return None;
517     }
518
519     let cg_flags = matches.opt_strs("C");
520     if cg_flags.iter().any(|x| *x == "help") {
521         describe_codegen_flags();
522         return None;
523     }
524
525     if cg_flags.contains(&"passes=list".to_string()) {
526         unsafe { ::llvm::LLVMRustPrintPasses(); }
527         return None;
528     }
529
530     if matches.opt_present("version") {
531         version("rustc", &matches);
532         return None;
533     }
534
535     Some(matches)
536 }
537
538 fn print_crate_info(sess: &Session,
539                     input: Option<&Input>,
540                     odir: &Option<Path>,
541                     ofile: &Option<Path>)
542                     -> bool {
543     if sess.opts.prints.len() == 0 { return false }
544
545     let attrs = input.map(|input| parse_crate_attrs(sess, input));
546     for req in sess.opts.prints.iter() {
547         match *req {
548             PrintRequest::Sysroot => println!("{}", sess.sysroot().display()),
549             PrintRequest::FileNames |
550             PrintRequest::CrateName => {
551                 let input = match input {
552                     Some(input) => input,
553                     None => early_error("no input file provided"),
554                 };
555                 let attrs = attrs.as_ref().unwrap().as_slice();
556                 let t_outputs = driver::build_output_filenames(input,
557                                                                odir,
558                                                                ofile,
559                                                                attrs,
560                                                                sess);
561                 let id = link::find_crate_name(Some(sess), attrs.as_slice(),
562                                                input);
563                 if *req == PrintRequest::CrateName {
564                     println!("{}", id);
565                     continue
566                 }
567                 let crate_types = driver::collect_crate_types(sess, attrs);
568                 let metadata = driver::collect_crate_metadata(sess, attrs);
569                 *sess.crate_metadata.borrow_mut() = metadata;
570                 for &style in crate_types.iter() {
571                     let fname = link::filename_for_input(sess, style,
572                                                          id.as_slice(),
573                                                          &t_outputs.with_extension(""));
574                     println!("{}", fname.filename_display());
575                 }
576             }
577         }
578     }
579     return true;
580 }
581
582 fn parse_crate_attrs(sess: &Session, input: &Input) ->
583                      Vec<ast::Attribute> {
584     let result = match *input {
585         Input::File(ref ifile) => {
586             parse::parse_crate_attrs_from_file(ifile,
587                                                Vec::new(),
588                                                &sess.parse_sess)
589         }
590         Input::Str(ref src) => {
591             parse::parse_crate_attrs_from_source_str(
592                 driver::anon_src().to_string(),
593                 src.to_string(),
594                 Vec::new(),
595                 &sess.parse_sess)
596         }
597     };
598     result.into_iter().collect()
599 }
600
601 pub fn list_metadata(sess: &Session, path: &Path,
602                      out: &mut old_io::Writer) -> old_io::IoResult<()> {
603     metadata::loader::list_file_metadata(sess.target.target.options.is_like_osx, path, out)
604 }
605
606 /// Run a procedure which will detect panics in the compiler and print nicer
607 /// error messages rather than just failing the test.
608 ///
609 /// The diagnostic emitter yielded to the procedure should be used for reporting
610 /// errors of the compiler.
611 pub fn monitor<F:FnOnce()+Send>(f: F) {
612     static STACK_SIZE: uint = 8 * 1024 * 1024; // 8MB
613
614     let (tx, rx) = channel();
615     let w = old_io::ChanWriter::new(tx);
616     let mut r = old_io::ChanReader::new(rx);
617
618     let mut cfg = thread::Builder::new().name("rustc".to_string());
619
620     // FIXME: Hacks on hacks. If the env is trying to override the stack size
621     // then *don't* set it explicitly.
622     if os::getenv("RUST_MIN_STACK").is_none() {
623         cfg = cfg.stack_size(STACK_SIZE);
624     }
625
626     match cfg.scoped(move || { std::old_io::stdio::set_stderr(box w); f() }).join() {
627         Ok(()) => { /* fallthrough */ }
628         Err(value) => {
629             // Thread panicked without emitting a fatal diagnostic
630             if !value.is::<diagnostic::FatalError>() {
631                 let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None);
632
633                 // a .span_bug or .bug call has already printed what
634                 // it wants to print.
635                 if !value.is::<diagnostic::ExplicitBug>() {
636                     emitter.emit(
637                         None,
638                         "unexpected panic",
639                         None,
640                         diagnostic::Bug);
641                 }
642
643                 let xs = [
644                     "the compiler unexpectedly panicked. this is a bug.".to_string(),
645                     format!("we would appreciate a bug report: {}",
646                             BUG_REPORT_URL),
647                     "run with `RUST_BACKTRACE=1` for a backtrace".to_string(),
648                 ];
649                 for note in xs.iter() {
650                     emitter.emit(None, &note[], None, diagnostic::Note)
651                 }
652
653                 match r.read_to_string() {
654                     Ok(s) => println!("{}", s),
655                     Err(e) => {
656                         emitter.emit(None,
657                                      &format!("failed to read internal \
658                                               stderr: {}", e)[],
659                                      None,
660                                      diagnostic::Error)
661                     }
662                 }
663             }
664
665             // Panic so the process returns a failure code, but don't pollute the
666             // output with some unnecessary panic messages, we've already
667             // printed everything that we needed to.
668             old_io::stdio::set_stderr(box old_io::util::NullWriter);
669             panic!();
670         }
671     }
672 }
673
674 pub fn diagnostics_registry() -> diagnostics::registry::Registry {
675     use syntax::diagnostics::registry::Registry;
676
677     let all_errors = Vec::new() +
678         rustc::diagnostics::DIAGNOSTICS.as_slice() +
679         rustc_typeck::diagnostics::DIAGNOSTICS.as_slice() +
680         rustc_resolve::diagnostics::DIAGNOSTICS.as_slice();
681
682     Registry::new(&*all_errors)
683 }
684
685 pub fn main() {
686     let args = std::os::args();
687     let result = run(args);
688     std::os::set_exit_status(result);
689 }
690