<commit_before>//! Workspace information we get from cargo consists of two pieces. The first is
//! the output of `cargo metadata`. The second is the output of running
//! `build.rs` files (`OUT_DIR` env var, extra cfg flags) and compiling proc
//! macro.
//!
//! This module implements this second part. We use "build script" terminology
//! here, but it covers procedural macros as well.

use std::{cell::RefCell, io, path::PathBuf, process::Command};

use cargo_metadata::{camino::Utf8Path, Message};
use la_arena::ArenaMap;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use serde::Deserialize;

use crate::{cfg_flag::CfgFlag, CargoConfig, CargoWorkspace, Package};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
    outputs: ArenaMap<Package, Option<BuildScriptOutput>>,
    error: Option<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput {
    /// List of config flags defined by this package's build script.
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value.
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output.
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros.
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl WorkspaceBuildScripts {
    fn build_command(config: &CargoConfig) -> Command {
        if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
            let mut cmd = Command::new(program);
            cmd.args(args);
            return cmd;
        }

        let mut cmd = Command::new(toolchain::cargo());

        cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &config.target {
            cmd.args(&["--target", target]);
        }

        if config.all_features {
            cmd.arg("--all-features");
        } else {
            if config.no_default_features {
                cmd.arg("--no-default-features");
            }
            if !config.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(config.features.join(" "));
            }
        }

        cmd
    }

    pub(crate) fn run(
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
    ) -> io::Result<WorkspaceBuildScripts> {
        let mut cmd = Self::build_command(config);

        if config.wrap_rustc_in_build_scripts {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.current_dir(workspace.workspace_root());

        let mut res = WorkspaceBuildScripts::default();
        let outputs = &mut res.outputs;
        // NB: Cargo.toml could have been modified between `cargo metadata` and
        // `cargo check`. We shouldn't assume that package ids we see here are
        // exactly those from `config`.
        let mut by_id: FxHashMap<String, Package> = FxHashMap::default();
        for package in workspace.packages() {
            outputs.insert(package, None);
            by_id.insert(workspace[package].id.clone(), package);
        }

        let errors = RefCell::new(String::new());
        let push_err = |err: &str| {
            let mut e = errors.borrow_mut();
            e.push_str(err);
            e.push('\n');
        };

        tracing::info!("Running build scripts: {:?}", cmd);
        let output = stdx::process::streaming_output(
            cmd,
            &mut |line| {
                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using serde_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or_else(|_| Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(&it) => it,
                            None => return,
                        };
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in message.cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        push_err(&format!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        let package_build_data =
                            outputs[package].get_or_insert_with(Default::default);
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        if !message.out_dir.as_str().is_empty() {
                            let out_dir =
                                AbsPathBuf::assert(PathBuf::from(message.out_dir.into_os_string()));
                            package_build_data.out_dir = Some(out_dir);
                            package_build_data.cfgs = cfgs;
                        }

                        package_build_data.envs = message.env;
                    }
                    Message::CompilerArtifact(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(it) => *it,
                            None => return,
                        };

                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.iter().any(|k| k == "proc-macro") {
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                outputs[package]
                                    .get_or_insert_with(Default::default)
                                    .proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name);

                        if let Some(diag) = message.message.rendered.as_deref() {
                            push_err(diag);
                        }
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |line| {
                push_err(line);
            },
        )?;

        for package in workspace.packages() {
            if let Some(package_build_data) = &mut outputs[package] {
                tracing::info!(
                    "{} BuildScriptOutput: {:?}",
                    workspace[package].manifest.parent().display(),
                    package_build_data,
                );
                // inject_cargo_env(package, package_build_data);
                if let Some(out_dir) = &package_build_data.out_dir {
                    // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                    if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
                        package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                    }
                }
            }
        }

        let mut errors = errors.into_inner();
        if !output.status.success() {
            if errors.is_empty() {
                errors = "cargo check failed".to_string();
            }
            res.error = Some(errors);
        }

        Ok(res)
    }

    pub fn error(&self) -> Option<&str> {
        self.error.as_deref()
    }

    pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> {
        self.outputs.get(idx)?.as_ref()
    }
}

