|
| 1 | +# `cargo-script` |
| 2 | + |
| 3 | +`cargo-script` is a Cargo subcommand designed to let people quickly and easily run Rust "scripts" which can make use of Cargo's package ecosystem. |
| 4 | + |
| 5 | +Or, to put it in other words, it lets you write useful, but small, Rust programs without having to create a new directory and faff about with `Cargo.toml`. |
| 6 | + |
| 7 | +As such, `cargo-script` does two major things: |
| 8 | + |
| 9 | +1. Given a script, it extracts the embedded Cargo manifest and merges it with some sensible defaults. This manifest, along with the source code, is written to a fresh Cargo package on-disk. |
| 10 | + |
| 11 | +2. It caches the generated and compiled packages, regenerating them only if the script or its metadata have changed. |
| 12 | + |
| 13 | +## Installation |
| 14 | + |
| 15 | +`cargo-script` requires a nightly build of `rustc` due to the use of several unstable features. Aside from that, it should build cleanly with `cargo`. |
| 16 | + |
| 17 | +Once built, you should place the resulting executable somewhere on your `PATH`. At that point, you should be able to invoke it by using `cargo script`. |
| 18 | + |
| 19 | +Note that you *can* run the executable directly, but the first argument will *need* to be `script`. |
| 20 | + |
| 21 | +## Usage |
| 22 | + |
| 23 | +The simplest way to use `cargo-script` is to simply pass it the name of the Rust script you want to execute: |
| 24 | + |
| 25 | +```shell |
| 26 | +$ echo 'fn main() { println!("Hello, World!"); }' > hello.rs |
| 27 | +$ cargo script hello.rs |
| 28 | + Compiling hello v0.1.0 (file:///C:/Users/drk/AppData/Local/Cargo/script-cache/file-hello-25c8c198030c5d089740-3ace88497b98af47db6e) |
| 29 | +Hello, World! |
| 30 | +$ cargo script hello # you can omit the file extension |
| 31 | +Hello, World! |
| 32 | +``` |
| 33 | + |
| 34 | +Note that `cargo-script` does not *currently* do anything to suppress the regular output of Cargo. This is *definitely* on purpose and *not* simply out of abject laziness. |
| 35 | + |
| 36 | +You may also embed a partial Cargo manifest at the start of your script, as shown below. `cargo-script` specifically supports the `.crs` extension to distinguish such files from regular Rust source, but it will process regular `.rs` files in *exactly* the same manner. |
| 37 | + |
| 38 | +`now.crs`: |
| 39 | + |
| 40 | + ```rust |
| 41 | + [dependencies] |
| 42 | + time = "0.1.25" |
| 43 | + --- |
| 44 | + extern crate time; |
| 45 | + fn main() { |
| 46 | + println!("{}", time::now().rfc822z()); |
| 47 | + } |
| 48 | + ``` |
| 49 | + |
| 50 | +```shell |
| 51 | +$ cargo script now |
| 52 | + Updating registry `https://github.com/rust-lang/crates.io-index` |
| 53 | + Compiling libc v0.1.8 |
| 54 | + Compiling gcc v0.3.5 |
| 55 | + Compiling time v0.1.25 |
| 56 | + Compiling now v0.1.0 (file:///C:/Users/drk/AppData/Local/Cargo/script-cache/file-now-1410beff463a5c50726f-8dbf2bcf69d2d8208c4c) |
| 57 | +Sat, 30 May 2015 19:26:57 +1000 |
| 58 | +``` |
| 59 | + |
| 60 | +The partial manifest is terminated by a line consisting entirely of whitespace and *at least* three hyphens. `cargo-script` will also end the manifest if it encounters anything that looks suscpiciously like Rust code, but this should not be relied upon; such detection is *extremely* hacky. |
| 61 | + |
| 62 | +If you are in a hurry, the above can also be accomplished by telling `cargo-script` that you wish to evaluate an *expression*, rather than an actual file: |
| 63 | + |
| 64 | +```shell |
| 65 | +$ cargo script --dep time --expr "{extern crate time; time::now().rfc822z()}" |
| 66 | + Updating registry `https://github.com/rust-lang/crates.io-index` |
| 67 | + Compiling gcc v0.3.5 |
| 68 | + Compiling libc v0.1.8 |
| 69 | + Compiling time v0.1.25 |
| 70 | + Compiling expr v0.1.0 (file:///C:/Users/drk/AppData/Local/Cargo/script-cache/expr-a7ffe37fbe6dccff132f) |
| 71 | +Sat, 30 May 2015 19:32:18 +1000 |
| 72 | +``` |
| 73 | + |
| 74 | +Dependencies can also be specified with specific versions (*e.g.* `--dep time=0.1.25`); when omitted, `cargo-script` will simply use `"*"` for the manifest. |
| 75 | + |
| 76 | +Finally, you can also use `cargo-script` to write a quick stream filter, by specifying a closure to be called for each line read from stdin, like so: |
| 77 | + |
| 78 | +```shell |
| 79 | +$ cat now.crs | cargo script --count --loop \ |
| 80 | + '|l,n| println!("{:>6}: {}", n, l.trim_right())' |
| 81 | + Compiling loop v0.1.0 (file:///C:/Users/drk/AppData/Local/Cargo/script-cache/loop-58079283761aab8433b1) |
| 82 | + 1: [dependencies] |
| 83 | + 2: time = "0.1.25" |
| 84 | + 3: --- |
| 85 | + 4: extern crate time; |
| 86 | + 5: fn main() { |
| 87 | + 6: println!("{}", time::now().rfc822z()); |
| 88 | + 7: } |
| 89 | +``` |
| 90 | +
|
| 91 | +Without the `--count` argument, only the contents of each line is passed to your closure. No, there is no easy way to create state that is captured from outside the closure; sorry. |
| 92 | +
|
| 93 | +## Things That Should Probably Be Done |
| 94 | +
|
| 95 | +* `not(windows)` port; see the `platform` module. |
| 96 | +
|
| 97 | +* Actually clean up the cache directory, rather than flooding it with megabytes of code and executables. |
| 98 | +
|
| 99 | +* *Definitely* clean up after a failed compilation. |
| 100 | +
|
| 101 | +* Somehow convince the Cargo devs to add aggressive caching of dependencies so that compiling anything that has dependencies doesn't take an age. |
| 102 | +
|
| 103 | +* *Maybe* don't cache based on content; currently, it means that *any* change to a script or expression causes Cargo to re-download and re-compile all dependencies which is *bloody miserable*. |
| 104 | +
|
| 105 | +* ...that, or add some sort of `--no-cache` flag that shoves everything into a single folder. |
| 106 | +
|
| 107 | +* Gist support? I mean, if it's good enough for playpen... |
0 commit comments