<commit_before>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use serde_reflection::{ContainerFormat, Format, Named, Registry, VariantFormat};
use std::collections::BTreeMap;
use std::io::{Result, Write};
use std::path::PathBuf;

/// Write container definitions in Python.
/// * The packages `dataclasses` and `typing` are assumed to be available.
/// * The module `serde_types` is assumed to be available.
pub fn output(out: &mut dyn Write, registry: &Registry) -> Result<()> {
    output_with_optional_serde_package(out, registry, None)
}

fn output_with_optional_serde_package(
    out: &mut dyn Write,
    registry: &Registry,
    serde_package_name: Option<String>,
) -> Result<()> {
    let mut emitter = PythonEmitter {
        out,
        indentation: 0,
        serde_package_name,
    };
    emitter.output_preamble()?;
    for (name, format) in registry {
        emitter.output_container(name, format)?;
    }
    Ok(())
}

struct PythonEmitter<T> {
    out: T,
    indentation: usize,
    serde_package_name: Option<String>,
}

impl<T> PythonEmitter<T>
where
    T: Write,
{
    fn output_preamble(&mut self) -> Result<()> {
        assert_eq!(self.indentation, 0);
        writeln!(
            self.out,
            r#"from dataclasses import dataclass
import typing
{}import serde_types as st"#,
            match &self.serde_package_name {
                None => "".to_string(),
                Some(name) => format!("from {} ", name),
            }
        )
    }

    fn quote_type(format: &Format) -> String {
        use Format::*;
        match format {
            TypeName(x) => format!("\"{}\"", x), // Need quotes because of circular dependencies.
            Unit => "st.unit".into(),
            Bool => "st.bool".into(),
            I8 => "st.int8".into(),
            I16 => "st.int16".into(),
            I32 => "st.int32".into(),
            I64 => "st.int64".into(),
            I128 => "st.int128".into(),
            U8 => "st.uint8".into(),
            U16 => "st.uint16".into(),
            U32 => "st.uint32".into(),
            U64 => "st.uint64".into(),
            U128 => "st.uint128".into(),
            F32 => "st.float32".into(),
            F64 => "st.float64".into(),
            Char => "st.char".into(),
            Str => "str".into(),
            Bytes => "bytes".into(),

            Option(format) => format!("typing.Optional[{}]", Self::quote_type(format)),
            Seq(format) => format!("typing.Sequence[{}]", Self::quote_type(format)),
            Map { key, value } => format!(
                "typing.Dict[{}, {}]",
                Self::quote_type(key),
                Self::quote_type(value)
            ),
            Tuple(formats) => format!("typing.Tuple[{}]", Self::quote_types(formats)),
            TupleArray { content, size } => format!(
                "typing.Tuple[{}]",
                Self::quote_types(&vec![content.as_ref().clone(); *size])
            ), // Sadly, there are no fixed-size arrays in python.

            Variable(_) => panic!("unexpected value"),
        }
    }

    fn quote_types(formats: &[Format]) -> String {
        formats
            .iter()
            .map(Self::quote_type)
            .collect::<Vec<_>>()
            .join(", ")
    }

    fn output_fields(&mut self, fields: &[Named<Format>]) -> Result<()> {
        self.indentation += 1;
        let tab = "    ".repeat(self.indentation);
        for field in fields {
            writeln!(
                self.out,
                "{}{}: {}",
                tab,
                field.name,
                Self::quote_type(&field.value)
            )?;
        }
        self.indentation -= 1;
        Ok(())
    }

    fn output_variant(
        &mut self,
        base: &str,
        name: &str,
        index: u32,
        variant: &VariantFormat,
    ) -> Result<()> {
        assert_eq!(self.indentation, 0);
        use VariantFormat::*;
        match variant {
            Unit => writeln!(
                self.out,
                "\n@dataclass\nclass {}__{}({}):\n    INDEX = {}\n",
                base, name, base, index,
            ),
            NewType(format) => writeln!(
                self.out,
                "\n@dataclass\nclass {}__{}({}):\n    INDEX = {}\n    value: {}\n",
                base,
                name,
                base,
                index,
                Self::quote_type(format)
            ),
            Tuple(formats) => writeln!(
                self.out,
                "\n@dataclass\nclass {}__{}({}):\n    INDEX = {}\n    value: typing.Tuple[{}]\n",
                base,
                name,
                base,
                index,
                Self::quote_types(formats)
            ),
            Struct(fields) => {
                writeln!(
                    self.out,
                    "\n@dataclass\nclass {}__{}({}):\n    INDEX = {}",
                    base, name, base, index
                )?;
                self.output_fields(fields)?;
                writeln!(self.out)
            }
            Variable(_) => panic!("incorrect value"),
        }
    }

    fn output_variants(
        &mut self,
        base: &str,
        variants: &BTreeMap<u32, Named<VariantFormat>>,
    ) -> Result<()> {
        assert_eq!(self.indentation, 0);
        for (index, variant) in variants {
            self.output_variant(base, &variant.name, *index, &variant.value)?;
        }
        Ok(())
    }

    fn output_container(&mut self, name: &str, format: &ContainerFormat) -> Result<()> {
        assert_eq!(self.indentation, 0);
        use ContainerFormat::*;
        match format {
            UnitStruct => writeln!(self.out, "\n@dataclass\nclass {}:\n    pass\n", name),
            NewTypeStruct(format) => writeln!(
                self.out,
                "\n@dataclass\nclass {}:\n    value: {}\n",
                name,
                Self::quote_type(format)
            ),
            TupleStruct(formats) => writeln!(
                self.out,
                "\n@dataclass\nclass {}:\n    value: typing.Tuple[{}]\n",
                name,
                Self::quote_types(formats)
            ),
            Struct(fields) => {
                writeln!(self.out, "\n@dataclass\nclass {}:", name)?;
                self.output_fields(fields)?;
                writeln!(self.out)
            }
            Enum(variants) => {
                // Initializing VARIANTS with a temporary value for typechecking purposes.
                writeln!(self.out, "\nclass {}:\n    VARIANTS = []\n", name)?;
                self.output_variants(name, variants)?;
                writeln!(
                    self.out,
                    "{}.VARIANTS = [\n{}]\n",
                    name,
                    variants
                        .iter()
                        .map(|(_, v)| format!("    {}__{},\n", name, v.name))
                        .collect::<Vec<_>>()
                        .join("")
                )
            }
        }
    }
}

pub struct Installer {
    install_dir: PathBuf,
    serde_package_name: Option<String>,
}

impl Installer {
    pub fn new(install_dir: PathBuf, serde_package_name: Option<String>) -> Self {
        Installer {
            install_dir,
            serde_package_name,
        }
    }

    fn create_module_init_file(&self, name: &str) -> Result<std::fs::File> {
        let dir_path = self.install_dir.join(name);
        std::fs::create_dir_all(&dir_path)?;
        std::fs::File::create(dir_path.join("__init__.py"))
    }

    fn fix_serde_package(&self, content: &str) -> String {
        match &self.serde_package_name {
            None => content.into(),
            Some(name) => content.replace(
                "import serde_types",
                &format!("from {} import serde_types", name),
            ),
        }
    }
}

impl crate::SourceInstaller for Installer {
    type Error = Box<dyn std::error::Error>;

    fn install_module(
        &self,
        name: &str,
        registry: &Registry,
    ) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file(name)?;
        output_with_optional_serde_package(&mut file, registry, self.serde_package_name.clone())?;
        Ok(())
    }

    fn install_serde_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("serde_types")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/serde_types/__init__.py"))
        )?;
        Ok(())
    }

    fn install_bincode_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("bincode")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/bincode/__init__.py"))
        )?;
        Ok(())
    }

    fn install_lcs_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("lcs")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/lcs/__init__.py"))
        )?;
        Ok(())
    }
}
<commit_msg>[serde generate] Python: use IndentWriter<commit_after>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::indent::{IndentConfig, IndentedWriter};
use serde_reflection::{ContainerFormat, Format, Named, Registry, VariantFormat};
use std::collections::BTreeMap;
use std::io::{Result, Write};
use std::path::PathBuf;

/// Write container definitions in Python.
/// * The packages `dataclasses` and `typing` are assumed to be available.
/// * The module `serde_types` is assumed to be available.
pub fn output(out: &mut dyn Write, registry: &Registry) -> Result<()> {
    output_with_optional_serde_package(out, registry, None)
}

fn output_with_optional_serde_package(
    out: &mut dyn Write,
    registry: &Registry,
    serde_package_name: Option<String>,
) -> Result<()> {
    let mut emitter = PythonEmitter {
        out: IndentedWriter::new(out, IndentConfig::Space(4)),
        serde_package_name,
    };
    emitter.output_preamble()?;
    for (name, format) in registry {
        emitter.output_container(name, format)?;
    }
    Ok(())
}

struct PythonEmitter<T> {
    out: IndentedWriter<T>,
    serde_package_name: Option<String>,
}