// FIXME: File a better way to know if it is a dylib.
fn is_dylib(path: &Utf8Path) -> bool {
    match path.extension().map(|e| e.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<commit_msg>Only record build script outputs when the output has been changed<commit_after>//! Workspace information we get from cargo consists of two pieces. The first is
//! the output of `cargo metadata`. The second is the output of running
//! `build.rs` files (`OUT_DIR` env var, extra cfg flags) and compiling proc
//! macro.
//!
//! This module implements this second part. We use "build script" terminology
//! here, but it covers procedural macros as well.

use std::{cell::RefCell, io, path::PathBuf, process::Command};

use cargo_metadata::{camino::Utf8Path, Message};
use la_arena::ArenaMap;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use serde::Deserialize;

use crate::{cfg_flag::CfgFlag, CargoConfig, CargoWorkspace, Package};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
    outputs: ArenaMap<Package, Option<BuildScriptOutput>>,
    error: Option<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput {
    /// List of config flags defined by this package's build script.
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value.
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output.
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros.
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl WorkspaceBuildScripts {
    fn build_command(config: &CargoConfig) -> Command {
        if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
            let mut cmd = Command::new(program);
            cmd.args(args);
            return cmd;
        }

        let mut cmd = Command::new(toolchain::cargo());

        cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &config.target {
            cmd.args(&["--target", target]);
        }

        if config.all_features {
            cmd.arg("--all-features");
        } else {
            if config.no_default_features {
                cmd.arg("--no-default-features");
            }
            if !config.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(config.features.join(" "));
            }
        }

        cmd
    }

    pub(crate) fn run(
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
    ) -> io::Result<WorkspaceBuildScripts> {
        let mut cmd = Self::build_command(config);

        if config.wrap_rustc_in_build_scripts {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.current_dir(workspace.workspace_root());

        let mut res = WorkspaceBuildScripts::default();
        let outputs = &mut res.outputs;
        // NB: Cargo.toml could have been modified between `cargo metadata` and
        // `cargo check`. We shouldn't assume that package ids we see here are
        // exactly those from `config`.
        let mut by_id: FxHashMap<String, Package> = FxHashMap::default();
        for package in workspace.packages() {
            outputs.insert(package, None);
            by_id.insert(workspace[package].id.clone(), package);
        }

        let errors = RefCell::new(String::new());
        let push_err = |err: &str| {
            let mut e = errors.borrow_mut();
            e.push_str(err);
            e.push('\n');
        };

        tracing::info!("Running build scripts: {:?}", cmd);
        let output = stdx::process::streaming_output(
            cmd,
            &mut |line| {
                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using serde_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or_else(|_| Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(&it) => it,
                            None => return,
                        };
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in message.cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        push_err(&format!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        let out_dir = message.out_dir.into_os_string();
                        if !out_dir.is_empty() {
                            let data = outputs[package].get_or_insert_with(Default::default);
                            let out_dir = Some(AbsPathBuf::assert(PathBuf::from(out_dir)));
                            (data.out_dir, data.cfgs) = (out_dir, cfgs);
                        }
                        if !message.env.is_empty() {
                            outputs[package].get_or_insert_with(Default::default).envs =
                                message.env;
                        }
                    }
                    Message::CompilerArtifact(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(it) => *it,
                            None => return,
                        };

                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.iter().any(|k| k == "proc-macro") {
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                outputs[package]
                                    .get_or_insert_with(Default::default)
                                    .proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name);

                        if let Some(diag) = message.message.rendered.as_deref() {
                            push_err(diag);
                        }
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |line| {
                push_err(line);
            },
        )?;

        for package in workspace.packages() {
            if let Some(package_build_data) = &mut outputs[package] {
                tracing::info!(
                    "{}: {:?}",
                    workspace[package].manifest.parent().display(),
                    package_build_data,
                );
                // inject_cargo_env(package, package_build_data);
                if let Some(out_dir) = &package_build_data.out_dir {
                    // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                    if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
                        package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                    }
                }
            }
        }

        let mut errors = errors.into_inner();
        if !output.status.success() {
            if errors.is_empty() {
                errors = "cargo check failed".to_string();
            }
            res.error = Some(errors);
        }

        Ok(res)
    }

    pub fn error(&self) -> Option<&str> {
        self.error.as_deref()
    }

    pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> {
        self.outputs.get(idx)?.as_ref()
    }
}

// FIXME: File a better way to know if it is a dylib.
fn is_dylib(path: &Utf8Path) -> bool {
    match path.extension().map(|e| e.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<|endoftext|>


<commit_before>use crate::instance::{InstanceHandle, InternalRunResult, RunResult, State, TerminationDetails};
use crate::module::FunctionHandle;
use crate::val::Val;
use crate::vmctx::{Vmctx, VmctxInternal};
use crate::{error::Error, instance::EmptyYieldVal};
use std::pin::Pin;
use std::sync::Arc;
use std::task::Context;
use std::task::Poll;
use std::task::Waker;
use std::{any::Any, future::Future};

/// a representation of AsyncContext which can be freely cloned
#[doc(hidden)]
#[derive(Clone)]
pub struct AsyncContext {
    waker: Waker,
}

const DEFAULT_INST_COUNT_BOUND: u64 = i64::MAX as u64;

/// A value representing that the guest instance yielded because it was blocked on a future.
struct AsyncYielded;

/// Providing the private `AsyncResume` as a resume value certifies that
/// RunAsync upheld the invariants necessary to safely resume the instance.
struct AsyncResume;

/// An error representing a failure of `try_block_on`
#[doc(hidden)]
pub enum BlockOnError {
    NeedsAsyncContext,
}

impl From<BlockOnError> for TerminationDetails {
    fn from(err: BlockOnError) -> Self {
        match err {
            BlockOnError::NeedsAsyncContext => TerminationDetails::BlockOnNeedsAsync,
        }
    }
}

impl Vmctx {
    /// Block on the result of an `async` computation from an instance run by `Instance::run_async`.
    ///
    /// While this method is supported and part of the public API, it's easiest to define the hostcall
    /// function itself as async (the `#[lucet_hostcall]` macro simply calls this function).
    /// If you need to provide a synchronous fallback, use [`Vmctx::try_block_on()`] instead.
    ///
    /// Note:
    /// - This method may only be used if `Instance::run_async` was used to run the VM,
    ///   otherwise it will terminate the instance with `TerminationDetails::BlockOnNeedsAsync`.
    #[doc(hidden)]
    #[inline(always)]
    pub fn block_on<R>(&self, f: impl Future<Output = R>) -> R {
        match self.try_block_on(f) {
            Ok(res) => res,
            Err(err) => panic!(TerminationDetails::from(err)),
        }
    }

    /// Block on the result of an `async` computation from an instance run by `Instance::run_async`.
    ///
    /// The easiest way to make a hostcall async is simply to add the `async` modifier:
    ///
    /// ```ignore
    /// #[lucet_hostcall]
    /// #[no_mangle]
    /// pub async fn hostcall_async(vmctx: &Vmctx) {
    ///    foobar().await
    /// }
    /// ```
    ///
    /// Of course, we can only expose synchronous interfaces to Wasm guests. To implement
    /// async hostcalls, Lucet blocks guest execution while waiting the future to complete,
    /// yielding control of the thread back the async executor so that other tasks can make
    /// progress in the meantime.
    ///
    /// It's recommended to use the async modifier, rather than call this function directly.
    /// The primary reason you may want to do so directly is if you want to provide a fallback
    /// implementation for the case when calling a hostcall from outside of an async context.
    ///
    /// There is no additional yield to the host when the future passed is immediately ready.
    /// This behavior is the same as implemented by the `.await` operator.
    ///
    /// The future passed is polled from within the guest execution context. If the future is not
    /// immediately ready, the instance will yield and return control to the async executor.
    /// Later, once the future is ready to make progress, the async executor will return us to
    /// the guest context, where Lucet will resume polling the future to completion.
    ///
    /// For async hostcalls that may yield to the async executor many times, it's recommended that
    /// you use `tokio::spawn`, or the equivalent from your async executor, which will spawn the task
    /// to be run from the host execution context. This avoids the overhead of context switching into
    /// the guest execution context every time the future needs to make progress.
    ///
    /// If `Instance::run_async` is not being used to run the VM, this function will return
    /// `Err(BlockOnError::NeedsAsyncContext)`.
    pub fn try_block_on<R>(&self, mut f: impl Future<Output = R>) -> Result<R, BlockOnError> {
        // We pin the future to the stack (a requirement for being able to poll the future).
        // By pinning to the stack instead of using `Box::pin`, we avoid heap allocations for
        // immediately-ready futures.
        //
        // SAFETY: We must uphold the invariants of Pin, namely that future does not move until
        // it is dropped. By overriding the variable named `f`, it is impossible to access f again,
        // except through the pinned reference.
        let mut f = unsafe { Pin::new_unchecked(&mut f) };

        loop {
            // Get the AsyncContext, or die if we aren't async
            let arc_cx = match &self.instance().state {
                State::Running {
                    async_context: Some(cx),
                } => cx.clone(),
                State::Running {
                    async_context: None,
                } => return Err(BlockOnError::NeedsAsyncContext),
                _ => unreachable!("Access to vmctx implies instance is Running"),
            };

            // build an std::task::Context
            let mut cx = Context::from_waker(&arc_cx.waker);

            match f.as_mut().poll(&mut cx) {
                Poll::Ready(ret) => return Ok(ret),
                Poll::Pending => {
                    // The future is pending, so we need to yield to the async executor
                    self.yield_impl::<AsyncYielded, AsyncResume>(AsyncYielded, false, false);

                    // Poll is synchronous and may have yielded back to this host, so it is possible that we are
                    // executing from a new RunAsync future (and thus, a new async executor). If the future has awoken
                    // the previous waker, we would miss a wakeup.
                    //
                    // Rather than try to prevent this from happening at all, we check if the waker changed from under us,
                    // and if so, we simply poll the future again. If the future is Ready by that point, there's no need
                    // for another wakeup (we've already done so!). Otherwise, polling the future registers the new waker,
                    // allowing us to yield back to the async executor without risking a missed wakup.
                    //
                    // Note: Waking a waker unnecessarily will not cause unsafety or logic errors. In the worst case, the
                    // async executor may waste CPU time polling pending futures an extra time.

                    match &self.instance().state {
                        State::Running { async_context: Some(new_cx) } => {
                            let same_waker = Arc::ptr_eq(&arc_cx, &new_cx) || arc_cx.waker.will_wake(&new_cx.waker);

                            if !same_waker {
                                // The AsyncContext changed on us.
                                // Poll the future again before yielding to the executor in order to register the new waker.
                                continue;
                            }
                        },
                        _ => panic!("Lucet instance blocked on a future, but no longer running in async context. Make sure to use resume_async when resuming an async guest.")
                    }

                    // Providing the private `AsyncResume` as a resume value certifies that
                    // RunAsync upheld the invariants necessary for us to avoid a borrow check.
                    //
                    // If we resume with any other value, the instance may have been modified, and it is
                    // unsound to resume the instance.
                    let AsyncResume = self.take_resumed_val::<AsyncResume>();
                }
            }
        }
    }
}

impl InstanceHandle {
    /// Run a WebAssembly function with arguments in the guest context at the given entrypoint.
    ///
    /// This method is similar to `Instance::run()`, but allows the Wasm program to invoke async hostcalls.
    ///
    /// To define an async hostcall, simply add an `async` modifier to your hostcall:
    ///
    /// ```ignore
    /// #[lucet_hostcall]
    /// #[no_mangle]
    /// pub async fn hostcall_async(vmctx: &Vmctx) {
    ///    foobar().await
    /// }
    /// ```
    ///
    /// See [`Vmctx::try_block_on()`] for details.
    ///
    /// If `runtime_bound` is provided, it will also pause the Wasm execution and yield a future
    /// that resumes it after (approximately) that many Wasm opcodes have executed.
    pub fn run_async<'a>(&'a mut self, entrypoint: &'a str, args: &'a [Val]) -> RunAsync<'a> {
        let func = self.module.get_export_func(entrypoint);

        match func {
            Ok(func) => self.run_async_internal(func, args),
            Err(err) => self.run_async_failed(err),
        }
    }

    /// Run the module's [start function][start], if one exists.
    ///
    /// If there is no start function in the module, this does nothing.
    ///
    /// All of the other restrictions on the start function, what it may do, and
    /// the requirement that it must be invoked first, are described in the
    /// documentation for `Instance::run_start()`. This async version of that
    /// function satisfies the requirement to run the start function first, as
    /// long as the async function fully returns (not just yields).
    ///
    /// This method is similar to `Instance::run_start()`, except that it bounds
    /// runtime between async future yields (invocations of `.poll()` on the
    /// underlying generated future) if `runtime_bound` is provided. This
    /// behaves the same way as `Instance::run_async()`.
    ///
    /// Just like `Instance::run_start()`, hostcalls, including async hostcalls,
    /// cannot be called from the instance start function.
    ///
    /// The result of the `RunAsync` future is unspecified, and should not be relied on.
    pub fn run_async_start<'a>(&'a mut self) -> RunAsync<'a> {
        if !self.is_not_started() {
            return self.run_async_failed(Error::StartAlreadyRun);
        }

        let func = match self.module.get_start_func() {
            Ok(start) => start.expect("NotStarted, but no start function"), // can only be in NotStarted state if a start function exists,
            Err(err) => return self.run_async_failed(err),
        };

        self.run_async_internal(func, &[])
    }

    /// Resume async execution of an instance that has yielded, providing a value to the guest.
    ///
    /// If an async execution context yields from within a future, resuming with [`Instance::resume()`],
    /// [`Instance::resume_with_val()`], may panic if the instance needs to block on an async function.
    /// Use this function instead, which will resume the instance within an async context.
    ///
    /// The provided value will be dynamically typechecked against the type the guest expects to
    /// receive, and if that check fails, this call will fail with `Error::InvalidArgument`.
    ///
    /// See [`Instance::resume()`], [`Instance::resume_with_val()`], and [`Instance::run_async()`].
    ///
    /// # Safety
    ///
    /// The foreign code safety caveat of [`Instance::run()`](struct.Instance.html#method.run)
    /// applies.
    pub fn resume_async_with_val<'a>(&'a mut self, val: impl Any + 'static + Send) -> RunAsync<'a> {
        let val = Box::new(val) as Box<dyn Any + 'static + Send>;

        RunAsync {
            inst: self,
            inst_count_bound: DEFAULT_INST_COUNT_BOUND,
            state: RunAsyncState::ResumeYielded(val),
        }
    }

    /// Resume execution of an instance that has yielded without providing a value to the guest.
    ///
    /// See [`Instance::resume_async_with_val()`]
    pub fn resume_async<'a>(&'a mut self) -> RunAsync<'a> {
        self.resume_async_with_val(EmptyYieldVal)
    }

    /// Returns a `RunAsync` that will asynchronously execute the guest instnace.
    fn run_async_internal<'a>(&'a mut self, func: FunctionHandle, args: &'a [Val]) -> RunAsync<'a> {
        RunAsync {
            inst: self,
            inst_count_bound: DEFAULT_INST_COUNT_BOUND,
            state: RunAsyncState::Start(func, args),
        }
    }

    /// Returns a `RunAsync` that will immediately fail with the given error, without executing the guest instance.
    fn run_async_failed<'a>(&'a mut self, err: Error) -> RunAsync<'a> {
        RunAsync {
            inst: self,
            inst_count_bound: DEFAULT_INST_COUNT_BOUND,
            state: RunAsyncState::Failed(err),
        }
    }
}

