<commit_before>#![feature(test)]
extern crate handlebars;
extern crate serde;
extern crate serde_json;
extern crate test;

use std::collections::BTreeMap;

use serde_json::value::Value as Json;
use handlebars::{Handlebars, Template, to_json};

static SOURCE: &'static str = "

    <title>{{year}}</title>

CSL {{year}}

    {{#each teams}}

      {{name}}: {{score}}

    {{/each}}

";

fn make_data() -> BTreeMap<String, Json> {
    let mut data = BTreeMap::new();

    data.insert("year".to_string(), to_json(&"2015".to_owned()));

    let mut teams = Vec::new();

    for v in vec![
        ("Jiangsu", 43u16),
        ("Beijing", 27u16),
        ("Guangzhou", 22u16),
        ("Shandong", 12u16),
    ].iter()
    {
        let (name, score) = *v;
        let mut t = BTreeMap::new();
        t.insert("name".to_string(), to_json(&name));
        t.insert("score".to_string(), to_json(&score));
        teams.push(t)
    }

    data.insert("teams".to_string(), to_json(&teams));
    data
}

#[bench]
fn parse_template(b: &mut test::Bencher) {
    b.iter(|| Template::compile(SOURCE).ok().unwrap());
}

#[bench]
fn render_template(b: &mut test::Bencher) {
    let mut handlebars = Handlebars::new();
    handlebars
        .register_template_string("table", SOURCE)
        .ok()
        .expect("Invalid template format");

    let data = make_data();
    b.iter(|| handlebars.render("table", &data).ok().unwrap())
}
<commit_msg>(test) added bench code for large each loop [#166]<commit_after>#![feature(test)]
extern crate handlebars;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

extern crate test;

use std::collections::BTreeMap;

use serde_json::value::Value as Json;
use handlebars::{Handlebars, Template, to_json};

static SOURCE: &'static str = "

    <title>{{year}}</title>

CSL {{year}}

    {{#each teams}}

      {{name}}: {{score}}

    {{/each}}

";

fn make_data() -> BTreeMap<String, Json> {
    let mut data = BTreeMap::new();

    data.insert("year".to_string(), to_json(&"2015".to_owned()));

    let mut teams = Vec::new();

    for v in vec![
        ("Jiangsu", 43u16),
        ("Beijing", 27u16),
        ("Guangzhou", 22u16),
        ("Shandong", 12u16),
    ].iter()
    {
        let (name, score) = *v;
        let mut t = BTreeMap::new();
        t.insert("name".to_string(), to_json(&name));
        t.insert("score".to_string(), to_json(&score));
        teams.push(t)
    }

    data.insert("teams".to_string(), to_json(&teams));
    data
}

#[bench]
fn parse_template(b: &mut test::Bencher) {
    b.iter(|| Template::compile(SOURCE).ok().unwrap());
}

#[bench]
fn render_template(b: &mut test::Bencher) {
    let mut handlebars = Handlebars::new();
    handlebars
        .register_template_string("table", SOURCE)
        .ok()
        .expect("Invalid template format");

    let data = make_data();
    b.iter(|| handlebars.render("table", &data).ok().unwrap())
}

#[derive(Serialize)]
struct DataWrapper {
    v: String,
}

#[derive(Serialize)]
struct RowWrapper {
    real: Vec<DataWrapper>,
    dummy: Vec<DataWrapper>,
}

#[bench]
fn large_loop_helper(b: &mut test::Bencher) {
    let mut handlebars = Handlebars::new();
    handlebars
        .register_template_string("test", "BEFORE\n{{#each real}}{{this.v}}{{/each}}AFTER")
        .ok()
        .expect("Invalid template format");

    let real: Vec<DataWrapper> = (1..1000)
        .into_iter()
        .map(|i| DataWrapper { v: format!("n={}", i) })
        .collect();
    let dummy: Vec<DataWrapper> = (1..1000)
        .into_iter()
        .map(|i| DataWrapper { v: format!("n={}", i) })
        .collect();
    let rows = RowWrapper { real, dummy };
    b.iter(|| handlebars.render("test", &rows).ok().unwrap());
}
<|endoftext|>


<commit_before>// Copyright 2017 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::sync::Arc;
use std::time::*;

use fail;
use futures::Future;
use kvproto::raft_serverpb::{PeerState, RegionLocalState};
use tikv::pd::PdClient;
use tikv::raftstore::store::keys;
use tikv::raftstore::store::Peekable;
use tikv::storage::CF_RAFT;

use raftstore::node::new_node_cluster;
use raftstore::transport_simulate::*;
use raftstore::util;
use raftstore::util::*;

/// Test if merge is rollback as expected.
#[test]
fn test_node_merge_rollback() {
    let _guard = ::setup();
    let mut cluster = new_node_cluster(0, 3);
    configure_for_merge(&mut cluster);
    let pd_client = Arc::clone(&cluster.pd_client);
    pd_client.disable_default_operator();

    cluster.run_conf_change();

    let region = pd_client.get_region(b"k1").unwrap();
    cluster.must_split(&region, b"k2");
    let left = pd_client.get_region(b"k1").unwrap();
    let right = pd_client.get_region(b"k2").unwrap();

    pd_client.must_add_peer(left.get_id(), new_peer(2, 2));
    pd_client.must_add_peer(right.get_id(), new_peer(2, 4));

    cluster.must_put(b"k1", b"v1");
    cluster.must_put(b"k3", b"v3");

    let region = pd_client.get_region(b"k1").unwrap();
    let target_region = pd_client.get_region(b"k3").unwrap();

    let schedule_merge_fp = "on_schedule_merge";
    fail::cfg(schedule_merge_fp, "return()").unwrap();

    // The call is finished when prepare_merge is applied.
    cluster.try_merge(region.get_id(), target_region.get_id());

    // Add a peer to trigger rollback.
    pd_client.must_add_peer(right.get_id(), new_peer(3, 5));
    cluster.must_put(b"k4", b"v4");
    util::must_get_equal(&cluster.get_engine(3), b"k4", b"v4");

    let mut region = pd_client.get_region(b"k1").unwrap();
    // After split and prepare_merge, version becomes 1 + 2 = 3;
    assert_eq!(region.get_region_epoch().get_version(), 3);
    // After ConfChange and prepare_merge, conf version becomes 1 + 2 = 3;
    assert_eq!(region.get_region_epoch().get_conf_ver(), 3);
    fail::remove(schedule_merge_fp);
    // Wait till rollback.
    cluster.must_put(b"k11", b"v11");

    // After rollback, version becomes 3 + 1 = 4;
    region.mut_region_epoch().set_version(4);
    for i in 1..3 {
        must_get_equal(&cluster.get_engine(i), b"k11", b"v11");
        let state_key = keys::region_state_key(region.get_id());
        let state: RegionLocalState = cluster
            .get_engine(i)
            .get_msg_cf(CF_RAFT, &state_key)
            .unwrap()
            .unwrap();
        assert_eq!(state.get_state(), PeerState::Normal);
        assert_eq!(*state.get_region(), region);
    }

    pd_client.must_remove_peer(right.get_id(), new_peer(3, 5));
    fail::cfg(schedule_merge_fp, "return()").unwrap();

    let target_region = pd_client.get_region(b"k1").unwrap();
    cluster.try_merge(region.get_id(), target_region.get_id());
    let mut region = pd_client.get_region(b"k1").unwrap();

    // Split to trigger rollback.
    cluster.must_split(&right, b"k3");
    fail::remove(schedule_merge_fp);
    // Wait till rollback.
    cluster.must_put(b"k12", b"v12");

    // After premerge and rollback, version becomes 4 + 2 = 6;
    region.mut_region_epoch().set_version(4);
    for i in 1..3 {
        must_get_equal(&cluster.get_engine(i), b"k12", b"v12");
        let state_key = keys::region_state_key(region.get_id());
        let state: RegionLocalState = cluster
            .get_engine(i)
            .get_msg_cf(CF_RAFT, &state_key)
            .unwrap()
            .unwrap();
        assert_eq!(state.get_state(), PeerState::Normal);
        assert_eq!(*state.get_region(), region);
    }
}

/// Test if merge is still working when restart a cluster during merge.
#[test]
fn test_node_merge_restart() {
    let _guard = ::setup();
    // ::util::init_log();
    let mut cluster = new_node_cluster(0, 3);
    configure_for_merge(&mut cluster);
    cluster.run();

    let pd_client = Arc::clone(&cluster.pd_client);
    let region = pd_client.get_region(b"k1").unwrap();
    cluster.must_split(&region, b"k2");
    let left = pd_client.get_region(b"k1").unwrap();
    let right = pd_client.get_region(b"k2").unwrap();

    cluster.must_put(b"k1", b"v1");
    cluster.must_put(b"k3", b"v3");

    let schedule_merge_fp = "on_schedule_merge";
    fail::cfg(schedule_merge_fp, "return()").unwrap();

    cluster.try_merge(left.get_id(), right.get_id());
    let leader = cluster.leader_of_region(left.get_id()).unwrap();

    cluster.shutdown();
    let engine = cluster.get_engine(leader.get_store_id());
    let state_key = keys::region_state_key(left.get_id());
    let state: RegionLocalState = engine.get_msg_cf(CF_RAFT, &state_key).unwrap().unwrap();
    assert_eq!(state.get_state(), PeerState::Merging, "{:?}", state);
    let state_key = keys::region_state_key(right.get_id());
    let state: RegionLocalState = engine.get_msg_cf(CF_RAFT, &state_key).unwrap().unwrap();
    assert_eq!(state.get_state(), PeerState::Normal, "{:?}", state);
    fail::remove(schedule_merge_fp);
    cluster.start();

    // Wait till merge is finished.
    let timer = Instant::now();
    loop {
        if pd_client
            .get_region_by_id(left.get_id())
            .wait()
            .unwrap()
            .is_none()
        {
            break;
        }

        if timer.elapsed() > Duration::from_secs(5) {
            panic!("region still not merged after 5 secs");
        }
        sleep_ms(10);
    }

    cluster.must_put(b"k4", b"v4");

    for i in 1..4 {
        util::must_get_equal(&cluster.get_engine(i), b"k4", b"v4");
        let state_key = keys::region_state_key(left.get_id());
        let state: RegionLocalState = cluster
            .get_engine(i)
            .get_msg_cf(CF_RAFT, &state_key)
            .unwrap()
            .unwrap();
        assert_eq!(state.get_state(), PeerState::Tombstone, "{:?}", state);
        let state_key = keys::region_state_key(right.get_id());
        let state: RegionLocalState = cluster
            .get_engine(i)
            .get_msg_cf(CF_RAFT, &state_key)
            .unwrap()
            .unwrap();
        assert_eq!(state.get_state(), PeerState::Normal, "{:?}", state);
        assert!(state.get_region().get_start_key().is_empty());
        assert!(state.get_region().get_end_key().is_empty());
    }

    // Now test if cluster works fine when it crash after merge is applied
    // but before notifying raftstore thread.
    let region = pd_client.get_region(b"k1").unwrap();
    let peer_on_store1 = find_peer(&region, 1).unwrap().to_owned();
    cluster.must_transfer_leader(region.get_id(), peer_on_store1);
    cluster.must_split(&region, b"k2");
    let left = pd_client.get_region(b"k1").unwrap();
    let right = pd_client.get_region(b"k2").unwrap();
    cluster.must_put(b"k11", b"v11");
    must_get_equal(&cluster.get_engine(3), b"k11", b"v11");
    let skip_destroy_fp = "raft_store_skip_destroy_peer";
    fail::cfg(skip_destroy_fp, "return()").unwrap();
    cluster.add_send_filter(IsolationFilterFactory::new(3));
    pd_client.must_merge(left.get_id(), right.get_id());
    let peer = find_peer(&right, 3).unwrap().to_owned();
    pd_client.must_remove_peer(right.get_id(), peer);
    cluster.shutdown();
    fail::remove(skip_destroy_fp);
    cluster.clear_send_filters();
    cluster.start();
    must_get_none(&cluster.get_engine(3), b"k1");
    must_get_none(&cluster.get_engine(3), b"k3");
}

/// Test if merging state will be removed after accepting a snapshot.
#[test]
fn test_node_merge_recover_snapshot() {
    let _guard = ::setup();
    let mut cluster = new_node_cluster(0, 3);
    configure_for_merge(&mut cluster);
    cluster.cfg.raft_store.raft_log_gc_threshold = 12;
    cluster.cfg.raft_store.raft_log_gc_count_limit = 12;
    let pd_client = Arc::clone(&cluster.pd_client);
    pd_client.disable_default_operator();

    cluster.run();

    let region = pd_client.get_region(b"k1").unwrap();
    cluster.must_split(&region, b"k2");
    let left = pd_client.get_region(b"k1").unwrap();

    cluster.must_put(b"k1", b"v1");
    cluster.must_put(b"k3", b"v3");

    let region = pd_client.get_region(b"k3").unwrap();
    let target_region = pd_client.get_region(b"k1").unwrap();

    let schedule_merge_fp = "on_schedule_merge";
    fail::cfg(schedule_merge_fp, "return()").unwrap();

    cluster.try_merge(region.get_id(), target_region.get_id());

    // Remove a peer to trigger rollback.
    pd_client.must_remove_peer(left.get_id(), left.get_peers()[0].to_owned());
    util::must_get_none(&cluster.get_engine(3), b"k4");

    let step_store_3_region_1 = "step_message_3_1";
    fail::cfg(step_store_3_region_1, "return()").unwrap();
    fail::remove(schedule_merge_fp);

    for i in 0..100 {
        cluster.must_put(format!("k4{}", i).as_bytes(), b"v4");
    }
    fail::remove(step_store_3_region_1);
    util::must_get_equal(&cluster.get_engine(3), b"k40", b"v4");
    cluster.must_transfer_leader(1, new_peer(3, 3));
    cluster.must_put(b"k40", b"v5");
}
<commit_msg>tests: transfer leader before merge (#3293)<commit_after>// Copyright 2017 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::sync::Arc;
use std::time::*;

use fail;
use futures::Future;
use kvproto::raft_serverpb::{PeerState, RegionLocalState};
use tikv::pd::PdClient;
use tikv::raftstore::store::keys;
use tikv::raftstore::store::Peekable;
use tikv::storage::CF_RAFT;

use raftstore::node::new_node_cluster;
use raftstore::transport_simulate::*;
use raftstore::util;
use raftstore::util::*;

/// Test if merge is rollback as expected.
#[test]
fn test_node_merge_rollback() {
    let _guard = ::setup();
    let mut cluster = new_node_cluster(0, 3);
    configure_for_merge(&mut cluster);
    let pd_client = Arc::clone(&cluster.pd_client);
    pd_client.disable_default_operator();

    cluster.run_conf_change();

    let region = pd_client.get_region(b"k1").unwrap();
    cluster.must_split(&region, b"k2");
    let left = pd_client.get_region(b"k1").unwrap();
    let right = pd_client.get_region(b"k2").unwrap();

    pd_client.must_add_peer(left.get_id(), new_peer(2, 2));
    pd_client.must_add_peer(right.get_id(), new_peer(2, 4));

    cluster.must_put(b"k1", b"v1");
    cluster.must_put(b"k3", b"v3");

    let region = pd_client.get_region(b"k1").unwrap();
    let target_region = pd_client.get_region(b"k3").unwrap();

    let schedule_merge_fp = "on_schedule_merge";
    fail::cfg(schedule_merge_fp, "return()").unwrap();

    // The call is finished when prepare_merge is applied.
    cluster.try_merge(region.get_id(), target_region.get_id());

    // Add a peer to trigger rollback.
    pd_client.must_add_peer(right.get_id(), new_peer(3, 5));
    cluster.must_put(b"k4", b"v4");
    util::must_get_equal(&cluster.get_engine(3), b"k4", b"v4");

    let mut region = pd_client.get_region(b"k1").unwrap();
    // After split and prepare_merge, version becomes 1 + 2 = 3;
    assert_eq!(region.get_region_epoch().get_version(), 3);
    // After ConfChange and prepare_merge, conf version becomes 1 + 2 = 3;
    assert_eq!(region.get_region_epoch().get_conf_ver(), 3);
    fail::remove(schedule_merge_fp);
    // Wait till rollback.
    cluster.must_put(b"k11", b"v11");

    // After rollback, version becomes 3 + 1 = 4;
    region.mut_region_epoch().set_version(4);
    for i in 1..3 {
        must_get_equal(&cluster.get_engine(i), b"k11", b"v11");
        let state_key = keys::region_state_key(region.get_id());
        let state: RegionLocalState = cluster
            .get_engine(i)
            .get_msg_cf(CF_RAFT, &state_key)
            .unwrap()
            .unwrap();
        assert_eq!(state.get_state(), PeerState::Normal);
        assert_eq!(*state.get_region(), region);
    }

    pd_client.must_remove_peer(right.get_id(), new_peer(3, 5));
    fail::cfg(schedule_merge_fp, "return()").unwrap();

    let target_region = pd_client.get_region(b"k1").unwrap();
    cluster.try_merge(region.get_id(), target_region.get_id());
    let mut region = pd_client.get_region(b"k1").unwrap();

    // Split to trigger rollback.
    cluster.must_split(&right, b"k3");
    fail::remove(schedule_merge_fp);
    // Wait till rollback.
    cluster.must_put(b"k12", b"v12");

    // After premerge and rollback, version becomes 4 + 2 = 6;
    region.mut_region_epoch().set_version(4);
    for i in 1..3 {
        must_get_equal(&cluster.get_engine(i), b"k12", b"v12");
        let state_key = keys::region_state_key(region.get_id());
        let state: RegionLocalState = cluster
            .get_engine(i)
            .get_msg_cf(CF_RAFT, &state_key)
            .unwrap()
            .unwrap();
        assert_eq!(state.get_state(), PeerState::Normal);
        assert_eq!(*state.get_region(), region);
    }
}

/// Test if merge is still working when restart a cluster during merge.
#[test]
fn test_node_merge_restart() {
    let _guard = ::setup();
    // ::util::init_log();
    let mut cluster = new_node_cluster(0, 3);
    configure_for_merge(&mut cluster);
    cluster.run();

    let pd_client = Arc::clone(&cluster.pd_client);
    let region = pd_client.get_region(b"k1").unwrap();
    cluster.must_split(&region, b"k2");
    let left = pd_client.get_region(b"k1").unwrap();
    let right = pd_client.get_region(b"k2").unwrap();

    cluster.must_put(b"k1", b"v1");
    cluster.must_put(b"k3", b"v3");

    let schedule_merge_fp = "on_schedule_merge";
    fail::cfg(schedule_merge_fp, "return()").unwrap();

    cluster.try_merge(left.get_id(), right.get_id());
    let leader = cluster.leader_of_region(left.get_id()).unwrap();

    cluster.shutdown();
    let engine = cluster.get_engine(leader.get_store_id());
    let state_key = keys::region_state_key(left.get_id());
    let state: RegionLocalState = engine.get_msg_cf(CF_RAFT, &state_key).unwrap().unwrap();
    assert_eq!(state.get_state(), PeerState::Merging, "{:?}", state);
    let state_key = keys::region_state_key(right.get_id());
    let state: RegionLocalState = engine.get_msg_cf(CF_RAFT, &state_key).unwrap().unwrap();
    assert_eq!(state.get_state(), PeerState::Normal, "{:?}", state);
    fail::remove(schedule_merge_fp);
    cluster.start();

    // Wait till merge is finished.
    let timer = Instant::now();
    loop {
        if pd_client
            .get_region_by_id(left.get_id())
            .wait()
            .unwrap()
            .is_none()
        {
            break;
        }

        if timer.elapsed() > Duration::from_secs(5) {
            panic!("region still not merged after 5 secs");
        }
        sleep_ms(10);
    }

    cluster.must_put(b"k4", b"v4");

    for i in 1..4 {
        util::must_get_equal(&cluster.get_engine(i), b"k4", b"v4");
        let state_key = keys::region_state_key(left.get_id());
        let state: RegionLocalState = cluster
            .get_engine(i)
            .get_msg_cf(CF_RAFT, &state_key)
            .unwrap()
            .unwrap();
        assert_eq!(state.get_state(), PeerState::Tombstone, "{:?}", state);
        let state_key = keys::region_state_key(right.get_id());
        let state: RegionLocalState = cluster
            .get_engine(i)
            .get_msg_cf(CF_RAFT, &state_key)
            .unwrap()
            .unwrap();
        assert_eq!(state.get_state(), PeerState::Normal, "{:?}", state);
        assert!(state.get_region().get_start_key().is_empty());
        assert!(state.get_region().get_end_key().is_empty());
    }

    // Now test if cluster works fine when it crash after merge is applied
    // but before notifying raftstore thread.
    let region = pd_client.get_region(b"k1").unwrap();
    let peer_on_store1 = find_peer(&region, 1).unwrap().to_owned();
    cluster.must_transfer_leader(region.get_id(), peer_on_store1);
    cluster.must_split(&region, b"k2");
    let left = pd_client.get_region(b"k1").unwrap();
    let right = pd_client.get_region(b"k2").unwrap();
    let peer_on_store1 = find_peer(&left, 1).unwrap().to_owned();
    cluster.must_transfer_leader(left.get_id(), peer_on_store1);
    cluster.must_put(b"k11", b"v11");
    must_get_equal(&cluster.get_engine(3), b"k11", b"v11");
    let skip_destroy_fp = "raft_store_skip_destroy_peer";
    fail::cfg(skip_destroy_fp, "return()").unwrap();
    cluster.add_send_filter(IsolationFilterFactory::new(3));
    pd_client.must_merge(left.get_id(), right.get_id());
    let peer = find_peer(&right, 3).unwrap().to_owned();
    pd_client.must_remove_peer(right.get_id(), peer);
    cluster.shutdown();
    fail::remove(skip_destroy_fp);
    cluster.clear_send_filters();
    cluster.start();
    must_get_none(&cluster.get_engine(3), b"k1");
    must_get_none(&cluster.get_engine(3), b"k3");
}

/// Test if merging state will be removed after accepting a snapshot.
#[test]
fn test_node_merge_recover_snapshot() {
    let _guard = ::setup();
    let mut cluster = new_node_cluster(0, 3);
    configure_for_merge(&mut cluster);
    cluster.cfg.raft_store.raft_log_gc_threshold = 12;
    cluster.cfg.raft_store.raft_log_gc_count_limit = 12;
    let pd_client = Arc::clone(&cluster.pd_client);
    pd_client.disable_default_operator();

    cluster.run();

    let region = pd_client.get_region(b"k1").unwrap();
    cluster.must_split(&region, b"k2");
    let left = pd_client.get_region(b"k1").unwrap();

    cluster.must_put(b"k1", b"v1");
    cluster.must_put(b"k3", b"v3");

    let region = pd_client.get_region(b"k3").unwrap();
    let target_region = pd_client.get_region(b"k1").unwrap();

    let schedule_merge_fp = "on_schedule_merge";
    fail::cfg(schedule_merge_fp, "return()").unwrap();

    cluster.try_merge(region.get_id(), target_region.get_id());

    // Remove a peer to trigger rollback.
    pd_client.must_remove_peer(left.get_id(), left.get_peers()[0].to_owned());
    util::must_get_none(&cluster.get_engine(3), b"k4");

    let step_store_3_region_1 = "step_message_3_1";
    fail::cfg(step_store_3_region_1, "return()").unwrap();
    fail::remove(schedule_merge_fp);

    for i in 0..100 {
        cluster.must_put(format!("k4{}", i).as_bytes(), b"v4");
    }
    fail::remove(step_store_3_region_1);
    util::must_get_equal(&cluster.get_engine(3), b"k40", b"v4");
    cluster.must_transfer_leader(1, new_peer(3, 3));
    cluster.must_put(b"k40", b"v5");
}
<|endoftext|>


<commit_before>use std::path::PathBuf;

use xshell::pushd;

use crate::{cmd, Metadata, Result};

const MSRV: &str = "1.45";

/// Task to run CI tests.
pub struct CiTask {
    /// Which version of Rust to test against.
    version: Option<String>,

    /// The root of the workspace.
    project_root: PathBuf,
}

impl CiTask {
    pub(crate) fn new(version: Option<String>) -> Result<Self> {
        let project_root = Metadata::load()?.workspace_root;
        Ok(Self { version, project_root })
    }

    pub(crate) fn run(self) -> Result<()> {
        let _p = pushd(&self.project_root)?;

        match self.version.as_deref() {
            Some("msrv") => self.build_msrv()?,
            Some("stable") => self.build_stable()?,
            Some("nightly") => self.build_nightly()?,
            Some(_) => return Err("Wrong Rust version specified.".into()),
            None => {
                self.build_msrv().and(self.build_stable()).and(self.build_nightly())?;
            }
        }

        Ok(())
    }

    fn build_msrv(&self) -> xshell::Result<()> {
        let _p = pushd("crates/ruma")?;
        cmd!("rustup run {MSRV} cargo build --features full").run()
    }

    fn build_stable(&self) -> xshell::Result<()> {
        // 1. Make sure everything compiles
        cmd!("rustup run stable cargo check --workspace --all-features").run()?;
        cmd!("rustup run stable cargo check -p ruma-client --no-default-features").run()?;
        cmd!("rustup run stable cargo check -p ruma-identifiers --no-default-features").run()?;

        // 2. Run tests
        cmd!("rustup run stable cargo test --workspace").run()
    }

    fn build_nightly(&self) -> xshell::Result<()> {
        let fmt_res = cmd!("rustup run nightly cargo fmt -- --check").run();
        let clippy_res = cmd!("rustup run nightly cargo ruma-clippy -D warnings").run();
        let sort_res = cmd!("rustup run nightly cargo sort --workspace --grouped --check").run();

        fmt_res.and(clippy_res).and(sort_res)
    }
}
<commit_msg>ci: Update msrv job<commit_after>use std::path::PathBuf;

use xshell::pushd;

use crate::{cmd, Metadata, Result};

const MSRV: &str = "1.45";

/// Task to run CI tests.
pub struct CiTask {
    /// Which version of Rust to test against.
    version: Option<String>,

    /// The root of the workspace.
    project_root: PathBuf,
}

impl CiTask {
    pub(crate) fn new(version: Option<String>) -> Result<Self> {
        let project_root = Metadata::load()?.workspace_root;
        Ok(Self { version, project_root })
    }

    pub(crate) fn run(self) -> Result<()> {
        let _p = pushd(&self.project_root)?;

        match self.version.as_deref() {
            Some("msrv") => self.build_msrv()?,
            Some("stable") => self.build_stable()?,
            Some("nightly") => self.build_nightly()?,
            Some(_) => return Err("Wrong Rust version specified.".into()),
            None => {
                self.build_msrv().and(self.build_stable()).and(self.build_nightly())?;
            }
        }

        Ok(())
    }

    fn build_msrv(&self) -> xshell::Result<()> {
        // Check all crates with all features except
        // * ruma (all-features would include the other crates)
        // * ruma-client (due to transitive ring dependency which only supports latest stable)
        // * ruma-signatures (for the same reason)
        // * ruma-state-res (initially exempt from MSRV policy)
        // * xtask (no real reason to enforce an MSRV for it)
        cmd!(
            "rustup run {MSRV} cargo check --workspace --all-features
                --exclude ruma
                --exclude ruma-client
                --exclude ruma-signatures
                --exclude ruma-state-res
                --exclude xtask"
        )
        .run()?;

        // Check ruma crate with default features
        cmd!("rustup run {MSRV} cargo check -p ruma").run()
    }

    fn build_stable(&self) -> xshell::Result<()> {
        // 1. Make sure everything compiles
        cmd!("rustup run stable cargo check --workspace --all-features").run()?;
        cmd!("rustup run stable cargo check -p ruma-client --no-default-features").run()?;
        cmd!("rustup run stable cargo check -p ruma-identifiers --no-default-features").run()?;

        // 2. Run tests
        cmd!("rustup run stable cargo test --workspace").run()
    }

    fn build_nightly(&self) -> xshell::Result<()> {
        let fmt_res = cmd!("rustup run nightly cargo fmt -- --check").run();
        let clippy_res = cmd!("rustup run nightly cargo ruma-clippy -D warnings").run();
        let sort_res = cmd!("rustup run nightly cargo sort --workspace --grouped --check").run();

        fmt_res.and(clippy_res).and(sort_res)
    }
}
<|endoftext|>


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

use std::rc::Rc;
use rustc_data_structures::bitvec::BitMatrix;
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc::mir::{BasicBlock, Location, Mir};
use rustc::ty::RegionVid;

/// Maps between the various kinds of elements of a region value to
/// the internal indices that w use.
pub(super) struct RegionValueElements {
    /// For each basic block, how many points are contained within?
    statements_before_block: IndexVec<BasicBlock, usize>,
    num_points: usize,
    num_universal_regions: usize,
}

impl RegionValueElements {
    pub(super) fn new(mir: &Mir<'_>, num_universal_regions: usize) -> Self {
        let mut num_points = 0;
        let statements_before_block =
            mir.basic_blocks()
               .iter()
               .map(|block_data| {
                   let v = num_points;
                   num_points += block_data.statements.len() + 1;
                   v
               })
               .collect();

        debug!("RegionValueElements(num_universal_regions={:?})", num_universal_regions);
        debug!("RegionValueElements: statements_before_block={:#?}", statements_before_block);
        debug!("RegionValueElements: num_points={:#?}", num_points);

        Self {
            statements_before_block,
            num_universal_regions,
            num_points,
        }
    }

    /// Total number of element indices that exist.
    pub(super) fn num_elements(&self) -> usize {
        self.num_points + self.num_universal_regions
    }

    /// Converts an element of a region value into a `RegionElementIndex`.
    pub(super) fn index<T: ToElementIndex>(&self, elem: T) -> RegionElementIndex {
        elem.to_element_index(self)
    }

    /// Iterates over the `RegionElementIndex` for all points in the CFG.
    pub(super) fn all_point_indices<'a>(&'a self) -> impl Iterator<Item = RegionElementIndex> + 'a {
        (0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions))
    }

    /// Iterates over the `RegionElementIndex` for all points in the CFG.
    pub(super) fn all_universal_region_indices(&self) -> impl Iterator<Item = RegionElementIndex> {
        (0..self.num_universal_regions).map(move |i| RegionElementIndex::new(i))
    }

    /// Converts a particular `RegionElementIndex` to the `RegionElement` it represents.
    pub(super) fn to_element(&self, i: RegionElementIndex) -> RegionElement {
        debug!("to_element(i={:?})", i);

        if let Some(r) = self.to_universal_region(i) {
            RegionElement::UniversalRegion(r)
        } else {
            let point_index = i.index() - self.num_universal_regions;

            // Find the basic block. We have a vector with the
            // starting index of the statement in each block. Imagine
            // we have statement #22, and we have a vector like:
            //
            // [0, 10, 20]
            //
            // In that case, this represents point_index 2 of
            // basic block BB2. We know this because BB0 accounts for
            // 0..10, BB1 accounts for 11..20, and BB2 accounts for
            // 20...
            //
            // To compute this, we could do a binary search, but
            // because I am lazy we instead iterate through to find
            // the last point where the "first index" (0, 10, or 20)
            // was less than the statement index (22). In our case, this will
            // be (BB2, 20).
            //
            // Nit: we could do a binary search here but I'm too lazy.
            let (block, &first_index) =
                self.statements_before_block
                    .iter_enumerated()
                    .filter(|(_, first_index)| **first_index <= point_index)
                    .last()
                    .unwrap();

            RegionElement::Location(Location {
                block,
                statement_index: point_index - first_index,
            })
        }
    }

    /// Converts a particular `RegionElementIndex` to a universal
    /// region, if that is what it represents. Returns `None`
    /// otherwise.
    pub(super) fn to_universal_region(&self, i: RegionElementIndex) -> Option<RegionVid> {
        if i.index() < self.num_universal_regions {
            Some(RegionVid::new(i.index()))
        } else {
            None
        }
    }
}

/// A newtype for the integers that represent one of the possible
/// elements in a region. These are the rows in the `BitMatrix` that
/// is used to store the values of all regions. They have the following
/// convention:
///
/// - The first N indices represent free regions (where N = universal_regions.len()).
/// - The remainder represent the points in the CFG (see `point_indices` map).
///
/// You can convert a `RegionElementIndex` into a `RegionElement`
/// using the `to_region_elem` method.
newtype_index!(RegionElementIndex { DEBUG_FORMAT = "RegionElementIndex({})" });

/// An individual element in a region value -- the value of a
/// particular region variable consists of a set of these elements.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(super) enum RegionElement {
    /// A point in the control-flow graph.
    Location(Location),

    /// An in-scope, universally quantified region (e.g., a liftime parameter).
    UniversalRegion(RegionVid),
}

pub(super) trait ToElementIndex {
    fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex;
}

impl ToElementIndex for Location {
    fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex {
        let Location { block, statement_index } = self;
        let start_index = elements.statements_before_block[block];
        RegionElementIndex::new(elements.num_universal_regions + start_index + statement_index)
    }
}

impl ToElementIndex for RegionVid {
    fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex {
        assert!(self.index() < elements.num_universal_regions);
        RegionElementIndex::new(self.index())
    }
}

impl ToElementIndex for RegionElementIndex {
    fn to_element_index(self, _elements: &RegionValueElements) -> RegionElementIndex {
        self
    }
}

/// Stores the values for a set of regions. These are stored in a
/// compact `BitMatrix` representation, with one row per region
/// variable. The columns consist of either universal regions or
/// points in the CFG.
#[derive(Clone)]
pub(super) struct RegionValues {
    elements: Rc<RegionValueElements>,
    matrix: BitMatrix,
}

impl RegionValues {
    pub(super) fn new(elements: &Rc<RegionValueElements>, num_region_variables: usize) -> Self {
        assert!(
            elements.num_universal_regions <= num_region_variables,
            "universal regions are a subset of the region variables"
        );

        Self {
            elements: elements.clone(),
            matrix: BitMatrix::new(num_region_variables, elements.num_elements()),
        }
    }

    /// Adds the given element to the value for the given region. Returns true if
    /// the element is newly added (i.e., was not already present).
    pub(super) fn add<E: ToElementIndex>(&mut self, r: RegionVid, elem: E) -> bool {
        let i = self.elements.index(elem);
        if self.matrix.add(r.index(), i.index()) {
            debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i));
            true
        } else {
            false
        }
    }

    /// Adds all the universal regions outlived by `from_region` to
    /// `to_region`.
    pub(super) fn add_universal_regions_outlived_by(
        &mut self,
        from_region: RegionVid,
        to_region: RegionVid,
    ) -> bool {
        // FIXME. We could optimize this by improving
        // `BitMatrix::merge` so it does not always merge an entire
        // row.
        debug!(
            "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
            from_region,
            to_region
        );
        let mut changed = false;
        for elem in self.elements.all_universal_region_indices() {
            if self.contains(from_region, elem) {
                changed |= self.add(to_region, elem);
            }
        }
        changed
    }

    /// True if the region `r` contains the given element.
    pub(super) fn contains<E: ToElementIndex>(&self, r: RegionVid, elem: E) -> bool {
        let i = self.elements.index(elem);
        self.matrix.contains(r.index(), i.index())
    }

    /// Iterate over the value of the region `r`, yielding up element
    /// indices. You may prefer `universal_regions_outlived_by` or
    /// `elements_contained_in`.
    pub(super) fn element_indices_contained_in<'a>(
        &'a self,
        r: RegionVid,
    ) -> impl Iterator<Item = RegionElementIndex> + 'a {
        self.matrix
            .iter(r.index())
            .map(move |i| RegionElementIndex::new(i))
    }

    /// Returns just the universal regions that are contained in a given region's value.
    pub(super) fn universal_regions_outlived_by<'a>(
        &'a self,
        r: RegionVid,
    ) -> impl Iterator<Item = RegionVid> + 'a {
        self.element_indices_contained_in(r)
            .map(move |i| self.elements.to_universal_region(i))
            .take_while(move |v| v.is_some()) // universal regions are a prefix
            .map(move |v| v.unwrap())
    }

    /// Returns all the elements contained in a given region's value.
    pub(super) fn elements_contained_in<'a>(
        &'a self,
        r: RegionVid,
    ) -> impl Iterator<Item = RegionElement> + 'a {
        self.element_indices_contained_in(r)
            .map(move |r| self.elements.to_element(r))
    }

    /// Returns a "pretty" string value of the region. Meant for debugging.
    pub(super) fn region_value_str(&self, r: RegionVid) -> String {
        let mut result = String::new();
        result.push_str("{");

        // Set to Some(l1, l2) when we have observed all the locations
        // from l1..=l2 (inclusive) but not yet printed them. This
        // gets extended if we then see l3 where l3 is the successor
        // to l2.
        let mut open_location: Option<(Location, Location)> = None;

        let mut sep = "";
        let mut push_sep = |s: &mut String| {
            s.push_str(sep);
            sep = ", ";
        };

        for element in self.elements_contained_in(r) {
            match element {
                RegionElement::Location(l) => {
                    if let Some((location1, location2)) = open_location {
                        if location2.block == l.block
                            && location2.statement_index == l.statement_index - 1
                        {
                            open_location = Some((location1, l));
                            continue;
                        }

                        push_sep(&mut result);
                        Self::push_location_range(&mut result, location1, location2);
                    }

                    open_location = Some((l, l));
                }

                RegionElement::UniversalRegion(fr) => {
                    if let Some((location1, location2)) = open_location {
                        push_sep(&mut result);
                        Self::push_location_range(&mut result, location1, location2);
                        open_location = None;
                    }

                    push_sep(&mut result);
                    result.push_str(&format!("{:?}", fr));
                }
            }
        }

        if let Some((location1, location2)) = open_location {
            push_sep(&mut result);
            Self::push_location_range(&mut result, location1, location2);
        }

        result.push_str("}");

        result
    }

    fn push_location_range(str: &mut String, location1: Location, location2: Location) {
        if location1 == location2 {
            str.push_str(&format!("{:?}", location1));
        } else {
            assert_eq!(location1.block, location2.block);
            str.push_str(&format!(
                "{:?}[{}..={}]",
                location1.block,
                location1.statement_index,
                location2.statement_index
            ));
        }
    }
}
<commit_msg>region_infer/values.rs: rustfmt<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::rc::Rc;
use rustc_data_structures::bitvec::BitMatrix;
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc::mir::{BasicBlock, Location, Mir};
use rustc::ty::RegionVid;

/// Maps between the various kinds of elements of a region value to
/// the internal indices that w use.
pub(super) struct RegionValueElements {
    /// For each basic block, how many points are contained within?
    statements_before_block: IndexVec<BasicBlock, usize>,
    num_points: usize,
    num_universal_regions: usize,
}

impl RegionValueElements {
    pub(super) fn new(mir: &Mir<'_>, num_universal_regions: usize) -> Self {
        let mut num_points = 0;
        let statements_before_block = mir.basic_blocks()
            .iter()
            .map(|block_data| {
                let v = num_points;
                num_points += block_data.statements.len() + 1;
                v
            })
            .collect();

        debug!(
            "RegionValueElements(num_universal_regions={:?})",
            num_universal_regions
        );
        debug!(
            "RegionValueElements: statements_before_block={:#?}",
            statements_before_block
        );
        debug!("RegionValueElements: num_points={:#?}", num_points);

        Self {
            statements_before_block,
            num_universal_regions,
            num_points,
        }
    }

    /// Total number of element indices that exist.
    pub(super) fn num_elements(&self) -> usize {
        self.num_points + self.num_universal_regions
    }

    /// Converts an element of a region value into a `RegionElementIndex`.
    pub(super) fn index<T: ToElementIndex>(&self, elem: T) -> RegionElementIndex {
        elem.to_element_index(self)
    }

