<commit_before>use std::iter::FromIterator;
use std::mem;
use union_find::{Union, UnionFind, UnionResult, QuickFindUf as Uf};
use slsr_core::puzzle::{Edge, Puzzle, Side};
use slsr_core::geom::{CellId, Geom, Point, Move, OUTSIDE_CELL_ID};

use {Error, State, SolverResult};
use model::side_map::SideMap;

#[derive(Debug)]
pub struct Area {
    coord: CellId,
    side: State<Side>,
    unknown_edge: Vec<CellId>,
    sum_of_hint: u32,
    size: usize,
}

impl Clone for Area {
    fn clone(&self) -> Area {
        Area {
            coord: self.coord,
            side: self.side,
            unknown_edge: self.unknown_edge.clone(),
            sum_of_hint: self.sum_of_hint,
            size: self.size,
        }
    }

    fn clone_from(&mut self, other: &Area) {
        self.coord = other.coord;
        self.side = other.side;
        self.unknown_edge.clone_from(&other.unknown_edge);
        self.sum_of_hint = other.sum_of_hint;
        self.size = other.size;
    }
}

impl Area {
    pub fn coord(&self) -> CellId {
        self.coord
    }
    pub fn side(&self) -> State<Side> {
        self.side
    }
    pub fn unknown_edge(&self) -> &[CellId] {
        &self.unknown_edge
    }
    pub fn sum_of_hint(&self) -> u32 {
        self.sum_of_hint
    }
}

impl Area {
    fn new(p: Point, puzzle: &Puzzle, side_map: &mut SideMap) -> Area {
        let cp = puzzle.point_to_cellid(p);
        let sum = puzzle.hint(p).unwrap_or(0) as u32;

        let mut edge = vec![];
        if cp != OUTSIDE_CELL_ID {
            for &r in &Move::ALL_DIRECTIONS {
                let cp_r = puzzle.point_to_cellid(p + r);
                if side_map.get_edge(cp, cp_r) == State::Unknown {
                    edge.push(cp_r);
                }
            }
        } else {
            let points = puzzle.points_in_column(0)
                               .chain(puzzle.points_in_column(puzzle.column() - 1))
                               .chain(puzzle.points_in_row(0))
                               .chain(puzzle.points_in_row(puzzle.row() - 1));
            for p2 in points {
                let cp2 = puzzle.point_to_cellid(p2);
                if side_map.get_edge(cp, cp2) == State::Unknown {
                    edge.push(cp2);
                }
            }
        }
        edge.sort();
        edge.dedup();

        Area {
            coord: cp,
            side: side_map.get_side(cp),
            unknown_edge: edge,
            sum_of_hint: sum,
            size: 1,
        }
    }
}

impl Union for Area {
    fn union(mut lval: Area, mut rval: Area) -> UnionResult<Area> {
        let coord = if lval.coord < rval.coord {
            lval.coord
        } else {
            rval.coord
        };
        let side = match (lval.side, rval.side) {
            (State::Conflict, _) | (_, State::Conflict) => State::Conflict,
            (State::Unknown, x) | (x, State::Unknown) => x,
            (State::Fixed(l), State::Fixed(r)) => {
                if l == r {
                    State::Fixed(l)
                } else {
                    State::Conflict
                }
            }
        };
        let unknown_edge = {
            if lval.unknown_edge.is_empty() {
                rval.unknown_edge
            } else {
                lval.unknown_edge.append(&mut rval.unknown_edge);
                lval.unknown_edge
            }
        };
        let area = Area {
            coord: coord,
            side: side,
            unknown_edge: unknown_edge,
            sum_of_hint: lval.sum_of_hint + rval.sum_of_hint,
            size: lval.size + rval.size,
        };
        if lval.size >= rval.size {
            UnionResult::Left(area)
        } else {
            UnionResult::Right(area)
        }
    }
}

#[derive(Debug)]
pub struct ConnectMap {
    sum_of_hint: u32,
    uf: Uf<Area>,
}

impl Clone for ConnectMap {
    fn clone(&self) -> ConnectMap {
        ConnectMap {
            sum_of_hint: self.sum_of_hint,
            uf: self.uf.clone(),
        }
    }

    fn clone_from(&mut self, other: &ConnectMap) {
        self.sum_of_hint = other.sum_of_hint;
        self.uf.clone_from(&other.uf);
    }
}

impl ConnectMap {
    pub fn new(puzzle: &Puzzle, side_map: &mut SideMap) -> ConnectMap {
        let size = puzzle.size();
        let cell_len = size.cell_len();

        let mut uf = Uf::from_iter((0..cell_len)
                                       .map(|id| size.cellid_to_point(CellId::new(id)))
                                       .map(|p| Area::new(p, puzzle, side_map)));

        let mut sum_of_hint = 0;
        for i in 0..cell_len {
            sum_of_hint += uf.get(i).sum_of_hint;
        }

        let mut conn_map = ConnectMap {
            sum_of_hint: sum_of_hint,
            uf: uf,
        };

        for p in size.points() {
            let cp = size.point_to_cellid(p);
            for &r in &Move::ALL_DIRECTIONS {
                let p2 = p + r;
                let cp2 = size.point_to_cellid(p2);
                if side_map.get_edge(cp, cp2) == State::Fixed(Edge::Cross) {
                    conn_map.union(cp, cp2);
                }
            }
        }
        conn_map
    }

    pub fn cell_len(&self) -> usize {
        self.uf.size()
    }
    pub fn sum_of_hint(&self) -> u32 {
        self.sum_of_hint
    }

    pub fn sync(&mut self, side_map: &mut SideMap) -> SolverResult<()> {
        for i in 0..self.cell_len() {
            let c = CellId::new(i);
            update_conn(side_map, self, c);
        }
        for i in 0..self.cell_len() {
            let c = CellId::new(i);
            try!(update_area(side_map, self, c));
        }

        Ok(())
    }

    pub fn count_area(&mut self) -> usize {
        (0..self.cell_len())
            .map(CellId::new)
            .filter(|&c| self.get(c).coord() == c)
            .count()
    }

    pub fn union(&mut self, i: CellId, j: CellId) -> bool {
        self.uf.union(i.id(), j.id())
    }
    pub fn get(&mut self, i: CellId) -> &Area {
        self.uf.get(i.id())
    }
    pub fn get_mut(&mut self, i: CellId) -> &mut Area {
        self.uf.get_mut(i.id())
    }
}

fn update_conn(side_map: &mut SideMap, conn_map: &mut ConnectMap, p: CellId) {
    let mut unknown_edge = {
        let a = conn_map.get_mut(p);
        if a.coord != p {
            return;
        }
        mem::replace(&mut a.unknown_edge, vec![])
    };

    for &p2 in &unknown_edge {
        if side_map.get_edge(p, p2) == State::Fixed(Edge::Cross) {
            conn_map.union(p, p2);
        }
    }

    let mut need_update = None;
    {
        let mut area = conn_map.get_mut(p);
        if area.unknown_edge.is_empty() {
            area.unknown_edge = unknown_edge;
        } else {
            area.unknown_edge.append(&mut unknown_edge);
            if area.coord <= p {
                need_update = Some(area.coord);
            }
        }
    }

    if let Some(coord) = need_update {
        update_conn(side_map, conn_map, coord);
    }
}

fn update_area(side_map: &mut SideMap, conn_map: &mut ConnectMap, p: CellId) -> SolverResult<()> {
    let mut unknown_edge = {
        let a = conn_map.get_mut(p);
        if a.coord != p {
            return Ok(());
        }
        mem::replace(&mut a.unknown_edge, vec![])
    };

    unsafe {
        // Assume the elements of unknown_edge is copyable.
        let ptr = unknown_edge.as_mut_ptr();
        let mut w = 0;
        for r in 0..unknown_edge.len() {
            let read = ptr.offset(r as isize);
            match side_map.get_edge(p, *read) {
                State::Fixed(_) => {}
                State::Unknown => {
                    let write = ptr.offset(w as isize);
                    *write = conn_map.get(*read).coord();
                    w += 1;
                }
                State::Conflict => {
                    return Err(Error::invalid_board());
                }
            }
        }
        unknown_edge.truncate(w);
    }

    unknown_edge.sort();
    unknown_edge.dedup();

    let mut area = conn_map.get_mut(p);
    area.side = side_map.get_side(p);
    area.unknown_edge = unknown_edge;

    Ok(())
}
<commit_msg>solver: make error detection more early<commit_after>use std::iter::FromIterator;
use std::mem;
use union_find::{Union, UnionFind, UnionResult, QuickFindUf as Uf};
use slsr_core::puzzle::{Edge, Puzzle, Side};
use slsr_core::geom::{CellId, Geom, Point, Move, OUTSIDE_CELL_ID};

use {Error, State, SolverResult};
use model::side_map::SideMap;

#[derive(Debug)]
pub struct Area {
    coord: CellId,
    side: State<Side>,
    unknown_edge: Vec<CellId>,
    sum_of_hint: u32,
    size: usize,
}

impl Clone for Area {
    fn clone(&self) -> Area {
        Area {
            coord: self.coord,
            side: self.side,
            unknown_edge: self.unknown_edge.clone(),
            sum_of_hint: self.sum_of_hint,
            size: self.size,
        }
    }

    fn clone_from(&mut self, other: &Area) {
        self.coord = other.coord;
        self.side = other.side;
        self.unknown_edge.clone_from(&other.unknown_edge);
        self.sum_of_hint = other.sum_of_hint;
        self.size = other.size;
    }
}

impl Area {
    pub fn coord(&self) -> CellId {
        self.coord
    }
    pub fn side(&self) -> State<Side> {
        self.side
    }
    pub fn unknown_edge(&self) -> &[CellId] {
        &self.unknown_edge
    }
    pub fn sum_of_hint(&self) -> u32 {
        self.sum_of_hint
    }
}

impl Area {
    fn new(p: Point, puzzle: &Puzzle, side_map: &mut SideMap) -> Area {
        let cp = puzzle.point_to_cellid(p);
        let sum = puzzle.hint(p).unwrap_or(0) as u32;

        let mut edge = vec![];
        if cp != OUTSIDE_CELL_ID {
            for &r in &Move::ALL_DIRECTIONS {
                let cp_r = puzzle.point_to_cellid(p + r);
                if side_map.get_edge(cp, cp_r) == State::Unknown {
                    edge.push(cp_r);
                }
            }
        } else {
            let points = puzzle.points_in_column(0)
                               .chain(puzzle.points_in_column(puzzle.column() - 1))
                               .chain(puzzle.points_in_row(0))
                               .chain(puzzle.points_in_row(puzzle.row() - 1));
            for p2 in points {
                let cp2 = puzzle.point_to_cellid(p2);
                if side_map.get_edge(cp, cp2) == State::Unknown {
                    edge.push(cp2);
                }
            }
        }
        edge.sort();
        edge.dedup();

        Area {
            coord: cp,
            side: side_map.get_side(cp),
            unknown_edge: edge,
            sum_of_hint: sum,
            size: 1,
        }
    }
}

