<commit_before>#![cfg(feature = "interchange")]

use crate::chip::Chip;
use crate::database::Database;
use std::collections::{HashSet, HashMap};

use crate::bba::idstring::*;
use crate::bba::idxset::*;
use crate::bba::tiletype::{Neighbour, TileTypes};

use crate::sites::*;
use crate::wires::*;

// A tile type from the interchange format perspective
// this is not the same as a database tile type; because the Oxide graph can have
// more than one tile at a grid location whereas the interchange grid is one tile
// per location

#[derive(Clone, Eq, PartialEq, Hash)]
pub struct TileTypeKey {
    pub tile_types: Vec<String>,
}
impl TileTypeKey {
    pub fn new() -> TileTypeKey {
        TileTypeKey { tile_types: Vec::new() }
    }
}

pub struct IcTileType {
    pub key: TileTypeKey,
    pub wires: IndexedMap<IdString, IcWire>,
    pub pips: Vec<IcPip>,
    pub site_types: Vec<Site>,
    // TODO: constants, sites, etc
}

impl IcTileType {
    pub fn new(key: TileTypeKey, family: &str, db: &mut Database) -> IcTileType {
        let mut site_types = Vec::new();
        for tt in key.tile_types.iter() {
            site_types.extend(build_sites(&tt, &db.tile_bitdb(family, &tt).db));
        }
        IcTileType {
            key: key,
            wires: IndexedMap::new(),
            pips: Vec::new(),
            site_types: site_types,
        }
    }
    pub fn wire(&mut self, name: IdString) -> usize {
        self.wires.add(&name, IcWire::new(name))
    }
    pub fn add_pip(&mut self, src: IdString, dst: IdString) {
        let src_idx = self.wire(src);
        let dst_idx = self.wire(dst);
        self.pips.push(IcPip {
            src_wire: src_idx,
            dst_wire: dst_idx,
            pseudo_cells: Vec::new(),
        });
    }
    pub fn add_ppip(&mut self, src: IdString, dst: IdString, pseudo_cells: Vec<IcPseudoCell>) {
        let src_idx = self.wire(src);
        let dst_idx = self.wire(dst);
        self.pips.push(IcPip {
            src_wire: src_idx,
            dst_wire: dst_idx,
            pseudo_cells: pseudo_cells,
        });
    }
}

pub struct IcWire {
    pub name: IdString,
}

impl IcWire {
    pub fn new(name: IdString) -> IcWire {
        IcWire {
            name: name,
        }
    }
}

pub struct IcPseudoCell {
    pub bel: IdString,
    pub pins: Vec<IdString>,
}

pub struct IcPip {
    pub src_wire: usize,
    pub dst_wire: usize,
    pub pseudo_cells: Vec<IcPseudoCell>,
}

// A tile instance
pub struct IcTileInst {
    pub name: IdString,
    pub x: u32,
    pub y: u32,
    pub type_idx: usize,
    // mapping between wires and nodes
    wire_to_node: HashMap<IdString, usize>,
}

impl IcTileInst {
    pub fn new(ids: &mut IdStringDB, x: u32, y: u32) -> IcTileInst {
        IcTileInst {
            name: ids.id(&format!("R{}C{}", y, x)),
            x: x,
            y: y,
            type_idx: 0,
            wire_to_node: HashMap::new(),
        }
    }
}

// A reference to a tile wire
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct IcWireRef {
    pub tile_name: IdString,
    pub wire_name: IdString,
}

// A node instance
pub struct IcNode {
    // list of tile wires in the node
    pub wires: HashSet<IcWireRef>,
    pub root_wire: IcWireRef,
}

impl IcNode {
    pub fn new(root_wire: IcWireRef) -> IcNode {
        IcNode {
            wires: [root_wire.clone()].iter().cloned().collect(),
            root_wire: root_wire,
        }
    }
}

// The overall routing resource graph
pub struct IcGraph {
    pub tile_types: IndexedMap<TileTypeKey, IcTileType>,
    pub tiles: Vec<IcTileInst>,
    pub nodes: Vec<IcNode>,
    pub width: u32,
    pub height: u32,
}

impl IcGraph {
    pub fn new(ids: &mut IdStringDB, width: u32, height: u32) -> IcGraph {
        IcGraph {
            tile_types: IndexedMap::new(),
            tiles: (0..width*height).map(|i| IcTileInst::new(ids, i%width, i/width)).collect(),
            nodes: Vec::new(),
            width: width,
            height: height
        }
    }
    pub fn tile_idx(&self, x: u32, y: u32) -> usize {
        assert!(x < self.width);
        assert!(y < self.height);
        ((y * self.width) + x) as usize
    }
    pub fn tile_at(&mut self, x: u32, y: u32) -> &mut IcTileInst {
        let idx = self.tile_idx(x, y);
        &mut self.tiles[idx]
    }
    pub fn type_at(&mut self, x: u32, y: u32) -> &mut IcTileType {
        let idx = self.tile_at(x, y).type_idx;
        self.tile_types.value_mut(idx)
    }
    pub fn map_node(&mut self, root_x: u32, root_y: u32, root_wire: IdString, wire_x: u32, wire_y: u32, wire: IdString) {
        // Make sure wire exists in both tiles
        self.type_at(root_x, root_y).wire(root_wire);
        self.type_at(wire_x, wire_y).wire(wire);
        // Update wire-node mapping
        let root_tile_idx = self.tile_idx(root_x, root_y);
        let node_idx = match self.tiles[root_tile_idx].wire_to_node.get(&root_wire) {
            Some(i) => *i,
            None => {
                let idx = self.nodes.len();
                self.nodes.push(IcNode::new(IcWireRef { tile_name: self.tiles[root_tile_idx].name, wire_name: root_wire }));
                self.tiles[root_tile_idx].wire_to_node.insert(root_wire, idx);
                idx
            }
        };
        let wire_tile_idx = self.tile_idx(wire_x, wire_y);
        self.nodes[node_idx].wires.insert(IcWireRef {tile_name: self.tiles[wire_tile_idx].name, wire_name: wire });
        self.tiles[wire_tile_idx].wire_to_node.insert(wire, node_idx);
    }
}

pub struct GraphBuilder<'a> {
    g: IcGraph,
    ids: &'a mut IdStringDB,
    chip: &'a Chip,
    db: &'a mut Database,
    tiletypes_by_xy: HashMap<(u32, u32), TileTypeKey>,
    orig_tts: TileTypes,
}

impl <'a> GraphBuilder<'a> {
    fn new(ids: &'a mut IdStringDB, chip: &'a Chip, db: &'a mut Database) -> GraphBuilder<'a> {
        let mut width = 0;
        let mut height = 0;
        let mut tiletypes_by_xy = HashMap::new();
        for t in chip.tiles.iter() {
            tiletypes_by_xy.entry((t.x, t.y)).or_insert(TileTypeKey::new()).tile_types.push(t.tiletype.to_string());
            width = std::cmp::max(width, t.x + 1);
            height = std::cmp::max(height, t.y + 1);
        }

        let orig_tts = TileTypes::new(db, ids, &chip.family, &[&chip.device]);

        GraphBuilder {
            g: IcGraph::new(ids, width, height),
            ids: ids,
            chip: chip,
            db: db,
            tiletypes_by_xy: tiletypes_by_xy,
            // the original tiletypes from the database
            orig_tts: orig_tts,
        }
    }
    fn setup_tiletypes(&mut self) {
        for t in self.g.tiles.iter_mut() {
            let key = self.tiletypes_by_xy.get(&(t.x, t.y)).unwrap();
            t.type_idx = self.g.tile_types.add(key, IcTileType::new(key.clone(), &self.chip.family, self.db));
        }
        for (key, lt) in self.g.tile_types.iter_mut() {
            // setup wires for site pins
            let site_wires : Vec<String> = lt.site_types.iter().map(|s| s.pins.iter()).flatten().map(|p| p.tile_wire.clone()).collect();
            for w in site_wires {
                lt.wire(self.ids.id(&w));
            }
            for tt in key.tile_types.iter() {
                // setup wires for all sub-tile-types
                let tt_data = self.orig_tts.get(tt).unwrap();
                for wire in tt_data.wire_ids.iter() {
                    lt.wire(*wire);
                }
                // setup pips, both fixed and not
                // TODO: skip site wires and pips and deal with these later
                let tdb = &self.db.tile_bitdb(&self.chip.family, tt).db;
                for (to_wire, pips) in tdb.pips.iter() {
                    for pip in pips.iter() {
                        if is_site_wire(tt, &pip.from_wire) && is_site_wire(tt, to_wire) {
                            continue;
                        }
                        lt.add_pip(self.ids.id(&pip.from_wire), self.ids.id(to_wire));
                    }
                }
                for (to_wire, conns) in tdb.conns.iter() {
                    for conn in conns.iter() {
                        if is_site_wire(tt, &conn.from_wire) && is_site_wire(tt, to_wire) {
                            continue;
                        }
                        lt.add_pip(self.ids.id(&conn.from_wire), self.ids.id(to_wire));
                    }
                }
            }
            if lt.site_types.iter().find(|s| s.site_type == "PLC").is_some() {
                let gnd_wire = self.ids.id("G:GND");
                lt.wire(gnd_wire);
                for i in 0..8 {
                    // Create pseudo-ground drivers for LUT outputs
                    lt.add_ppip(gnd_wire, self.ids.id(&format!("JF{}", i)),
                        vec![
                            IcPseudoCell {
                                bel: self.ids.id(&format!("SLICE{}_LUT{}", &"ABCD"[(i/2)..(i/2)+1], i%2)),
                                pins: vec![self.ids.id("F")],
                            }
                        ]);
                    // Create LUT route-through PIPs
                    for j in &["A", "B", "C", "D"] {
                        lt.add_ppip(self.ids.id(&format!("J{}{}", j, i)), self.ids.id(&format!("JF{}", i)),
                        vec![
                            IcPseudoCell {
                                bel: self.ids.id(&format!("SLICE{}_LUT{}", &"ABCD"[(i/2)..(i/2)+1], i%2)),
                                pins: vec![self.ids.id(j), self.ids.id("F")],
                            }
                        ]);
                    }
                }
            }
        }
    }
    // Convert a neighbour to a coordinate
    pub fn neighbour_tile(&self, x: u32, y: u32, n: &Neighbour) -> Option<(u32, u32)> {
        match n {
            Neighbour::RelXY { rel_x, rel_y } => {
                let nx = (x as i32) + rel_x;
                let ny = (y as i32) + rel_y;
                if nx >= 0 && ny >= 0 && (nx as u32) < self.g.width && (ny as u32) < self.g.height {
                    Some((nx as u32, ny as u32))
                } else {
                    None
                }
            }
            // TODO: globals
            _ => None
        }
    }
    fn setup_wire2node(&mut self) {
        for ((x, y), key) in self.tiletypes_by_xy.iter() {
            for tt in key.tile_types.iter() {
                for wire in self.orig_tts.get(tt).unwrap().wires.iter() {
                    let (neigh, base_wire) = Neighbour::parse_wire(wire);
                    if let Some(neigh) = neigh {
                        // it's a neighbour wire, map to the base tile
                        if let Some((root_x, root_y)) = self.neighbour_tile(*x, *y, &neigh) {
                            self.g.map_node(root_x, root_y, self.ids.id(base_wire), *x, *y, self.ids.id(wire));
                        }

                    } else {
                        // root node, map to itself
                        self.g.map_node(*x, *y, self.ids.id(base_wire), *x, *y, self.ids.id(base_wire));
                    }
                }
            }
        }
    }

    pub fn run(ids: &'a mut IdStringDB, chip: &'a Chip, db: &'a mut Database) -> IcGraph {
        let mut builder = GraphBuilder::new(ids, chip, db);
        builder.setup_tiletypes();
        builder.setup_wire2node();
        builder.g
    }
}
<commit_msg>interchange: Add globals to routing graph<commit_after>#![cfg(feature = "interchange")]

use crate::chip::Chip;
use crate::database::{Database, DeviceGlobalsData,};
use std::collections::{HashSet, HashMap};
use std::convert::TryInto;

use crate::bba::idstring::*;
use crate::bba::idxset::*;
use crate::bba::tiletype::{Neighbour, BranchSide, TileTypes};

use crate::sites::*;
use crate::wires::*;

// A tile type from the interchange format perspective
// this is not the same as a database tile type; because the Oxide graph can have
// more than one tile at a grid location whereas the interchange grid is one tile
// per location

#[derive(Clone, Eq, PartialEq, Hash)]
pub struct TileTypeKey {
    pub tile_types: Vec<String>,
}
impl TileTypeKey {
    pub fn new() -> TileTypeKey {
        TileTypeKey { tile_types: Vec::new() }
    }
}

pub struct IcTileType {
    pub key: TileTypeKey,
    pub wires: IndexedMap<IdString, IcWire>,
    pub pips: Vec<IcPip>,
    pub site_types: Vec<Site>,
    // TODO: constants, sites, etc
}

impl IcTileType {
    pub fn new(key: TileTypeKey, family: &str, db: &mut Database) -> IcTileType {
        let mut site_types = Vec::new();
        for tt in key.tile_types.iter() {
            site_types.extend(build_sites(&tt, &db.tile_bitdb(family, &tt).db));
        }
        IcTileType {
            key: key,
            wires: IndexedMap::new(),
            pips: Vec::new(),
            site_types: site_types,
        }
    }
    pub fn wire(&mut self, name: IdString) -> usize {
        self.wires.add(&name, IcWire::new(name))
    }
    pub fn add_pip(&mut self, src: IdString, dst: IdString) {
        let src_idx = self.wire(src);
        let dst_idx = self.wire(dst);
        self.pips.push(IcPip {
            src_wire: src_idx,
            dst_wire: dst_idx,
            pseudo_cells: Vec::new(),
        });
    }
    pub fn add_ppip(&mut self, src: IdString, dst: IdString, pseudo_cells: Vec<IcPseudoCell>) {
        let src_idx = self.wire(src);
        let dst_idx = self.wire(dst);
        self.pips.push(IcPip {
            src_wire: src_idx,
            dst_wire: dst_idx,
            pseudo_cells: pseudo_cells,
        });
    }
}

pub struct IcWire {
    pub name: IdString,
}

impl IcWire {
    pub fn new(name: IdString) -> IcWire {
        IcWire {
            name: name,
        }
    }
}

pub struct IcPseudoCell {
    pub bel: IdString,
    pub pins: Vec<IdString>,
}

pub struct IcPip {
    pub src_wire: usize,
    pub dst_wire: usize,
    pub pseudo_cells: Vec<IcPseudoCell>,
}

// A tile instance
pub struct IcTileInst {
    pub name: IdString,
    pub x: u32,
    pub y: u32,
    pub type_idx: usize,
    // mapping between wires and nodes
    wire_to_node: HashMap<IdString, usize>,
}

impl IcTileInst {
    pub fn new(ids: &mut IdStringDB, x: u32, y: u32) -> IcTileInst {
        IcTileInst {
            name: ids.id(&format!("R{}C{}", y, x)),
            x: x,
            y: y,
            type_idx: 0,
            wire_to_node: HashMap::new(),
        }
    }
}

// A reference to a tile wire
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct IcWireRef {
    pub tile_name: IdString,
    pub wire_name: IdString,
}

// A node instance
pub struct IcNode {
    // list of tile wires in the node
    pub wires: HashSet<IcWireRef>,
    pub root_wire: IcWireRef,
}

impl IcNode {
    pub fn new(root_wire: IcWireRef) -> IcNode {
        IcNode {
            wires: [root_wire.clone()].iter().cloned().collect(),
            root_wire: root_wire,
        }
    }
}

// The overall routing resource graph
pub struct IcGraph {
    pub tile_types: IndexedMap<TileTypeKey, IcTileType>,
    pub tiles: Vec<IcTileInst>,
    pub nodes: Vec<IcNode>,
    pub width: u32,
    pub height: u32,
}

impl IcGraph {
    pub fn new(ids: &mut IdStringDB, width: u32, height: u32) -> IcGraph {
        IcGraph {
            tile_types: IndexedMap::new(),
            tiles: (0..width*height).map(|i| IcTileInst::new(ids, i%width, i/width)).collect(),
            nodes: Vec::new(),
            width: width,
            height: height
        }
    }
    pub fn tile_idx(&self, x: u32, y: u32) -> usize {
        assert!(x < self.width);
        assert!(y < self.height);
        ((y * self.width) + x) as usize
    }
    pub fn tile_at(&mut self, x: u32, y: u32) -> &mut IcTileInst {
        let idx = self.tile_idx(x, y);
        &mut self.tiles[idx]
    }
    pub fn type_at(&mut self, x: u32, y: u32) -> &mut IcTileType {
        let idx = self.tile_at(x, y).type_idx;
        self.tile_types.value_mut(idx)
    }
    pub fn map_node(&mut self, root_x: u32, root_y: u32, root_wire: IdString, wire_x: u32, wire_y: u32, wire: IdString) {
        // Make sure wire exists in both tiles
        self.type_at(root_x, root_y).wire(root_wire);
        self.type_at(wire_x, wire_y).wire(wire);
        // Update wire-node mapping
        let root_tile_idx = self.tile_idx(root_x, root_y);
        let node_idx = match self.tiles[root_tile_idx].wire_to_node.get(&root_wire) {
            Some(i) => *i,
            None => {
                let idx = self.nodes.len();
                self.nodes.push(IcNode::new(IcWireRef { tile_name: self.tiles[root_tile_idx].name, wire_name: root_wire }));
                self.tiles[root_tile_idx].wire_to_node.insert(root_wire, idx);
                idx
            }
        };
        let wire_tile_idx = self.tile_idx(wire_x, wire_y);
        self.nodes[node_idx].wires.insert(IcWireRef {tile_name: self.tiles[wire_tile_idx].name, wire_name: wire });
        self.tiles[wire_tile_idx].wire_to_node.insert(wire, node_idx);
    }
}

pub struct GraphBuilder<'a> {
    g: IcGraph,
    ids: &'a mut IdStringDB,
    chip: &'a Chip,
    glb: DeviceGlobalsData,
    db: &'a mut Database,
    tiletypes_by_xy: HashMap<(u32, u32), TileTypeKey>,
    orig_tts: TileTypes,
}

impl <'a> GraphBuilder<'a> {
    fn new(ids: &'a mut IdStringDB, chip: &'a Chip, db: &'a mut Database) -> GraphBuilder<'a> {
        let mut width = 0;
        let mut height = 0;
        let mut tiletypes_by_xy = HashMap::new();
        for t in chip.tiles.iter() {
            tiletypes_by_xy.entry((t.x, t.y)).or_insert(TileTypeKey::new()).tile_types.push(t.tiletype.to_string());
            width = std::cmp::max(width, t.x + 1);
            height = std::cmp::max(height, t.y + 1);
        }

        let orig_tts = TileTypes::new(db, ids, &chip.family, &[&chip.device]);

        let globals = db.device_globals(&chip.family, &chip.device);

        GraphBuilder {
            g: IcGraph::new(ids, width, height),
            ids: ids,
            chip: chip,
            glb: globals.clone(),
            db: db,
            tiletypes_by_xy: tiletypes_by_xy,
            // the original tiletypes from the database
            orig_tts: orig_tts,
        }
    }
    fn setup_tiletypes(&mut self) {
        for t in self.g.tiles.iter_mut() {
            let key = self.tiletypes_by_xy.get(&(t.x, t.y)).unwrap();
            t.type_idx = self.g.tile_types.add(key, IcTileType::new(key.clone(), &self.chip.family, self.db));
        }
        for (key, lt) in self.g.tile_types.iter_mut() {
            // setup wires for site pins
            let site_wires : Vec<String> = lt.site_types.iter().map(|s| s.pins.iter()).flatten().map(|p| p.tile_wire.clone()).collect();
            for w in site_wires {
                lt.wire(self.ids.id(&w));
            }
            for tt in key.tile_types.iter() {
                // setup wires for all sub-tile-types
                let tt_data = self.orig_tts.get(tt).unwrap();
                for wire in tt_data.wire_ids.iter() {
                    lt.wire(*wire);
                }
                // setup pips, both fixed and not
                // TODO: skip site wires and pips and deal with these later
                let tdb = &self.db.tile_bitdb(&self.chip.family, tt).db;
                for (to_wire, pips) in tdb.pips.iter() {
                    for pip in pips.iter() {
                        if is_site_wire(tt, &pip.from_wire) && is_site_wire(tt, to_wire) {
                            continue;
                        }
                        lt.add_pip(self.ids.id(&pip.from_wire), self.ids.id(to_wire));
                    }
                }
                for (to_wire, conns) in tdb.conns.iter() {
                    for conn in conns.iter() {
                        if is_site_wire(tt, &conn.from_wire) && is_site_wire(tt, to_wire) {
                            continue;
                        }
                        lt.add_pip(self.ids.id(&conn.from_wire), self.ids.id(to_wire));
                    }
                }
            }
            if lt.site_types.iter().find(|s| s.site_type == "PLC").is_some() {
                let gnd_wire = self.ids.id("G:GND");
                lt.wire(gnd_wire);
                for i in 0..8 {
                    // Create pseudo-ground drivers for LUT outputs
                    lt.add_ppip(gnd_wire, self.ids.id(&format!("JF{}", i)),
                        vec![
                            IcPseudoCell {
                                bel: self.ids.id(&format!("SLICE{}_LUT{}", &"ABCD"[(i/2)..(i/2)+1], i%2)),
                                pins: vec![self.ids.id("F")],
                            }
                        ]);
                    // Create LUT route-through PIPs
                    for j in &["A", "B", "C", "D"] {
                        lt.add_ppip(self.ids.id(&format!("J{}{}", j, i)), self.ids.id(&format!("JF{}", i)),
                        vec![
                            IcPseudoCell {
                                bel: self.ids.id(&format!("SLICE{}_LUT{}", &"ABCD"[(i/2)..(i/2)+1], i%2)),
                                pins: vec![self.ids.id(j), self.ids.id("F")],
                            }
                        ]);
                    }
                }
            }
        }
    }
    // Convert a neighbour to a coordinate
    pub fn neighbour_tile(&self, x: u32, y: u32, n: &Neighbour) -> Option<(u32, u32)> {
        let conv_tuple = |(x, y)| (x as u32, y as u32);
        match n {
            Neighbour::RelXY { rel_x, rel_y } => {
                let nx = (x as i32) + rel_x;
                let ny = (y as i32) + rel_y;
                if nx >= 0 && ny >= 0 && (nx as u32) < self.g.width && (ny as u32) < self.g.height {
                    Some((nx as u32, ny as u32))
                } else {
                    None
                }
            }
            Neighbour::Global => {
                Some((0, 0))
            }
            Neighbour::Branch => {
                let branch_col = self.glb.branch_sink_to_origin(x as usize).unwrap();
                Some((branch_col.try_into().unwrap(), y))
            }
            Neighbour::BranchDriver { side } => {
                let offset: i32 = match side {
                    BranchSide::Right => 2,
                    BranchSide::Left => -2,
                };
                let branch_col = self
                    .glb
                    .branch_sink_to_origin((x as i32 + offset) as usize)
                    .unwrap();
                Some((branch_col.try_into().unwrap(), y))
            }
            Neighbour::Spine => Some(conv_tuple(self.glb.spine_sink_to_origin(x as usize, y as usize).unwrap())),
            Neighbour::HRow => Some(conv_tuple(self.glb.hrow_sink_to_origin(x as usize, y as usize).unwrap())),
            _ => None
        }
    }
    fn setup_wire2node(&mut self) {
        for ((x, y), key) in self.tiletypes_by_xy.iter() {
            for tt in key.tile_types.iter() {
                for wire in self.orig_tts.get(tt).unwrap().wires.iter() {
                    let (neigh, base_wire) = Neighbour::parse_wire(wire);
                    if let Some(neigh) = neigh {
                        // it's a neighbour wire, map to the base tile
                        if let Some((root_x, root_y)) = self.neighbour_tile(*x, *y, &neigh) {
                            self.g.map_node(root_x, root_y, self.ids.id(base_wire), *x, *y, self.ids.id(wire));
                        }

                    } else {
                        // root node, map to itself
                        self.g.map_node(*x, *y, self.ids.id(base_wire), *x, *y, self.ids.id(base_wire));
                    }
                }
            }
        }
    }

    pub fn run(ids: &'a mut IdStringDB, chip: &'a Chip, db: &'a mut Database) -> IcGraph {
        let mut builder = GraphBuilder::new(ids, chip, db);
        builder.setup_tiletypes();
        builder.setup_wire2node();
        builder.g
    }
}
<|endoftext|>


<commit_before>extern crate fs2;
extern crate toml;
#[cfg(test)] extern crate tempdir;
extern crate semver;

pub mod storeid;
pub mod content;
pub mod entry;
pub mod error;
pub mod header;
pub mod store;
mod lazyfile;

<commit_msg>Include regex<commit_after>extern crate fs2;
extern crate regex;
extern crate toml;
#[cfg(test)] extern crate tempdir;
extern crate semver;

pub mod storeid;
pub mod content;
pub mod entry;
pub mod error;
pub mod header;
pub mod store;
mod lazyfile;

<|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,
        ignore_tiles: BTreeSet<String>, // changes in these tiles don't cause pips to be rejected
    },
    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,
        ignore_tiles: &BTreeSet<String>,
        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(),
                ignore_tiles: ignore_tiles.clone(),
            },
            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,
                ignore_tiles,
            } => {
                // 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) && !ignore_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();
                            if changed_bits.len() == 0 {
                                continue;
                            }
                            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>Fix full_mux_style pip fuzzing where base value is 1<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,
        ignore_tiles: BTreeSet<String>, // changes in these tiles don't cause pips to be rejected
    },
    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,
        ignore_tiles: &BTreeSet<String>,
        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(),
                ignore_tiles: ignore_tiles.clone(),
            },
            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,
                ignore_tiles,
            } => {
                // 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) && !ignore_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,
                                                    !self
                                                        .base
                                                        .tile_by_name(tile)
                                                        .unwrap()
                                                        .cram
                                                        .get(*f, *b),
                                                ))
                                            }) == self
                                                .base
                                                .tile_by_name(tile)
                                                .unwrap()
                                                .cram
                                                .get(*f, *b),
                                        })
                                        .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();
                            if changed_bits.len() == 0 {
                                continue;
                            }
                            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>use std::env;
use std::path::Path;

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let out_path = Path::new(&out_dir).join("bindgen.rs");
    build::main(&out_dir, &out_path);
}

#[cfg(feature = "bundled")]
mod build {
    use cc;
    use std::path::Path;

    pub fn main(out_dir: &str, out_path: &Path) {
        if cfg!(feature = "sqlcipher") {
            panic!("Builds with bundled SQLCipher are not supported");
        }

        #[cfg(feature = "buildtime_bindgen")]
        {
            use super::{bindings, HeaderLocation};
            let header = HeaderLocation::FromPath("sqlite3/sqlite3.h".to_owned());
            bindings::write_to_out_dir(header, out_path);
        }
        #[cfg(not(feature = "buildtime_bindgen"))]
        {
            use std::fs;
            fs::copy("sqlite3/bindgen_bundled_version.rs", out_path)
                .expect("Could not copy bindings to output directory");
        }

        let mut cfg = cc::Build::new();
        cfg.file("sqlite3/sqlite3.c")
            .flag("-DSQLITE_CORE")
            .flag("-DSQLITE_DEFAULT_FOREIGN_KEYS=1")
            .flag("-DSQLITE_ENABLE_API_ARMOR")
            .flag("-DSQLITE_ENABLE_COLUMN_METADATA")
            .flag("-DSQLITE_ENABLE_DBSTAT_VTAB")
            .flag("-DSQLITE_ENABLE_FTS3")
            .flag("-DSQLITE_ENABLE_FTS3_PARENTHESIS")
            .flag("-DSQLITE_ENABLE_FTS5")
            .flag("-DSQLITE_ENABLE_JSON1")
            .flag("-DSQLITE_ENABLE_LOAD_EXTENSION=1")
            .flag("-DSQLITE_ENABLE_MEMORY_MANAGEMENT")
            .flag("-DSQLITE_ENABLE_RTREE")
            .flag("-DSQLITE_ENABLE_STAT2")
            .flag("-DSQLITE_ENABLE_STAT4")
            .flag("-DSQLITE_HAVE_ISNAN")
            .flag("-DSQLITE_SOUNDEX")
            .flag("-DSQLITE_THREADSAFE=1")
            .flag("-DSQLITE_USE_URI")
            .flag("-DHAVE_USLEEP=1");
        if cfg!(feature = "unlock_notify") {
            cfg.flag("-DSQLITE_ENABLE_UNLOCK_NOTIFY");
        }
        if cfg!(feature = "preupdate_hook") {
            cfg.flag("-DSQLITE_ENABLE_PREUPDATE_HOOK");
        }
        if cfg!(feature = "session") {
            cfg.flag("-DSQLITE_ENABLE_SESSION");
        }
        cfg.compile("libsqlite3.a");

        println!("cargo:lib_dir={}", out_dir);
    }
}

fn env_prefix() -> &'static str {
    if cfg!(feature = "sqlcipher") {
        "SQLCIPHER"
    } else {
        "SQLITE3"
    }
}

pub enum HeaderLocation {
    FromEnvironment,
    Wrapper,
    FromPath(String),
}

impl From<HeaderLocation> for String {
    fn from(header: HeaderLocation) -> String {
        match header {
            HeaderLocation::FromEnvironment => {
                let prefix = env_prefix();
                let mut header = env::var(format!("{}_INCLUDE_DIR", prefix)).expect(&format!(
                    "{}_INCLUDE_DIR must be set if {}_LIB_DIR is set",
                    prefix, prefix
                ));
                header.push_str("/sqlite3.h");
                header
            }
            HeaderLocation::Wrapper => "wrapper.h".into(),
            HeaderLocation::FromPath(path) => path,
        }
    }
}

#[cfg(not(feature = "bundled"))]
mod build {
    use pkg_config;

    #[cfg(all(feature = "vcpkg", target_env = "msvc"))]
    extern crate vcpkg;

    use super::{bindings, env_prefix, HeaderLocation};
    use std::env;
    use std::path::Path;

    pub fn main(_out_dir: &str, out_path: &Path) {
        let header = find_sqlite();
        bindings::write_to_out_dir(header, out_path);
    }

