]> git.lizzy.rs Git - rust.git/blob - src/librustc/rustc.rs
Merge remote-tracking branch 'brson/codemap'
[rust.git] / src / librustc / rustc.rs
1 // -*- rust -*-
2 use result::{Ok, Err};
3 use io::ReaderUtil;
4 use std::getopts;
5 use std::map::HashMap;
6 use getopts::{opt_present};
7 use getopts::groups;
8 use syntax::codemap;
9 use syntax::diagnostic;
10 use driver::driver::{host_triple, optgroups, early_error,
11                      str_input, file_input, build_session_options,
12                      build_session, build_configuration, parse_pretty,
13                      pp_mode, pretty_print_input, list_metadata,
14                      compile_input};
15 use driver::session;
16 use middle::lint;
17
18 fn version(argv0: &str) {
19     let mut vers = ~"unknown version";
20     let env_vers = env!("CFG_VERSION");
21     if env_vers.len() != 0 { vers = env_vers; }
22     io::println(fmt!("%s %s", argv0, vers));
23     io::println(fmt!("host: %s", host_triple()));
24 }
25
26 fn usage(argv0: &str) {
27     let message = fmt!("Usage: %s [OPTIONS] INPUT", argv0);
28     io::println(groups::usage(message, optgroups()) +
29                 ~"Additional help:
30     -W help             Print 'lint' options and default settings
31     -Z help             Print internal options for debugging rustc
32 ");
33 }
34
35 fn describe_warnings() {
36     io::println(fmt!("
37 Available lint options:
38     -W <foo>           Warn about <foo>
39     -A <foo>           Allow <foo>
40     -D <foo>           Deny <foo>
41     -F <foo>           Forbid <foo> (deny, and deny all overrides)
42 "));
43
44     let lint_dict = lint::get_lint_dict();
45     let mut max_key = 0;
46     for lint_dict.each_key |k| { max_key = uint::max(k.len(), max_key); }
47     fn padded(max: uint, s: &str) -> ~str {
48         str::from_bytes(vec::from_elem(max - s.len(), ' ' as u8)) + s
49     }
50     io::println(fmt!("\nAvailable lint checks:\n"));
51     io::println(fmt!("    %s  %7.7s  %s",
52                      padded(max_key, ~"name"), ~"default", ~"meaning"));
53     io::println(fmt!("    %s  %7.7s  %s\n",
54                      padded(max_key, ~"----"), ~"-------", ~"-------"));
55     for lint_dict.each |k, v| {
56         let k = str::replace(k, ~"_", ~"-");
57         io::println(fmt!("    %s  %7.7s  %s",
58                          padded(max_key, k),
59                          match v.default {
60                              lint::allow => ~"allow",
61                              lint::warn => ~"warn",
62                              lint::deny => ~"deny",
63                              lint::forbid => ~"forbid"
64                          },
65                          v.desc));
66     }
67     io::println(~"");
68 }
69
70 fn describe_debug_flags() {
71     io::println(fmt!("\nAvailable debug options:\n"));
72     for session::debugging_opts_map().each |pair| {
73         let (name, desc, _) = *pair;
74         io::println(fmt!("    -Z %-20s -- %s", name, desc));
75     }
76 }
77
78 fn run_compiler(args: &~[~str], demitter: diagnostic::emitter) {
79     // Don't display log spew by default. Can override with RUST_LOG.
80     logging::console_off();
81
82     let mut args = *args;
83     let binary = args.shift();
84
85     if args.is_empty() { usage(binary); return; }
86
87     let matches =
88         match getopts::groups::getopts(args, optgroups()) {
89           Ok(m) => m,
90           Err(f) => {
91             early_error(demitter, getopts::fail_str(f))
92           }
93         };
94
95     if opt_present(matches, ~"h") || opt_present(matches, ~"help") {
96         usage(binary);
97         return;
98     }
99
100     let lint_flags = vec::append(getopts::opt_strs(matches, ~"W"),
101                                  getopts::opt_strs(matches, ~"warn"));
102     if lint_flags.contains(&~"help") {
103         describe_warnings();
104         return;
105     }
106
107     if getopts::opt_strs(matches, ~"Z").contains(&~"help") {
108         describe_debug_flags();
109         return;
110     }
111
112     if opt_present(matches, ~"v") || opt_present(matches, ~"version") {
113         version(binary);
114         return;
115     }
116     let input = match vec::len(matches.free) {
117       0u => early_error(demitter, ~"no input filename given"),
118       1u => {
119         let ifile = matches.free[0];
120         if ifile == ~"-" {
121             let src = str::from_bytes(io::stdin().read_whole_stream());
122             str_input(src)
123         } else {
124             file_input(Path(ifile))
125         }
126       }
127       _ => early_error(demitter, ~"multiple input filenames provided")
128     };
129
130     let sopts = build_session_options(binary, matches, demitter);
131     let sess = build_session(sopts, demitter);
132     let odir = getopts::opt_maybe_str(matches, ~"out-dir");
133     let odir = odir.map(|o| Path(*o));
134     let ofile = getopts::opt_maybe_str(matches, ~"o");
135     let ofile = ofile.map(|o| Path(*o));
136     let cfg = build_configuration(sess, binary, input);
137     let pretty =
138         option::map(&getopts::opt_default(matches, ~"pretty",
139                                          ~"normal"),
140                     |a| parse_pretty(sess, *a) );
141     match pretty {
142       Some::<pp_mode>(ppm) => {
143         pretty_print_input(sess, cfg, input, ppm);
144         return;
145       }
146       None::<pp_mode> => {/* continue */ }
147     }
148     let ls = opt_present(matches, ~"ls");
149     if ls {
150         match input {
151           file_input(ifile) => {
152             list_metadata(sess, &ifile, io::stdout());
153           }
154           str_input(_) => {
155             early_error(demitter, ~"can not list metadata for stdin");
156           }
157         }
158         return;
159     }
160
161     compile_input(sess, cfg, input, &odir, &ofile);
162 }
163
164 enum monitor_msg {
165     fatal,
166     done,
167 }
168
169 impl monitor_msg : cmp::Eq {
170     pure fn eq(other: &monitor_msg) -> bool {
171         (self as uint) == ((*other) as uint)
172     }
173     pure fn ne(other: &monitor_msg) -> bool { !self.eq(other) }
174 }
175
176 /*
177 This is a sanity check that any failure of the compiler is performed
178 through the diagnostic module and reported properly - we shouldn't be calling
179 plain-old-fail on any execution path that might be taken. Since we have
180 console logging off by default, hitting a plain fail statement would make the
181 compiler silently exit, which would be terrible.
182
183 This method wraps the compiler in a subtask and injects a function into the
184 diagnostic emitter which records when we hit a fatal error. If the task
185 fails without recording a fatal error then we've encountered a compiler
186 bug and need to present an error.
187 */
188 fn monitor(+f: fn~(diagnostic::emitter)) {
189     let p = comm::Port();
190     let ch = comm::Chan(&p);
191
192     match do task::try |move f| {
193
194         // The 'diagnostics emitter'. Every error, warning, etc. should
195         // go through this function.
196         let demitter = fn@(cmsp: Option<(@codemap::CodeMap, codemap::span)>,
197                            msg: &str, lvl: diagnostic::level) {
198             if lvl == diagnostic::fatal {
199                 comm::send(ch, fatal);
200             }
201             diagnostic::emit(cmsp, msg, lvl);
202         };
203
204         struct finally {
205             ch: comm::Chan<monitor_msg>,
206             drop { comm::send(self.ch, done); }
207         }
208
209         let _finally = finally { ch: ch };
210
211         f(demitter)
212     } {
213         result::Ok(_) => { /* fallthrough */ }
214         result::Err(_) => {
215             // Task failed without emitting a fatal diagnostic
216             if comm::recv(p) == done {
217                 diagnostic::emit(
218                     None,
219                     diagnostic::ice_msg(~"unexpected failure"),
220                     diagnostic::error);
221
222                 for [
223                     ~"the compiler hit an unexpected failure path. \
224                      this is a bug",
225                     ~"try running with RUST_LOG=rustc=1,::rt::backtrace \
226                      to get further details and report the results \
227                      to github.com/mozilla/rust/issues"
228                 ].each |note| {
229                     diagnostic::emit(None, *note, diagnostic::note)
230                 }
231             }
232             // Fail so the process returns a failure code
233             fail;
234         }
235     }
236 }
237
238 fn main() {
239     let mut args = os::args();
240     do monitor |move args, demitter| {
241         run_compiler(&args, demitter);
242     }
243 }
244
245 // Local Variables:
246 // mode: rust
247 // fill-column: 78;
248 // indent-tabs-mode: nil
249 // c-basic-offset: 4
250 // buffer-file-coding-system: utf-8-unix
251 // End: