<commit_before>//! Shim which is passed to Cargo as "rustc" when running the bootstrap.
//!
//! This shim will take care of some various tasks that our build process
//! requires that Cargo can't quite do through normal configuration:
//!
//! 1. When compiling build scripts and build dependencies, we need a guaranteed
//!    full standard library available. The only compiler which actually has
//!    this is the snapshot, so we detect this situation and always compile with
//!    the snapshot compiler.
//! 2. We pass a bunch of `--cfg` and other flags based on what we're compiling
//!    (and this slightly differs based on a whether we're using a snapshot or
//!    not), so we do that all here.
//!
//! This may one day be replaced by RUSTFLAGS, but the dynamic nature of
//! switching compilers for the bootstrap and for build scripts will probably
//! never get replaced.

use std::env;
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
use std::time::Instant;

fn main() {
    let args = env::args_os().skip(1).collect::<Vec<_>>();

    // Detect whether or not we're a build script depending on whether --target
    // is passed (a bit janky...)
    let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str());
    let version = args.iter().find(|w| &**w == "-vV");

    let verbose = match env::var("RUSTC_VERBOSE") {
        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
        Err(_) => 0,
    };

    // Use a different compiler for build scripts, since there may not yet be a
    // libstd for the real compiler to use. However, if Cargo is attempting to
    // determine the version of the compiler, the real compiler needs to be
    // used. Currently, these two states are differentiated based on whether
    // --target and -vV is/isn't passed.
    let (rustc, libdir) = if target.is_none() && version.is_none() {
        ("RUSTC_SNAPSHOT", "RUSTC_SNAPSHOT_LIBDIR")
    } else {
        ("RUSTC_REAL", "RUSTC_LIBDIR")
    };
    let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set");
    let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
    let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new);

    let rustc = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc));
    let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{:?} was not set", libdir));
    let mut dylib_path = bootstrap::util::dylib_path();
    dylib_path.insert(0, PathBuf::from(&libdir));

    let mut cmd = Command::new(rustc);
    cmd.args(&args).env(bootstrap::util::dylib_path_var(), env::join_paths(&dylib_path).unwrap());

    // Get the name of the crate we're compiling, if any.
    let crate_name =
        args.windows(2).find(|args| args[0] == "--crate-name").and_then(|args| args[1].to_str());

    if let Some(crate_name) = crate_name {
        if let Some(target) = env::var_os("RUSTC_TIME") {
            if target == "all"
                || target.into_string().unwrap().split(',').any(|c| c.trim() == crate_name)
            {
                cmd.arg("-Ztime");
            }
        }
    }

    // Print backtrace in case of ICE
    if env::var("RUSTC_BACKTRACE_ON_ICE").is_ok() && env::var("RUST_BACKTRACE").is_err() {
        cmd.env("RUST_BACKTRACE", "1");
    }

    if let Ok(lint_flags) = env::var("RUSTC_LINT_FLAGS") {
        cmd.args(lint_flags.split_whitespace());
    }

    if target.is_some() {
        // The stage0 compiler has a special sysroot distinct from what we
        // actually downloaded, so we just always pass the `--sysroot` option,
        // unless one is already set.
        if !args.iter().any(|arg| arg == "--sysroot") {
            cmd.arg("--sysroot").arg(&sysroot);
        }

        // If we're compiling specifically the `panic_abort` crate then we pass
        // the `-C panic=abort` option. Note that we do not do this for any
        // other crate intentionally as this is the only crate for now that we
        // ship with panic=abort.
        //
        // This... is a bit of a hack how we detect this. Ideally this
        // information should be encoded in the crate I guess? Would likely
        // require an RFC amendment to RFC 1513, however.
        //
        // `compiler_builtins` are unconditionally compiled with panic=abort to
        // workaround undefined references to `rust_eh_unwind_resume` generated
        // otherwise, see issue https://github.com/rust-lang/rust/issues/43095.
        if crate_name == Some("panic_abort")
            || crate_name == Some("compiler_builtins") && stage != "0"
        {
            cmd.arg("-C").arg("panic=abort");
        }
    } else {
        // FIXME(rust-lang/cargo#5754) we shouldn't be using special env vars
        // here, but rather Cargo should know what flags to pass rustc itself.

        // Override linker if necessary.
        if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
            cmd.arg(format!("-Clinker={}", host_linker));
        }
        if env::var_os("RUSTC_HOST_FUSE_LD_LLD").is_some() {
            cmd.arg("-Clink-args=-fuse-ld=lld");
        }

        if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") {
            if s == "true" {
                cmd.arg("-C").arg("target-feature=+crt-static");
            }
            if s == "false" {
                cmd.arg("-C").arg("target-feature=-crt-static");
            }
        }
    }

    if let Ok(map) = env::var("RUSTC_DEBUGINFO_MAP") {
        cmd.arg("--remap-path-prefix").arg(&map);
    }

    // Force all crates compiled by this compiler to (a) be unstable and (b)
    // allow the `rustc_private` feature to link to other unstable crates
    // also in the sysroot. We also do this for host crates, since those
    // may be proc macros, in which case we might ship them.
    if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() && (stage != "0" || target.is_some()) {
        cmd.arg("-Z").arg("force-unstable-if-unmarked");
    }

    if verbose > 1 {
        let rust_env_vars =
            env::vars().filter(|(k, _)| k.starts_with("RUST") || k.starts_with("CARGO"));
        for (i, (k, v)) in rust_env_vars.enumerate() {
            eprintln!("rustc env[{}]: {:?}={:?}", i, k, v);
        }
        eprintln!("rustc working directory: {}", env::current_dir().unwrap().display());
        eprintln!(
            "rustc command: {:?}={:?} {:?}",
            bootstrap::util::dylib_path_var(),
            env::join_paths(&dylib_path).unwrap(),
            cmd,
        );
        eprintln!("sysroot: {:?}", sysroot);
        eprintln!("libdir: {:?}", libdir);
    }

    let start = Instant::now();
    let (child, status) = {
        let errmsg = format!("\nFailed to run:\n{:?}\n-------------", cmd);
        let mut child = cmd.spawn().expect(&errmsg);
        let status = child.wait().expect(&errmsg);
        (child, status)
    };

    if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some()
        || env::var_os("RUSTC_PRINT_STEP_RUSAGE").is_some()
    {
        if let Some(crate_name) = crate_name {
            let dur = start.elapsed();
            let is_test = args.iter().any(|a| a == "--test");
            // If the user requested resource usage data, then
            // include that in addition to the timing output.
            let rusage_data = env::var_os("RUSTC_PRINT_STEP_RUSAGE").and_then(|_| {
                #[cfg(windows)]
                {
                    use std::os::windows::io::AsRawHandle;
                    let handle = child.as_raw_handle();
                    format_rusage_data(handle)
                }
                #[cfg(not(windows))]
                {
                    let _child = child;
                    format_rusage_data()
                }
            });
            eprintln!(
                "[RUSTC-TIMING] {} test:{} {}.{:03}{}{}",
                crate_name,
                is_test,
                dur.as_secs(),
                dur.subsec_millis(),
                if rusage_data.is_some() { " " } else { "" },
                rusage_data.unwrap_or(String::new()),
            );
        }
    }

    if status.success() {
        std::process::exit(0);
        // note: everything below here is unreachable. do not put code that
        // should run on success, after this block.
    }
    if verbose > 0 {
        println!("\nDid not run successfully: {}\n{:?}\n-------------", status, cmd);
    }

    if let Some(mut on_fail) = on_fail {
        on_fail.status().expect("Could not run the on_fail command");
    }

    // Preserve the exit code. In case of signal, exit with 0xfe since it's
    // awkward to preserve this status in a cross-platform way.
    match status.code() {
        Some(i) => std::process::exit(i),
        None => {
            eprintln!("rustc exited with {}", status);
            std::process::exit(0xfe);
        }
    }
}

#[cfg(all(not(unix), not(windows)))]
/// getrusage is not available on non-unix platforms. So for now, we do not
/// bother trying to make a shim for it.
fn format_rusage_data() -> Option<String> {
    None
}

#[cfg(windows)]
fn format_rusage_data(handle: std::os::windows::raw::HANDLE) -> Option<String> {
    use winapi::um::{processthreadsapi, psapi, timezoneapi};
    macro_rules! try_bool {
        ($e:expr) => {
            if $e != 1 {
                return None;
            }
        };
    }

    let mut user_filetime = Default::default();
    let mut user_time = Default::default();
    let mut kernel_filetime = Default::default();
    let mut kernel_time = Default::default();
    let mut memory_counters = psapi::PROCESS_MEMORY_COUNTERS::default();

    unsafe {
        try_bool!(processthreadsapi::GetProcessTimes(
            handle,
            &mut Default::default(),
            &mut Default::default(),
            &mut kernel_filetime,
            &mut user_filetime,
        ));
        try_bool!(timezoneapi::FileTimeToSystemTime(&user_filetime, &mut user_time));
        try_bool!(timezoneapi::FileTimeToSystemTime(&kernel_filetime, &mut kernel_time));

        // Unlike on Linux with RUSAGE_CHILDREN, this will only return memory information for the process
        // with the given handle and none of that process's children.
        try_bool!(psapi::GetProcessMemoryInfo(
            handle as _,
            &mut memory_counters as *mut _ as _,
            std::mem::size_of::<psapi::PROCESS_MEMORY_COUNTERS_EX>() as u32,
        ));
    }

    // Guide on interpreting these numbers:
    // https://docs.microsoft.com/en-us/windows/win32/psapi/process-memory-usage-information
    let peak_working_set = memory_counters.PeakWorkingSetSize / 1024;
    let peak_page_file = memory_counters.PeakPagefileUsage / 1024;
    let peak_paged_pool = memory_counters.QuotaPeakPagedPoolUsage / 1024;
    let peak_nonpaged_pool = memory_counters.QuotaPeakNonPagedPoolUsage / 1024;
    Some(format!(
        "user: {USER_SEC}.{USER_USEC:03} \
         sys: {SYS_SEC}.{SYS_USEC:03} \
         peak working set (kb): {PEAK_WORKING_SET} \
         peak page file usage (kb): {PEAK_PAGE_FILE} \
         peak paged pool usage (kb): {PEAK_PAGED_POOL} \
         peak non-paged pool usage (kb): {PEAK_NONPAGED_POOL} \
         page faults: {PAGE_FAULTS}",
        USER_SEC = user_time.wSecond + (user_time.wMinute * 60),
        USER_USEC = user_time.wMilliseconds,
        SYS_SEC = kernel_time.wSecond + (kernel_time.wMinute * 60),
        SYS_USEC = kernel_time.wMilliseconds,
        PEAK_WORKING_SET = peak_working_set,
        PEAK_PAGE_FILE = peak_page_file,
        PEAK_PAGED_POOL = peak_paged_pool,
        PEAK_NONPAGED_POOL = peak_nonpaged_pool,
        PAGE_FAULTS = memory_counters.PageFaultCount,
    ))
}