    fn find_link_mode() -> &'static str {
        // If the user specifies SQLITE_STATIC (or SQLCIPHER_STATIC), do static
        // linking, unless it's explicitly set to 0.
        match &env::var(format!("{}_STATIC", env_prefix())) {
            Ok(v) if v != "0" => "static",
            _ => "dylib",
        }
    }
    // Prints the necessary cargo link commands and returns the path to the header.
    fn find_sqlite() -> HeaderLocation {
        let link_lib = link_lib();

        println!("cargo:rerun-if-env-changed={}_INCLUDE_DIR", env_prefix());
        println!("cargo:rerun-if-env-changed={}_LIB_DIR", env_prefix());
        println!("cargo:rerun-if-env-changed={}_STATIC", env_prefix());
        if cfg!(target_os = "windows") {
            println!("cargo:rerun-if-env-changed=PATH");
        }
        // Allow users to specify where to find SQLite.
        if let Ok(dir) = env::var(format!("{}_LIB_DIR", env_prefix())) {
            // Try to use pkg-config to determine link commands
            let pkgconfig_path = Path::new(&dir).join("pkgconfig");
            env::set_var("PKG_CONFIG_PATH", pkgconfig_path);
            if let Err(_) = pkg_config::Config::new().probe(link_lib) {
                // Otherwise just emit the bare minimum link commands.
                println!("cargo:rustc-link-lib={}={}", find_link_mode(), link_lib);
                println!("cargo:rustc-link-search={}", dir);
            }
            return HeaderLocation::FromEnvironment;
        }

        if let Some(header) = try_vcpkg() {
            return header;
        }

        // See if pkg-config can do everything for us.
        match pkg_config::Config::new()
            .print_system_libs(false)
            .probe(link_lib)
        {
            Ok(mut lib) => {
                if let Some(mut header) = lib.include_paths.pop() {
                    header.push("sqlite3.h");
                    HeaderLocation::FromPath(header.to_string_lossy().into())
                } else {
                    HeaderLocation::Wrapper
                }
            }
            Err(_) => {
                // No env var set and pkg-config couldn't help; just output the link-lib
                // request and hope that the library exists on the system paths. We used to
                // output /usr/lib explicitly, but that can introduce other linking problems;
                // see https://github.com/jgallagher/rusqlite/issues/207.
                println!("cargo:rustc-link-lib={}={}", find_link_mode(), link_lib);
                HeaderLocation::Wrapper
            }
        }
    }

    #[cfg(all(feature = "vcpkg", target_env = "msvc"))]
    fn try_vcpkg() -> Option<HeaderLocation> {
        // See if vcpkg can find it.
        if let Ok(mut lib) = vcpkg::Config::new().probe(link_lib()) {
            if let Some(mut header) = lib.include_paths.pop() {
                header.push("sqlite3.h");
                return Some(HeaderLocation::FromPath(header.to_string_lossy().into()));
            }
        }
        None
    }

    #[cfg(not(all(feature = "vcpkg", target_env = "msvc")))]
    fn try_vcpkg() -> Option<HeaderLocation> {
        None
    }

    fn link_lib() -> &'static str {
        if cfg!(feature = "sqlcipher") {
            "sqlcipher"
        } else {
            "sqlite3"
        }
    }
}

#[cfg(all(not(feature = "buildtime_bindgen"), not(feature = "bundled")))]
mod bindings {
    use super::HeaderLocation;

    use std::fs;
    use std::path::Path;

    static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[
        "bindgen-bindings/bindgen_3.6.8.rs",
        #[cfg(feature = "min_sqlite_version_3_6_23")]
        "bindgen-bindings/bindgen_3.6.23.rs",
        #[cfg(feature = "min_sqlite_version_3_7_7")]
        "bindgen-bindings/bindgen_3.7.7.rs",
        #[cfg(feature = "min_sqlite_version_3_7_16")]
        "bindgen-bindings/bindgen_3.7.16.rs",
    ];

    pub fn write_to_out_dir(_header: HeaderLocation, out_path: &Path) {
        let in_path = PREBUILT_BINDGEN_PATHS[PREBUILT_BINDGEN_PATHS.len() - 1];
        fs::copy(in_path, out_path).expect("Could not copy bindings to output directory");
    }
}

#[cfg(feature = "buildtime_bindgen")]
mod bindings {
    use bindgen;

    use self::bindgen::callbacks::{IntKind, ParseCallbacks};
    use super::HeaderLocation;

    use std::fs::OpenOptions;
    use std::io::Write;
    use std::path::Path;

    #[derive(Debug)]
    struct SqliteTypeChooser;

    impl ParseCallbacks for SqliteTypeChooser {
        fn int_macro(&self, _name: &str, value: i64) -> Option<IntKind> {
            if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 {
                Some(IntKind::I32)
            } else {
                None
            }
        }
    }

    pub fn write_to_out_dir(header: HeaderLocation, out_path: &Path) {
        let header: String = header.into();
        let mut output = Vec::new();
        let mut bindings = bindgen::builder()
            .header(header.clone())
            .parse_callbacks(Box::new(SqliteTypeChooser))
            .rustfmt_bindings(true);

        if cfg!(feature = "unlock_notify") {
            bindings = bindings.clang_arg("-DSQLITE_ENABLE_UNLOCK_NOTIFY");
        }
        if cfg!(feature = "preupdate_hook") {
            bindings = bindings.clang_arg("-DSQLITE_ENABLE_PREUPDATE_HOOK");
        }
        if cfg!(feature = "session") {
            bindings = bindings.clang_arg("-DSQLITE_ENABLE_SESSION");
        }

        bindings
            .generate()
            .expect(&format!("could not run bindgen on header {}", header))
            .write(Box::new(&mut output))
            .expect("could not write output of bindgen");
        let mut output = String::from_utf8(output).expect("bindgen output was not UTF-8?!");

        // rusqlite's functions feature ors in the SQLITE_DETERMINISTIC flag when it
        // can. This flag was added in SQLite 3.8.3, but oring it in in prior
        // versions of SQLite is harmless. We don't want to not build just
        // because this flag is missing (e.g., if we're linking against
        // SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's
        // output.
        if !output.contains("pub const SQLITE_DETERMINISTIC") {
            output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n");
        }

        let mut file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(out_path.clone())
            .expect(&format!("Could not write to {:?}", out_path));

        file.write_all(output.as_bytes())
            .expect(&format!("Could not write to {:?}", out_path));
    }
}
<commit_msg>rebuild when VCPKGRS_DYNAMIC changes<commit_after>use std::env;
use std::path::Path;

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let out_path = Path::new(&out_dir).join("bindgen.rs");
    build::main(&out_dir, &out_path);
}

#[cfg(feature = "bundled")]
mod build {
    use cc;
    use std::path::Path;

    pub fn main(out_dir: &str, out_path: &Path) {
        if cfg!(feature = "sqlcipher") {
            panic!("Builds with bundled SQLCipher are not supported");
        }

        #[cfg(feature = "buildtime_bindgen")]
        {
            use super::{bindings, HeaderLocation};
            let header = HeaderLocation::FromPath("sqlite3/sqlite3.h".to_owned());
            bindings::write_to_out_dir(header, out_path);
        }
        #[cfg(not(feature = "buildtime_bindgen"))]
        {
            use std::fs;
            fs::copy("sqlite3/bindgen_bundled_version.rs", out_path)
                .expect("Could not copy bindings to output directory");
        }

        let mut cfg = cc::Build::new();
        cfg.file("sqlite3/sqlite3.c")
            .flag("-DSQLITE_CORE")
            .flag("-DSQLITE_DEFAULT_FOREIGN_KEYS=1")
            .flag("-DSQLITE_ENABLE_API_ARMOR")
            .flag("-DSQLITE_ENABLE_COLUMN_METADATA")
            .flag("-DSQLITE_ENABLE_DBSTAT_VTAB")
            .flag("-DSQLITE_ENABLE_FTS3")
            .flag("-DSQLITE_ENABLE_FTS3_PARENTHESIS")
            .flag("-DSQLITE_ENABLE_FTS5")
            .flag("-DSQLITE_ENABLE_JSON1")
            .flag("-DSQLITE_ENABLE_LOAD_EXTENSION=1")
            .flag("-DSQLITE_ENABLE_MEMORY_MANAGEMENT")
            .flag("-DSQLITE_ENABLE_RTREE")
            .flag("-DSQLITE_ENABLE_STAT2")
            .flag("-DSQLITE_ENABLE_STAT4")
            .flag("-DSQLITE_HAVE_ISNAN")
            .flag("-DSQLITE_SOUNDEX")
            .flag("-DSQLITE_THREADSAFE=1")
            .flag("-DSQLITE_USE_URI")
            .flag("-DHAVE_USLEEP=1");
        if cfg!(feature = "unlock_notify") {
            cfg.flag("-DSQLITE_ENABLE_UNLOCK_NOTIFY");
        }
        if cfg!(feature = "preupdate_hook") {
            cfg.flag("-DSQLITE_ENABLE_PREUPDATE_HOOK");
        }
        if cfg!(feature = "session") {
            cfg.flag("-DSQLITE_ENABLE_SESSION");
        }
        cfg.compile("libsqlite3.a");

        println!("cargo:lib_dir={}", out_dir);
    }
}