/// A Future implementation that enables asynchronous execution of bounded slices of
/// WebAssembly, and of underlying async hostcalls that it invokes.
///
/// See [`Vmctx::try_block_on()`] for more details about how this works, and how
/// to define an async hostcall.
pub struct RunAsync<'a> {
    inst: &'a mut InstanceHandle,
    state: RunAsyncState<'a>,
    /// The instance count bound. Can be changed at any time, taking effect on the next entry to the guest execution context
    pub inst_count_bound: u64,
}

impl<'a> RunAsync<'a> {
    /// Set the instance count bound
    pub fn bound_inst_count(&mut self, inst_count_bound: u64) -> &mut Self {
        self.inst_count_bound = inst_count_bound;
        self
    }
}

enum RunAsyncState<'a> {
    Start(FunctionHandle, &'a [Val]),
    ResumeYielded(Box<dyn Any + 'static + Send>),
    BoundExpired,
    Failed(Error),
}

impl<'a> Future for RunAsync<'a> {
    type Output = Result<RunResult, Error>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let inst_count_bound = self.inst_count_bound;

        let waker = cx.waker();
        let cx = AsyncContext {
            waker: waker.clone(),
        };

        let state = std::mem::replace(
            &mut self.state,
            RunAsyncState::Failed(Error::InvalidArgument("Polled an invalid future")),
        );
        let run_result = match state {
            RunAsyncState::Start(func, args) => {
                // This is the first iteration, call the entrypoint:
                self.inst
                    .run_func(func, args, Some(cx), Some(inst_count_bound))
            }
            RunAsyncState::ResumeYielded(val) => {
                self.inst
                    .resume_with_val_impl(val, Some(cx), Some(inst_count_bound))
            }
            RunAsyncState::BoundExpired => self.inst.resume_bounded(cx, inst_count_bound),
            RunAsyncState::Failed(err) => Err(err),
        };

        let res = match run_result {
            Ok(InternalRunResult::Normal(r @ RunResult::Returned(_))) => Ok(r),
            Ok(InternalRunResult::Normal(RunResult::Yielded(yval))) => {
                match yval.downcast::<AsyncYielded>() {
                    Ok(_) => {
                        // When this future is polled next, we'll resume the guest instance using `AsyncResume`
                        self.state = RunAsyncState::ResumeYielded(Box::new(AsyncResume));
                        return Poll::Pending;
                    }
                    Err(yval) => Ok(RunResult::Yielded(yval)),
                }
            }
            Ok(InternalRunResult::BoundExpired) => {
                // The instruction count bound expired. Yield to the async exeuctor and immediately wake.
                //
                // By immediately waking, the future will be scheduled to run later (similar to tokio's yield_now())
                self.state = RunAsyncState::BoundExpired;
                waker.wake_by_ref();
                return Poll::Pending;
            }
            Err(err) => Err(err),
        };

        Poll::Ready(res)
    }
}

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

    /// Show that the futures returned by `InstanceHandle` methods are `Send`.
    ///
    /// This doesn't actually do anything at runtime, the fact that it compiles is what counts.
    #[test]
    fn async_futures_are_send() {
        fn _assert_send<T: Send>(_: &T) {}
        #[allow(unreachable_code)]
        fn _dont_run_me() {
            let mut _inst: InstanceHandle = unimplemented!();
            _assert_send(&_inst.run_async("", &[]));
            _assert_send(&_inst.run_async_start());
        }
    }
}
<commit_msg>Improve try_block_on styling<commit_after>use crate::instance::{InstanceHandle, InternalRunResult, RunResult, State, TerminationDetails};
use crate::module::FunctionHandle;
use crate::val::Val;
use crate::vmctx::{Vmctx, VmctxInternal};
use crate::{error::Error, instance::EmptyYieldVal};
use std::pin::Pin;
use std::sync::Arc;
use std::task::Context;
use std::task::Poll;
use std::task::Waker;
use std::{any::Any, future::Future};

/// a representation of AsyncContext which can be freely cloned
#[doc(hidden)]
#[derive(Clone)]
pub struct AsyncContext {
    waker: Waker,
}

const DEFAULT_INST_COUNT_BOUND: u64 = i64::MAX as u64;

/// A value representing that the guest instance yielded because it was blocked on a future.
struct AsyncYielded;

/// Providing the private `AsyncResume` as a resume value certifies that
/// RunAsync upheld the invariants necessary to safely resume the instance.
struct AsyncResume;

/// An error representing a failure of `try_block_on`
#[doc(hidden)]
pub enum BlockOnError {
    NeedsAsyncContext,
}

impl From<BlockOnError> for TerminationDetails {
    fn from(err: BlockOnError) -> Self {
        match err {
            BlockOnError::NeedsAsyncContext => TerminationDetails::BlockOnNeedsAsync,
        }
    }
}