    /// Iterates over the `RegionElementIndex` for all points in the CFG.
    pub(super) fn all_point_indices<'a>(&'a self) -> impl Iterator<Item = RegionElementIndex> + 'a {
        (0..self.num_points).map(move |i| {
            RegionElementIndex::new(i + self.num_universal_regions)
        })
    }

    /// Iterates over the `RegionElementIndex` for all points in the CFG.
    pub(super) fn all_universal_region_indices(&self) -> impl Iterator<Item = RegionElementIndex> {
        (0..self.num_universal_regions).map(move |i| RegionElementIndex::new(i))
    }

    /// Converts a particular `RegionElementIndex` to the `RegionElement` it represents.
    pub(super) fn to_element(&self, i: RegionElementIndex) -> RegionElement {
        debug!("to_element(i={:?})", i);

        if let Some(r) = self.to_universal_region(i) {
            RegionElement::UniversalRegion(r)
        } else {
            let point_index = i.index() - self.num_universal_regions;

            // Find the basic block. We have a vector with the
            // starting index of the statement in each block. Imagine
            // we have statement #22, and we have a vector like:
            //
            // [0, 10, 20]
            //
            // In that case, this represents point_index 2 of
            // basic block BB2. We know this because BB0 accounts for
            // 0..10, BB1 accounts for 11..20, and BB2 accounts for
            // 20...
            //
            // To compute this, we could do a binary search, but
            // because I am lazy we instead iterate through to find
            // the last point where the "first index" (0, 10, or 20)
            // was less than the statement index (22). In our case, this will
            // be (BB2, 20).
            //
            // Nit: we could do a binary search here but I'm too lazy.
            let (block, &first_index) = self.statements_before_block
                .iter_enumerated()
                .filter(|(_, first_index)| **first_index <= point_index)
                .last()
                .unwrap();

            RegionElement::Location(Location {
                block,
                statement_index: point_index - first_index,
            })
        }
    }

    /// Converts a particular `RegionElementIndex` to a universal
    /// region, if that is what it represents. Returns `None`
    /// otherwise.
    pub(super) fn to_universal_region(&self, i: RegionElementIndex) -> Option<RegionVid> {
        if i.index() < self.num_universal_regions {
            Some(RegionVid::new(i.index()))
        } else {
            None
        }
    }
}