impl Union for Area {
    fn union(mut lval: Area, mut rval: Area) -> UnionResult<Area> {
        let coord = if lval.coord < rval.coord {
            lval.coord
        } else {
            rval.coord
        };
        let side = match (lval.side, rval.side) {
            (State::Conflict, _) | (_, State::Conflict) => State::Conflict,
            (State::Unknown, x) | (x, State::Unknown) => x,
            (State::Fixed(l), State::Fixed(r)) => {
                if l == r {
                    State::Fixed(l)
                } else {
                    State::Conflict
                }
            }
        };
        let unknown_edge = {
            if lval.unknown_edge.is_empty() {
                rval.unknown_edge
            } else {
                lval.unknown_edge.append(&mut rval.unknown_edge);
                lval.unknown_edge
            }
        };
        let area = Area {
            coord: coord,
            side: side,
            unknown_edge: unknown_edge,
            sum_of_hint: lval.sum_of_hint + rval.sum_of_hint,
            size: lval.size + rval.size,
        };
        if lval.size >= rval.size {
            UnionResult::Left(area)
        } else {
            UnionResult::Right(area)
        }
    }
}

#[derive(Debug)]
pub struct ConnectMap {
    sum_of_hint: u32,
    uf: Uf<Area>,
}

impl Clone for ConnectMap {
    fn clone(&self) -> ConnectMap {
        ConnectMap {
            sum_of_hint: self.sum_of_hint,
            uf: self.uf.clone(),
        }
    }

    fn clone_from(&mut self, other: &ConnectMap) {
        self.sum_of_hint = other.sum_of_hint;
        self.uf.clone_from(&other.uf);
    }
}

impl ConnectMap {
    pub fn new(puzzle: &Puzzle, side_map: &mut SideMap) -> ConnectMap {
        let size = puzzle.size();
        let cell_len = size.cell_len();

        let mut uf = Uf::from_iter((0..cell_len)
                                       .map(|id| size.cellid_to_point(CellId::new(id)))
                                       .map(|p| Area::new(p, puzzle, side_map)));

        let mut sum_of_hint = 0;
        for i in 0..cell_len {
            sum_of_hint += uf.get(i).sum_of_hint;
        }

        let mut conn_map = ConnectMap {
            sum_of_hint: sum_of_hint,
            uf: uf,
        };

        for p in size.points() {
            let cp = size.point_to_cellid(p);
            for &r in &Move::ALL_DIRECTIONS {
                let p2 = p + r;
                let cp2 = size.point_to_cellid(p2);
                if side_map.get_edge(cp, cp2) == State::Fixed(Edge::Cross) {
                    conn_map.union(cp, cp2);
                }
            }
        }
        conn_map
    }

    pub fn cell_len(&self) -> usize {
        self.uf.size()
    }
    pub fn sum_of_hint(&self) -> u32 {
        self.sum_of_hint
    }

    pub fn sync(&mut self, side_map: &mut SideMap) -> SolverResult<()> {
        for i in 0..self.cell_len() {
            let c = CellId::new(i);
            update_conn(side_map, self, c);
        }

        let mut closed_cnt = 0;
        for i in 0..self.cell_len() {
            let c = CellId::new(i);
            if try!(update_area(side_map, self, c)) {
                closed_cnt += 1;
            }
        }

        if closed_cnt > 2 {
            return Err(Error::invalid_board());
        }

        Ok(())
    }

    pub fn count_area(&mut self) -> usize {
        (0..self.cell_len())
            .map(CellId::new)
            .filter(|&c| self.get(c).coord() == c)
            .count()
    }

    pub fn union(&mut self, i: CellId, j: CellId) -> bool {
        self.uf.union(i.id(), j.id())
    }
    pub fn get(&mut self, i: CellId) -> &Area {
        self.uf.get(i.id())
    }
    pub fn get_mut(&mut self, i: CellId) -> &mut Area {
        self.uf.get_mut(i.id())
    }
}

fn update_conn(side_map: &mut SideMap, conn_map: &mut ConnectMap, p: CellId) {
    let mut unknown_edge = {
        let a = conn_map.get_mut(p);
        if a.coord != p {
            return;
        }
        mem::replace(&mut a.unknown_edge, vec![])
    };

    for &p2 in &unknown_edge {
        if side_map.get_edge(p, p2) == State::Fixed(Edge::Cross) {
            conn_map.union(p, p2);
        }
    }

    let mut need_update = None;
    {
        let mut area = conn_map.get_mut(p);
        if area.unknown_edge.is_empty() {
            area.unknown_edge = unknown_edge;
        } else {
            area.unknown_edge.append(&mut unknown_edge);
            if area.coord <= p {
                need_update = Some(area.coord);
            }
        }
    }

    if let Some(coord) = need_update {
        update_conn(side_map, conn_map, coord);
    }
}

fn update_area(side_map: &mut SideMap, conn_map: &mut ConnectMap, p: CellId) -> SolverResult<bool> {
    let mut unknown_edge = {
        let a = conn_map.get_mut(p);
        if a.coord != p {
            return Ok(false);
        }
        mem::replace(&mut a.unknown_edge, vec![])
    };

    unsafe {
        // Assume the elements of unknown_edge is copyable.
        let ptr = unknown_edge.as_mut_ptr();
        let mut w = 0;
        for r in 0..unknown_edge.len() {
            let read = ptr.offset(r as isize);
            match side_map.get_edge(p, *read) {
                State::Fixed(_) => {}
                State::Unknown => {
                    let write = ptr.offset(w as isize);
                    *write = conn_map.get(*read).coord();
                    w += 1;
                }
                State::Conflict => {
                    return Err(Error::invalid_board());
                }
            }
        }
        unknown_edge.truncate(w);
    }

    unknown_edge.sort();
    unknown_edge.dedup();

    let is_closed = unknown_edge.is_empty();

    let mut area = conn_map.get_mut(p);
    area.side = side_map.get_side(p);
    area.unknown_edge = unknown_edge;

    Ok(is_closed)
}
<|endoftext|>


<commit_before><commit_msg>Add bcf biblatex control file<commit_after><|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.

use std::collections::HashSet;

use build::{Build, Compiler};

#[derive(Hash, Eq, PartialEq, Clone, Debug)]
pub struct Step<'a> {
    pub src: Source<'a>,
    pub target: &'a str,
}

