<commit_before>extern crate nannou;

use nannou::prelude::*;
use nannou::ui::prelude::*;

fn main() {
    nannou::run(model, update, draw);
}

struct Model {
    ui: nannou::Ui,
    ids: Ids,
}

struct Ids {
    text: widget::Id,
    background: widget::Id,
}

fn model(app: &App) -> Model {
    // Set the loop mode to wait for events, an energy-efficient option for pure-GUI apps.
    app.set_loop_mode(LoopMode::wait(3));

    // Create the window.
    let window = app.new_window().build().unwrap();

    // Create the UI.
    let mut ui = app.new_ui(window).build().unwrap();

    // Generate some ids for our widgets.
    let ids = Ids {
        text: ui.generate_widget_id(),
        background: ui.generate_widget_id(),
    };

    Model { ui, ids }
}

fn update(_app: &App, mut model: Model, event: Event) -> Model {
    match event {
        // Handle window events like mouse, keyboard, resize, etc here.
        Event::WindowEvent { simple: Some(event), .. } => {
            println!("{:?}", event);
        },
        // `Update` the model here.
        Event::Update(update) => {

            // Calling `set_widgets` allows us to instantiate some widgets.
            let ui = &mut model.ui.set_widgets();

            // Place a canvas as the background. A canvas auto-sizes to the window if no dimensions
            // were specified.
            widget::Canvas::new().color(color::DARK_BLUE).set(model.ids.background, ui);

            // Draw the update event using a `Text` widget.
            let text = format!("{:#?}", update);
            widget::Text::new(&text)
                .color(color::WHITE)
                .middle_of(model.ids.background)
                .set(model.ids.text, ui);
        },
        _ => (),
    }
    model
}

// Draw the state of your `Model` into the given `Frame` here.
fn draw(app: &App, model: &Model, frame: Frame) -> Frame {
    // Draw the state of the `Ui` to the frame.
    model.ui.draw_to_frame(app, &frame).unwrap();
    // Return the drawn frame.
    frame
}
<commit_msg>updated the simple_ui example so you can change the background color using a button widget<commit_after>extern crate nannou;

use nannou::prelude::*;
use nannou::ui::prelude::*;
use nannou::rand::random;

fn main() {
    nannou::run(model, update, draw);
}

struct Model {
    ui: nannou::Ui,
    ids: Ids,
    bg_color: nannou::ui::Color,
}

struct Ids {
    button: widget::Id,
    text: widget::Id,
    background: widget::Id,
}

fn model(app: &App) -> Model {
    // Set the loop mode to wait for events, an energy-efficient option for pure-GUI apps.
    app.set_loop_mode(LoopMode::wait(3));

    // Create the window.
    let window = app.new_window().with_vsync(true).build().unwrap();

    // Create the UI.
    let mut ui = app.new_ui(window).build().unwrap();

    // Generate some ids for our widgets.
    let ids = Ids {
        button: ui.generate_widget_id(),
        text: ui.generate_widget_id(),
        background: ui.generate_widget_id(),
    };

    // Init background color
    let bg_color = color::rgba(1.0,0.0,1.0,1.0);

    Model { ui, ids, bg_color }
}

fn update(_app: &App, mut model: Model, event: Event) -> Model {
    match event {
        // Handle window events like mouse, keyboard, resize, etc here.
        Event::WindowEvent { simple: Some(event), .. } => {
            println!("{:?}", event);
        },
        // `Update` the model here.
        Event::Update(update) => {

            // Calling `set_widgets` allows us to instantiate some widgets.
            let ui = &mut model.ui.set_widgets();

            // Place a canvas as the background. A canvas auto-sizes to the window if no dimensions
            // were specified.
            widget::Canvas::new().color(model.bg_color).set(model.ids.background, ui);

            // Draw the button and increment `count` if pressed.
            for _click in widget::Button::new()
                .up_from(model.ids.text,100.0)
                .w_h(180.0, 80.0)
                .label("random bg color")
                .set(model.ids.button, ui)
            {
                model.bg_color = color::rgba(random(),random(),random(),1.0);
            }

            // Draw the update event using a `Text` widget.
            let text = format!("{:#?}", update);
            widget::Text::new(&text)
                .color(color::WHITE)
                .middle_of(model.ids.background)
                .set(model.ids.text, ui);
        },
        _ => (),
    }
    model
}

// Draw the state of your `Model` into the given `Frame` here.
fn draw(app: &App, model: &Model, frame: Frame) -> Frame {
    // Draw the state of the `Ui` to the frame.
    model.ui.draw_to_frame(app, &frame).unwrap();
    // Return the drawn frame.
    frame
}
<|endoftext|>


<commit_before>use crate::bitstream::*;
use crate::chip::*;
use crate::database::*;
use crate::wires;
use std::collections::{BTreeMap, BTreeSet};
use std::iter::FromIterator;