/// A newtype for the integers that represent one of the possible
/// elements in a region. These are the rows in the `BitMatrix` that
/// is used to store the values of all regions. They have the following
/// convention:
///
/// - The first N indices represent free regions (where N = universal_regions.len()).
/// - The remainder represent the points in the CFG (see `point_indices` map).
///
/// You can convert a `RegionElementIndex` into a `RegionElement`
/// using the `to_region_elem` method.
newtype_index!(RegionElementIndex { DEBUG_FORMAT = "RegionElementIndex({})" });

/// An individual element in a region value -- the value of a
/// particular region variable consists of a set of these elements.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(super) enum RegionElement {
    /// A point in the control-flow graph.
    Location(Location),

    /// An in-scope, universally quantified region (e.g., a liftime parameter).
    UniversalRegion(RegionVid),
}

pub(super) trait ToElementIndex {
    fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex;
}

impl ToElementIndex for Location {
    fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex {
        let Location {
            block,
            statement_index,
        } = self;
        let start_index = elements.statements_before_block[block];
        RegionElementIndex::new(elements.num_universal_regions + start_index + statement_index)
    }
}

impl ToElementIndex for RegionVid {
    fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex {
        assert!(self.index() < elements.num_universal_regions);
        RegionElementIndex::new(self.index())
    }
}

impl ToElementIndex for RegionElementIndex {
    fn to_element_index(self, _elements: &RegionValueElements) -> RegionElementIndex {
        self
    }
}

/// Stores the values for a set of regions. These are stored in a
/// compact `BitMatrix` representation, with one row per region
/// variable. The columns consist of either universal regions or
/// points in the CFG.
#[derive(Clone)]
pub(super) struct RegionValues {
    elements: Rc<RegionValueElements>,
    matrix: BitMatrix,
}

impl RegionValues {
    pub(super) fn new(elements: &Rc<RegionValueElements>, num_region_variables: usize) -> Self {
        assert!(
            elements.num_universal_regions <= num_region_variables,
            "universal regions are a subset of the region variables"
        );

        Self {
            elements: elements.clone(),
            matrix: BitMatrix::new(num_region_variables, elements.num_elements()),
        }
    }