#[cfg(unix)]
/// Tries to build a string with human readable data for several of the rusage
/// fields. Note that we are focusing mainly on data that we believe to be
/// supplied on Linux (the `rusage` struct has other fields in it but they are
/// currently unsupported by Linux).
fn format_rusage_data() -> Option<String> {
    let rusage: libc::rusage = unsafe {
        let mut recv = std::mem::zeroed();
        // -1 is RUSAGE_CHILDREN, which means to get the rusage for all children
        // (and grandchildren, etc) processes that have respectively terminated
        // and been waited for.
        let retval = libc::getrusage(-1, &mut recv);
        if retval != 0 {
            return None;
        }
        recv
    };
    // Mac OS X reports the maxrss in bytes, not kb.
    let divisor = if env::consts::OS == "macos" { 1024 } else { 1 };
    let maxrss = rusage.ru_maxrss + (divisor - 1) / divisor;

    let mut init_str = format!(
        "user: {USER_SEC}.{USER_USEC:03} \
         sys: {SYS_SEC}.{SYS_USEC:03} \
         max rss (kb): {MAXRSS}",
        USER_SEC = rusage.ru_utime.tv_sec,
        USER_USEC = rusage.ru_utime.tv_usec,
        SYS_SEC = rusage.ru_stime.tv_sec,
        SYS_USEC = rusage.ru_stime.tv_usec,
        MAXRSS = maxrss
    );

    // The remaining rusage stats vary in platform support. So we treat
    // uniformly zero values in each category as "not worth printing", since it
    // either means no events of that type occurred, or that the platform
    // does not support it.

    let minflt = rusage.ru_minflt;
    let majflt = rusage.ru_majflt;
    if minflt != 0 || majflt != 0 {
        init_str.push_str(&format!(" page reclaims: {} page faults: {}", minflt, majflt));
    }

    let inblock = rusage.ru_inblock;
    let oublock = rusage.ru_oublock;
    if inblock != 0 || oublock != 0 {
        init_str.push_str(&format!(" fs block inputs: {} fs block outputs: {}", inblock, oublock));
    }

    let nvcsw = rusage.ru_nvcsw;
    let nivcsw = rusage.ru_nivcsw;
    if nvcsw != 0 || nivcsw != 0 {
        init_str.push_str(&format!(
            " voluntary ctxt switches: {} involuntary ctxt switches: {}",
            nvcsw, nivcsw
        ));
    }

    return Some(init_str);
}
<commit_msg>Clean up handling of child process<commit_after>//! Shim which is passed to Cargo as "rustc" when running the bootstrap.
//!
//! This shim will take care of some various tasks that our build process
//! requires that Cargo can't quite do through normal configuration:
//!
//! 1. When compiling build scripts and build dependencies, we need a guaranteed
//!    full standard library available. The only compiler which actually has
//!    this is the snapshot, so we detect this situation and always compile with
//!    the snapshot compiler.
//! 2. We pass a bunch of `--cfg` and other flags based on what we're compiling
//!    (and this slightly differs based on a whether we're using a snapshot or
//!    not), so we do that all here.
//!
//! This may one day be replaced by RUSTFLAGS, but the dynamic nature of
//! switching compilers for the bootstrap and for build scripts will probably
//! never get replaced.

use std::env;
use std::path::PathBuf;
use std::process::{Child, Command};
use std::str::FromStr;
use std::time::Instant;

fn main() {
    let args = env::args_os().skip(1).collect::<Vec<_>>();

    // Detect whether or not we're a build script depending on whether --target
    // is passed (a bit janky...)
    let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str());
    let version = args.iter().find(|w| &**w == "-vV");

    let verbose = match env::var("RUSTC_VERBOSE") {
        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
        Err(_) => 0,
    };

    // Use a different compiler for build scripts, since there may not yet be a
    // libstd for the real compiler to use. However, if Cargo is attempting to
    // determine the version of the compiler, the real compiler needs to be
    // used. Currently, these two states are differentiated based on whether
    // --target and -vV is/isn't passed.
    let (rustc, libdir) = if target.is_none() && version.is_none() {
        ("RUSTC_SNAPSHOT", "RUSTC_SNAPSHOT_LIBDIR")
    } else {
        ("RUSTC_REAL", "RUSTC_LIBDIR")
    };
    let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set");
    let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
    let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new);

    let rustc = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc));
    let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{:?} was not set", libdir));
    let mut dylib_path = bootstrap::util::dylib_path();
    dylib_path.insert(0, PathBuf::from(&libdir));

    let mut cmd = Command::new(rustc);
    cmd.args(&args).env(bootstrap::util::dylib_path_var(), env::join_paths(&dylib_path).unwrap());

    // Get the name of the crate we're compiling, if any.
    let crate_name =
        args.windows(2).find(|args| args[0] == "--crate-name").and_then(|args| args[1].to_str());

    if let Some(crate_name) = crate_name {
        if let Some(target) = env::var_os("RUSTC_TIME") {
            if target == "all"
                || target.into_string().unwrap().split(',').any(|c| c.trim() == crate_name)
            {
                cmd.arg("-Ztime");
            }
        }
    }

    // Print backtrace in case of ICE
    if env::var("RUSTC_BACKTRACE_ON_ICE").is_ok() && env::var("RUST_BACKTRACE").is_err() {
        cmd.env("RUST_BACKTRACE", "1");
    }

    if let Ok(lint_flags) = env::var("RUSTC_LINT_FLAGS") {
        cmd.args(lint_flags.split_whitespace());
    }

    if target.is_some() {
        // The stage0 compiler has a special sysroot distinct from what we
        // actually downloaded, so we just always pass the `--sysroot` option,
        // unless one is already set.
        if !args.iter().any(|arg| arg == "--sysroot") {
            cmd.arg("--sysroot").arg(&sysroot);
        }

        // If we're compiling specifically the `panic_abort` crate then we pass
        // the `-C panic=abort` option. Note that we do not do this for any
        // other crate intentionally as this is the only crate for now that we
        // ship with panic=abort.
        //
        // This... is a bit of a hack how we detect this. Ideally this
        // information should be encoded in the crate I guess? Would likely
        // require an RFC amendment to RFC 1513, however.
        //
        // `compiler_builtins` are unconditionally compiled with panic=abort to
        // workaround undefined references to `rust_eh_unwind_resume` generated
        // otherwise, see issue https://github.com/rust-lang/rust/issues/43095.
        if crate_name == Some("panic_abort")
            || crate_name == Some("compiler_builtins") && stage != "0"
        {
            cmd.arg("-C").arg("panic=abort");
        }
    } else {
        // FIXME(rust-lang/cargo#5754) we shouldn't be using special env vars
        // here, but rather Cargo should know what flags to pass rustc itself.

        // Override linker if necessary.
        if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
            cmd.arg(format!("-Clinker={}", host_linker));
        }
        if env::var_os("RUSTC_HOST_FUSE_LD_LLD").is_some() {
            cmd.arg("-Clink-args=-fuse-ld=lld");
        }

        if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") {
            if s == "true" {
                cmd.arg("-C").arg("target-feature=+crt-static");
            }
            if s == "false" {
                cmd.arg("-C").arg("target-feature=-crt-static");
            }
        }
    }

    if let Ok(map) = env::var("RUSTC_DEBUGINFO_MAP") {
        cmd.arg("--remap-path-prefix").arg(&map);
    }

    // Force all crates compiled by this compiler to (a) be unstable and (b)
    // allow the `rustc_private` feature to link to other unstable crates
    // also in the sysroot. We also do this for host crates, since those
    // may be proc macros, in which case we might ship them.
    if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() && (stage != "0" || target.is_some()) {
        cmd.arg("-Z").arg("force-unstable-if-unmarked");
    }

    if verbose > 1 {
        let rust_env_vars =
            env::vars().filter(|(k, _)| k.starts_with("RUST") || k.starts_with("CARGO"));
        for (i, (k, v)) in rust_env_vars.enumerate() {
            eprintln!("rustc env[{}]: {:?}={:?}", i, k, v);
        }
        eprintln!("rustc working directory: {}", env::current_dir().unwrap().display());
        eprintln!(
            "rustc command: {:?}={:?} {:?}",
            bootstrap::util::dylib_path_var(),
            env::join_paths(&dylib_path).unwrap(),
            cmd,
        );
        eprintln!("sysroot: {:?}", sysroot);
        eprintln!("libdir: {:?}", libdir);
    }

    let start = Instant::now();
    let (child, status) = {
        let errmsg = format!("\nFailed to run:\n{:?}\n-------------", cmd);
        let mut child = cmd.spawn().expect(&errmsg);
        let status = child.wait().expect(&errmsg);
        (child, status)
    };

    if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some()
        || env::var_os("RUSTC_PRINT_STEP_RUSAGE").is_some()
    {
        if let Some(crate_name) = crate_name {
            let dur = start.elapsed();
            let is_test = args.iter().any(|a| a == "--test");
            // If the user requested resource usage data, then
            // include that in addition to the timing output.
            let rusage_data =
                env::var_os("RUSTC_PRINT_STEP_RUSAGE").and_then(|_| format_rusage_data(child));
            eprintln!(
                "[RUSTC-TIMING] {} test:{} {}.{:03}{}{}",
                crate_name,
                is_test,
                dur.as_secs(),
                dur.subsec_millis(),
                if rusage_data.is_some() { " " } else { "" },
                rusage_data.unwrap_or(String::new()),
            );
        }
    }

    if status.success() {
        std::process::exit(0);
        // note: everything below here is unreachable. do not put code that
        // should run on success, after this block.
    }
    if verbose > 0 {
        println!("\nDid not run successfully: {}\n{:?}\n-------------", status, cmd);
    }

    if let Some(mut on_fail) = on_fail {
        on_fail.status().expect("Could not run the on_fail command");
    }

    // Preserve the exit code. In case of signal, exit with 0xfe since it's
    // awkward to preserve this status in a cross-platform way.
    match status.code() {
        Some(i) => std::process::exit(i),
        None => {
            eprintln!("rustc exited with {}", status);
            std::process::exit(0xfe);
        }
    }
}

#[cfg(all(not(unix), not(windows)))]
// In the future we can add this for more platforms
fn format_rusage_data(_child: Child) -> Option<String> {
    None
}

#[cfg(windows)]
fn format_rusage_data(child: Child) -> Option<String> {
    use std::os::windows::io::AsRawHandle;
    use winapi::um::{processthreadsapi, psapi, timezoneapi};
    let handle = child.as_raw_handle();
    macro_rules! try_bool {
        ($e:expr) => {
            if $e != 1 {
                return None;
            }
        };
    }

    let mut user_filetime = Default::default();
    let mut user_time = Default::default();
    let mut kernel_filetime = Default::default();
    let mut kernel_time = Default::default();
    let mut memory_counters = psapi::PROCESS_MEMORY_COUNTERS::default();

    unsafe {
        try_bool!(processthreadsapi::GetProcessTimes(
            handle,
            &mut Default::default(),
            &mut Default::default(),
            &mut kernel_filetime,
            &mut user_filetime,
        ));
        try_bool!(timezoneapi::FileTimeToSystemTime(&user_filetime, &mut user_time));
        try_bool!(timezoneapi::FileTimeToSystemTime(&kernel_filetime, &mut kernel_time));

        // Unlike on Linux with RUSAGE_CHILDREN, this will only return memory information for the process
        // with the given handle and none of that process's children.
        try_bool!(psapi::GetProcessMemoryInfo(
            handle as _,
            &mut memory_counters as *mut _ as _,
            std::mem::size_of::<psapi::PROCESS_MEMORY_COUNTERS_EX>() as u32,
        ));
    }

    // Guide on interpreting these numbers:
    // https://docs.microsoft.com/en-us/windows/win32/psapi/process-memory-usage-information
    let peak_working_set = memory_counters.PeakWorkingSetSize / 1024;
    let peak_page_file = memory_counters.PeakPagefileUsage / 1024;
    let peak_paged_pool = memory_counters.QuotaPeakPagedPoolUsage / 1024;
    let peak_nonpaged_pool = memory_counters.QuotaPeakNonPagedPoolUsage / 1024;
    Some(format!(
        "user: {USER_SEC}.{USER_USEC:03} \
         sys: {SYS_SEC}.{SYS_USEC:03} \
         peak working set (kb): {PEAK_WORKING_SET} \
         peak page file usage (kb): {PEAK_PAGE_FILE} \
         peak paged pool usage (kb): {PEAK_PAGED_POOL} \
         peak non-paged pool usage (kb): {PEAK_NONPAGED_POOL} \
         page faults: {PAGE_FAULTS}",
        USER_SEC = user_time.wSecond + (user_time.wMinute * 60),
        USER_USEC = user_time.wMilliseconds,
        SYS_SEC = kernel_time.wSecond + (kernel_time.wMinute * 60),
        SYS_USEC = kernel_time.wMilliseconds,
        PEAK_WORKING_SET = peak_working_set,
        PEAK_PAGE_FILE = peak_page_file,
        PEAK_PAGED_POOL = peak_paged_pool,
        PEAK_NONPAGED_POOL = peak_nonpaged_pool,
        PAGE_FAULTS = memory_counters.PageFaultCount,
    ))
}

#[cfg(unix)]
/// Tries to build a string with human readable data for several of the rusage
/// fields. Note that we are focusing mainly on data that we believe to be
/// supplied on Linux (the `rusage` struct has other fields in it but they are
/// currently unsupported by Linux).
fn format_rusage_data(_child: Child) -> Option<String> {
    let rusage: libc::rusage = unsafe {
        let mut recv = std::mem::zeroed();
        // -1 is RUSAGE_CHILDREN, which means to get the rusage for all children
        // (and grandchildren, etc) processes that have respectively terminated
        // and been waited for.
        let retval = libc::getrusage(-1, &mut recv);
        if retval != 0 {
            return None;
        }
        recv
    };
    // Mac OS X reports the maxrss in bytes, not kb.
    let divisor = if env::consts::OS == "macos" { 1024 } else { 1 };
    let maxrss = rusage.ru_maxrss + (divisor - 1) / divisor;

    let mut init_str = format!(
        "user: {USER_SEC}.{USER_USEC:03} \
         sys: {SYS_SEC}.{SYS_USEC:03} \
         max rss (kb): {MAXRSS}",
        USER_SEC = rusage.ru_utime.tv_sec,
        USER_USEC = rusage.ru_utime.tv_usec,
        SYS_SEC = rusage.ru_stime.tv_sec,
        SYS_USEC = rusage.ru_stime.tv_usec,
        MAXRSS = maxrss
    );

    // The remaining rusage stats vary in platform support. So we treat
    // uniformly zero values in each category as "not worth printing", since it
    // either means no events of that type occurred, or that the platform
    // does not support it.

    let minflt = rusage.ru_minflt;
    let majflt = rusage.ru_majflt;
    if minflt != 0 || majflt != 0 {
        init_str.push_str(&format!(" page reclaims: {} page faults: {}", minflt, majflt));
    }

    let inblock = rusage.ru_inblock;
    let oublock = rusage.ru_oublock;
    if inblock != 0 || oublock != 0 {
        init_str.push_str(&format!(" fs block inputs: {} fs block outputs: {}", inblock, oublock));
    }

    let nvcsw = rusage.ru_nvcsw;
    let nivcsw = rusage.ru_nivcsw;
    if nvcsw != 0 || nivcsw != 0 {
        init_str.push_str(&format!(
            " voluntary ctxt switches: {} involuntary ctxt switches: {}",
            nvcsw, nivcsw
        ));
    }

    return Some(init_str);
}
<|endoftext|>


<commit_before>//! Shim which is passed to Cargo as "rustc" when running the bootstrap.
//!
//! This shim will take care of some various tasks that our build process
//! requires that Cargo can't quite do through normal configuration:
//!
//! 1. When compiling build scripts and build dependencies, we need a guaranteed
//!    full standard library available. The only compiler which actually has
//!    this is the snapshot, so we detect this situation and always compile with
//!    the snapshot compiler.
//! 2. We pass a bunch of `--cfg` and other flags based on what we're compiling
//!    (and this slightly differs based on a whether we're using a snapshot or
//!    not), so we do that all here.
//!
//! This may one day be replaced by RUSTFLAGS, but the dynamic nature of
//! switching compilers for the bootstrap and for build scripts will probably
//! never get replaced.

use std::env;
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
use std::time::Instant;

fn main() {
    let args = env::args_os().skip(1).collect::<Vec<_>>();

    // Detect whether or not we're a build script depending on whether --target
    // is passed (a bit janky...)
    let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str());
    let version = args.iter().find(|w| &**w == "-vV");

    let verbose = match env::var("RUSTC_VERBOSE") {
        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
        Err(_) => 0,
    };

    // Use a different compiler for build scripts, since there may not yet be a
    // libstd for the real compiler to use. However, if Cargo is attempting to
    // determine the version of the compiler, the real compiler needs to be
    // used. Currently, these two states are differentiated based on whether
    // --target and -vV is/isn't passed.
    let (rustc, libdir) = if target.is_none() && version.is_none() {
        ("RUSTC_SNAPSHOT", "RUSTC_SNAPSHOT_LIBDIR")
    } else {
        ("RUSTC_REAL", "RUSTC_LIBDIR")
    };
    let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set");
    let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
    let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new);

    let rustc = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc));
    let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{:?} was not set", libdir));
    let mut dylib_path = bootstrap::util::dylib_path();
    dylib_path.insert(0, PathBuf::from(&libdir));

    let mut cmd = Command::new(rustc);
    cmd.args(&args).env(bootstrap::util::dylib_path_var(), env::join_paths(&dylib_path).unwrap());

    // Get the name of the crate we're compiling, if any.
    let crate_name =
        args.windows(2).find(|args| args[0] == "--crate-name").and_then(|args| args[1].to_str());

    if let Some(crate_name) = crate_name {
        if let Some(target) = env::var_os("RUSTC_TIME") {
            if target == "all"
                || target.into_string().unwrap().split(',').any(|c| c.trim() == crate_name)
            {
                cmd.arg("-Ztime");
            }
        }
    }

    // Print backtrace in case of ICE
    if env::var("RUSTC_BACKTRACE_ON_ICE").is_ok() && env::var("RUST_BACKTRACE").is_err() {
        cmd.env("RUST_BACKTRACE", "1");
    }

    if let Ok(lint_flags) = env::var("RUSTC_LINT_FLAGS") {
        cmd.args(lint_flags.split_whitespace());
    }

    if target.is_some() {
        // The stage0 compiler has a special sysroot distinct from what we
        // actually downloaded, so we just always pass the `--sysroot` option,
        // unless one is already set.
        if !args.iter().any(|arg| arg == "--sysroot") {
            cmd.arg("--sysroot").arg(&sysroot);
        }

        // If we're compiling specifically the `panic_abort` crate then we pass
        // the `-C panic=abort` option. Note that we do not do this for any
        // other crate intentionally as this is the only crate for now that we
        // ship with panic=abort.
        //
        // This... is a bit of a hack how we detect this. Ideally this
        // information should be encoded in the crate I guess? Would likely
        // require an RFC amendment to RFC 1513, however.
        //
        // `compiler_builtins` are unconditionally compiled with panic=abort to
        // workaround undefined references to `rust_eh_unwind_resume` generated
        // otherwise, see issue https://github.com/rust-lang/rust/issues/43095.
        if crate_name == Some("panic_abort")
            || crate_name == Some("compiler_builtins") && stage != "0"
        {
            cmd.arg("-C").arg("panic=abort");
        }
    } else {
        // FIXME(rust-lang/cargo#5754) we shouldn't be using special env vars
        // here, but rather Cargo should know what flags to pass rustc itself.

        // Override linker if necessary.
        if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
            cmd.arg(format!("-Clinker={}", host_linker));
        }
        if env::var_os("RUSTC_HOST_FUSE_LD_LLD").is_some() {
            cmd.arg("-Clink-args=-fuse-ld=lld");
        }

        if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") {
            if s == "true" {
                cmd.arg("-C").arg("target-feature=+crt-static");
            }
            if s == "false" {
                cmd.arg("-C").arg("target-feature=-crt-static");
            }
        }
    }

    if let Ok(map) = env::var("RUSTC_DEBUGINFO_MAP") {
        cmd.arg("--remap-path-prefix").arg(&map);
    }

    // Force all crates compiled by this compiler to (a) be unstable and (b)
    // allow the `rustc_private` feature to link to other unstable crates
    // also in the sysroot. We also do this for host crates, since those
    // may be proc macros, in which case we might ship them.
    if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() && (stage != "0" || target.is_some()) {
        cmd.arg("-Z").arg("force-unstable-if-unmarked");
    }

    if verbose > 1 {
        eprintln!(
            "rustc command: {:?}={:?} {:?}",
            bootstrap::util::dylib_path_var(),
            env::join_paths(&dylib_path).unwrap(),
            cmd,
        );
        eprintln!("sysroot: {:?}", sysroot);
        eprintln!("libdir: {:?}", libdir);
    }

    let start = Instant::now();
    let status = {
        let errmsg = format!("\nFailed to run:\n{:?}\n-------------", cmd);
        cmd.status().expect(&errmsg)
    };

    if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some() {
        if let Some(crate_name) = crate_name {
            let dur = start.elapsed();
            let is_test = args.iter().any(|a| a == "--test");
            eprintln!(
                "[RUSTC-TIMING] {} test:{} {}.{:03}",
                crate_name,
                is_test,
                dur.as_secs(),
                dur.subsec_millis()
            );
        }
    }

    if status.success() {
        std::process::exit(0);
        // note: everything below here is unreachable. do not put code that
        // should run on success, after this block.
    }
    if verbose > 0 {
        println!("\nDid not run successfully: {}\n{:?}\n-------------", status, cmd);
    }

    if let Some(mut on_fail) = on_fail {
        on_fail.status().expect("Could not run the on_fail command");
    }

    // Preserve the exit code. In case of signal, exit with 0xfe since it's
    // awkward to preserve this status in a cross-platform way.
    match status.code() {
        Some(i) => std::process::exit(i),
        None => {
            eprintln!("rustc exited with {}", status);
            std::process::exit(0xfe);
        }
    }
}
<commit_msg>Rollup merge of #82403 - pnkfelix:rustbuild-emit-env-vars-on-verbose-verbose, r=Mark-Simulacrum<commit_after>//! Shim which is passed to Cargo as "rustc" when running the bootstrap.
//!
//! This shim will take care of some various tasks that our build process
//! requires that Cargo can't quite do through normal configuration:
//!
//! 1. When compiling build scripts and build dependencies, we need a guaranteed
//!    full standard library available. The only compiler which actually has
//!    this is the snapshot, so we detect this situation and always compile with
//!    the snapshot compiler.
//! 2. We pass a bunch of `--cfg` and other flags based on what we're compiling
//!    (and this slightly differs based on a whether we're using a snapshot or
//!    not), so we do that all here.
//!
//! This may one day be replaced by RUSTFLAGS, but the dynamic nature of
//! switching compilers for the bootstrap and for build scripts will probably
//! never get replaced.

