]> git.lizzy.rs Git - rust.git/blob - src/librust/rust.rs
librust: Stop rust tool from crashing on macos.
[rust.git] / src / librust / rust.rs
1 // Copyright 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.
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 // rust - central access to other rust tools
12 // FIXME #2238 Make commands run and test emit proper file endings on windows
13 // FIXME #2238 Make run only accept source that emits an executable
14
15 #[link(name = "rust",
16        vers = "0.8-pre",
17        uuid = "4a24da33-5cc8-4037-9352-2cbe9bd9d27c",
18        url = "https://github.com/mozilla/rust/tree/master/src/rust")];
19
20 #[license = "MIT/ASL2"];
21 #[crate_type = "lib"];
22
23 extern mod rustpkg;
24 extern mod rustdoc;
25 extern mod rusti;
26 extern mod rustc;
27
28 use std::io;
29 use std::os;
30 use std::run;
31 use std::libc::exit;
32
33 enum ValidUsage {
34     Valid(int), Invalid
35 }
36
37 impl ValidUsage {
38     fn is_valid(&self) -> bool {
39         match *self {
40             Valid(_)   => true,
41             Invalid    => false
42         }
43     }
44 }
45
46 enum Action {
47     Call(extern "Rust" fn(args: &[~str]) -> ValidUsage),
48     CallMain(&'static str, extern "Rust" fn()),
49 }
50
51 enum UsageSource<'self> {
52     UsgStr(&'self str),
53     UsgCall(extern "Rust" fn()),
54 }
55
56 struct Command<'self> {
57     cmd: &'self str,
58     action: Action,
59     usage_line: &'self str,
60     usage_full: UsageSource<'self>,
61 }
62
63 static NUM_OF_COMMANDS: uint = 7;
64
65 // FIXME(#7617): should just be &'static [Command<'static>]
66 // but mac os doesn't seem to like that and tries to loop
67 // past the end of COMMANDS in usage thus passing garbage
68 // to str::repeat and eventually malloc and crashing.
69 static COMMANDS: [Command<'static>, .. NUM_OF_COMMANDS] = [
70     Command{
71         cmd: "build",
72         action: CallMain("rustc", rustc::main),
73         usage_line: "compile rust source files",
74         usage_full: UsgCall(rustc_help),
75     },
76     Command{
77         cmd: "run",
78         action: Call(cmd_run),
79         usage_line: "build an executable, and run it",
80         usage_full: UsgStr(
81             "The run command is an shortcut for the command line \n\
82              \"rustc <filename> -o <filestem>~ && ./<filestem>~ [<arguments>...]\".\
83             \n\nUsage:\trust run <filename> [<arguments>...]"
84         )
85     },
86     Command{
87         cmd: "test",
88         action: Call(cmd_test),
89         usage_line: "build a test executable, and run it",
90         usage_full: UsgStr(
91             "The test command is an shortcut for the command line \n\
92             \"rustc --test <filename> -o <filestem>test~ && \
93             ./<filestem>test~\"\n\nUsage:\trust test <filename>"
94         )
95     },
96     Command{
97         cmd: "doc",
98         action: CallMain("rustdoc", rustdoc::main),
99         usage_line: "generate documentation from doc comments",
100         usage_full: UsgCall(rustdoc::config::usage),
101     },
102     Command{
103         cmd: "pkg",
104         action: CallMain("rustpkg", rustpkg::main),
105         usage_line: "download, build, install rust packages",
106         usage_full: UsgCall(rustpkg::usage::general),
107     },
108     Command{
109         cmd: "sketch",
110         action: CallMain("rusti", rusti::main),
111         usage_line: "run a rust interpreter",
112         usage_full: UsgStr("\nUsage:\trusti"),
113     },
114     Command{
115         cmd: "help",
116         action: Call(cmd_help),
117         usage_line: "show detailed usage of a command",
118         usage_full: UsgStr(
119             "The help command displays the usage text of another command.\n\
120             The text is either build in, or provided by the corresponding \
121             program.\n\nUsage:\trust help <command>"
122         )
123     }
124 ];
125
126 fn rustc_help() {
127     rustc::usage(os::args()[0].clone())
128 }
129
130 fn find_cmd(command_string: &str) -> Option<Command> {
131     do COMMANDS.iter().find_ |command| {
132         command.cmd == command_string
133     }.map_consume(|x| *x)
134 }
135
136 fn cmd_help(args: &[~str]) -> ValidUsage {
137     fn print_usage(command_string: ~str) -> ValidUsage {
138         match find_cmd(command_string) {
139             Some(command) => {
140                 match command.action {
141                     CallMain(prog, _) => printfln!(
142                         "The %s command is an alias for the %s program.",
143                         command.cmd, prog),
144                     _       => ()
145                 }
146                 match command.usage_full {
147                     UsgStr(msg) => printfln!("%s\n", msg),
148                     UsgCall(f)  => f(),
149                 }
150                 Valid(0)
151             },
152             None => Invalid
153         }
154     }
155
156     match args {
157         [ref command_string] => print_usage((*command_string).clone()),
158         _                    => Invalid
159     }
160 }
161
162 fn cmd_test(args: &[~str]) -> ValidUsage {
163     match args {
164         [ref filename] => {
165             let test_exec = Path(*filename).filestem().unwrap() + "test~";
166             invoke("rustc", &[~"--test", filename.to_owned(),
167                               ~"-o", test_exec.to_owned()], rustc::main);
168             let exit_code = run::process_status(~"./" + test_exec, []);
169             Valid(exit_code)
170         }
171         _ => Invalid
172     }
173 }
174
175 fn cmd_run(args: &[~str]) -> ValidUsage {
176     match args {
177         [ref filename, ..prog_args] => {
178             let exec = Path(*filename).filestem().unwrap() + "~";
179             invoke("rustc", &[filename.to_owned(), ~"-o", exec.to_owned()],
180                    rustc::main);
181             let exit_code = run::process_status(~"./"+exec, prog_args);
182             Valid(exit_code)
183         }
184         _ => Invalid
185     }
186 }
187
188 fn invoke(prog: &str, args: &[~str], f: &fn()) {
189     let mut osargs = ~[prog.to_owned()];
190     osargs.push_all_move(args.to_owned());
191     os::set_args(osargs);
192     f();
193 }
194
195 fn do_command(command: &Command, args: &[~str]) -> ValidUsage {
196     match command.action {
197         Call(f) => f(args),
198         CallMain(prog, f) => {
199             invoke(prog, args, f);
200             Valid(0)
201         }
202     }
203 }
204
205 fn usage() {
206     static INDENT: uint = 8;
207
208     io::print(
209         "The rust tool is a convenience for managing rust source code.\n\
210         It acts as a shortcut for programs of the rust tool chain.\n\
211         \n\
212         Usage:\trust <command> [arguments]\n\
213         \n\
214         The commands are:\n\
215         \n"
216     );
217
218     for COMMANDS.iter().advance |command| {
219         let padding = " ".repeat(INDENT - command.cmd.len());
220         printfln!("    %s%s%s", command.cmd, padding, command.usage_line);
221     }
222
223     io::print(
224         "\n\
225         Use \"rust help <command>\" for more information about a command.\n\
226         \n"
227     );
228
229 }
230
231 pub fn main() {
232     let os_args = os::args();
233
234     if (os_args.len() > 1 && (os_args[1] == ~"-v" || os_args[1] == ~"--version")) {
235         rustc::version(os_args[0]);
236         unsafe { exit(0); }
237     }
238
239     let args = os_args.tail();
240
241     if !args.is_empty() {
242         let r = find_cmd(*args.head());
243         for r.iter().advance |command| {
244             let result = do_command(command, args.tail());
245             match result {
246                 Valid(exit_code) => unsafe { exit(exit_code.to_i32()) },
247                 _                => loop
248             }
249         }
250     }
251
252     usage();
253 }