    /// Adds the given element to the value for the given region. Returns true if
    /// the element is newly added (i.e., was not already present).
    pub(super) fn add<E: ToElementIndex>(&mut self, r: RegionVid, elem: E) -> bool {
        let i = self.elements.index(elem);
        if self.matrix.add(r.index(), i.index()) {
            debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i));
            true
        } else {
            false
        }
    }

    /// Adds all the universal regions outlived by `from_region` to
    /// `to_region`.
    pub(super) fn add_universal_regions_outlived_by(
        &mut self,
        from_region: RegionVid,
        to_region: RegionVid,
    ) -> bool {
        // FIXME. We could optimize this by improving
        // `BitMatrix::merge` so it does not always merge an entire
        // row.
        debug!(
            "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
            from_region,
            to_region
        );
        let mut changed = false;
        for elem in self.elements.all_universal_region_indices() {
            if self.contains(from_region, elem) {
                changed |= self.add(to_region, elem);
            }
        }
        changed
    }

    /// True if the region `r` contains the given element.
    pub(super) fn contains<E: ToElementIndex>(&self, r: RegionVid, elem: E) -> bool {
        let i = self.elements.index(elem);
        self.matrix.contains(r.index(), i.index())
    }

    /// Iterate over the value of the region `r`, yielding up element
    /// indices. You may prefer `universal_regions_outlived_by` or
    /// `elements_contained_in`.
    pub(super) fn element_indices_contained_in<'a>(
        &'a self,
        r: RegionVid,
    ) -> impl Iterator<Item = RegionElementIndex> + 'a {
        self.matrix
            .iter(r.index())
            .map(move |i| RegionElementIndex::new(i))
    }

    /// Returns just the universal regions that are contained in a given region's value.
    pub(super) fn universal_regions_outlived_by<'a>(
        &'a self,
        r: RegionVid,
    ) -> impl Iterator<Item = RegionVid> + 'a {
        self.element_indices_contained_in(r)
            .map(move |i| self.elements.to_universal_region(i))
            .take_while(move |v| v.is_some()) // universal regions are a prefix
            .map(move |v| v.unwrap())
    }

    /// Returns all the elements contained in a given region's value.
    pub(super) fn elements_contained_in<'a>(
        &'a self,
        r: RegionVid,
    ) -> impl Iterator<Item = RegionElement> + 'a {
        self.element_indices_contained_in(r)
            .map(move |r| self.elements.to_element(r))
    }

    /// Returns a "pretty" string value of the region. Meant for debugging.
    pub(super) fn region_value_str(&self, r: RegionVid) -> String {
        let mut result = String::new();
        result.push_str("{");

        // Set to Some(l1, l2) when we have observed all the locations
        // from l1..=l2 (inclusive) but not yet printed them. This
        // gets extended if we then see l3 where l3 is the successor
        // to l2.
        let mut open_location: Option<(Location, Location)> = None;

        let mut sep = "";
        let mut push_sep = |s: &mut String| {
            s.push_str(sep);
            sep = ", ";
        };

        for element in self.elements_contained_in(r) {
            match element {
                RegionElement::Location(l) => {
                    if let Some((location1, location2)) = open_location {
                        if location2.block == l.block
                            && location2.statement_index == l.statement_index - 1
                        {
                            open_location = Some((location1, l));
                            continue;
                        }

                        push_sep(&mut result);
                        Self::push_location_range(&mut result, location1, location2);
                    }

                    open_location = Some((l, l));
                }

                RegionElement::UniversalRegion(fr) => {
                    if let Some((location1, location2)) = open_location {
                        push_sep(&mut result);
                        Self::push_location_range(&mut result, location1, location2);
                        open_location = None;
                    }

                    push_sep(&mut result);
                    result.push_str(&format!("{:?}", fr));
                }
            }
        }

        if let Some((location1, location2)) = open_location {
            push_sep(&mut result);
            Self::push_location_range(&mut result, location1, location2);
        }

        result.push_str("}");

        result
    }

    fn push_location_range(str: &mut String, location1: Location, location2: Location) {
        if location1 == location2 {
            str.push_str(&format!("{:?}", location1));
        } else {
            assert_eq!(location1.block, location2.block);
            str.push_str(&format!(
                "{:?}[{}..={}]",
                location1.block,
                location1.statement_index,
                location2.statement_index
            ));
        }
    }
}
<|endoftext|>


<commit_before>pub trait XmlStr {
    fn end_of_attribute(&self, quote: &str) -> Option<uint>;
    fn end_of_literal(&self, expected: &str) -> Option<uint>;
    fn end_of_version_num(&self) -> Option<uint>;
    fn end_of_char_data(&self) -> Option<uint>;
    fn end_of_cdata(&self) -> Option<uint>;
    fn end_of_decimal_chars(&self) -> Option<uint>;
    fn end_of_hex_chars(&self) -> Option<uint>;
    fn end_of_comment(&self) -> Option<uint>;
    fn end_of_pi_value(&self) -> Option<uint>;
    fn end_of_start_rest(&self, is_first: |char| -> bool, is_rest: |char| -> bool) -> Option<uint>;
    fn end_of_name(&self) -> Option<uint>;
    fn end_of_space(&self) -> Option<uint>;
    fn end_of_start_tag(&self) -> Option<uint>;
}

impl<'a> XmlStr for &'a str {
    fn end_of_attribute(&self, quote: &str) -> Option<uint> {
        if self.len() == 0 ||
           self.starts_with("&") ||
           self.starts_with("<") ||
           self.starts_with(quote)
        {
            return None;
        }

        let (quote_char, _) = quote.slice_shift_char();
        let quote_char = quote_char.expect("Cant have null quote");

        let mut positions = self.char_indices().skip_while(|&(_, c)| c != '&' && c != '<' && c != quote_char);

        match positions.next() {
            Some((offset, _)) => Some(offset),
            None => Some(self.len())
        }
    }

    fn end_of_literal(&self, expected: &str) -> Option<uint> {
        if self.starts_with(expected) {
            Some(expected.len())
        } else {
            None
        }
    }

    fn end_of_version_num(&self) -> Option<uint> {
        if self.starts_with("1.") {
            let mut positions = self.char_indices().peekable();
            positions.next();
            positions.next();

            // Need at least one character
            match positions.peek() {
                Some(&(_, c)) if c.is_decimal_char() => {},
                _ => return None,
            };

            let mut positions = positions.skip_while(|&(_, c)| c.is_decimal_char());
            match positions.next() {
                Some((offset, _)) => Some(offset),
                None => Some(self.len()),
            }
        } else {
            None
        }
    }

    fn end_of_char_data(&self) -> Option<uint> {
        if self.starts_with("<") ||
           self.starts_with("&") ||
           self.starts_with("]]>")
        {
            return None
        }

        // Using a hex literal because emacs' rust-mode doesn't
        // understand ] in a char literal. :-(
        let mut positions = self.char_indices().skip_while(|&(_, c)| c != '<' && c != '&' && c != '\x5d');

        loop {
            match positions.next() {
                None => return Some(self.len()),
                Some((offset, c)) if c == '<' || c == '&' => return Some(offset),
                Some((offset, _)) => {
                    let tail = self.slice_from(offset);
                    if tail.starts_with("]]>") {
                        return Some(offset)
                    } else {
                        // False alarm, resume scanning
                        continue;
                    }
                },
            }
        }
    }

    fn end_of_cdata(&self) -> Option<uint> {
        match self.find_str("]]>") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_decimal_chars(&self) -> Option<uint> {
        self.end_of_start_rest(|c| c.is_decimal_char(),
                               |c| c.is_decimal_char())
    }

    fn end_of_hex_chars(&self) -> Option<uint> {
        self.end_of_start_rest(|c| c.is_hex_char(),
                               |c| c.is_hex_char())
    }

    fn end_of_comment(&self) -> Option<uint> {
        // This deliberately does not include the >. -- is not allowed
        // in a comment, so we can just test the end if it matches the
        // complete close delimiter.
        match self.find_str("--") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_pi_value(&self) -> Option<uint> {
        match self.find_str("?>") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_start_rest(&self,
                         is_first: |char| -> bool,
                         is_rest: |char| -> bool)
                         -> Option<uint>
    {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if is_first(c) => (),
            Some((_, _)) => return None,
            None => return None,
        };

        let mut positions = positions.skip_while(|&(_, c)| is_rest(c));
        match positions.next() {
            Some((offset, _)) => Some(offset),
            None => Some(self.len()),
        }
    }

    fn end_of_name(&self) -> Option<uint> {
        self.end_of_start_rest(|c| c.is_name_start_char(), |c| c.is_name_char())
    }

    fn end_of_space(&self) -> Option<uint> {
        self.end_of_start_rest(|c| c.is_space_char(), |c| c.is_space_char())
    }

    fn end_of_start_tag(&self) -> Option<uint> {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if '<' == c => (),
            _ => return None,
        };

        match positions.next() {
            Some((offset, c)) =>
                match c {
                    '?' | '!' | '/' => None,
                    _ => Some(offset),
                },
            None => Some(self.len()),
        }
    }
}

trait XmlChar {
    fn is_name_start_char(&self) -> bool;
    fn is_name_char(&self) -> bool;
    fn is_space_char(&self) -> bool;
    fn is_decimal_char(&self) -> bool;
    fn is_hex_char(&self) -> bool;
}

impl XmlChar for char {
    fn is_name_start_char(&self) -> bool {
        match *self {
            ':'                        |
            'A'..'Z'                   |
            '_'                        |
            'a'..'z'                   |
            '\U000000C0'..'\U000000D6' |
            '\U000000D8'..'\U000000F6' |
            '\U000000F8'..'\U000002FF' |
            '\U00000370'..'\U0000037D' |
            '\U0000037F'..'\U00001FFF' |
            '\U0000200C'..'\U0000200D' |
            '\U00002070'..'\U0000218F' |
            '\U00002C00'..'\U00002FEF' |
            '\U00003001'..'\U0000D7FF' |
            '\U0000F900'..'\U0000FDCF' |
            '\U0000FDF0'..'\U0000FFFD' |
            '\U00010000'..'\U000EFFFF' => true,
            _ => false,
        }
    }

    fn is_name_char(&self) -> bool {
        if self.is_name_start_char() { return true; }
        match *self {
            '-'                |
            '.'                |
            '0'..'9'           |
            '\u00B7'           |
            '\u0300'..'\u036F' |
            '\u203F'..'\u2040' => true,
            _ => false
        }
    }

    fn is_space_char(&self) -> bool {
        match *self {
            '\x20' |
            '\x09' |
            '\x0D' |
            '\x0A' => true,
            _ => false,
        }
    }

    fn is_decimal_char(&self) -> bool {
        match *self {
            '0'..'9' => true,
            _ => false,
        }
    }

    fn is_hex_char(&self) -> bool {
        match *self {
            '0'..'9' |
            'a'..'f' |
            'A'..'F' => true,
            _ => false,
        }
    }
}

#[cfg(test)]
mod test {

use super::XmlStr;

#[test]
fn end_of_char_data_leading_ampersand() {
    assert_eq!("&".end_of_char_data(), None);
}

#[test]
fn end_of_char_data_leading_less_than() {
    assert_eq!("<".end_of_char_data(), None);
}

#[test]
fn end_of_char_data_leading_cdata_end() {
    assert_eq!("]]>".end_of_char_data(), None);
}

#[test]
fn end_of_char_data_until_ampersand() {
    assert_eq!("hello&world".end_of_char_data(), Some("hello".len()));
}

#[test]
fn end_of_char_data_until_less_than() {
    assert_eq!("hello<world".end_of_char_data(), Some("hello".len()));
}

#[test]
fn end_of_char_data_until_cdata_end() {
    assert_eq!("hello]]>world".end_of_char_data(), Some("hello".len()));
}

#[test]
fn end_of_char_data_includes_right_square() {
    assert_eq!("hello]world".end_of_char_data(), Some("hello]world".len()));
}

}
<commit_msg>Update range syntax from Rust update<commit_after>pub trait XmlStr {
    fn end_of_attribute(&self, quote: &str) -> Option<uint>;
    fn end_of_literal(&self, expected: &str) -> Option<uint>;
    fn end_of_version_num(&self) -> Option<uint>;
    fn end_of_char_data(&self) -> Option<uint>;
    fn end_of_cdata(&self) -> Option<uint>;
    fn end_of_decimal_chars(&self) -> Option<uint>;
    fn end_of_hex_chars(&self) -> Option<uint>;
    fn end_of_comment(&self) -> Option<uint>;
    fn end_of_pi_value(&self) -> Option<uint>;
    fn end_of_start_rest(&self, is_first: |char| -> bool, is_rest: |char| -> bool) -> Option<uint>;
    fn end_of_name(&self) -> Option<uint>;
    fn end_of_space(&self) -> Option<uint>;
    fn end_of_start_tag(&self) -> Option<uint>;
}

impl<'a> XmlStr for &'a str {
    fn end_of_attribute(&self, quote: &str) -> Option<uint> {
        if self.len() == 0 ||
           self.starts_with("&") ||
           self.starts_with("<") ||
           self.starts_with(quote)
        {
            return None;
        }

        let (quote_char, _) = quote.slice_shift_char();
        let quote_char = quote_char.expect("Cant have null quote");

        let mut positions = self.char_indices().skip_while(|&(_, c)| c != '&' && c != '<' && c != quote_char);

        match positions.next() {
            Some((offset, _)) => Some(offset),
            None => Some(self.len())
        }
    }