pub enum FuzzMode {
    Pip {
        to_wire: String,
        full_mux: bool, // if true, explicit 0s instead of base will be created for unset bits for a setting
        skip_fixed: bool, // if true, skip pips that have no bits associated with them (rather than created fixed conns)
        fixed_conn_tile: String,
    },
    Word {
        name: String,
        width: usize,
    },
    Enum {
        name: String,
        include_zeros: bool, // if true, explicit 0s instead of base will be created for unset bits for a setting
        disambiguate: bool,  // add explicit 0s to disambiguate settings only
    },
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
enum FuzzKey {
    PipKey { from_wire: String },
    WordKey { bit: usize },
    EnumKey { option: String },
}

pub struct Fuzzer {
    mode: FuzzMode,
    tiles: BTreeSet<String>,
    base: Chip,                           // bitstream with nothing set
    deltas: BTreeMap<FuzzKey, ChipDelta>, // used for arcs, words and enums
}

impl Fuzzer {
    pub fn init_pip_fuzzer(
        base_bit: &Chip,
        fuzz_tiles: &BTreeSet<String>,
        to_wire: &str,
        fixed_conn_tile: &str,
        full_mux: bool,
        skip_fixed: bool,
    ) -> Fuzzer {
        Fuzzer {
            mode: FuzzMode::Pip {
                to_wire: to_wire.to_string(),
                full_mux: full_mux,
                skip_fixed: skip_fixed,
                fixed_conn_tile: fixed_conn_tile.to_string(),
            },
            tiles: fuzz_tiles.clone(),
            base: base_bit.clone(),
            deltas: BTreeMap::new(),
        }
    }
    pub fn init_word_fuzzer(
        db: &mut Database,
        base_bit: &Chip,
        fuzz_tiles: &BTreeSet<String>,
        name: &str,
        width: usize,
        zero_bitfile: &str,
    ) -> Fuzzer {
        Fuzzer {
            mode: FuzzMode::Word {
                name: name.to_string(),
                width: width,
            },
            tiles: fuzz_tiles.clone(),
            base: base_bit.clone(),
            deltas: BTreeMap::new(),
        }
    }
    pub fn init_enum_fuzzer(
        base_bit: &Chip,
        fuzz_tiles: &BTreeSet<String>,
        name: &str,
        include_zeros: bool,
    ) -> Fuzzer {
        Fuzzer {
            mode: FuzzMode::Enum {
                name: name.to_string(),
                include_zeros: include_zeros,
                disambiguate: false, // fixme
            },
            tiles: fuzz_tiles.clone(),
            base: base_bit.clone(),
            deltas: BTreeMap::new(),
        }
    }
    fn add_sample(&mut self, db: &mut Database, key: FuzzKey, bitfile: &str) {
        let parsed_bitstream = BitstreamParser::parse_file(db, bitfile).unwrap();
        let delta = parsed_bitstream.delta(&self.base);
        self.deltas.insert(key, delta);
    }
    pub fn add_pip_sample(&mut self, db: &mut Database, from_wire: &str, bitfile: &str) {
        self.add_sample(
            db,
            FuzzKey::PipKey {
                from_wire: from_wire.to_string(),
            },
            bitfile,
        );
    }
    pub fn add_word_sample(&mut self, db: &mut Database, index: usize, bitfile: &str) {
        self.add_sample(db, FuzzKey::WordKey { bit: index }, bitfile);
    }
    pub fn add_enum_sample(&mut self, db: &mut Database, option: &str, bitfile: &str) {
        self.add_sample(
            db,
            FuzzKey::EnumKey {
                option: option.to_string(),
            },
            bitfile,
        );
    }
    pub fn solve(&mut self, db: &mut Database) {
        // Get a set of tiles that have been changed
        let changed_tiles: BTreeSet<String> = self
            .deltas
            .iter()
            .flat_map(|(_k, v)| v.keys())
            .filter(|t| self.tiles.contains(*t))
            .map(String::to_string)
            .collect();
        match &self.mode {
            FuzzMode::Pip {
                to_wire,
                full_mux,
                skip_fixed,
                fixed_conn_tile,
            } => {
                // In full mux mode; we need the coverage sets of the changes
                let mut coverage: BTreeMap<String, BTreeSet<(usize, usize)>> = BTreeMap::new();
                if *full_mux {
                    for tile in self.tiles.iter() {
                        coverage.insert(
                            tile.to_string(),
                            self.deltas
                                .iter()
                                .filter_map(|(_k, v)| v.get(tile))
                                .flatten()
                                .map(|(f, b, _v)| (*f, *b))
                                .collect(),
                        );
                    }
                }

                for (key, value) in self.deltas.iter() {
                    if let FuzzKey::PipKey { from_wire } = key {
                        if value.iter().any(|(k, _v)| !self.tiles.contains(k)) {
                            // If this pip affects tiles outside of the fuzz region, skip it
                            continue;
                        }
                        if changed_tiles.len() == 0 {
                            // No changes; it is a fixed connection
                            if *skip_fixed {
                                continue;
                            }
                            let db_tile = self.base.tile_by_name(fixed_conn_tile).unwrap();
                            let tile_db = db.tile_bitdb(&self.base.family, &db_tile.tiletype);
                            tile_db.add_conn(
                                &wires::normalize_wire(&self.base, db_tile, from_wire),
                                &wires::normalize_wire(&self.base, db_tile, to_wire),
                            );
                        } else {
                            for tile in changed_tiles.iter() {
                                if !(*full_mux) && !value.contains_key(tile) {
                                    continue;
                                }
                                // Get the set of bits for this config
                                let bits: BTreeSet<ConfigBit> = if *full_mux {
                                    // In full mux mode, we add a value for all bits even if they didn't change
                                    let value_bits = value.get(tile);
                                    coverage
                                        .get(tile)
                                        .unwrap()
                                        .iter()
                                        .map(|(f, b)| ConfigBit {
                                            frame: *f,
                                            bit: *b,
                                            invert: value_bits
                                                .iter()
                                                .any(|x| x.contains(&(*f, *b, true))),
                                        })
                                        .collect()
                                } else {
                                    // Get the changed bits in this tile as ConfigBits; or the base set if the tile didn't change
                                    value
                                        .get(tile)
                                        .iter()
                                        .map(|x| *x)
                                        .flatten()
                                        .map(|(f, b, v)| ConfigBit {
                                            frame: *f,
                                            bit: *b,
                                            invert: !(*v),
                                        })
                                        .collect()
                                };
                                // Add the pip to the tile data
                                let tile_data = self.base.tile_by_name(tile).unwrap();
                                let tile_db = db.tile_bitdb(&self.base.family, &tile_data.tiletype);
                                tile_db.add_pip(
                                    &wires::normalize_wire(&self.base, tile_data, from_wire),
                                    &wires::normalize_wire(&self.base, tile_data, to_wire),
                                    bits,
                                );
                            }
                        }
                    }
                }
            }
            FuzzMode::Word { name, width } => {
                for tile in changed_tiles.iter() {
                    let mut cbits = Vec::new();
                    for i in 0..*width {
                        let key = FuzzKey::WordKey { bit: i };
                        let b = match self.deltas.get(&key) {
                            None => BTreeSet::new(),
                            Some(delta) => match delta.get(tile) {
                                None => BTreeSet::new(),
                                Some(td) => td
                                    .iter()
                                    .map(|(f, b, v)| ConfigBit {
                                        frame: *f,
                                        bit: *b,
                                        invert: !(*v),
                                    })
                                    .collect(),
                            },
                        };
                        cbits.push(b);
                    }
                    // Add the word to the tile data
                    let tile_data = self.base.tile_by_name(tile).unwrap();
                    let tile_db = db.tile_bitdb(&self.base.family, &tile_data.tiletype);
                    tile_db.add_word(&name, cbits);
                }
            }
            FuzzMode::Enum {
                name,
                include_zeros,
                disambiguate: _,
            } => {
                if self.deltas.len() < 2 {
                    return;
                }
                for tile in changed_tiles {
                    let mut bit_sets = self.deltas.values().map(|v| match v.get(&tile) {
                        Some(td) => BTreeSet::from_iter(td.iter().map(|(f, b, v)| (*f, *b, *v))),
                        None => BTreeSet::new(),
                    });
                    let all_changed_bits: BTreeSet<(usize, usize, bool)> = self
                        .deltas
                        .values()
                        .filter_map(|v| v.get(&tile))
                        .flatten()
                        .map(|&x| x)
                        .collect();
                    match bit_sets.next() {
                        None => continue, // no changes in this tile
                        Some(set0) => {
                            let unchanged_bits = bit_sets.fold(set0, |set1, set2| &set1 & &set2);
                            let changed_bits: BTreeSet<(usize, usize, bool)> = all_changed_bits
                                .difference(&unchanged_bits)
                                .map(|&x| x)
                                .collect();
                            for (key, delta) in self.deltas.iter() {
                                if let FuzzKey::EnumKey { option } = key {
                                    let b = match delta.get(&tile) {
                                        None => {
                                            if *include_zeros {
                                                // All bits as default
                                                changed_bits
                                                    .iter()
                                                    .map(|(f, b, v)| ConfigBit {
                                                        frame: *f,
                                                        bit: *b,
                                                        invert: *v,
                                                    })
                                                    .collect()
                                            } else {
                                                BTreeSet::new()
                                            }
                                        }
                                        Some(td) => changed_bits
                                            .iter()
                                            .filter(|(f, b, v)| {
                                                *include_zeros || *v || td.contains(&(*f, *b, *v))
                                            })
                                            .map(|(f, b, v)| ConfigBit {
                                                frame: *f,
                                                bit: *b,
                                                invert: if td.contains(&(*f, *b, *v)) {
                                                    !(*v)
                                                } else {
                                                    *v
                                                },
                                            })
                                            .collect(),
                                    };
                                    // Add the enum to the tile data
                                    let tile_data = self.base.tile_by_name(&tile).unwrap();
                                    let tile_db =
                                        db.tile_bitdb(&self.base.family, &tile_data.tiletype);
                                    tile_db.add_enum_option(name, &option, b);
                                }
                            }
                        }
                    }
                }
            }
        }
        db.flush();
    }
}
<commit_msg>Fuzzer fixes<commit_after>use crate::bitstream::*;
use crate::chip::*;
use crate::database::*;
use crate::wires;
use std::collections::{BTreeMap, BTreeSet};
use std::iter::FromIterator;

pub enum FuzzMode {
    Pip {
        to_wire: String,
        full_mux: bool, // if true, explicit 0s instead of base will be created for unset bits for a setting
        skip_fixed: bool, // if true, skip pips that have no bits associated with them (rather than created fixed conns)
        fixed_conn_tile: String,
    },
    Word {
        name: String,
        width: usize,
    },
    Enum {
        name: String,
        include_zeros: bool, // if true, explicit 0s instead of base will be created for unset bits for a setting
        disambiguate: bool,  // add explicit 0s to disambiguate settings only
    },
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
enum FuzzKey {
    PipKey { from_wire: String },
    WordKey { bit: usize },
    EnumKey { option: String },
}

pub struct Fuzzer {
    mode: FuzzMode,
    tiles: BTreeSet<String>,
    base: Chip,                           // bitstream with nothing set
    deltas: BTreeMap<FuzzKey, ChipDelta>, // used for arcs, words and enums
}

impl Fuzzer {
    pub fn init_pip_fuzzer(
        base_bit: &Chip,
        fuzz_tiles: &BTreeSet<String>,
        to_wire: &str,
        fixed_conn_tile: &str,
        full_mux: bool,
        skip_fixed: bool,
    ) -> Fuzzer {
        Fuzzer {
            mode: FuzzMode::Pip {
                to_wire: to_wire.to_string(),
                full_mux: full_mux,
                skip_fixed: skip_fixed,
                fixed_conn_tile: fixed_conn_tile.to_string(),
            },
            tiles: fuzz_tiles.clone(),
            base: base_bit.clone(),
            deltas: BTreeMap::new(),
        }
    }
    pub fn init_word_fuzzer(
        db: &mut Database,
        base_bit: &Chip,
        fuzz_tiles: &BTreeSet<String>,
        name: &str,
        width: usize,
        zero_bitfile: &str,
    ) -> Fuzzer {
        Fuzzer {
            mode: FuzzMode::Word {
                name: name.to_string(),
                width: width,
            },
            tiles: fuzz_tiles.clone(),
            base: base_bit.clone(),
            deltas: BTreeMap::new(),
        }
    }
    pub fn init_enum_fuzzer(
        base_bit: &Chip,
        fuzz_tiles: &BTreeSet<String>,
        name: &str,
        include_zeros: bool,
    ) -> Fuzzer {
        Fuzzer {
            mode: FuzzMode::Enum {
                name: name.to_string(),
                include_zeros: include_zeros,
                disambiguate: false, // fixme
            },
            tiles: fuzz_tiles.clone(),
            base: base_bit.clone(),
            deltas: BTreeMap::new(),
        }
    }
    fn add_sample(&mut self, db: &mut Database, key: FuzzKey, bitfile: &str) {
        let parsed_bitstream = BitstreamParser::parse_file(db, bitfile).unwrap();
        let delta = parsed_bitstream.delta(&self.base);
        self.deltas.insert(key, delta);
    }
    pub fn add_pip_sample(&mut self, db: &mut Database, from_wire: &str, bitfile: &str) {
        self.add_sample(
            db,
            FuzzKey::PipKey {
                from_wire: from_wire.to_string(),
            },
            bitfile,
        );
    }
    pub fn add_word_sample(&mut self, db: &mut Database, index: usize, bitfile: &str) {
        self.add_sample(db, FuzzKey::WordKey { bit: index }, bitfile);
    }
    pub fn add_enum_sample(&mut self, db: &mut Database, option: &str, bitfile: &str) {
        self.add_sample(
            db,
            FuzzKey::EnumKey {
                option: option.to_string(),
            },
            bitfile,
        );
    }
    pub fn solve(&mut self, db: &mut Database) {
        // Get a set of tiles that have been changed
        let changed_tiles: BTreeSet<String> = self
            .deltas
            .iter()
            .flat_map(|(_k, v)| v.keys())
            .filter(|t| self.tiles.contains(*t))
            .map(String::to_string)
            .collect();
        match &self.mode {
            FuzzMode::Pip {
                to_wire,
                full_mux,
                skip_fixed,
                fixed_conn_tile,
            } => {
                // In full mux mode; we need the coverage sets of the changes
                let mut coverage: BTreeMap<String, BTreeSet<(usize, usize)>> = BTreeMap::new();
                if *full_mux {
                    for tile in self.tiles.iter() {
                        coverage.insert(
                            tile.to_string(),
                            self.deltas
                                .iter()
                                .filter_map(|(_k, v)| v.get(tile))
                                .flatten()
                                .map(|(f, b, _v)| (*f, *b))
                                .collect(),
                        );
                    }
                }

                for (key, value) in self.deltas.iter() {
                    if let FuzzKey::PipKey { from_wire } = key {
                        if value.iter().any(|(k, _v)| !self.tiles.contains(k)) {
                            // If this pip affects tiles outside of the fuzz region, skip it
                            continue;
                        }
                        if changed_tiles.len() == 0 {
                            // No changes; it is a fixed connection
                            if *skip_fixed {
                                continue;
                            }
                            let db_tile = self.base.tile_by_name(fixed_conn_tile).unwrap();
                            let tile_db = db.tile_bitdb(&self.base.family, &db_tile.tiletype);
                            tile_db.add_conn(
                                &wires::normalize_wire(&self.base, db_tile, from_wire),
                                &wires::normalize_wire(&self.base, db_tile, to_wire),
                            );
                        } else {
                            for tile in changed_tiles.iter() {
                                // Get the set of bits for this config
                                let bits: BTreeSet<ConfigBit> = if *full_mux {
                                    // In full mux mode, we add a value for all bits even if they didn't change
                                    let value_bits = value.get(tile);
                                    coverage
                                        .get(tile)
                                        .iter()
                                        .map(|&x| x)
                                        .flatten()
                                        .map(|(f, b)| ConfigBit {
                                            frame: *f,
                                            bit: *b,
                                            invert: value_bits
                                                .iter()
                                                .any(|x| x.contains(&(*f, *b, true))),
                                        })
                                        .collect()
                                } else {
                                    // Get the changed bits in this tile as ConfigBits; or the base set if the tile didn't change
                                    value
                                        .get(tile)
                                        .iter()
                                        .map(|&x| x)
                                        .flatten()
                                        .map(|(f, b, v)| ConfigBit {
                                            frame: *f,
                                            bit: *b,
                                            invert: !(*v),
                                        })
                                        .collect()
                                };
                                // Add the pip to the tile data
                                let tile_data = self.base.tile_by_name(tile).unwrap();
                                let tile_db = db.tile_bitdb(&self.base.family, &tile_data.tiletype);
                                tile_db.add_pip(
                                    &wires::normalize_wire(&self.base, tile_data, from_wire),
                                    &wires::normalize_wire(&self.base, tile_data, to_wire),
                                    bits,
                                );
                            }
                        }
                    }
                }
            }
            FuzzMode::Word { name, width } => {
                for tile in changed_tiles.iter() {
                    let mut cbits = Vec::new();
                    for i in 0..*width {
                        let key = FuzzKey::WordKey { bit: i };
                        let b = match self.deltas.get(&key) {
                            None => BTreeSet::new(),
                            Some(delta) => match delta.get(tile) {
                                None => BTreeSet::new(),
                                Some(td) => td
                                    .iter()
                                    .map(|(f, b, v)| ConfigBit {
                                        frame: *f,
                                        bit: *b,
                                        invert: !(*v),
                                    })
                                    .collect(),
                            },
                        };
                        cbits.push(b);
                    }
                    // Add the word to the tile data
                    let tile_data = self.base.tile_by_name(tile).unwrap();
                    let tile_db = db.tile_bitdb(&self.base.family, &tile_data.tiletype);
                    tile_db.add_word(&name, cbits);
                }
            }
            FuzzMode::Enum {
                name,
                include_zeros,
                disambiguate: _,
            } => {
                if self.deltas.len() < 2 {
                    return;
                }
                for tile in changed_tiles {
                    let mut bit_sets = self.deltas.values().map(|v| match v.get(&tile) {
                        Some(td) => BTreeSet::from_iter(td.iter().map(|(f, b, v)| (*f, *b, *v))),
                        None => BTreeSet::new(),
                    });
                    let all_changed_bits: BTreeSet<(usize, usize, bool)> = self
                        .deltas
                        .values()
                        .filter_map(|v| v.get(&tile))
                        .flatten()
                        .map(|&x| x)
                        .collect();
                    match bit_sets.next() {
                        None => continue, // no changes in this tile
                        Some(set0) => {
                            let unchanged_bits = bit_sets.fold(set0, |set1, set2| &set1 & &set2);
                            let changed_bits: BTreeSet<(usize, usize, bool)> = all_changed_bits
                                .difference(&unchanged_bits)
                                .map(|&x| x)
                                .collect();
                            for (key, delta) in self.deltas.iter() {
                                if let FuzzKey::EnumKey { option } = key {
                                    let b = match delta.get(&tile) {
                                        None => {
                                            if *include_zeros {
                                                // All bits as default
                                                changed_bits
                                                    .iter()
                                                    .map(|(f, b, v)| ConfigBit {
                                                        frame: *f,
                                                        bit: *b,
                                                        invert: *v,
                                                    })
                                                    .collect()
                                            } else {
                                                BTreeSet::new()
                                            }
                                        }
                                        Some(td) => changed_bits
                                            .iter()
                                            .filter(|(f, b, v)| {
                                                *include_zeros
                                                    || !(*v)
                                                    || td.contains(&(*f, *b, *v))
                                            })
                                            .map(|(f, b, v)| ConfigBit {
                                                frame: *f,
                                                bit: *b,
                                                invert: if td.contains(&(*f, *b, *v)) {
                                                    !(*v)
                                                } else {
                                                    *v
                                                },
                                            })
                                            .collect(),
                                    };
                                    // Add the enum to the tile data
                                    let tile_data = self.base.tile_by_name(&tile).unwrap();
                                    let tile_db =
                                        db.tile_bitdb(&self.base.family, &tile_data.tiletype);
                                    tile_db.add_enum_option(name, &option, b);
                                }
                            }
                        }
                    }
                }
            }
        }
        db.flush();
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use rand::Rng;

static BINARY: [&str; 2] = ["0", "1"];

#[derive(Debug)]
pub struct Cromosome {
    size: usize,
    genotype: String,
}

impl Cromosome {
    pub fn new(size: usize) -> Cromosome {
        Cromosome {
            size: size,
            genotype: String::new()
        }
    }

    pub fn random(&mut self) {
        let mut rng = rand::thread_rng();
        for _ in 0..self.size {
            self.genotype += BINARY[rng.gen::<usize>() % BINARY.len()];
        }
    }

    pub fn fitness(&self) -> u32 {
        let mut fitness = 0;
        let mut exp = self.genotype.len() as u32;

        for i in 0..self.genotype.len() {
            fitness += if self.genotype.chars().nth(i).unwrap() == '1' {
                2u32.pow(exp-1)
            } else {
                0
            };
            exp -= 1;
        }

        fitness
    }
}

#[derive(Debug)]
pub struct Population {
    pob_size: usize,
    crom_size: usize,
    population: Vec<Cromosome>,
    ruleta_vec: Vec<f32>,
}

impl Population {
    pub fn new(pob_size: usize, crom_size: usize) -> Population {
        Population {
            pob_size: pob_size,
            crom_size: crom_size,
            population: Vec::new(),
            ruleta_vec: Vec::new()
        }
    }

    pub fn generate(&mut self) {
        println!("\nGenerando Población inicial");

        for _ in 0..self.pob_size {
            let mut crom = Cromosome::new(self.crom_size);
            crom.random();
            self.population.push(crom);
        }

        for i in 0..self.pob_size {
            println!("{}) {}", i + 1, self.population[i].genotype);
        }
    }

    pub fn eval(&self) {
        println!("Evaluando Individuos");
        for i in 0..self.pob_size {
            println!("{}) {} - {}", i+1, self.population[i].genotype, self.population[i].fitness());
        }
    }

    pub fn ruleta(&mut self) {
        println!("Selección de Individuos - Método de la Ruleta");
        let mut sum_ruleta = 0;
        self.ruleta_vec = Vec::new();

        for i in 0..self.pob_size {
            sum_ruleta += self.population[i].fitness();
        }

        for i in 0..self.pob_size {
            self.ruleta_vec.push((self.population[i].fitness() as f32 * 100.0) / sum_ruleta as f32);
        }

        for i in 0..self.pob_size {
            println!("{}) {} - {} - {}", i+1, self.population[i].genotype, self.population[i].fitness(), self.ruleta_vec[i]);
        }
    }

    pub fn random_parent_index(&self) -> usize {
        let mut rng = rand::thread_rng();
        let num = rng.gen::<f32>() * 100.0;
        let mut s = 0.0;

        for i in 0..self.ruleta_vec.len() {
            s += self.ruleta_vec[i];
            if num < s {
                return i;
            }
        }

        self.ruleta_vec.len()
    }

    pub fn crossover(&self, crossover_prob: f32, crossover_point: usize) {
        let parent1 = self.random_parent_index();
        let mut parent2 = self.random_parent_index();

        while parent2 == parent1 {
            let mut rng = rand::thread_rng();
            parent2 = rng.gen::<usize>() % self.pob_size;
        }

        println!("Padre: {}", parent1 + 1);
        println!("Madre: {}", parent2 + 1);
    }
}

#[derive(Debug)]
pub struct Solver {
    population: Population,
    iterations: usize,
    crossover_prob: f32,
    crossover_point: usize,
    mutation_prob: f32,
}

impl Solver {
    pub fn new(pob_size: usize,
               crom_size: usize,
               iterations: usize,
               crossover_prob: f32,
               crossover_point: usize,
               mutation_prob: f32) -> Solver {
        println!("run:");
        println!("Tamaño de la Población: {}", pob_size);
        println!("Tamaño de los Cromosomas: {}", crom_size);
        println!("Cantidad de Iteraciones: {}", iterations);
        println!("Probabilidad de Cruzamiento: {}", crossover_prob);
        println!("Cruzamiento de un Punto - Punto {}", crossover_point);
        println!("Probabilidad de Mutación: {}", mutation_prob);
        println!("Mutación Simple");

        let mut population = Population::new(pob_size, crom_size);
        population.generate();

        Solver {
            population: population,
            iterations: iterations,
            crossover_prob: crossover_prob,
            crossover_point: crossover_point,
            mutation_prob: mutation_prob
        }
    }

    pub fn evolve(&mut self) {
        for i in 0..self.iterations+1 {
            println!("\nIteración: {}", i);
            self.population.eval();
            self.population.ruleta();
            self.population.crossover(self.crossover_prob, self.crossover_point);
            // self.population.mutate();
            // self.population.crossover();
            // self.population.mutate();
            // self.population.selection();
        }

        println!("\nFin del Proceso");
    }
}<commit_msg>latest changes<commit_after>extern crate rand;

use rand::Rng;

static BINARY: [&str; 2] = ["0", "1"];

#[derive(Debug)]
pub struct Cromosome {
    size: usize,
    genotype: String,
}

impl Cromosome {
    pub fn new(size: usize) -> Cromosome {
        Cromosome {
            size: size,
            genotype: String::new()
        }
    }

    pub fn random(&mut self) {
        let mut rng = rand::thread_rng();
        for _ in 0..self.size {
            self.genotype += BINARY[rng.gen::<usize>() % BINARY.len()];
        }
    }

    pub fn fitness(&self) -> u32 {
        let mut fitness = 0;
        let mut exp = self.genotype.len() as u32;

        for i in 0..self.genotype.len() {
            fitness += if self.genotype.chars().nth(i).unwrap() == '1' {
                2u32.pow(exp-1)
            } else {
                0
            };
            exp -= 1;
        }

        fitness
    }
}

#[derive(Debug)]
pub struct Population {
    pob_size: usize,
    crom_size: usize,
    population: Vec<Cromosome>,
    ruleta_vec: Vec<f32>,
}

impl Population {
    pub fn new(pob_size: usize, crom_size: usize) -> Population {
        Population {
            pob_size: pob_size,
            crom_size: crom_size,
            population: Vec::new(),
            ruleta_vec: Vec::new()
        }
    }

    pub fn generate(&mut self) {
        println!("\nGenerando Población inicial");

        for _ in 0..self.pob_size {
            let mut crom = Cromosome::new(self.crom_size);
            crom.random();
            self.population.push(crom);
        }

        for i in 0..self.pob_size {
            println!("{}) {}", i + 1, self.population[i].genotype);
        }
    }

    pub fn eval(&self) {
        println!("Evaluando Individuos");
        for i in 0..self.pob_size {
            println!("{}) {} - {}", i+1, self.population[i].genotype, self.population[i].fitness());
        }
    }

    pub fn ruleta(&mut self) {
        println!("Selección de Individuos - Método de la Ruleta");
        let mut sum_ruleta = 0;
        self.ruleta_vec = Vec::new();

        for i in 0..self.pob_size {
            sum_ruleta += self.population[i].fitness();
        }

        for i in 0..self.pob_size {
            self.ruleta_vec.push((self.population[i].fitness() as f32 * 100.0) / sum_ruleta as f32);
        }

        for i in 0..self.pob_size {
            println!("{}) {} - {} - {}", i+1, self.population[i].genotype, self.population[i].fitness(), self.ruleta_vec[i]);
        }
    }

    pub fn random_parent_index(&self) -> usize {
        let mut rng = rand::thread_rng();
        let num = rng.gen::<f32>() * 100.0;
        let mut s = 0.0;

        for i in 0..self.ruleta_vec.len() {
            s += self.ruleta_vec[i];
            if num < s {
                return i;
            }
        }

        self.ruleta_vec.len()
    }

    pub fn crossover(&self, crossover_prob: f32, crossover_point: usize) {
        let parent1 = self.random_parent_index();
        let mut parent2 = self.random_parent_index();

        while parent2 == parent1 {
            let mut rng = rand::thread_rng();
            parent2 = rng.gen::<usize>() % self.pob_size;
        }

        println!("Padre: {}", parent1 + 1);
        println!("Madre: {}", parent2 + 1);

    }
}

#[derive(Debug)]
pub struct Solver {
    population: Population,
    iterations: usize,
    crossover_prob: f32,
    crossover_point: usize,
    mutation_prob: f32,
}

impl Solver {
    pub fn new(pob_size: usize,
               crom_size: usize,
               iterations: usize,
               crossover_prob: f32,
               crossover_point: usize,
               mutation_prob: f32) -> Solver {
        println!("run:");
        println!("Tamaño de la Población: {}", pob_size);
        println!("Tamaño de los Cromosomas: {}", crom_size);
        println!("Cantidad de Iteraciones: {}", iterations);
        println!("Probabilidad de Cruzamiento: {}", crossover_prob);
        println!("Cruzamiento de un Punto - Punto {}", crossover_point);
        println!("Probabilidad de Mutación: {}", mutation_prob);
        println!("Mutación Simple");

        let mut population = Population::new(pob_size, crom_size);
        population.generate();

        Solver {
            population: population,
            iterations: iterations,
            crossover_prob: crossover_prob,
            crossover_point: crossover_point,
            mutation_prob: mutation_prob
        }
    }

    pub fn evolve(&mut self) {
        for i in 0..self.iterations+1 {
            println!("\nIteración: {}", i);
            self.population.eval();
            self.population.ruleta();
            self.population.crossover(self.crossover_prob, self.crossover_point);
            // self.population.mutate();
            // self.population.crossover();
            // self.population.mutate();
            // self.population.selection();
        }

        println!("\nFin del Proceso");
    }
}<|endoftext|>


<commit_before>// Copyright 2014-2015 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 io::{self, Error, ErrorKind};
use libc::{self, c_int, gid_t, pid_t, uid_t};
use ptr;

use sys::cvt;
use sys::process::process_common::*;

////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////

impl Command {
    pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
                 -> io::Result<(Process, StdioPipes)> {
        use sys;

        const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";

        let envp = self.capture_env();

        if self.saw_nul() {
            return Err(io::Error::new(ErrorKind::InvalidInput,
                                      "nul byte found in provided data"));
        }

        let (ours, theirs) = self.setup_io(default, needs_stdin)?;

        if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
            return Ok((ret, ours))
        }

        let (input, output) = sys::pipe::anon_pipe()?;

        let pid = unsafe {
            match cvt(libc::fork())? {
                0 => {
                    drop(input);
                    let err = self.do_exec(theirs, envp.as_ref());
                    let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
                    let bytes = [
                        (errno >> 24) as u8,
                        (errno >> 16) as u8,
                        (errno >>  8) as u8,
                        (errno >>  0) as u8,
                        CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
                        CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
                    ];
                    // pipe I/O up to PIPE_BUF bytes should be atomic, and then
                    // we want to be sure we *don't* run at_exit destructors as
                    // we're being torn down regardless
                    assert!(output.write(&bytes).is_ok());
                    libc::_exit(1)
                }
                n => n,
            }
        };

        let mut p = Process { pid: pid, status: None };
        drop(output);
        let mut bytes = [0; 8];

        // loop to handle EINTR
        loop {
            match input.read(&mut bytes) {
                Ok(0) => return Ok((p, ours)),
                Ok(8) => {
                    assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
                            "Validation on the CLOEXEC pipe failed: {:?}", bytes);
                    let errno = combine(&bytes[0.. 4]);
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    return Err(Error::from_raw_os_error(errno))
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => {
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("the CLOEXEC pipe failed: {:?}", e)
                },
                Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("short read on the CLOEXEC pipe")
                }
            }
        }

        fn combine(arr: &[u8]) -> i32 {
            let a = arr[0] as u32;
            let b = arr[1] as u32;
            let c = arr[2] as u32;
            let d = arr[3] as u32;

            ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
        }
    }

