mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 00:34:06 +00:00
auto merge of #13401 : cmr/rust/docs, r=brson
Adds docs where previously there were no docs. Also adds windows support to libterm.
This commit is contained in:
commit
25c54226c3
@ -73,7 +73,7 @@ DEPS_arena := std collections
|
||||
DEPS_graphviz := std
|
||||
DEPS_glob := std
|
||||
DEPS_serialize := std collections log
|
||||
DEPS_term := std collections
|
||||
DEPS_term := std collections log
|
||||
DEPS_semver := std
|
||||
DEPS_uuid := std serialize rand
|
||||
DEPS_sync := std
|
||||
|
@ -14,6 +14,10 @@
|
||||
//! once, once the arena itself is destroyed. They do not support deallocation
|
||||
//! of individual objects while the arena itself is still alive. The benefit
|
||||
//! of an arena is very fast allocation; just a pointer bump.
|
||||
//!
|
||||
//! This crate has two arenas implemented: TypedArena, which is a simpler
|
||||
//! arena but can only hold objects of a single type, and Arena, which is a
|
||||
//! more complex, slower Arena which can hold objects of any type.
|
||||
|
||||
#![crate_id = "arena#0.11.0-pre"]
|
||||
#![crate_type = "rlib"]
|
||||
@ -56,41 +60,42 @@ impl Chunk {
|
||||
}
|
||||
}
|
||||
|
||||
// Arenas are used to quickly allocate objects that share a
|
||||
// lifetime. The arena uses ~[u8] vectors as a backing store to
|
||||
// allocate objects from. For each allocated object, the arena stores
|
||||
// a pointer to the type descriptor followed by the
|
||||
// object. (Potentially with alignment padding after each of them.)
|
||||
// When the arena is destroyed, it iterates through all of its chunks,
|
||||
// and uses the tydesc information to trace through the objects,
|
||||
// calling the destructors on them.
|
||||
// One subtle point that needs to be addressed is how to handle
|
||||
// failures while running the user provided initializer function. It
|
||||
// is important to not run the destructor on uninitialized objects, but
|
||||
// how to detect them is somewhat subtle. Since alloc() can be invoked
|
||||
// recursively, it is not sufficient to simply exclude the most recent
|
||||
// object. To solve this without requiring extra space, we use the low
|
||||
// order bit of the tydesc pointer to encode whether the object it
|
||||
// describes has been fully initialized.
|
||||
|
||||
// As an optimization, objects with destructors are stored in
|
||||
// different chunks than objects without destructors. This reduces
|
||||
// overhead when initializing plain-old-data and means we don't need
|
||||
// to waste time running the destructors of POD.
|
||||
/// A slower reflection-based arena that can allocate objects of any type.
|
||||
///
|
||||
/// This arena uses Vec<u8> as a backing store to allocate objects from. For
|
||||
/// each allocated object, the arena stores a pointer to the type descriptor
|
||||
/// followed by the object. (Potentially with alignment padding after each
|
||||
/// element.) When the arena is destroyed, it iterates through all of its
|
||||
/// chunks, and uses the tydesc information to trace through the objects,
|
||||
/// calling the destructors on them. One subtle point that needs to be
|
||||
/// addressed is how to handle failures while running the user provided
|
||||
/// initializer function. It is important to not run the destructor on
|
||||
/// uninitialized objects, but how to detect them is somewhat subtle. Since
|
||||
/// alloc() can be invoked recursively, it is not sufficient to simply exclude
|
||||
/// the most recent object. To solve this without requiring extra space, we
|
||||
/// use the low order bit of the tydesc pointer to encode whether the object
|
||||
/// it describes has been fully initialized.
|
||||
///
|
||||
/// As an optimization, objects with destructors are stored in
|
||||
/// different chunks than objects without destructors. This reduces
|
||||
/// overhead when initializing plain-old-data and means we don't need
|
||||
/// to waste time running the destructors of POD.
|
||||
pub struct Arena {
|
||||
// The head is separated out from the list as a unbenchmarked
|
||||
// microoptimization, to avoid needing to case on the list to
|
||||
// access the head.
|
||||
// microoptimization, to avoid needing to case on the list to access the
|
||||
// head.
|
||||
head: Chunk,
|
||||
copy_head: Chunk,
|
||||
chunks: RefCell<Vec<Chunk>>,
|
||||
}
|
||||
|
||||
impl Arena {
|
||||
/// Allocate a new Arena with 32 bytes preallocated.
|
||||
pub fn new() -> Arena {
|
||||
Arena::new_with_size(32u)
|
||||
}
|
||||
|
||||
/// Allocate a new Arena with `initial_size` bytes preallocated.
|
||||
pub fn new_with_size(initial_size: uint) -> Arena {
|
||||
Arena {
|
||||
head: chunk(initial_size, false),
|
||||
@ -265,7 +270,8 @@ impl Arena {
|
||||
}
|
||||
}
|
||||
|
||||
// The external interface
|
||||
/// Allocate a new item in the arena, using `op` to initialize the value
|
||||
/// and returning a reference to it.
|
||||
#[inline]
|
||||
pub fn alloc<'a, T>(&'a self, op: || -> T) -> &'a T {
|
||||
unsafe {
|
||||
@ -313,7 +319,7 @@ fn test_arena_destructors_fail() {
|
||||
});
|
||||
}
|
||||
|
||||
/// An arena that can hold objects of only one type.
|
||||
/// A faster arena that can hold objects of only one type.
|
||||
///
|
||||
/// Safety note: Modifying objects in the arena that have already had their
|
||||
/// `drop` destructors run can cause leaks, because the destructor will not
|
||||
@ -405,13 +411,13 @@ impl<T> TypedArenaChunk<T> {
|
||||
}
|
||||
|
||||
impl<T> TypedArena<T> {
|
||||
/// Creates a new arena with preallocated space for 8 objects.
|
||||
/// Creates a new TypedArena with preallocated space for 8 objects.
|
||||
#[inline]
|
||||
pub fn new() -> TypedArena<T> {
|
||||
TypedArena::with_capacity(8)
|
||||
}
|
||||
|
||||
/// Creates a new arena with preallocated space for the given number of
|
||||
/// Creates a new TypedArena with preallocated space for the given number of
|
||||
/// objects.
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: uint) -> TypedArena<T> {
|
||||
@ -423,7 +429,7 @@ impl<T> TypedArena<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates an object into this arena.
|
||||
/// Allocates an object in the TypedArena, returning a reference to it.
|
||||
#[inline]
|
||||
pub fn alloc<'a>(&'a self, object: T) -> &'a T {
|
||||
unsafe {
|
||||
|
@ -10,7 +10,11 @@
|
||||
|
||||
/*!
|
||||
|
||||
Simple compression
|
||||
Simple [DEFLATE][def]-based compression. This is a wrapper around the
|
||||
[`miniz`][mz] library, which is a one-file pure-C implementation of zlib.
|
||||
|
||||
[def]: https://en.wikipedia.org/wiki/DEFLATE
|
||||
[mz]: https://code.google.com/p/miniz/
|
||||
|
||||
*/
|
||||
|
||||
@ -31,23 +35,21 @@ extern crate libc;
|
||||
use std::c_vec::CVec;
|
||||
use libc::{c_void, size_t, c_int};
|
||||
|
||||
#[link(name = "miniz", kind = "static")]
|
||||
extern {
|
||||
/// Raw miniz compression function.
|
||||
fn tdefl_compress_mem_to_heap(psrc_buf: *c_void,
|
||||
src_buf_len: size_t,
|
||||
pout_len: *mut size_t,
|
||||
flags: c_int)
|
||||
-> *mut c_void;
|
||||
|
||||
pub mod rustrt {
|
||||
use libc::{c_void, size_t, c_int};
|
||||
#[link(name = "miniz", kind = "static")]
|
||||
extern {
|
||||
pub fn tdefl_compress_mem_to_heap(psrc_buf: *c_void,
|
||||
src_buf_len: size_t,
|
||||
pout_len: *mut size_t,
|
||||
flags: c_int)
|
||||
-> *mut c_void;
|
||||
|
||||
pub fn tinfl_decompress_mem_to_heap(psrc_buf: *c_void,
|
||||
src_buf_len: size_t,
|
||||
pout_len: *mut size_t,
|
||||
flags: c_int)
|
||||
-> *mut c_void;
|
||||
}
|
||||
/// Raw miniz decompression function.
|
||||
fn tinfl_decompress_mem_to_heap(psrc_buf: *c_void,
|
||||
src_buf_len: size_t,
|
||||
pout_len: *mut size_t,
|
||||
flags: c_int)
|
||||
-> *mut c_void;
|
||||
}
|
||||
|
||||
static LZ_NORM : c_int = 0x80; // LZ with 128 probes, "normal"
|
||||
@ -57,7 +59,7 @@ static TDEFL_WRITE_ZLIB_HEADER : c_int = 0x01000; // write zlib header and adler
|
||||
fn deflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<CVec<u8>> {
|
||||
unsafe {
|
||||
let mut outsz : size_t = 0;
|
||||
let res = rustrt::tdefl_compress_mem_to_heap(bytes.as_ptr() as *c_void,
|
||||
let res = tdefl_compress_mem_to_heap(bytes.as_ptr() as *c_void,
|
||||
bytes.len() as size_t,
|
||||
&mut outsz,
|
||||
flags);
|
||||
@ -69,10 +71,12 @@ fn deflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<CVec<u8>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Compress a buffer, without writing any sort of header on the output.
|
||||
pub fn deflate_bytes(bytes: &[u8]) -> Option<CVec<u8>> {
|
||||
deflate_bytes_internal(bytes, LZ_NORM)
|
||||
}
|
||||
|
||||
/// Compress a buffer, using a header that zlib can understand.
|
||||
pub fn deflate_bytes_zlib(bytes: &[u8]) -> Option<CVec<u8>> {
|
||||
deflate_bytes_internal(bytes, LZ_NORM | TDEFL_WRITE_ZLIB_HEADER)
|
||||
}
|
||||
@ -80,7 +84,7 @@ pub fn deflate_bytes_zlib(bytes: &[u8]) -> Option<CVec<u8>> {
|
||||
fn inflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<CVec<u8>> {
|
||||
unsafe {
|
||||
let mut outsz : size_t = 0;
|
||||
let res = rustrt::tinfl_decompress_mem_to_heap(bytes.as_ptr() as *c_void,
|
||||
let res = tinfl_decompress_mem_to_heap(bytes.as_ptr() as *c_void,
|
||||
bytes.len() as size_t,
|
||||
&mut outsz,
|
||||
flags);
|
||||
@ -92,10 +96,12 @@ fn inflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<CVec<u8>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Decompress a buffer, without parsing any sort of header on the input.
|
||||
pub fn inflate_bytes(bytes: &[u8]) -> Option<CVec<u8>> {
|
||||
inflate_bytes_internal(bytes, 0)
|
||||
}
|
||||
|
||||
/// Decompress a buffer that starts with a zlib header.
|
||||
pub fn inflate_bytes_zlib(bytes: &[u8]) -> Option<CVec<u8>> {
|
||||
inflate_bytes_internal(bytes, TINFL_FLAG_PARSE_ZLIB_HEADER)
|
||||
}
|
||||
|
@ -8,6 +8,40 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Simple numerics.
|
||||
//!
|
||||
//! This crate contains arbitrary-sized integer, rational, and complex types.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! This example uses the BigRational type and [Newton's method][newt] to
|
||||
//! approximate a square root to arbitrary precision:
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate num;
|
||||
//!
|
||||
//! use num::bigint::BigInt;
|
||||
//! use num::rational::{Ratio, BigRational};
|
||||
//!
|
||||
//! fn approx_sqrt(number: u64, iterations: uint) -> BigRational {
|
||||
//! let start: Ratio<BigInt> = Ratio::from_integer(FromPrimitive::from_u64(number).unwrap());
|
||||
//! let mut approx = start.clone();
|
||||
//!
|
||||
//! for _ in range(0, iterations) {
|
||||
//! approx = (approx + (start / approx)) /
|
||||
//! Ratio::from_integer(FromPrimitive::from_u64(2).unwrap());
|
||||
//! }
|
||||
//!
|
||||
//! approx
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! println!("{}", approx_sqrt(10, 4)); // prints 4057691201/1283082416
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [newt]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
|
||||
|
||||
#![feature(macro_rules)]
|
||||
|
||||
#![crate_id = "num#0.11.0-pre"]
|
||||
|
@ -259,7 +259,7 @@ pub struct EmitterWriter {
|
||||
}
|
||||
|
||||
enum Destination {
|
||||
Terminal(term::Terminal<io::stdio::StdWriter>),
|
||||
Terminal(Box<term::Terminal<Box<Writer:Send>>:Send>),
|
||||
Raw(Box<Writer:Send>),
|
||||
}
|
||||
|
||||
@ -274,9 +274,9 @@ impl EmitterWriter {
|
||||
};
|
||||
|
||||
if use_color {
|
||||
let dst = match term::Terminal::new(stderr.unwrap()) {
|
||||
Ok(t) => Terminal(t),
|
||||
Err(..) => Raw(box io::stderr()),
|
||||
let dst = match term::stderr() {
|
||||
Some(t) => Terminal(t),
|
||||
None => Raw(box stderr),
|
||||
};
|
||||
EmitterWriter { dst: dst }
|
||||
} else {
|
||||
|
@ -8,7 +8,32 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Simple ANSI color library
|
||||
//! Terminal formatting library.
|
||||
//!
|
||||
//! This crate provides the `Terminal` trait, which abstracts over an [ANSI
|
||||
//! Termina][ansi] to provide color printing, among other things. There are two implementations,
|
||||
//! the `TerminfoTerminal`, which uses control characters from a
|
||||
//! [terminfo][ti] database, and `WinConsole`, which uses the [Win32 Console
|
||||
//! API][win].
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate term;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let mut t = term::stdout().unwrap();
|
||||
//! t.fg(term::color::GREEN).unwrap();
|
||||
//! println!("hello, ");
|
||||
//! t.fg(term::color::RED).unwrap();
|
||||
//! println!("world!");
|
||||
//! t.reset().unwrap();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
//! [win]: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682010%28v=vs.85%29.aspx
|
||||
//! [ti]: https://en.wikipedia.org/wiki/Terminfo
|
||||
|
||||
#![crate_id = "term#0.11.0-pre"]
|
||||
#![comment = "Simple ANSI color library"]
|
||||
@ -19,22 +44,76 @@
|
||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||
html_root_url = "http://static.rust-lang.org/doc/master")]
|
||||
|
||||
#![feature(macro_rules)]
|
||||
#![feature(macro_rules, phase)]
|
||||
|
||||
#![deny(missing_doc)]
|
||||
|
||||
#[phase(syntax, link)] extern crate log;
|
||||
extern crate collections;
|
||||
|
||||
use std::io;
|
||||
use std::os;
|
||||
use terminfo::TermInfo;
|
||||
use terminfo::searcher::open;
|
||||
use terminfo::parser::compiled::{parse, msys_terminfo};
|
||||
use terminfo::parm::{expand, Number, Variables};
|
||||
pub use terminfo::TerminfoTerminal;
|
||||
#[cfg(windows)]
|
||||
pub use win::WinConsole;
|
||||
|
||||
use std::io::IoResult;
|
||||
|
||||
pub mod terminfo;
|
||||
|
||||
// FIXME (#2807): Windows support.
|
||||
#[cfg(windows)]
|
||||
mod win;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
|
||||
/// opened.
|
||||
pub fn stdout() -> Option<Box<Terminal<Box<Writer:Send>>:Send>> {
|
||||
let ti: Option<TerminfoTerminal<Box<Writer:Send>>>
|
||||
= Terminal::new(box std::io::stdout() as Box<Writer:Send>);
|
||||
ti.map(|t| box t as Box<Terminal<Box<Writer:Send>:Send>:Send>)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
|
||||
/// opened.
|
||||
pub fn stdout() -> Option<Box<Terminal<Box<Writer:Send>:Send>:Send>> {
|
||||
let ti: Option<TerminfoTerminal<Box<Writer:Send>>>
|
||||
= Terminal::new(box std::io::stdout() as Box<Writer:Send>);
|
||||
|
||||
match ti {
|
||||
Some(t) => Some(box t as Box<Terminal<Box<Writer:Send>:Send>:Send>),
|
||||
None => {
|
||||
let wc: Option<WinConsole<Box<Writer:Send>>>
|
||||
= Terminal::new(box std::io::stdout() as Box<Writer:Send>);
|
||||
wc.map(|w| box w as Box<Terminal<Box<Writer:Send>:Send>:Send>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
|
||||
/// opened.
|
||||
pub fn stderr() -> Option<Box<Terminal<Box<Writer:Send>:Send>:Send>:Send> {
|
||||
let ti: Option<TerminfoTerminal<Box<Writer:Send>>>
|
||||
= Terminal::new(box std::io::stderr() as Box<Writer:Send>);
|
||||
ti.map(|t| box t as Box<Terminal<Box<Writer:Send>:Send>:Send>)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
|
||||
/// opened.
|
||||
pub fn stderr() -> Option<Box<Terminal<Box<Writer:Send>:Send>:Send>> {
|
||||
let ti: Option<TerminfoTerminal<Box<Writer:Send>>>
|
||||
= Terminal::new(box std::io::stderr() as Box<Writer:Send>);
|
||||
|
||||
match ti {
|
||||
Some(t) => Some(box t as Box<Terminal<Box<Writer:Send>:Send>:Send>),
|
||||
None => {
|
||||
let wc: Option<WinConsole<Box<Writer:Send>>>
|
||||
= Terminal::new(box std::io::stderr() as Box<Writer:Send>);
|
||||
wc.map(|w| box w as Box<Terminal<Box<Writer:Send>:Send>:Send>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Terminal color definitions
|
||||
pub mod color {
|
||||
@ -91,72 +170,13 @@ pub mod attr {
|
||||
}
|
||||
}
|
||||
|
||||
fn cap_for_attr(attr: attr::Attr) -> &'static str {
|
||||
match attr {
|
||||
attr::Bold => "bold",
|
||||
attr::Dim => "dim",
|
||||
attr::Italic(true) => "sitm",
|
||||
attr::Italic(false) => "ritm",
|
||||
attr::Underline(true) => "smul",
|
||||
attr::Underline(false) => "rmul",
|
||||
attr::Blink => "blink",
|
||||
attr::Standout(true) => "smso",
|
||||
attr::Standout(false) => "rmso",
|
||||
attr::Reverse => "rev",
|
||||
attr::Secure => "invis",
|
||||
attr::ForegroundColor(_) => "setaf",
|
||||
attr::BackgroundColor(_) => "setab"
|
||||
}
|
||||
}
|
||||
/// A terminal with similar capabilities to an ANSI Terminal
|
||||
/// (foreground/background colors etc).
|
||||
pub trait Terminal<T: Writer>: Writer {
|
||||
/// Returns `None` whenever the terminal cannot be created for some
|
||||
/// reason.
|
||||
fn new(out: T) -> Option<Self>;
|
||||
|
||||
/// A Terminal that knows how many colors it supports, with a reference to its
|
||||
/// parsed TermInfo database record.
|
||||
pub struct Terminal<T> {
|
||||
num_colors: u16,
|
||||
out: T,
|
||||
ti: Box<TermInfo>,
|
||||
}
|
||||
|
||||
impl<T: Writer> Terminal<T> {
|
||||
/// Returns a wrapped output stream (`Terminal<T>`) as a `Result`.
|
||||
///
|
||||
/// Returns `Err()` if the TERM environment variable is undefined.
|
||||
/// TERM should be set to something like `xterm-color` or `screen-256color`.
|
||||
///
|
||||
/// Returns `Err()` on failure to open the terminfo database correctly.
|
||||
/// Also, in the event that the individual terminfo database entry can not
|
||||
/// be parsed.
|
||||
pub fn new(out: T) -> Result<Terminal<T>, StrBuf> {
|
||||
let term = match os::getenv("TERM") {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
return Err("TERM environment variable undefined".to_strbuf())
|
||||
}
|
||||
};
|
||||
|
||||
let mut file = match open(term) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
if "cygwin" == term { // msys terminal
|
||||
return Ok(Terminal {
|
||||
out: out,
|
||||
ti: msys_terminfo(),
|
||||
num_colors: 8
|
||||
});
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
let inf = try!(parse(&mut file, false));
|
||||
|
||||
let nc = if inf.strings.find_equiv(&("setaf")).is_some()
|
||||
&& inf.strings.find_equiv(&("setab")).is_some() {
|
||||
inf.numbers.find_equiv(&("colors")).map_or(0, |&n| n)
|
||||
} else { 0 };
|
||||
|
||||
return Ok(Terminal {out: out, ti: inf, num_colors: nc});
|
||||
}
|
||||
/// Sets the foreground color to the given color.
|
||||
///
|
||||
/// If the color is a bright color, but the terminal only supports 8 colors,
|
||||
@ -164,22 +184,8 @@ impl<T: Writer> Terminal<T> {
|
||||
///
|
||||
/// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
|
||||
/// if there was an I/O error.
|
||||
pub fn fg(&mut self, color: color::Color) -> io::IoResult<bool> {
|
||||
let color = self.dim_if_necessary(color);
|
||||
if self.num_colors > color {
|
||||
let s = expand(self.ti
|
||||
.strings
|
||||
.find_equiv(&("setaf"))
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
[Number(color as int)], &mut Variables::new());
|
||||
if s.is_ok() {
|
||||
try!(self.out.write(s.unwrap().as_slice()));
|
||||
return Ok(true)
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
fn fg(&mut self, color: color::Color) -> IoResult<bool>;
|
||||
|
||||
/// Sets the background color to the given color.
|
||||
///
|
||||
/// If the color is a bright color, but the terminal only supports 8 colors,
|
||||
@ -187,104 +193,26 @@ impl<T: Writer> Terminal<T> {
|
||||
///
|
||||
/// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
|
||||
/// if there was an I/O error.
|
||||
pub fn bg(&mut self, color: color::Color) -> io::IoResult<bool> {
|
||||
let color = self.dim_if_necessary(color);
|
||||
if self.num_colors > color {
|
||||
let s = expand(self.ti
|
||||
.strings
|
||||
.find_equiv(&("setab"))
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
[Number(color as int)], &mut Variables::new());
|
||||
if s.is_ok() {
|
||||
try!(self.out.write(s.unwrap().as_slice()));
|
||||
return Ok(true)
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
fn bg(&mut self, color: color::Color) -> IoResult<bool>;
|
||||
|
||||
/// Sets the given terminal attribute, if supported.
|
||||
/// Returns `Ok(true)` if the attribute was supported, `Ok(false)` otherwise,
|
||||
/// and `Err(e)` if there was an I/O error.
|
||||
pub fn attr(&mut self, attr: attr::Attr) -> io::IoResult<bool> {
|
||||
match attr {
|
||||
attr::ForegroundColor(c) => self.fg(c),
|
||||
attr::BackgroundColor(c) => self.bg(c),
|
||||
_ => {
|
||||
let cap = cap_for_attr(attr);
|
||||
let parm = self.ti.strings.find_equiv(&cap);
|
||||
if parm.is_some() {
|
||||
let s = expand(parm.unwrap().as_slice(),
|
||||
[],
|
||||
&mut Variables::new());
|
||||
if s.is_ok() {
|
||||
try!(self.out.write(s.unwrap().as_slice()));
|
||||
return Ok(true)
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Sets the given terminal attribute, if supported. Returns `Ok(true)`
|
||||
/// if the attribute was supported, `Ok(false)` otherwise, and `Err(e)` if
|
||||
/// there was an I/O error.
|
||||
fn attr(&mut self, attr: attr::Attr) -> IoResult<bool>;
|
||||
|
||||
/// Returns whether the given terminal attribute is supported.
|
||||
pub fn supports_attr(&self, attr: attr::Attr) -> bool {
|
||||
match attr {
|
||||
attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
|
||||
self.num_colors > 0
|
||||
}
|
||||
_ => {
|
||||
let cap = cap_for_attr(attr);
|
||||
self.ti.strings.find_equiv(&cap).is_some()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn supports_attr(&self, attr: attr::Attr) -> bool;
|
||||
|
||||
/// Resets all terminal attributes and color to the default.
|
||||
/// Returns `Ok()`.
|
||||
pub fn reset(&mut self) -> io::IoResult<()> {
|
||||
let mut cap = self.ti.strings.find_equiv(&("sgr0"));
|
||||
if cap.is_none() {
|
||||
// are there any terminals that have color/attrs and not sgr0?
|
||||
// Try falling back to sgr, then op
|
||||
cap = self.ti.strings.find_equiv(&("sgr"));
|
||||
if cap.is_none() {
|
||||
cap = self.ti.strings.find_equiv(&("op"));
|
||||
}
|
||||
}
|
||||
let s = cap.map_or(Err("can't find terminfo capability \
|
||||
`sgr0`".to_strbuf()), |op| {
|
||||
expand(op.as_slice(), [], &mut Variables::new())
|
||||
});
|
||||
if s.is_ok() {
|
||||
return self.out.write(s.unwrap().as_slice())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn reset(&mut self) -> IoResult<()>;
|
||||
|
||||
fn dim_if_necessary(&self, color: color::Color) -> color::Color {
|
||||
if color >= self.num_colors && color >= 8 && color < 16 {
|
||||
color-8
|
||||
} else { color }
|
||||
}
|
||||
|
||||
/// Returns the contained stream
|
||||
pub fn unwrap(self) -> T { self.out }
|
||||
/// Returns the contained stream, destroying the `Terminal`
|
||||
fn unwrap(self) -> T;
|
||||
|
||||
/// Gets an immutable reference to the stream inside
|
||||
pub fn get_ref<'a>(&'a self) -> &'a T { &self.out }
|
||||
fn get_ref<'a>(&'a self) -> &'a T;
|
||||
|
||||
/// Gets a mutable reference to the stream inside
|
||||
pub fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
|
||||
}
|
||||
|
||||
impl<T: Writer> Writer for Terminal<T> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::IoResult<()> {
|
||||
self.out.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::IoResult<()> {
|
||||
self.out.flush()
|
||||
}
|
||||
fn get_mut<'a>(&'a mut self) -> &'a mut T;
|
||||
}
|
||||
|
@ -11,8 +11,19 @@
|
||||
//! Terminfo database interface.
|
||||
|
||||
use collections::HashMap;
|
||||
use std::io::IoResult;
|
||||
use std::os;
|
||||
|
||||
use attr;
|
||||
use color;
|
||||
use Terminal;
|
||||
use self::searcher::open;
|
||||
use self::parser::compiled::{parse, msys_terminfo};
|
||||
use self::parm::{expand, Number, Variables};
|
||||
|
||||
|
||||
/// A parsed terminfo database entry.
|
||||
#[deriving(Show)]
|
||||
pub struct TermInfo {
|
||||
/// Names for the terminal
|
||||
pub names: Vec<StrBuf> ,
|
||||
@ -32,3 +43,179 @@ pub mod parser {
|
||||
pub mod compiled;
|
||||
}
|
||||
pub mod parm;
|
||||
|
||||
|
||||
fn cap_for_attr(attr: attr::Attr) -> &'static str {
|
||||
match attr {
|
||||
attr::Bold => "bold",
|
||||
attr::Dim => "dim",
|
||||
attr::Italic(true) => "sitm",
|
||||
attr::Italic(false) => "ritm",
|
||||
attr::Underline(true) => "smul",
|
||||
attr::Underline(false) => "rmul",
|
||||
attr::Blink => "blink",
|
||||
attr::Standout(true) => "smso",
|
||||
attr::Standout(false) => "rmso",
|
||||
attr::Reverse => "rev",
|
||||
attr::Secure => "invis",
|
||||
attr::ForegroundColor(_) => "setaf",
|
||||
attr::BackgroundColor(_) => "setab"
|
||||
}
|
||||
}
|
||||
|
||||
/// A Terminal that knows how many colors it supports, with a reference to its
|
||||
/// parsed Terminfo database record.
|
||||
pub struct TerminfoTerminal<T> {
|
||||
num_colors: u16,
|
||||
out: T,
|
||||
ti: Box<TermInfo>
|
||||
}
|
||||
|
||||
impl<T: Writer> Terminal<T> for TerminfoTerminal<T> {
|
||||
fn new(out: T) -> Option<TerminfoTerminal<T>> {
|
||||
let term = match os::getenv("TERM") {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
debug!("TERM environment variable not defined");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let entry = open(term);
|
||||
if entry.is_err() {
|
||||
if os::getenv("MSYSCON").map_or(false, |s| "mintty.exe" == s) {
|
||||
// msys terminal
|
||||
return Some(TerminfoTerminal {out: out, ti: msys_terminfo(), num_colors: 8});
|
||||
}
|
||||
debug!("error finding terminfo entry: {}", entry.err().unwrap());
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut file = entry.unwrap();
|
||||
let ti = parse(&mut file, false);
|
||||
if ti.is_err() {
|
||||
debug!("error parsing terminfo entry: {}", ti.unwrap_err());
|
||||
return None;
|
||||
}
|
||||
|
||||
let inf = ti.unwrap();
|
||||
let nc = if inf.strings.find_equiv(&("setaf")).is_some()
|
||||
&& inf.strings.find_equiv(&("setab")).is_some() {
|
||||
inf.numbers.find_equiv(&("colors")).map_or(0, |&n| n)
|
||||
} else { 0 };
|
||||
|
||||
return Some(TerminfoTerminal {out: out, ti: inf, num_colors: nc});
|
||||
}
|
||||
|
||||
fn fg(&mut self, color: color::Color) -> IoResult<bool> {
|
||||
let color = self.dim_if_necessary(color);
|
||||
if self.num_colors > color {
|
||||
let s = expand(self.ti
|
||||
.strings
|
||||
.find_equiv(&("setaf"))
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
[Number(color as int)], &mut Variables::new());
|
||||
if s.is_ok() {
|
||||
try!(self.out.write(s.unwrap().as_slice()));
|
||||
return Ok(true)
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn bg(&mut self, color: color::Color) -> IoResult<bool> {
|
||||
let color = self.dim_if_necessary(color);
|
||||
if self.num_colors > color {
|
||||
let s = expand(self.ti
|
||||
.strings
|
||||
.find_equiv(&("setab"))
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
[Number(color as int)], &mut Variables::new());
|
||||
if s.is_ok() {
|
||||
try!(self.out.write(s.unwrap().as_slice()));
|
||||
return Ok(true)
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn attr(&mut self, attr: attr::Attr) -> IoResult<bool> {
|
||||
match attr {
|
||||
attr::ForegroundColor(c) => self.fg(c),
|
||||
attr::BackgroundColor(c) => self.bg(c),
|
||||
_ => {
|
||||
let cap = cap_for_attr(attr);
|
||||
let parm = self.ti.strings.find_equiv(&cap);
|
||||
if parm.is_some() {
|
||||
let s = expand(parm.unwrap().as_slice(),
|
||||
[],
|
||||
&mut Variables::new());
|
||||
if s.is_ok() {
|
||||
try!(self.out.write(s.unwrap().as_slice()));
|
||||
return Ok(true)
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn supports_attr(&self, attr: attr::Attr) -> bool {
|
||||
match attr {
|
||||
attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
|
||||
self.num_colors > 0
|
||||
}
|
||||
_ => {
|
||||
let cap = cap_for_attr(attr);
|
||||
self.ti.strings.find_equiv(&cap).is_some()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> IoResult<()> {
|
||||
let mut cap = self.ti.strings.find_equiv(&("sgr0"));
|
||||
if cap.is_none() {
|
||||
// are there any terminals that have color/attrs and not sgr0?
|
||||
// Try falling back to sgr, then op
|
||||
cap = self.ti.strings.find_equiv(&("sgr"));
|
||||
if cap.is_none() {
|
||||
cap = self.ti.strings.find_equiv(&("op"));
|
||||
}
|
||||
}
|
||||
let s = cap.map_or(Err("can't find terminfo capability `sgr0`".to_strbuf()), |op| {
|
||||
expand(op.as_slice(), [], &mut Variables::new())
|
||||
});
|
||||
if s.is_ok() {
|
||||
return self.out.write(s.unwrap().as_slice())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unwrap(self) -> T { self.out }
|
||||
|
||||
fn get_ref<'a>(&'a self) -> &'a T { &self.out }
|
||||
|
||||
fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
|
||||
}
|
||||
|
||||
impl<T: Writer> TerminfoTerminal<T> {
|
||||
fn dim_if_necessary(&self, color: color::Color) -> color::Color {
|
||||
if color >= self.num_colors && color >= 8 && color < 16 {
|
||||
color-8
|
||||
} else { color }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T: Writer> Writer for TerminfoTerminal<T> {
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.out.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> IoResult<()> {
|
||||
self.out.flush()
|
||||
}
|
||||
}
|
||||
|
||||
|
148
src/libterm/win.rs
Normal file
148
src/libterm/win.rs
Normal file
@ -0,0 +1,148 @@
|
||||
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Windows console handling
|
||||
|
||||
// FIXME (#13400): this is only a tiny fraction of the win32 console api
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std::io::IoResult;
|
||||
|
||||
use attr;
|
||||
use color;
|
||||
use Terminal;
|
||||
|
||||
/// A Terminal implementation which uses the Win32 Console API.
|
||||
pub struct WinConsole<T> {
|
||||
buf: T,
|
||||
foreground: color::Color,
|
||||
background: color::Color,
|
||||
}
|
||||
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
fn SetConsoleTextAttribute(handle: libc::HANDLE, attr: libc::WORD) -> libc::BOOL;
|
||||
fn GetStdHandle(which: libc::DWORD) -> libc::HANDLE;
|
||||
}
|
||||
|
||||
fn color_to_bits(color: color::Color) -> u16 {
|
||||
// magic numbers from mingw-w64's wincon.h
|
||||
|
||||
let bits = match color % 8 {
|
||||
color::BLACK => 0,
|
||||
color::BLUE => 0x1,
|
||||
color::GREEN => 0x2,
|
||||
color::RED => 0x4,
|
||||
color::YELLOW => 0x2 | 0x4,
|
||||
color::MAGENTA => 0x1 | 0x4,
|
||||
color::CYAN => 0x1 | 0x2,
|
||||
color::WHITE => 0x1 | 0x2 | 0x4,
|
||||
_ => unreachable!()
|
||||
};
|
||||
|
||||
if color >= 8 {
|
||||
bits | 0x8
|
||||
} else {
|
||||
bits
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Writer> WinConsole<T> {
|
||||
fn apply(&mut self) {
|
||||
let _unused = self.buf.flush();
|
||||
let mut accum: libc::WORD = 0;
|
||||
accum |= color_to_bits(self.foreground);
|
||||
accum |= color_to_bits(self.background) << 4;
|
||||
|
||||
unsafe {
|
||||
// Magic -11 means stdout, from
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx
|
||||
//
|
||||
// You may be wondering, "but what about stderr?", and the answer
|
||||
// to that is that setting terminal attributes on the stdout
|
||||
// handle also sets them for stderr, since they go to the same
|
||||
// terminal! Admittedly, this is fragile, since stderr could be
|
||||
// redirected to a different console. This is good enough for
|
||||
// rustc though. See #13400.
|
||||
let out = GetStdHandle(-11);
|
||||
SetConsoleTextAttribute(out, accum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Writer> Writer for WinConsole<T> {
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.buf.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> IoResult<()> {
|
||||
self.buf.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Writer> Terminal<T> for WinConsole<T> {
|
||||
fn new(out: T) -> Option<WinConsole<T>> {
|
||||
Some(WinConsole { buf: out, foreground: color::WHITE, background: color::BLACK })
|
||||
}
|
||||
|
||||
fn fg(&mut self, color: color::Color) -> IoResult<bool> {
|
||||
self.foreground = color;
|
||||
self.apply();
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn bg(&mut self, color: color::Color) -> IoResult<bool> {
|
||||
self.background = color;
|
||||
self.apply();
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn attr(&mut self, attr: attr::Attr) -> IoResult<bool> {
|
||||
match attr {
|
||||
attr::ForegroundColor(f) => {
|
||||
self.foreground = f;
|
||||
self.apply();
|
||||
Ok(true)
|
||||
},
|
||||
attr::BackgroundColor(b) => {
|
||||
self.background = b;
|
||||
self.apply();
|
||||
Ok(true)
|
||||
},
|
||||
_ => Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn supports_attr(&self, attr: attr::Attr) -> bool {
|
||||
// it claims support for underscore and reverse video, but I can't get
|
||||
// it to do anything -cmr
|
||||
match attr {
|
||||
attr::ForegroundColor(_) | attr::BackgroundColor(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> IoResult<()> {
|
||||
self.foreground = color::WHITE;
|
||||
self.background = color::BLACK;
|
||||
self.apply();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unwrap(self) -> T { self.buf }
|
||||
|
||||
fn get_ref<'a>(&'a self) -> &'a T { &self.buf }
|
||||
|
||||
fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.buf }
|
||||
}
|
@ -447,7 +447,7 @@ pub enum TestResult {
|
||||
}
|
||||
|
||||
enum OutputLocation<T> {
|
||||
Pretty(term::Terminal<T>),
|
||||
Pretty(Box<term::Terminal<Box<Writer:Send>>:Send>),
|
||||
Raw(T),
|
||||
}
|
||||
|
||||
@ -472,10 +472,11 @@ impl<T: Writer> ConsoleTestState<T> {
|
||||
Some(ref path) => Some(try!(File::create(path))),
|
||||
None => None
|
||||
};
|
||||
let out = match term::Terminal::new(io::stdio::stdout_raw()) {
|
||||
Err(_) => Raw(io::stdio::stdout_raw()),
|
||||
Ok(t) => Pretty(t)
|
||||
let out = match term::stdout() {
|
||||
None => Raw(io::stdio::stdout_raw()),
|
||||
Some(t) => Pretty(t)
|
||||
};
|
||||
|
||||
Ok(ConsoleTestState {
|
||||
out: out,
|
||||
log_out: log_out,
|
||||
|
@ -8,7 +8,10 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Simple time handling.
|
||||
|
||||
#![crate_id = "time#0.11.0-pre"]
|
||||
|
||||
#![crate_type = "rlib"]
|
||||
#![crate_type = "dylib"]
|
||||
#![license = "MIT/ASL2"]
|
||||
|
@ -8,6 +8,72 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A simple function caching system.
|
||||
//!
|
||||
//! This is a loose clone of the [fbuild build system](https://github.com/felix-lang/fbuild),
|
||||
//! made a touch more generic (not wired to special cases on files) and much
|
||||
//! less metaprogram-y due to rust's comparative weakness there, relative to
|
||||
//! python.
|
||||
//!
|
||||
//! It's based around _imperative builds_ that happen to have some function
|
||||
//! calls cached. That is, it's _just_ a mechanism for describing cached
|
||||
//! functions. This makes it much simpler and smaller than a "build system"
|
||||
//! that produces an IR and evaluates it. The evaluation order is normal
|
||||
//! function calls. Some of them just return really quickly.
|
||||
//!
|
||||
//! A cached function consumes and produces a set of _works_. A work has a
|
||||
//! name, a kind (that determines how the value is to be checked for
|
||||
//! freshness) and a value. Works must also be (de)serializable. Some
|
||||
//! examples of works:
|
||||
//!
|
||||
//! kind name value
|
||||
//! ------------------------
|
||||
//! cfg os linux
|
||||
//! file foo.c <sha1>
|
||||
//! url foo.com <etag>
|
||||
//!
|
||||
//! Works are conceptually single units, but we store them most of the time
|
||||
//! in maps of the form (type,name) => value. These are WorkMaps.
|
||||
//!
|
||||
//! A cached function divides the works it's interested in into inputs and
|
||||
//! outputs, and subdivides those into declared (input) works and
|
||||
//! discovered (input and output) works.
|
||||
//!
|
||||
//! A _declared_ input or is one that is given to the workcache before
|
||||
//! any work actually happens, in the "prep" phase. Even when a function's
|
||||
//! work-doing part (the "exec" phase) never gets called, it has declared
|
||||
//! inputs, which can be checked for freshness (and potentially
|
||||
//! used to determine that the function can be skipped).
|
||||
//!
|
||||
//! The workcache checks _all_ works for freshness, but uses the set of
|
||||
//! discovered outputs from the _previous_ exec (which it will re-discover
|
||||
//! and re-record each time the exec phase runs).
|
||||
//!
|
||||
//! Therefore the discovered works cached in the db might be a
|
||||
//! mis-approximation of the current discoverable works, but this is ok for
|
||||
//! the following reason: we assume that if an artifact A changed from
|
||||
//! depending on B,C,D to depending on B,C,D,E, then A itself changed (as
|
||||
//! part of the change-in-dependencies), so we will be ok.
|
||||
//!
|
||||
//! Each function has a single discriminated output work called its _result_.
|
||||
//! This is only different from other works in that it is returned, by value,
|
||||
//! from a call to the cacheable function; the other output works are used in
|
||||
//! passing to invalidate dependencies elsewhere in the cache, but do not
|
||||
//! otherwise escape from a function invocation. Most functions only have one
|
||||
//! output work anyways.
|
||||
//!
|
||||
//! A database (the central store of a workcache) stores a mappings:
|
||||
//!
|
||||
//! (fn_name,{declared_input}) => ({discovered_input},
|
||||
//! {discovered_output},result)
|
||||
//!
|
||||
//! (Note: fbuild, which workcache is based on, has the concept of a declared
|
||||
//! output as separate from a discovered output. This distinction exists only
|
||||
//! as an artifact of how fbuild works: via annotations on function types
|
||||
//! and metaprogramming, with explicit dependency declaration as a fallback.
|
||||
//! Workcache is more explicit about dependencies, and as such treats all
|
||||
//! outputs the same, as discovered-during-the-last-run.)
|
||||
|
||||
#![crate_id = "workcache#0.11.0-pre"]
|
||||
#![crate_type = "rlib"]
|
||||
#![crate_type = "dylib"]
|
||||
@ -33,74 +99,6 @@ use std::str;
|
||||
use std::io;
|
||||
use std::io::{File, MemWriter};
|
||||
|
||||
/**
|
||||
*
|
||||
* This is a loose clone of the [fbuild build system](https://github.com/felix-lang/fbuild),
|
||||
* made a touch more generic (not wired to special cases on files) and much
|
||||
* less metaprogram-y due to rust's comparative weakness there, relative to
|
||||
* python.
|
||||
*
|
||||
* It's based around _imperative builds_ that happen to have some function
|
||||
* calls cached. That is, it's _just_ a mechanism for describing cached
|
||||
* functions. This makes it much simpler and smaller than a "build system"
|
||||
* that produces an IR and evaluates it. The evaluation order is normal
|
||||
* function calls. Some of them just return really quickly.
|
||||
*
|
||||
* A cached function consumes and produces a set of _works_. A work has a
|
||||
* name, a kind (that determines how the value is to be checked for
|
||||
* freshness) and a value. Works must also be (de)serializable. Some
|
||||
* examples of works:
|
||||
*
|
||||
* kind name value
|
||||
* ------------------------
|
||||
* cfg os linux
|
||||
* file foo.c <sha1>
|
||||
* url foo.com <etag>
|
||||
*
|
||||
* Works are conceptually single units, but we store them most of the time
|
||||
* in maps of the form (type,name) => value. These are WorkMaps.
|
||||
*
|
||||
* A cached function divides the works it's interested in into inputs and
|
||||
* outputs, and subdivides those into declared (input) works and
|
||||
* discovered (input and output) works.
|
||||
*
|
||||
* A _declared_ input or is one that is given to the workcache before
|
||||
* any work actually happens, in the "prep" phase. Even when a function's
|
||||
* work-doing part (the "exec" phase) never gets called, it has declared
|
||||
* inputs, which can be checked for freshness (and potentially
|
||||
* used to determine that the function can be skipped).
|
||||
*
|
||||
* The workcache checks _all_ works for freshness, but uses the set of
|
||||
* discovered outputs from the _previous_ exec (which it will re-discover
|
||||
* and re-record each time the exec phase runs).
|
||||
*
|
||||
* Therefore the discovered works cached in the db might be a
|
||||
* mis-approximation of the current discoverable works, but this is ok for
|
||||
* the following reason: we assume that if an artifact A changed from
|
||||
* depending on B,C,D to depending on B,C,D,E, then A itself changed (as
|
||||
* part of the change-in-dependencies), so we will be ok.
|
||||
*
|
||||
* Each function has a single discriminated output work called its _result_.
|
||||
* This is only different from other works in that it is returned, by value,
|
||||
* from a call to the cacheable function; the other output works are used in
|
||||
* passing to invalidate dependencies elsewhere in the cache, but do not
|
||||
* otherwise escape from a function invocation. Most functions only have one
|
||||
* output work anyways.
|
||||
*
|
||||
* A database (the central store of a workcache) stores a mappings:
|
||||
*
|
||||
* (fn_name,{declared_input}) => ({discovered_input},
|
||||
* {discovered_output},result)
|
||||
*
|
||||
* (Note: fbuild, which workcache is based on, has the concept of a declared
|
||||
* output as separate from a discovered output. This distinction exists only
|
||||
* as an artifact of how fbuild works: via annotations on function types
|
||||
* and metaprogramming, with explicit dependency declaration as a fallback.
|
||||
* Workcache is more explicit about dependencies, and as such treats all
|
||||
* outputs the same, as discovered-during-the-last-run.)
|
||||
*
|
||||
*/
|
||||
|
||||
#[deriving(Clone, Eq, Encodable, Decodable, Ord, TotalOrd, TotalEq)]
|
||||
struct WorkKey {
|
||||
kind: StrBuf,
|
||||
|
Loading…
Reference in New Issue
Block a user