impl Vmctx {
    /// Block on the result of an `async` computation from an instance run by `Instance::run_async`.
    ///
    /// While this method is supported and part of the public API, it's easiest to define the hostcall
    /// function itself as async (the `#[lucet_hostcall]` macro simply calls this function).
    /// If you need to provide a synchronous fallback, use [`Vmctx::try_block_on()`] instead.
    ///
    /// Note:
    /// - This method may only be used if `Instance::run_async` was used to run the VM,
    ///   otherwise it will terminate the instance with `TerminationDetails::BlockOnNeedsAsync`.
    #[doc(hidden)]
    #[inline(always)]
    pub fn block_on<R>(&self, f: impl Future<Output = R>) -> R {
        match self.try_block_on(f) {
            Ok(res) => res,
            Err(err) => panic!(TerminationDetails::from(err)),
        }
    }

    /// Block on the result of an `async` computation from an instance run by `Instance::run_async`.
    ///
    /// The easiest way to make a hostcall async is simply to add the `async` modifier:
    ///
    /// ```ignore
    /// #[lucet_hostcall]
    /// #[no_mangle]
    /// pub async fn hostcall_async(vmctx: &Vmctx) {
    ///    foobar().await
    /// }
    /// ```
    ///
    /// Of course, we can only expose synchronous interfaces to Wasm guests. To implement
    /// async hostcalls, Lucet blocks guest execution while waiting the future to complete,
    /// yielding control of the thread back the async executor so that other tasks can make
    /// progress in the meantime.
    ///
    /// It's recommended to use the async modifier, rather than call this function directly.
    /// The primary reason you may want to do so directly is if you want to provide a fallback
    /// implementation for the case when calling a hostcall from outside of an async context.
    ///
    /// There is no additional yield to the host when the future passed is immediately ready.
    /// This behavior is the same as implemented by the `.await` operator.
    ///
    /// The future passed is polled from within the guest execution context. If the future is not
    /// immediately ready, the instance will yield and return control to the async executor.
    /// Later, once the future is ready to make progress, the async executor will return us to
    /// the guest context, where Lucet will resume polling the future to completion.
    ///
    /// For async hostcalls that may yield to the async executor many times, it's recommended that
    /// you use `tokio::spawn`, or the equivalent from your async executor, which will spawn the task
    /// to be run from the host execution context. This avoids the overhead of context switching into
    /// the guest execution context every time the future needs to make progress.
    ///
    /// If `Instance::run_async` is not being used to run the VM, this function will return
    /// `Err(BlockOnError::NeedsAsyncContext)`.
    pub fn try_block_on<R>(&self, mut f: impl Future<Output = R>) -> Result<R, BlockOnError> {
        // We pin the future to the stack (a requirement for being able to poll the future).
        // By pinning to the stack instead of using `Box::pin`, we avoid heap allocations for
        // immediately-ready futures.
        //
        // SAFETY: We must uphold the invariants of Pin, namely that future does not move until
        // it is dropped. By overriding the variable named `f`, it is impossible to access f again,
        // except through the pinned reference.
        let mut f = unsafe { Pin::new_unchecked(&mut f) };

        loop {
            // Get the AsyncContext, or die if we aren't async
            let arc_cx = match &self.instance().state {
                State::Running {
                    async_context: Some(cx),
                } => cx.clone(),
                State::Running {
                    async_context: None,
                } => return Err(BlockOnError::NeedsAsyncContext),
                _ => unreachable!("Access to vmctx implies instance is Running"),
            };

            // build an std::task::Context
            let mut cx = Context::from_waker(&arc_cx.waker);

            if let Poll::Ready(ret) = f.as_mut().poll(&mut cx) {
                return Ok(ret);
            }

            // The future is pending, so we need to yield to the async executor
            self.yield_impl::<AsyncYielded, AsyncResume>(AsyncYielded, false, false);

            // Poll is synchronous and may have yielded back to this host, so it is possible that we are
            // executing from a new RunAsync future (and thus, a new async executor). If the future has awoken
            // the previous waker, we would miss a wakeup.
            //
            // Rather than try to prevent this from happening at all, we check if the waker changed from under us,
            // and if so, we simply poll the future again. If the future is Ready by that point, there's no need
            // for another wakeup (we've already done so!). Otherwise, polling the future registers the new waker,
            // allowing us to yield back to the async executor without risking a missed wakup.
            //
            // Note: Waking a waker unnecessarily will not cause unsafety or logic errors. In the worst case, the
            // async executor may waste CPU time polling pending futures an extra time.

            match &self.instance().state {
                State::Running { async_context: Some(new_cx) } => {
                    let same_waker = Arc::ptr_eq(&arc_cx, &new_cx) || arc_cx.waker.will_wake(&new_cx.waker);

                    if !same_waker {
                        // The AsyncContext changed on us.
                        // Poll the future again before yielding to the executor in order to register the new waker.
                        continue;
                    }
                },
                _ => panic!("Lucet instance blocked on a future, but no longer running in async context. Make sure to use resume_async when resuming an async guest.")
            }

            // Providing the private `AsyncResume` as a resume value certifies that
            // RunAsync upheld the invariants necessary for us to avoid a borrow check.
            //
            // If we resume with any other value, the instance may have been modified, and it is
            // unsound to resume the instance.
            let AsyncResume = self.take_resumed_val::<AsyncResume>();
        }
    }
}

impl InstanceHandle {
    /// Run a WebAssembly function with arguments in the guest context at the given entrypoint.
    ///
    /// This method is similar to `Instance::run()`, but allows the Wasm program to invoke async hostcalls.
    ///
    /// To define an async hostcall, simply add an `async` modifier to your hostcall:
    ///
    /// ```ignore
    /// #[lucet_hostcall]
    /// #[no_mangle]
    /// pub async fn hostcall_async(vmctx: &Vmctx) {
    ///    foobar().await
    /// }
    /// ```
    ///
    /// See [`Vmctx::try_block_on()`] for details.
    ///
    /// If `runtime_bound` is provided, it will also pause the Wasm execution and yield a future
    /// that resumes it after (approximately) that many Wasm opcodes have executed.
    pub fn run_async<'a>(&'a mut self, entrypoint: &'a str, args: &'a [Val]) -> RunAsync<'a> {
        let func = self.module.get_export_func(entrypoint);

        match func {
            Ok(func) => self.run_async_internal(func, args),
            Err(err) => self.run_async_failed(err),
        }
    }

    /// Run the module's [start function][start], if one exists.
    ///
    /// If there is no start function in the module, this does nothing.
    ///
    /// All of the other restrictions on the start function, what it may do, and
    /// the requirement that it must be invoked first, are described in the
    /// documentation for `Instance::run_start()`. This async version of that
    /// function satisfies the requirement to run the start function first, as
    /// long as the async function fully returns (not just yields).
    ///
    /// This method is similar to `Instance::run_start()`, except that it bounds
    /// runtime between async future yields (invocations of `.poll()` on the
    /// underlying generated future) if `runtime_bound` is provided. This
    /// behaves the same way as `Instance::run_async()`.
    ///
    /// Just like `Instance::run_start()`, hostcalls, including async hostcalls,
    /// cannot be called from the instance start function.
    ///
    /// The result of the `RunAsync` future is unspecified, and should not be relied on.
    pub fn run_async_start<'a>(&'a mut self) -> RunAsync<'a> {
        if !self.is_not_started() {
            return self.run_async_failed(Error::StartAlreadyRun);
        }

        let func = match self.module.get_start_func() {
            Ok(start) => start.expect("NotStarted, but no start function"), // can only be in NotStarted state if a start function exists,
            Err(err) => return self.run_async_failed(err),
        };

        self.run_async_internal(func, &[])
    }

    /// Resume async execution of an instance that has yielded, providing a value to the guest.
    ///
    /// If an async execution context yields from within a future, resuming with [`Instance::resume()`],
    /// [`Instance::resume_with_val()`], may panic if the instance needs to block on an async function.
    /// Use this function instead, which will resume the instance within an async context.
    ///
    /// The provided value will be dynamically typechecked against the type the guest expects to
    /// receive, and if that check fails, this call will fail with `Error::InvalidArgument`.
    ///
    /// See [`Instance::resume()`], [`Instance::resume_with_val()`], and [`Instance::run_async()`].
    ///
    /// # Safety
    ///
    /// The foreign code safety caveat of [`Instance::run()`](struct.Instance.html#method.run)
    /// applies.
    pub fn resume_async_with_val<'a>(&'a mut self, val: impl Any + 'static + Send) -> RunAsync<'a> {
        let val = Box::new(val) as Box<dyn Any + 'static + Send>;

        RunAsync {
            inst: self,
            inst_count_bound: DEFAULT_INST_COUNT_BOUND,
            state: RunAsyncState::ResumeYielded(val),
        }
    }

    /// Resume execution of an instance that has yielded without providing a value to the guest.
    ///
    /// See [`Instance::resume_async_with_val()`]
    pub fn resume_async<'a>(&'a mut self) -> RunAsync<'a> {
        self.resume_async_with_val(EmptyYieldVal)
    }

    /// Returns a `RunAsync` that will asynchronously execute the guest instnace.
    fn run_async_internal<'a>(&'a mut self, func: FunctionHandle, args: &'a [Val]) -> RunAsync<'a> {
        RunAsync {
            inst: self,
            inst_count_bound: DEFAULT_INST_COUNT_BOUND,
            state: RunAsyncState::Start(func, args),
        }
    }

    /// Returns a `RunAsync` that will immediately fail with the given error, without executing the guest instance.
    fn run_async_failed<'a>(&'a mut self, err: Error) -> RunAsync<'a> {
        RunAsync {
            inst: self,
            inst_count_bound: DEFAULT_INST_COUNT_BOUND,
            state: RunAsyncState::Failed(err),
        }
    }
}