    fn end_of_literal(&self, expected: &str) -> Option<uint> {
        if self.starts_with(expected) {
            Some(expected.len())
        } else {
            None
        }
    }

    fn end_of_version_num(&self) -> Option<uint> {
        if self.starts_with("1.") {
            let mut positions = self.char_indices().peekable();
            positions.next();
            positions.next();

            // Need at least one character
            match positions.peek() {
                Some(&(_, c)) if c.is_decimal_char() => {},
                _ => return None,
            };

            let mut positions = positions.skip_while(|&(_, c)| c.is_decimal_char());
            match positions.next() {
                Some((offset, _)) => Some(offset),
                None => Some(self.len()),
            }
        } else {
            None
        }
    }

    fn end_of_char_data(&self) -> Option<uint> {
        if self.starts_with("<") ||
           self.starts_with("&") ||
           self.starts_with("]]>")
        {
            return None
        }

        // Using a hex literal because emacs' rust-mode doesn't
        // understand ] in a char literal. :-(
        let mut positions = self.char_indices().skip_while(|&(_, c)| c != '<' && c != '&' && c != '\x5d');

        loop {
            match positions.next() {
                None => return Some(self.len()),
                Some((offset, c)) if c == '<' || c == '&' => return Some(offset),
                Some((offset, _)) => {
                    let tail = self.slice_from(offset);
                    if tail.starts_with("]]>") {
                        return Some(offset)
                    } else {
                        // False alarm, resume scanning
                        continue;
                    }
                },
            }
        }
    }

    fn end_of_cdata(&self) -> Option<uint> {
        match self.find_str("]]>") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_decimal_chars(&self) -> Option<uint> {
        self.end_of_start_rest(|c| c.is_decimal_char(),
                               |c| c.is_decimal_char())
    }

    fn end_of_hex_chars(&self) -> Option<uint> {
        self.end_of_start_rest(|c| c.is_hex_char(),
                               |c| c.is_hex_char())
    }

    fn end_of_comment(&self) -> Option<uint> {
        // This deliberately does not include the >. -- is not allowed
        // in a comment, so we can just test the end if it matches the
        // complete close delimiter.
        match self.find_str("--") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_pi_value(&self) -> Option<uint> {
        match self.find_str("?>") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_start_rest(&self,
                         is_first: |char| -> bool,
                         is_rest: |char| -> bool)
                         -> Option<uint>
    {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if is_first(c) => (),
            Some((_, _)) => return None,
            None => return None,
        };

        let mut positions = positions.skip_while(|&(_, c)| is_rest(c));
        match positions.next() {
            Some((offset, _)) => Some(offset),
            None => Some(self.len()),
        }
    }

    fn end_of_name(&self) -> Option<uint> {
        self.end_of_start_rest(|c| c.is_name_start_char(), |c| c.is_name_char())
    }

    fn end_of_space(&self) -> Option<uint> {
        self.end_of_start_rest(|c| c.is_space_char(), |c| c.is_space_char())
    }

    fn end_of_start_tag(&self) -> Option<uint> {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if '<' == c => (),
            _ => return None,
        };

        match positions.next() {
            Some((offset, c)) =>
                match c {
                    '?' | '!' | '/' => None,
                    _ => Some(offset),
                },
            None => Some(self.len()),
        }
    }
}

trait XmlChar {
    fn is_name_start_char(&self) -> bool;
    fn is_name_char(&self) -> bool;
    fn is_space_char(&self) -> bool;
    fn is_decimal_char(&self) -> bool;
    fn is_hex_char(&self) -> bool;
}

impl XmlChar for char {
    fn is_name_start_char(&self) -> bool {
        match *self {
            ':'                         |
            'A'...'Z'                   |
            '_'                         |
            'a'...'z'                   |
            '\U000000C0'...'\U000000D6' |
            '\U000000D8'...'\U000000F6' |
            '\U000000F8'...'\U000002FF' |
            '\U00000370'...'\U0000037D' |
            '\U0000037F'...'\U00001FFF' |
            '\U0000200C'...'\U0000200D' |
            '\U00002070'...'\U0000218F' |
            '\U00002C00'...'\U00002FEF' |
            '\U00003001'...'\U0000D7FF' |
            '\U0000F900'...'\U0000FDCF' |
            '\U0000FDF0'...'\U0000FFFD' |
            '\U00010000'...'\U000EFFFF' => true,
            _ => false,
        }
    }

    fn is_name_char(&self) -> bool {
        if self.is_name_start_char() { return true; }
        match *self {
            '-'                 |
            '.'                 |
            '0'...'9'           |
            '\u00B7'            |
            '\u0300'...'\u036F' |
            '\u203F'...'\u2040' => true,
            _ => false
        }
    }

    fn is_space_char(&self) -> bool {
        match *self {
            '\x20' |
            '\x09' |
            '\x0D' |
            '\x0A' => true,
            _ => false,
        }
    }

    fn is_decimal_char(&self) -> bool {
        match *self {
            '0'...'9' => true,
            _ => false,
        }
    }

    fn is_hex_char(&self) -> bool {
        match *self {
            '0'...'9' |
            'a'...'f' |
            'A'...'F' => true,
            _ => false,
        }
    }
}

#[cfg(test)]
mod test {

use super::XmlStr;

#[test]
fn end_of_char_data_leading_ampersand() {
    assert_eq!("&".end_of_char_data(), None);
}

#[test]
fn end_of_char_data_leading_less_than() {
    assert_eq!("<".end_of_char_data(), None);
}

#[test]
fn end_of_char_data_leading_cdata_end() {
    assert_eq!("]]>".end_of_char_data(), None);
}

#[test]
fn end_of_char_data_until_ampersand() {
    assert_eq!("hello&world".end_of_char_data(), Some("hello".len()));
}

#[test]
fn end_of_char_data_until_less_than() {
    assert_eq!("hello<world".end_of_char_data(), Some("hello".len()));
}

#[test]
fn end_of_char_data_until_cdata_end() {
    assert_eq!("hello]]>world".end_of_char_data(), Some("hello".len()));
}

#[test]
fn end_of_char_data_includes_right_square() {
    assert_eq!("hello]world".end_of_char_data(), Some("hello]world".len()));
}

}
<|endoftext|>


<commit_before>
use winapi::shared::minwindef::{DWORD, HKEY};
use winapi::um::winnt::{KEY_READ};
use winapi::um::winreg;

use intercom::*;

use std::path::PathBuf;
use std::ffi::OsString;
use std::os::windows::ffi::{OsStrExt, OsStringExt};

#[allow(dead_code)]
#[allow(non_upper_case_globals)]
const CLSID_SetupConfiguration : GUID = GUID {
    data1: 0x177f_0c4a,
    data2: 0x1cd3,
    data3: 0x4de7,
    data4: [ 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d ]
};

#[repr(C)]
#[derive(Default, BidirectionalTypeInfo, Debug)]
pub struct FILETIME {
    low_part : u32,
    high_part : u32,
}

impl<TS: intercom::type_system::TypeSystem>
        intercom::type_system::ExternType<TS>
        for FILETIME {

    type ExternInputType = Self;
    type ExternOutputType = Self;
    type OwnedExternType = Self;
    type OwnedNativeType = Self;
}

#[com_interface( com_iid = "B41463C3-8866-43B5-BC33-2B0676F7F42E" )]
pub trait ISetupInstance
{
    fn get_instance_id( &self ) -> ComResult< String >;

    fn get_install_date( &self ) -> ComResult< FILETIME >;

    fn get_installation_name( &self ) -> ComResult< String >;

    fn get_installation_path( &self ) -> ComResult< String >;

    fn get_installation_version( &self ) -> ComResult< String >;

    fn get_display_name( &self, lcid: u32 ) -> ComResult< String >;

    fn get_description( &self, lcid: u32 ) -> ComResult< String >;

    fn resolve_path( &self, path : String ) -> ComResult< String >;
}

#[com_interface( com_iid = "6380BCFF-41D3-4B2E-8B2E-BF8A6810C848" )]
pub trait IEnumSetupInstances
{
    fn next(
        &self,
        celt : u32
    ) -> ComResult< ( ComItf<dyn ISetupInstance>, u32 ) >;

    fn skip( &self, celt : u32 ) -> ComResult<()>;

    fn reset( &self ) -> ComResult<()>;

    fn clone( &self ) -> ComResult< ComItf<dyn IEnumSetupInstances> >;
}

#[com_interface( com_iid = "26AAB78C-4A60-49D6-AF3B-3C35BC93365D" )]
pub trait ISetupConfiguration2
{
    fn enum_instances( &self )
        -> ComResult< ComItf<dyn IEnumSetupInstances> >;

    fn get_instance_for_current_process( &self )
        -> ComResult< ComItf<dyn ISetupInstance> >;

    fn get_instance_for_path( &self, path : String )
        -> ComResult< ComItf<dyn ISetupInstance> >;

    fn enum_all_instances( &self )
        -> ComResult< ComItf<dyn IEnumSetupInstances> >;
}

pub struct ToolPaths {
    pub mt : PathBuf,
    pub rc : PathBuf,
    pub midl : PathBuf,

    pub vs_bin : PathBuf,