impl<T> PythonEmitter<T>
where
    T: Write,
{
    fn output_preamble(&mut self) -> Result<()> {
        writeln!(
            self.out,
            r#"from dataclasses import dataclass
import typing
{}import serde_types as st"#,
            match &self.serde_package_name {
                None => "".to_string(),
                Some(name) => format!("from {} ", name),
            }
        )
    }

    fn quote_type(format: &Format) -> String {
        use Format::*;
        match format {
            TypeName(x) => format!("\"{}\"", x), // Need quotes because of circular dependencies.
            Unit => "st.unit".into(),
            Bool => "st.bool".into(),
            I8 => "st.int8".into(),
            I16 => "st.int16".into(),
            I32 => "st.int32".into(),
            I64 => "st.int64".into(),
            I128 => "st.int128".into(),
            U8 => "st.uint8".into(),
            U16 => "st.uint16".into(),
            U32 => "st.uint32".into(),
            U64 => "st.uint64".into(),
            U128 => "st.uint128".into(),
            F32 => "st.float32".into(),
            F64 => "st.float64".into(),
            Char => "st.char".into(),
            Str => "str".into(),
            Bytes => "bytes".into(),

            Option(format) => format!("typing.Optional[{}]", Self::quote_type(format)),
            Seq(format) => format!("typing.Sequence[{}]", Self::quote_type(format)),
            Map { key, value } => format!(
                "typing.Dict[{}, {}]",
                Self::quote_type(key),
                Self::quote_type(value)
            ),
            Tuple(formats) => format!("typing.Tuple[{}]", Self::quote_types(formats)),
            TupleArray { content, size } => format!(
                "typing.Tuple[{}]",
                Self::quote_types(&vec![content.as_ref().clone(); *size])
            ), // Sadly, there are no fixed-size arrays in python.

            Variable(_) => panic!("unexpected value"),
        }
    }

    fn quote_types(formats: &[Format]) -> String {
        formats
            .iter()
            .map(Self::quote_type)
            .collect::<Vec<_>>()
            .join(", ")
    }

    fn output_fields(&mut self, fields: &[Named<Format>]) -> Result<()> {
        for field in fields {
            writeln!(
                self.out,
                "{}: {}",
                field.name,
                Self::quote_type(&field.value)
            )?;
        }
        Ok(())
    }

    fn output_variant(
        &mut self,
        base: &str,
        name: &str,
        index: u32,
        variant: &VariantFormat,
    ) -> Result<()> {
        use VariantFormat::*;
        match variant {
            Unit => writeln!(
                self.out,
                "\n@dataclass\nclass {}__{}({}):\n    INDEX = {}\n",
                base, name, base, index,
            ),
            NewType(format) => writeln!(
                self.out,
                "\n@dataclass\nclass {}__{}({}):\n    INDEX = {}\n    value: {}\n",
                base,
                name,
                base,
                index,
                Self::quote_type(format)
            ),
            Tuple(formats) => writeln!(
                self.out,
                "\n@dataclass\nclass {}__{}({}):\n    INDEX = {}\n    value: typing.Tuple[{}]\n",
                base,
                name,
                base,
                index,
                Self::quote_types(formats)
            ),
            Struct(fields) => {
                writeln!(
                    self.out,
                    "\n@dataclass\nclass {}__{}({}):\n    INDEX = {}",
                    base, name, base, index
                )?;
                self.out.inc_level();
                self.output_fields(fields)?;
                self.out.dec_level();
                writeln!(self.out)
            }
            Variable(_) => panic!("incorrect value"),
        }
    }

    fn output_variants(
        &mut self,
        base: &str,
        variants: &BTreeMap<u32, Named<VariantFormat>>,
    ) -> Result<()> {
        for (index, variant) in variants {
            self.output_variant(base, &variant.name, *index, &variant.value)?;
        }
        Ok(())
    }

    fn output_container(&mut self, name: &str, format: &ContainerFormat) -> Result<()> {
        use ContainerFormat::*;
        match format {
            UnitStruct => writeln!(self.out, "\n@dataclass\nclass {}:\n    pass\n", name),
            NewTypeStruct(format) => writeln!(
                self.out,
                "\n@dataclass\nclass {}:\n    value: {}\n",
                name,
                Self::quote_type(format)
            ),
            TupleStruct(formats) => writeln!(
                self.out,
                "\n@dataclass\nclass {}:\n    value: typing.Tuple[{}]\n",
                name,
                Self::quote_types(formats)
            ),
            Struct(fields) => {
                writeln!(self.out, "\n@dataclass\nclass {}:", name)?;
                self.out.inc_level();
                self.output_fields(fields)?;
                self.out.dec_level();
                writeln!(self.out)
            }
            Enum(variants) => {
                // Initializing VARIANTS with a temporary value for typechecking purposes.
                writeln!(self.out, "\nclass {}:\n    VARIANTS = []\n", name)?;
                self.output_variants(name, variants)?;
                writeln!(
                    self.out,
                    "{}.VARIANTS = [\n{}]\n",
                    name,
                    variants
                        .iter()
                        .map(|(_, v)| format!("    {}__{},\n", name, v.name))
                        .collect::<Vec<_>>()
                        .join("")
                )
            }
        }
    }
}

pub struct Installer {
    install_dir: PathBuf,
    serde_package_name: Option<String>,
}

impl Installer {
    pub fn new(install_dir: PathBuf, serde_package_name: Option<String>) -> Self {
        Installer {
            install_dir,
            serde_package_name,
        }
    }

    fn create_module_init_file(&self, name: &str) -> Result<std::fs::File> {
        let dir_path = self.install_dir.join(name);
        std::fs::create_dir_all(&dir_path)?;
        std::fs::File::create(dir_path.join("__init__.py"))
    }

    fn fix_serde_package(&self, content: &str) -> String {
        match &self.serde_package_name {
            None => content.into(),
            Some(name) => content.replace(
                "import serde_types",
                &format!("from {} import serde_types", name),
            ),
        }
    }
}

impl crate::SourceInstaller for Installer {
    type Error = Box<dyn std::error::Error>;

    fn install_module(
        &self,
        name: &str,
        registry: &Registry,
    ) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file(name)?;
        output_with_optional_serde_package(&mut file, registry, self.serde_package_name.clone())?;
        Ok(())
    }

    fn install_serde_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("serde_types")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/serde_types/__init__.py"))
        )?;
        Ok(())
    }

    fn install_bincode_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("bincode")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/bincode/__init__.py"))
        )?;
        Ok(())
    }

    fn install_lcs_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("lcs")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/lcs/__init__.py"))
        )?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>fn foo() {
   for x in 0 .. {
       break;
   };
   for _x in 0 .. (0 .. {1 + 2}).sum::<u32>() {
       break;
   };
}
<commit_msg>fix test<commit_after>fn foo() {
   for x in 0 .. {
       break;
   }
   for _x in 0 .. (0 .. {1 + 2}).sum::<u32>() {
       break;
   }
}
<|endoftext|>


<commit_before>//! Calls `chalk-solve` to solve a `ty::Predicate`
//!
//! In order to call `chalk-solve`, this file must convert a `CanonicalChalkEnvironmentAndGoal` into
//! a Chalk uncanonical goal. It then calls Chalk, and converts the answer back into rustc solution.

crate mod db;
crate mod lowering;

use rustc_data_structures::fx::FxHashMap;

use rustc_index::vec::IndexVec;

use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind};
use rustc_middle::traits::ChalkRustInterner;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::GenericArg;
use rustc_middle::ty::{self, BoundVar, ParamTy, TyCtxt, TypeFoldable};

use rustc_infer::infer::canonical::{
    Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse,
};
use rustc_infer::traits::{self, CanonicalChalkEnvironmentAndGoal};

use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase;
use crate::chalk::lowering::{
    LowerInto, ParamsSubstitutor, PlaceholdersCollector, RegionsSubstitutor,
};

use chalk_solve::Solution;

crate fn provide(p: &mut Providers) {
    *p = Providers { evaluate_goal, ..*p };
}

crate fn evaluate_goal<'tcx>(
    tcx: TyCtxt<'tcx>,
    obligation: CanonicalChalkEnvironmentAndGoal<'tcx>,
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, traits::query::NoSolution> {
    let interner = ChalkRustInterner { tcx };

    // Chalk doesn't have a notion of `Params`, so instead we use placeholders.
    let mut placeholders_collector = PlaceholdersCollector::new();
    obligation.visit_with(&mut placeholders_collector);

    let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder {
        universe: ty::UniverseIndex::ROOT,
        name: ty::BoundRegionKind::BrAnon(placeholders_collector.next_anon_region_placeholder + 1),
    }));

    let mut params_substitutor =
        ParamsSubstitutor::new(tcx, placeholders_collector.next_ty_placeholder);
    let obligation = obligation.fold_with(&mut params_substitutor);
    // FIXME(chalk): we really should be substituting these back in the solution
    let _params: FxHashMap<usize, ParamTy> = params_substitutor.params;

    let mut regions_substitutor = RegionsSubstitutor::new(tcx, reempty_placeholder);
    let obligation = obligation.fold_with(&mut regions_substitutor);

    let max_universe = obligation.max_universe.index();

    let lowered_goal: chalk_ir::UCanonical<
        chalk_ir::InEnvironment<chalk_ir::Goal<ChalkRustInterner<'tcx>>>,
    > = chalk_ir::UCanonical {
        canonical: chalk_ir::Canonical {
            binders: chalk_ir::CanonicalVarKinds::from_iter(
                &interner,
                obligation.variables.iter().map(|v| match v.kind {
                    CanonicalVarKind::PlaceholderTy(_ty) => unimplemented!(),
                    CanonicalVarKind::PlaceholderRegion(_ui) => unimplemented!(),
                    CanonicalVarKind::Ty(ty) => match ty {
                        CanonicalTyVarKind::General(ui) => chalk_ir::WithKind::new(
                            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
                            chalk_ir::UniverseIndex { counter: ui.index() },
                        ),
                        CanonicalTyVarKind::Int => chalk_ir::WithKind::new(
                            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::Integer),
                            chalk_ir::UniverseIndex::root(),
                        ),
                        CanonicalTyVarKind::Float => chalk_ir::WithKind::new(
                            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::Float),
                            chalk_ir::UniverseIndex::root(),
                        ),
                    },
                    CanonicalVarKind::Region(ui) => chalk_ir::WithKind::new(
                        chalk_ir::VariableKind::Lifetime,
                        chalk_ir::UniverseIndex { counter: ui.index() },
                    ),
                    CanonicalVarKind::Const(_ui) => unimplemented!(),
                    CanonicalVarKind::PlaceholderConst(_pc) => unimplemented!(),
                }),
            ),
            value: obligation.value.lower_into(&interner),
        },
        universes: max_universe + 1,
    };

    use chalk_solve::Solver;
    let mut solver = chalk_engine::solve::SLGSolver::new(32, None);
    let db = ChalkRustIrDatabase { interner, reempty_placeholder };
    let solution = solver.solve(&db, &lowered_goal);
    debug!(?obligation, ?solution, "evaluatate goal");

    // Ideally, the code to convert *back* to rustc types would live close to
    // the code to convert *from* rustc types. Right now though, we don't
    // really need this and so it's really minimal.
    // Right now, we also treat a `Unique` solution the same as
    // `Ambig(Definite)`. This really isn't right.
    let make_solution = |subst: chalk_ir::Substitution<_>| {
        let mut var_values: IndexVec<BoundVar, GenericArg<'tcx>> = IndexVec::new();
        subst.as_slice(&interner).iter().for_each(|p| {
            var_values.push(p.lower_into(&interner));
        });
        let sol = Canonical {
            max_universe: ty::UniverseIndex::from_usize(0),
            variables: obligation.variables.clone(),
            value: QueryResponse {
                var_values: CanonicalVarValues { var_values },
                region_constraints: QueryRegionConstraints::default(),
                certainty: Certainty::Proven,
                value: (),
            },
        };
        tcx.arena.alloc(sol)
    };
    solution
        .map(|s| match s {
            Solution::Unique(subst) => {
                // FIXME(chalk): handle constraints
                make_solution(subst.value.subst)
            }
            Solution::Ambig(guidance) => {
                match guidance {
                    chalk_solve::Guidance::Definite(subst) => make_solution(subst.value),
                    chalk_solve::Guidance::Suggested(_) => unimplemented!(),
                    chalk_solve::Guidance::Unknown => {
                        // chalk_fulfill doesn't use the var_values here, so
                        // let's just ignore that
                        let sol = Canonical {
                            max_universe: ty::UniverseIndex::from_usize(0),
                            variables: obligation.variables.clone(),
                            value: QueryResponse {
                                var_values: CanonicalVarValues { var_values: IndexVec::new() }
                                    .make_identity(tcx),
                                region_constraints: QueryRegionConstraints::default(),
                                certainty: Certainty::Ambiguous,
                                value: (),
                            },
                        };
                        &*tcx.arena.alloc(sol)
                    }
                }
            }
        })
        .ok_or(traits::query::NoSolution)
}
<commit_msg>fix: small typo error in chalk/mod.rs<commit_after>//! Calls `chalk-solve` to solve a `ty::Predicate`
//!
//! In order to call `chalk-solve`, this file must convert a `CanonicalChalkEnvironmentAndGoal` into
//! a Chalk uncanonical goal. It then calls Chalk, and converts the answer back into rustc solution.

crate mod db;
crate mod lowering;

use rustc_data_structures::fx::FxHashMap;

use rustc_index::vec::IndexVec;

use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind};
use rustc_middle::traits::ChalkRustInterner;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::GenericArg;
use rustc_middle::ty::{self, BoundVar, ParamTy, TyCtxt, TypeFoldable};

use rustc_infer::infer::canonical::{
    Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse,
};
use rustc_infer::traits::{self, CanonicalChalkEnvironmentAndGoal};

use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase;
use crate::chalk::lowering::{
    LowerInto, ParamsSubstitutor, PlaceholdersCollector, RegionsSubstitutor,
};

use chalk_solve::Solution;

crate fn provide(p: &mut Providers) {
    *p = Providers { evaluate_goal, ..*p };
}

crate fn evaluate_goal<'tcx>(
    tcx: TyCtxt<'tcx>,
    obligation: CanonicalChalkEnvironmentAndGoal<'tcx>,
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, traits::query::NoSolution> {
    let interner = ChalkRustInterner { tcx };

    // Chalk doesn't have a notion of `Params`, so instead we use placeholders.
    let mut placeholders_collector = PlaceholdersCollector::new();
    obligation.visit_with(&mut placeholders_collector);

    let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder {
        universe: ty::UniverseIndex::ROOT,
        name: ty::BoundRegionKind::BrAnon(placeholders_collector.next_anon_region_placeholder + 1),
    }));

    let mut params_substitutor =
        ParamsSubstitutor::new(tcx, placeholders_collector.next_ty_placeholder);
    let obligation = obligation.fold_with(&mut params_substitutor);
    // FIXME(chalk): we really should be substituting these back in the solution
    let _params: FxHashMap<usize, ParamTy> = params_substitutor.params;

    let mut regions_substitutor = RegionsSubstitutor::new(tcx, reempty_placeholder);
    let obligation = obligation.fold_with(&mut regions_substitutor);

    let max_universe = obligation.max_universe.index();

    let lowered_goal: chalk_ir::UCanonical<
        chalk_ir::InEnvironment<chalk_ir::Goal<ChalkRustInterner<'tcx>>>,
    > = chalk_ir::UCanonical {
        canonical: chalk_ir::Canonical {
            binders: chalk_ir::CanonicalVarKinds::from_iter(
                &interner,
                obligation.variables.iter().map(|v| match v.kind {
                    CanonicalVarKind::PlaceholderTy(_ty) => unimplemented!(),
                    CanonicalVarKind::PlaceholderRegion(_ui) => unimplemented!(),
                    CanonicalVarKind::Ty(ty) => match ty {
                        CanonicalTyVarKind::General(ui) => chalk_ir::WithKind::new(
                            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
                            chalk_ir::UniverseIndex { counter: ui.index() },
                        ),
                        CanonicalTyVarKind::Int => chalk_ir::WithKind::new(
                            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::Integer),
                            chalk_ir::UniverseIndex::root(),
                        ),
                        CanonicalTyVarKind::Float => chalk_ir::WithKind::new(
                            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::Float),
                            chalk_ir::UniverseIndex::root(),
                        ),
                    },
                    CanonicalVarKind::Region(ui) => chalk_ir::WithKind::new(
                        chalk_ir::VariableKind::Lifetime,
                        chalk_ir::UniverseIndex { counter: ui.index() },
                    ),
                    CanonicalVarKind::Const(_ui) => unimplemented!(),
                    CanonicalVarKind::PlaceholderConst(_pc) => unimplemented!(),
                }),
            ),
            value: obligation.value.lower_into(&interner),
        },
        universes: max_universe + 1,
    };

    use chalk_solve::Solver;
    let mut solver = chalk_engine::solve::SLGSolver::new(32, None);
    let db = ChalkRustIrDatabase { interner, reempty_placeholder };
    let solution = solver.solve(&db, &lowered_goal);
    debug!(?obligation, ?solution, "evaluate goal");

    // Ideally, the code to convert *back* to rustc types would live close to
    // the code to convert *from* rustc types. Right now though, we don't
    // really need this and so it's really minimal.
    // Right now, we also treat a `Unique` solution the same as
    // `Ambig(Definite)`. This really isn't right.
    let make_solution = |subst: chalk_ir::Substitution<_>| {
        let mut var_values: IndexVec<BoundVar, GenericArg<'tcx>> = IndexVec::new();
        subst.as_slice(&interner).iter().for_each(|p| {
            var_values.push(p.lower_into(&interner));
        });
        let sol = Canonical {
            max_universe: ty::UniverseIndex::from_usize(0),
            variables: obligation.variables.clone(),
            value: QueryResponse {
                var_values: CanonicalVarValues { var_values },
                region_constraints: QueryRegionConstraints::default(),
                certainty: Certainty::Proven,
                value: (),
            },
        };
        tcx.arena.alloc(sol)
    };
    solution
        .map(|s| match s {
            Solution::Unique(subst) => {
                // FIXME(chalk): handle constraints
                make_solution(subst.value.subst)
            }
            Solution::Ambig(guidance) => {
                match guidance {
                    chalk_solve::Guidance::Definite(subst) => make_solution(subst.value),
                    chalk_solve::Guidance::Suggested(_) => unimplemented!(),
                    chalk_solve::Guidance::Unknown => {
                        // chalk_fulfill doesn't use the var_values here, so
                        // let's just ignore that
                        let sol = Canonical {
                            max_universe: ty::UniverseIndex::from_usize(0),
                            variables: obligation.variables.clone(),
                            value: QueryResponse {
                                var_values: CanonicalVarValues { var_values: IndexVec::new() }
                                    .make_identity(tcx),
                                region_constraints: QueryRegionConstraints::default(),
                                certainty: Certainty::Ambiguous,
                                value: (),
                            },
                        };
                        &*tcx.arena.alloc(sol)
                    }
                }
            }
        })
        .ok_or(traits::query::NoSolution)
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::fragments::{BoxFragment, Fragment};
use crate::geom::physical::{Rect, Vec2};
use embedder_traits::Cursor;
use euclid::{Point2D, SideOffsets2D, Size2D};
use gfx::text::glyph::GlyphStore;
use std::sync::Arc;
use style::properties::ComputedValues;
use style::values::computed::{BorderStyle, Length, LengthPercentage};
use webrender_api::{self as wr, units, CommonItemProperties, PrimitiveFlags};

pub struct DisplayListBuilder {
    pipeline_id: wr::PipelineId,
    pub wr: wr::DisplayListBuilder,
    pub is_contentful: bool,
}

impl DisplayListBuilder {
    pub fn new(pipeline_id: wr::PipelineId, viewport_size: wr::units::LayoutSize) -> Self {
        Self {
            pipeline_id,
            is_contentful: false,
            wr: wr::DisplayListBuilder::new(pipeline_id, viewport_size),
        }
    }
}

/// Contentful paint, for the purpose of
/// https://w3c.github.io/paint-timing/#first-contentful-paint
/// (i.e. the display list contains items of type text,
/// image, non-white canvas or SVG). Used by metrics.
pub struct IsContentful(pub bool);