use std::env;
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
use std::time::Instant;

fn main() {
    let args = env::args_os().skip(1).collect::<Vec<_>>();

    // Detect whether or not we're a build script depending on whether --target
    // is passed (a bit janky...)
    let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str());
    let version = args.iter().find(|w| &**w == "-vV");

    let verbose = match env::var("RUSTC_VERBOSE") {
        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
        Err(_) => 0,
    };

    // Use a different compiler for build scripts, since there may not yet be a
    // libstd for the real compiler to use. However, if Cargo is attempting to
    // determine the version of the compiler, the real compiler needs to be
    // used. Currently, these two states are differentiated based on whether
    // --target and -vV is/isn't passed.
    let (rustc, libdir) = if target.is_none() && version.is_none() {
        ("RUSTC_SNAPSHOT", "RUSTC_SNAPSHOT_LIBDIR")
    } else {
        ("RUSTC_REAL", "RUSTC_LIBDIR")
    };
    let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set");
    let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
    let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new);

    let rustc = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc));
    let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{:?} was not set", libdir));
    let mut dylib_path = bootstrap::util::dylib_path();
    dylib_path.insert(0, PathBuf::from(&libdir));

    let mut cmd = Command::new(rustc);
    cmd.args(&args).env(bootstrap::util::dylib_path_var(), env::join_paths(&dylib_path).unwrap());

    // Get the name of the crate we're compiling, if any.
    let crate_name =
        args.windows(2).find(|args| args[0] == "--crate-name").and_then(|args| args[1].to_str());

    if let Some(crate_name) = crate_name {
        if let Some(target) = env::var_os("RUSTC_TIME") {
            if target == "all"
                || target.into_string().unwrap().split(',').any(|c| c.trim() == crate_name)
            {
                cmd.arg("-Ztime");
            }
        }
    }

    // Print backtrace in case of ICE
    if env::var("RUSTC_BACKTRACE_ON_ICE").is_ok() && env::var("RUST_BACKTRACE").is_err() {
        cmd.env("RUST_BACKTRACE", "1");
    }

    if let Ok(lint_flags) = env::var("RUSTC_LINT_FLAGS") {
        cmd.args(lint_flags.split_whitespace());
    }

    if target.is_some() {
        // The stage0 compiler has a special sysroot distinct from what we
        // actually downloaded, so we just always pass the `--sysroot` option,
        // unless one is already set.
        if !args.iter().any(|arg| arg == "--sysroot") {
            cmd.arg("--sysroot").arg(&sysroot);
        }

        // If we're compiling specifically the `panic_abort` crate then we pass
        // the `-C panic=abort` option. Note that we do not do this for any
        // other crate intentionally as this is the only crate for now that we
        // ship with panic=abort.
        //
        // This... is a bit of a hack how we detect this. Ideally this
        // information should be encoded in the crate I guess? Would likely
        // require an RFC amendment to RFC 1513, however.
        //
        // `compiler_builtins` are unconditionally compiled with panic=abort to
        // workaround undefined references to `rust_eh_unwind_resume` generated
        // otherwise, see issue https://github.com/rust-lang/rust/issues/43095.
        if crate_name == Some("panic_abort")
            || crate_name == Some("compiler_builtins") && stage != "0"
        {
            cmd.arg("-C").arg("panic=abort");
        }
    } else {
        // FIXME(rust-lang/cargo#5754) we shouldn't be using special env vars
        // here, but rather Cargo should know what flags to pass rustc itself.

        // Override linker if necessary.
        if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
            cmd.arg(format!("-Clinker={}", host_linker));
        }
        if env::var_os("RUSTC_HOST_FUSE_LD_LLD").is_some() {
            cmd.arg("-Clink-args=-fuse-ld=lld");
        }

        if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") {
            if s == "true" {
                cmd.arg("-C").arg("target-feature=+crt-static");
            }
            if s == "false" {
                cmd.arg("-C").arg("target-feature=-crt-static");
            }
        }
    }

    if let Ok(map) = env::var("RUSTC_DEBUGINFO_MAP") {
        cmd.arg("--remap-path-prefix").arg(&map);
    }

    // Force all crates compiled by this compiler to (a) be unstable and (b)
    // allow the `rustc_private` feature to link to other unstable crates
    // also in the sysroot. We also do this for host crates, since those
    // may be proc macros, in which case we might ship them.
    if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() && (stage != "0" || target.is_some()) {
        cmd.arg("-Z").arg("force-unstable-if-unmarked");
    }

    if verbose > 1 {
        let rust_env_vars =
            env::vars().filter(|(k, _)| k.starts_with("RUST") || k.starts_with("CARGO"));
        for (i, (k, v)) in rust_env_vars.enumerate() {
            eprintln!("rustc env[{}]: {:?}={:?}", i, k, v);
        }
        eprintln!("rustc working directory: {}", env::current_dir().unwrap().display());
        eprintln!(
            "rustc command: {:?}={:?} {:?}",
            bootstrap::util::dylib_path_var(),
            env::join_paths(&dylib_path).unwrap(),
            cmd,
        );
        eprintln!("sysroot: {:?}", sysroot);
        eprintln!("libdir: {:?}", libdir);
    }

    let start = Instant::now();
    let status = {
        let errmsg = format!("\nFailed to run:\n{:?}\n-------------", cmd);
        cmd.status().expect(&errmsg)
    };

    if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some() {
        if let Some(crate_name) = crate_name {
            let dur = start.elapsed();
            let is_test = args.iter().any(|a| a == "--test");
            eprintln!(
                "[RUSTC-TIMING] {} test:{} {}.{:03}",
                crate_name,
                is_test,
                dur.as_secs(),
                dur.subsec_millis()
            );
        }
    }

    if status.success() {
        std::process::exit(0);
        // note: everything below here is unreachable. do not put code that
        // should run on success, after this block.
    }
    if verbose > 0 {
        println!("\nDid not run successfully: {}\n{:?}\n-------------", status, cmd);
    }

    if let Some(mut on_fail) = on_fail {
        on_fail.status().expect("Could not run the on_fail command");
    }

    // Preserve the exit code. In case of signal, exit with 0xfe since it's
    // awkward to preserve this status in a cross-platform way.
    match status.code() {
        Some(i) => std::process::exit(i),
        None => {
            eprintln!("rustc exited with {}", status);
            std::process::exit(0xfe);
        }
    }
}
<|endoftext|>


<commit_before>//! Sequential IR for Weld programs

use std::fmt;
use std::collections::{BTreeMap, HashMap};

use super::ast::*;
use super::ast::ExprKind::*;
use super::error::*;
use super::pretty_print::*;
use super::util::SymbolGenerator;

type BasicBlockId = usize;

/// A non-terminating statement inside a basic block.
pub enum Statement {
    /// output, op, type, left, right
    AssignBinOp(Symbol, BinOpKind, Type, Symbol, Symbol),
    /// output, value
    Assign(Symbol, Symbol),
    /// output, value
    AssignLiteral(Symbol, LiteralKind),
    /// output, builder, value
    DoMerge(Symbol, Symbol, Symbol),
    /// output, builder
    GetResult(Symbol, Symbol),
    /// output, builder type
    CreateBuilder(Symbol, Type),
}

/// A terminating statement inside a basic block.
pub enum Terminator {
    /// condition, on_true, on_false
    Branch(Symbol, BasicBlockId, BasicBlockId),
    Jump(BasicBlockId),
    Return(Symbol),
    ParallelFor {
        data: Symbol,
        builder: Symbol,
        body: BasicBlockId,
        data_arg: Symbol,
        builder_arg: Symbol,
        exit: BasicBlockId,
        result: Symbol,
    },
    Crash
}

/// A basic block inside a SIR program
pub struct BasicBlock {
    id: BasicBlockId,
    statements: Vec<Statement>,
    terminator: Terminator
}

pub struct SirFunction {
    params: Vec<TypedParameter>,
    locals: HashMap<Symbol, Type>,
    blocks: Vec<BasicBlock>,
    sym_gen: SymbolGenerator,
}

impl SirFunction {
    pub fn new() -> SirFunction {
        SirFunction {
            params: vec![],
            blocks: vec![],
            locals: HashMap::new(),
            sym_gen: SymbolGenerator::new(),
        }
    }

    /// Add a new basic block and return its block ID.
    pub fn add_block(&mut self) -> BasicBlockId {
        let block = BasicBlock {
            id: self.blocks.len(),
            statements: vec![],
            terminator: Terminator::Crash
        };
        self.blocks.push(block);
        self.blocks.len() - 1
    }

    /// Add a local variable of the given type and return a symbol for it.
    pub fn add_local(&mut self, ty: &Type) -> Symbol {
        let sym = self.sym_gen.new_symbol("tmp");
        self.locals.insert(sym.clone(), ty.clone());
        sym
    }
}

impl BasicBlock {
    pub fn add_statement(&mut self, statement: Statement) {
        self.statements.push(statement);
    }
}

impl fmt::Display for Statement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Statement::*;
        match *self {
            AssignBinOp(ref out, ref op, ref ty, ref left, ref right) => {
                write!(f, "{} = {} {} {} {}", out, op, print_type(ty), left, right)
            },
            Assign(ref out, ref value) => write!(f, "{} = {}", out, value),
            AssignLiteral(ref out, ref lit) => write!(f, "{} = {}", out, print_literal(lit)),
            DoMerge(ref out, ref bld, ref elem) => write!(f, "{} = merge {} {}", out, bld, elem),
            GetResult(ref out, ref value) => write!(f, "{} = result {}", out, value),
            CreateBuilder(ref out, ref ty) => write!(f, "{} = new {}", out, print_type(ty)),
        }
    }
}

impl fmt::Display for Terminator {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Terminator::*;
        match *self {
            Branch(ref cond, ref on_true, ref on_false) => {
                write!(f, "branch {} B{} B{}", cond, on_true, on_false)
            },
            ParallelFor {
                ref data, ref builder, body, ref data_arg, ref builder_arg, exit, ref result
            } => {
                write!(f, "for {} {} B{} {} {} B{} {}",
                    data, builder, body, data_arg, builder_arg, exit, result)
            },
            Jump(block) => write!(f, "jump B{}", block),
            Return(ref sym) => write!(f, "return {}", sym),
            Crash => write!(f, "crash")
        }
    }
}

impl fmt::Display for BasicBlock {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "B{}:\n", self.id)?;
        for stmt in &self.statements {
            write!(f, "  {}\n", stmt)?;
        }
        write!(f, "  {}\n", self.terminator)?;
        Ok(())
    }
}

impl fmt::Display for SirFunction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Params:\n")?;
        for param in &self.params {
            write!(f, "  {}: {}\n", param.name, print_type(&param.ty))?;
        }
        write!(f, "Locals:\n")?;
        let locals_sorted: BTreeMap<&Symbol, &Type> = self.locals.iter().collect();
        for (name, ty) in locals_sorted {
            write!(f, "  {}: {}\n", name, print_type(ty))?;
        }
        for block in &self.blocks {
            write!(f, "{}", block)?;
        }
        Ok(())
    }
}

pub fn ast_to_sir(expr: &TypedExpr) -> WeldResult<SirFunction> {
    if let Lambda(ref params, ref body) = expr.kind {
        let mut func = SirFunction::new();
        func.sym_gen = SymbolGenerator::from_expression(expr);
        if func.sym_gen.next_id("tmp") == 0 {
            func.sym_gen.new_symbol("tmp");
        }
        func.params = params.clone();
        let first_block = func.add_block();
        let (res_block, res_sym) = gen_expr(body, &mut func, first_block)?;
        func.blocks[res_block].terminator = Terminator::Return(res_sym);
        Ok((func))
    } else {
        weld_err!("Expression passed to ast_to_sir was not a Lambda")
    }
}