    pub libs : Vec<PathBuf>,
    pub incs : Vec<PathBuf>,
}

/// Finds a path for the target within the roots.
///
/// * `roots` - Roots to search the target under.
/// * `target` - Path to send. May contain directories, such as `x86/foo.lib`.
///
/// Returns the full path to the target or None if the target was not found.
fn find_path( roots : &[&PathBuf], path : &str ) -> Option< PathBuf > {

    // Glob options. We'll want case insensitive searching thanks to Windows.
    let options = glob::MatchOptions {
        case_sensitive: false,
        require_literal_separator: true,
        require_literal_leading_dot: true,
    };

    // Go through all the roots one by one.
    for root in roots {

        // Create the glob pattern. Concatenate the root with the target and
        // throw ** in between.
        let root_str = root.to_string_lossy();
        let pattern = format!( "{}/**/{}", root_str, path );

        // Go through all entries.
        for entry in glob::glob_with( &pattern, options ).unwrap() {
            if let Ok( entry_path ) = entry {

                // The first entry we find, we'll return.
                return Some( entry_path );
            }
        }
    }

    // No paths found. Return none as the default.
    None
}

/// Get compiler paths from absolute file paths.
///
/// The compiler takes paths to the directories containing the files we want
/// as LIB and INCLUDE paths. This function strips the file name from the path
/// and deduplicates the resulting directories.
fn get_compiler_paths( paths : &[ PathBuf ] ) -> Vec<PathBuf>
{
    // Get the directory paths from the file paths.
    let mut dir_paths = paths.iter()
        .filter_map( |p| p.parent().map( |p| p.to_owned() ) )
        .collect::<Vec<_>>();

    // De-duplicate the directories.
    dir_paths.sort();
    dir_paths.dedup();

    dir_paths
}

/// Gets the Windows kit path and version.
fn get_kit_path() -> Result<( String, String ), String> {

    unsafe {

        // Turn the various string constants into WCHAR* strings for winapi.
        let key = OsString::from( "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots\0" )
                    .encode_wide()
                    .collect::<Vec<_>>();
        let value = OsString::from( "KitsRoot10\0" )
                    .encode_wide()
                    .collect::<Vec<_>>();

        // Open the Installed Roots registry key.
        let mut hkey_roots : HKEY = std::ptr::null_mut();
        let mut disp : DWORD = 0;
        winreg::RegCreateKeyExW(
            winreg::HKEY_LOCAL_MACHINE,
            key.as_slice().as_ptr(),
            0,::std::ptr::null_mut(), 0,
            KEY_READ,
            std::ptr::null_mut(),
            &mut hkey_roots,
            &mut disp );
        if hkey_roots.is_null() {
            return Err( "Could not open Windows Kits registry key".to_owned() );
        }

        // Get the 'KitsRoot10' value from the above key.
        let mut buf = Vec::with_capacity( 250 );
        let mut buf_len : DWORD = 250;
        let hr = winreg::RegQueryValueExW(
                hkey_roots, value.as_slice().as_ptr(), std::ptr::null_mut(),
                std::ptr::null_mut(),
                buf.as_mut_ptr() as *mut _,
                &mut buf_len );
        if hr != 0 {
            return Err( "Could not read KitsRoot10 registry value".to_owned() );
        }

        // Turn the value into a rust string.
        buf.set_len( ( buf_len / 2 ) as usize - 1 );
        let kitsroot_osstring = OsString::from_wide( &buf );
        let kitsroot = kitsroot_osstring.to_string_lossy().to_owned();

        // Get the first sub-key under the Installed Roots. We don't really
        // care which kit version this is as long as it's one that is present
        // on the system.
        let mut buf = Vec::with_capacity( 250 );
        let mut buf_len : DWORD = 250;
        let hr = winreg::RegEnumKeyExW(
            hkey_roots, 0,
            buf.as_mut_ptr(), &mut buf_len,
            std::ptr::null_mut(),
            std::ptr::null_mut(),
            std::ptr::null_mut(),
            std::ptr::null_mut() );
        if hr != 0 {
            return Err( "Could not read Windows Kits versions".to_owned() );
        }

        // Turn the installed version to a rust string.
        buf.set_len( buf_len as usize );
        let kitsversion_osstring = OsString::from_wide( &buf );
        let kitsversion = kitsversion_osstring.to_string_lossy().to_owned();

        Ok( ( kitsroot.into_owned(), kitsversion.into_owned() ) )
    }
}

/// Gets the VS 2017+ installation path.
fn get_vs_path() -> Result<String, String> {

    intercom::runtime::initialize()
            .map_err( |hr| format!( "Failed to initialize COM: {:?}", hr ) )?;

    let installation_path = {

        // Get the COM API entry point for the new VS configuration API.
        let setup_conf = ComRc::<dyn ISetupConfiguration2>
                ::create( CLSID_SetupConfiguration ).unwrap();

        // Get the first instance.
        let instances = setup_conf.enum_instances().unwrap();
        let ( next, _ ) = instances.next( 1 ).unwrap();

        // Read the installation path. We don't care about anything else for now.
        next.get_installation_path().unwrap()
    };

    intercom::runtime::uninitialize();

    Ok( installation_path )
}

pub fn get_tool_paths() -> Result<ToolPaths, String> {

    // Get the Windows kit base path and version.
    let ( kitroot, kitversion ) = get_kit_path()?;
    let kitroot = PathBuf::from( &kitroot );

    // Form the kit lib and include paths.
    // let kit_lib_root = kitroot.join( "Lib" ).join( &kitversion );
    let kit_include_root = kitroot.join( "Include" ).join( &kitversion );

    // Get the Visual C++ tools path.
    let vsroot = PathBuf::from( &get_vs_path()? ).join( r"VC\Tools\MSVC" );

    // Resolve the required libs.
    let libs = get_compiler_paths( &[

            // These paths cover most of the lib dirs. However no idea if we
            // need them so they are commented out until something complains.

            // find_path( &[ &kit_lib_root ], r"x64\ucrt.lib" ).unwrap(),
            // find_path( &[ &kit_lib_root ], r"x64\ole32.lib" ).unwrap(),
            // find_path( &[ &vsroot ], r"lib\x64\vcruntime.lib" ).unwrap(),
        ] );

    // Resolve the required include directories.
    let incs = get_compiler_paths( &[
        find_path( &[ &kit_include_root ], r"oaidl.idl" ).unwrap(),
        find_path( &[ &kit_include_root ], r"wtypes.idl" ).unwrap(),
    ] );

    // We'll need the C compiler for the preprocessing of the IDL.
    let mut vs_bin = find_path( &[ &vsroot ], r"Hostx64\x64\cl.exe" ).unwrap();
    vs_bin.pop();

    Ok( ToolPaths {
        mt: kitroot.join( format!( r"bin\{}\x64\mt.exe", kitversion ) ),
        midl: kitroot.join( format!( r"bin\{}\x64\midl.exe", kitversion ) ),
        rc : kitroot.join( format!( r"bin\{}\x64\rc.exe", kitversion ) ),

        vs_bin, libs, incs
    } )
}

#[cfg(test)]
mod test
{
    use super::*;
    use std::process::Command;
    use std::path::PathBuf;

    #[test]
    fn get_vs_2017_details() {
        intercom::runtime::initialize().unwrap();
        {

            let setup_conf = ComRc::<dyn ISetupConfiguration2>
                    ::create( CLSID_SetupConfiguration ).unwrap();
            let instances = setup_conf.enum_instances().unwrap();

            let ( next, _ ) = instances.next( 1 ).unwrap();
            let installation_name = next.get_installation_name().unwrap();
            let installation_path = next.get_installation_path().unwrap();
            let installation_version = next.get_installation_version().unwrap();

            let installation_name_actual = get_vswhere_property( "installationName" );
            let installation_path_actual = get_vswhere_property( "installationPath" );
            let installation_version_actual = get_vswhere_property( "installationVersion" );

            assert_eq!( installation_name_actual.trim(), installation_name );
            assert_eq!( installation_path_actual.trim(), installation_path );
            assert_eq!( installation_version_actual.trim(), installation_version );

        }
        intercom::runtime::uninitialize();
    }

    fn get_intercom_root() -> PathBuf {
        let mut root_path = std::env::current_exe().unwrap();
        loop {
            if root_path.join( "intercom-cli" ).exists() {
                break;
            }
            assert!( root_path.pop() );
        }

        root_path
    }

    /// Gets the specified vswhere property.
    fn get_vswhere_property(
        property: &str,
    ) -> String {

        // First attempt to get the property without the "BuildTools" product.
        // If we don't get a result then try "BuildTools" explicitly.
        // We do this in two steps to avoid issues when the developer has both the Visual Studio and the BuildTools installed.
        let v = Some("[15, 16)");
        match get_vswhere_property_for_products( property, &[], v )
        {
            Ok( value ) => value,
            Err( _ ) => get_vswhere_property_for_products(
                    property, &[ "BuildTools" ], v ).unwrap(),
        }
    }

    /// Gets the specified vswhere property for the specified products.
    fn get_vswhere_property_for_products(
        property: &str,
        products: &[&str],
        version: Option<&str>,
    ) -> Result<String, String>
    {
        let vswhere_path = get_intercom_root().join( "scripts/vswhere.exe" );
        let mut cmd = Command::new( &vswhere_path );
        cmd.arg( "/nologo" )
            .arg( "-property" ).arg( property );
        if products.len() > 0 {
            cmd.arg( "-products" ).args( products );
        }
        if let Some(v) = version {
            cmd.arg( "-version" ).arg( v );
        }
        let property_from_output = cmd.output().unwrap().stdout;

        // Ensure we got exactly one result.
        let property_from_output: String = String::from_utf8_lossy( &property_from_output ).to_owned().to_string();
        let mut values: Vec<&str> = property_from_output
                .split( "\r\n" )
                .filter_map( |s| { if s.is_empty() { None } else { Some( s ) } } )
                .collect();

        values.sort();
        values.first()
                .map( |l| l.to_string() )
                .ok_or_else( || format!( "No VS build tools installed" ) )
    }

    #[test]
    fn get_tool_paths_returns_valid_paths() {
        intercom::runtime::initialize().unwrap();

        // The HOST env variable is set for build.rs, not for tests.
        // We need to fake one here.
        std::env::set_var( "HOST", "x86_64-pc-windows-msvc" );

        let paths = get_tool_paths().unwrap();
        assert!( paths.mt.exists() );
        assert!( paths.rc.exists() );
        assert!( paths.midl.exists() );

        intercom::runtime::uninitialize();
    }
}
<commit_msg>Fix Windows build<commit_after>
use winapi::shared::minwindef::{DWORD, HKEY};
use winapi::um::winnt::{KEY_READ};
use winapi::um::winreg;

use intercom::*;

use std::path::PathBuf;
use std::ffi::OsString;
use std::os::windows::ffi::{OsStrExt, OsStringExt};

#[allow(dead_code)]
#[allow(non_upper_case_globals)]
const CLSID_SetupConfiguration : GUID = GUID {
    data1: 0x177f_0c4a,
    data2: 0x1cd3,
    data3: 0x4de7,
    data4: [ 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d ]
};

#[repr(C)]
#[derive(Default, BidirectionalTypeInfo, Debug)]
pub struct FILETIME {
    low_part : u32,
    high_part : u32,
}

impl<TS: intercom::type_system::TypeSystem>
        intercom::type_system::ExternType<TS>
        for FILETIME {

    type ExternInputType = Self;
    type ExternOutputType = Self;
    type OwnedExternType = Self;
    type OwnedNativeType = Self;
}

#[com_interface( com_iid = "B41463C3-8866-43B5-BC33-2B0676F7F42E" )]
pub trait ISetupInstance
{
    fn get_instance_id( &self ) -> ComResult< String >;

    fn get_install_date( &self ) -> ComResult< FILETIME >;

    fn get_installation_name( &self ) -> ComResult< String >;

    fn get_installation_path( &self ) -> ComResult< String >;

    fn get_installation_version( &self ) -> ComResult< String >;

    fn get_display_name( &self, lcid: u32 ) -> ComResult< String >;

    fn get_description( &self, lcid: u32 ) -> ComResult< String >;

    fn resolve_path( &self, path : String ) -> ComResult< String >;
}

#[com_interface( com_iid = "6380BCFF-41D3-4B2E-8B2E-BF8A6810C848" )]
pub trait IEnumSetupInstances
{
    fn next(
        &self,
        celt : u32
    ) -> ComResult< ( ComRc<dyn ISetupInstance>, u32 ) >;

    fn skip( &self, celt : u32 ) -> ComResult<()>;

    fn reset( &self ) -> ComResult<()>;