impl Fragment {
    pub(crate) fn build_display_list(
        &self,
        builder: &mut DisplayListBuilder,
        is_contentful: &mut IsContentful,
        containing_block: &Rect<Length>,
    ) {
        match self {
            Fragment::Box(b) => b.build_display_list(builder, is_contentful, containing_block),
            Fragment::Anonymous(a) => {
                let rect = a
                    .rect
                    .to_physical(a.mode, containing_block)
                    .translate(&containing_block.top_left);
                for child in &a.children {
                    child.build_display_list(builder, is_contentful, &rect)
                }
            },
            Fragment::Text(t) => {
                is_contentful.0 = true;
                let rect = t
                    .rect
                    .to_physical(t.parent_style.writing_mode, containing_block)
                    .translate(&containing_block.top_left);
                let mut baseline_origin = rect.top_left.clone();
                baseline_origin.y += t.ascent;
                let cursor = cursor(&t.parent_style, Cursor::Text);
                let common = CommonItemProperties {
                    clip_rect: rect.clone().into(),
                    clip_id: wr::ClipId::root(builder.pipeline_id),
                    spatial_id: wr::SpatialId::root_scroll_node(builder.pipeline_id),
                    hit_info: cursor.map(|cursor| (t.tag.0 as u64, cursor as u16)),
                    // TODO(gw): Make use of the WR backface visibility functionality.
                    flags: PrimitiveFlags::default(),
                };
                let glyphs = glyphs(&t.glyphs, baseline_origin);
                if glyphs.is_empty() {
                    return;
                }
                let color = t.parent_style.clone_color();
                builder
                    .wr
                    .push_text(&common, rect.into(), &glyphs, t.font_key, rgba(color), None);
            },
            Fragment::Image(i) => {
                use style::computed_values::image_rendering::T as ImageRendering;
                is_contentful.0 = true;
                let rect = i
                    .rect
                    .to_physical(i.style.writing_mode, containing_block)
                    .translate(&containing_block.top_left);
                let common = CommonItemProperties {
                    clip_rect: rect.clone().into(),
                    clip_id: wr::ClipId::root(builder.pipeline_id),
                    spatial_id: wr::SpatialId::root_scroll_node(builder.pipeline_id),
                    hit_info: None,
                    // TODO(gw): Make use of the WR backface visibility functionality.
                    flags: PrimitiveFlags::default(),
                };
                builder.wr.push_image(
                    &common,
                    rect.into(),
                    match i.style.get_inherited_box().image_rendering {
                        ImageRendering::Auto => wr::ImageRendering::Auto,
                        ImageRendering::CrispEdges => wr::ImageRendering::CrispEdges,
                        ImageRendering::Pixelated => wr::ImageRendering::Pixelated,
                    },
                    wr::AlphaType::PremultipliedAlpha,
                    i.image_key,
                    wr::ColorF::WHITE,
                );
            },
        }
    }
}

impl BoxFragment {
    fn build_display_list(
        &self,
        builder: &mut DisplayListBuilder,
        is_contentful: &mut IsContentful,
        containing_block: &Rect<Length>,
    ) {
        let border_rect = self
            .border_rect()
            .to_physical(self.style.writing_mode, containing_block)
            .translate(&containing_block.top_left)
            .into();
        let cursor = cursor(&self.style, Cursor::Default);
        let common = CommonItemProperties {
            clip_rect: border_rect,
            clip_id: wr::ClipId::root(builder.pipeline_id),
            spatial_id: wr::SpatialId::root_scroll_node(builder.pipeline_id),
            hit_info: cursor.map(|cursor| (self.tag.0 as u64, cursor as u16)),
            // TODO(gw): Make use of the WR backface visibility functionality.
            flags: PrimitiveFlags::default(),
        };
        let border_radius = self.border_radius(&border_rect);

        self.background_display_items(builder, &common);
        self.border_display_items(builder, &common, border_rect, border_radius);
        let content_rect = self
            .content_rect
            .to_physical(self.style.writing_mode, containing_block)
            .translate(&containing_block.top_left);
        for child in &self.children {
            child.build_display_list(builder, is_contentful, &content_rect)
        }
    }

    fn border_radius(&self, border_rect: &units::LayoutRect) -> wr::BorderRadius {
        let resolve = |radius: &LengthPercentage, box_size: f32| {
            radius.percentage_relative_to(Length::new(box_size)).px()
        };
        let corner = |corner: &style::values::computed::BorderCornerRadius| {
            Size2D::new(
                resolve(&corner.0.width.0, border_rect.size.width),
                resolve(&corner.0.height.0, border_rect.size.height),
            )
        };
        let b = self.style.get_border();
        wr::BorderRadius {
            top_left: corner(&b.border_top_left_radius),
            top_right: corner(&b.border_top_right_radius),
            bottom_right: corner(&b.border_bottom_right_radius),
            bottom_left: corner(&b.border_bottom_left_radius),
        }
    }

    fn background_display_items(
        &self,
        builder: &mut DisplayListBuilder,
        common: &CommonItemProperties,
    ) {
        let background_color = self
            .style
            .resolve_color(self.style.clone_background_color());
        if background_color.alpha > 0 || common.hit_info.is_some() {
            builder.wr.push_rect(common, rgba(background_color))
        }
    }

    fn border_display_items(
        &self,
        builder: &mut DisplayListBuilder,
        common: &CommonItemProperties,
        border_rect: units::LayoutRect,
        radius: wr::BorderRadius,
    ) {
        let b = self.style.get_border();
        let widths = SideOffsets2D::new(
            b.border_top_width.px(),
            b.border_right_width.px(),
            b.border_bottom_width.px(),
            b.border_left_width.px(),
        );
        if widths == SideOffsets2D::zero() {
            return;
        }
        let side = |style, color| wr::BorderSide {
            color: rgba(self.style.resolve_color(color)),
            style: match style {
                BorderStyle::None => wr::BorderStyle::None,
                BorderStyle::Solid => wr::BorderStyle::Solid,
                BorderStyle::Double => wr::BorderStyle::Double,
                BorderStyle::Dotted => wr::BorderStyle::Dotted,
                BorderStyle::Dashed => wr::BorderStyle::Dashed,
                BorderStyle::Hidden => wr::BorderStyle::Hidden,
                BorderStyle::Groove => wr::BorderStyle::Groove,
                BorderStyle::Ridge => wr::BorderStyle::Ridge,
                BorderStyle::Inset => wr::BorderStyle::Inset,
                BorderStyle::Outset => wr::BorderStyle::Outset,
            },
        };
        let details = wr::BorderDetails::Normal(wr::NormalBorder {
            top: side(b.border_top_style, b.border_top_color),
            right: side(b.border_right_style, b.border_right_color),
            bottom: side(b.border_bottom_style, b.border_bottom_color),
            left: side(b.border_left_style, b.border_left_color),
            radius,
            do_aa: true,
        });
        builder.wr.push_border(common, border_rect, widths, details)
    }
}

fn rgba(rgba: cssparser::RGBA) -> wr::ColorF {
    wr::ColorF::new(
        rgba.red_f32(),
        rgba.green_f32(),
        rgba.blue_f32(),
        rgba.alpha_f32(),
    )
}

fn glyphs(glyph_runs: &[Arc<GlyphStore>], mut origin: Vec2<Length>) -> Vec<wr::GlyphInstance> {
    use gfx_traits::ByteIndex;
    use range::Range;

    let mut glyphs = vec![];
    for run in glyph_runs {
        for glyph in run.iter_glyphs_for_byte_range(&Range::new(ByteIndex(0), run.len())) {
            if !run.is_whitespace() {
                let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
                let point = units::LayoutPoint::new(
                    origin.x.px() + glyph_offset.x.to_f32_px(),
                    origin.y.px() + glyph_offset.y.to_f32_px(),
                );
                let glyph = wr::GlyphInstance {
                    index: glyph.id(),
                    point,
                };
                glyphs.push(glyph);
            }
            origin.x += Length::from(glyph.advance());
        }
    }
    glyphs
}

fn cursor(values: &ComputedValues, default: Cursor) -> Option<Cursor> {
    use style::computed_values::pointer_events::T as PointerEvents;
    use style::values::specified::ui::CursorKind;

    let inherited_ui = values.get_inherited_ui();
    if inherited_ui.pointer_events == PointerEvents::None {
        return None;
    }
    Some(match inherited_ui.cursor.keyword {
        CursorKind::Auto => default,
        CursorKind::None => Cursor::None,
        CursorKind::Default => Cursor::Default,
        CursorKind::Pointer => Cursor::Pointer,
        CursorKind::ContextMenu => Cursor::ContextMenu,
        CursorKind::Help => Cursor::Help,
        CursorKind::Progress => Cursor::Progress,
        CursorKind::Wait => Cursor::Wait,
        CursorKind::Cell => Cursor::Cell,
        CursorKind::Crosshair => Cursor::Crosshair,
        CursorKind::Text => Cursor::Text,
        CursorKind::VerticalText => Cursor::VerticalText,
        CursorKind::Alias => Cursor::Alias,
        CursorKind::Copy => Cursor::Copy,
        CursorKind::Move => Cursor::Move,
        CursorKind::NoDrop => Cursor::NoDrop,
        CursorKind::NotAllowed => Cursor::NotAllowed,
        CursorKind::Grab => Cursor::Grab,
        CursorKind::Grabbing => Cursor::Grabbing,
        CursorKind::EResize => Cursor::EResize,
        CursorKind::NResize => Cursor::NResize,
        CursorKind::NeResize => Cursor::NeResize,
        CursorKind::NwResize => Cursor::NwResize,
        CursorKind::SResize => Cursor::SResize,
        CursorKind::SeResize => Cursor::SeResize,
        CursorKind::SwResize => Cursor::SwResize,
        CursorKind::WResize => Cursor::WResize,
        CursorKind::EwResize => Cursor::EwResize,
        CursorKind::NsResize => Cursor::NsResize,
        CursorKind::NeswResize => Cursor::NeswResize,
        CursorKind::NwseResize => Cursor::NwseResize,
        CursorKind::ColResize => Cursor::ColResize,
        CursorKind::RowResize => Cursor::RowResize,
        CursorKind::AllScroll => Cursor::AllScroll,
        CursorKind::ZoomIn => Cursor::ZoomIn,
        CursorKind::ZoomOut => Cursor::ZoomOut,
    })
}
<commit_msg>Refactor creation of `webrender_api::CommonItemProperties`<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::fragments::{BoxFragment, Fragment};
use crate::geom::physical::{Rect, Vec2};
use embedder_traits::Cursor;
use euclid::{Point2D, SideOffsets2D, Size2D};
use gfx::text::glyph::GlyphStore;
use std::sync::Arc;
use style::dom::OpaqueNode;
use style::properties::ComputedValues;
use style::values::computed::{BorderStyle, Length, LengthPercentage};
use style::values::specified::ui::CursorKind;
use webrender_api::{self as wr, units};