/// A Future implementation that enables asynchronous execution of bounded slices of
/// WebAssembly, and of underlying async hostcalls that it invokes.
///
/// See [`Vmctx::try_block_on()`] for more details about how this works, and how
/// to define an async hostcall.
pub struct RunAsync<'a> {
    inst: &'a mut InstanceHandle,
    state: RunAsyncState<'a>,
    /// The instance count bound. Can be changed at any time, taking effect on the next entry to the guest execution context
    pub inst_count_bound: u64,
}

impl<'a> RunAsync<'a> {
    /// Set the instance count bound
    pub fn bound_inst_count(&mut self, inst_count_bound: u64) -> &mut Self {
        self.inst_count_bound = inst_count_bound;
        self
    }
}

enum RunAsyncState<'a> {
    Start(FunctionHandle, &'a [Val]),
    ResumeYielded(Box<dyn Any + 'static + Send>),
    BoundExpired,
    Failed(Error),
}

impl<'a> Future for RunAsync<'a> {
    type Output = Result<RunResult, Error>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let inst_count_bound = self.inst_count_bound;

        let waker = cx.waker();
        let cx = AsyncContext {
            waker: waker.clone(),
        };

        let state = std::mem::replace(
            &mut self.state,
            RunAsyncState::Failed(Error::InvalidArgument("Polled an invalid future")),
        );
        let run_result = match state {
            RunAsyncState::Start(func, args) => {
                // This is the first iteration, call the entrypoint:
                self.inst
                    .run_func(func, args, Some(cx), Some(inst_count_bound))
            }
            RunAsyncState::ResumeYielded(val) => {
                self.inst
                    .resume_with_val_impl(val, Some(cx), Some(inst_count_bound))
            }
            RunAsyncState::BoundExpired => self.inst.resume_bounded(cx, inst_count_bound),
            RunAsyncState::Failed(err) => Err(err),
        };

        let res = match run_result {
            Ok(InternalRunResult::Normal(r @ RunResult::Returned(_))) => Ok(r),
            Ok(InternalRunResult::Normal(RunResult::Yielded(yval))) => {
                match yval.downcast::<AsyncYielded>() {
                    Ok(_) => {
                        // When this future is polled next, we'll resume the guest instance using `AsyncResume`
                        self.state = RunAsyncState::ResumeYielded(Box::new(AsyncResume));
                        return Poll::Pending;
                    }
                    Err(yval) => Ok(RunResult::Yielded(yval)),
                }
            }
            Ok(InternalRunResult::BoundExpired) => {
                // The instruction count bound expired. Yield to the async exeuctor and immediately wake.
                //
                // By immediately waking, the future will be scheduled to run later (similar to tokio's yield_now())
                self.state = RunAsyncState::BoundExpired;
                waker.wake_by_ref();
                return Poll::Pending;
            }
            Err(err) => Err(err),
        };

        Poll::Ready(res)
    }
}

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

    /// Show that the futures returned by `InstanceHandle` methods are `Send`.
    ///
    /// This doesn't actually do anything at runtime, the fact that it compiles is what counts.
    #[test]
    fn async_futures_are_send() {
        fn _assert_send<T: Send>(_: &T) {}
        #[allow(unreachable_code)]
        fn _dont_run_me() {
            let mut _inst: InstanceHandle = unimplemented!();
            _assert_send(&_inst.run_async("", &[]));
            _assert_send(&_inst.run_async_start());
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Check license of third-party deps by inspecting src/vendor

use std::collections::{BTreeSet, HashSet};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::Command;

use serde_json;

static LICENSES: &'static [&'static str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "MIT",
    "Unlicense/MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std / test).
static EXCEPTIONS: &'static [&'static str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0 rls
];

/// Which crates to check against the whitelist?
static WHITELIST_CRATES: &'static [CrateVersion] = &[
    CrateVersion("rustc", "0.0.0"),
    CrateVersion("rustc_trans", "0.0.0"),
];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
static WHITELIST: &'static [Crate] = &[
    Crate("ar"),
    Crate("backtrace"),
    Crate("backtrace-sys"),
    Crate("bitflags"),
    Crate("byteorder"),
    Crate("cc"),
    Crate("cfg-if"),
    Crate("cmake"),
    Crate("filetime"),
    Crate("flate2"),
    Crate("fuchsia-zircon"),
    Crate("fuchsia-zircon-sys"),
    Crate("jobserver"),
    Crate("kernel32-sys"),
    Crate("lazy_static"),
    Crate("libc"),
    Crate("log"),
    Crate("log_settings"),
    Crate("miniz-sys"),
    Crate("num_cpus"),
    Crate("owning_ref"),
    Crate("parking_lot"),
    Crate("parking_lot_core"),
    Crate("rand"),
    Crate("redox_syscall"),
    Crate("rustc-demangle"),
    Crate("smallvec"),
    Crate("stable_deref_trait"),
    Crate("tempdir"),
    Crate("unicode-width"),
    Crate("winapi"),
    Crate("winapi-build"),
    Crate("winapi-i686-pc-windows-gnu"),
    Crate("winapi-x86_64-pc-windows-gnu"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to...

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name,)

#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct CrateVersion<'a>(&'a str, &'a str); // (name, version)

impl<'a> Crate<'a> {
    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

impl<'a> CrateVersion<'a> {
    /// Returns the struct and whether or not the dep is in-tree
    pub fn from_str(s: &'a str) -> (Self, bool) {
        let mut parts = s.split(" ");
        let name = parts.next().unwrap();
        let version = parts.next().unwrap();
        let path = parts.next().unwrap();

        let is_path_dep = path.starts_with("(path+");

        (CrateVersion(name, version), is_path_dep)
    }

    pub fn id_str(&self) -> String {
        format!("{} {}", self.0, self.1)
    }
}

impl<'a> From<CrateVersion<'a>> for Crate<'a> {
    fn from(cv: CrateVersion<'a>) -> Crate<'a> {
        Crate(cv.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences
    let path = path.join("vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // skip our exceptions
        if EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("src/vendor/{}", exception))
        }) {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = *bad || !check_license(&toml);
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of WHITELIST_CRATES at the given path. Changes `bad` to `true` if a check
/// failed.
///
/// Specifically, this checks that the dependencies are on the WHITELIST.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from cargo metadata
    let resolve = get_deps(path, cargo);

    // Get the whitelist into a convenient form
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false);
        unapproved.append(&mut bad);
    }

    if unapproved.len() > 0 {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let mut contents = String::new();
    t!(t!(File::open(path)).read_to_string(&mut contents));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Get the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a, 'b>(
    whitelist: &'a HashSet<Crate>,
    resolve: &'a Resolve,
    visited: &'b mut BTreeSet<CrateVersion<'a>>,
    krate: CrateVersion<'a>,
    must_be_on_whitelist: bool,
) -> BTreeSet<Crate<'a>> {
    // Will contain bad deps
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this path is in-tree, we don't require it to be on the whitelist
    if must_be_on_whitelist {
        // If this dependency is not on the WHITELIST, add to bad set
        if !whitelist.contains(&krate.into()) {
            unapproved.insert(krate.into());
        }
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!)
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let (krate, is_path_dep) = CrateVersion::from_str(dep);

        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep);
        unapproved.append(&mut bad);
    }

    unapproved
}
<commit_msg>Add ena to whitelist<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Check license of third-party deps by inspecting src/vendor