/// Generate code to compute the expression `expr` starting at the current tail of `cur_block`,
/// possibly creating new basic blocks in the process. Return the basic block that the expression
/// will be ready in, and its symbol therein.
fn gen_expr(
    expr: &TypedExpr,
    func: &mut SirFunction,
    cur_block: BasicBlockId
) -> WeldResult<(BasicBlockId, Symbol)> {
    use self::Statement::*;
    use self::Terminator::*;
    match expr.kind {
        Ident(ref sym) => Ok((cur_block, sym.clone())),

        Literal(lit) => {
            let res_sym = func.add_local(&expr.ty);
            func.blocks[cur_block].add_statement(AssignLiteral(res_sym.clone(), lit));
            Ok((cur_block, res_sym))
        },

        BinOp(kind, ref left, ref right) => {
            let (cur_block, left_sym) = gen_expr(left, func, cur_block)?;
            let (cur_block, right_sym) = gen_expr(right, func, cur_block)?;
            let res_sym = func.add_local(&expr.ty);
            func.blocks[cur_block].add_statement(
                AssignBinOp(res_sym.clone(), kind, left.ty.clone(), left_sym, right_sym));
            Ok((cur_block, res_sym))
        },

        If(ref cond, ref on_true, ref on_false) => {
            let (cur_block, cond_sym) = gen_expr(cond, func, cur_block)?;
            let true_block = func.add_block();
            let false_block = func.add_block();
            func.blocks[cur_block].terminator = Branch(cond_sym, true_block, false_block);
            let (true_block, true_sym) = gen_expr(on_true, func, true_block)?;
            let (false_block, false_sym) = gen_expr(on_false, func, false_block)?;
            let res_sym = func.add_local(&expr.ty);
            let res_block = func.add_block();
            func.blocks[true_block].add_statement(Assign(res_sym.clone(), true_sym));
            func.blocks[true_block].terminator = Jump(res_block);
            func.blocks[false_block].add_statement(Assign(res_sym.clone(), false_sym));
            func.blocks[false_block].terminator = Jump(res_block);
            Ok((res_block, res_sym))
        },

        Merge(ref builder, ref elem) => {
            let (cur_block, builder_sym) = gen_expr(builder, func, cur_block)?;
            let (cur_block, elem_sym) = gen_expr(elem, func, cur_block)?;
            let res_sym = func.add_local(&expr.ty);
            func.blocks[cur_block].add_statement(DoMerge(res_sym.clone(), builder_sym, elem_sym));
            Ok((cur_block, res_sym))
        },

        Res(ref builder) => {
            let (cur_block, builder_sym) = gen_expr(builder, func, cur_block)?;
            let res_sym = func.add_local(&expr.ty);
            func.blocks[cur_block].add_statement(GetResult(res_sym.clone(), builder_sym));
            Ok((cur_block, res_sym))
        },

        NewBuilder => {
            let res_sym = func.add_local(&expr.ty);
            func.blocks[cur_block].add_statement(CreateBuilder(res_sym.clone(), expr.ty.clone()));
            Ok((cur_block, res_sym))
        },

        For(ref data, ref builder, ref update) => {
            if let Lambda(ref params, ref body) = update.kind {
                let (cur_block, data_sym) = gen_expr(data, func, cur_block)?;
                let (cur_block, builder_sym) = gen_expr(builder, func, cur_block)?;
                let body_block = func.add_block();
                let (body_end_block, body_res) = gen_expr(body, func, body_block)?;
                func.blocks[body_end_block].terminator = Return(body_res);
                let exit_block = func.add_block();
                let res_sym = func.add_local(&expr.ty);
                func.blocks[cur_block].terminator = ParallelFor {
                    data: data_sym,
                    builder: builder_sym,
                    body: body_block,
                    data_arg: params[0].name.clone(),
                    builder_arg: params[1].name.clone(),
                    exit: exit_block,
                    result: res_sym.clone()
                };
                Ok((exit_block, res_sym))
            } else {
                weld_err!("Argument to For was not a Lambda: {}", print_expr(update))
            }
        },

        _ => weld_err!("Unsupported expression: {}", print_expr(expr))
    }
}
<commit_msg>Let support (#10)<commit_after>//! Sequential IR for Weld programs

use std::fmt;
use std::collections::{BTreeMap, HashMap};

use super::ast::*;
use super::ast::ExprKind::*;
use super::error::*;
use super::pretty_print::*;
use super::util::SymbolGenerator;

type BasicBlockId = usize;

/// A non-terminating statement inside a basic block.
pub enum Statement {
    /// output, op, type, left, right
    AssignBinOp(Symbol, BinOpKind, Type, Symbol, Symbol),
    /// output, value
    Assign(Symbol, Symbol),
    /// output, value
    AssignLiteral(Symbol, LiteralKind),
    /// output, builder, value
    DoMerge(Symbol, Symbol, Symbol),
    /// output, builder
    GetResult(Symbol, Symbol),
    /// output, builder type
    CreateBuilder(Symbol, Type),
}

/// A terminating statement inside a basic block.
pub enum Terminator {
    /// condition, on_true, on_false
    Branch(Symbol, BasicBlockId, BasicBlockId),
    Jump(BasicBlockId),
    Return(Symbol),
    ParallelFor {
        data: Symbol,
        builder: Symbol,
        body: BasicBlockId,
        data_arg: Symbol,
        builder_arg: Symbol,
        exit: BasicBlockId,
        result: Symbol,
    },
    Crash
}

/// A basic block inside a SIR program
pub struct BasicBlock {
    id: BasicBlockId,
    statements: Vec<Statement>,
    terminator: Terminator
}

pub struct SirFunction {
    params: Vec<TypedParameter>,
    locals: HashMap<Symbol, Type>,
    blocks: Vec<BasicBlock>,
    sym_gen: SymbolGenerator,
}

impl SirFunction {
    pub fn new() -> SirFunction {
        SirFunction {
            params: vec![],
            blocks: vec![],
            locals: HashMap::new(),
            sym_gen: SymbolGenerator::new(),
        }
    }

    /// Add a new basic block and return its block ID.
    pub fn add_block(&mut self) -> BasicBlockId {
        let block = BasicBlock {
            id: self.blocks.len(),
            statements: vec![],
            terminator: Terminator::Crash
        };
        self.blocks.push(block);
        self.blocks.len() - 1
    }

    /// Add a local variable of the given type and return a symbol for it.
    pub fn add_local(&mut self, ty: &Type) -> Symbol {
        let sym = self.sym_gen.new_symbol("tmp");
        self.locals.insert(sym.clone(), ty.clone());
        sym
    }

    /// Add a local variable of the given type and name
    pub fn add_local_named(&mut self, ty: &Type, sym: &Symbol) {
        self.locals.insert(sym.clone(), ty.clone());
    }
}

impl BasicBlock {
    pub fn add_statement(&mut self, statement: Statement) {
        self.statements.push(statement);
    }
}

impl fmt::Display for Statement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Statement::*;
        match *self {
            AssignBinOp(ref out, ref op, ref ty, ref left, ref right) => {
                write!(f, "{} = {} {} {} {}", out, op, print_type(ty), left, right)
            },
            Assign(ref out, ref value) => write!(f, "{} = {}", out, value),
            AssignLiteral(ref out, ref lit) => write!(f, "{} = {}", out, print_literal(lit)),
            DoMerge(ref out, ref bld, ref elem) => write!(f, "{} = merge {} {}", out, bld, elem),
            GetResult(ref out, ref value) => write!(f, "{} = result {}", out, value),
            CreateBuilder(ref out, ref ty) => write!(f, "{} = new {}", out, print_type(ty)),
        }
    }
}

impl fmt::Display for Terminator {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Terminator::*;
        match *self {
            Branch(ref cond, ref on_true, ref on_false) => {
                write!(f, "branch {} B{} B{}", cond, on_true, on_false)
            },
            ParallelFor {
                ref data, ref builder, body, ref data_arg, ref builder_arg, exit, ref result
            } => {
                write!(f, "for {} {} B{} {} {} B{} {}",
                    data, builder, body, data_arg, builder_arg, exit, result)
            },
            Jump(block) => write!(f, "jump B{}", block),
            Return(ref sym) => write!(f, "return {}", sym),
            Crash => write!(f, "crash")
        }
    }
}

impl fmt::Display for BasicBlock {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "B{}:\n", self.id)?;
        for stmt in &self.statements {
            write!(f, "  {}\n", stmt)?;
        }
        write!(f, "  {}\n", self.terminator)?;
        Ok(())
    }
}

impl fmt::Display for SirFunction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Params:\n")?;
        for param in &self.params {
            write!(f, "  {}: {}\n", param.name, print_type(&param.ty))?;
        }
        write!(f, "Locals:\n")?;
        let locals_sorted: BTreeMap<&Symbol, &Type> = self.locals.iter().collect();
        for (name, ty) in locals_sorted {
            write!(f, "  {}: {}\n", name, print_type(ty))?;
        }
        for block in &self.blocks {
            write!(f, "{}", block)?;
        }
        Ok(())
    }
}

pub fn ast_to_sir(expr: &TypedExpr) -> WeldResult<SirFunction> {
    if let Lambda(ref params, ref body) = expr.kind {
        let mut func = SirFunction::new();
        func.sym_gen = SymbolGenerator::from_expression(expr);
        if func.sym_gen.next_id("tmp") == 0 {
            func.sym_gen.new_symbol("tmp");
        }
        func.params = params.clone();
        let first_block = func.add_block();
        let (res_block, res_sym) = gen_expr(body, &mut func, first_block)?;
        func.blocks[res_block].terminator = Terminator::Return(res_sym);
        Ok((func))
    } else {
        weld_err!("Expression passed to ast_to_sir was not a Lambda")
    }
}