macro_rules! targets {
    ($m:ident) => {
        $m! {
            // Step representing building the stageN compiler. This is just the
            // compiler executable itself, not any of the support libraries
            (rustc, Rustc { stage: u32 }),

            // Steps for the two main cargo builds. These are parameterized over
            // the compiler which is producing the artifact.
            (libstd, Libstd { compiler: Compiler<'a> }),
            (libtest, Libtest { compiler: Compiler<'a> }),
            (librustc, Librustc { compiler: Compiler<'a> }),

            // Links the target produced by the compiler provided into the
            // host's directory also provided.
            (libstd_link, LibstdLink {
                compiler: Compiler<'a>,
                host: &'a str
            }),
            (libtest_link, LibtestLink {
                compiler: Compiler<'a>,
                host: &'a str
            }),
            (librustc_link, LibrustcLink {
                compiler: Compiler<'a>,
                host: &'a str
            }),

            // Various tools that we can build as part of the build.
            (tool_linkchecker, ToolLinkchecker { stage: u32 }),
            (tool_rustbook, ToolRustbook { stage: u32 }),
            (tool_error_index, ToolErrorIndex { stage: u32 }),
            (tool_cargotest, ToolCargoTest { stage: u32 }),
            (tool_tidy, ToolTidy { stage: u32 }),

            // Steps for long-running native builds. Ideally these wouldn't
            // actually exist and would be part of build scripts, but for now
            // these are here.
            //
            // There aren't really any parameters to this, but empty structs
            // with braces are unstable so we just pick something that works.
            (llvm, Llvm { _dummy: () }),
            (compiler_rt, CompilerRt { _dummy: () }),

            // Steps for various pieces of documentation that we can generate,
            // the 'doc' step is just a pseudo target to depend on a bunch of
            // others.
            (doc, Doc { stage: u32 }),
            (doc_book, DocBook { stage: u32 }),
            (doc_nomicon, DocNomicon { stage: u32 }),
            (doc_style, DocStyle { stage: u32 }),
            (doc_standalone, DocStandalone { stage: u32 }),
            (doc_std, DocStd { stage: u32 }),
            (doc_test, DocTest { stage: u32 }),
            (doc_rustc, DocRustc { stage: u32 }),
            (doc_error_index, DocErrorIndex { stage: u32 }),

            // Steps for running tests. The 'check' target is just a pseudo
            // target to depend on a bunch of others.
            (check, Check { stage: u32, compiler: Compiler<'a> }),
            (check_linkcheck, CheckLinkcheck { stage: u32 }),
            (check_cargotest, CheckCargoTest { stage: u32 }),
            (check_tidy, CheckTidy { stage: u32 }),

            // Distribution targets, creating tarballs
            (dist, Dist { stage: u32 }),
            (dist_docs, DistDocs { stage: u32 }),
            (dist_mingw, DistMingw { _dummy: () }),
            (dist_rustc, DistRustc { stage: u32 }),
            (dist_std, DistStd { compiler: Compiler<'a> }),
        }
    }
}

macro_rules! item { ($a:item) => ($a) }

macro_rules! define_source {
    ($(($short:ident, $name:ident { $($args:tt)* }),)*) => {
        item! {
            #[derive(Hash, Eq, PartialEq, Clone, Debug)]
            pub enum Source<'a> {
                $($name { $($args)* }),*
            }
        }
    }
}

targets!(define_source);

pub fn all(build: &Build) -> Vec<Step> {
    let mut ret = Vec::new();
    let mut all = HashSet::new();
    for target in top_level(build) {
        fill(build, &target, &mut ret, &mut all);
    }
    return ret;

    fn fill<'a>(build: &'a Build,
                target: &Step<'a>,
                ret: &mut Vec<Step<'a>>,
                set: &mut HashSet<Step<'a>>) {
        if set.insert(target.clone()) {
            for dep in target.deps(build) {
                fill(build, &dep, ret, set);
            }
            ret.push(target.clone());
        }
    }
}

fn top_level(build: &Build) -> Vec<Step> {
    let mut targets = Vec::new();
    let stage = build.flags.stage.unwrap_or(2);

    let host = Step {
        src: Source::Llvm { _dummy: () },
        target: build.flags.host.iter().next()
                     .unwrap_or(&build.config.build),
    };
    let target = Step {
        src: Source::Llvm { _dummy: () },
        target: build.flags.target.iter().next().map(|x| &x[..])
                     .unwrap_or(host.target)
    };

    add_steps(build, stage, &host, &target, &mut targets);

    if targets.len() == 0 {
        let t = Step {
            src: Source::Llvm { _dummy: () },
            target: &build.config.build,
        };
        if build.config.docs {
          targets.push(t.doc(stage));
        }
        for host in build.config.host.iter() {
            if !build.flags.host.contains(host) {
                continue
            }
            let host = t.target(host);
            if host.target == build.config.build {
                targets.push(host.librustc(host.compiler(stage)));
            } else {
                targets.push(host.librustc_link(t.compiler(stage), host.target));
            }
            for target in build.config.target.iter() {
                if !build.flags.target.contains(target) {
                    continue
                }

                if host.target == build.config.build {
                    targets.push(host.target(target)
                                     .libtest(host.compiler(stage)));
                } else {
                    targets.push(host.target(target)
                                     .libtest_link(t.compiler(stage), host.target));
                }
            }
        }
    }

    return targets

}

fn add_steps<'a>(build: &'a Build,
                 stage: u32,
                 host: &Step<'a>,
                 target: &Step<'a>,
                 targets: &mut Vec<Step<'a>>) {
    struct Context<'a> {
        stage: u32,
        compiler: Compiler<'a>,
        _dummy: (),
        host: &'a str,
    }
    for step in build.flags.step.iter() {

        // The macro below insists on hygienic access to all local variables, so
        // we shove them all in a struct and subvert hygiene by accessing struct
        // fields instead,
        let cx = Context {
            stage: stage,
            compiler: host.target(&build.config.build).compiler(stage),
            _dummy: (),
            host: host.target,
        };
        macro_rules! add_step {
            ($(($short:ident, $name:ident { $($arg:ident: $t:ty),* }),)*) => ({$(
                let name = stringify!($short).replace("_", "-");
                if &step[..] == &name[..] {
                    targets.push(target.$short($(cx.$arg),*));
                    continue
                }
                drop(name);
            )*})
        }

        targets!(add_step);

        panic!("unknown step: {}", step);
    }
}

macro_rules! constructors {
    ($(($short:ident, $name:ident { $($arg:ident: $t:ty),* }),)*) => {$(
        fn $short(&self, $($arg: $t),*) -> Step<'a> {
            Step {
                src: Source::$name { $($arg: $arg),* },
                target: self.target,
            }
        }
    )*}
}

impl<'a> Step<'a> {
    fn compiler(&self, stage: u32) -> Compiler<'a> {
        Compiler::new(stage, self.target)
    }

    fn target(&self, target: &'a str) -> Step<'a> {
        Step { target: target, src: self.src.clone() }
    }

    targets!(constructors);

    pub fn deps(&self, build: &'a Build) -> Vec<Step<'a>> {
        match self.src {
            Source::Rustc { stage: 0 } => {
                Vec::new()
            }
            Source::Rustc { stage } => {
                let compiler = Compiler::new(stage - 1, &build.config.build);
                vec![self.librustc(compiler)]
            }
            Source::Librustc { compiler } => {
                vec![self.libtest(compiler), self.llvm(())]
            }
            Source::Libtest { compiler } => {
                vec![self.libstd(compiler)]
            }
            Source::Libstd { compiler } => {
                vec![self.compiler_rt(()),
                     self.rustc(compiler.stage).target(compiler.host)]
            }
            Source::LibrustcLink { compiler, host } => {
                vec![self.librustc(compiler),
                     self.libtest_link(compiler, host)]
            }
            Source::LibtestLink { compiler, host } => {
                vec![self.libtest(compiler), self.libstd_link(compiler, host)]
            }
            Source::LibstdLink { compiler, host } => {
                vec![self.libstd(compiler),
                     self.target(host).rustc(compiler.stage)]
            }
            Source::CompilerRt { _dummy } => {
                vec![self.llvm(()).target(&build.config.build)]
            }
            Source::Llvm { _dummy } => Vec::new(),

            // Note that all doc targets depend on artifacts from the build
            // architecture, not the target (which is where we're generating
            // docs into).
            Source::DocStd { stage } => {
                let compiler = self.target(&build.config.build).compiler(stage);
                vec![self.libstd(compiler)]
            }
            Source::DocTest { stage } => {
                let compiler = self.target(&build.config.build).compiler(stage);
                vec![self.libtest(compiler)]
            }
            Source::DocBook { stage } |
            Source::DocNomicon { stage } |
            Source::DocStyle { stage } => {
                vec![self.target(&build.config.build).tool_rustbook(stage)]
            }
            Source::DocErrorIndex { stage } => {
                vec![self.target(&build.config.build).tool_error_index(stage)]
            }
            Source::DocStandalone { stage } => {
                vec![self.target(&build.config.build).rustc(stage)]
            }
            Source::DocRustc { stage } => {
                vec![self.doc_test(stage)]
            }
            Source::Doc { stage } => {
                vec![self.doc_book(stage), self.doc_nomicon(stage),
                     self.doc_style(stage), self.doc_standalone(stage),
                     self.doc_std(stage),
                     self.doc_error_index(stage)]
            }
            Source::Check { stage, compiler: _ } => {
                vec![self.check_linkcheck(stage),
                     self.dist(stage)]
            }
            Source::CheckLinkcheck { stage } => {
                vec![self.tool_linkchecker(stage), self.doc(stage)]
            }
            Source::CheckCargoTest { stage } => {
                vec![self.tool_cargotest(stage),
                     self.librustc(self.compiler(stage))]
            }
            Source::CheckTidy { stage } => {
                vec![self.tool_tidy(stage)]
            }

            Source::ToolLinkchecker { stage } |
            Source::ToolTidy { stage } => {
                vec![self.libstd(self.compiler(stage))]
            }
            Source::ToolErrorIndex { stage } |
            Source::ToolRustbook { stage } => {
                vec![self.librustc(self.compiler(stage))]
            }
            Source::ToolCargoTest { stage } => {
                vec![self.libstd(self.compiler(stage))]
            }

            Source::DistDocs { stage } => vec![self.doc(stage)],
            Source::DistMingw { _dummy: _ } => Vec::new(),
            Source::DistRustc { stage } => {
                vec![self.rustc(stage)]
            }
            Source::DistStd { compiler } => {
                vec![self.libtest(compiler)]
            }

            Source::Dist { stage } => {
                let mut base = Vec::new();

                for host in build.config.host.iter() {
                    let host = self.target(host);
                    base.push(host.dist_rustc(stage));
                    if host.target.contains("windows-gnu") {
                        base.push(host.dist_mingw(()));
                    }

                    let compiler = self.compiler(stage);
                    for target in build.config.target.iter() {
                        let target = self.target(target);
                        if build.config.docs {
                            base.push(target.dist_docs(stage));
                        }
                        base.push(target.dist_std(compiler));
                    }
                }
                return base
            }
        }
    }
}
<commit_msg>rustbuild: Package librustc & co for cross-hosts<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.

use std::collections::HashSet;

use build::{Build, Compiler};

#[derive(Hash, Eq, PartialEq, Clone, Debug)]
pub struct Step<'a> {
    pub src: Source<'a>,
    pub target: &'a str,
}

macro_rules! targets {
    ($m:ident) => {
        $m! {
            // Step representing building the stageN compiler. This is just the
            // compiler executable itself, not any of the support libraries
            (rustc, Rustc { stage: u32 }),

            // Steps for the two main cargo builds. These are parameterized over
            // the compiler which is producing the artifact.
            (libstd, Libstd { compiler: Compiler<'a> }),
            (libtest, Libtest { compiler: Compiler<'a> }),
            (librustc, Librustc { compiler: Compiler<'a> }),

            // Links the target produced by the compiler provided into the
            // host's directory also provided.
            (libstd_link, LibstdLink {
                compiler: Compiler<'a>,
                host: &'a str
            }),
            (libtest_link, LibtestLink {
                compiler: Compiler<'a>,
                host: &'a str
            }),
            (librustc_link, LibrustcLink {
                compiler: Compiler<'a>,
                host: &'a str
            }),

            // Various tools that we can build as part of the build.
            (tool_linkchecker, ToolLinkchecker { stage: u32 }),
            (tool_rustbook, ToolRustbook { stage: u32 }),
            (tool_error_index, ToolErrorIndex { stage: u32 }),
            (tool_cargotest, ToolCargoTest { stage: u32 }),
            (tool_tidy, ToolTidy { stage: u32 }),

            // Steps for long-running native builds. Ideally these wouldn't
            // actually exist and would be part of build scripts, but for now
            // these are here.
            //
            // There aren't really any parameters to this, but empty structs
            // with braces are unstable so we just pick something that works.
            (llvm, Llvm { _dummy: () }),
            (compiler_rt, CompilerRt { _dummy: () }),

            // Steps for various pieces of documentation that we can generate,
            // the 'doc' step is just a pseudo target to depend on a bunch of
            // others.
            (doc, Doc { stage: u32 }),
            (doc_book, DocBook { stage: u32 }),
            (doc_nomicon, DocNomicon { stage: u32 }),
            (doc_style, DocStyle { stage: u32 }),
            (doc_standalone, DocStandalone { stage: u32 }),
            (doc_std, DocStd { stage: u32 }),
            (doc_test, DocTest { stage: u32 }),
            (doc_rustc, DocRustc { stage: u32 }),
            (doc_error_index, DocErrorIndex { stage: u32 }),

            // Steps for running tests. The 'check' target is just a pseudo
            // target to depend on a bunch of others.
            (check, Check { stage: u32, compiler: Compiler<'a> }),
            (check_linkcheck, CheckLinkcheck { stage: u32 }),
            (check_cargotest, CheckCargoTest { stage: u32 }),
            (check_tidy, CheckTidy { stage: u32 }),

            // Distribution targets, creating tarballs
            (dist, Dist { stage: u32 }),
            (dist_docs, DistDocs { stage: u32 }),
            (dist_mingw, DistMingw { _dummy: () }),
            (dist_rustc, DistRustc { stage: u32 }),
            (dist_std, DistStd { compiler: Compiler<'a> }),
        }
    }
}

macro_rules! item { ($a:item) => ($a) }

macro_rules! define_source {
    ($(($short:ident, $name:ident { $($args:tt)* }),)*) => {
        item! {
            #[derive(Hash, Eq, PartialEq, Clone, Debug)]
            pub enum Source<'a> {
                $($name { $($args)* }),*
            }
        }
    }
}

targets!(define_source);

pub fn all(build: &Build) -> Vec<Step> {
    let mut ret = Vec::new();
    let mut all = HashSet::new();
    for target in top_level(build) {
        fill(build, &target, &mut ret, &mut all);
    }
    return ret;

    fn fill<'a>(build: &'a Build,
                target: &Step<'a>,
                ret: &mut Vec<Step<'a>>,
                set: &mut HashSet<Step<'a>>) {
        if set.insert(target.clone()) {
            for dep in target.deps(build) {
                fill(build, &dep, ret, set);
            }
            ret.push(target.clone());
        }
    }
}

fn top_level(build: &Build) -> Vec<Step> {
    let mut targets = Vec::new();
    let stage = build.flags.stage.unwrap_or(2);

    let host = Step {
        src: Source::Llvm { _dummy: () },
        target: build.flags.host.iter().next()
                     .unwrap_or(&build.config.build),
    };
    let target = Step {
        src: Source::Llvm { _dummy: () },
        target: build.flags.target.iter().next().map(|x| &x[..])
                     .unwrap_or(host.target)
    };

    add_steps(build, stage, &host, &target, &mut targets);

    if targets.len() == 0 {
        let t = Step {
            src: Source::Llvm { _dummy: () },
            target: &build.config.build,
        };
        if build.config.docs {
          targets.push(t.doc(stage));
        }
        for host in build.config.host.iter() {
            if !build.flags.host.contains(host) {
                continue
            }
            let host = t.target(host);
            if host.target == build.config.build {
                targets.push(host.librustc(host.compiler(stage)));
            } else {
                targets.push(host.librustc_link(t.compiler(stage), host.target));
            }
            for target in build.config.target.iter() {
                if !build.flags.target.contains(target) {
                    continue
                }

                if host.target == build.config.build {
                    targets.push(host.target(target)
                                     .libtest(host.compiler(stage)));
                } else {
                    targets.push(host.target(target)
                                     .libtest_link(t.compiler(stage), host.target));
                }
            }
        }
    }

    return targets

}

fn add_steps<'a>(build: &'a Build,
                 stage: u32,
                 host: &Step<'a>,
                 target: &Step<'a>,
                 targets: &mut Vec<Step<'a>>) {
    struct Context<'a> {
        stage: u32,
        compiler: Compiler<'a>,
        _dummy: (),
        host: &'a str,
    }
    for step in build.flags.step.iter() {

        // The macro below insists on hygienic access to all local variables, so
        // we shove them all in a struct and subvert hygiene by accessing struct
        // fields instead,
        let cx = Context {
            stage: stage,
            compiler: host.target(&build.config.build).compiler(stage),
            _dummy: (),
            host: host.target,
        };
        macro_rules! add_step {
            ($(($short:ident, $name:ident { $($arg:ident: $t:ty),* }),)*) => ({$(
                let name = stringify!($short).replace("_", "-");
                if &step[..] == &name[..] {
                    targets.push(target.$short($(cx.$arg),*));
                    continue
                }
                drop(name);
            )*})
        }

        targets!(add_step);

        panic!("unknown step: {}", step);
    }
}

macro_rules! constructors {
    ($(($short:ident, $name:ident { $($arg:ident: $t:ty),* }),)*) => {$(
        fn $short(&self, $($arg: $t),*) -> Step<'a> {
            Step {
                src: Source::$name { $($arg: $arg),* },
                target: self.target,
            }
        }
    )*}
}

impl<'a> Step<'a> {
    fn compiler(&self, stage: u32) -> Compiler<'a> {
        Compiler::new(stage, self.target)
    }

    fn target(&self, target: &'a str) -> Step<'a> {
        Step { target: target, src: self.src.clone() }
    }

    targets!(constructors);

    pub fn deps(&self, build: &'a Build) -> Vec<Step<'a>> {
        match self.src {
            Source::Rustc { stage: 0 } => {
                Vec::new()
            }
            Source::Rustc { stage } => {
                let compiler = Compiler::new(stage - 1, &build.config.build);
                vec![self.librustc(compiler)]
            }
            Source::Librustc { compiler } => {
                vec![self.libtest(compiler), self.llvm(())]
            }
            Source::Libtest { compiler } => {
                vec![self.libstd(compiler)]
            }
            Source::Libstd { compiler } => {
                vec![self.compiler_rt(()),
                     self.rustc(compiler.stage).target(compiler.host)]
            }
            Source::LibrustcLink { compiler, host } => {
                vec![self.librustc(compiler),
                     self.libtest_link(compiler, host)]
            }
            Source::LibtestLink { compiler, host } => {
                vec![self.libtest(compiler), self.libstd_link(compiler, host)]
            }
            Source::LibstdLink { compiler, host } => {
                vec![self.libstd(compiler),
                     self.target(host).rustc(compiler.stage)]
            }
            Source::CompilerRt { _dummy } => {
                vec![self.llvm(()).target(&build.config.build)]
            }
            Source::Llvm { _dummy } => Vec::new(),

            // Note that all doc targets depend on artifacts from the build
            // architecture, not the target (which is where we're generating
            // docs into).
            Source::DocStd { stage } => {
                let compiler = self.target(&build.config.build).compiler(stage);
                vec![self.libstd(compiler)]
            }
            Source::DocTest { stage } => {
                let compiler = self.target(&build.config.build).compiler(stage);
                vec![self.libtest(compiler)]
            }
            Source::DocBook { stage } |
            Source::DocNomicon { stage } |
            Source::DocStyle { stage } => {
                vec![self.target(&build.config.build).tool_rustbook(stage)]
            }
            Source::DocErrorIndex { stage } => {
                vec![self.target(&build.config.build).tool_error_index(stage)]
            }
            Source::DocStandalone { stage } => {
                vec![self.target(&build.config.build).rustc(stage)]
            }
            Source::DocRustc { stage } => {
                vec![self.doc_test(stage)]
            }
            Source::Doc { stage } => {
                vec![self.doc_book(stage), self.doc_nomicon(stage),
                     self.doc_style(stage), self.doc_standalone(stage),
                     self.doc_std(stage),
                     self.doc_error_index(stage)]
            }
            Source::Check { stage, compiler: _ } => {
                vec![self.check_linkcheck(stage),
                     self.dist(stage)]
            }
            Source::CheckLinkcheck { stage } => {
                vec![self.tool_linkchecker(stage), self.doc(stage)]
            }
            Source::CheckCargoTest { stage } => {
                vec![self.tool_cargotest(stage),
                     self.librustc(self.compiler(stage))]
            }
            Source::CheckTidy { stage } => {
                vec![self.tool_tidy(stage)]
            }

            Source::ToolLinkchecker { stage } |
            Source::ToolTidy { stage } => {
                vec![self.libstd(self.compiler(stage))]
            }
            Source::ToolErrorIndex { stage } |
            Source::ToolRustbook { stage } => {
                vec![self.librustc(self.compiler(stage))]
            }
            Source::ToolCargoTest { stage } => {
                vec![self.libstd(self.compiler(stage))]
            }

            Source::DistDocs { stage } => vec![self.doc(stage)],
            Source::DistMingw { _dummy: _ } => Vec::new(),
            Source::DistRustc { stage } => {
                vec![self.rustc(stage)]
            }
            Source::DistStd { compiler } => {
                // We want to package up as many target libraries as possible
                // for the `rust-std` package, so if this is a host target we
                // depend on librustc and otherwise we just depend on libtest.
                if build.config.host.iter().any(|t| t == self.target) {
                    vec![self.librustc(compiler)]
                } else {
                    vec![self.libtest(compiler)]
                }
            }

            Source::Dist { stage } => {
                let mut base = Vec::new();

                for host in build.config.host.iter() {
                    let host = self.target(host);
                    base.push(host.dist_rustc(stage));
                    if host.target.contains("windows-gnu") {
                        base.push(host.dist_mingw(()));
                    }

                    let compiler = self.compiler(stage);
                    for target in build.config.target.iter() {
                        let target = self.target(target);
                        if build.config.docs {
                            base.push(target.dist_docs(stage));
                        }
                        base.push(target.dist_std(compiler));
                    }
                }
                return base
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::{Read, Write, BufReader, BufRead};

use regex::Regex;
use quire::validate as V;

use super::super::context::Context;
use super::super::packages;
use super::generic::{capture_command, run_command_at_env};
use builder::distrib::Distribution;
use builder::commands::generic::{command, run};
use process_util::capture_stdout;
use build_step::{BuildStep, VersionError, StepError, Digest, Config, Guard};

const DEFAULT_GEM_EXE: &'static str = "/usr/bin/gem";
const BIN_DIR: &'static str = "/usr/local/bin";
const GEM_VERSION_WITH_NO_DOCUMENT_OPT: f32 = 2.0;
const VALID_TRUST_POLICIES: [&'static str; 3] = ["LowSecurity", "MediumSecurity", "HighSecurity"];

#[derive(Debug, Deserialize)]
pub struct GemInstall(Vec<String>);

impl GemInstall {
    pub fn config() -> V::Sequence<'static> {
        V::Sequence::new(V::Scalar::new())
    }
}

#[derive(Deserialize, Debug, Clone)]
pub struct GemConfig {
    pub install_ruby: bool,
    pub gem_exe: Option<String>,
    pub update_gem: bool,
}

impl GemConfig {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("install_ruby", V::Scalar::new().default(true))
        .member("gem_exe", V::Scalar::new().optional())
        .member("update_gem", V::Scalar::new().default(true))
    }
}

#[derive(Deserialize, Debug)]
pub struct GemBundle {
    pub gemfile: PathBuf,
    pub without: Vec<String>,
    pub trust_policy: Option<String>,
}

impl GemBundle {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("gemfile", V::Scalar::new().default("Gemfile"))
        .member("without", V::Sequence::new(V::Scalar::new()))
        .member("trust_policy", V::Scalar::new().optional())
    }
}

impl Default for GemConfig {
    fn default() -> Self {
        GemConfig {
            install_ruby: true,
            gem_exe: None,
            update_gem: true,
        }
    }
}

fn no_doc_args(ctx: &mut Context) -> Result<Vec<&'static str>, String> {
    if ctx.gem_settings.update_gem {
        Ok(vec!("--no-document"))
    } else {
        let version = gem_version(ctx)?;
        if version < GEM_VERSION_WITH_NO_DOCUMENT_OPT {
            Ok(vec!("--no-rdoc", "--no-ri"))
        } else {
            Ok(vec!("--no-document"))
        }
    }
}

fn gem_version(ctx: &mut Context) -> Result<f32, String> {
    let gem_exe = ctx.gem_settings.gem_exe.clone()
        .unwrap_or(DEFAULT_GEM_EXE.to_owned());

    let args = [
        gem_exe,
        "--version".to_owned(),
    ];

    let gem_ver = capture_command(ctx, &args, &[])
        .and_then(|x| String::from_utf8(x)
            .map_err(|e| format!("Error parsing gem version: {}", e)))
        .map_err(|e| format!("Error getting gem version: {}", e))?;

    let re = Regex::new(r#"^(\d+?\.\d+?)\."#).expect("Invalid regex");
    let version = re.captures(&gem_ver)
        .and_then(|cap| cap.get(1))
        .ok_or("Gem version was not found".to_owned())?;

    version.as_str().parse::<f32>()
        .map_err(|e| format!("Erro parsing gem version: {}", e))
}

fn gem_cache_dir(ctx: &mut Context) -> Result<PathBuf, String> {
    let gem_exe = ctx.gem_settings.gem_exe.clone()
        .unwrap_or(DEFAULT_GEM_EXE.to_owned());

    let args = [
        gem_exe,
        "env".to_owned(),
        "gemdir".to_owned(),
    ];

    let gem_dir = capture_command(ctx, &args, &[])
        .and_then(|x| String::from_utf8(x)
            .map_err(|e| format!("Error getting gem dir: {}", e)))?;

    Ok(Path::new(gem_dir.trim()).join("cache"))
}

fn requires_git(gemfile: &Path) -> Result<bool, String> {
    let gemfile = Path::new("/work").join(gemfile);

    let re = Regex::new(
        r#"(git .*? do)|(:(git|github|gist|bitbucket) =>)|(git_source\(.*?\))"#
    ).expect("Invalid regex");

    let gemfile_data = {
        let mut buf = String::new();
        File::open(&gemfile)
            .and_then(|mut f| f.read_to_string(&mut buf))
            .map_err(|e| format!("Error reading Gemfile ({:?}): {}", &gemfile, e))?;

        buf
    };

    Ok(re.is_match(&gemfile_data))
}

fn scan_features(settings: &GemConfig, info: Option<&GemBundle>)
    -> Result<Vec<packages::Package>, String>
{
    let mut res = vec!();
    res.push(packages::BuildEssential);

    if settings.install_ruby {
        res.push(packages::Ruby);
        res.push(packages::RubyDev);
    }

    res.push(packages::Https);
    res.push(packages::Bundler);

    if let Some(info) = info {
        let git_required = requires_git(&info.gemfile)?;
        if git_required {
            res.push(packages::Git);
        }
    }

    Ok(res)
}

pub fn install(distro: &mut Box<Distribution>,
    ctx: &mut Context, pkgs: &Vec<String>)
    -> Result<(), String>
{
    let features = scan_features(&ctx.gem_settings, None)?;
    packages::ensure_packages(distro, ctx, &features)?;

    configure(ctx)?;

    if pkgs.len() == 0 {
        return Ok(());
    }

    let gem_exe = ctx.gem_settings.gem_exe.clone()
        .unwrap_or(DEFAULT_GEM_EXE.to_owned());

    let mut cmd = command(ctx, &gem_exe)?;
    cmd.arg("install");
    cmd.args(&["--bindir", BIN_DIR]);

    let no_doc = no_doc_args(ctx)?;
    cmd.args(&no_doc);

    cmd.args(pkgs);
    run(cmd)?;
    Ok(())
}

pub fn bundle(distro: &mut Box<Distribution>,
    ctx: &mut Context, info: &GemBundle)
    -> Result<(), StepError>
{
    let features = scan_features(&ctx.gem_settings, Some(info))?;
    packages::ensure_packages(distro, ctx, &features)?;

    configure(ctx)?;

    let mut cmd = command(ctx, "bundle")?;
    cmd.args(&["install", "--system", "--binstubs", BIN_DIR]);

    cmd.arg("--gemfile");
    cmd.arg(&info.gemfile);

    if !info.without.is_empty() {
        cmd.arg("--without");
        cmd.args(&info.without);
    }

    if let Some(ref trust_policy) = info.trust_policy {
        if !VALID_TRUST_POLICIES.contains(&trust_policy.as_ref()) {
            return Err(From::from(format!(
                "Value of 'GemBundle.trust_policy' must be \
                    '{}', '{}' or '{}', '{}' given",
                VALID_TRUST_POLICIES[0],
                VALID_TRUST_POLICIES[1],
                VALID_TRUST_POLICIES[2],
                trust_policy
            )))
        }
        cmd.arg("--trust-policy");
        cmd.arg(trust_policy);
    }

    run(cmd)
}

pub fn configure(ctx: &mut Context) -> Result<(), String> {
    if ctx.gem_settings.gem_exe.is_none() &&
        ctx.gem_settings.update_gem
    {
        let mut args = vec!(
            DEFAULT_GEM_EXE.to_owned(),
            "update".to_owned(),
            "--system".to_owned(),
        );

        let version = gem_version(ctx)?;
        if version < GEM_VERSION_WITH_NO_DOCUMENT_OPT {
            args.extend(vec!("--no-rdoc".to_owned(), "--no-ri".to_owned()));
        } else {
            args.push("--no-document".to_owned());
        }

        // Debian based distros doesn't allow updating gem unless this flag is set
        let env = [("REALLY_GEM_UPDATE_SYSTEM", "1")];
        run_command_at_env(ctx, &args, Path::new("/work"), &env)?;
    }

    let gem_cache = gem_cache_dir(ctx)?;
    ctx.add_cache_dir(&gem_cache, "gems-cache".to_string())?;

    Ok(())
}

pub fn setup_bundler(ctx: &mut Context) -> Result<(), String> {
    configure(ctx)?;

    let gem_exe = ctx.gem_settings.gem_exe.clone()
        .unwrap_or(DEFAULT_GEM_EXE.to_owned());

    let mut cmd = command(ctx, gem_exe)?;
    cmd.args(&["install", "bundler"]);

    let no_doc = no_doc_args(ctx)?;
    cmd.args(&no_doc);

    run(cmd)?;

    Ok(())
}

pub fn list(ctx: &mut Context) -> Result<(), StepError> {
    let gem_exe = ctx.gem_settings.gem_exe.clone()
        .unwrap_or(DEFAULT_GEM_EXE.to_owned());

    let mut cmd = command(ctx, gem_exe)?;
    cmd.arg("list");
    cmd.arg("--local");

    capture_stdout(cmd)
        .and_then(|out| {
            File::create("/vagga/container/gems-list.txt")
            .and_then(|mut f| f.write_all(&out))
            .map_err(|e| format!("Error dumping gems package list: {}", e))
        })
        .map_err(|e| warn!("Can't list gems: {}", e)).ok();
    Ok(())
}

impl BuildStep for GemInstall {
    fn name(&self) -> &'static str { "GemInstall" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        hash.field("packages", &self.0);
        Ok(())
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if build {
            install(&mut guard.distro, &mut guard.ctx, &self.0)?;
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

impl BuildStep for GemConfig {
    fn name(&self) -> &'static str { "GemConfig" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        hash.field("install_ruby", self.install_ruby);
        hash.opt_field("gem_exe", &self.gem_exe);
        hash.field("update_gem", self.update_gem);
        Ok(())
    }
    fn build(&self, guard: &mut Guard, _build: bool)
        -> Result<(), StepError>
    {
        guard.ctx.gem_settings = self.clone();
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

impl BuildStep for GemBundle {
    fn name(&self) -> &'static str { "GemBundle" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        let path = Path::new("/work").join(&self.gemfile);

        hash.field("gemfile", &self.gemfile);
        let gemlock = path.parent()
            .map(|dir| dir.join("Gemfile.lock"))
            .ok_or("Gemfile should be under /work".to_owned())?;
        if gemlock.exists() {
            let mut lockfile = File::open(&path)
                .map_err(|e| VersionError::Io(e, gemlock.clone()))?;
            hash.file(&path, &mut lockfile)
                .map_err(|e| VersionError::Io(e, gemlock.clone()))?;
        }

        let f = File::open(&path)
            .map_err(|e| VersionError::Io(e, path.clone()))?;
        let reader = BufReader::new(f);

        for line in reader.lines() {
            let line = line
                .map_err(|e| VersionError::Io(e, path.clone()))?;
            let line = line.trim();
            if line.is_empty() || line.starts_with("#") {
                continue
            }
            hash.field("line", line);
        }

        Ok(())
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if build {
            bundle(&mut guard.distro, &mut guard.ctx, self)?;
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}
<commit_msg>Fix updating bundler under alpine<commit_after>use std::path::{Path, PathBuf};
use std::fs::{File, symlink_metadata};
use std::io::{Read, Write, BufReader, BufRead};

use regex::Regex;
use quire::validate as V;

use super::super::context::Context;
use super::super::packages;
use super::generic::{capture_command, run_command_at_env};
use builder::distrib::Distribution;
use builder::commands::generic::{command, run};
use process_util::capture_stdout;
use build_step::{BuildStep, VersionError, StepError, Digest, Config, Guard};

const DEFAULT_GEM_EXE: &'static str = "/usr/bin/gem";
const BIN_DIR: &'static str = "/usr/local/bin";
const GEM_VERSION_WITH_NO_DOCUMENT_OPT: f32 = 2.0;
const VALID_TRUST_POLICIES: [&'static str; 3] = ["LowSecurity", "MediumSecurity", "HighSecurity"];

#[derive(Debug, Deserialize)]
pub struct GemInstall(Vec<String>);

impl GemInstall {
    pub fn config() -> V::Sequence<'static> {
        V::Sequence::new(V::Scalar::new())
    }
}

#[derive(Deserialize, Debug, Clone)]
pub struct GemConfig {
    pub install_ruby: bool,
    pub gem_exe: Option<String>,
    pub update_gem: bool,
}

impl GemConfig {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("install_ruby", V::Scalar::new().default(true))
        .member("gem_exe", V::Scalar::new().optional())
        .member("update_gem", V::Scalar::new().default(true))
    }
}

#[derive(Deserialize, Debug)]
pub struct GemBundle {
    pub gemfile: PathBuf,
    pub without: Vec<String>,
    pub trust_policy: Option<String>,
}

impl GemBundle {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("gemfile", V::Scalar::new().default("Gemfile"))
        .member("without", V::Sequence::new(V::Scalar::new()))
        .member("trust_policy", V::Scalar::new().optional())
    }
}

impl Default for GemConfig {
    fn default() -> Self {
        GemConfig {
            install_ruby: true,
            gem_exe: None,
            update_gem: true,
        }
    }
}

fn no_doc_args(ctx: &mut Context) -> Result<Vec<&'static str>, String> {
    if ctx.gem_settings.update_gem {
        Ok(vec!("--no-document"))
    } else {
        let version = gem_version(ctx)?;
        if version < GEM_VERSION_WITH_NO_DOCUMENT_OPT {
            Ok(vec!("--no-rdoc", "--no-ri"))
        } else {
            Ok(vec!("--no-document"))
        }
    }
}

fn gem_version(ctx: &mut Context) -> Result<f32, String> {
    let gem_exe = ctx.gem_settings.gem_exe.clone()
        .unwrap_or(DEFAULT_GEM_EXE.to_owned());

    let args = [
        gem_exe,
        "--version".to_owned(),
    ];

    let gem_ver = capture_command(ctx, &args, &[])
        .and_then(|x| String::from_utf8(x)
            .map_err(|e| format!("Error parsing gem version: {}", e)))
        .map_err(|e| format!("Error getting gem version: {}", e))?;

    let re = Regex::new(r#"^(\d+?\.\d+?)\."#).expect("Invalid regex");
    let version = re.captures(&gem_ver)
        .and_then(|cap| cap.get(1))
        .ok_or("Gem version was not found".to_owned())?;

    version.as_str().parse::<f32>()
        .map_err(|e| format!("Erro parsing gem version: {}", e))
}

fn gem_cache_dir(ctx: &mut Context) -> Result<PathBuf, String> {
    let gem_exe = ctx.gem_settings.gem_exe.clone()
        .unwrap_or(DEFAULT_GEM_EXE.to_owned());

    let args = [
        gem_exe,
        "env".to_owned(),
        "gemdir".to_owned(),
    ];

    let gem_dir = capture_command(ctx, &args, &[])
        .and_then(|x| String::from_utf8(x)
            .map_err(|e| format!("Error getting gem dir: {}", e)))?;

    Ok(Path::new(gem_dir.trim()).join("cache"))
}

fn requires_git(gemfile: &Path) -> Result<bool, String> {
    let gemfile = Path::new("/work").join(gemfile);

    let re = Regex::new(
        r#"(git .*? do)|(:(git|github|gist|bitbucket) =>)|(git_source\(.*?\))"#
    ).expect("Invalid regex");

    let gemfile_data = {
        let mut buf = String::new();
        File::open(&gemfile)
            .and_then(|mut f| f.read_to_string(&mut buf))
            .map_err(|e| format!("Error reading Gemfile ({:?}): {}", &gemfile, e))?;

        buf
    };

    Ok(re.is_match(&gemfile_data))
}

fn scan_features(settings: &GemConfig, info: Option<&GemBundle>)
    -> Result<Vec<packages::Package>, String>
{
    let mut res = vec!();
    res.push(packages::BuildEssential);

    if settings.install_ruby {
        res.push(packages::Ruby);
        res.push(packages::RubyDev);
    }

    res.push(packages::Https);
    res.push(packages::Bundler);

    if let Some(info) = info {
        let git_required = requires_git(&info.gemfile)?;
        if git_required {
            res.push(packages::Git);
        }
    }

    Ok(res)
}

pub fn install(distro: &mut Box<Distribution>,
    ctx: &mut Context, pkgs: &Vec<String>)
    -> Result<(), String>
{
    let features = scan_features(&ctx.gem_settings, None)?;
    packages::ensure_packages(distro, ctx, &features)?;

    configure(ctx)?;

    if pkgs.len() == 0 {
        return Ok(());
    }

    let gem_exe = ctx.gem_settings.gem_exe.clone()
        .unwrap_or(DEFAULT_GEM_EXE.to_owned());

    let mut cmd = command(ctx, &gem_exe)?;
    cmd.arg("install");
    cmd.args(&["--bindir", BIN_DIR]);

    let no_doc = no_doc_args(ctx)?;
    cmd.args(&no_doc);

    cmd.args(pkgs);
    run(cmd)?;
    Ok(())
}

pub fn bundle(distro: &mut Box<Distribution>,
    ctx: &mut Context, info: &GemBundle)
    -> Result<(), StepError>
{
    let features = scan_features(&ctx.gem_settings, Some(info))?;
    packages::ensure_packages(distro, ctx, &features)?;

    configure(ctx)?;

    let mut cmd = command(ctx, "bundle")?;
    cmd.args(&["install", "--system", "--binstubs", BIN_DIR]);

    cmd.arg("--gemfile");
    cmd.arg(&info.gemfile);

    if !info.without.is_empty() {
        cmd.arg("--without");
        cmd.args(&info.without);
    }

    if let Some(ref trust_policy) = info.trust_policy {
        if !VALID_TRUST_POLICIES.contains(&trust_policy.as_ref()) {
            return Err(From::from(format!(
                "Value of 'GemBundle.trust_policy' must be \
                    '{}', '{}' or '{}', '{}' given",
                VALID_TRUST_POLICIES[0],
                VALID_TRUST_POLICIES[1],
                VALID_TRUST_POLICIES[2],
                trust_policy
            )))
        }
        cmd.arg("--trust-policy");
        cmd.arg(trust_policy);
    }

    run(cmd)
}

pub fn configure(ctx: &mut Context) -> Result<(), String> {
    if ctx.gem_settings.gem_exe.is_none() &&
        ctx.gem_settings.update_gem
    {
        let mut args = vec!(
            DEFAULT_GEM_EXE.to_owned(),
            "update".to_owned(),
            "--system".to_owned(),
        );

        let version = gem_version(ctx)?;
        if version < GEM_VERSION_WITH_NO_DOCUMENT_OPT {
            args.extend(vec!("--no-rdoc".to_owned(), "--no-ri".to_owned()));
        } else {
            args.push("--no-document".to_owned());
        }

        // Debian based distros doesn't allow updating gem unless this flag is set
        let env = [("REALLY_GEM_UPDATE_SYSTEM", "1")];
        run_command_at_env(ctx, &args, Path::new("/work"), &env)?;
    }

    let gem_cache = gem_cache_dir(ctx)?;
    ctx.add_cache_dir(&gem_cache, "gems-cache".to_string())?;

    Ok(())
}

pub fn setup_bundler(ctx: &mut Context) -> Result<(), String> {
    configure(ctx)?;

    let gem_exe = ctx.gem_settings.gem_exe.clone()
        .unwrap_or(DEFAULT_GEM_EXE.to_owned());

    // It looks like recent gem update --system installs bundler
    let bundler = symlink_metadata("/vagga/root/usr/bin/bundle").is_ok();
    if !bundler {
        let mut cmd = command(ctx, gem_exe)?;
        cmd.args(&["install", "bundler"]);

        let no_doc = no_doc_args(ctx)?;
        cmd.args(&no_doc);

        run(cmd)?;
    }

    Ok(())
}

pub fn list(ctx: &mut Context) -> Result<(), StepError> {
    let gem_exe = ctx.gem_settings.gem_exe.clone()
        .unwrap_or(DEFAULT_GEM_EXE.to_owned());

    let mut cmd = command(ctx, gem_exe)?;
    cmd.arg("list");
    cmd.arg("--local");

    capture_stdout(cmd)
        .and_then(|out| {
            File::create("/vagga/container/gems-list.txt")
            .and_then(|mut f| f.write_all(&out))
            .map_err(|e| format!("Error dumping gems package list: {}", e))
        })
        .map_err(|e| warn!("Can't list gems: {}", e)).ok();
    Ok(())
}

impl BuildStep for GemInstall {
    fn name(&self) -> &'static str { "GemInstall" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        hash.field("packages", &self.0);
        Ok(())
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if build {
            install(&mut guard.distro, &mut guard.ctx, &self.0)?;
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

impl BuildStep for GemConfig {
    fn name(&self) -> &'static str { "GemConfig" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        hash.field("install_ruby", self.install_ruby);
        hash.opt_field("gem_exe", &self.gem_exe);
        hash.field("update_gem", self.update_gem);
        Ok(())
    }
    fn build(&self, guard: &mut Guard, _build: bool)
        -> Result<(), StepError>
    {
        guard.ctx.gem_settings = self.clone();
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

impl BuildStep for GemBundle {
    fn name(&self) -> &'static str { "GemBundle" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        let path = Path::new("/work").join(&self.gemfile);

        hash.field("gemfile", &self.gemfile);
        let gemlock = path.parent()
            .map(|dir| dir.join("Gemfile.lock"))
            .ok_or("Gemfile should be under /work".to_owned())?;
        if gemlock.exists() {
            let mut lockfile = File::open(&path)
                .map_err(|e| VersionError::Io(e, gemlock.clone()))?;
            hash.file(&path, &mut lockfile)
                .map_err(|e| VersionError::Io(e, gemlock.clone()))?;
        }

        let f = File::open(&path)
            .map_err(|e| VersionError::Io(e, path.clone()))?;
        let reader = BufReader::new(f);

        for line in reader.lines() {
            let line = line
                .map_err(|e| VersionError::Io(e, path.clone()))?;
            let line = line.trim();
            if line.is_empty() || line.starts_with("#") {
                continue
            }
            hash.field("line", line);
        }

        Ok(())
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if build {
            bundle(&mut guard.distro, &mut guard.ctx, self)?;
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}
<|endoftext|>


<commit_before>#![allow(unstable)] // For to_lowercase

pub fn anagrams_for<'a>(word: &str, inputs: &[&'a str]) -> Vec<&'a str> {
    let lower_word: Vec<char> = word.chars().map(|c| c.to_lowercase()).collect();
    let mut norm_word = lower_word.clone();
    norm_word.sort();
    inputs.iter()
        .filter(|other| {
            let lower_other: Vec<char> = other.chars().map(|c| c.to_lowercase()).collect();
            let mut norm_other = lower_other.clone();
            norm_other.sort();
            lower_other != lower_word && norm_other == norm_word
        })
        .map(|s| *s)
        .collect()
}
<commit_msg>update anagrams to use newest rust<commit_after>use std::slice::SliceExt;

fn sort(word: &String) -> String {
    let mut sorted: Vec<char> = word.chars().collect();
    sorted.sort();
    let mut output = String::with_capacity(sorted.len());
    for c in sorted.drain() {
        output.push(c);
    }
    output
}

pub fn anagrams_for<'a>(word: &str, inputs: &[&'a str]) -> Vec<&'a str> {
    let lower = word.to_lowercase();
    let sorted = sort(&lower);
    let mut anagrams = Vec::new();
    for input in inputs.iter() {
        let input_lower = input.to_lowercase();
        if lower != input_lower {
            if sorted == sort(&input_lower) {
                anagrams.push(*input);
            }
        }
    }
    anagrams
}
<|endoftext|>


<commit_before><commit_msg>wc: return UResult from uumain() function<commit_after><|endoftext|>


<commit_before>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Small system utility modules for usage by other modules.

extern crate data_model;
extern crate libc;
extern crate syscall_defines;
#[allow(unused_imports)]
#[macro_use]
extern crate poll_token_derive;

#[macro_use]
pub mod handle_eintr;
#[macro_use]
pub mod ioctl;
#[macro_use]
pub mod syslog;
mod mmap;
mod shm;
mod eventfd;
mod errno;
mod guest_address;
mod guest_memory;
mod poll;
mod struct_util;
mod tempdir;
mod terminal;
pub mod signal;
mod fork;
mod signalfd;
mod sock_ctrl_msg;
mod passwd;

pub use mmap::*;
pub use shm::*;
pub use eventfd::*;
pub use errno::{Error, Result};
use errno::errno_result;
pub use guest_address::*;
pub use guest_memory::*;
pub use poll::*;
pub use struct_util::*;
pub use tempdir::*;
pub use terminal::*;
pub use signal::*;
pub use fork::*;
pub use signalfd::*;
pub use ioctl::*;
pub use sock_ctrl_msg::*;
pub use passwd::*;
pub use poll_token_derive::*;

pub use mmap::Error as MmapError;
pub use guest_memory::Error as GuestMemoryError;
pub use signalfd::Error as SignalFdError;

use std::ffi::CStr;
use std::os::unix::io::AsRawFd;
use std::ptr;

use libc::{kill, syscall, sysconf, waitpid, c_long, pid_t, uid_t, gid_t, _SC_PAGESIZE,
           SIGKILL, WNOHANG};

use syscall_defines::linux::LinuxSyscall::SYS_getpid;

/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
#[inline(always)]
pub fn pagesize() -> usize {
    // Trivially safe
    unsafe { sysconf(_SC_PAGESIZE) as usize }
}

/// This bypasses `libc`'s caching `getpid(2)` wrapper which can be invalid if a raw clone was used
/// elsewhere.
#[inline(always)]
pub fn getpid() -> pid_t {
    // Safe because this syscall can never fail and we give it a valid syscall number.
    unsafe { syscall(SYS_getpid as c_long) as pid_t }
}

/// Safe wrapper for `geteuid(2)`.
#[inline(always)]
pub fn geteuid() -> uid_t {
    // trivially safe
    unsafe { libc::geteuid() }
}

/// Safe wrapper for `getegid(2)`.
#[inline(always)]
pub fn getegid() -> gid_t {
    // trivially safe
    unsafe { libc::getegid() }
}

/// Safe wrapper for chown(2).
#[inline(always)]
pub fn chown(path: &CStr, uid: uid_t, gid: gid_t) -> Result<()> {
    // Safe since we pass in a valid string pointer and check the return value.
    let ret = unsafe { libc::chown(path.as_ptr(), uid, gid) };

    if ret < 0 {
        errno_result()
    } else {
        Ok(())
    }
}

/// The operation to perform with `flock`.
pub enum FlockOperation {
    LockShared,
    LockExclusive,
    Unlock,
}

/// Safe wrapper for flock(2) with the operation `op` and optionally `nonblocking`. The lock will be
/// dropped automatically when `file` is dropped.
#[inline(always)]
pub fn flock(file: &AsRawFd, op: FlockOperation, nonblocking: bool) -> Result<()> {
    let mut operation = match op {
        FlockOperation::LockShared => libc::LOCK_SH,
        FlockOperation::LockExclusive => libc::LOCK_EX,
        FlockOperation::Unlock => libc::LOCK_UN,
    };

    if nonblocking {
        operation |= libc::LOCK_NB;
    }

    // Safe since we pass in a valid fd and flock operation, and check the return value.
    let ret = unsafe { libc::flock(file.as_raw_fd(), operation) };

    if ret < 0 {
        errno_result()
    } else {
        Ok(())
    }
}

/// Reaps a child process that has terminated.
///
/// Returns `Ok(pid)` where `pid` is the process that was reaped or `Ok(0)` if none of the children
/// have terminated. An `Error` is with `errno == ECHILD` if there are no children left to reap.
///
/// # Examples
///
/// Reaps all child processes until there are no terminated children to reap.
///
/// ```
/// # extern crate libc;
/// # extern crate sys_util;
/// fn reap_children() {
///     loop {
///         match sys_util::reap_child() {
///             Ok(0) => println!("no children ready to reap"),
///             Ok(pid) => {
///                 println!("reaped {}", pid);
///                 continue
///             },
///             Err(e) if e.errno() == libc::ECHILD => println!("no children left"),
///             Err(e) => println!("error reaping children: {:?}", e),
///         }
///         break
///     }
/// }
/// ```
pub fn reap_child() -> Result<pid_t> {
    // Safe because we pass in no memory, prevent blocking with WNOHANG, and check for error.
    let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
    if ret == -1 {
        errno_result()
    } else {
        Ok(ret)
    }
}

/// Kill all processes in the current process group.
///
/// On success, this kills all processes in the current process group, including the current
/// process, meaning this will not return. This is equivalent to a call to `kill(0, SIGKILL)`.
pub fn kill_process_group() -> Result<()> {
    let ret = unsafe { kill(0, SIGKILL) };
    if ret == -1 {
        errno_result()
    } else {
        // Kill succeeded, so this process never reaches here.
        unreachable!();
    }
}
<commit_msg>sys_util: add function for creating pipe pairs<commit_after>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Small system utility modules for usage by other modules.

extern crate data_model;
extern crate libc;
extern crate syscall_defines;
#[allow(unused_imports)]
#[macro_use]
extern crate poll_token_derive;

#[macro_use]
pub mod handle_eintr;
#[macro_use]
pub mod ioctl;
#[macro_use]
pub mod syslog;
mod mmap;
mod shm;
mod eventfd;
mod errno;
mod guest_address;
mod guest_memory;
mod poll;
mod struct_util;
mod tempdir;
mod terminal;
pub mod signal;
mod fork;
mod signalfd;
mod sock_ctrl_msg;
mod passwd;

pub use mmap::*;
pub use shm::*;
pub use eventfd::*;
pub use errno::{Error, Result};
use errno::errno_result;
pub use guest_address::*;
pub use guest_memory::*;
pub use poll::*;
pub use struct_util::*;
pub use tempdir::*;
pub use terminal::*;
pub use signal::*;
pub use fork::*;
pub use signalfd::*;
pub use ioctl::*;
pub use sock_ctrl_msg::*;
pub use passwd::*;
pub use poll_token_derive::*;

pub use mmap::Error as MmapError;
pub use guest_memory::Error as GuestMemoryError;
pub use signalfd::Error as SignalFdError;

use std::ffi::CStr;
use std::fs::File;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::ptr;

use libc::{kill, syscall, sysconf, waitpid, pipe2, c_long, pid_t, uid_t, gid_t, _SC_PAGESIZE,
           SIGKILL, WNOHANG, O_CLOEXEC};

use syscall_defines::linux::LinuxSyscall::SYS_getpid;

/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
#[inline(always)]
pub fn pagesize() -> usize {
    // Trivially safe
    unsafe { sysconf(_SC_PAGESIZE) as usize }
}

/// This bypasses `libc`'s caching `getpid(2)` wrapper which can be invalid if a raw clone was used
/// elsewhere.
#[inline(always)]
pub fn getpid() -> pid_t {
    // Safe because this syscall can never fail and we give it a valid syscall number.
    unsafe { syscall(SYS_getpid as c_long) as pid_t }
}

/// Safe wrapper for `geteuid(2)`.
#[inline(always)]
pub fn geteuid() -> uid_t {
    // trivially safe
    unsafe { libc::geteuid() }
}

/// Safe wrapper for `getegid(2)`.
#[inline(always)]
pub fn getegid() -> gid_t {
    // trivially safe
    unsafe { libc::getegid() }
}

/// Safe wrapper for chown(2).
#[inline(always)]
pub fn chown(path: &CStr, uid: uid_t, gid: gid_t) -> Result<()> {
    // Safe since we pass in a valid string pointer and check the return value.
    let ret = unsafe { libc::chown(path.as_ptr(), uid, gid) };

    if ret < 0 {
        errno_result()
    } else {
        Ok(())
    }
}

/// The operation to perform with `flock`.
pub enum FlockOperation {
    LockShared,
    LockExclusive,
    Unlock,
}

/// Safe wrapper for flock(2) with the operation `op` and optionally `nonblocking`. The lock will be
/// dropped automatically when `file` is dropped.
#[inline(always)]
pub fn flock(file: &AsRawFd, op: FlockOperation, nonblocking: bool) -> Result<()> {
    let mut operation = match op {
        FlockOperation::LockShared => libc::LOCK_SH,
        FlockOperation::LockExclusive => libc::LOCK_EX,
        FlockOperation::Unlock => libc::LOCK_UN,
    };

    if nonblocking {
        operation |= libc::LOCK_NB;
    }

    // Safe since we pass in a valid fd and flock operation, and check the return value.
    let ret = unsafe { libc::flock(file.as_raw_fd(), operation) };

    if ret < 0 {
        errno_result()
    } else {
        Ok(())
    }
}

/// Reaps a child process that has terminated.
///
/// Returns `Ok(pid)` where `pid` is the process that was reaped or `Ok(0)` if none of the children
/// have terminated. An `Error` is with `errno == ECHILD` if there are no children left to reap.
///
/// # Examples
///
/// Reaps all child processes until there are no terminated children to reap.
///
/// ```
/// # extern crate libc;
/// # extern crate sys_util;
/// fn reap_children() {
///     loop {
///         match sys_util::reap_child() {
///             Ok(0) => println!("no children ready to reap"),
///             Ok(pid) => {
///                 println!("reaped {}", pid);
///                 continue
///             },
///             Err(e) if e.errno() == libc::ECHILD => println!("no children left"),
///             Err(e) => println!("error reaping children: {:?}", e),
///         }
///         break
///     }
/// }
/// ```
pub fn reap_child() -> Result<pid_t> {
    // Safe because we pass in no memory, prevent blocking with WNOHANG, and check for error.
    let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
    if ret == -1 {
        errno_result()
    } else {
        Ok(ret)
    }
}

/// Kill all processes in the current process group.
///
/// On success, this kills all processes in the current process group, including the current
/// process, meaning this will not return. This is equivalent to a call to `kill(0, SIGKILL)`.
pub fn kill_process_group() -> Result<()> {
    let ret = unsafe { kill(0, SIGKILL) };
    if ret == -1 {
        errno_result()
    } else {
        // Kill succeeded, so this process never reaches here.
        unreachable!();
    }
}

/// Spawns a pipe pair where the first pipe is the read end and the second pipe is the write end.
///
/// If `close_on_exec` is true, the `O_CLOEXEC` flag will be set during pipe creation.
pub fn pipe(close_on_exec: bool) -> Result<(File, File)> {
    let flags = if close_on_exec { O_CLOEXEC } else { 0 };
    let mut pipe_fds = [-1; 2];
    // Safe because pipe2 will only write 2 element array of i32 to the given pointer, and we check
    // for error.
    let ret = unsafe { pipe2(&mut pipe_fds[0], flags) };
    if ret == -1 {
        errno_result()
    } else {
        // Safe because both fds must be valid for pipe2 to have returned sucessfully and we have
        // exclusive ownership of them.
        Ok(unsafe {
            (
                File::from_raw_fd(pipe_fds[0]),
                File::from_raw_fd(pipe_fds[1])
            )
        })
    }
}
<|endoftext|>


<commit_before>use analysis::c_type::rustify_pointers;
use analysis::rust_type::Result;
use env::Env;
use library;
use library::*;
use nameutil::crate_name;
use traits::*;

// FIXME: This module needs redundant allocations audit
// TODO: ffi_type computations should be cached

pub fn ffi_type(env: &Env, tid: library::TypeId, c_type: &str) -> Result {
    let (ptr, inner) = rustify_pointers(c_type);
    let res = if ptr.is_empty() {
        if let Some(c_tid) = env.library.find_type(0, c_type) {
            // Fast track plain fundamental types avoiding some checks
            if env.library.type_(c_tid).maybe_ref_as::<Fundamental>().is_some() {
                if let &library::Type::FixedArray(_, size) = env.library.type_(tid) {
                    ffi_inner(env, c_tid, c_type.into())
                        .map_any(|s| format!("[{}; {}]", s, size))
                }
                else {
                    ffi_inner(env, c_tid, c_type.into())
                }
            }
            else { // c_type isn't fundamental
                ffi_inner(env, tid, inner)
            }
        }
        else { // c_type doesn't match any type in the library by name
            ffi_inner(env, tid, inner)
        }
    }
    else { // ptr not empty
        ffi_inner(env, tid, inner)
            .map_any(|s| format!("{} {}", ptr, s))
    };
    trace!("ffi_type({:?}, {}) -> {:?}", tid, c_type, res);
    res
}

fn ffi_inner(env: &Env, tid: library::TypeId, mut inner: String) -> Result {
    let volatile = inner.starts_with("volatile ");
    if volatile {
        inner = inner["volatile ".len()..].into();
    }

    let typ = env.library.type_(tid);
    let res = match *typ {
        Type::Fundamental(fund) => {
            use library::Fundamental::*;
            let inner = match fund {
                None => "c_void",
                Boolean => "gboolean",
                Int8 => "i8",
                UInt8 => "u8",
                Int16 => "i16",
                UInt16 => "u16",
                Int32 => "i32",
                UInt32 => "u32",
                Int64 => "i64",
                UInt64 => "u64",
                Char => "c_char",
                UChar => "c_uchar",
                Short => "c_short",
                UShort => "c_ushort",
                Int => "c_int",
                UInt => "c_uint",
                Long => "c_long",
                ULong => "c_ulong",
                Size => "size_t",
                SSize => "ssize_t",
                Float => "c_float",
                Double => "c_double",
                UniChar => "u32",
                Utf8 => "c_char",
                Filename => "c_char",
                Type => "GType",
                Pointer => match &inner[..]  {
                    "void" => "c_void",
                    "tm" => return Err(inner),  //TODO: try use time:Tm
                    _ => &*inner,
                },
                Unsupported => return Err(format!("[Unsupported type {}]", inner)),
                VarArgs => panic!("Should not reach here"),
            };
            Ok(inner.into())
        }
        Type::Record(..) | Type::Alias(..) | Type::Function(..) => {
            if let Some(declared_c_type) = typ.get_glib_name() {
                if declared_c_type != inner {
                    let msg = format!("[c:type mismatch `{}` != `{}` of `{}`]",
                                      inner, declared_c_type, typ.get_name());
                    warn!("{}", msg);
                    return Err(msg);
                }
            }
            else {
                warn!("Type `{}` missing c_type", typ.get_name());
            }
            fix_name(env, tid, &inner)
        }
        Type::CArray(inner_tid) => ffi_inner(env, inner_tid, inner),
        Type::FixedArray(inner_tid, size) => {
            match ffi_inner(env, inner_tid, inner) {
                Ok(s) => Ok(format!("[{}; {}]", s, size)),
                Err(s) => Err(format!("[{}; {}]", s, size)),
            }
        }
        Type::Array(..) | Type::PtrArray(..)
                | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
            fix_name(env, tid, &inner)
        }
        _ => {
            if let Some(glib_name) = env.library.type_(tid).get_glib_name() {
                if inner != glib_name && !implements_c_type(&env.library, tid, &inner) {
                    let msg = format!("[c:type mismatch {} != {} of {}]", inner, glib_name,
                      env.library.type_(tid).get_name());
                    warn!("{}", msg);
                    Err(msg)
                }
                else {
                    fix_name(env, tid, &inner)
                }
            }
            else {
                let msg = format!("[Missing glib_name of {}, can't match != {}]",
                                  env.library.type_(tid).get_name(), inner);
                warn!("{}", msg);
                Err(msg)
            }
        }
    };

    if volatile {
        res.map(|s| format!("Volatile<{}>", s))
    }
    else {
        res
    }
}

fn fix_name(env: &Env, type_id: library::TypeId, name: &str) -> Result {
    if type_id.ns_id == library::INTERNAL_NAMESPACE {
        match *env.library.type_(type_id) {
            Type::Array(..) | Type::PtrArray(..)
                    | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
                if Some(MAIN_NAMESPACE) == env.library.find_namespace("GLib") {
                    Ok(name.into())
                }
                else {
                    Ok(format!("{}::{}", crate_name("GLib"), name))
                }
            }
            _ => Ok(name.into())
        }
    } else {
        let name_with_prefix = if type_id.ns_id == library::MAIN_NAMESPACE {
            name.into()
        } else {
            format!("{}::{}", fix_namespace(env, type_id), name)
        };
        if env.type_status_sys(&type_id.full_name(&env.library)).ignored() {
            Err(name_with_prefix)
        } else {
            Ok(name_with_prefix)
        }
    }
}

//TODO: check if need to use in non sys codegen
fn fix_namespace(env: &Env, type_id: library::TypeId) -> String {
    crate_name(&env.library.namespace(type_id.ns_id).name)
}

fn implements_c_type(library: &library::Library, tid: TypeId, c_type: &str) -> bool {
    if let Some(ref klass) = library.type_(tid).maybe_ref_as::<library::Class>() {
        klass.implements.iter().chain(klass.parents.iter())
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else if let Some(ref iface) = library.type_(tid).maybe_ref_as::<library::Interface>() {
        iface.prereq_parents.iter()
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else {
        false
    }
}
<commit_msg>Auto merge of #116 - gkoz:base_c_type, r=gkoz<commit_after>use analysis::c_type::rustify_pointers;
use analysis::rust_type::Result;
use env::Env;
use library;
use library::*;
use nameutil::crate_name;
use traits::*;

// FIXME: This module needs redundant allocations audit
// TODO: ffi_type computations should be cached

pub fn ffi_type(env: &Env, tid: library::TypeId, c_type: &str) -> Result {
    let (ptr, inner) = rustify_pointers(c_type);
    let res = if ptr.is_empty() {
        if let Some(c_tid) = env.library.find_type(0, c_type) {
            // Fast track plain fundamental types avoiding some checks
            if env.library.type_(c_tid).maybe_ref_as::<Fundamental>().is_some() {
                if let &library::Type::FixedArray(_, size) = env.library.type_(tid) {
                    ffi_inner(env, c_tid, c_type.into())
                        .map_any(|s| format!("[{}; {}]", s, size))
                }
                else {
                    ffi_inner(env, c_tid, c_type.into())
                }
            }
            else { // c_type isn't fundamental
                ffi_inner(env, tid, inner)
            }
        }
        else { // c_type doesn't match any type in the library by name
            ffi_inner(env, tid, inner)
        }
    }
    else { // ptr not empty
        ffi_inner(env, tid, inner)
            .map_any(|s| format!("{} {}", ptr, s))
    };
    trace!("ffi_type({:?}, {}) -> {:?}", tid, c_type, res);
    res
}

fn ffi_inner(env: &Env, tid: library::TypeId, mut inner: String) -> Result {
    let volatile = inner.starts_with("volatile ");
    if volatile {
        inner = inner["volatile ".len()..].into();
    }

    let typ = env.library.type_(tid);
    let res = match *typ {
        Type::Fundamental(fund) => {
            use library::Fundamental::*;
            let inner = match fund {
                None => "c_void",
                Boolean => "gboolean",
                Int8 => "i8",
                UInt8 => "u8",
                Int16 => "i16",
                UInt16 => "u16",
                Int32 => "i32",
                UInt32 => "u32",
                Int64 => "i64",
                UInt64 => "u64",
                Char => "c_char",
                UChar => "c_uchar",
                Short => "c_short",
                UShort => "c_ushort",
                Int => "c_int",
                UInt => "c_uint",
                Long => "c_long",
                ULong => "c_ulong",
                Size => "size_t",
                SSize => "ssize_t",
                Float => "c_float",
                Double => "c_double",
                UniChar => "u32",
                Utf8 => "c_char",
                Filename => "c_char",
                Type => "GType",
                Pointer => match &inner[..]  {
                    "void" => "c_void",
                    "tm" => return Err(inner),  //TODO: try use time:Tm
                    _ => &*inner,
                },
                Unsupported => return Err(format!("[Unsupported type {}]", inner)),
                VarArgs => panic!("Should not reach here"),
            };
            Ok(inner.into())
        }
        Type::Record(..) | Type::Alias(..) | Type::Function(..) => {
            if let Some(declared_c_type) = typ.get_glib_name() {
                if declared_c_type != inner {
                    let msg = format!("[c:type mismatch `{}` != `{}` of `{}`]",
                                      inner, declared_c_type, typ.get_name());
                    warn!("{}", msg);
                    return Err(msg);
                }
            }
            else {
                warn!("Type `{}` missing c_type", typ.get_name());
            }
            fix_name(env, tid, &inner)
        }
        Type::CArray(inner_tid) => ffi_inner(env, inner_tid, inner),
        Type::FixedArray(inner_tid, size) => {
            match ffi_inner(env, inner_tid, inner) {
                Ok(s) => Ok(format!("[{}; {}]", s, size)),
                Err(s) => Err(format!("[{}; {}]", s, size)),
            }
        }
        Type::Array(..) | Type::PtrArray(..)
                | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
            fix_name(env, tid, &inner)
        }
        _ => {
            if let Some(glib_name) = env.library.type_(tid).get_glib_name() {
                if inner != glib_name {
                    if implements_c_type(&env.library, tid, &inner) {
                        info!("[c:type {} of {} <: {}, fixing]", glib_name,
                            env.library.type_(tid).get_name(), inner);
                        fix_name(env, tid, &glib_name)
                    }
                    else {
                        let msg = format!("[c:type mismatch {} != {} of {}]", inner, glib_name,
                            env.library.type_(tid).get_name());
                        warn!("{}", msg);
                        Err(msg)
                    }
                }
                else {
                    fix_name(env, tid, &inner)
                }
            }
            else {
                let msg = format!("[Missing glib_name of {}, can't match != {}]",
                                  env.library.type_(tid).get_name(), inner);
                warn!("{}", msg);
                Err(msg)
            }
        }
    };

    if volatile {
        res.map(|s| format!("Volatile<{}>", s))
    }
    else {
        res
    }
}

fn fix_name(env: &Env, type_id: library::TypeId, name: &str) -> Result {
    if type_id.ns_id == library::INTERNAL_NAMESPACE {
        match *env.library.type_(type_id) {
            Type::Array(..) | Type::PtrArray(..)
                    | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
                if Some(MAIN_NAMESPACE) == env.library.find_namespace("GLib") {
                    Ok(name.into())
                }
                else {
                    Ok(format!("{}::{}", crate_name("GLib"), name))
                }
            }
            _ => Ok(name.into())
        }
    } else {
        let name_with_prefix = if type_id.ns_id == library::MAIN_NAMESPACE {
            name.into()
        } else {
            format!("{}::{}", fix_namespace(env, type_id), name)
        };
        if env.type_status_sys(&type_id.full_name(&env.library)).ignored() {
            Err(name_with_prefix)
        } else {
            Ok(name_with_prefix)
        }
    }
}

//TODO: check if need to use in non sys codegen
fn fix_namespace(env: &Env, type_id: library::TypeId) -> String {
    crate_name(&env.library.namespace(type_id.ns_id).name)
}

fn implements_c_type(library: &library::Library, tid: TypeId, c_type: &str) -> bool {
    if let Some(ref klass) = library.type_(tid).maybe_ref_as::<library::Class>() {
        klass.implements.iter().chain(klass.parents.iter())
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else if let Some(ref iface) = library.type_(tid).maybe_ref_as::<library::Interface>() {
        iface.prereq_parents.iter()
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else {
        false
    }
}
<|endoftext|>