use std::collections::{BTreeSet, HashSet};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::Command;

use serde_json;

static LICENSES: &'static [&'static str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "MIT",
    "Unlicense/MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std / test).
static EXCEPTIONS: &'static [&'static str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0 rls
];

/// Which crates to check against the whitelist?
static WHITELIST_CRATES: &'static [CrateVersion] = &[
    CrateVersion("rustc", "0.0.0"),
    CrateVersion("rustc_trans", "0.0.0"),
];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
static WHITELIST: &'static [Crate] = &[
    Crate("ar"),
    Crate("backtrace"),
    Crate("backtrace-sys"),
    Crate("bitflags"),
    Crate("byteorder"),
    Crate("cc"),
    Crate("cfg-if"),
    Crate("cmake"),
    Crate("ena"),
    Crate("filetime"),
    Crate("flate2"),
    Crate("fuchsia-zircon"),
    Crate("fuchsia-zircon-sys"),
    Crate("jobserver"),
    Crate("kernel32-sys"),
    Crate("lazy_static"),
    Crate("libc"),
    Crate("log"),
    Crate("log_settings"),
    Crate("miniz-sys"),
    Crate("num_cpus"),
    Crate("owning_ref"),
    Crate("parking_lot"),
    Crate("parking_lot_core"),
    Crate("rand"),
    Crate("redox_syscall"),
    Crate("rustc-demangle"),
    Crate("smallvec"),
    Crate("stable_deref_trait"),
    Crate("tempdir"),
    Crate("unicode-width"),
    Crate("winapi"),
    Crate("winapi-build"),
    Crate("winapi-i686-pc-windows-gnu"),
    Crate("winapi-x86_64-pc-windows-gnu"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to...

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name,)

#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct CrateVersion<'a>(&'a str, &'a str); // (name, version)

impl<'a> Crate<'a> {
    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

impl<'a> CrateVersion<'a> {
    /// Returns the struct and whether or not the dep is in-tree
    pub fn from_str(s: &'a str) -> (Self, bool) {
        let mut parts = s.split(" ");
        let name = parts.next().unwrap();
        let version = parts.next().unwrap();
        let path = parts.next().unwrap();

        let is_path_dep = path.starts_with("(path+");

        (CrateVersion(name, version), is_path_dep)
    }

    pub fn id_str(&self) -> String {
        format!("{} {}", self.0, self.1)
    }
}

impl<'a> From<CrateVersion<'a>> for Crate<'a> {
    fn from(cv: CrateVersion<'a>) -> Crate<'a> {
        Crate(cv.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences
    let path = path.join("vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // skip our exceptions
        if EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("src/vendor/{}", exception))
        }) {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = *bad || !check_license(&toml);
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of WHITELIST_CRATES at the given path. Changes `bad` to `true` if a check
/// failed.
///
/// Specifically, this checks that the dependencies are on the WHITELIST.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from cargo metadata
    let resolve = get_deps(path, cargo);

    // Get the whitelist into a convenient form
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false);
        unapproved.append(&mut bad);
    }

    if unapproved.len() > 0 {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let mut contents = String::new();
    t!(t!(File::open(path)).read_to_string(&mut contents));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Get the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a, 'b>(
    whitelist: &'a HashSet<Crate>,
    resolve: &'a Resolve,
    visited: &'b mut BTreeSet<CrateVersion<'a>>,
    krate: CrateVersion<'a>,
    must_be_on_whitelist: bool,
) -> BTreeSet<Crate<'a>> {
    // Will contain bad deps
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this path is in-tree, we don't require it to be on the whitelist
    if must_be_on_whitelist {
        // If this dependency is not on the WHITELIST, add to bad set
        if !whitelist.contains(&krate.into()) {
            unapproved.insert(krate.into());
        }
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!)
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let (krate, is_path_dep) = CrateVersion::from_str(dep);

        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep);
        unapproved.append(&mut bad);
    }

    unapproved
}
<|endoftext|>


<commit_before>//! An interpreter for the rust-installer package format.  Responsible
//! for installing from a directory or tarball to an installation
//! prefix, represented by a `Components` instance.

use crate::dist::component::components::*;
use crate::dist::component::transaction::*;

use crate::dist::temp;
use crate::errors::*;
use crate::utils::utils;

use std::collections::HashSet;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

/// The current metadata revision used by rust-installer
pub const INSTALLER_VERSION: &str = "3";
pub const VERSION_FILE: &str = "rust-installer-version";

pub trait Package: fmt::Debug {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool;
    fn install<'a>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'a>,
    ) -> Result<Transaction<'a>>;
    fn components(&self) -> Vec<String>;
}

#[derive(Debug)]
pub struct DirectoryPackage {
    path: PathBuf,
    components: HashSet<String>,
    copy: bool,
}

impl DirectoryPackage {
    pub fn new(path: PathBuf, copy: bool) -> Result<Self> {
        validate_installer_version(&path)?;

        let content = utils::read_file("package components", &path.join("components"))?;
        let components = content
            .lines()
            .map(std::borrow::ToOwned::to_owned)
            .collect();
        Ok(DirectoryPackage {
            path,
            components,
            copy,
        })
    }
}

fn validate_installer_version(path: &Path) -> Result<()> {
    let file = utils::read_file("installer version", &path.join(VERSION_FILE))?;
    let v = file.trim();
    if v == INSTALLER_VERSION {
        Ok(())
    } else {
        Err(ErrorKind::BadInstallerVersion(v.to_owned()).into())
    }
}

impl Package for DirectoryPackage {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.components.contains(component)
            || if let Some(n) = short_name {
                self.components.contains(n)
            } else {
                false
            }
    }
    fn install<'a>(
        &self,
        target: &Components,
        name: &str,
        short_name: Option<&str>,
        tx: Transaction<'a>,
    ) -> Result<Transaction<'a>> {
        let actual_name = if self.components.contains(name) {
            name
        } else if let Some(n) = short_name {
            n
        } else {
            name
        };

        let root = self.path.join(actual_name);

        let manifest = utils::read_file("package manifest", &root.join("manifest.in"))?;
        let mut builder = target.add(name, tx);

        for l in manifest.lines() {
            let part = ComponentPart::decode(l)
                .ok_or_else(|| ErrorKind::CorruptComponent(name.to_owned()))?;

            let path = part.1;
            let src_path = root.join(&path);

            match &*part.0 {
                "file" => {
                    if self.copy {
                        builder.copy_file(path.clone(), &src_path)?
                    } else {
                        builder.move_file(path.clone(), &src_path)?
                    }
                }
                "dir" => {
                    if self.copy {
                        builder.copy_dir(path.clone(), &src_path)?
                    } else {
                        builder.move_dir(path.clone(), &src_path)?
                    }
                }
                _ => return Err(ErrorKind::CorruptComponent(name.to_owned()).into()),
            }

            set_file_perms(&target.prefix().path().join(path), &src_path)?;
        }

        let tx = builder.finish()?;

        Ok(tx)
    }

    fn components(&self) -> Vec<String> {
        self.components.iter().cloned().collect()
    }
}

// On Unix we need to set up the file permissions correctly so
// binaries are executable and directories readable. This shouldn't be
// necessary: the source files *should* have the right permissions,
// but due to rust-lang/rust#25479 they don't.
#[cfg(unix)]
fn set_file_perms(dest_path: &Path, src_path: &Path) -> Result<()> {
    use std::fs::{self, Metadata};
    use std::os::unix::fs::PermissionsExt;
    use walkdir::WalkDir;

    // Compute whether this entry needs the X bit
    fn needs_x(meta: &Metadata) -> bool {
        meta.is_dir() || // Directories need it
        meta.permissions().mode() & 0o700 == 0o700 // If it is rwx for the user, it gets the X bit
    }

    // By convention, anything in the bin/ directory of the package is a binary
    let is_bin = if let Some(p) = src_path.parent() {
        p.ends_with("bin")
    } else {
        false
    };

    let is_dir = utils::is_directory(dest_path);

    if is_dir {
        // Walk the directory setting everything
        for entry in WalkDir::new(dest_path) {
            let entry = entry.chain_err(|| ErrorKind::ComponentDirPermissionsFailed)?;
            let meta = entry
                .metadata()
                .chain_err(|| ErrorKind::ComponentDirPermissionsFailed)?;

            let mut perm = meta.permissions();
            perm.set_mode(if needs_x(&meta) { 0o755 } else { 0o644 });
            fs::set_permissions(entry.path(), perm)
                .chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
        }
    } else {
        let meta =
            fs::metadata(dest_path).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
        let mut perm = meta.permissions();
        perm.set_mode(if is_bin || needs_x(&meta) {
            0o755
        } else {
            0o644
        });
        fs::set_permissions(dest_path, perm)
            .chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
    }

    Ok(())
}