/// Generate code to compute the expression `expr` starting at the current tail of `cur_block`,
/// possibly creating new basic blocks in the process. Return the basic block that the expression
/// will be ready in, and its symbol therein.
fn gen_expr(
    expr: &TypedExpr,
    func: &mut SirFunction,
    cur_block: BasicBlockId
) -> WeldResult<(BasicBlockId, Symbol)> {
    use self::Statement::*;
    use self::Terminator::*;
    match expr.kind {
        Ident(ref sym) => Ok((cur_block, sym.clone())),

        Literal(lit) => {
            let res_sym = func.add_local(&expr.ty);
            func.blocks[cur_block].add_statement(AssignLiteral(res_sym.clone(), lit));
            Ok((cur_block, res_sym))
        },

        Let(ref sym, ref value, ref body) => {
            let (cur_block, val_sym) = gen_expr(value, func, cur_block)?;
            func.add_local_named(&value.ty, sym);
            func.blocks[cur_block].add_statement(Assign(sym.clone(), val_sym));
            let (cur_block, res_sym) = gen_expr(body, func, cur_block)?;
            Ok((cur_block, res_sym))
        },

        BinOp(kind, ref left, ref right) => {
            let (cur_block, left_sym) = gen_expr(left, func, cur_block)?;
            let (cur_block, right_sym) = gen_expr(right, func, cur_block)?;
            let res_sym = func.add_local(&expr.ty);
            func.blocks[cur_block].add_statement(
                AssignBinOp(res_sym.clone(), kind, left.ty.clone(), left_sym, right_sym));
            Ok((cur_block, res_sym))
        },

        If(ref cond, ref on_true, ref on_false) => {
            let (cur_block, cond_sym) = gen_expr(cond, func, cur_block)?;
            let true_block = func.add_block();
            let false_block = func.add_block();
            func.blocks[cur_block].terminator = Branch(cond_sym, true_block, false_block);
            let (true_block, true_sym) = gen_expr(on_true, func, true_block)?;
            let (false_block, false_sym) = gen_expr(on_false, func, false_block)?;
            let res_sym = func.add_local(&expr.ty);
            let res_block = func.add_block();
            func.blocks[true_block].add_statement(Assign(res_sym.clone(), true_sym));
            func.blocks[true_block].terminator = Jump(res_block);
            func.blocks[false_block].add_statement(Assign(res_sym.clone(), false_sym));
            func.blocks[false_block].terminator = Jump(res_block);
            Ok((res_block, res_sym))
        },

        Merge(ref builder, ref elem) => {
            let (cur_block, builder_sym) = gen_expr(builder, func, cur_block)?;
            let (cur_block, elem_sym) = gen_expr(elem, func, cur_block)?;
            let res_sym = func.add_local(&expr.ty);
            func.blocks[cur_block].add_statement(DoMerge(res_sym.clone(), builder_sym, elem_sym));
            Ok((cur_block, res_sym))
        },

        Res(ref builder) => {
            let (cur_block, builder_sym) = gen_expr(builder, func, cur_block)?;
            let res_sym = func.add_local(&expr.ty);
            func.blocks[cur_block].add_statement(GetResult(res_sym.clone(), builder_sym));
            Ok((cur_block, res_sym))
        },

        NewBuilder => {
            let res_sym = func.add_local(&expr.ty);
            func.blocks[cur_block].add_statement(CreateBuilder(res_sym.clone(), expr.ty.clone()));
            Ok((cur_block, res_sym))
        },

        For(ref data, ref builder, ref update) => {
            if let Lambda(ref params, ref body) = update.kind {
                let (cur_block, data_sym) = gen_expr(data, func, cur_block)?;
                let (cur_block, builder_sym) = gen_expr(builder, func, cur_block)?;
                let body_block = func.add_block();
                let (body_end_block, body_res) = gen_expr(body, func, body_block)?;
                func.blocks[body_end_block].terminator = Return(body_res);
                let exit_block = func.add_block();
                let res_sym = func.add_local(&expr.ty);
                func.blocks[cur_block].terminator = ParallelFor {
                    data: data_sym,
                    builder: builder_sym,
                    body: body_block,
                    data_arg: params[0].name.clone(),
                    builder_arg: params[1].name.clone(),
                    exit: exit_block,
                    result: res_sym.clone()
                };
                Ok((exit_block, res_sym))
            } else {
                weld_err!("Argument to For was not a Lambda: {}", print_expr(update))
            }
        },

        _ => weld_err!("Unsupported expression: {}", print_expr(expr))
    }
}
<|endoftext|>


<commit_before>use super::Rope;
use std::iter;

#[test]
fn rebalance_test_1() {
    let r = Rope::from("This is a large string \
                        that will need to be rebalanced.".to_string());
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn rebalance_test_2() {
    let r = Rope::from("Lorem ipsum dolor sit amet, consectetur adipiscing eli\
                        t, sed do eiusmod tempor incididunt ut labore et dolor\
                        e magna aliqua. Ut enim ad minim veniam, quis nostrud \
                        exercitation ullamco laboris nisi ut aliquip ex ea com\
                        modo consequat. Duis aute irure dolor in reprehenderit\
                         in voluptate velit esse cillum dolore eu fugiat nulla\
                         pariatur. Excepteur sint occaecat cupidatat non proid\
                        ent, sunt in culpa qui officia deserunt mollit anim id\
                         est laborum.".to_string());
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn big_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let r = Rope::from(s);
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_left_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        r = r + iter::repeat('a').take(100).collect::<String>();
    }
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_right_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        let s2 = iter::repeat('a').take(100).collect::<String>();
        r = Rope::from(s2) + r;
    }
    assert!(r.is_balanced());
}

#[test]
fn merge_rebalance_test() {
    let s = "Lorem ipsum dolor sit amet, consectetur adipiscing eli\
             t, sed do eiusmod tempor incididunt ut labore et dolor\
             e magna aliqua. Ut enim ad minim veniam, quis nostrud \
             exercitation ullamco laboris nisi ut aliquip ex ea com\
             modo consequat. Duis aute irure dolor in reprehenderit\
              in voluptate velit esse cillum dolore eu fugiat nulla\
              pariatur. Excepteur sint occaecat cupidatat non proid\
             ent, sunt in culpa qui officia deserunt mollit anim id\
              est laborum.";

     let t = Rope::from(s.to_owned());
     let u = t + s;
     let u = u.rebalance();
     assert!(u.is_balanced());
}

#[test]
fn insert_balance_test() {
    let s: String = iter::repeat('a').take(10).collect();
    let r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1.insert(&Rope::from(t), 5);
    }

    r_1.rebalance();
    assert!(r_1.is_balanced());
}

#[test]
fn insert_test_1() {
    let s: String = iter::repeat('a').take(1_000).collect();
    let r_1 = Rope::from(s);
    let r_2 = Rope::new();
    for _ in 0..100 {
        let t: String = iter::repeat('a').take(10).collect();
        r_2.insert(&Rope::from(t), 0);
    }
    assert_eq!(r_1, r_2);
}

#[test]
fn insert_test_2() {
    let s: String = iter::repeat('a').take(10).collect();
    let r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1.insert(&Rope::from(t), 5);
    }

    let q: String = iter::repeat('a').take(1_000).collect();
    let r_2 = Rope::from(q);
    assert_eq!(r_1, r_2);
}

#[test]
fn insert_test_3() {
    let s_1 = Rope::from(String::from("aaaaa"));
    let s_2 = Rope::from(String::from("bbbbb"));
    let s_3 = Rope::from(String::from("ccccc"));
    s_2.insert(&s_3, 0);
    s_1.insert(&s_2, 0);
    assert_eq!(s_1, "cccccbbbbbaaaaa");
}
<commit_msg>rope: fix comparison between rope and &str in test<commit_after>use super::Rope;
use std::iter;

#[test]
fn rebalance_test_1() {
    let r = Rope::from("This is a large string \
                        that will need to be rebalanced.".to_string());
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn rebalance_test_2() {
    let r = Rope::from("Lorem ipsum dolor sit amet, consectetur adipiscing eli\
                        t, sed do eiusmod tempor incididunt ut labore et dolor\
                        e magna aliqua. Ut enim ad minim veniam, quis nostrud \
                        exercitation ullamco laboris nisi ut aliquip ex ea com\
                        modo consequat. Duis aute irure dolor in reprehenderit\
                         in voluptate velit esse cillum dolore eu fugiat nulla\
                         pariatur. Excepteur sint occaecat cupidatat non proid\
                        ent, sunt in culpa qui officia deserunt mollit anim id\
                         est laborum.".to_string());
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn big_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let r = Rope::from(s);
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_left_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        r = r + iter::repeat('a').take(100).collect::<String>();
    }
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_right_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        let s2 = iter::repeat('a').take(100).collect::<String>();
        r = Rope::from(s2) + r;
    }
    assert!(r.is_balanced());
}

#[test]
fn merge_rebalance_test() {
    let s = "Lorem ipsum dolor sit amet, consectetur adipiscing eli\
             t, sed do eiusmod tempor incididunt ut labore et dolor\
             e magna aliqua. Ut enim ad minim veniam, quis nostrud \
             exercitation ullamco laboris nisi ut aliquip ex ea com\
             modo consequat. Duis aute irure dolor in reprehenderit\
              in voluptate velit esse cillum dolore eu fugiat nulla\
              pariatur. Excepteur sint occaecat cupidatat non proid\
             ent, sunt in culpa qui officia deserunt mollit anim id\
              est laborum.";

     let t = Rope::from(s.to_owned());
     let u = t + s;
     let u = u.rebalance();
     assert!(u.is_balanced());
}

#[test]
fn insert_balance_test() {
    let s: String = iter::repeat('a').take(10).collect();
    let r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1.insert(&Rope::from(t), 5);
    }

    r_1.rebalance();
    assert!(r_1.is_balanced());
}

#[test]
fn insert_test_1() {
    let s: String = iter::repeat('a').take(1_000).collect();
    let r_1 = Rope::from(s);
    let r_2 = Rope::new();
    for _ in 0..100 {
        let t: String = iter::repeat('a').take(10).collect();
        r_2.insert(&Rope::from(t), 0);
    }
    assert_eq!(r_1, r_2);
}

#[test]
fn insert_test_2() {
    let s: String = iter::repeat('a').take(10).collect();
    let r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1.insert(&Rope::from(t), 5);
    }

    let q: String = iter::repeat('a').take(1_000).collect();
    let r_2 = Rope::from(q);
    assert_eq!(r_1, r_2);
}

#[test]
fn insert_test_3() {
    let s_1 = Rope::from(String::from("aaaaa"));
    let s_2 = Rope::from(String::from("bbbbb"));
    let s_3 = Rope::from(String::from("ccccc"));
    s_2.insert(&s_3, 0);
    s_1.insert(&s_2, 0);
    assert_eq!(&s_1, "cccccbbbbbaaaaa");
}
<|endoftext|>


<commit_before>use std::num::atan2;
use glfw;
use nalgebra::na::{Vec2, Vec3, Mat4, Iso3};
use nalgebra::na;
use camera::Camera;
use event;

/// First-person camera mode.
///
///   * Left button press + drag - look around
///   * Right button press + drag - translates the camera position on the plane orthogonal to the
///   view direction
///   * Scroll in/out - zoom in/out
#[deriving(ToStr)]
pub struct FirstPerson {
    /// The camera position
    priv eye:        Vec3<f32>,
    /// Yaw of the camera (rotation along the y axis).
    priv yaw:        f32,
    /// Pitch of the camera (rotation along the x axis).
    priv pitch:      f32,

    /// Increment of the yaw per unit mouse movement. The default value is 0.005.
    priv yaw_step:   f32,
    /// Increment of the pitch per unit mouse movement. The default value is 0.005.
    priv pitch_step: f32,
    /// Increment of the translation per arrow press. The default value is 0.1.
    priv move_step:  f32,

    /// Low level datas
    priv fov:        f32,
    priv znear:      f32,
    priv zfar:       f32,
    priv projection:      Mat4<f32>,
    priv proj_view:       Mat4<f32>,
    priv inv_proj_view:   Mat4<f32>,
    priv last_cursor_pos: Vec2<f32>
}

impl FirstPerson {
    /// Creates a first person camera with default sensitivity values.
    pub fn new(eye: Vec3<f32>, at: Vec3<f32>) -> FirstPerson {
        FirstPerson::new_with_frustrum(45.0f32.to_radians(), 0.1, 1024.0, eye, at)
    }