fn env_prefix() -> &'static str {
    if cfg!(feature = "sqlcipher") {
        "SQLCIPHER"
    } else {
        "SQLITE3"
    }
}

pub enum HeaderLocation {
    FromEnvironment,
    Wrapper,
    FromPath(String),
}

impl From<HeaderLocation> for String {
    fn from(header: HeaderLocation) -> String {
        match header {
            HeaderLocation::FromEnvironment => {
                let prefix = env_prefix();
                let mut header = env::var(format!("{}_INCLUDE_DIR", prefix)).expect(&format!(
                    "{}_INCLUDE_DIR must be set if {}_LIB_DIR is set",
                    prefix, prefix
                ));
                header.push_str("/sqlite3.h");
                header
            }
            HeaderLocation::Wrapper => "wrapper.h".into(),
            HeaderLocation::FromPath(path) => path,
        }
    }
}

#[cfg(not(feature = "bundled"))]
mod build {
    use pkg_config;

    #[cfg(all(feature = "vcpkg", target_env = "msvc"))]
    extern crate vcpkg;

    use super::{bindings, env_prefix, HeaderLocation};
    use std::env;
    use std::path::Path;

    pub fn main(_out_dir: &str, out_path: &Path) {
        let header = find_sqlite();
        bindings::write_to_out_dir(header, out_path);
    }

    fn find_link_mode() -> &'static str {
        // If the user specifies SQLITE_STATIC (or SQLCIPHER_STATIC), do static
        // linking, unless it's explicitly set to 0.
        match &env::var(format!("{}_STATIC", env_prefix())) {
            Ok(v) if v != "0" => "static",
            _ => "dylib",
        }
    }
    // Prints the necessary cargo link commands and returns the path to the header.
    fn find_sqlite() -> HeaderLocation {
        let link_lib = link_lib();

        println!("cargo:rerun-if-env-changed={}_INCLUDE_DIR", env_prefix());
        println!("cargo:rerun-if-env-changed={}_LIB_DIR", env_prefix());
        println!("cargo:rerun-if-env-changed={}_STATIC", env_prefix());
        if cfg!(target_os = "windows") {
            println!("cargo:rerun-if-env-changed=PATH");
        }
        if cfg!(all(feature = "vcpkg", target_env = "msvc")) {
            println!("cargo:rerun-if-env-changed=VCPKGRS_DYNAMIC");
        }
        // Allow users to specify where to find SQLite.
        if let Ok(dir) = env::var(format!("{}_LIB_DIR", env_prefix())) {
            // Try to use pkg-config to determine link commands
            let pkgconfig_path = Path::new(&dir).join("pkgconfig");
            env::set_var("PKG_CONFIG_PATH", pkgconfig_path);
            if let Err(_) = pkg_config::Config::new().probe(link_lib) {
                // Otherwise just emit the bare minimum link commands.
                println!("cargo:rustc-link-lib={}={}", find_link_mode(), link_lib);
                println!("cargo:rustc-link-search={}", dir);
            }
            return HeaderLocation::FromEnvironment;
        }

        if let Some(header) = try_vcpkg() {
            return header;
        }

        // See if pkg-config can do everything for us.
        match pkg_config::Config::new()
            .print_system_libs(false)
            .probe(link_lib)
        {
            Ok(mut lib) => {
                if let Some(mut header) = lib.include_paths.pop() {
                    header.push("sqlite3.h");
                    HeaderLocation::FromPath(header.to_string_lossy().into())
                } else {
                    HeaderLocation::Wrapper
                }
            }
            Err(_) => {
                // No env var set and pkg-config couldn't help; just output the link-lib
                // request and hope that the library exists on the system paths. We used to
                // output /usr/lib explicitly, but that can introduce other linking problems;
                // see https://github.com/jgallagher/rusqlite/issues/207.
                println!("cargo:rustc-link-lib={}={}", find_link_mode(), link_lib);
                HeaderLocation::Wrapper
            }
        }
    }

    #[cfg(all(feature = "vcpkg", target_env = "msvc"))]
    fn try_vcpkg() -> Option<HeaderLocation> {
        // See if vcpkg can find it.
        if let Ok(mut lib) = vcpkg::Config::new().probe(link_lib()) {
            if let Some(mut header) = lib.include_paths.pop() {
                header.push("sqlite3.h");
                return Some(HeaderLocation::FromPath(header.to_string_lossy().into()));
            }
        }
        None
    }

    #[cfg(not(all(feature = "vcpkg", target_env = "msvc")))]
    fn try_vcpkg() -> Option<HeaderLocation> {
        None
    }

    fn link_lib() -> &'static str {
        if cfg!(feature = "sqlcipher") {
            "sqlcipher"
        } else {
            "sqlite3"
        }
    }
}

#[cfg(all(not(feature = "buildtime_bindgen"), not(feature = "bundled")))]
mod bindings {
    use super::HeaderLocation;

    use std::fs;
    use std::path::Path;

    static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[
        "bindgen-bindings/bindgen_3.6.8.rs",
        #[cfg(feature = "min_sqlite_version_3_6_23")]
        "bindgen-bindings/bindgen_3.6.23.rs",
        #[cfg(feature = "min_sqlite_version_3_7_7")]
        "bindgen-bindings/bindgen_3.7.7.rs",
        #[cfg(feature = "min_sqlite_version_3_7_16")]
        "bindgen-bindings/bindgen_3.7.16.rs",
    ];

    pub fn write_to_out_dir(_header: HeaderLocation, out_path: &Path) {
        let in_path = PREBUILT_BINDGEN_PATHS[PREBUILT_BINDGEN_PATHS.len() - 1];
        fs::copy(in_path, out_path).expect("Could not copy bindings to output directory");
    }
}

#[cfg(feature = "buildtime_bindgen")]
mod bindings {
    use bindgen;

    use self::bindgen::callbacks::{IntKind, ParseCallbacks};
    use super::HeaderLocation;

    use std::fs::OpenOptions;
    use std::io::Write;
    use std::path::Path;

    #[derive(Debug)]
    struct SqliteTypeChooser;

    impl ParseCallbacks for SqliteTypeChooser {
        fn int_macro(&self, _name: &str, value: i64) -> Option<IntKind> {
            if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 {
                Some(IntKind::I32)
            } else {
                None
            }
        }
    }