    pub fn exec(&mut self, default: Stdio) -> io::Error {
        let envp = self.capture_env();

        if self.saw_nul() {
            return io::Error::new(ErrorKind::InvalidInput,
                                  "nul byte found in provided data")
        }

        match self.setup_io(default, true) {
            Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) },
            Err(e) => e,
        }
    }

    // And at this point we've reached a special time in the life of the
    // child. The child must now be considered hamstrung and unable to
    // do anything other than syscalls really. Consider the following
    // scenario:
    //
    //      1. Thread A of process 1 grabs the malloc() mutex
    //      2. Thread B of process 1 forks(), creating thread C
    //      3. Thread C of process 2 then attempts to malloc()
    //      4. The memory of process 2 is the same as the memory of
    //         process 1, so the mutex is locked.
    //
    // This situation looks a lot like deadlock, right? It turns out
    // that this is what pthread_atfork() takes care of, which is
    // presumably implemented across platforms. The first thing that
    // threads to *before* forking is to do things like grab the malloc
    // mutex, and then after the fork they unlock it.
    //
    // Despite this information, libnative's spawn has been witnessed to
    // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
    // all collected backtraces point at malloc/free traffic in the
    // child spawned process.
    //
    // For this reason, the block of code below should contain 0
    // invocations of either malloc of free (or their related friends).
    //
    // As an example of not having malloc/free traffic, we don't close
    // this file descriptor by dropping the FileDesc (which contains an
    // allocation). Instead we just close it manually. This will never
    // have the drop glue anyway because this code never returns (the
    // child will either exec() or invoke libc::exit)
    unsafe fn do_exec(
        &mut self,
        stdio: ChildPipes,
        maybe_envp: Option<&CStringArray>
    ) -> io::Error {
        use sys::{self, cvt_r};

        macro_rules! t {
            ($e:expr) => (match $e {
                Ok(e) => e,
                Err(e) => return e,
            })
        }

        if let Some(fd) = stdio.stdin.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
        }
        if let Some(fd) = stdio.stdout.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
        }
        if let Some(fd) = stdio.stderr.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
        }

        if cfg!(not(any(target_os = "l4re"))) {
            if let Some(u) = self.get_gid() {
                t!(cvt(libc::setgid(u as gid_t)));
            }
            if let Some(u) = self.get_uid() {
                // When dropping privileges from root, the `setgroups` call
                // will remove any extraneous groups. If we don't call this,
                // then even though our uid has dropped, we may still have
                // groups that enable us to do super-user things. This will
                // fail if we aren't root, so don't bother checking the
                // return value, this is just done as an optimistic
                // privilege dropping function.
                let _ = libc::setgroups(0, ptr::null());

                t!(cvt(libc::setuid(u as uid_t)));
            }
        }
        if let Some(ref cwd) = *self.get_cwd() {
            t!(cvt(libc::chdir(cwd.as_ptr())));
        }
        if let Some(envp) = maybe_envp {
            *sys::os::environ() = envp.as_ptr();
        }

        // emscripten has no signal support.
        #[cfg(not(any(target_os = "emscripten")))]
        {
            use mem;
            // Reset signal handling so the child process starts in a
            // standardized state. libstd ignores SIGPIPE, and signal-handling
            // libraries often set a mask. Child processes inherit ignored
            // signals and the signal mask from their parent, but most
            // UNIX programs do not reset these things on their own, so we
            // need to clean things up now to avoid confusing the program
            // we're about to run.
            let mut set: libc::sigset_t = mem::uninitialized();
            if cfg!(target_os = "android") {
                // Implementing sigemptyset allow us to support older Android
                // versions. See the comment about Android and sig* functions in
                // process_common.rs
                libc::memset(&mut set as *mut _ as *mut _,
                             0,
                             mem::size_of::<libc::sigset_t>());
            } else {
                t!(cvt(libc::sigemptyset(&mut set)));
            }
            t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set,
                                         ptr::null_mut())));
            let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
            if ret == libc::SIG_ERR {
                return io::Error::last_os_error()
            }
        }

        for callback in self.get_closures().iter_mut() {
            t!(callback());
        }

        libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
        io::Error::last_os_error()
    }

    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
    fn posix_spawn(&mut self, _stdio: &ChildPipes, _envp: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        Ok(None)
    }

    #[cfg(any(target_os = "linux", target_os = "macos"))]
    fn posix_spawn(&mut self, stdio: &ChildPipes, envp: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        use mem;
        use sys;

        if self.get_cwd().is_some() ||
            self.get_gid().is_some() ||
            self.get_uid().is_some() ||
            self.get_closures().len() != 0 {
            return Ok(None)
        }

        let mut p = Process { pid: 0, status: None };

        struct PosixSpawnFileActions(libc::posix_spawn_file_actions_t);

        impl Drop for PosixSpawnFileActions {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawn_file_actions_destroy(&mut self.0);
                }
            }
        }

        struct PosixSpawnattr(libc::posix_spawnattr_t);

        impl Drop for PosixSpawnattr {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawnattr_destroy(&mut self.0);
                }
            }
        }

        unsafe {
            let mut file_actions = PosixSpawnFileActions(mem::uninitialized());
            let mut attrs = PosixSpawnattr(mem::uninitialized());

            libc::posix_spawnattr_init(&mut attrs.0);
            libc::posix_spawn_file_actions_init(&mut file_actions.0);

            if let Some(fd) = stdio.stdin.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDIN_FILENO))?;
            }
            if let Some(fd) = stdio.stdout.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDOUT_FILENO))?;
            }
            if let Some(fd) = stdio.stderr.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDERR_FILENO))?;
            }

            let mut set: libc::sigset_t = mem::uninitialized();
            cvt(libc::sigemptyset(&mut set))?;
            cvt(libc::posix_spawnattr_setsigmask(&mut attrs.0,
                                                 &set))?;
            cvt(libc::sigaddset(&mut set, libc::SIGPIPE))?;
            cvt(libc::posix_spawnattr_setsigdefault(&mut attrs.0,
                                                    &set))?;

            let flags = libc::POSIX_SPAWN_SETSIGDEF |
                libc::POSIX_SPAWN_SETSIGMASK;
            cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?;

            let envp = envp.map(|c| c.as_ptr())
                .unwrap_or(*sys::os::environ() as *const _);
            let ret = libc::posix_spawnp(
                &mut p.pid,
                self.get_argv()[0],
                &file_actions.0,
                &attrs.0,
                self.get_argv().as_ptr() as *const _,
                envp as *const _,
            );
            if ret == 0 {
                Ok(Some(p))
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Processes
////////////////////////////////////////////////////////////////////////////////

/// The unique id of the process (this should never be negative).
pub struct Process {
    pid: pid_t,
    status: Option<ExitStatus>,
}

impl Process {
    pub fn id(&self) -> u32 {
        self.pid as u32
    }

    pub fn kill(&mut self) -> io::Result<()> {
        // If we've already waited on this process then the pid can be recycled
        // and used for another process, and we probably shouldn't be killing
        // random processes, so just return an error.
        if self.status.is_some() {
            Err(Error::new(ErrorKind::InvalidInput,
                           "invalid argument: can't kill an exited process"))
        } else {
            cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
        }
    }

    pub fn wait(&mut self) -> io::Result<ExitStatus> {
        use sys::cvt_r;
        if let Some(status) = self.status {
            return Ok(status)
        }
        let mut status = 0 as c_int;
        cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
        self.status = Some(ExitStatus::new(status));
        Ok(ExitStatus::new(status))
    }

    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
        if let Some(status) = self.status {
            return Ok(Some(status))
        }
        let mut status = 0 as c_int;
        let pid = cvt(unsafe {
            libc::waitpid(self.pid, &mut status, libc::WNOHANG)
        })?;
        if pid == 0 {
            Ok(None)
        } else {
            self.status = Some(ExitStatus::new(status));
            Ok(Some(ExitStatus::new(status)))
        }
    }
}
<commit_msg>Remove excess newline<commit_after>// Copyright 2014-2015 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 io::{self, Error, ErrorKind};
use libc::{self, c_int, gid_t, pid_t, uid_t};
use ptr;