#[cfg(windows)]
fn set_file_perms(_dest_path: &Path, _src_path: &Path) -> Result<()> {
    Ok(())
}

#[derive(Debug)]
pub struct TarPackage<'a>(DirectoryPackage, temp::Dir<'a>);

impl<'a> TarPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let temp_dir = temp_cfg.new_directory()?;
        let mut archive = tar::Archive::new(stream);
        // The rust-installer packages unpack to a directory called
        // $pkgname-$version-$target. Skip that directory when
        // unpacking.
        unpack_without_first_dir(&mut archive, &*temp_dir)?;

        Ok(TarPackage(
            DirectoryPackage::new(temp_dir.to_owned(), false)?,
            temp_dir,
        ))
    }
}

fn unpack_without_first_dir<R: Read>(archive: &mut tar::Archive<R>, path: &Path) -> Result<()> {
    let entries = archive
        .entries()
        .chain_err(|| ErrorKind::ExtractingPackage)?;
    for entry in entries {
        let mut entry = entry.chain_err(|| ErrorKind::ExtractingPackage)?;
        let relpath = {
            let path = entry.path();
            let path = path.chain_err(|| ErrorKind::ExtractingPackage)?;
            path.into_owned()
        };
        let mut components = relpath.components();
        // Throw away the first path component
        components.next();
        let full_path = path.join(&components.as_path());

        // Create the full path to the entry if it does not exist already
        match full_path.parent() {
            Some(parent) if !parent.exists() => {
                ::std::fs::create_dir_all(&parent).chain_err(|| ErrorKind::ExtractingPackage)?
            }
            _ => (),
        };

        entry
            .unpack(&full_path)
            .chain_err(|| ErrorKind::ExtractingPackage)?;
    }

    Ok(())
}

impl<'a> Package for TarPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}

#[derive(Debug)]
pub struct TarGzPackage<'a>(TarPackage<'a>);

impl<'a> TarGzPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let stream = flate2::read::GzDecoder::new(stream);

        Ok(TarGzPackage(TarPackage::new(stream, temp_cfg)?))
    }
    pub fn new_file(path: &Path, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let file = File::open(path).chain_err(|| ErrorKind::ExtractingPackage)?;
        Self::new(file, temp_cfg)
    }
}

impl<'a> Package for TarGzPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}

#[derive(Debug)]
pub struct TarXzPackage<'a>(TarPackage<'a>);

impl<'a> TarXzPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let stream = xz2::read::XzDecoder::new(stream);

        Ok(TarXzPackage(TarPackage::new(stream, temp_cfg)?))
    }
    pub fn new_file(path: &Path, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let file = File::open(path).chain_err(|| ErrorKind::ExtractingPackage)?;
        Self::new(file, temp_cfg)
    }
}

impl<'a> Package for TarXzPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}
<commit_msg>Reduce parent stat-per-file to stat-per-dir<commit_after>//! An interpreter for the rust-installer package format.  Responsible
//! for installing from a directory or tarball to an installation
//! prefix, represented by a `Components` instance.

use crate::dist::component::components::*;
use crate::dist::component::transaction::*;

use crate::dist::temp;
use crate::errors::*;
use crate::utils::utils;

use std::collections::HashSet;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

/// The current metadata revision used by rust-installer
pub const INSTALLER_VERSION: &str = "3";
pub const VERSION_FILE: &str = "rust-installer-version";

pub trait Package: fmt::Debug {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool;
    fn install<'a>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'a>,
    ) -> Result<Transaction<'a>>;
    fn components(&self) -> Vec<String>;
}

#[derive(Debug)]
pub struct DirectoryPackage {
    path: PathBuf,
    components: HashSet<String>,
    copy: bool,
}

impl DirectoryPackage {
    pub fn new(path: PathBuf, copy: bool) -> Result<Self> {
        validate_installer_version(&path)?;

        let content = utils::read_file("package components", &path.join("components"))?;
        let components = content
            .lines()
            .map(std::borrow::ToOwned::to_owned)
            .collect();
        Ok(DirectoryPackage {
            path,
            components,
            copy,
        })
    }
}

fn validate_installer_version(path: &Path) -> Result<()> {
    let file = utils::read_file("installer version", &path.join(VERSION_FILE))?;
    let v = file.trim();
    if v == INSTALLER_VERSION {
        Ok(())
    } else {
        Err(ErrorKind::BadInstallerVersion(v.to_owned()).into())
    }
}

impl Package for DirectoryPackage {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.components.contains(component)
            || if let Some(n) = short_name {
                self.components.contains(n)
            } else {
                false
            }
    }
    fn install<'a>(
        &self,
        target: &Components,
        name: &str,
        short_name: Option<&str>,
        tx: Transaction<'a>,
    ) -> Result<Transaction<'a>> {
        let actual_name = if self.components.contains(name) {
            name
        } else if let Some(n) = short_name {
            n
        } else {
            name
        };

        let root = self.path.join(actual_name);

        let manifest = utils::read_file("package manifest", &root.join("manifest.in"))?;
        let mut builder = target.add(name, tx);

        for l in manifest.lines() {
            let part = ComponentPart::decode(l)
                .ok_or_else(|| ErrorKind::CorruptComponent(name.to_owned()))?;

            let path = part.1;
            let src_path = root.join(&path);

            match &*part.0 {
                "file" => {
                    if self.copy {
                        builder.copy_file(path.clone(), &src_path)?
                    } else {
                        builder.move_file(path.clone(), &src_path)?
                    }
                }
                "dir" => {
                    if self.copy {
                        builder.copy_dir(path.clone(), &src_path)?
                    } else {
                        builder.move_dir(path.clone(), &src_path)?
                    }
                }
                _ => return Err(ErrorKind::CorruptComponent(name.to_owned()).into()),
            }

            set_file_perms(&target.prefix().path().join(path), &src_path)?;
        }

        let tx = builder.finish()?;

        Ok(tx)
    }

    fn components(&self) -> Vec<String> {
        self.components.iter().cloned().collect()
    }
}

// On Unix we need to set up the file permissions correctly so
// binaries are executable and directories readable. This shouldn't be
// necessary: the source files *should* have the right permissions,
// but due to rust-lang/rust#25479 they don't.
#[cfg(unix)]
fn set_file_perms(dest_path: &Path, src_path: &Path) -> Result<()> {
    use std::fs::{self, Metadata};
    use std::os::unix::fs::PermissionsExt;
    use walkdir::WalkDir;

    // Compute whether this entry needs the X bit
    fn needs_x(meta: &Metadata) -> bool {
        meta.is_dir() || // Directories need it
        meta.permissions().mode() & 0o700 == 0o700 // If it is rwx for the user, it gets the X bit
    }

    // By convention, anything in the bin/ directory of the package is a binary
    let is_bin = if let Some(p) = src_path.parent() {
        p.ends_with("bin")
    } else {
        false
    };

    let is_dir = utils::is_directory(dest_path);

    if is_dir {
        // Walk the directory setting everything
        for entry in WalkDir::new(dest_path) {
            let entry = entry.chain_err(|| ErrorKind::ComponentDirPermissionsFailed)?;
            let meta = entry
                .metadata()
                .chain_err(|| ErrorKind::ComponentDirPermissionsFailed)?;

            let mut perm = meta.permissions();
            perm.set_mode(if needs_x(&meta) { 0o755 } else { 0o644 });
            fs::set_permissions(entry.path(), perm)
                .chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
        }
    } else {
        let meta =
            fs::metadata(dest_path).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
        let mut perm = meta.permissions();
        perm.set_mode(if is_bin || needs_x(&meta) {
            0o755
        } else {
            0o644
        });
        fs::set_permissions(dest_path, perm)
            .chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
    }

    Ok(())
}