// `webrender_api::display_item::ItemTag` is private
type ItemTag = (u64, u16);
type HitInfo = Option<ItemTag>;

pub struct DisplayListBuilder {
    current_space_and_clip: wr::SpaceAndClipInfo,
    pub wr: wr::DisplayListBuilder,
    pub is_contentful: bool,
}

impl DisplayListBuilder {
    pub fn new(pipeline_id: wr::PipelineId, viewport_size: wr::units::LayoutSize) -> Self {
        Self {
            current_space_and_clip: wr::SpaceAndClipInfo::root_scroll(pipeline_id),
            is_contentful: false,
            wr: wr::DisplayListBuilder::new(pipeline_id, viewport_size),
        }
    }

    fn common_properties(
        &self,
        clip_rect: units::LayoutRect,
        hit_info: HitInfo,
    ) -> wr::CommonItemProperties {
        wr::CommonItemProperties {
            clip_rect,
            clip_id: self.current_space_and_clip.clip_id,
            spatial_id: self.current_space_and_clip.spatial_id,
            hit_info,
            // TODO(gw): Make use of the WR backface visibility functionality.
            flags: wr::PrimitiveFlags::default(),
        }
    }
}

/// Contentful paint, for the purpose of
/// https://w3c.github.io/paint-timing/#first-contentful-paint
/// (i.e. the display list contains items of type text,
/// image, non-white canvas or SVG). Used by metrics.
pub struct IsContentful(pub bool);

impl Fragment {
    pub(crate) fn build_display_list(
        &self,
        builder: &mut DisplayListBuilder,
        is_contentful: &mut IsContentful,
        containing_block: &Rect<Length>,
    ) {
        match self {
            Fragment::Box(b) => b.build_display_list(builder, is_contentful, containing_block),
            Fragment::Anonymous(a) => {
                let rect = a
                    .rect
                    .to_physical(a.mode, containing_block)
                    .translate(&containing_block.top_left);
                for child in &a.children {
                    child.build_display_list(builder, is_contentful, &rect)
                }
            },
            Fragment::Text(t) => {
                is_contentful.0 = true;
                let rect = t
                    .rect
                    .to_physical(t.parent_style.writing_mode, containing_block)
                    .translate(&containing_block.top_left);
                let mut baseline_origin = rect.top_left.clone();
                baseline_origin.y += t.ascent;
                let glyphs = glyphs(&t.glyphs, baseline_origin);
                if glyphs.is_empty() {
                    return;
                }
                let hit_info = hit_info(&t.parent_style, t.tag, Cursor::Text);
                let common = builder.common_properties(rect.clone().into(), hit_info);
                let color = t.parent_style.clone_color();
                builder
                    .wr
                    .push_text(&common, rect.into(), &glyphs, t.font_key, rgba(color), None);
            },
            Fragment::Image(i) => {
                use style::computed_values::image_rendering::T as ImageRendering;
                is_contentful.0 = true;
                let rect = i
                    .rect
                    .to_physical(i.style.writing_mode, containing_block)
                    .translate(&containing_block.top_left);
                let hit_info = None;
                let common = builder.common_properties(rect.clone().into(), hit_info);
                builder.wr.push_image(
                    &common,
                    rect.into(),
                    match i.style.get_inherited_box().image_rendering {
                        ImageRendering::Auto => wr::ImageRendering::Auto,
                        ImageRendering::CrispEdges => wr::ImageRendering::CrispEdges,
                        ImageRendering::Pixelated => wr::ImageRendering::Pixelated,
                    },
                    wr::AlphaType::PremultipliedAlpha,
                    i.image_key,
                    wr::ColorF::WHITE,
                );
            },
        }
    }
}

impl BoxFragment {
    fn build_display_list(
        &self,
        builder: &mut DisplayListBuilder,
        is_contentful: &mut IsContentful,
        containing_block: &Rect<Length>,
    ) {
        let border_rect = self
            .border_rect()
            .to_physical(self.style.writing_mode, containing_block)
            .translate(&containing_block.top_left)
            .into();
        let hit_info = hit_info(&self.style, self.tag, Cursor::Default);
        let border_radius = self.border_radius(&border_rect);

        self.background_display_items(builder, hit_info, border_rect);
        self.border_display_items(builder, hit_info, border_rect, border_radius);
        let content_rect = self
            .content_rect
            .to_physical(self.style.writing_mode, containing_block)
            .translate(&containing_block.top_left);
        for child in &self.children {
            child.build_display_list(builder, is_contentful, &content_rect)
        }
    }

    fn border_radius(&self, border_rect: &units::LayoutRect) -> wr::BorderRadius {
        let resolve = |radius: &LengthPercentage, box_size: f32| {
            radius.percentage_relative_to(Length::new(box_size)).px()
        };
        let corner = |corner: &style::values::computed::BorderCornerRadius| {
            Size2D::new(
                resolve(&corner.0.width.0, border_rect.size.width),
                resolve(&corner.0.height.0, border_rect.size.height),
            )
        };
        let b = self.style.get_border();
        wr::BorderRadius {
            top_left: corner(&b.border_top_left_radius),
            top_right: corner(&b.border_top_right_radius),
            bottom_right: corner(&b.border_bottom_right_radius),
            bottom_left: corner(&b.border_bottom_left_radius),
        }
    }

    fn background_display_items(
        &self,
        builder: &mut DisplayListBuilder,
        hit_info: HitInfo,
        border_rect: units::LayoutRect,
    ) {
        let background_color = self
            .style
            .resolve_color(self.style.clone_background_color());
        if background_color.alpha > 0 || hit_info.is_some() {
            let common = builder.common_properties(border_rect, hit_info);
            builder.wr.push_rect(&common, rgba(background_color))
        }
    }

    fn border_display_items(
        &self,
        builder: &mut DisplayListBuilder,
        hit_info: HitInfo,
        border_rect: units::LayoutRect,
        radius: wr::BorderRadius,
    ) {
        let b = self.style.get_border();
        let widths = SideOffsets2D::new(
            b.border_top_width.px(),
            b.border_right_width.px(),
            b.border_bottom_width.px(),
            b.border_left_width.px(),
        );
        if widths == SideOffsets2D::zero() {
            return;
        }
        let side = |style, color| wr::BorderSide {
            color: rgba(self.style.resolve_color(color)),
            style: match style {
                BorderStyle::None => wr::BorderStyle::None,
                BorderStyle::Solid => wr::BorderStyle::Solid,
                BorderStyle::Double => wr::BorderStyle::Double,
                BorderStyle::Dotted => wr::BorderStyle::Dotted,
                BorderStyle::Dashed => wr::BorderStyle::Dashed,
                BorderStyle::Hidden => wr::BorderStyle::Hidden,
                BorderStyle::Groove => wr::BorderStyle::Groove,
                BorderStyle::Ridge => wr::BorderStyle::Ridge,
                BorderStyle::Inset => wr::BorderStyle::Inset,
                BorderStyle::Outset => wr::BorderStyle::Outset,
            },
        };
        let common = builder.common_properties(border_rect, hit_info);
        let details = wr::BorderDetails::Normal(wr::NormalBorder {
            top: side(b.border_top_style, b.border_top_color),
            right: side(b.border_right_style, b.border_right_color),
            bottom: side(b.border_bottom_style, b.border_bottom_color),
            left: side(b.border_left_style, b.border_left_color),
            radius,
            do_aa: true,
        });
        builder
            .wr
            .push_border(&common, border_rect, widths, details)
    }
}

fn rgba(rgba: cssparser::RGBA) -> wr::ColorF {
    wr::ColorF::new(
        rgba.red_f32(),
        rgba.green_f32(),
        rgba.blue_f32(),
        rgba.alpha_f32(),
    )
}

fn glyphs(glyph_runs: &[Arc<GlyphStore>], mut origin: Vec2<Length>) -> Vec<wr::GlyphInstance> {
    use gfx_traits::ByteIndex;
    use range::Range;

    let mut glyphs = vec![];
    for run in glyph_runs {
        for glyph in run.iter_glyphs_for_byte_range(&Range::new(ByteIndex(0), run.len())) {
            if !run.is_whitespace() {
                let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
                let point = units::LayoutPoint::new(
                    origin.x.px() + glyph_offset.x.to_f32_px(),
                    origin.y.px() + glyph_offset.y.to_f32_px(),
                );
                let glyph = wr::GlyphInstance {
                    index: glyph.id(),
                    point,
                };
                glyphs.push(glyph);
            }
            origin.x += Length::from(glyph.advance());
        }
    }
    glyphs
}

fn hit_info(style: &ComputedValues, tag: OpaqueNode, auto_cursor: Cursor) -> HitInfo {
    use style::computed_values::pointer_events::T as PointerEvents;

    let inherited_ui = style.get_inherited_ui();
    if inherited_ui.pointer_events == PointerEvents::None {
        None
    } else {
        let cursor = cursor(inherited_ui.cursor.keyword, auto_cursor);
        Some((tag.0 as u64, cursor as u16))
    }
}