use sys::cvt;
use sys::process::process_common::*;

////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////

impl Command {
    pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
                 -> io::Result<(Process, StdioPipes)> {
        use sys;

        const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";

        let envp = self.capture_env();

        if self.saw_nul() {
            return Err(io::Error::new(ErrorKind::InvalidInput,
                                      "nul byte found in provided data"));
        }

        let (ours, theirs) = self.setup_io(default, needs_stdin)?;

        if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
            return Ok((ret, ours))
        }

        let (input, output) = sys::pipe::anon_pipe()?;

        let pid = unsafe {
            match cvt(libc::fork())? {
                0 => {
                    drop(input);
                    let err = self.do_exec(theirs, envp.as_ref());
                    let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
                    let bytes = [
                        (errno >> 24) as u8,
                        (errno >> 16) as u8,
                        (errno >>  8) as u8,
                        (errno >>  0) as u8,
                        CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
                        CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
                    ];
                    // pipe I/O up to PIPE_BUF bytes should be atomic, and then
                    // we want to be sure we *don't* run at_exit destructors as
                    // we're being torn down regardless
                    assert!(output.write(&bytes).is_ok());
                    libc::_exit(1)
                }
                n => n,
            }
        };

        let mut p = Process { pid: pid, status: None };
        drop(output);
        let mut bytes = [0; 8];