    pub fn write_to_out_dir(header: HeaderLocation, out_path: &Path) {
        let header: String = header.into();
        let mut output = Vec::new();
        let mut bindings = bindgen::builder()
            .header(header.clone())
            .parse_callbacks(Box::new(SqliteTypeChooser))
            .rustfmt_bindings(true);

        if cfg!(feature = "unlock_notify") {
            bindings = bindings.clang_arg("-DSQLITE_ENABLE_UNLOCK_NOTIFY");
        }
        if cfg!(feature = "preupdate_hook") {
            bindings = bindings.clang_arg("-DSQLITE_ENABLE_PREUPDATE_HOOK");
        }
        if cfg!(feature = "session") {
            bindings = bindings.clang_arg("-DSQLITE_ENABLE_SESSION");
        }

        bindings
            .generate()
            .expect(&format!("could not run bindgen on header {}", header))
            .write(Box::new(&mut output))
            .expect("could not write output of bindgen");
        let mut output = String::from_utf8(output).expect("bindgen output was not UTF-8?!");

        // rusqlite's functions feature ors in the SQLITE_DETERMINISTIC flag when it
        // can. This flag was added in SQLite 3.8.3, but oring it in in prior
        // versions of SQLite is harmless. We don't want to not build just
        // because this flag is missing (e.g., if we're linking against
        // SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's
        // output.
        if !output.contains("pub const SQLITE_DETERMINISTIC") {
            output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n");
        }

        let mut file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(out_path.clone())
            .expect(&format!("Could not write to {:?}", out_path));

        file.write_all(output.as_bytes())
            .expect(&format!("Could not write to {:?}", out_path));
    }
}
<|endoftext|>


<commit_before>fn main() {
    build::main();
}

#[cfg(feature = "bundled")]
mod build {
    extern crate gcc;
    use std::{env, fs};
    use std::path::Path;

    pub fn main() {
        let out_dir = env::var("OUT_DIR").unwrap();
        let out_path = Path::new(&out_dir).join("bindgen.rs");
        fs::copy("sqlite3/bindgen_bundled_version.rs", out_path)
            .expect("Could not copy bindings to output directory");

        gcc::Config::new()
            .file("sqlite3/sqlite3.c")
            .flag("-DSQLITE_CORE")
            .flag("-DSQLITE_DEFAULT_FOREIGN_KEYS=1")
            .flag("-DSQLITE_ENABLE_API_ARMOR")
            .flag("-DSQLITE_ENABLE_COLUMN_METADATA")
            .flag("-DSQLITE_ENABLE_DBSTAT_VTAB")
            .flag("-DSQLITE_ENABLE_FTS3")
            .flag("-DSQLITE_ENABLE_FTS3_PARENTHESIS")
            .flag("-DSQLITE_ENABLE_FTS5")
            .flag("-DSQLITE_ENABLE_JSON1")
            .flag("-DSQLITE_ENABLE_LOAD_EXTENSION=1")
            .flag("-DSQLITE_ENABLE_MEMORY_MANAGEMENT")
            .flag("-DSQLITE_ENABLE_RTREE")
            .flag("-DSQLITE_ENABLE_STAT2")
            .flag("-DSQLITE_ENABLE_STAT4")
            .flag("-DSQLITE_HAVE_ISNAN")
            .flag("-DSQLITE_SOUNDEX")
            .flag("-DSQLITE_THREADSAFE=1")
            .flag("-DSQLITE_USE_URI")
            .compile("libsqlite3.a");
    }
}

#[cfg(not(feature = "bundled"))]
mod build {
    extern crate pkg_config;

    use std::env;

    pub enum HeaderLocation {
        FromEnvironment,
        Wrapper,
        FromPath(String),
    }

    impl From<HeaderLocation> for String {
        fn from(header: HeaderLocation) -> String {
            match header {
                HeaderLocation::FromEnvironment => {
                    let mut header = env::var("SQLITE3_INCLUDE_DIR")
                        .expect("SQLITE3_INCLUDE_DIR must be set if SQLITE3_LIB_DIR is set");
                    header.push_str("/sqlite3.h");
                    header
                }
                HeaderLocation::Wrapper => "wrapper.h".into(),
                HeaderLocation::FromPath(path) => path,
            }
        }
    }

    pub fn main() {
        let header = find_sqlite();
        bindings::write_to_out_dir(header);
    }

    // Prints the necessary cargo link commands and returns the path to the header.
    fn find_sqlite() -> HeaderLocation {
        // Allow users to specify where to find SQLite.
        if let Ok(dir) = env::var("SQLITE3_LIB_DIR") {
            println!("cargo:rustc-link-lib=sqlite3");
            println!("cargo:rustc-link-search={}", dir);
            return HeaderLocation::FromEnvironment;
        }

        // See if pkg-config can do everything for us.
        match pkg_config::Config::new().print_system_libs(false).probe("sqlite3") {
            Ok(mut lib) => {
                if let Some(mut header) = lib.include_paths.pop() {
                    header.push("sqlite3.h");
                    HeaderLocation::FromPath(header.to_string_lossy().into())
                } else {
                    HeaderLocation::Wrapper
                }
            }
            Err(_) => {
                // No env var set and pkg-config couldn't help; just output the link-lib
                // request and hope that the library exists on the system paths. We used to
                // output /usr/lib explicitly, but that can introduce other linking problems; see
                // https://github.com/jgallagher/rusqlite/issues/207.
                println!("cargo:rustc-link-lib=sqlite3");
                HeaderLocation::Wrapper
            }
        }
    }

    #[cfg(not(feature = "buildtime_bindgen"))]
    mod bindings {
        use super::HeaderLocation;

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

        #[cfg_attr(rustfmt, rustfmt_skip)]
        static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[
            "bindgen-bindings/bindgen_3.6.8.rs",

            #[cfg(feature = "min_sqlite_version_3_6_11")]
            "bindgen-bindings/bindgen_3.6.11.rs",

            #[cfg(feature = "min_sqlite_version_3_6_23")]
            "bindgen-bindings/bindgen_3.6.23.rs",

            #[cfg(feature = "min_sqlite_version_3_7_3")]
            "bindgen-bindings/bindgen_3.7.3.rs",

            #[cfg(feature = "min_sqlite_version_3_7_4")]
            "bindgen-bindings/bindgen_3.7.4.rs",

            #[cfg(feature = "min_sqlite_version_3_7_16")]
            "bindgen-bindings/bindgen_3.7.16.rs",
        ];

        pub fn write_to_out_dir(_header: HeaderLocation) {
            let out_dir = env::var("OUT_DIR").unwrap();
            let out_path = Path::new(&out_dir).join("bindgen.rs");
            let in_path = PREBUILT_BINDGEN_PATHS[PREBUILT_BINDGEN_PATHS.len() - 1];
            fs::copy(in_path, out_path).expect("Could not copy bindings to output directory");
        }
    }

    #[cfg(feature = "buildtime_bindgen")]
    mod bindings {
        extern crate bindgen;

        use self::bindgen::chooser::{TypeChooser, IntKind};
        use super::HeaderLocation;

        use std::env;
        use std::io::Write;
        use std::fs::OpenOptions;
        use std::path::Path;

        #[derive(Debug)]
        struct SqliteTypeChooser;

        impl TypeChooser for SqliteTypeChooser {
            fn int_macro(&self, _name: &str, value: i64) -> Option<IntKind> {
                if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 {
                    Some(IntKind::I32)
                } else {
                    None
                }
            }
        }