    /// Creates a new first person camera with default sensitivity values.
    pub fn new_with_frustrum(fov:    f32,
                             znear:  f32,
                             zfar:   f32,
                             eye:    Vec3<f32>,
                             at:     Vec3<f32>) -> FirstPerson {
        let mut res = FirstPerson {
            eye:           Vec3::new(0.0, 0.0, 0.0),
            yaw:           0.0,
            pitch:         0.0,
            yaw_step:      0.005,
            pitch_step:    0.005,
            move_step:     0.5,
            fov:        fov,
            znear:      znear,
            zfar:       zfar,
            projection: na::perspective3d(800.0, 600.0, fov, znear, zfar),
            proj_view:  na::zero(),
            inv_proj_view:   na::zero(),
            last_cursor_pos: na::zero()
        };

        res.look_at_z(eye, at);

        res
    }

    /// Changes the orientation and position of the camera to look at the specified point.
    pub fn look_at_z(&mut self, eye: Vec3<f32>, at: Vec3<f32>) {
        let dist  = na::norm(&(eye - at));

        let pitch = ((at.y - eye.y) / dist).acos();
        let yaw   = atan2(at.z - eye.z, at.x - eye.x);

        self.eye   = eye;
        self.yaw   = yaw;
        self.pitch = pitch;
        self.update_projviews();
    }

    /// The point the camera is looking at.
    pub fn at(&self) -> Vec3<f32> {
        let ax = self.eye.x + self.yaw.cos() * self.pitch.sin();
        let ay = self.eye.y + self.pitch.cos();
        let az = self.eye.z + self.yaw.sin() * self.pitch.sin();

        Vec3::new(ax, ay, az)
    }

    fn update_restrictions(&mut self) {
        if self.pitch <= 0.0001 {
            self.pitch = 0.0001
        }

        let _pi: f32 = Real::pi();
        if self.pitch > _pi - 0.0001 {
            self.pitch = _pi - 0.0001
        }
    }

    #[doc(hidden)]
    pub fn handle_left_button_displacement(&mut self, dpos: &Vec2<f32>) {
        self.yaw   = self.yaw   + dpos.x * self.yaw_step;
        self.pitch = self.pitch + dpos.y * self.pitch_step;

        self.update_restrictions();
        self.update_projviews();
    }

    #[doc(hidden)]
    pub fn handle_right_button_displacement(&mut self, dpos: &Vec2<f32>) {
        let at        = self.at();
        let dir       = na::normalize(&(at - self.eye));
        let tangent   = na::normalize(&na::cross(&Vec3::y(), &dir));
        let bitangent = na::cross(&dir, &tangent);

        self.eye = self.eye + tangent * (0.01 * dpos.x / 10.0) + bitangent * (0.01 * dpos.y / 10.0);
        self.update_restrictions();
        self.update_projviews();
    }

    #[doc(hidden)]
    pub fn handle_scroll(&mut self, yoff: f32) {
        let front: Vec3<f32> = na::rotate(&self.view_transform(), &Vec3::z());

        self.eye = self.eye + front * (self.move_step * yoff);

        self.update_restrictions();
        self.update_projviews();
    }

    fn update_projviews(&mut self) {
        self.proj_view     = self.projection * na::to_homogeneous(&na::inv(&self.view_transform()).unwrap());
        self.inv_proj_view = na::inv(&self.proj_view).unwrap();
    }
}

impl Camera for FirstPerson {
    fn clip_planes(&self) -> (f32, f32) {
        (self.znear, self.zfar)
    }

    /// The camera view transformation (i-e transformation without projection).
    fn view_transform(&self) -> Iso3<f32> {
        let mut id: Iso3<f32> = na::one();
        id.look_at_z(&self.eye, &self.at(), &Vec3::y());

        id
    }

    fn handle_event(&mut self, window: &glfw::Window, event: &event::Event) {
        match *event {
            event::CursorPos(x, y) => {
                let curr_pos = Vec2::new(x, y);

                if window.get_mouse_button(glfw::MouseButtonLeft) == glfw::Press {
                    let dpos = curr_pos - self.last_cursor_pos;
                    self.handle_left_button_displacement(&dpos)
                }

                if window.get_mouse_button(glfw::MouseButtonRight) == glfw::Press {
                    let dpos = curr_pos - self.last_cursor_pos;
                    self.handle_right_button_displacement(&dpos)
                }

                self.last_cursor_pos = curr_pos;
            },
            event::Scroll(_, off) => self.handle_scroll(off),
            event::FramebufferSize(w, h) => {
                self.projection = na::perspective3d(w, h, self.fov, self.znear, self.zfar);
                self.update_projviews();
            }
            _ => { }
        }
    }

    fn eye(&self) -> Vec3<f32> {
        self.eye
    }

    fn transformation(&self) -> Mat4<f32> {
        self.proj_view
    }

    fn inv_transformation(&self) -> Mat4<f32> {
        self.inv_proj_view
    }

    fn update(&mut self, window: &glfw::Window) {
        let t                = self.view_transform();
        let front: Vec3<f32> = na::rotate(&t, &Vec3::z());
        let right: Vec3<f32> = na::rotate(&t, &Vec3::x());

        if window.get_key(glfw::KeyUp) == glfw::Press {
            self.eye = self.eye + front * self.move_step
        }

        if window.get_key(glfw::KeyDown) == glfw::Press {
            self.eye = self.eye + front * (-self.move_step)
        }

        if window.get_key(glfw::KeyRight) == glfw::Press {
            self.eye = self.eye + right * (-self.move_step)
        }

        if window.get_key(glfw::KeyLeft) == glfw::Press {
            self.eye = self.eye + right * self.move_step
        }

        self.update_restrictions();
        self.update_projviews();
    }
}
<commit_msg>Add methods to the `FirstPerson` camera in order to read/modify the displacement increments.<commit_after>use std::num::atan2;
use glfw;
use nalgebra::na::{Vec2, Vec3, Mat4, Iso3};
use nalgebra::na;
use camera::Camera;
use event;

/// First-person camera mode.
///
///   * Left button press + drag - look around
///   * Right button press + drag - translates the camera position on the plane orthogonal to the
///   view direction
///   * Scroll in/out - zoom in/out
#[deriving(ToStr)]
pub struct FirstPerson {
    /// The camera position
    priv eye:        Vec3<f32>,
    /// Yaw of the camera (rotation along the y axis).
    priv yaw:        f32,
    /// Pitch of the camera (rotation along the x axis).
    priv pitch:      f32,

    /// Increment of the yaw per unit mouse movement. The default value is 0.005.
    priv yaw_step:   f32,
    /// Increment of the pitch per unit mouse movement. The default value is 0.005.
    priv pitch_step: f32,
    /// Increment of the translation per arrow press. The default value is 0.1.
    priv move_step:  f32,

    /// Low level datas
    priv fov:        f32,
    priv znear:      f32,
    priv zfar:       f32,
    priv projection:      Mat4<f32>,
    priv proj_view:       Mat4<f32>,
    priv inv_proj_view:   Mat4<f32>,
    priv last_cursor_pos: Vec2<f32>
}

impl FirstPerson {
    /// Creates a first person camera with default sensitivity values.
    pub fn new(eye: Vec3<f32>, at: Vec3<f32>) -> FirstPerson {
        FirstPerson::new_with_frustrum(45.0f32.to_radians(), 0.1, 1024.0, eye, at)
    }

    /// Creates a new first person camera with default sensitivity values.
    pub fn new_with_frustrum(fov:    f32,
                             znear:  f32,
                             zfar:   f32,
                             eye:    Vec3<f32>,
                             at:     Vec3<f32>) -> FirstPerson {
        let mut res = FirstPerson {
            eye:           Vec3::new(0.0, 0.0, 0.0),
            yaw:           0.0,
            pitch:         0.0,
            yaw_step:      0.005,
            pitch_step:    0.005,
            move_step:     0.5,
            fov:        fov,
            znear:      znear,
            zfar:       zfar,
            projection: na::perspective3d(800.0, 600.0, fov, znear, zfar),
            proj_view:  na::zero(),
            inv_proj_view:   na::zero(),
            last_cursor_pos: na::zero()
        };

        res.look_at_z(eye, at);

        res
    }

    /// Sets the translational increment per arrow press.
    ///
    /// The default value is 0.5.
    #[inline]
    pub fn set_move_step(&mut self, step: f32) {
        self.move_step = step;
    }

    /// Sets the pitch increment per mouse movement.
    ///
    /// The default value is 0.005.
    #[inline]
    pub fn set_pitch_step(&mut self, step: f32) {
        self.pitch_step = step;
    }

    /// Sets the yaw increment per mouse movement.
    ///
    /// The default value is 0.005.
    #[inline]
    pub fn set_yaw_step(&mut self, step: f32) {
        self.yaw_step = step;
    }

    /// Gets the translational increment per arrow press.
    #[inline]
    pub fn move_step(&self) -> f32 {
        self.move_step
    }

    /// Gets the pitch increment per mouse movement.
    #[inline]
    pub fn pitch_step(&self) -> f32 {
        self.pitch_step
    }

    /// Gets the yaw  increment per mouse movement.
    #[inline]
    pub fn yaw_step(&self) -> f32 {
        self.yaw_step
    }

    /// Changes the orientation and position of the camera to look at the specified point.
    pub fn look_at_z(&mut self, eye: Vec3<f32>, at: Vec3<f32>) {
        let dist  = na::norm(&(eye - at));

        let pitch = ((at.y - eye.y) / dist).acos();
        let yaw   = atan2(at.z - eye.z, at.x - eye.x);

        self.eye   = eye;
        self.yaw   = yaw;
        self.pitch = pitch;
        self.update_projviews();
    }

    /// The point the camera is looking at.
    pub fn at(&self) -> Vec3<f32> {
        let ax = self.eye.x + self.yaw.cos() * self.pitch.sin();
        let ay = self.eye.y + self.pitch.cos();
        let az = self.eye.z + self.yaw.sin() * self.pitch.sin();

        Vec3::new(ax, ay, az)
    }

    fn update_restrictions(&mut self) {
        if self.pitch <= 0.0001 {
            self.pitch = 0.0001
        }

        let _pi: f32 = Real::pi();
        if self.pitch > _pi - 0.0001 {
            self.pitch = _pi - 0.0001
        }
    }

    #[doc(hidden)]
    pub fn handle_left_button_displacement(&mut self, dpos: &Vec2<f32>) {
        self.yaw   = self.yaw   + dpos.x * self.yaw_step;
        self.pitch = self.pitch + dpos.y * self.pitch_step;

        self.update_restrictions();
        self.update_projviews();
    }

    #[doc(hidden)]
    pub fn handle_right_button_displacement(&mut self, dpos: &Vec2<f32>) {
        let at        = self.at();
        let dir       = na::normalize(&(at - self.eye));
        let tangent   = na::normalize(&na::cross(&Vec3::y(), &dir));
        let bitangent = na::cross(&dir, &tangent);

        self.eye = self.eye + tangent * (0.01 * dpos.x / 10.0) + bitangent * (0.01 * dpos.y / 10.0);
        self.update_restrictions();
        self.update_projviews();
    }

    #[doc(hidden)]
    pub fn handle_scroll(&mut self, yoff: f32) {
        let front: Vec3<f32> = na::rotate(&self.view_transform(), &Vec3::z());

        self.eye = self.eye + front * (self.move_step * yoff);

        self.update_restrictions();
        self.update_projviews();
    }

    fn update_projviews(&mut self) {
        self.proj_view     = self.projection * na::to_homogeneous(&na::inv(&self.view_transform()).unwrap());
        self.inv_proj_view = na::inv(&self.proj_view).unwrap();
    }
}