fn cursor(kind: CursorKind, auto_cursor: Cursor) -> Cursor {
    match kind {
        CursorKind::Auto => auto_cursor,
        CursorKind::None => Cursor::None,
        CursorKind::Default => Cursor::Default,
        CursorKind::Pointer => Cursor::Pointer,
        CursorKind::ContextMenu => Cursor::ContextMenu,
        CursorKind::Help => Cursor::Help,
        CursorKind::Progress => Cursor::Progress,
        CursorKind::Wait => Cursor::Wait,
        CursorKind::Cell => Cursor::Cell,
        CursorKind::Crosshair => Cursor::Crosshair,
        CursorKind::Text => Cursor::Text,
        CursorKind::VerticalText => Cursor::VerticalText,
        CursorKind::Alias => Cursor::Alias,
        CursorKind::Copy => Cursor::Copy,
        CursorKind::Move => Cursor::Move,
        CursorKind::NoDrop => Cursor::NoDrop,
        CursorKind::NotAllowed => Cursor::NotAllowed,
        CursorKind::Grab => Cursor::Grab,
        CursorKind::Grabbing => Cursor::Grabbing,
        CursorKind::EResize => Cursor::EResize,
        CursorKind::NResize => Cursor::NResize,
        CursorKind::NeResize => Cursor::NeResize,
        CursorKind::NwResize => Cursor::NwResize,
        CursorKind::SResize => Cursor::SResize,
        CursorKind::SeResize => Cursor::SeResize,
        CursorKind::SwResize => Cursor::SwResize,
        CursorKind::WResize => Cursor::WResize,
        CursorKind::EwResize => Cursor::EwResize,
        CursorKind::NsResize => Cursor::NsResize,
        CursorKind::NeswResize => Cursor::NeswResize,
        CursorKind::NwseResize => Cursor::NwseResize,
        CursorKind::ColResize => Cursor::ColResize,
        CursorKind::RowResize => Cursor::RowResize,
        CursorKind::AllScroll => Cursor::AllScroll,
        CursorKind::ZoomIn => Cursor::ZoomIn,
        CursorKind::ZoomOut => Cursor::ZoomOut,
    }
}
<|endoftext|>


<commit_before>use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_map::HashMap;
use std::str;
use std::sync::Arc;

use regex::Regex;

use core::{SourceMap, Package, PackageId, PackageSet, Resolve, Target, Profile};
use util::{self, CargoResult, ChainError, internal, Config, profile};
use util::human;

use super::TargetConfig;
use super::custom_build::BuildState;
use super::fingerprint::Fingerprint;
use super::layout::{Layout, LayoutProxy};
use super::{Kind, Compilation, BuildConfig};
use super::{ProcessEngine, ExecEngine};

#[derive(Debug, Copy)]
pub enum Platform {
    Target,
    Plugin,
    PluginAndTarget,
}

pub struct Context<'a, 'b: 'a> {
    pub config: &'a Config<'b>,
    pub resolve: &'a Resolve,
    pub sources: &'a SourceMap<'a>,
    pub compilation: Compilation,
    pub build_state: Arc<BuildState>,
    pub exec_engine: Arc<Box<ExecEngine>>,
    pub fingerprints: HashMap<(&'a PackageId, &'a Target, Kind), Fingerprint>,

    env: &'a str,
    host: Layout,
    target: Option<Layout>,
    target_triple: String,
    host_dylib: Option<(String, String)>,
    host_exe: String,
    package_set: &'a PackageSet,
    target_dylib: Option<(String, String)>,
    target_exe: String,
    requirements: HashMap<(&'a PackageId, &'a str), Platform>,
    build_config: BuildConfig,
}

impl<'a, 'b: 'a> Context<'a, 'b> {
    pub fn new(env: &'a str,
               resolve: &'a Resolve,
               sources: &'a SourceMap<'a>,
               deps: &'a PackageSet,
               config: &'a Config<'b>,
               host: Layout,
               target_layout: Option<Layout>,
               root_pkg: &Package,
               build_config: BuildConfig) -> CargoResult<Context<'a, 'b>> {
        let target = build_config.requested_target.clone();
        let target = target.as_ref().map(|s| &s[]);
        let (target_dylib, target_exe) = try!(Context::filename_parts(target));
        let (host_dylib, host_exe) = if build_config.requested_target.is_none() {
            (target_dylib.clone(), target_exe.clone())
        } else {
            try!(Context::filename_parts(None))
        };
        let target_triple = target.unwrap_or(config.rustc_host()).to_string();
        Ok(Context {
            target_triple: target_triple,
            env: env,
            host: host,
            target: target_layout,
            resolve: resolve,
            sources: sources,
            package_set: deps,
            config: config,
            target_dylib: target_dylib,
            target_exe: target_exe,
            host_dylib: host_dylib,
            host_exe: host_exe,
            requirements: HashMap::new(),
            compilation: Compilation::new(root_pkg),
            build_state: Arc::new(BuildState::new(build_config.clone(), deps)),
            build_config: build_config,
            exec_engine: Arc::new(Box::new(ProcessEngine) as Box<ExecEngine>),
            fingerprints: HashMap::new(),
        })
    }

    /// Run `rustc` to discover the dylib prefix/suffix for the target
    /// specified as well as the exe suffix
    fn filename_parts(target: Option<&str>)
                      -> CargoResult<(Option<(String, String)>, String)> {
        let process = try!(util::process("rustc"))
                           .arg("-")
                           .arg("--crate-name").arg("-")
                           .arg("--crate-type").arg("dylib")
                           .arg("--crate-type").arg("bin")
                           .arg("--print=file-names");
        let process = match target {
            Some(s) => process.arg("--target").arg(s),
            None => process,
        };
        let output = try!(process.exec_with_output());

        let error = str::from_utf8(&output.error).unwrap();
        let output = str::from_utf8(&output.output).unwrap();
        let mut lines = output.lines();
        let nodylib = Regex::new("unsupported crate type.*dylib").unwrap();
        let nobin = Regex::new("unsupported crate type.*bin").unwrap();
        let dylib = if nodylib.is_match(error) {
            None
        } else {
            let dylib_parts: Vec<&str> = lines.next().unwrap().trim()
                                              .split('-').collect();
            assert!(dylib_parts.len() == 2,
                    "rustc --print-file-name output has changed");
            Some((dylib_parts[0].to_string(), dylib_parts[1].to_string()))
        };

        let exe_suffix = if nobin.is_match(error) {
            String::new()
        } else {
            lines.next().unwrap().trim()
                 .split('-').skip(1).next().unwrap().to_string()
        };
        Ok((dylib, exe_suffix.to_string()))
    }

    /// Prepare this context, ensuring that all filesystem directories are in
    /// place.
    pub fn prepare(&mut self, pkg: &'a Package) -> CargoResult<()> {
        let _p = profile::start("preparing layout");

        try!(self.host.prepare().chain_error(|| {
            internal(format!("couldn't prepare build directories for `{}`",
                             pkg.name()))
        }));
        match self.target {
            Some(ref mut target) => {
                try!(target.prepare().chain_error(|| {
                    internal(format!("couldn't prepare build directories \
                                      for `{}`", pkg.name()))
                }));
            }
            None => {}
        }

        let targets = pkg.targets().iter();
        for target in targets.filter(|t| t.profile().is_compile()) {
            self.build_requirements(pkg, target, Platform::Target);
        }

        let jobs = self.jobs();
        self.compilation.extra_env.insert("NUM_JOBS".to_string(),
                                          Some(jobs.to_string()));
        self.compilation.root_output =
                self.layout(pkg, Kind::Target).proxy().dest().clone();
        self.compilation.deps_output =
                self.layout(pkg, Kind::Target).proxy().deps().clone();

        return Ok(());
    }

    fn build_requirements(&mut self, pkg: &'a Package, target: &'a Target,
                          req: Platform) {
        let req = if target.profile().is_for_host() {Platform::Plugin} else {req};
        match self.requirements.entry((pkg.package_id(), target.name())) {
            Occupied(mut entry) => match (*entry.get(), req) {
                (Platform::Plugin, Platform::Plugin) |
                (Platform::PluginAndTarget, Platform::Plugin) |
                (Platform::Target, Platform::Target) |
                (Platform::PluginAndTarget, Platform::Target) |
                (Platform::PluginAndTarget, Platform::PluginAndTarget) => return,
                _ => *entry.get_mut() = entry.get().combine(req),
            },
            Vacant(entry) => { entry.insert(req); }
        };

        for &(pkg, dep) in self.dep_targets(pkg, target).iter() {
            self.build_requirements(pkg, dep, req);
        }

        match pkg.targets().iter().find(|t| t.profile().is_custom_build()) {
            Some(custom_build) => {
                self.build_requirements(pkg, custom_build, Platform::Plugin);
            }
            None => {}
        }
    }

    pub fn get_requirement(&self, pkg: &'a Package,
                           target: &'a Target) -> Platform {
        let default = if target.profile().is_for_host() {
            Platform::Plugin
        } else {
            Platform::Target
        };
        self.requirements.get(&(pkg.package_id(), target.name()))
            .map(|a| *a).unwrap_or(default)
    }

    /// Returns the appropriate directory layout for either a plugin or not.
    pub fn layout(&self, pkg: &Package, kind: Kind) -> LayoutProxy {
        let primary = pkg.package_id() == self.resolve.root();
        match kind {
            Kind::Host => LayoutProxy::new(&self.host, primary),
            Kind::Target =>  LayoutProxy::new(self.target.as_ref()
                                                .unwrap_or(&self.host),
                                            primary),
        }
    }

    /// Returns the appropriate output directory for the specified package and
    /// target.
    pub fn out_dir(&self, pkg: &Package, kind: Kind, target: &Target) -> Path {
        let out_dir = self.layout(pkg, kind);
        if target.profile().is_custom_build() {
            out_dir.build(pkg)
        } else if target.is_example() {
            out_dir.examples().clone()
        } else {
            out_dir.root().clone()
        }
    }

    /// Return the (prefix, suffix) pair for dynamic libraries.
    ///
    /// If `plugin` is true, the pair corresponds to the host platform,
    /// otherwise it corresponds to the target platform.
    fn dylib(&self, kind: Kind) -> CargoResult<(&str, &str)> {
        let (triple, pair) = if kind == Kind::Host {
            (self.config.rustc_host(), &self.host_dylib)
        } else {
            (self.target_triple.as_slice(), &self.target_dylib)
        };
        match *pair {
            None => return Err(human(format!("dylib outputs are not supported \
                                              for {}", triple))),
            Some((ref s1, ref s2)) => Ok((s1, s2)),
        }
    }

    /// Return the target triple which this context is targeting.
    pub fn target_triple(&self) -> &str {
        &self.target_triple
    }

    /// Return the exact filename of the target.
    pub fn target_filenames(&self, target: &Target) -> CargoResult<Vec<String>> {
        let stem = target.file_stem();

        let mut ret = Vec::new();
        if target.is_example() || target.is_bin() ||
           target.profile().is_test() {
            ret.push(format!("{}{}", stem,
                             if target.profile().is_for_host() {
                                 &self.host_exe
                             } else {
                                 &self.target_exe
                             }));
        } else {
            if target.is_dylib() {
                let plugin = target.profile().is_for_host();
                let kind = if plugin {Kind::Host} else {Kind::Target};
                let (prefix, suffix) = try!(self.dylib(kind));
                ret.push(format!("{}{}{}", prefix, stem, suffix));
            }
            if target.is_rlib() {
                ret.push(format!("lib{}.rlib", stem));
            }
            if target.is_staticlib() {
                ret.push(format!("lib{}.a", stem));
            }
        }
        assert!(ret.len() > 0);
        return Ok(ret);
    }

    /// For a package, return all targets which are registered as dependencies
    /// for that package.
    pub fn dep_targets(&self, pkg: &Package, target: &Target)
                       -> Vec<(&'a Package, &'a Target)> {
        let deps = match self.resolve.deps(pkg.package_id()) {
            None => return vec!(),
            Some(deps) => deps,
        };
        deps.map(|id| self.get_package(id)).filter(|dep| {
            let pkg_dep = pkg.dependencies().iter().find(|d| {
                d.name() == dep.name()
            }).unwrap();

            // If this target is a build command, then we only want build
            // dependencies, otherwise we want everything *other than* build
            // dependencies.
            let is_correct_dep =
                target.profile().is_custom_build() == pkg_dep.is_build();

            // If this dependency is *not* a transitive dependency, then it
            // only applies to test/example targets
            let is_actual_dep = pkg_dep.is_transitive() ||
                                target.profile().is_test() ||
                                target.is_example();

            is_correct_dep && is_actual_dep
        }).filter_map(|pkg| {
            pkg.targets().iter().find(|&t| self.is_relevant_target(t))
               .map(|t| (pkg, t))
        }).collect()
    }

    /// Gets a package for the given package id.
    pub fn get_package(&self, id: &PackageId) -> &'a Package {
        self.package_set.iter()
            .find(|pkg| id == pkg.package_id())
            .expect("Should have found package")
    }

    pub fn env(&self) -> &str {
        // The "doc-all" environment just means to document everything (see
        // below), but we want to canonicalize that the the "doc" profile
        // environment, so do that here.
        if self.env == "doc-all" {"doc"} else {self.env}
    }

    pub fn is_relevant_target(&self, target: &Target) -> bool {
        target.is_lib() && match self.env {
            "doc" | "test" => target.profile().is_compile(),
            // doc-all == document everything, so look for doc targets and
            //            compile targets in dependencies
            "doc-all" => target.profile().is_compile() ||
                         (target.profile().env() == "doc" &&
                          target.profile().is_doc()),
            _ => target.profile().env() == self.env &&
                 !target.profile().is_test(),
        }
    }

    /// Get the user-specified linker for a particular host or target
    pub fn linker(&self, kind: Kind) -> Option<&str> {
        self.target_config(kind).linker.as_ref().map(|s| s.as_slice())
    }

    /// Get the user-specified `ar` program for a particular host or target
    pub fn ar(&self, kind: Kind) -> Option<&str> {
        self.target_config(kind).ar.as_ref().map(|s| s.as_slice())
    }

    /// Get the target configuration for a particular host or target
    fn target_config(&self, kind: Kind) -> &TargetConfig {
        match kind {
            Kind::Host => &self.build_config.host,
            Kind::Target => &self.build_config.target,
        }
    }

    /// Number of jobs specified for this build
    pub fn jobs(&self) -> u32 { self.build_config.jobs }

    /// Requested (not actual) target for the build
    pub fn requested_target(&self) -> Option<&str> {
        self.build_config.requested_target.as_ref().map(|s| &s[])
    }

    /// Calculate the actual profile to use for a target's compliation.
    ///
    /// This may involve overriding some options such as debug information,
    /// rpath, opt level, etc.
    pub fn profile(&self, target: &Target) -> Profile {
        let mut profile = target.profile().clone();
        let root_package = self.get_package(self.resolve.root());
        for target in root_package.manifest().targets().iter() {
            let root_profile = target.profile();
            if root_profile.env() != profile.env() { continue }
            profile = profile.set_opt_level(root_profile.opt_level())
                             .set_debug(root_profile.debug())
                             .set_rpath(root_profile.rpath())
        }
        profile
    }
}