#[cfg(windows)]
fn set_file_perms(_dest_path: &Path, _src_path: &Path) -> Result<()> {
    Ok(())
}

#[derive(Debug)]
pub struct TarPackage<'a>(DirectoryPackage, temp::Dir<'a>);

impl<'a> TarPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let temp_dir = temp_cfg.new_directory()?;
        let mut archive = tar::Archive::new(stream);
        // The rust-installer packages unpack to a directory called
        // $pkgname-$version-$target. Skip that directory when
        // unpacking.
        unpack_without_first_dir(&mut archive, &*temp_dir)?;

        Ok(TarPackage(
            DirectoryPackage::new(temp_dir.to_owned(), false)?,
            temp_dir,
        ))
    }
}

fn unpack_without_first_dir<R: Read>(archive: &mut tar::Archive<R>, path: &Path) -> Result<()> {
    let entries = archive
        .entries()
        .chain_err(|| ErrorKind::ExtractingPackage)?;
    let mut checked_parents: HashSet<PathBuf> = HashSet::new();
    for entry in entries {
        let mut entry = entry.chain_err(|| ErrorKind::ExtractingPackage)?;
        let relpath = {
            let path = entry.path();
            let path = path.chain_err(|| ErrorKind::ExtractingPackage)?;
            path.into_owned()
        };
        let mut components = relpath.components();
        // Throw away the first path component
        components.next();
        let full_path = path.join(&components.as_path());

        // Create the full path to the entry if it does not exist already
        match full_path.parent() {
            Some(parent) => {
                if !checked_parents.contains(parent) {
                    checked_parents.insert(parent.clone().to_owned());
                    // It would be nice to optimise this stat out, but the tar could be like so:
                    // a/deep/file.txt
                    // a/file.txt
                    // which would require tracking the segments rather than a simple hash.
                    // Until profile shows that one stat per dir is a problem (vs one stat per file)
                    // leave till later.

                    if !parent.exists() {
                        ::std::fs::create_dir_all(&parent)
                            .chain_err(|| ErrorKind::ExtractingPackage)?
                    }
                }
            }
            _ => (),
        };

        entry
            .unpack(&full_path)
            .chain_err(|| ErrorKind::ExtractingPackage)?;
    }

    Ok(())
}

impl<'a> Package for TarPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}

#[derive(Debug)]
pub struct TarGzPackage<'a>(TarPackage<'a>);

impl<'a> TarGzPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let stream = flate2::read::GzDecoder::new(stream);

        Ok(TarGzPackage(TarPackage::new(stream, temp_cfg)?))
    }
    pub fn new_file(path: &Path, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let file = File::open(path).chain_err(|| ErrorKind::ExtractingPackage)?;
        Self::new(file, temp_cfg)
    }
}

impl<'a> Package for TarGzPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}

#[derive(Debug)]
pub struct TarXzPackage<'a>(TarPackage<'a>);

impl<'a> TarXzPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let stream = xz2::read::XzDecoder::new(stream);

        Ok(TarXzPackage(TarPackage::new(stream, temp_cfg)?))
    }
    pub fn new_file(path: &Path, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let file = File::open(path).chain_err(|| ErrorKind::ExtractingPackage)?;
        Self::new(file, temp_cfg)
    }
}

impl<'a> Package for TarXzPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}
<|endoftext|>


<commit_before>extern crate gfx;
extern crate glfw;

static VERTEX_SRC: &'static [u8] = b"
    #version 150 core
    in vec2 a_Pos;
    out vec4 v_Color;
    void main() {
        v_Color = vec4(a_Pos+0.5, 0.0, 1.0);
        gl_Position = vec4(a_Pos, 0.0, 1.0);
    }
";

static FRAGMENT_SRC: &'static [u8] = b"
    #version 150 core
    in vec4 v_Color;
    out vec4 o_Color;
    uniform sampler3D tex3D;
    uniform MyBlock {
        vec4 color;
    } block;
    void main() {
        vec4 texel = texture(tex3D, vec3(0.5,0.5,0.5));
        vec4 unused = mix(texel, block.color, 0.5);
        o_Color = v_Color.x<0.0 ? unused : v_Color;
    }
";

#[start]
fn start(argc: int, argv: **u8) -> int {
     native::start(argc, argv, main)
}

fn main() {
    let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
    glfw.window_hint(glfw::ContextVersion(3, 2));
    glfw.window_hint(glfw::OpenglForwardCompat(true));
    glfw.window_hint(glfw::OpenglProfile(glfw::OpenGlCoreProfile));

    let (mut window, events) = glfw
        .create_window(300, 300, "Hello this is window", glfw::Windowed)
        .expect("Failed to create GLFW window.");

    window.set_key_polling(true);
    let platform = gfx::platform::Glfw::new(window.render_context());

    // spawn render task
    let (renderer, mut device) = gfx::start(platform, &glfw).unwrap();

    // spawn game task
    spawn(proc() {
        let program = renderer.create_program(
            VERTEX_SRC.to_owned(),
            FRAGMENT_SRC.to_owned());
        let data = vec![-0.0f32, 0.5, 0.5, -0.5, -0.5, -0.5];
        let mesh = renderer.create_mesh(3, data, 8);
        loop {
            let cdata = gfx::ClearData {
                color: Some([0.3, 0.3, 0.3, 1.0]),
                depth: None,
                stencil: None,
            };
            renderer.clear(cdata, None);
            renderer.draw(mesh, None, program);
            renderer.end_frame();
        }
    });

    while device.update() {
        // update device
    }
}
<commit_msg>Add window closing for triangle example<commit_after>extern crate gfx;
extern crate glfw;

static VERTEX_SRC: &'static [u8] = b"
    #version 150 core
    in vec2 a_Pos;
    out vec4 v_Color;
    void main() {
        v_Color = vec4(a_Pos+0.5, 0.0, 1.0);
        gl_Position = vec4(a_Pos, 0.0, 1.0);
    }
";

static FRAGMENT_SRC: &'static [u8] = b"
    #version 150 core
    in vec4 v_Color;
    out vec4 o_Color;
    uniform sampler3D tex3D;
    uniform MyBlock {
        vec4 color;
    } block;
    void main() {
        vec4 texel = texture(tex3D, vec3(0.5,0.5,0.5));
        vec4 unused = mix(texel, block.color, 0.5);
        o_Color = v_Color.x<0.0 ? unused : v_Color;
    }
";

#[start]
fn start(argc: int, argv: **u8) -> int {
     native::start(argc, argv, main)
}

fn main() {
    let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
    glfw.window_hint(glfw::ContextVersion(3, 2));
    glfw.window_hint(glfw::OpenglForwardCompat(true));
    glfw.window_hint(glfw::OpenglProfile(glfw::OpenGlCoreProfile));

    let (mut window, events) = glfw
        .create_window(300, 300, "Hello this is window", glfw::Windowed)
        .expect("Failed to create GLFW window.");

    window.set_key_polling(true);
    let platform = gfx::platform::Glfw::new(window.render_context());

    // spawn render task
    let (renderer, mut device) = gfx::start(platform, &glfw).unwrap();

    // spawn game task
    spawn(proc() {
        let program = renderer.create_program(
            VERTEX_SRC.to_owned(),
            FRAGMENT_SRC.to_owned());
        let data = vec![-0.0f32, 0.5, 0.5, -0.5, -0.5, -0.5];
        let mesh = renderer.create_mesh(3, data, 8);
        loop {
            let cdata = gfx::ClearData {
                color: Some([0.3, 0.3, 0.3, 1.0]),
                depth: None,
                stencil: None,
            };
            renderer.clear(cdata, None);
            renderer.draw(mesh, None, program);
            renderer.end_frame();
        }
    });

    // FIXME: some task fails when the window closes
    while device.update() && !window.should_close() {
        // update device
        glfw.poll_events();
        for (_, event) in glfw::flush_messages(&events) {
            match event {
                glfw::KeyEvent(glfw::KeyEscape, _, glfw::Press, _) => {
                    window.set_should_close(true);
                },
                _ => {},
            }
        }
    }
}
<|endoftext|>