        // loop to handle EINTR
        loop {
            match input.read(&mut bytes) {
                Ok(0) => return Ok((p, ours)),
                Ok(8) => {
                    assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
                            "Validation on the CLOEXEC pipe failed: {:?}", bytes);
                    let errno = combine(&bytes[0.. 4]);
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    return Err(Error::from_raw_os_error(errno))
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => {
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("the CLOEXEC pipe failed: {:?}", e)
                },
                Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("short read on the CLOEXEC pipe")
                }
            }
        }

        fn combine(arr: &[u8]) -> i32 {
            let a = arr[0] as u32;
            let b = arr[1] as u32;
            let c = arr[2] as u32;
            let d = arr[3] as u32;

            ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
        }
    }

    pub fn exec(&mut self, default: Stdio) -> io::Error {
        let envp = self.capture_env();

        if self.saw_nul() {
            return io::Error::new(ErrorKind::InvalidInput,
                                  "nul byte found in provided data")
        }

        match self.setup_io(default, true) {
            Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) },
            Err(e) => e,
        }
    }

    // And at this point we've reached a special time in the life of the
    // child. The child must now be considered hamstrung and unable to
    // do anything other than syscalls really. Consider the following
    // scenario:
    //
    //      1. Thread A of process 1 grabs the malloc() mutex
    //      2. Thread B of process 1 forks(), creating thread C
    //      3. Thread C of process 2 then attempts to malloc()
    //      4. The memory of process 2 is the same as the memory of
    //         process 1, so the mutex is locked.
    //
    // This situation looks a lot like deadlock, right? It turns out
    // that this is what pthread_atfork() takes care of, which is
    // presumably implemented across platforms. The first thing that
    // threads to *before* forking is to do things like grab the malloc
    // mutex, and then after the fork they unlock it.
    //
    // Despite this information, libnative's spawn has been witnessed to
    // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
    // all collected backtraces point at malloc/free traffic in the
    // child spawned process.
    //
    // For this reason, the block of code below should contain 0
    // invocations of either malloc of free (or their related friends).
    //
    // As an example of not having malloc/free traffic, we don't close
    // this file descriptor by dropping the FileDesc (which contains an
    // allocation). Instead we just close it manually. This will never
    // have the drop glue anyway because this code never returns (the
    // child will either exec() or invoke libc::exit)
    unsafe fn do_exec(
        &mut self,
        stdio: ChildPipes,
        maybe_envp: Option<&CStringArray>
    ) -> io::Error {
        use sys::{self, cvt_r};

        macro_rules! t {
            ($e:expr) => (match $e {
                Ok(e) => e,
                Err(e) => return e,
            })
        }

        if let Some(fd) = stdio.stdin.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
        }
        if let Some(fd) = stdio.stdout.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
        }
        if let Some(fd) = stdio.stderr.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
        }

        if cfg!(not(any(target_os = "l4re"))) {
            if let Some(u) = self.get_gid() {
                t!(cvt(libc::setgid(u as gid_t)));
            }
            if let Some(u) = self.get_uid() {
                // When dropping privileges from root, the `setgroups` call
                // will remove any extraneous groups. If we don't call this,
                // then even though our uid has dropped, we may still have
                // groups that enable us to do super-user things. This will
                // fail if we aren't root, so don't bother checking the
                // return value, this is just done as an optimistic
                // privilege dropping function.
                let _ = libc::setgroups(0, ptr::null());

                t!(cvt(libc::setuid(u as uid_t)));
            }
        }
        if let Some(ref cwd) = *self.get_cwd() {
            t!(cvt(libc::chdir(cwd.as_ptr())));
        }
        if let Some(envp) = maybe_envp {
            *sys::os::environ() = envp.as_ptr();
        }

        // emscripten has no signal support.
        #[cfg(not(any(target_os = "emscripten")))]
        {
            use mem;
            // Reset signal handling so the child process starts in a
            // standardized state. libstd ignores SIGPIPE, and signal-handling
            // libraries often set a mask. Child processes inherit ignored
            // signals and the signal mask from their parent, but most
            // UNIX programs do not reset these things on their own, so we
            // need to clean things up now to avoid confusing the program
            // we're about to run.
            let mut set: libc::sigset_t = mem::uninitialized();
            if cfg!(target_os = "android") {
                // Implementing sigemptyset allow us to support older Android
                // versions. See the comment about Android and sig* functions in
                // process_common.rs
                libc::memset(&mut set as *mut _ as *mut _,
                             0,
                             mem::size_of::<libc::sigset_t>());
            } else {
                t!(cvt(libc::sigemptyset(&mut set)));
            }
            t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set,
                                         ptr::null_mut())));
            let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
            if ret == libc::SIG_ERR {
                return io::Error::last_os_error()
            }
        }

        for callback in self.get_closures().iter_mut() {
            t!(callback());
        }

        libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
        io::Error::last_os_error()
    }

    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
    fn posix_spawn(&mut self, _stdio: &ChildPipes, _envp: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        Ok(None)
    }

    #[cfg(any(target_os = "linux", target_os = "macos"))]
    fn posix_spawn(&mut self, stdio: &ChildPipes, envp: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        use mem;
        use sys;

        if self.get_cwd().is_some() ||
            self.get_gid().is_some() ||
            self.get_uid().is_some() ||
            self.get_closures().len() != 0 {
            return Ok(None)
        }

        let mut p = Process { pid: 0, status: None };

        struct PosixSpawnFileActions(libc::posix_spawn_file_actions_t);

        impl Drop for PosixSpawnFileActions {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawn_file_actions_destroy(&mut self.0);
                }
            }
        }

        struct PosixSpawnattr(libc::posix_spawnattr_t);

        impl Drop for PosixSpawnattr {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawnattr_destroy(&mut self.0);
                }
            }
        }

        unsafe {
            let mut file_actions = PosixSpawnFileActions(mem::uninitialized());
            let mut attrs = PosixSpawnattr(mem::uninitialized());

            libc::posix_spawnattr_init(&mut attrs.0);
            libc::posix_spawn_file_actions_init(&mut file_actions.0);

            if let Some(fd) = stdio.stdin.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDIN_FILENO))?;
            }
            if let Some(fd) = stdio.stdout.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDOUT_FILENO))?;
            }
            if let Some(fd) = stdio.stderr.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDERR_FILENO))?;
            }

            let mut set: libc::sigset_t = mem::uninitialized();
            cvt(libc::sigemptyset(&mut set))?;
            cvt(libc::posix_spawnattr_setsigmask(&mut attrs.0,
                                                 &set))?;
            cvt(libc::sigaddset(&mut set, libc::SIGPIPE))?;
            cvt(libc::posix_spawnattr_setsigdefault(&mut attrs.0,
                                                    &set))?;

            let flags = libc::POSIX_SPAWN_SETSIGDEF |
                libc::POSIX_SPAWN_SETSIGMASK;
            cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?;

            let envp = envp.map(|c| c.as_ptr())
                .unwrap_or(*sys::os::environ() as *const _);
            let ret = libc::posix_spawnp(
                &mut p.pid,
                self.get_argv()[0],
                &file_actions.0,
                &attrs.0,
                self.get_argv().as_ptr() as *const _,
                envp as *const _,
            );
            if ret == 0 {
                Ok(Some(p))
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Processes
////////////////////////////////////////////////////////////////////////////////

/// The unique id of the process (this should never be negative).
pub struct Process {
    pid: pid_t,
    status: Option<ExitStatus>,
}

impl Process {
    pub fn id(&self) -> u32 {
        self.pid as u32
    }

    pub fn kill(&mut self) -> io::Result<()> {
        // If we've already waited on this process then the pid can be recycled
        // and used for another process, and we probably shouldn't be killing
        // random processes, so just return an error.
        if self.status.is_some() {
            Err(Error::new(ErrorKind::InvalidInput,
                           "invalid argument: can't kill an exited process"))
        } else {
            cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
        }
    }

    pub fn wait(&mut self) -> io::Result<ExitStatus> {
        use sys::cvt_r;
        if let Some(status) = self.status {
            return Ok(status)
        }
        let mut status = 0 as c_int;
        cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
        self.status = Some(ExitStatus::new(status));
        Ok(ExitStatus::new(status))
    }

    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
        if let Some(status) = self.status {
            return Ok(Some(status))
        }
        let mut status = 0 as c_int;
        let pid = cvt(unsafe {
            libc::waitpid(self.pid, &mut status, libc::WNOHANG)
        })?;
        if pid == 0 {
            Ok(None)
        } else {
            self.status = Some(ExitStatus::new(status));
            Ok(Some(ExitStatus::new(status)))
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod cmd_finder;

use std::env;
use std::env::consts;
use std::fs;
use std::path::{Path, PathBuf};

static SHADERC_STATIC_LIB: &str = "shaderc_combined";
static SHADERC_SHARED_LIB: &str = "shaderc_shared";
static SHADERC_STATIC_LIB_FILE_UNIX: &str = "libshaderc_combined.a";
static SHADERC_STATIC_LIB_FILE_WIN: &str = "shaderc_combined.lib";

fn get_apple_sdk_path() -> Option<PathBuf> {
    let target = std::env::var("TARGET").unwrap();
    use std::process::Command;

    // tvOS (and the simulator) could be added here in the future.
    let sdk = if target == "x86_64-apple-ios" || target == "i386-apple-ios" {
        "iphonesimulator"
    } else if target == "aarch64-apple-ios"
        || target == "armv7-apple-ios"
        || target == "armv7s-apple-ios"
    {
        "iphoneos"
    } else {
        return None;
    };

    let output = if let Ok(out) = Command::new("xcrun")
        .args(&["--sdk", sdk, "--show-sdk-path"])
        .output()
    {
        out.stdout
    } else {
        return None;
    };
    let prefix_str = std::str::from_utf8(&output).expect("invalid output from `xcrun`");
    Some(PathBuf::from(prefix_str.trim_end().to_string()))
}

fn build_shaderc_unix(shaderc_dir: &PathBuf, use_ninja: bool, target_os: String) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        // CMake options
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        // Glslang options
        .define("ENABLE_SPVREMAPPER", "OFF")
        .define("ENABLE_GLSLANG_BINARIES", "OFF")
        // Shaderc options
        .define("SHADERC_SKIP_TESTS", "ON")
        // SPIRV-Tools options
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF");
    if use_ninja {
        config.generator("Ninja");
    }

    if target_os == "ios" {
        if let Some(path) = get_apple_sdk_path() {
            config.define("CMAKE_OSX_SYSROOT", path);
        }
    }

    config.build()
}

fn build_shaderc_msvc(shaderc_dir: &PathBuf) -> PathBuf {
    let linkage = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or_default();

    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        // CMake options
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        // Glslang options
        .define("ENABLE_SPVREMAPPER", "OFF")
        .define("ENABLE_GLSLANG_BINARIES", "OFF")
        // Shaderc options
        .define("SHADERC_SKIP_TESTS", "ON")
        // SPIRV-Tools options
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .generator("Ninja");

    // cmake-rs tries to be clever on Windows by injecting several
    // C/C++ flags, which causes problems. So I have to manually
    // define CMAKE_*_FLAGS_* here to suppress that.
    let config = if linkage.contains("crt-static") {
        // statically-linked CRT
        config
            .define("CMAKE_C_FLAGS", " /nologo /EHsc /MT")
            .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MT")
            .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MT")
            .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MT")
    } else {
        // dynamically-linked CRT
        config
            .define("CMAKE_C_FLAGS", " /nologo /EHsc /MD")
            .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MD")
            .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MD")
            .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MD")
            // prevent shaderc's cmake script messes with crt flags
            .define("SHADERC_ENABLE_SHARED_CRT", "ON")
    };

    config.build()
}

fn main() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
    let config_build_from_source = env::var("CARGO_FEATURE_BUILD_FROM_SOURCE").is_ok();
    let has_explicit_set_search_dir = env::var("SHADERC_LIB_DIR").is_ok();

    // Initialize explicit shaderc search directory first.
    let mut search_dir = if let Ok(lib_dir) = env::var("SHADERC_LIB_DIR") {
        println!(
            "cargo:warning=shaderc: searching native shaderc libraries in '{}'",
            lib_dir
        );
        Some(lib_dir)
    } else {
        None
    };

    // Try to find native shaderc library from Vulkan SDK if possible.
    if search_dir.is_none() {
        search_dir = if let Ok(sdk_dir) = env::var("VULKAN_SDK") {
            println!(
                "cargo:warning=shaderc: searching native shaderc libraries in Vulkan SDK '{}/lib'",
                sdk_dir
            );
            Some(format!("{}/lib/", sdk_dir))
        } else {
            None
        };
    }

    // If no explicit path is set and no explicit request is made to build from
    // source, check known system locations before falling back to build from source.
    // This set `search_dir` for later usage.
    if search_dir.is_none() && !config_build_from_source {
        println!(
            "cargo:warning=shaderc: searching for native shaderc libraries on system;  \
             use '--features build-from-source' to force building from source code"
        );

        if target_os == "macos" {
            // Vulkan SDK is installed in `/usr/local/` by default on macOS
            let macos_path = "/usr/local/lib/";
            if Path::new(macos_path).exists() {
                search_dir = Some(macos_path.to_owned());
            }
        } else if target_os == "linux" {
            // https://wiki.ubuntu.com/MultiarchSpec
            // https://wiki.debian.org/Multiarch/Implementation
            let debian_arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap() {
                arch if arch == "x86" => "i386".to_owned(),
                arch => arch,
            };
            let debian_triple_path = format!("/usr/lib/{}-linux-gnu/", debian_arch);

            search_dir = if Path::new(&debian_triple_path).exists() {
                // Debian, Ubuntu and their derivatives.
                Some(debian_triple_path)
            } else if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86_64"
                && Path::new("/usr/lib64/").exists()
            {
                // Other distributions running on x86_64 usually use this path.
                Some("/usr/lib64/".to_owned())
            } else {
                // Other distributions, not x86_64.
                Some("/usr/lib/".to_owned())
            };
        }
    }

    // Canonicalize the sarch directory first.
    let search_dir = if let Some(search_dir) = search_dir {
        let path = Path::new(&search_dir);
        let cannonical = fs::canonicalize(&path);
        if path.is_relative() {
            println!(
                "cargo:warning=shaderc: the given search path '{:?}' is relative; \
                 path must be relative to shaderc-sys crate, \
                 likely not your current working directory",
                &path
            );
        } else if !path.is_dir() {
            println!(
                "cargo:warning=shaderc: the given search path '{:?}' is not a directory",
                &path
            );
        }
        if (cannonical.is_err()) && has_explicit_set_search_dir {
            println!("cargo:warning=shaderc: {:?}", cannonical.err().unwrap());
            println!(
                "cargo:warning=shaderc: failed to canonicalize the given search path '{:?}'",
                &path
            );
            None
        } else {
            cannonical.ok()
        }
    } else {
        None
    };

    // Try to build with the dynamic or static library if a path was explicit set
    // or implicitly chosen.
    if let Some(search_dir) = search_dir {
        let search_dir_str = search_dir.to_string_lossy();

        let static_lib_path = search_dir.join(if target_os == "windows" && target_env == "msvc" {
            SHADERC_STATIC_LIB_FILE_WIN
        } else {
            SHADERC_STATIC_LIB_FILE_UNIX
        });

        let dylib_name = format!(
            "{}{}{}",
            consts::DLL_PREFIX,
            SHADERC_SHARED_LIB,
            consts::DLL_SUFFIX
        );
        let dylib_path = search_dir.join(dylib_name);

        if let Some((lib_name, lib_kind)) = {
            if dylib_path.exists() {
                Some((SHADERC_SHARED_LIB, "dylib"))
            } else if static_lib_path.exists() {
                Some((SHADERC_STATIC_LIB, "static"))
            } else {
                None
            }
        } {
            match (target_os.as_str(), target_env.as_str()) {
                ("linux", _) => {
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("windows", "msvc") => {
                    println!("cargo:warning=shaderc: Windows MSVC static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    return;
                }
                ("windows", "gnu") => {
                    println!("cargo:warning=shaderc: Windows MinGW static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("macos", _) => {
                    println!("cargo:warning=shaderc: macOS static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                ("ios", _) => {
                    println!("cargo:warning=shaderc: macOS static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                (_, _) => {
                    println!(
                        "cargo:warning=shaderc: unsupported platform for linking against \
                         native shaderc libraries installed on system"
                    );
                }
            }
        }
    }

    if config_build_from_source {
        println!("cargo:warning=shaderc: requested to build from source");
    } else {
        println!(
            "cargo:warning=shaderc: cannot find native shaderc library on system; \
             falling back to build from source"
        );
    }

    let mut finder = cmd_finder::CommandFinder::new();

    finder.must_have("cmake");
    finder.must_have("git");
    finder.must_have("python");

    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let shaderc_dir = Path::new(&manifest_dir).join("build");

    let mut lib_path = if target_env == "msvc" {
        finder.must_have("ninja");
        build_shaderc_msvc(&shaderc_dir)
    } else {
        let has_ninja = finder.maybe_have("ninja").is_some();
        build_shaderc_unix(&shaderc_dir, has_ninja, target_os)
    };

    lib_path.push("lib");
    println!("cargo:rustc-link-search=native={}", lib_path.display());
    println!("cargo:rustc-link-lib=static={}", SHADERC_STATIC_LIB);

    emit_std_cpp_link();
}

fn emit_std_cpp_link() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    match (target_os.as_str(), target_env.as_str()) {
        ("linux", _) | ("windows", "gnu") => println!("cargo:rustc-link-lib=dylib=stdc++"),
        ("macos", _) => println!("cargo:rustc-link-lib=dylib=c++"),
        _ => {}
    }
}
<commit_msg>Allow python or python3 binaries when building (#105)<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod cmd_finder;

use std::env;
use std::env::consts;
use std::fs;
use std::path::{Path, PathBuf};

static SHADERC_STATIC_LIB: &str = "shaderc_combined";
static SHADERC_SHARED_LIB: &str = "shaderc_shared";
static SHADERC_STATIC_LIB_FILE_UNIX: &str = "libshaderc_combined.a";
static SHADERC_STATIC_LIB_FILE_WIN: &str = "shaderc_combined.lib";

fn get_apple_sdk_path() -> Option<PathBuf> {
    let target = std::env::var("TARGET").unwrap();
    use std::process::Command;

    // tvOS (and the simulator) could be added here in the future.
    let sdk = if target == "x86_64-apple-ios" || target == "i386-apple-ios" {
        "iphonesimulator"
    } else if target == "aarch64-apple-ios"
        || target == "armv7-apple-ios"
        || target == "armv7s-apple-ios"
    {
        "iphoneos"
    } else {
        return None;
    };

    let output = if let Ok(out) = Command::new("xcrun")
        .args(&["--sdk", sdk, "--show-sdk-path"])
        .output()
    {
        out.stdout
    } else {
        return None;
    };
    let prefix_str = std::str::from_utf8(&output).expect("invalid output from `xcrun`");
    Some(PathBuf::from(prefix_str.trim_end().to_string()))
}

fn build_shaderc_unix(shaderc_dir: &PathBuf, use_ninja: bool, target_os: String) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        // CMake options
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        // Glslang options
        .define("ENABLE_SPVREMAPPER", "OFF")
        .define("ENABLE_GLSLANG_BINARIES", "OFF")
        // Shaderc options
        .define("SHADERC_SKIP_TESTS", "ON")
        // SPIRV-Tools options
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF");
    if use_ninja {
        config.generator("Ninja");
    }

    if target_os == "ios" {
        if let Some(path) = get_apple_sdk_path() {
            config.define("CMAKE_OSX_SYSROOT", path);
        }
    }

    config.build()
}

fn build_shaderc_msvc(shaderc_dir: &PathBuf) -> PathBuf {
    let linkage = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or_default();

    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        // CMake options
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        // Glslang options
        .define("ENABLE_SPVREMAPPER", "OFF")
        .define("ENABLE_GLSLANG_BINARIES", "OFF")
        // Shaderc options
        .define("SHADERC_SKIP_TESTS", "ON")
        // SPIRV-Tools options
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .generator("Ninja");

    // cmake-rs tries to be clever on Windows by injecting several
    // C/C++ flags, which causes problems. So I have to manually
    // define CMAKE_*_FLAGS_* here to suppress that.
    let config = if linkage.contains("crt-static") {
        // statically-linked CRT
        config
            .define("CMAKE_C_FLAGS", " /nologo /EHsc /MT")
            .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MT")
            .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MT")
            .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MT")
    } else {
        // dynamically-linked CRT
        config
            .define("CMAKE_C_FLAGS", " /nologo /EHsc /MD")
            .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MD")
            .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MD")
            .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MD")
            // prevent shaderc's cmake script messes with crt flags
            .define("SHADERC_ENABLE_SHARED_CRT", "ON")
    };

    config.build()
}

fn main() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
    let config_build_from_source = env::var("CARGO_FEATURE_BUILD_FROM_SOURCE").is_ok();
    let has_explicit_set_search_dir = env::var("SHADERC_LIB_DIR").is_ok();

    // Initialize explicit shaderc search directory first.
    let mut search_dir = if let Ok(lib_dir) = env::var("SHADERC_LIB_DIR") {
        println!(
            "cargo:warning=shaderc: searching native shaderc libraries in '{}'",
            lib_dir
        );
        Some(lib_dir)
    } else {
        None
    };

    // Try to find native shaderc library from Vulkan SDK if possible.
    if search_dir.is_none() {
        search_dir = if let Ok(sdk_dir) = env::var("VULKAN_SDK") {
            println!(
                "cargo:warning=shaderc: searching native shaderc libraries in Vulkan SDK '{}/lib'",
                sdk_dir
            );
            Some(format!("{}/lib/", sdk_dir))
        } else {
            None
        };
    }

    // If no explicit path is set and no explicit request is made to build from
    // source, check known system locations before falling back to build from source.
    // This set `search_dir` for later usage.
    if search_dir.is_none() && !config_build_from_source {
        println!(
            "cargo:warning=shaderc: searching for native shaderc libraries on system;  \
             use '--features build-from-source' to force building from source code"
        );

        if target_os == "macos" {
            // Vulkan SDK is installed in `/usr/local/` by default on macOS
            let macos_path = "/usr/local/lib/";
            if Path::new(macos_path).exists() {
                search_dir = Some(macos_path.to_owned());
            }
        } else if target_os == "linux" {
            // https://wiki.ubuntu.com/MultiarchSpec
            // https://wiki.debian.org/Multiarch/Implementation
            let debian_arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap() {
                arch if arch == "x86" => "i386".to_owned(),
                arch => arch,
            };
            let debian_triple_path = format!("/usr/lib/{}-linux-gnu/", debian_arch);

            search_dir = if Path::new(&debian_triple_path).exists() {
                // Debian, Ubuntu and their derivatives.
                Some(debian_triple_path)
            } else if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86_64"
                && Path::new("/usr/lib64/").exists()
            {
                // Other distributions running on x86_64 usually use this path.
                Some("/usr/lib64/".to_owned())
            } else {
                // Other distributions, not x86_64.
                Some("/usr/lib/".to_owned())
            };
        }
    }

    // Canonicalize the sarch directory first.
    let search_dir = if let Some(search_dir) = search_dir {
        let path = Path::new(&search_dir);
        let cannonical = fs::canonicalize(&path);
        if path.is_relative() {
            println!(
                "cargo:warning=shaderc: the given search path '{:?}' is relative; \
                 path must be relative to shaderc-sys crate, \
                 likely not your current working directory",
                &path
            );
        } else if !path.is_dir() {
            println!(
                "cargo:warning=shaderc: the given search path '{:?}' is not a directory",
                &path
            );
        }
        if (cannonical.is_err()) && has_explicit_set_search_dir {
            println!("cargo:warning=shaderc: {:?}", cannonical.err().unwrap());
            println!(
                "cargo:warning=shaderc: failed to canonicalize the given search path '{:?}'",
                &path
            );
            None
        } else {
            cannonical.ok()
        }
    } else {
        None
    };

    // Try to build with the dynamic or static library if a path was explicit set
    // or implicitly chosen.
    if let Some(search_dir) = search_dir {
        let search_dir_str = search_dir.to_string_lossy();

        let static_lib_path = search_dir.join(if target_os == "windows" && target_env == "msvc" {
            SHADERC_STATIC_LIB_FILE_WIN
        } else {
            SHADERC_STATIC_LIB_FILE_UNIX
        });

        let dylib_name = format!(
            "{}{}{}",
            consts::DLL_PREFIX,
            SHADERC_SHARED_LIB,
            consts::DLL_SUFFIX
        );
        let dylib_path = search_dir.join(dylib_name);

        if let Some((lib_name, lib_kind)) = {
            if dylib_path.exists() {
                Some((SHADERC_SHARED_LIB, "dylib"))
            } else if static_lib_path.exists() {
                Some((SHADERC_STATIC_LIB, "static"))
            } else {
                None
            }
        } {
            match (target_os.as_str(), target_env.as_str()) {
                ("linux", _) => {
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("windows", "msvc") => {
                    println!("cargo:warning=shaderc: Windows MSVC static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    return;
                }
                ("windows", "gnu") => {
                    println!("cargo:warning=shaderc: Windows MinGW static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("macos", _) => {
                    println!("cargo:warning=shaderc: macOS static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                ("ios", _) => {
                    println!("cargo:warning=shaderc: macOS static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                (_, _) => {
                    println!(
                        "cargo:warning=shaderc: unsupported platform for linking against \
                         native shaderc libraries installed on system"
                    );
                }
            }
        }
    }

    if config_build_from_source {
        println!("cargo:warning=shaderc: requested to build from source");
    } else {
        println!(
            "cargo:warning=shaderc: cannot find native shaderc library on system; \
             falling back to build from source"
        );
    }

    let mut finder = cmd_finder::CommandFinder::new();

    finder.must_have("cmake");
    finder.must_have("git");
    finder.maybe_have("python").or(finder.maybe_have("python3")).unwrap_or_else(|| {
        panic!("Build requires one of `python` or `python3`");
    });

    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let shaderc_dir = Path::new(&manifest_dir).join("build");

    let mut lib_path = if target_env == "msvc" {
        finder.must_have("ninja");
        build_shaderc_msvc(&shaderc_dir)
    } else {
        let has_ninja = finder.maybe_have("ninja").is_some();
        build_shaderc_unix(&shaderc_dir, has_ninja, target_os)
    };

    lib_path.push("lib");
    println!("cargo:rustc-link-search=native={}", lib_path.display());
    println!("cargo:rustc-link-lib=static={}", SHADERC_STATIC_LIB);

    emit_std_cpp_link();
}

fn emit_std_cpp_link() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    match (target_os.as_str(), target_env.as_str()) {
        ("linux", _) | ("windows", "gnu") => println!("cargo:rustc-link-lib=dylib=stdc++"),
        ("macos", _) => println!("cargo:rustc-link-lib=dylib=c++"),
        _ => {}
    }
}
<|endoftext|>