impl Platform {
    pub fn combine(self, other: Platform) -> Platform {
        match (self, other) {
            (Platform::Target, Platform::Target) => Platform::Target,
            (Platform::Plugin, Platform::Plugin) => Platform::Plugin,
            _ => Platform::PluginAndTarget,
        }
    }

    pub fn includes(self, kind: Kind) -> bool {
        match (self, kind) {
            (Platform::PluginAndTarget, _) |
            (Platform::Target, Kind::Target) |
            (Platform::Plugin, Kind::Host) => true,
            _ => false,
        }
    }

    pub fn each_kind<F>(self, mut f: F) where F: FnMut(Kind) {
        match self {
            Platform::Target => f(Kind::Target),
            Platform::Plugin => f(Kind::Host),
            Platform::PluginAndTarget => { f(Kind::Target); f(Kind::Host); }
        }
    }
}
<commit_msg>Auto merge of #1344 - lfairy:dehyphenator, r=alexcrichton<commit_after>use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_map::HashMap;
use std::str;
use std::sync::Arc;

use regex::Regex;

use core::{SourceMap, Package, PackageId, PackageSet, Resolve, Target, Profile};
use util::{self, CargoResult, ChainError, internal, Config, profile};
use util::human;

use super::TargetConfig;
use super::custom_build::BuildState;
use super::fingerprint::Fingerprint;
use super::layout::{Layout, LayoutProxy};
use super::{Kind, Compilation, BuildConfig};
use super::{ProcessEngine, ExecEngine};

#[derive(Debug, Copy)]
pub enum Platform {
    Target,
    Plugin,
    PluginAndTarget,
}

pub struct Context<'a, 'b: 'a> {
    pub config: &'a Config<'b>,
    pub resolve: &'a Resolve,
    pub sources: &'a SourceMap<'a>,
    pub compilation: Compilation,
    pub build_state: Arc<BuildState>,
    pub exec_engine: Arc<Box<ExecEngine>>,
    pub fingerprints: HashMap<(&'a PackageId, &'a Target, Kind), Fingerprint>,

    env: &'a str,
    host: Layout,
    target: Option<Layout>,
    target_triple: String,
    host_dylib: Option<(String, String)>,
    host_exe: String,
    package_set: &'a PackageSet,
    target_dylib: Option<(String, String)>,
    target_exe: String,
    requirements: HashMap<(&'a PackageId, &'a str), Platform>,
    build_config: BuildConfig,
}