    fn clone( &self ) -> ComResult< ComRc<dyn IEnumSetupInstances> >;
}

#[com_interface( com_iid = "26AAB78C-4A60-49D6-AF3B-3C35BC93365D" )]
pub trait ISetupConfiguration2
{
    fn enum_instances( &self )
        -> ComResult< ComRc<dyn IEnumSetupInstances> >;

    fn get_instance_for_current_process( &self )
        -> ComResult< ComRc<dyn ISetupInstance> >;

    fn get_instance_for_path( &self, path : String )
        -> ComResult< ComRc<dyn ISetupInstance> >;

    fn enum_all_instances( &self )
        -> ComResult< ComRc<dyn IEnumSetupInstances> >;
}

pub struct ToolPaths {
    pub mt : PathBuf,
    pub rc : PathBuf,
    pub midl : PathBuf,

    pub vs_bin : PathBuf,

    pub libs : Vec<PathBuf>,
    pub incs : Vec<PathBuf>,
}

/// Finds a path for the target within the roots.
///
/// * `roots` - Roots to search the target under.
/// * `target` - Path to send. May contain directories, such as `x86/foo.lib`.
///
/// Returns the full path to the target or None if the target was not found.
fn find_path( roots : &[&PathBuf], path : &str ) -> Option< PathBuf > {

    // Glob options. We'll want case insensitive searching thanks to Windows.
    let options = glob::MatchOptions {
        case_sensitive: false,
        require_literal_separator: true,
        require_literal_leading_dot: true,
    };

    // Go through all the roots one by one.
    for root in roots {

        // Create the glob pattern. Concatenate the root with the target and
        // throw ** in between.
        let root_str = root.to_string_lossy();
        let pattern = format!( "{}/**/{}", root_str, path );

        // Go through all entries.
        for entry in glob::glob_with( &pattern, options ).unwrap() {
            if let Ok( entry_path ) = entry {

                // The first entry we find, we'll return.
                return Some( entry_path );
            }
        }
    }

    // No paths found. Return none as the default.
    None
}

/// Get compiler paths from absolute file paths.
///
/// The compiler takes paths to the directories containing the files we want
/// as LIB and INCLUDE paths. This function strips the file name from the path
/// and deduplicates the resulting directories.
fn get_compiler_paths( paths : &[ PathBuf ] ) -> Vec<PathBuf>
{
    // Get the directory paths from the file paths.
    let mut dir_paths = paths.iter()
        .filter_map( |p| p.parent().map( |p| p.to_owned() ) )
        .collect::<Vec<_>>();

    // De-duplicate the directories.
    dir_paths.sort();
    dir_paths.dedup();

    dir_paths
}

/// Gets the Windows kit path and version.
fn get_kit_path() -> Result<( String, String ), String> {

    unsafe {

        // Turn the various string constants into WCHAR* strings for winapi.
        let key = OsString::from( "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots\0" )
                    .encode_wide()
                    .collect::<Vec<_>>();
        let value = OsString::from( "KitsRoot10\0" )
                    .encode_wide()
                    .collect::<Vec<_>>();

        // Open the Installed Roots registry key.
        let mut hkey_roots : HKEY = std::ptr::null_mut();
        let mut disp : DWORD = 0;
        winreg::RegCreateKeyExW(
            winreg::HKEY_LOCAL_MACHINE,
            key.as_slice().as_ptr(),
            0,::std::ptr::null_mut(), 0,
            KEY_READ,
            std::ptr::null_mut(),
            &mut hkey_roots,
            &mut disp );
        if hkey_roots.is_null() {
            return Err( "Could not open Windows Kits registry key".to_owned() );
        }

        // Get the 'KitsRoot10' value from the above key.
        let mut buf = Vec::with_capacity( 250 );
        let mut buf_len : DWORD = 250;
        let hr = winreg::RegQueryValueExW(
                hkey_roots, value.as_slice().as_ptr(), std::ptr::null_mut(),
                std::ptr::null_mut(),
                buf.as_mut_ptr() as *mut _,
                &mut buf_len );
        if hr != 0 {
            return Err( "Could not read KitsRoot10 registry value".to_owned() );
        }

        // Turn the value into a rust string.
        buf.set_len( ( buf_len / 2 ) as usize - 1 );
        let kitsroot_osstring = OsString::from_wide( &buf );
        let kitsroot = kitsroot_osstring.to_string_lossy().to_owned();

        // Get the first sub-key under the Installed Roots. We don't really
        // care which kit version this is as long as it's one that is present
        // on the system.
        let mut buf = Vec::with_capacity( 250 );
        let mut buf_len : DWORD = 250;
        let hr = winreg::RegEnumKeyExW(
            hkey_roots, 0,
            buf.as_mut_ptr(), &mut buf_len,
            std::ptr::null_mut(),
            std::ptr::null_mut(),
            std::ptr::null_mut(),
            std::ptr::null_mut() );
        if hr != 0 {
            return Err( "Could not read Windows Kits versions".to_owned() );
        }

        // Turn the installed version to a rust string.
        buf.set_len( buf_len as usize );
        let kitsversion_osstring = OsString::from_wide( &buf );
        let kitsversion = kitsversion_osstring.to_string_lossy().to_owned();

        Ok( ( kitsroot.into_owned(), kitsversion.into_owned() ) )
    }
}

/// Gets the VS 2017+ installation path.
fn get_vs_path() -> Result<String, String> {

    intercom::runtime::initialize()
            .map_err( |hr| format!( "Failed to initialize COM: {:?}", hr ) )?;

    let installation_path = {

        // Get the COM API entry point for the new VS configuration API.
        let setup_conf = ComRc::<dyn ISetupConfiguration2>
                ::create( CLSID_SetupConfiguration ).unwrap();

        // Get the first instance.
        let instances = setup_conf.enum_instances().unwrap();
        let ( next, _ ) = instances.next( 1 ).unwrap();

        // Read the installation path. We don't care about anything else for now.
        next.get_installation_path().unwrap()
    };

    intercom::runtime::uninitialize();

    Ok( installation_path )
}

pub fn get_tool_paths() -> Result<ToolPaths, String> {

    // Get the Windows kit base path and version.
    let ( kitroot, kitversion ) = get_kit_path()?;
    let kitroot = PathBuf::from( &kitroot );

    // Form the kit lib and include paths.
    // let kit_lib_root = kitroot.join( "Lib" ).join( &kitversion );
    let kit_include_root = kitroot.join( "Include" ).join( &kitversion );

    // Get the Visual C++ tools path.
    let vsroot = PathBuf::from( &get_vs_path()? ).join( r"VC\Tools\MSVC" );

    // Resolve the required libs.
    let libs = get_compiler_paths( &[

            // These paths cover most of the lib dirs. However no idea if we
            // need them so they are commented out until something complains.

            // find_path( &[ &kit_lib_root ], r"x64\ucrt.lib" ).unwrap(),
            // find_path( &[ &kit_lib_root ], r"x64\ole32.lib" ).unwrap(),
            // find_path( &[ &vsroot ], r"lib\x64\vcruntime.lib" ).unwrap(),
        ] );

    // Resolve the required include directories.
    let incs = get_compiler_paths( &[
        find_path( &[ &kit_include_root ], r"oaidl.idl" ).unwrap(),
        find_path( &[ &kit_include_root ], r"wtypes.idl" ).unwrap(),
    ] );

    // We'll need the C compiler for the preprocessing of the IDL.
    let mut vs_bin = find_path( &[ &vsroot ], r"Hostx64\x64\cl.exe" ).unwrap();
    vs_bin.pop();

    Ok( ToolPaths {
        mt: kitroot.join( format!( r"bin\{}\x64\mt.exe", kitversion ) ),
        midl: kitroot.join( format!( r"bin\{}\x64\midl.exe", kitversion ) ),
        rc : kitroot.join( format!( r"bin\{}\x64\rc.exe", kitversion ) ),

        vs_bin, libs, incs
    } )
}

#[cfg(test)]
mod test
{
    use super::*;
    use std::process::Command;
    use std::path::PathBuf;

    #[test]
    fn get_vs_2017_details() {
        intercom::runtime::initialize().unwrap();
        {

            let setup_conf = ComRc::<dyn ISetupConfiguration2>
                    ::create( CLSID_SetupConfiguration ).unwrap();
            let instances = setup_conf.enum_instances().unwrap();

            let ( next, _ ) = instances.next( 1 ).unwrap();
            let installation_name = next.get_installation_name().unwrap();
            let installation_path = next.get_installation_path().unwrap();
            let installation_version = next.get_installation_version().unwrap();

            let installation_name_actual = get_vswhere_property( "installationName" );
            let installation_path_actual = get_vswhere_property( "installationPath" );
            let installation_version_actual = get_vswhere_property( "installationVersion" );

            assert_eq!( installation_name_actual.trim(), installation_name );
            assert_eq!( installation_path_actual.trim(), installation_path );
            assert_eq!( installation_version_actual.trim(), installation_version );

        }
        intercom::runtime::uninitialize();
    }

    fn get_intercom_root() -> PathBuf {
        let mut root_path = std::env::current_exe().unwrap();
        loop {
            if root_path.join( "intercom-cli" ).exists() {
                break;
            }
            assert!( root_path.pop() );
        }

        root_path
    }

    /// Gets the specified vswhere property.
    fn get_vswhere_property(
        property: &str,
    ) -> String {

        // First attempt to get the property without the "BuildTools" product.
        // If we don't get a result then try "BuildTools" explicitly.
        // We do this in two steps to avoid issues when the developer has both the Visual Studio and the BuildTools installed.
        let v = Some("[15, 16)");
        match get_vswhere_property_for_products( property, &[], v )
        {
            Ok( value ) => value,
            Err( _ ) => get_vswhere_property_for_products(
                    property, &[ "BuildTools" ], v ).unwrap(),
        }
    }

    /// Gets the specified vswhere property for the specified products.
    fn get_vswhere_property_for_products(
        property: &str,
        products: &[&str],
        version: Option<&str>,
    ) -> Result<String, String>
    {
        let vswhere_path = get_intercom_root().join( "scripts/vswhere.exe" );
        let mut cmd = Command::new( &vswhere_path );
        cmd.arg( "/nologo" )
            .arg( "-property" ).arg( property );
        if products.len() > 0 {
            cmd.arg( "-products" ).args( products );
        }
        if let Some(v) = version {
            cmd.arg( "-version" ).arg( v );
        }
        let property_from_output = cmd.output().unwrap().stdout;

        // Ensure we got exactly one result.
        let property_from_output: String = String::from_utf8_lossy( &property_from_output ).to_owned().to_string();
        let mut values: Vec<&str> = property_from_output
                .split( "\r\n" )
                .filter_map( |s| { if s.is_empty() { None } else { Some( s ) } } )
                .collect();

        values.sort();
        values.first()
                .map( |l| l.to_string() )
                .ok_or_else( || format!( "No VS build tools installed" ) )
    }

    #[test]
    fn get_tool_paths_returns_valid_paths() {
        intercom::runtime::initialize().unwrap();

        // The HOST env variable is set for build.rs, not for tests.
        // We need to fake one here.
        std::env::set_var( "HOST", "x86_64-pc-windows-msvc" );

        let paths = get_tool_paths().unwrap();
        assert!( paths.mt.exists() );
        assert!( paths.rc.exists() );
        assert!( paths.midl.exists() );

        intercom::runtime::uninitialize();
    }
}
<|endoftext|>