impl Camera for FirstPerson {
    fn clip_planes(&self) -> (f32, f32) {
        (self.znear, self.zfar)
    }

    /// The camera view transformation (i-e transformation without projection).
    fn view_transform(&self) -> Iso3<f32> {
        let mut id: Iso3<f32> = na::one();
        id.look_at_z(&self.eye, &self.at(), &Vec3::y());

        id
    }

    fn handle_event(&mut self, window: &glfw::Window, event: &event::Event) {
        match *event {
            event::CursorPos(x, y) => {
                let curr_pos = Vec2::new(x, y);

                if window.get_mouse_button(glfw::MouseButtonLeft) == glfw::Press {
                    let dpos = curr_pos - self.last_cursor_pos;
                    self.handle_left_button_displacement(&dpos)
                }

                if window.get_mouse_button(glfw::MouseButtonRight) == glfw::Press {
                    let dpos = curr_pos - self.last_cursor_pos;
                    self.handle_right_button_displacement(&dpos)
                }

                self.last_cursor_pos = curr_pos;
            },
            event::Scroll(_, off) => self.handle_scroll(off),
            event::FramebufferSize(w, h) => {
                self.projection = na::perspective3d(w, h, self.fov, self.znear, self.zfar);
                self.update_projviews();
            }
            _ => { }
        }
    }

    fn eye(&self) -> Vec3<f32> {
        self.eye
    }

    fn transformation(&self) -> Mat4<f32> {
        self.proj_view
    }

    fn inv_transformation(&self) -> Mat4<f32> {
        self.inv_proj_view
    }

    fn update(&mut self, window: &glfw::Window) {
        let t                = self.view_transform();
        let front: Vec3<f32> = na::rotate(&t, &Vec3::z());
        let right: Vec3<f32> = na::rotate(&t, &Vec3::x());

        if window.get_key(glfw::KeyUp) == glfw::Press {
            self.eye = self.eye + front * self.move_step
        }

        if window.get_key(glfw::KeyDown) == glfw::Press {
            self.eye = self.eye + front * (-self.move_step)
        }

        if window.get_key(glfw::KeyRight) == glfw::Press {
            self.eye = self.eye + right * (-self.move_step)
        }

        if window.get_key(glfw::KeyLeft) == glfw::Press {
            self.eye = self.eye + right * self.move_step
        }

        self.update_restrictions();
        self.update_projviews();
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::process::Command;

use core::PackageIdSpec;
use core::source::Source;
use ops;
use sources::PathSource;
use util::{CargoResult, human};

pub struct DocOptions<'a, 'b: 'a> {
    pub open_result: bool,
    pub compile_opts: ops::CompileOptions<'a, 'b>,
}

pub fn doc(manifest_path: &Path,
           options: &DocOptions) -> CargoResult<()> {
    let mut source = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                               options.compile_opts.config));
    try!(source.update());
    let package = try!(source.root_package());

    let mut lib_names = HashSet::new();
    let mut bin_names = HashSet::new();
    if options.compile_opts.spec.is_none() {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                assert!(lib_names.insert(target.name()));
            } else {
                assert!(bin_names.insert(target.name()));
            }
        }
        for bin in bin_names.iter() {
            if lib_names.contains(bin) {
                return Err(human("Cannot document a package where a library \
                                  and a binary have the same name. Consider \
                                  renaming one or marking the target as \
                                  `doc = false`"))
            }
        }
    }

    try!(ops::compile(manifest_path, &options.compile_opts));

    if options.open_result {
        let name = match options.compile_opts.spec {
            Some(spec) => try!(PackageIdSpec::parse(spec)).name().to_string(),
            None => {
                match lib_names.iter().chain(bin_names.iter()).nth(0) {
                    Some(s) => s.to_string(),
                    None => return Ok(())
                }
            }
        };

        let path = package.absolute_target_dir().join("doc").join(&name)
                                                    .join("index.html");
        if fs::metadata(&path).is_ok() {
            open_docs(&path);
        }
    }

    Ok(())
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn open_docs(path: &Path) {
    // trying xdg-open
    match Command::new("xdg-open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };

    // trying gnome-open
    match Command::new("gnome-open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };

    // trying kde-open
    match Command::new("kde-open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };
}

#[cfg(target_os = "windows")]
fn open_docs(path: &Path) {
    match Command::new("start").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };
}

#[cfg(target_os = "macos")]
fn open_docs(path: &Path) {
    match Command::new("open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };
}
<commit_msg>Open file correctly in Windows<commit_after>use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::process::Command;

use core::PackageIdSpec;
use core::source::Source;
use ops;
use sources::PathSource;
use util::{CargoResult, human};

pub struct DocOptions<'a, 'b: 'a> {
    pub open_result: bool,
    pub compile_opts: ops::CompileOptions<'a, 'b>,
}

pub fn doc(manifest_path: &Path,
           options: &DocOptions) -> CargoResult<()> {
    let mut source = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                               options.compile_opts.config));
    try!(source.update());
    let package = try!(source.root_package());

    let mut lib_names = HashSet::new();
    let mut bin_names = HashSet::new();
    if options.compile_opts.spec.is_none() {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                assert!(lib_names.insert(target.name()));
            } else {
                assert!(bin_names.insert(target.name()));
            }
        }
        for bin in bin_names.iter() {
            if lib_names.contains(bin) {
                return Err(human("Cannot document a package where a library \
                                  and a binary have the same name. Consider \
                                  renaming one or marking the target as \
                                  `doc = false`"))
            }
        }
    }

    try!(ops::compile(manifest_path, &options.compile_opts));

    if options.open_result {
        let name = match options.compile_opts.spec {
            Some(spec) => try!(PackageIdSpec::parse(spec)).name().to_string(),
            None => {
                match lib_names.iter().chain(bin_names.iter()).nth(0) {
                    Some(s) => s.to_string(),
                    None => return Ok(())
                }
            }
        };

        let path = package.absolute_target_dir().join("doc").join(&name)
                                                    .join("index.html");
        if fs::metadata(&path).is_ok() {
            open_docs(&path);
        }
    }

    Ok(())
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn open_docs(path: &Path) {
    // trying xdg-open
    match Command::new("xdg-open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };

    // trying gnome-open
    match Command::new("gnome-open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };

    // trying kde-open
    match Command::new("kde-open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };
}

#[cfg(target_os = "windows")]
fn open_docs(path: &Path) {
    match Command::new("cmd").arg("/C").arg("start").arg("").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };
}

#[cfg(target_os = "macos")]
fn open_docs(path: &Path) {
    match Command::new("open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;

#[derive(Debug, Clone)]
pub struct Fifo<T> {
    inner: VecDeque<T>,
    max_len: usize,
}

impl<T> Fifo<T> {
    pub fn new(max_len: usize) -> Self {
        let mut inner = VecDeque::new();
        inner.reserve_exact(max_len);
        Self {
            inner,
            max_len
        }
    }

    pub fn push(&mut self, item: T) -> bool {
        if self.len() > self.max_len {
            return false
        }

        self.inner.push_back(item);
        true
    }

    pub fn pop(&mut self) -> Option<T> {
        self.inner.pop_front()
    }

    pub fn len(&self) -> usize {
        self.inner.len()
    }

    pub fn clear(&mut self) {
        self.inner.clear();
    }

    pub fn drain(&mut self, dst: &mut [T]) -> usize {
        let drain_amount = dst.len().min(self.len());
        for i in 0..drain_amount {
            dst[i] = self.pop().unwrap();
        }
        drain_amount
    }

    pub fn full(&self) -> bool {
        self.len() == self.max_len
    }

    pub fn empty(&self) -> bool {
        self.len() == 0
    }
}

impl<T: Clone> Fifo<T> {
    pub fn clone_extend(&mut self, items: &[T]) -> usize {
        let space_left = self.max_len - self.len();
        let copy_amount = items.len().min(space_left);

        self.inner.extend(items[..copy_amount].iter().cloned());

        copy_amount
    }
}
<commit_msg>fifo: Fix bounds check, add tests<commit_after>use std::collections::VecDeque;

#[derive(Debug, Clone)]
pub struct Fifo<T> {
    inner: VecDeque<T>,
    max_len: usize,
}

impl<T> Fifo<T> {
    pub fn new(max_len: usize) -> Self {
        let mut inner = VecDeque::new();
        inner.reserve_exact(max_len);
        Self {
            inner,
            max_len
        }
    }

    pub fn push(&mut self, item: T) -> bool {
        if self.len() >= self.max_len {
            return false
        }

        self.inner.push_back(item);
        true
    }

    pub fn pop(&mut self) -> Option<T> {
        self.inner.pop_front()
    }

    pub fn len(&self) -> usize {
        self.inner.len()
    }

    pub fn clear(&mut self) {
        self.inner.clear();
    }

    pub fn drain(&mut self, dst: &mut [T]) -> usize {
        let drain_amount = dst.len().min(self.len());
        for i in 0..drain_amount {
            dst[i] = self.pop().unwrap();
        }
        drain_amount
    }

    pub fn full(&self) -> bool {
        self.len() == self.max_len
    }

    pub fn empty(&self) -> bool {
        self.len() == 0
    }
}

impl<T: Clone> Fifo<T> {
    pub fn clone_extend(&mut self, items: &[T]) -> usize {
        let space_left = self.max_len - self.len();
        let copy_amount = items.len().min(space_left);

        self.inner.extend(items[..copy_amount].iter().cloned());

        copy_amount
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn fifo_bounds() {
        let mut fifo = Fifo::<u32>::new(16);
        assert!(fifo.empty());
        assert!(fifo.pop().is_none());

        for i in 0..16 {
            let ok = fifo.push(i);
            assert!(ok);
        }
        assert!(fifo.full());
        assert!(!fifo.push(16));

        for i in 0..16 {
            let out = fifo.pop().unwrap();
            assert_eq!(out, i);
        }

        assert!(fifo.empty());
        assert!(fifo.pop().is_none());
    }

    #[test]
    fn fifo_drain() {
        let mut fifo = Fifo::<u32>::new(16);
        for i in 0..16 {
            fifo.push(i);
        }

        let mut drain_buf = [0; 16];
        let amount = fifo.drain(&mut drain_buf[..4]);
        assert_eq!(amount, 4);
        assert_eq!(&drain_buf[..4], &(0..4).collect::<Vec<_>>()[..]);
        assert_eq!(fifo.len(), 12);

        let amount = fifo.drain(&mut drain_buf);
        assert_eq!(amount, 12);
        assert_eq!(&drain_buf[..12], &(4..16).collect::<Vec<_>>()[..]);
        assert!(fifo.empty());
    }

    #[test]
    fn fifo_extend() {
        let mut fifo = Fifo::<u32>::new(16);
        let in_buf: Vec<_> = (0..16).collect();

        let amount = fifo.clone_extend(&in_buf[..4]);
        assert_eq!(amount, 4);
        assert_eq!(fifo.len(), 4);

        let amount = fifo.clone_extend(&in_buf);
        assert_eq!(amount, 12);
        assert!(fifo.full());

        let mut drain_buf = [0; 16];
        let amount = fifo.drain(&mut drain_buf[..4]);
        assert_eq!(amount, 4);
        assert_eq!(&drain_buf[..4], &in_buf[..4]);

        let amount = fifo.drain(&mut drain_buf);
        assert_eq!(amount, 12);
        assert_eq!(&drain_buf[..12], &in_buf[..12]);
    }
}<|endoftext|>