impl<'a, 'b: 'a> Context<'a, 'b> {
    pub fn new(env: &'a str,
               resolve: &'a Resolve,
               sources: &'a SourceMap<'a>,
               deps: &'a PackageSet,
               config: &'a Config<'b>,
               host: Layout,
               target_layout: Option<Layout>,
               root_pkg: &Package,
               build_config: BuildConfig) -> CargoResult<Context<'a, 'b>> {
        let target = build_config.requested_target.clone();
        let target = target.as_ref().map(|s| &s[]);
        let (target_dylib, target_exe) = try!(Context::filename_parts(target));
        let (host_dylib, host_exe) = if build_config.requested_target.is_none() {
            (target_dylib.clone(), target_exe.clone())
        } else {
            try!(Context::filename_parts(None))
        };
        let target_triple = target.unwrap_or(config.rustc_host()).to_string();
        Ok(Context {
            target_triple: target_triple,
            env: env,
            host: host,
            target: target_layout,
            resolve: resolve,
            sources: sources,
            package_set: deps,
            config: config,
            target_dylib: target_dylib,
            target_exe: target_exe,
            host_dylib: host_dylib,
            host_exe: host_exe,
            requirements: HashMap::new(),
            compilation: Compilation::new(root_pkg),
            build_state: Arc::new(BuildState::new(build_config.clone(), deps)),
            build_config: build_config,
            exec_engine: Arc::new(Box::new(ProcessEngine) as Box<ExecEngine>),
            fingerprints: HashMap::new(),
        })
    }

    /// Run `rustc` to discover the dylib prefix/suffix for the target
    /// specified as well as the exe suffix
    fn filename_parts(target: Option<&str>)
                      -> CargoResult<(Option<(String, String)>, String)> {
        let process = try!(util::process("rustc"))
                           .arg("-")
                           .arg("--crate-name").arg("_")
                           .arg("--crate-type").arg("dylib")
                           .arg("--crate-type").arg("bin")
                           .arg("--print=file-names");
        let process = match target {
            Some(s) => process.arg("--target").arg(s),
            None => process,
        };
        let output = try!(process.exec_with_output());

        let error = str::from_utf8(&output.error).unwrap();
        let output = str::from_utf8(&output.output).unwrap();
        let mut lines = output.lines();
        let nodylib = Regex::new("unsupported crate type.*dylib").unwrap();
        let nobin = Regex::new("unsupported crate type.*bin").unwrap();
        let dylib = if nodylib.is_match(error) {
            None
        } else {
            let dylib_parts: Vec<&str> = lines.next().unwrap().trim()
                                              .split('_').collect();
            assert!(dylib_parts.len() == 2,
                    "rustc --print-file-name output has changed");
            Some((dylib_parts[0].to_string(), dylib_parts[1].to_string()))
        };

        let exe_suffix = if nobin.is_match(error) {
            String::new()
        } else {
            lines.next().unwrap().trim()
                 .split('_').skip(1).next().unwrap().to_string()
        };
        Ok((dylib, exe_suffix.to_string()))
    }

    /// Prepare this context, ensuring that all filesystem directories are in
    /// place.
    pub fn prepare(&mut self, pkg: &'a Package) -> CargoResult<()> {
        let _p = profile::start("preparing layout");

        try!(self.host.prepare().chain_error(|| {
            internal(format!("couldn't prepare build directories for `{}`",
                             pkg.name()))
        }));
        match self.target {
            Some(ref mut target) => {
                try!(target.prepare().chain_error(|| {
                    internal(format!("couldn't prepare build directories \
                                      for `{}`", pkg.name()))
                }));
            }
            None => {}
        }

        let targets = pkg.targets().iter();
        for target in targets.filter(|t| t.profile().is_compile()) {
            self.build_requirements(pkg, target, Platform::Target);
        }

        let jobs = self.jobs();
        self.compilation.extra_env.insert("NUM_JOBS".to_string(),
                                          Some(jobs.to_string()));
        self.compilation.root_output =
                self.layout(pkg, Kind::Target).proxy().dest().clone();
        self.compilation.deps_output =
                self.layout(pkg, Kind::Target).proxy().deps().clone();

        return Ok(());
    }

    fn build_requirements(&mut self, pkg: &'a Package, target: &'a Target,
                          req: Platform) {
        let req = if target.profile().is_for_host() {Platform::Plugin} else {req};
        match self.requirements.entry((pkg.package_id(), target.name())) {
            Occupied(mut entry) => match (*entry.get(), req) {
                (Platform::Plugin, Platform::Plugin) |
                (Platform::PluginAndTarget, Platform::Plugin) |
                (Platform::Target, Platform::Target) |
                (Platform::PluginAndTarget, Platform::Target) |
                (Platform::PluginAndTarget, Platform::PluginAndTarget) => return,
                _ => *entry.get_mut() = entry.get().combine(req),
            },
            Vacant(entry) => { entry.insert(req); }
        };

        for &(pkg, dep) in self.dep_targets(pkg, target).iter() {
            self.build_requirements(pkg, dep, req);
        }

        match pkg.targets().iter().find(|t| t.profile().is_custom_build()) {
            Some(custom_build) => {
                self.build_requirements(pkg, custom_build, Platform::Plugin);
            }
            None => {}
        }
    }

    pub fn get_requirement(&self, pkg: &'a Package,
                           target: &'a Target) -> Platform {
        let default = if target.profile().is_for_host() {
            Platform::Plugin
        } else {
            Platform::Target
        };
        self.requirements.get(&(pkg.package_id(), target.name()))
            .map(|a| *a).unwrap_or(default)
    }

    /// Returns the appropriate directory layout for either a plugin or not.
    pub fn layout(&self, pkg: &Package, kind: Kind) -> LayoutProxy {
        let primary = pkg.package_id() == self.resolve.root();
        match kind {
            Kind::Host => LayoutProxy::new(&self.host, primary),
            Kind::Target =>  LayoutProxy::new(self.target.as_ref()
                                                .unwrap_or(&self.host),
                                            primary),
        }
    }

    /// Returns the appropriate output directory for the specified package and
    /// target.
    pub fn out_dir(&self, pkg: &Package, kind: Kind, target: &Target) -> Path {
        let out_dir = self.layout(pkg, kind);
        if target.profile().is_custom_build() {
            out_dir.build(pkg)
        } else if target.is_example() {
            out_dir.examples().clone()
        } else {
            out_dir.root().clone()
        }
    }

    /// Return the (prefix, suffix) pair for dynamic libraries.
    ///
    /// If `plugin` is true, the pair corresponds to the host platform,
    /// otherwise it corresponds to the target platform.
    fn dylib(&self, kind: Kind) -> CargoResult<(&str, &str)> {
        let (triple, pair) = if kind == Kind::Host {
            (self.config.rustc_host(), &self.host_dylib)
        } else {
            (self.target_triple.as_slice(), &self.target_dylib)
        };
        match *pair {
            None => return Err(human(format!("dylib outputs are not supported \
                                              for {}", triple))),
            Some((ref s1, ref s2)) => Ok((s1, s2)),
        }
    }

    /// Return the target triple which this context is targeting.
    pub fn target_triple(&self) -> &str {
        &self.target_triple
    }

    /// Return the exact filename of the target.
    pub fn target_filenames(&self, target: &Target) -> CargoResult<Vec<String>> {
        let stem = target.file_stem();

        let mut ret = Vec::new();
        if target.is_example() || target.is_bin() ||
           target.profile().is_test() {
            ret.push(format!("{}{}", stem,
                             if target.profile().is_for_host() {
                                 &self.host_exe
                             } else {
                                 &self.target_exe
                             }));
        } else {
            if target.is_dylib() {
                let plugin = target.profile().is_for_host();
                let kind = if plugin {Kind::Host} else {Kind::Target};
                let (prefix, suffix) = try!(self.dylib(kind));
                ret.push(format!("{}{}{}", prefix, stem, suffix));
            }
            if target.is_rlib() {
                ret.push(format!("lib{}.rlib", stem));
            }
            if target.is_staticlib() {
                ret.push(format!("lib{}.a", stem));
            }
        }
        assert!(ret.len() > 0);
        return Ok(ret);
    }

    /// For a package, return all targets which are registered as dependencies
    /// for that package.
    pub fn dep_targets(&self, pkg: &Package, target: &Target)
                       -> Vec<(&'a Package, &'a Target)> {
        let deps = match self.resolve.deps(pkg.package_id()) {
            None => return vec!(),
            Some(deps) => deps,
        };
        deps.map(|id| self.get_package(id)).filter(|dep| {
            let pkg_dep = pkg.dependencies().iter().find(|d| {
                d.name() == dep.name()
            }).unwrap();

            // If this target is a build command, then we only want build
            // dependencies, otherwise we want everything *other than* build
            // dependencies.
            let is_correct_dep =
                target.profile().is_custom_build() == pkg_dep.is_build();

            // If this dependency is *not* a transitive dependency, then it
            // only applies to test/example targets
            let is_actual_dep = pkg_dep.is_transitive() ||
                                target.profile().is_test() ||
                                target.is_example();

            is_correct_dep && is_actual_dep
        }).filter_map(|pkg| {
            pkg.targets().iter().find(|&t| self.is_relevant_target(t))
               .map(|t| (pkg, t))
        }).collect()
    }

    /// Gets a package for the given package id.
    pub fn get_package(&self, id: &PackageId) -> &'a Package {
        self.package_set.iter()
            .find(|pkg| id == pkg.package_id())
            .expect("Should have found package")
    }

    pub fn env(&self) -> &str {
        // The "doc-all" environment just means to document everything (see
        // below), but we want to canonicalize that the the "doc" profile
        // environment, so do that here.
        if self.env == "doc-all" {"doc"} else {self.env}
    }

    pub fn is_relevant_target(&self, target: &Target) -> bool {
        target.is_lib() && match self.env {
            "doc" | "test" => target.profile().is_compile(),
            // doc-all == document everything, so look for doc targets and
            //            compile targets in dependencies
            "doc-all" => target.profile().is_compile() ||
                         (target.profile().env() == "doc" &&
                          target.profile().is_doc()),
            _ => target.profile().env() == self.env &&
                 !target.profile().is_test(),
        }
    }

    /// Get the user-specified linker for a particular host or target
    pub fn linker(&self, kind: Kind) -> Option<&str> {
        self.target_config(kind).linker.as_ref().map(|s| s.as_slice())
    }

    /// Get the user-specified `ar` program for a particular host or target
    pub fn ar(&self, kind: Kind) -> Option<&str> {
        self.target_config(kind).ar.as_ref().map(|s| s.as_slice())
    }

    /// Get the target configuration for a particular host or target
    fn target_config(&self, kind: Kind) -> &TargetConfig {
        match kind {
            Kind::Host => &self.build_config.host,
            Kind::Target => &self.build_config.target,
        }
    }

    /// Number of jobs specified for this build
    pub fn jobs(&self) -> u32 { self.build_config.jobs }

    /// Requested (not actual) target for the build
    pub fn requested_target(&self) -> Option<&str> {
        self.build_config.requested_target.as_ref().map(|s| &s[])
    }

    /// Calculate the actual profile to use for a target's compliation.
    ///
    /// This may involve overriding some options such as debug information,
    /// rpath, opt level, etc.
    pub fn profile(&self, target: &Target) -> Profile {
        let mut profile = target.profile().clone();
        let root_package = self.get_package(self.resolve.root());
        for target in root_package.manifest().targets().iter() {
            let root_profile = target.profile();
            if root_profile.env() != profile.env() { continue }
            profile = profile.set_opt_level(root_profile.opt_level())
                             .set_debug(root_profile.debug())
                             .set_rpath(root_profile.rpath())
        }
        profile
    }
}

impl Platform {
    pub fn combine(self, other: Platform) -> Platform {
        match (self, other) {
            (Platform::Target, Platform::Target) => Platform::Target,
            (Platform::Plugin, Platform::Plugin) => Platform::Plugin,
            _ => Platform::PluginAndTarget,
        }
    }

    pub fn includes(self, kind: Kind) -> bool {
        match (self, kind) {
            (Platform::PluginAndTarget, _) |
            (Platform::Target, Kind::Target) |
            (Platform::Plugin, Kind::Host) => true,
            _ => false,
        }
    }

    pub fn each_kind<F>(self, mut f: F) where F: FnMut(Kind) {
        match self {
            Platform::Target => f(Kind::Target),
            Platform::Plugin => f(Kind::Host),
            Platform::PluginAndTarget => { f(Kind::Target); f(Kind::Host); }
        }
    }
}
<|endoftext|>