        pub fn write_to_out_dir(header: HeaderLocation) {
            let header: String = header.into();
            let out_dir = env::var("OUT_DIR").unwrap();
            let mut output = Vec::new();
            bindgen::builder()
                .header(header.clone())
                .type_chooser(Box::new(SqliteTypeChooser))
                .generate()
                .expect(&format!("could not run bindgen on header {}", header))
                .write(Box::new(&mut output))
                .expect("could not write output of bindgen");
            let mut output = String::from_utf8(output).expect("bindgen output was not UTF-8?!");

            // rusqlite's functions feature ors in the SQLITE_DETERMINISTIC flag when it can. This flag
            // was added in SQLite 3.8.3, but oring it in in prior versions of SQLite is harmless. We
            // don't want to not build just because this flag is missing (e.g., if we're linking against
            // SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's output.
            if !output.contains("pub const SQLITE_DETERMINISTIC:") {
                output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n");
            }

            let path = Path::new(&out_dir).join("bindgen.rs");

            let mut file = OpenOptions::new()
                .write(true)
                .truncate(true)
                .create(true)
                .open(path.clone())
                .expect(&format!("Could not write to {:?}", path));

            file.write_all(output.as_bytes()).expect(&format!("Could not write to {:?}", path));
        }
    }
}
<commit_msg>TypeChooser has been renamed to ParseCallbacks<commit_after>fn main() {
    build::main();
}

#[cfg(feature = "bundled")]
mod build {
    extern crate gcc;
    use std::{env, fs};
    use std::path::Path;

    pub fn main() {
        let out_dir = env::var("OUT_DIR").unwrap();
        let out_path = Path::new(&out_dir).join("bindgen.rs");
        fs::copy("sqlite3/bindgen_bundled_version.rs", out_path)
            .expect("Could not copy bindings to output directory");

        gcc::Config::new()
            .file("sqlite3/sqlite3.c")
            .flag("-DSQLITE_CORE")
            .flag("-DSQLITE_DEFAULT_FOREIGN_KEYS=1")
            .flag("-DSQLITE_ENABLE_API_ARMOR")
            .flag("-DSQLITE_ENABLE_COLUMN_METADATA")
            .flag("-DSQLITE_ENABLE_DBSTAT_VTAB")
            .flag("-DSQLITE_ENABLE_FTS3")
            .flag("-DSQLITE_ENABLE_FTS3_PARENTHESIS")
            .flag("-DSQLITE_ENABLE_FTS5")
            .flag("-DSQLITE_ENABLE_JSON1")
            .flag("-DSQLITE_ENABLE_LOAD_EXTENSION=1")
            .flag("-DSQLITE_ENABLE_MEMORY_MANAGEMENT")
            .flag("-DSQLITE_ENABLE_RTREE")
            .flag("-DSQLITE_ENABLE_STAT2")
            .flag("-DSQLITE_ENABLE_STAT4")
            .flag("-DSQLITE_HAVE_ISNAN")
            .flag("-DSQLITE_SOUNDEX")
            .flag("-DSQLITE_THREADSAFE=1")
            .flag("-DSQLITE_USE_URI")
            .compile("libsqlite3.a");
    }
}

#[cfg(not(feature = "bundled"))]
mod build {
    extern crate pkg_config;

    use std::env;

    pub enum HeaderLocation {
        FromEnvironment,
        Wrapper,
        FromPath(String),
    }

    impl From<HeaderLocation> for String {
        fn from(header: HeaderLocation) -> String {
            match header {
                HeaderLocation::FromEnvironment => {
                    let mut header = env::var("SQLITE3_INCLUDE_DIR")
                        .expect("SQLITE3_INCLUDE_DIR must be set if SQLITE3_LIB_DIR is set");
                    header.push_str("/sqlite3.h");
                    header
                }
                HeaderLocation::Wrapper => "wrapper.h".into(),
                HeaderLocation::FromPath(path) => path,
            }
        }
    }

    pub fn main() {
        let header = find_sqlite();
        bindings::write_to_out_dir(header);
    }

    // Prints the necessary cargo link commands and returns the path to the header.
    fn find_sqlite() -> HeaderLocation {
        // Allow users to specify where to find SQLite.
        if let Ok(dir) = env::var("SQLITE3_LIB_DIR") {
            println!("cargo:rustc-link-lib=sqlite3");
            println!("cargo:rustc-link-search={}", dir);
            return HeaderLocation::FromEnvironment;
        }

        // See if pkg-config can do everything for us.
        match pkg_config::Config::new().print_system_libs(false).probe("sqlite3") {
            Ok(mut lib) => {
                if let Some(mut header) = lib.include_paths.pop() {
                    header.push("sqlite3.h");
                    HeaderLocation::FromPath(header.to_string_lossy().into())
                } else {
                    HeaderLocation::Wrapper
                }
            }
            Err(_) => {
                // No env var set and pkg-config couldn't help; just output the link-lib
                // request and hope that the library exists on the system paths. We used to
                // output /usr/lib explicitly, but that can introduce other linking problems; see
                // https://github.com/jgallagher/rusqlite/issues/207.
                println!("cargo:rustc-link-lib=sqlite3");
                HeaderLocation::Wrapper
            }
        }
    }

    #[cfg(not(feature = "buildtime_bindgen"))]
    mod bindings {
        use super::HeaderLocation;

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

        #[cfg_attr(rustfmt, rustfmt_skip)]
        static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[
            "bindgen-bindings/bindgen_3.6.8.rs",

            #[cfg(feature = "min_sqlite_version_3_6_11")]
            "bindgen-bindings/bindgen_3.6.11.rs",

            #[cfg(feature = "min_sqlite_version_3_6_23")]
            "bindgen-bindings/bindgen_3.6.23.rs",

            #[cfg(feature = "min_sqlite_version_3_7_3")]
            "bindgen-bindings/bindgen_3.7.3.rs",

            #[cfg(feature = "min_sqlite_version_3_7_4")]
            "bindgen-bindings/bindgen_3.7.4.rs",

            #[cfg(feature = "min_sqlite_version_3_7_16")]
            "bindgen-bindings/bindgen_3.7.16.rs",
        ];

        pub fn write_to_out_dir(_header: HeaderLocation) {
            let out_dir = env::var("OUT_DIR").unwrap();
            let out_path = Path::new(&out_dir).join("bindgen.rs");
            let in_path = PREBUILT_BINDGEN_PATHS[PREBUILT_BINDGEN_PATHS.len() - 1];
            fs::copy(in_path, out_path).expect("Could not copy bindings to output directory");
        }
    }

    #[cfg(feature = "buildtime_bindgen")]
    mod bindings {
        extern crate bindgen;

        use self::bindgen::callbacks::{ParseCallbacks, IntKind};
        use super::HeaderLocation;

        use std::env;
        use std::io::Write;
        use std::fs::OpenOptions;
        use std::path::Path;

        #[derive(Debug)]
        struct SqliteTypeChooser;

        impl ParseCallbacks for SqliteTypeChooser {
            fn int_macro(&self, _name: &str, value: i64) -> Option<IntKind> {
                if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 {
                    Some(IntKind::I32)
                } else {
                    None
                }
            }
        }

        pub fn write_to_out_dir(header: HeaderLocation) {
            let header: String = header.into();
            let out_dir = env::var("OUT_DIR").unwrap();
            let mut output = Vec::new();
            bindgen::builder()
                .header(header.clone())
                .parse_callbacks(Box::new(SqliteTypeChooser))
                .generate()
                .expect(&format!("could not run bindgen on header {}", header))
                .write(Box::new(&mut output))
                .expect("could not write output of bindgen");
            let mut output = String::from_utf8(output).expect("bindgen output was not UTF-8?!");

            // rusqlite's functions feature ors in the SQLITE_DETERMINISTIC flag when it can. This flag
            // was added in SQLite 3.8.3, but oring it in in prior versions of SQLite is harmless. We
            // don't want to not build just because this flag is missing (e.g., if we're linking against
            // SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's output.
            if !output.contains("pub const SQLITE_DETERMINISTIC:") {
                output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n");
            }

            let path = Path::new(&out_dir).join("bindgen.rs");

            let mut file = OpenOptions::new()
                .write(true)
                .truncate(true)
                .create(true)
                .open(path.clone())
                .expect(&format!("Could not write to {:?}", path));

            file.write_all(output.as_bytes()).expect(&format!("Could not write to {:?}", path));
        }
    }
}
<|endoftext|>