Skip to content

Commit 28027d3

Browse files
bleibigbrson
authored andcommitted
Add :load command to REPL that can compile and load external libraries
1 parent bbc04db commit 28027d3

File tree

1 file changed

+96
-5
lines changed

1 file changed

+96
-5
lines changed

src/librusti/rusti.rc

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ struct Repl {
5151
binary: ~str,
5252
running: bool,
5353
view_items: ~str,
54+
lib_search_paths: ~[~str],
5455
stmts: ~str
5556
}
5657

@@ -132,7 +133,7 @@ fn run(repl: Repl, input: ~str) -> Repl {
132133
let options: @session::options = @{
133134
crate_type: session::unknown_crate,
134135
binary: repl.binary,
135-
addl_lib_search_paths: ~[os::getcwd()],
136+
addl_lib_search_paths: repl.lib_search_paths.map(|p| Path(*p)),
136137
.. *session::basic_options()
137138
};
138139

@@ -284,6 +285,63 @@ fn run(repl: Repl, input: ~str) -> Repl {
284285
record(repl, blk, sess.parse_sess.interner)
285286
}
286287

288+
// Compiles a crate given by the filename as a library if the compiled
289+
// version doesn't exist or is older than the source file. Binary is
290+
// the name of the compiling executable. Returns Some(true) if it
291+
// successfully compiled, Some(false) if the crate wasn't compiled
292+
// because it already exists and is newer than the source file, or
293+
// None if there were compile errors.
294+
fn compile_crate(src_filename: ~str, binary: ~str) -> Option<bool> {
295+
match do task::try {
296+
let src_path = Path(src_filename);
297+
let options: @session::options = @{
298+
binary: binary,
299+
addl_lib_search_paths: ~[os::getcwd()],
300+
.. *session::basic_options()
301+
};
302+
let input = driver::file_input(src_path);
303+
let sess = driver::build_session(options, diagnostic::emit);
304+
sess.building_library = true;
305+
let cfg = driver::build_configuration(sess, binary, input);
306+
let outputs = driver::build_output_filenames(input, &None, &None, sess);
307+
// If the library already exists and is newer than the source
308+
// file, skip compilation and return None.
309+
let mut should_compile = true;
310+
let dir = os::list_dir_path(&Path(outputs.out_filename.dirname()));
311+
let maybe_lib_path = do dir.find |file| {
312+
// The actual file's name has a hash value and version
313+
// number in it which is unknown at this time, so looking
314+
// for a file that matches out_filename won't work,
315+
// instead we guess which file is the library by matching
316+
// the prefix and suffix of out_filename to files in the
317+
// directory.
318+
let file_str = file.filename().get();
319+
file_str.starts_with(outputs.out_filename.filestem().get())
320+
&& file_str.ends_with(outputs.out_filename.filetype().get())
321+
};
322+
match maybe_lib_path {
323+
Some(lib_path) => {
324+
let (src_mtime, _) = src_path.get_mtime().get();
325+
let (lib_mtime, _) = lib_path.get_mtime().get();
326+
if lib_mtime >= src_mtime {
327+
should_compile = false;
328+
}
329+
},
330+
None => { },
331+
}
332+
if (should_compile) {
333+
io::println(fmt!("compiling %s...", src_filename));
334+
driver::compile_upto(sess, cfg, input, driver::cu_everything,
335+
Some(outputs));
336+
true
337+
} else { false }
338+
} {
339+
Ok(true) => Some(true),
340+
Ok(false) => Some(false),
341+
Err(_) => None,
342+
}
343+
}
344+
287345
/// Tries to get a line from rl after outputting a prompt. Returns
288346
/// None if no input was read (e.g. EOF was reached).
289347
fn get_line(prompt: ~str) -> Option<~str> {
@@ -302,7 +360,7 @@ fn get_line(prompt: ~str) -> Option<~str> {
302360

303361
/// Run a command, e.g. :clear, :exit, etc.
304362
fn run_cmd(repl: &mut Repl, _in: io::Reader, _out: io::Writer,
305-
cmd: ~str, _args: ~[~str]) -> CmdAction {
363+
cmd: ~str, args: ~[~str]) -> CmdAction {
306364
let mut action = action_none;
307365
match cmd {
308366
~"exit" => repl.running = false,
@@ -316,10 +374,43 @@ fn run_cmd(repl: &mut Repl, _in: io::Reader, _out: io::Writer,
316374
~"help" => {
317375
io::println(
318376
~":{\\n ..lines.. \\n:}\\n - execute multiline command\n" +
377+
~":load <crate> ... - loads given crates as dynamic libraries" +
319378
~":clear - clear the screen\n" +
320379
~":exit - exit from the repl\n" +
321380
~":help - show this message");
322381
}
382+
~"load" => {
383+
let mut loaded_crates: ~[~str] = ~[];
384+
for args.each |arg| {
385+
let (crate, filename) =
386+
if arg.ends_with(".rs") || arg.ends_with(".rc") {
387+
(arg.substr(0, arg.len() - 3), *arg)
388+
} else {
389+
(*arg, arg + ~".rs")
390+
};
391+
match compile_crate(filename, repl.binary) {
392+
Some(_) => loaded_crates.push(crate),
393+
None => { }
394+
}
395+
}
396+
for loaded_crates.each |crate| {
397+
let crate_path = Path(*crate);
398+
let crate_dir = crate_path.dirname();
399+
let crate_name = crate_path.filename().get();
400+
if !repl.view_items.contains(*crate) {
401+
repl.view_items += fmt!("extern mod %s;\n", crate_name);
402+
if !repl.lib_search_paths.contains(&crate_dir) {
403+
repl.lib_search_paths.push(crate_dir);
404+
}
405+
}
406+
}
407+
if loaded_crates.is_empty() {
408+
io::println("no crates loaded");
409+
} else {
410+
io::println(fmt!("crates loaded: %s",
411+
str::connect(loaded_crates, ", ")));
412+
}
413+
}
323414
~"{" => {
324415
let mut multiline_cmd = ~"";
325416
let mut end_multiline = false;
@@ -356,9 +447,7 @@ fn run_line(repl: &mut Repl, in: io::Reader, out: io::Writer, line: ~str)
356447

357448
if !cmd.is_empty() {
358449
let args = if len > 1 {
359-
do vec::view(split, 1, len - 1).map |arg| {
360-
*arg
361-
}
450+
vec::slice(split, 1, len)
362451
} else { ~[] };
363452

364453
match run_cmd(repl, in, out, cmd, args) {
@@ -394,6 +483,7 @@ pub fn main() {
394483
binary: args[0],
395484
running: true,
396485
view_items: ~"",
486+
lib_search_paths: ~[],
397487
stmts: ~""
398488
};
399489

@@ -403,6 +493,7 @@ pub fn main() {
403493
suggest(~":clear");
404494
suggest(~":exit");
405495
suggest(~":help");
496+
suggest(~":load");
406497
}
407498
}
408499
}

0 commit comments

Comments
 (0)