<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![deny(clippy::all)]

use signedsource::{sign_file, SIGNING_TOKEN};
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(name = "fixture-tests", about = "Fixture tests")]
struct Options {
    #[structopt(name = "DIR", parse(from_os_str))]
    dirs: Vec<PathBuf>,
}

#[derive(Debug)]
struct TestCase {
    name: String,
    input: Option<PathBuf>,
    expected: Option<PathBuf>,
}

const EXPECTED_EXTENSION: &str = "expected";
const TODO: &str = "TODO";

fn main() {
    let opt = Options::from_args();

    for dir in opt.dirs {
        let test_name = dir.file_name().unwrap().to_str().unwrap();
        let fixtures_dir = dir.join("fixtures");
        let paths = fs::read_dir(&fixtures_dir)
            .unwrap_or_else(|_| panic!("Fixtures dir does not exist: {:?}", &fixtures_dir));
        let mut test_cases: HashMap<String, TestCase> = HashMap::new();
        for dir_entry in paths {
            let path = dir_entry.unwrap().path();
            if path
                .extension()
                .unwrap_or_else(|| panic!("unexpected path {:?}", &path))
                == TODO
            {
                continue;
            }
            let name = sanitize_identifier(path.file_stem().unwrap().to_str().unwrap());
            let mut test_case = test_cases.entry(name.clone()).or_insert_with(|| TestCase {
                name,
                input: None,
                expected: None,
            });
            if path.extension().unwrap() == EXPECTED_EXTENSION {
                if let Some(ref previous) = test_case.expected {
                    panic!("Conflicting fixture name, {:?} and {:?}", previous, path);
                }
                test_case.expected = Some(path);
            } else {
                if let Some(ref previous) = test_case.input {
                    panic!("Conflicting fixture name, {:?} and {:?}", previous, path);
                }
                test_case.input = Some(path);
            }
        }
        for mut test_case in test_cases.values_mut() {
            if test_case.expected.is_none() {
                if let Some(ref input) = test_case.input {
                    let mut expected = input.clone();
                    expected.set_extension(EXPECTED_EXTENSION);
                    File::create(&expected)
                        .unwrap()
                        .write_all(
                            "\x40nocommit\nRun snapshot tests with UPDATE_SNAPSHOTS=1 to update this new file.\n"
                            .as_bytes(),
                        )
                        .unwrap();
                    test_case.expected = Some(expected);
                }
            }
        }
        let mut test_cases: Vec<(_, _)> = test_cases.into_iter().collect();
        test_cases.sort_by_key(|entry| entry.0.to_owned());

        let test_cases = test_cases
            .into_iter()
            .map(|(_, test_case)| {
                let test_case_name = &test_case.name;
                format!(
                    r#"#[test]
fn {0}() {{
    let input = include_str!("{1}/fixtures/{2}");
    let expected = include_str!("{1}/fixtures/{3}");
    test_fixture(transform_fixture, "{2}", "{1}/fixtures/{3}", input, expected);
}}"#,
                    test_case.name,
                    &test_name,
                    test_case
                        .input
                        .unwrap_or_else(|| panic!(
                            "Expected input for test {:?} to exist",
                            test_case_name
                        ))
                        .file_name()
                        .and_then(|x| x.to_str())
                        .unwrap(),
                    test_case
                        .expected
                        .unwrap_or_else(|| panic!(
                            "Expected output for test {:?} to exist",
                            test_case_name
                        ))
                        .file_name()
                        .and_then(|x| x.to_str())
                        .unwrap()
                )
            })
            .collect::<Vec<_>>()
            .join("\n\n");

        let mut file = File::create(
            dir.parent()
                .unwrap()
                .join(format!("{}_test.rs", &test_name)),
        )
        .unwrap();
        file.write_all(
            sign_file(&format!(
                "// {0}

mod {1};

use {1}::transform_fixture;
use fixture_tests::test_fixture;

{2}
",
                SIGNING_TOKEN, &test_name, test_cases
            ))
            .as_bytes(),
        )
        .unwrap();
    }
}

fn sanitize_identifier(input: &str) -> String {
    input
        .chars()
        .map(|chr| match chr {
            'a'..='z' | '0'..='9' | '_' => chr,
            'A'..='Z' => chr.to_ascii_lowercase(),
            _ => '_',
        })
        .collect()
}
<commit_msg>warn on missing buck test definitions<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![deny(clippy::all)]

use colored::Colorize;
use signedsource::{sign_file, SIGNING_TOKEN};
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(name = "fixture-tests", about = "Fixture tests")]
struct Options {
    #[structopt(name = "DIR", parse(from_os_str))]
    dirs: Vec<PathBuf>,
}

#[derive(Debug)]
struct TestCase {
    name: String,
    input: Option<PathBuf>,
    expected: Option<PathBuf>,
}

const EXPECTED_EXTENSION: &str = "expected";
const TODO: &str = "TODO";

fn main() {
    let opt = Options::from_args();

    for dir in opt.dirs {
        let test_name = dir.file_name().unwrap().to_str().unwrap();
        let fixtures_dir = dir.join("fixtures");
        let paths = fs::read_dir(&fixtures_dir)
            .unwrap_or_else(|_| panic!("Fixtures dir does not exist: {:?}", &fixtures_dir));
        let mut test_cases: HashMap<String, TestCase> = HashMap::new();
        for dir_entry in paths {
            let path = dir_entry.unwrap().path();
            if path
                .extension()
                .unwrap_or_else(|| panic!("unexpected path {:?}", &path))
                == TODO
            {
                continue;
            }
            let name = sanitize_identifier(path.file_stem().unwrap().to_str().unwrap());
            let mut test_case = test_cases.entry(name.clone()).or_insert_with(|| TestCase {
                name,
                input: None,
                expected: None,
            });
            if path.extension().unwrap() == EXPECTED_EXTENSION {
                if let Some(ref previous) = test_case.expected {
                    panic!("Conflicting fixture name, {:?} and {:?}", previous, path);
                }
                test_case.expected = Some(path);
            } else {
                if let Some(ref previous) = test_case.input {
                    panic!("Conflicting fixture name, {:?} and {:?}", previous, path);
                }
                test_case.input = Some(path);
            }
        }
        for mut test_case in test_cases.values_mut() {
            if test_case.expected.is_none() {
                if let Some(ref input) = test_case.input {
                    let mut expected = input.clone();
                    expected.set_extension(EXPECTED_EXTENSION);
                    File::create(&expected)
                        .unwrap()
                        .write_all(
                            "\x40nocommit\nRun snapshot tests with UPDATE_SNAPSHOTS=1 to update this new file.\n"
                            .as_bytes(),
                        )
                        .unwrap();
                    test_case.expected = Some(expected);
                }
            }
        }
        let mut test_cases: Vec<(_, _)> = test_cases.into_iter().collect();
        test_cases.sort_by_key(|entry| entry.0.to_owned());

        let test_cases = test_cases
            .into_iter()
            .map(|(_, test_case)| {
                let test_case_name = &test_case.name;
                format!(
                    r#"#[test]
fn {0}() {{
    let input = include_str!("{1}/fixtures/{2}");
    let expected = include_str!("{1}/fixtures/{3}");
    test_fixture(transform_fixture, "{2}", "{1}/fixtures/{3}", input, expected);
}}"#,
                    test_case.name,
                    &test_name,
                    test_case
                        .input
                        .unwrap_or_else(|| panic!(
                            "Expected input for test {:?} to exist",
                            test_case_name
                        ))
                        .file_name()
                        .and_then(|x| x.to_str())
                        .unwrap(),
                    test_case
                        .expected
                        .unwrap_or_else(|| panic!(
                            "Expected output for test {:?} to exist",
                            test_case_name
                        ))
                        .file_name()
                        .and_then(|x| x.to_str())
                        .unwrap()
                )
            })
            .collect::<Vec<_>>()
            .join("\n\n");

        let mut file = File::create(
            dir.parent()
                .unwrap()
                .join(format!("{}_test.rs", &test_name)),
        )
        .unwrap();
        file.write_all(
            sign_file(&format!(
                "// {0}

mod {1};

use {1}::transform_fixture;
use fixture_tests::test_fixture;

{2}
",
                SIGNING_TOKEN, &test_name, test_cases
            ))
            .as_bytes(),
        )
        .unwrap();

        check_targets_file(&dir);
    }
}

fn check_targets_file(test_dir: &PathBuf) {
    let targets_path = test_dir.parent().unwrap().parent().unwrap().join("TARGETS");
    let targets_content = match std::fs::read_to_string(&targets_path) {
        Ok(content) => content,
        Err(_) => return,
    };

    let expected_substr = format!(
        "\"tests/{}_test.rs\"",
        test_dir.file_stem().unwrap().to_str().unwrap()
    );

    if !targets_content.contains(&expected_substr) {
        eprintln!(
            "{}",
            format!(
                "WARNING: expected {:?} to contain substring {}",
                targets_path, expected_substr
            )
            .yellow()
        );
    }
}

fn sanitize_identifier(input: &str) -> String {
    input
        .chars()
        .map(|chr| match chr {
            'a'..='z' | '0'..='9' | '_' => chr,
            'A'..='Z' => chr.to_ascii_lowercase(),
            _ => '_',
        })
        .collect()
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::io::{self, Read, Write};

use futures::task::TaskRc;

/// The readable half of an object returned from `Io::split`.
pub struct ReadHalf<T> {
    handle: TaskRc<RefCell<T>>,
}

/// The readable half of an object returned from `Io::split`.
pub struct WriteHalf<T> {
    handle: TaskRc<RefCell<T>>,
}

pub fn split<T: Read + Write>(t: T) -> (ReadHalf<T>, WriteHalf<T>) {
    let rc = TaskRc::new(RefCell::new(t));
    (ReadHalf { handle: rc.clone() }, WriteHalf { handle: rc })
}

impl<T: Read> Read for ReadHalf<T> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.handle.with(|t| t.borrow_mut().read(buf))
    }
}

impl<T: Write> Write for WriteHalf<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.handle.with(|t| t.borrow_mut().write(buf))
    }

    fn flush(&mut self) -> io::Result<()> {
        self.handle.with(|t| t.borrow_mut().flush())
    }
}
<commit_msg>Add poll_{read,write} on halves<commit_after>use std::cell::RefCell;
use std::io::{self, Read, Write};

use futures::Async;
use futures::task::TaskRc;

use io::Io;

/// The readable half of an object returned from `Io::split`.
pub struct ReadHalf<T> {
    handle: TaskRc<RefCell<T>>,
}

/// The readable half of an object returned from `Io::split`.
pub struct WriteHalf<T> {
    handle: TaskRc<RefCell<T>>,
}

pub fn split<T: Io>(t: T) -> (ReadHalf<T>, WriteHalf<T>) {
    let rc = TaskRc::new(RefCell::new(t));
    (ReadHalf { handle: rc.clone() }, WriteHalf { handle: rc })
}

impl<T: Io> ReadHalf<T> {
    /// Calls the underlying `poll_read` function on this handling, testing to
    /// see if it's ready to be read from.
    pub fn poll_read(&mut self) -> Async<()> {
        self.handle.with(|t| t.borrow_mut().poll_read())
    }
}

impl<T: Io> WriteHalf<T> {
    /// Calls the underlying `poll_write` function on this handling, testing to
    /// see if it's ready to be written to.
    pub fn poll_write(&mut self) -> Async<()> {
        self.handle.with(|t| t.borrow_mut().poll_write())
    }
}

impl<T: Read> Read for ReadHalf<T> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.handle.with(|t| t.borrow_mut().read(buf))
    }
}

impl<T: Write> Write for WriteHalf<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.handle.with(|t| t.borrow_mut().write(buf))
    }

    fn flush(&mut self) -> io::Result<()> {
        self.handle.with(|t| t.borrow_mut().flush())
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use crate::logging::{LogEntry, LogSchema};
use anyhow::Result;
use aptos_crypto::HashValue;
use aptos_infallible::RwLock;
use aptos_logger::prelude::*;
use aptos_state_view::StateViewId;
use aptos_types::{
    ledger_info::LedgerInfoWithSignatures, state_store::state_value::StateValue,
    transaction::Transaction,
};
use aptos_vm::VMExecutor;
use executor_types::{BlockExecutorTrait, Error, StateComputeResult};
use fail::fail_point;
use scratchpad::SparseMerkleTree;
use std::{marker::PhantomData, sync::Arc};
use storage_interface::async_proof_fetcher::AsyncProofFetcher;

use crate::{
    components::{block_tree::BlockTree, chunk_output::ChunkOutput},
    metrics::{
        APTOS_EXECUTOR_COMMIT_BLOCKS_SECONDS, APTOS_EXECUTOR_EXECUTE_BLOCK_SECONDS,
        APTOS_EXECUTOR_SAVE_TRANSACTIONS_SECONDS, APTOS_EXECUTOR_TRANSACTIONS_SAVED,
        APTOS_EXECUTOR_VM_EXECUTE_BLOCK_SECONDS,
    },
};
use storage_interface::DbReaderWriter;

pub struct BlockExecutor<V> {
    pub db: DbReaderWriter,
    inner: RwLock<Option<BlockExecutorInner<V>>>,
}

impl<V> BlockExecutor<V>
where
    V: VMExecutor,
{
    pub fn new(db: DbReaderWriter) -> Self {
        Self {
            db,
            inner: RwLock::new(None),
        }
    }

    pub fn root_smt(&self) -> SparseMerkleTree<StateValue> {
        self.inner
            .read()
            .as_ref()
            .expect("BlockExecutor is not reset")
            .root_smt()
    }

    fn maybe_initialize(&self) -> Result<()> {
        if self.inner.read().is_none() {
            self.reset()?;
        }
        Ok(())
    }
}

impl<V> BlockExecutorTrait for BlockExecutor<V>
where
    V: VMExecutor,
{
    fn committed_block_id(&self) -> HashValue {
        self.maybe_initialize().expect("Failed to initialize.");
        self.inner
            .read()
            .as_ref()
            .expect("BlockExecutor is not reset")
            .committed_block_id()
    }

    fn reset(&self) -> Result<()> {
        *self.inner.write() = Some(BlockExecutorInner::new(self.db.clone())?);
        Ok(())
    }

    fn execute_block(
        &self,
        block: (HashValue, Vec<Transaction>),
        parent_block_id: HashValue,
    ) -> Result<StateComputeResult, Error> {
        self.maybe_initialize()?;
        self.inner
            .read()
            .as_ref()
            .expect("BlockExecutor is not reset")
            .execute_block(block, parent_block_id)
    }

    fn commit_blocks_ext(
        &self,
        block_ids: Vec<HashValue>,
        ledger_info_with_sigs: LedgerInfoWithSignatures,
        save_state_snapshots: bool,
    ) -> Result<(), Error> {
        self.inner
            .read()
            .as_ref()
            .expect("BlockExecutor is not reset")
            .commit_blocks_ext(block_ids, ledger_info_with_sigs, save_state_snapshots)
    }

    fn finish(&self) {
        *self.inner.write() = None;
    }
}

struct BlockExecutorInner<V> {
    db: DbReaderWriter,
    block_tree: BlockTree,
    phantom: PhantomData<V>,
}

impl<V> BlockExecutorInner<V>
where
    V: VMExecutor,
{
    pub fn new(db: DbReaderWriter) -> Result<Self> {
        let block_tree = BlockTree::new(&db.reader)?;
        Ok(Self {
            db,
            block_tree,
            phantom: PhantomData,
        })
    }

    fn root_smt(&self) -> SparseMerkleTree<StateValue> {
        self.block_tree
            .root_block()
            .output
            .result_view
            .state()
            .current
            .clone()
    }
}

impl<V> BlockExecutorInner<V>
where
    V: VMExecutor,
{
    fn committed_block_id(&self) -> HashValue {
        self.block_tree.root_block().id
    }

    fn execute_block(
        &self,
        block: (HashValue, Vec<Transaction>),
        parent_block_id: HashValue,
    ) -> Result<StateComputeResult, Error> {
        let (block_id, transactions) = block;
        let committed_block = self.block_tree.root_block();
        let mut block_vec = self
            .block_tree
            .get_blocks_opt(&[block_id, parent_block_id])?;
        let parent_block = block_vec
            .pop()
            .expect("Must exist.")
            .ok_or(Error::BlockNotFound(parent_block_id))?;
        let parent_output = &parent_block.output;
        let parent_view = &parent_output.result_view;
        let parent_accumulator = parent_view.txn_accumulator();

        if let Some(b) = block_vec.pop().expect("Must exist") {
            // this is a retry
            parent_block.ensure_has_child(block_id)?;
            return Ok(b.output.as_state_compute_result(parent_accumulator));
        }

        let output = if parent_block_id != committed_block.id && parent_output.has_reconfiguration()
        {
            info!(
                LogSchema::new(LogEntry::BlockExecutor).block_id(block_id),
                "reconfig_descendant_block_received"
            );
            parent_output.reconfig_suffix()
        } else {
            info!(
                LogSchema::new(LogEntry::BlockExecutor).block_id(block_id),
                "execute_block"
            );
            let _timer = APTOS_EXECUTOR_EXECUTE_BLOCK_SECONDS.start_timer();
            let state_view = parent_view.verified_state_view(
                StateViewId::BlockExecution { block_id },
                Arc::clone(&self.db.reader),
                Arc::new(AsyncProofFetcher::new(self.db.reader.clone())),
            )?;

            let chunk_output = {
                let _timer = APTOS_EXECUTOR_VM_EXECUTE_BLOCK_SECONDS.start_timer();
                fail_point!("executor::vm_execute_block", |_| {
                    Err(Error::from(anyhow::anyhow!(
                        "Injected error in vm_execute_block"
                    )))
                });
                ChunkOutput::by_transaction_execution::<V>(transactions, state_view)?
            };
            chunk_output.trace_log_transaction_status();

            let (output, _, _) = chunk_output.apply_to_ledger(parent_view)?;
            output
        };
        output.ensure_ends_with_state_checkpoint()?;

        let block = self
            .block_tree
            .add_block(parent_block_id, block_id, output)?;
        Ok(block.output.as_state_compute_result(parent_accumulator))
    }

    fn commit_blocks_ext(
        &self,
        block_ids: Vec<HashValue>,
        ledger_info_with_sigs: LedgerInfoWithSignatures,
        sync_commit: bool,
    ) -> Result<(), Error> {
        let _timer = APTOS_EXECUTOR_COMMIT_BLOCKS_SECONDS.start_timer();
        let committed_block = self.block_tree.root_block();
        if committed_block.num_persisted_transactions()
            == ledger_info_with_sigs.ledger_info().version() + 1
        {
            // a retry
            return Ok(());
        }

        let block_id_to_commit = ledger_info_with_sigs.ledger_info().consensus_block_id();
        info!(
            LogSchema::new(LogEntry::BlockExecutor).block_id(block_id_to_commit),
            "commit_block"
        );

        let blocks = self.block_tree.get_blocks(&block_ids)?;
        let txns_to_commit: Vec<_> = blocks
            .into_iter()
            .map(|block| block.output.transactions_to_commit())
            .collect::<Result<Vec<_>>>()?
            .into_iter()
            .flatten()
            .collect();
        let first_version = committed_block
            .output
            .result_view
            .txn_accumulator()
            .num_leaves();
        let to_commit = txns_to_commit.len();
        let target_version = ledger_info_with_sigs.ledger_info().version();
        if first_version + txns_to_commit.len() as u64 != target_version + 1 {
            return Err(Error::BadNumTxnsToCommit {
                first_version,
                to_commit,
                target_version,
            });
        }

        let _timer = APTOS_EXECUTOR_SAVE_TRANSACTIONS_SECONDS.start_timer();
        APTOS_EXECUTOR_TRANSACTIONS_SAVED.observe(to_commit as f64);

        fail_point!("executor::commit_blocks", |_| {
            Err(anyhow::anyhow!("Injected error in commit_blocks.").into())
        });
        let result_in_memory_state = self
            .block_tree
            .get_block(block_id_to_commit)?
            .output
            .result_view
            .state()
            .clone();
        self.db.writer.save_transactions(
            &txns_to_commit,
            first_version,
            committed_block.output.result_view.state().base_version,
            Some(&ledger_info_with_sigs),
            sync_commit,
            result_in_memory_state,
        )?;
        self.block_tree
            .prune(ledger_info_with_sigs.ledger_info())
            .expect("Failure pruning block tree.");

        Ok(())
    }
}
<commit_msg>[Execution] Add simple check to commit_blocks_ext.<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use crate::logging::{LogEntry, LogSchema};
use anyhow::Result;
use aptos_crypto::HashValue;
use aptos_infallible::RwLock;
use aptos_logger::prelude::*;
use aptos_state_view::StateViewId;
use aptos_types::{
    ledger_info::LedgerInfoWithSignatures, state_store::state_value::StateValue,
    transaction::Transaction,
};
use aptos_vm::VMExecutor;
use executor_types::{BlockExecutorTrait, Error, StateComputeResult};
use fail::fail_point;
use scratchpad::SparseMerkleTree;
use std::{marker::PhantomData, sync::Arc};
use storage_interface::async_proof_fetcher::AsyncProofFetcher;

use crate::{
    components::{block_tree::BlockTree, chunk_output::ChunkOutput},
    metrics::{
        APTOS_EXECUTOR_COMMIT_BLOCKS_SECONDS, APTOS_EXECUTOR_EXECUTE_BLOCK_SECONDS,
        APTOS_EXECUTOR_SAVE_TRANSACTIONS_SECONDS, APTOS_EXECUTOR_TRANSACTIONS_SAVED,
        APTOS_EXECUTOR_VM_EXECUTE_BLOCK_SECONDS,
    },
};
use storage_interface::DbReaderWriter;

pub struct BlockExecutor<V> {
    pub db: DbReaderWriter,
    inner: RwLock<Option<BlockExecutorInner<V>>>,
}

impl<V> BlockExecutor<V>
where
    V: VMExecutor,
{
    pub fn new(db: DbReaderWriter) -> Self {
        Self {
            db,
            inner: RwLock::new(None),
        }
    }

    pub fn root_smt(&self) -> SparseMerkleTree<StateValue> {
        self.inner
            .read()
            .as_ref()
            .expect("BlockExecutor is not reset")
            .root_smt()
    }

    fn maybe_initialize(&self) -> Result<()> {
        if self.inner.read().is_none() {
            self.reset()?;
        }
        Ok(())
    }
}

impl<V> BlockExecutorTrait for BlockExecutor<V>
where
    V: VMExecutor,
{
    fn committed_block_id(&self) -> HashValue {
        self.maybe_initialize().expect("Failed to initialize.");
        self.inner
            .read()
            .as_ref()
            .expect("BlockExecutor is not reset")
            .committed_block_id()
    }

    fn reset(&self) -> Result<()> {
        *self.inner.write() = Some(BlockExecutorInner::new(self.db.clone())?);
        Ok(())
    }

    fn execute_block(
        &self,
        block: (HashValue, Vec<Transaction>),
        parent_block_id: HashValue,
    ) -> Result<StateComputeResult, Error> {
        self.maybe_initialize()?;
        self.inner
            .read()
            .as_ref()
            .expect("BlockExecutor is not reset")
            .execute_block(block, parent_block_id)
    }

    fn commit_blocks_ext(
        &self,
        block_ids: Vec<HashValue>,
        ledger_info_with_sigs: LedgerInfoWithSignatures,
        save_state_snapshots: bool,
    ) -> Result<(), Error> {
        self.inner
            .read()
            .as_ref()
            .expect("BlockExecutor is not reset")
            .commit_blocks_ext(block_ids, ledger_info_with_sigs, save_state_snapshots)
    }

    fn finish(&self) {
        *self.inner.write() = None;
    }
}

struct BlockExecutorInner<V> {
    db: DbReaderWriter,
    block_tree: BlockTree,
    phantom: PhantomData<V>,
}

impl<V> BlockExecutorInner<V>
where
    V: VMExecutor,
{
    pub fn new(db: DbReaderWriter) -> Result<Self> {
        let block_tree = BlockTree::new(&db.reader)?;
        Ok(Self {
            db,
            block_tree,
            phantom: PhantomData,
        })
    }

    fn root_smt(&self) -> SparseMerkleTree<StateValue> {
        self.block_tree
            .root_block()
            .output
            .result_view
            .state()
            .current
            .clone()
    }
}

impl<V> BlockExecutorInner<V>
where
    V: VMExecutor,
{
    fn committed_block_id(&self) -> HashValue {
        self.block_tree.root_block().id
    }

    fn execute_block(
        &self,
        block: (HashValue, Vec<Transaction>),
        parent_block_id: HashValue,
    ) -> Result<StateComputeResult, Error> {
        let (block_id, transactions) = block;
        let committed_block = self.block_tree.root_block();
        let mut block_vec = self
            .block_tree
            .get_blocks_opt(&[block_id, parent_block_id])?;
        let parent_block = block_vec
            .pop()
            .expect("Must exist.")
            .ok_or(Error::BlockNotFound(parent_block_id))?;
        let parent_output = &parent_block.output;
        let parent_view = &parent_output.result_view;
        let parent_accumulator = parent_view.txn_accumulator();

        if let Some(b) = block_vec.pop().expect("Must exist") {
            // this is a retry
            parent_block.ensure_has_child(block_id)?;
            return Ok(b.output.as_state_compute_result(parent_accumulator));
        }

        let output = if parent_block_id != committed_block.id && parent_output.has_reconfiguration()
        {
            info!(
                LogSchema::new(LogEntry::BlockExecutor).block_id(block_id),
                "reconfig_descendant_block_received"
            );
            parent_output.reconfig_suffix()
        } else {
            info!(
                LogSchema::new(LogEntry::BlockExecutor).block_id(block_id),
                "execute_block"
            );
            let _timer = APTOS_EXECUTOR_EXECUTE_BLOCK_SECONDS.start_timer();
            let state_view = parent_view.verified_state_view(
                StateViewId::BlockExecution { block_id },
                Arc::clone(&self.db.reader),
                Arc::new(AsyncProofFetcher::new(self.db.reader.clone())),
            )?;

            let chunk_output = {
                let _timer = APTOS_EXECUTOR_VM_EXECUTE_BLOCK_SECONDS.start_timer();
                fail_point!("executor::vm_execute_block", |_| {
                    Err(Error::from(anyhow::anyhow!(
                        "Injected error in vm_execute_block"
                    )))
                });
                ChunkOutput::by_transaction_execution::<V>(transactions, state_view)?
            };
            chunk_output.trace_log_transaction_status();

            let (output, _, _) = chunk_output.apply_to_ledger(parent_view)?;
            output
        };
        output.ensure_ends_with_state_checkpoint()?;

        let block = self
            .block_tree
            .add_block(parent_block_id, block_id, output)?;
        Ok(block.output.as_state_compute_result(parent_accumulator))
    }

    fn commit_blocks_ext(
        &self,
        block_ids: Vec<HashValue>,
        ledger_info_with_sigs: LedgerInfoWithSignatures,
        sync_commit: bool,
    ) -> Result<(), Error> {
        let _timer = APTOS_EXECUTOR_COMMIT_BLOCKS_SECONDS.start_timer();

        // Ensure the block ids are not empty
        if block_ids.is_empty() {
            return Err(anyhow::anyhow!("Cannot commit 0 blocks!").into());
        }

        // Check for any potential retries
        let committed_block = self.block_tree.root_block();
        if committed_block.num_persisted_transactions()
            == ledger_info_with_sigs.ledger_info().version() + 1
        {
            return Ok(());
        }

        // Ensure the last block id matches the ledger info block id to commit
        let block_id_to_commit = ledger_info_with_sigs.ledger_info().consensus_block_id();
        info!(
            LogSchema::new(LogEntry::BlockExecutor).block_id(block_id_to_commit),
            "commit_block"
        );
        let last_block_id = *block_ids.last().unwrap();
        if last_block_id != block_id_to_commit {
            // This should not happen. If it does, we need to panic!
            panic!(
                "Block id to commit ({:?}) does not match last block id ({:?})!",
                block_id_to_commit, last_block_id
            );
        }

        let blocks = self.block_tree.get_blocks(&block_ids)?;
        let txns_to_commit: Vec<_> = blocks
            .into_iter()
            .map(|block| block.output.transactions_to_commit())
            .collect::<Result<Vec<_>>>()?
            .into_iter()
            .flatten()
            .collect();
        let first_version = committed_block
            .output
            .result_view
            .txn_accumulator()
            .num_leaves();
        let to_commit = txns_to_commit.len();
        let target_version = ledger_info_with_sigs.ledger_info().version();
        if first_version + txns_to_commit.len() as u64 != target_version + 1 {
            return Err(Error::BadNumTxnsToCommit {
                first_version,
                to_commit,
                target_version,
            });
        }

        let _timer = APTOS_EXECUTOR_SAVE_TRANSACTIONS_SECONDS.start_timer();
        APTOS_EXECUTOR_TRANSACTIONS_SAVED.observe(to_commit as f64);

        fail_point!("executor::commit_blocks", |_| {
            Err(anyhow::anyhow!("Injected error in commit_blocks.").into())
        });
        let result_in_memory_state = self
            .block_tree
            .get_block(block_id_to_commit)?
            .output
            .result_view
            .state()
            .clone();
        self.db.writer.save_transactions(
            &txns_to_commit,
            first_version,
            committed_block.output.result_view.state().base_version,
            Some(&ledger_info_with_sigs),
            sync_commit,
            result_in_memory_state,
        )?;
        self.block_tree
            .prune(ledger_info_with_sigs.ledger_info())
            .expect("Failure pruning block tree.");

        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate nalgebra as na;

use std::marker::PhantomData;
use alga::general::Real;
use na::{Isometry3, Vector6, DMatrix};
use std::error::Error;
use std::fmt;
use links::*;
use math::*;

fn calc_vector6_pose<T: Real>(pose: &Isometry3<T>) -> Vector6<T> {
    let rpy = to_euler_angles(&pose.rotation);
    Vector6::new(pose.translation.vector[0],
                 pose.translation.vector[1],
                 pose.translation.vector[2],
                 rpy[0],
                 rpy[1],
                 rpy[2])
}

#[derive(Debug)]
pub enum IKError {
    NotConverged,
    InverseMatrixError,
    PreconditionError,
    JointOutOfLimit(JointError),
}

impl From<JointError> for IKError {
    fn from(err: JointError) -> IKError {
        IKError::JointOutOfLimit(err)
    }
}

impl fmt::Display for IKError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            IKError::NotConverged => write!(f, "ik solve not converted"),
            IKError::InverseMatrixError => write!(f, "ik failed to solve inverse matrix"),
            IKError::PreconditionError => write!(f, "ik precondition not match"),
            IKError::JointOutOfLimit(ref err) => write!(f, "ik error : {}", err),
        }
    }
}

impl Error for IKError {
    fn description(&self) -> &str {
        match *self {
            IKError::NotConverged => "not converged",
            IKError::InverseMatrixError => "inverse matrix error",
            IKError::PreconditionError => "precondition not match",
            IKError::JointOutOfLimit(ref err) => err.description(),
        }
    }
}

pub trait InverseKinematicsSolver<T, K>
    where T: Real,
          K: KinematicChain<T>
{
    fn solve(&self, arm: &mut K, target_pose: &Isometry3<T>) -> Result<T, IKError>;
}

#[derive(Debug)]
pub struct JacobianIKSolver<T: Real, K: KinematicChain<T>> {
    pub jacobian_move_epsilon: T,
    pub allowable_target_distance: T,
    pub num_max_try: i32,
    phantom: PhantomData<K>,
}

impl<T, K> JacobianIKSolver<T, K>
    where T: Real,
          K: KinematicChain<T>
{
    pub fn new(jacobian_move_epsilon: T,
               allowable_target_distance: T,
               num_max_try: i32)
               -> JacobianIKSolver<T, K> {
        JacobianIKSolver {
            jacobian_move_epsilon: jacobian_move_epsilon,
            allowable_target_distance: allowable_target_distance,
            num_max_try: num_max_try,
            phantom: PhantomData::<K>,
        }
    }
    fn solve_one_loop(&self, arm: &mut K, target_pose: &Isometry3<T>) -> Result<T, IKError> {
        let orig_angles = arm.get_joint_angles();
        let dof = orig_angles.len();
        let orig_pose6 = calc_vector6_pose(&arm.calc_end_transform());
        let target_pose6 = calc_vector6_pose(&target_pose);
        let mut jacobi_vec = Vec::new();
        for i in 0..dof {
            let mut small_diff_angles_i = orig_angles.clone();
            small_diff_angles_i[i] += self.jacobian_move_epsilon;
            try!(arm.set_joint_angles(&small_diff_angles_i));
            let small_diff_pose6 = calc_vector6_pose(&arm.calc_end_transform());
            jacobi_vec.push(small_diff_pose6 - orig_pose6);
        }
        let jacobi = DMatrix::from_fn(6, dof, |r, c| jacobi_vec[c][r]);
        let j_inv = if dof > 6 {
            // use pseudo inverse
            try!(try_pseudo_inverse(&jacobi).ok_or(IKError::InverseMatrixError))
        } else {
            try!(jacobi.try_inverse().ok_or(IKError::InverseMatrixError))
        };
        let new_angles_diff = j_inv * (target_pose6 - orig_pose6) * self.jacobian_move_epsilon;
        let mut angles_vec = Vec::new();
        for i in 0..dof {
            angles_vec.push(orig_angles[i] + new_angles_diff[i]);
        }
        try!(arm.set_joint_angles(&angles_vec));
        let new_pose6 = calc_vector6_pose(&arm.calc_end_transform());
        Ok((target_pose6 - new_pose6).norm())
    }
}

impl<T, K> InverseKinematicsSolver<T, K> for JacobianIKSolver<T, K>
    where T: Real,
          K: KinematicChain<T>
{
    fn solve(&self, arm: &mut K, target_pose: &Isometry3<T>) -> Result<T, IKError> {
        let orig_angles = arm.get_joint_angles();
        if orig_angles.len() < 6 {
            println!("support only 6 or more DoF now");
            return Err(IKError::PreconditionError);
        }
        for _ in 0..self.num_max_try {
            let target_distance = try!(self.solve_one_loop(arm, &target_pose));
            if target_distance < self.allowable_target_distance {
                return Ok(target_distance);
            }
        }
        try!(arm.set_joint_angles(&orig_angles));
        Err(IKError::NotConverged)
    }
}

/// Build jacobianIKSolverBuilder
///
/// This builder allow initialation of JacobianIKSolver
/// without any parameters.
///
pub struct JacobianIKSolverBuilder<T, K>
    where T: Real,
          K: KinematicChain<T> {
    pub jacobian_move_epsilon: T,
    pub allowable_target_distance: T,
    pub num_max_try: i32,
    phantom: PhantomData<K>,
}

impl<T, K> JacobianIKSolverBuilder<T, K>
    where T: Real,
          K: KinematicChain<T> {
    pub fn new() -> Self {
        JacobianIKSolverBuilder {
            jacobian_move_epsilon: na::convert(0.001),
            allowable_target_distance:  na::convert(0.001),
            num_max_try: 100,
            phantom: PhantomData::<K>,
        }
    }
    pub fn jacobian_move_epsilon(&mut self, jacobian_epsilon: T) -> &mut Self {
        self.jacobian_move_epsilon = jacobian_epsilon;
        self
    }
    pub fn allowable_target_distance(&mut self, allowable_diff: T) -> &mut Self {
        self.allowable_target_distance = allowable_diff;
        self
    }
    pub fn num_max_try(&mut self, max_try: i32) -> &mut Self {
        self.num_max_try = max_try;
        self
    }
    pub fn finalize(&self) -> JacobianIKSolver<T, K> {
        JacobianIKSolver {
            jacobian_move_epsilon: self.jacobian_move_epsilon,
            allowable_target_distance: self.allowable_target_distance,
            num_max_try: self.num_max_try,
            phantom: self.phantom,
        }
    }
}
<commit_msg>Remove unused phantom data from ik<commit_after>extern crate nalgebra as na;

use alga::general::Real;
use na::{Isometry3, Vector6, DMatrix};
use std::error::Error;
use std::fmt;
use links::*;
use math::*;

fn calc_vector6_pose<T: Real>(pose: &Isometry3<T>) -> Vector6<T> {
    let rpy = to_euler_angles(&pose.rotation);
    Vector6::new(pose.translation.vector[0],
                 pose.translation.vector[1],
                 pose.translation.vector[2],
                 rpy[0],
                 rpy[1],
                 rpy[2])
}

#[derive(Debug)]
pub enum IKError {
    NotConverged,
    InverseMatrixError,
    PreconditionError,
    JointOutOfLimit(JointError),
}

impl From<JointError> for IKError {
    fn from(err: JointError) -> IKError {
        IKError::JointOutOfLimit(err)
    }
}

impl fmt::Display for IKError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            IKError::NotConverged => write!(f, "ik solve not converted"),
            IKError::InverseMatrixError => write!(f, "ik failed to solve inverse matrix"),
            IKError::PreconditionError => write!(f, "ik precondition not match"),
            IKError::JointOutOfLimit(ref err) => write!(f, "ik error : {}", err),
        }
    }
}

impl Error for IKError {
    fn description(&self) -> &str {
        match *self {
            IKError::NotConverged => "not converged",
            IKError::InverseMatrixError => "inverse matrix error",
            IKError::PreconditionError => "precondition not match",
            IKError::JointOutOfLimit(ref err) => err.description(),
        }
    }
}

pub trait InverseKinematicsSolver<T: Real> {
    fn solve<K>(&self, arm: &mut K, target_pose: &Isometry3<T>) -> Result<T, IKError>
        where K: KinematicChain<T>;
}

#[derive(Debug)]
pub struct JacobianIKSolver<T: Real> {
    pub jacobian_move_epsilon: T,
    pub allowable_target_distance: T,
    pub num_max_try: i32,
}

impl<T> JacobianIKSolver<T>
    where T: Real
{
    pub fn new(jacobian_move_epsilon: T,
               allowable_target_distance: T,
               num_max_try: i32)
               -> JacobianIKSolver<T> {
        JacobianIKSolver {
            jacobian_move_epsilon: jacobian_move_epsilon,
            allowable_target_distance: allowable_target_distance,
            num_max_try: num_max_try,
        }
    }
    fn solve_one_loop<K>(&self, arm: &mut K, target_pose: &Isometry3<T>) -> Result<T, IKError>
        where K: KinematicChain<T>
    {
        let orig_angles = arm.get_joint_angles();
        let dof = orig_angles.len();
        let orig_pose6 = calc_vector6_pose(&arm.calc_end_transform());
        let target_pose6 = calc_vector6_pose(&target_pose);
        let mut jacobi_vec = Vec::new();
        for i in 0..dof {
            let mut small_diff_angles_i = orig_angles.clone();
            small_diff_angles_i[i] += self.jacobian_move_epsilon;
            try!(arm.set_joint_angles(&small_diff_angles_i));
            let small_diff_pose6 = calc_vector6_pose(&arm.calc_end_transform());
            jacobi_vec.push(small_diff_pose6 - orig_pose6);
        }
        let jacobi = DMatrix::from_fn(6, dof, |r, c| jacobi_vec[c][r]);
        let j_inv = if dof > 6 {
            // use pseudo inverse
            try!(try_pseudo_inverse(&jacobi).ok_or(IKError::InverseMatrixError))
        } else {
            try!(jacobi.try_inverse().ok_or(IKError::InverseMatrixError))
        };
        let new_angles_diff = j_inv * (target_pose6 - orig_pose6) * self.jacobian_move_epsilon;
        let mut angles_vec = Vec::new();
        for i in 0..dof {
            angles_vec.push(orig_angles[i] + new_angles_diff[i]);
        }
        try!(arm.set_joint_angles(&angles_vec));
        let new_pose6 = calc_vector6_pose(&arm.calc_end_transform());
        Ok((target_pose6 - new_pose6).norm())
    }
}

impl<T> InverseKinematicsSolver<T> for JacobianIKSolver<T>
    where T: Real
{
    fn solve<K>(&self, arm: &mut K, target_pose: &Isometry3<T>) -> Result<T, IKError>
        where K: KinematicChain<T>
    {
        let orig_angles = arm.get_joint_angles();
        if orig_angles.len() < 6 {
            println!("support only 6 or more DoF now");
            return Err(IKError::PreconditionError);
        }
        for _ in 0..self.num_max_try {
            let target_distance = try!(self.solve_one_loop(arm, &target_pose));
            if target_distance < self.allowable_target_distance {
                return Ok(target_distance);
            }
        }
        try!(arm.set_joint_angles(&orig_angles));
        Err(IKError::NotConverged)
    }
}

/// Build jacobianIKSolverBuilder
///
/// This builder allow initialation of JacobianIKSolver
/// without any parameters.
///
pub struct JacobianIKSolverBuilder<T>
    where T: Real
{
    pub jacobian_move_epsilon: T,
    pub allowable_target_distance: T,
    pub num_max_try: i32,
}

impl<T> JacobianIKSolverBuilder<T>
    where T: Real
{
    pub fn new() -> Self {
        JacobianIKSolverBuilder {
            jacobian_move_epsilon: na::convert(0.001),
            allowable_target_distance: na::convert(0.001),
            num_max_try: 100,
        }
    }
    pub fn jacobian_move_epsilon(&mut self, jacobian_epsilon: T) -> &mut Self {
        self.jacobian_move_epsilon = jacobian_epsilon;
        self
    }
    pub fn allowable_target_distance(&mut self, allowable_diff: T) -> &mut Self {
        self.allowable_target_distance = allowable_diff;
        self
    }
    pub fn num_max_try(&mut self, max_try: i32) -> &mut Self {
        self.num_max_try = max_try;
        self
    }
    pub fn finalize(&self) -> JacobianIKSolver<T> {
        JacobianIKSolver {
            jacobian_move_epsilon: self.jacobian_move_epsilon,
            allowable_target_distance: self.allowable_target_distance,
            num_max_try: self.num_max_try,
        }
    }
}
<|endoftext|>


<commit_before>
// Copyright 2014-2015 Johannes Köster, Peer Aramillo Irizar.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of the DNA alphabet.
//!
//! # Example
//!
//! ```
//! use bio::alphabets;
//! let alphabet = alphabets::dna::alphabet();
//! assert!(alphabet.is_word(b"GATTACA"));
//! assert!(alphabet.is_word(b"gattaca"));
//! assert!(!alphabet.is_word(b"ACGU"));
//! ```

use alphabets::Alphabet;
use utils::IntoTextIterator;

/// The DNA alphabet (uppercase and lowercase).
pub fn alphabet() -> Alphabet {
    Alphabet::new(b"ACGTacgt")
}

/// The DNA alphabet including N (uppercase and lowercase).
pub fn n_alphabet() -> Alphabet {
    Alphabet::new(b"ACGTNacgtn")
}

/// The IUPAC DNA alphabet (uppercase and lowercase).
pub fn iupac_alphabet() -> Alphabet {
    Alphabet::new(b"ACGTURYSWKMBDHVNacgturyswkmbdhvn")
}

lazy_static! {
    static ref COMPLEMENT: Vec<u8> = {
        let mut comp = Vec::new();
        comp.resize(256, 0);
        for (v, mut a) in comp.iter_mut().enumerate() {
            *a = v as u8;
        }
        for (&a, &b) in b"AGCTYRWSKMDVHBN".iter().zip(b"TCGARYWSMKHBDVN".iter()) {
            comp[a as usize] = b;
            comp[a as usize + 32] = b + 32;  // lowercase variants
        }
        comp
    };
}

/// Return complement of given DNA alphabet character (IUPAC alphabet supported).
pub fn complement(a: u8) -> u8 {
    COMPLEMENT[a as usize]
}

/// Calculate reverse complement of given text (IUPAC alphabet supported).
pub fn revcomp<'a, T: IntoTextIterator<'a>>(text: T) -> Vec<u8>
    where T::IntoIter: DoubleEndedIterator
{
    text.into_iter()
        .rev()
        .map(|&a| complement(a))
        .collect()
}
<commit_msg>Format with rustfmt<commit_after>
// Copyright 2014-2015 Johannes Köster, Peer Aramillo Irizar.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of the DNA alphabet.
//!
//! # Example
//!
//! ```
//! use bio::alphabets;
//! let alphabet = alphabets::dna::alphabet();
//! assert!(alphabet.is_word(b"GATTACA"));
//! assert!(alphabet.is_word(b"gattaca"));
//! assert!(!alphabet.is_word(b"ACGU"));
//! ```

use alphabets::Alphabet;
use utils::IntoTextIterator;

/// The DNA alphabet (uppercase and lowercase).
pub fn alphabet() -> Alphabet {
    Alphabet::new(b"ACGTacgt")
}

/// The DNA alphabet including N (uppercase and lowercase).
pub fn n_alphabet() -> Alphabet {
    Alphabet::new(b"ACGTNacgtn")
}

/// The IUPAC DNA alphabet (uppercase and lowercase).
pub fn iupac_alphabet() -> Alphabet {
    Alphabet::new(b"ACGTURYSWKMBDHVNacgturyswkmbdhvn")
}

lazy_static! {
    static ref COMPLEMENT: Vec<u8> = {
        let mut comp = Vec::new();
        comp.resize(256, 0);
        for (v, mut a) in comp.iter_mut().enumerate() {
            *a = v as u8;
        }
        for (&a, &b) in b"AGCTYRWSKMDVHBN".iter().zip(b"TCGARYWSMKHBDVN".iter()) {
            comp[a as usize] = b;
            comp[a as usize + 32] = b + 32;  // lowercase variants
        }
        comp
    };
}

/// Return complement of given DNA alphabet character (IUPAC alphabet supported).
pub fn complement(a: u8) -> u8 {
    COMPLEMENT[a as usize]
}

/// Calculate reverse complement of given text (IUPAC alphabet supported).
pub fn revcomp<'a, T: IntoTextIterator<'a>>(text: T) -> Vec<u8>
    where T::IntoIter: DoubleEndedIterator
{
    text.into_iter().rev().map(|&a| complement(a)).collect()
}
<|endoftext|>


<commit_before>// Copyright 2018 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.

/// For all the linkers we support, and information they might
/// need out of the shared crate context before we get rid of it.

use rustc::session::{Session, config};
use rustc::session::search_paths::PathKind;
use rustc::middle::dependency_format::Linkage;
use rustc::middle::cstore::LibSource;
use rustc_target::spec::LinkerFlavor;
use rustc::hir::def_id::CrateNum;

use super::command::Command;
use CrateInfo;

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

pub fn remove(sess: &Session, path: &Path) {
    if let Err(e) = fs::remove_file(path) {
        sess.err(&format!("failed to remove {}: {}",
                          path.display(),
                          e));
    }
}

// The third parameter is for env vars, used on windows to set up the
// path for MSVC to find its DLLs, and gcc to find its bundled
// toolchain
pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathBuf, Command) {
    let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe");

    // If our linker looks like a batch script on Windows then to execute this
    // we'll need to spawn `cmd` explicitly. This is primarily done to handle
    // emscripten where the linker is `emcc.bat` and needs to be spawned as
    // `cmd /c emcc.bat ...`.
    //
    // This worked historically but is needed manually since #42436 (regression
    // was tagged as #42791) and some more info can be found on #44443 for
    // emscripten itself.
    let mut cmd = match linker.to_str() {
        Some(linker) if cfg!(windows) && linker.ends_with(".bat") => Command::bat_script(linker),
        _ => match flavor {
            LinkerFlavor::Lld(f) => Command::lld(linker, f),
            LinkerFlavor::Msvc
                if sess.opts.cg.linker.is_none() && sess.target.target.options.linker.is_none() =>
            {
                Command::new(msvc_tool.as_ref().map(|t| t.path()).unwrap_or(linker))
            },
            _ => Command::new(linker),
        }
    };

    // The compiler's sysroot often has some bundled tools, so add it to the
    // PATH for the child.
    let mut new_path = sess.host_filesearch(PathKind::All)
                           .get_tools_search_paths();
    let mut msvc_changed_path = false;
    if sess.target.target.options.is_like_msvc {
        if let Some(ref tool) = msvc_tool {
            cmd.args(tool.args());
            for &(ref k, ref v) in tool.env() {
                if k == "PATH" {
                    new_path.extend(env::split_paths(v));
                    msvc_changed_path = true;
                } else {
                    cmd.env(k, v);
                }
            }
        }
    }

    if !msvc_changed_path {
        if let Some(path) = env::var_os("PATH") {
            new_path.extend(env::split_paths(&path));
        }
    }
    cmd.env("PATH", env::join_paths(new_path).unwrap());

    (linker.to_path_buf(), cmd)
}

pub fn each_linked_rlib(sess: &Session,
                               info: &CrateInfo,
                               f: &mut dyn FnMut(CrateNum, &Path)) -> Result<(), String> {
    let crates = info.used_crates_static.iter();
    let fmts = sess.dependency_formats.borrow();
    let fmts = fmts.get(&config::CrateType::Executable)
                   .or_else(|| fmts.get(&config::CrateType::Staticlib))
                   .or_else(|| fmts.get(&config::CrateType::Cdylib))
                   .or_else(|| fmts.get(&config::CrateType::ProcMacro));
    let fmts = match fmts {
        Some(f) => f,
        None => return Err("could not find formats for rlibs".to_string())
    };
    for &(cnum, ref path) in crates {
        match fmts.get(cnum.as_usize() - 1) {
            Some(&Linkage::NotLinked) |
            Some(&Linkage::IncludedFromDylib) => continue,
            Some(_) => {}
            None => return Err("could not find formats for rlibs".to_string())
        }
        let name = &info.crate_name[&cnum];
        let path = match *path {
            LibSource::Some(ref p) => p,
            LibSource::MetadataOnly => {
                return Err(format!("could not find rlib for: `{}`, found rmeta (metadata) file",
                                   name))
            }
            LibSource::None => {
                return Err(format!("could not find rlib for: `{}`", name))
            }
        };
        f(cnum, &path);
    }
    Ok(())
}

/// Returns a boolean indicating whether the specified crate should be ignored
/// during LTO.
///
/// Crates ignored during LTO are not lumped together in the "massive object
/// file" that we create and are linked in their normal rlib states. See
/// comments below for what crates do not participate in LTO.
///
/// It's unusual for a crate to not participate in LTO. Typically only
/// compiler-specific and unstable crates have a reason to not participate in
/// LTO.
pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool {
    // If our target enables builtin function lowering in LLVM then the
    // crates providing these functions don't participate in LTO (e.g.
    // no_builtins or compiler builtins crates).
    !sess.target.target.options.no_builtins &&
        (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum))
}

pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
    fn infer_from(
        sess: &Session,
        linker: Option<PathBuf>,
        flavor: Option<LinkerFlavor>,
    ) -> Option<(PathBuf, LinkerFlavor)> {
        match (linker, flavor) {
            (Some(linker), Some(flavor)) => Some((linker, flavor)),
            // only the linker flavor is known; use the default linker for the selected flavor
            (None, Some(flavor)) => Some((PathBuf::from(match flavor {
                LinkerFlavor::Em  => if cfg!(windows) { "emcc.bat" } else { "emcc" },
                LinkerFlavor::Gcc => "cc",
                LinkerFlavor::Ld => "ld",
                LinkerFlavor::Msvc => "link.exe",
                LinkerFlavor::Lld(_) => "lld",
            }), flavor)),
            (Some(linker), None) => {
                let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {
                    sess.fatal("couldn't extract file stem from specified linker");
                }).to_owned();

                let flavor = if stem == "emcc" {
                    LinkerFlavor::Em
                } else if stem == "gcc" || stem.ends_with("-gcc") {
                    LinkerFlavor::Gcc
                } else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") {
                    LinkerFlavor::Ld
                } else if stem == "link" || stem == "lld-link" {
                    LinkerFlavor::Msvc
                } else if stem == "lld" || stem == "rust-lld" {
                    LinkerFlavor::Lld(sess.target.target.options.lld_flavor)
                } else {
                    // fall back to the value in the target spec
                    sess.target.target.linker_flavor
                };

                Some((linker, flavor))
            },
            (None, None) => None,
        }
    }

    // linker and linker flavor specified via command line have precedence over what the target
    // specification specifies
    if let Some(ret) = infer_from(
        sess,
        sess.opts.cg.linker.clone(),
        sess.opts.debugging_opts.linker_flavor,
    ) {
        return ret;
    }

    if let Some(ret) = infer_from(
        sess,
        sess.target.target.options.linker.clone().map(PathBuf::from),
        Some(sess.target.target.linker_flavor),
    ) {
        return ret;
    }

    bug!("Not enough information provided to determine how to invoke the linker");
}
<commit_msg>Rollup merge of #56349 - davidtwco:issue-55396-inference-extension, r=nagisa<commit_after>// Copyright 2018 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.

/// For all the linkers we support, and information they might
/// need out of the shared crate context before we get rid of it.

use rustc::session::{Session, config};
use rustc::session::search_paths::PathKind;
use rustc::middle::dependency_format::Linkage;
use rustc::middle::cstore::LibSource;
use rustc_target::spec::LinkerFlavor;
use rustc::hir::def_id::CrateNum;

use super::command::Command;
use CrateInfo;

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

pub fn remove(sess: &Session, path: &Path) {
    if let Err(e) = fs::remove_file(path) {
        sess.err(&format!("failed to remove {}: {}",
                          path.display(),
                          e));
    }
}

// The third parameter is for env vars, used on windows to set up the
// path for MSVC to find its DLLs, and gcc to find its bundled
// toolchain
pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathBuf, Command) {
    let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe");

    // If our linker looks like a batch script on Windows then to execute this
    // we'll need to spawn `cmd` explicitly. This is primarily done to handle
    // emscripten where the linker is `emcc.bat` and needs to be spawned as
    // `cmd /c emcc.bat ...`.
    //
    // This worked historically but is needed manually since #42436 (regression
    // was tagged as #42791) and some more info can be found on #44443 for
    // emscripten itself.
    let mut cmd = match linker.to_str() {
        Some(linker) if cfg!(windows) && linker.ends_with(".bat") => Command::bat_script(linker),
        _ => match flavor {
            LinkerFlavor::Lld(f) => Command::lld(linker, f),
            LinkerFlavor::Msvc
                if sess.opts.cg.linker.is_none() && sess.target.target.options.linker.is_none() =>
            {
                Command::new(msvc_tool.as_ref().map(|t| t.path()).unwrap_or(linker))
            },
            _ => Command::new(linker),
        }
    };

    // The compiler's sysroot often has some bundled tools, so add it to the
    // PATH for the child.
    let mut new_path = sess.host_filesearch(PathKind::All)
                           .get_tools_search_paths();
    let mut msvc_changed_path = false;
    if sess.target.target.options.is_like_msvc {
        if let Some(ref tool) = msvc_tool {
            cmd.args(tool.args());
            for &(ref k, ref v) in tool.env() {
                if k == "PATH" {
                    new_path.extend(env::split_paths(v));
                    msvc_changed_path = true;
                } else {
                    cmd.env(k, v);
                }
            }
        }
    }

    if !msvc_changed_path {
        if let Some(path) = env::var_os("PATH") {
            new_path.extend(env::split_paths(&path));
        }
    }
    cmd.env("PATH", env::join_paths(new_path).unwrap());

    (linker.to_path_buf(), cmd)
}

pub fn each_linked_rlib(sess: &Session,
                               info: &CrateInfo,
                               f: &mut dyn FnMut(CrateNum, &Path)) -> Result<(), String> {
    let crates = info.used_crates_static.iter();
    let fmts = sess.dependency_formats.borrow();
    let fmts = fmts.get(&config::CrateType::Executable)
                   .or_else(|| fmts.get(&config::CrateType::Staticlib))
                   .or_else(|| fmts.get(&config::CrateType::Cdylib))
                   .or_else(|| fmts.get(&config::CrateType::ProcMacro));
    let fmts = match fmts {
        Some(f) => f,
        None => return Err("could not find formats for rlibs".to_string())
    };
    for &(cnum, ref path) in crates {
        match fmts.get(cnum.as_usize() - 1) {
            Some(&Linkage::NotLinked) |
            Some(&Linkage::IncludedFromDylib) => continue,
            Some(_) => {}
            None => return Err("could not find formats for rlibs".to_string())
        }
        let name = &info.crate_name[&cnum];
        let path = match *path {
            LibSource::Some(ref p) => p,
            LibSource::MetadataOnly => {
                return Err(format!("could not find rlib for: `{}`, found rmeta (metadata) file",
                                   name))
            }
            LibSource::None => {
                return Err(format!("could not find rlib for: `{}`", name))
            }
        };
        f(cnum, &path);
    }
    Ok(())
}

/// Returns a boolean indicating whether the specified crate should be ignored
/// during LTO.
///
/// Crates ignored during LTO are not lumped together in the "massive object
/// file" that we create and are linked in their normal rlib states. See
/// comments below for what crates do not participate in LTO.
///
/// It's unusual for a crate to not participate in LTO. Typically only
/// compiler-specific and unstable crates have a reason to not participate in
/// LTO.
pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool {
    // If our target enables builtin function lowering in LLVM then the
    // crates providing these functions don't participate in LTO (e.g.
    // no_builtins or compiler builtins crates).
    !sess.target.target.options.no_builtins &&
        (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum))
}

pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
    fn infer_from(
        sess: &Session,
        linker: Option<PathBuf>,
        flavor: Option<LinkerFlavor>,
    ) -> Option<(PathBuf, LinkerFlavor)> {
        match (linker, flavor) {
            (Some(linker), Some(flavor)) => Some((linker, flavor)),
            // only the linker flavor is known; use the default linker for the selected flavor
            (None, Some(flavor)) => Some((PathBuf::from(match flavor {
                LinkerFlavor::Em  => if cfg!(windows) { "emcc.bat" } else { "emcc" },
                LinkerFlavor::Gcc => "cc",
                LinkerFlavor::Ld => "ld",
                LinkerFlavor::Msvc => "link.exe",
                LinkerFlavor::Lld(_) => "lld",
            }), flavor)),
            (Some(linker), None) => {
                let stem = if linker.extension().and_then(|ext| ext.to_str()) == Some("exe") {
                    linker.file_stem().and_then(|stem| stem.to_str())
                } else {
                    linker.to_str()
                }.unwrap_or_else(|| {
                    sess.fatal("couldn't extract file stem from specified linker");
                }).to_owned();

                let flavor = if stem == "emcc" {
                    LinkerFlavor::Em
                } else if stem == "gcc" || stem.ends_with("-gcc") {
                    LinkerFlavor::Gcc
                } else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") {
                    LinkerFlavor::Ld
                } else if stem == "link" || stem == "lld-link" {
                    LinkerFlavor::Msvc
                } else if stem == "lld" || stem == "rust-lld" {
                    LinkerFlavor::Lld(sess.target.target.options.lld_flavor)
                } else {
                    // fall back to the value in the target spec
                    sess.target.target.linker_flavor
                };

                Some((linker, flavor))
            },
            (None, None) => None,
        }
    }

    // linker and linker flavor specified via command line have precedence over what the target
    // specification specifies
    if let Some(ret) = infer_from(
        sess,
        sess.opts.cg.linker.clone(),
        sess.opts.debugging_opts.linker_flavor,
    ) {
        return ret;
    }

    if let Some(ret) = infer_from(
        sess,
        sess.target.target.options.linker.clone().map(PathBuf::from),
        Some(sess.target.target.linker_flavor),
    ) {
        return ret;
    }

    bug!("Not enough information provided to determine how to invoke the linker");
}
<|endoftext|>


<commit_before>extern crate serialize;

use std::collections::HashMap;
use std::old_io::process::Command;
use std::old_io::{File, TempDir};
use std::old_io::fs;

fn main() {
    let (args, passthrough) = parse_arguments();

    // Find all the native shared libraries that exist in the target directory.
    let native_shared_libs = find_native_libs(&args);

    // getting the path from the ANDROID_HOME env
    let sdk_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "ANDROID_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the ANDROID_HOME environment variable");

    // hardcoding ndk path
    let ndk_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "NDK_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the NDK_HOME environment variable");

    // hardcoding ndk path
    let standalone_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "NDK_STANDALONE")
        .map(|(_, v)| Path::new(v)).unwrap_or(Path::new("/opt/ndk_standalone"));

    // creating the build directory that will contain all the necessary files to create teh apk
    let directory = build_directory(&sdk_path, args.output.filestem_str().unwrap(), &native_shared_libs);

    // Copy the additional native libs into the libs directory.
    for (name, path) in native_shared_libs.iter() {
        fs::copy(path, &directory.path().join("libs").join("armeabi").join(name)).unwrap();
    }

    // compiling android_native_app_glue.c
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .arg(ndk_path.join("sources").join("android").join("native_app_glue").join("android_native_app_glue.c"))
        .arg("-c")
        .arg("-o").arg(directory.path().join("android_native_app_glue.o"))
        .stdout(std::old_io::process::InheritFd(1)).stderr(std::old_io::process::InheritFd(2))
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling gcc to link to a shared object
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .args(passthrough.as_slice())
        .arg(directory.path().join("android_native_app_glue.o"))
        .arg("-o").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
        .arg("-shared")
        .arg("-Wl,-E")
        .stdout(std::old_io::process::InheritFd(1))
        .stderr(std::old_io::process::InheritFd(2))//.cwd(directory.path())
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling objdump to make sure that our object has `ANativeActivity_onCreate`
    // TODO: not working
    /*{
        let mut process =
            Command::new(standalone_path.join("bin").join("arm-linux-androideabi-objdump"))
            .arg("-x").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
            .stderr(std::old_io::process::InheritFd(2))
            .spawn().unwrap();

        // TODO: use UFCS instead
        fn by_ref<'a, T: Reader>(r: &'a mut T) -> std::old_io::RefReader<'a, T> { r.by_ref() };

        let stdout = process.stdout.as_mut().unwrap();
        let mut stdout = std::old_io::BufferedReader::new(by_ref(stdout));

        if stdout.lines().filter_map(|l| l.ok())
            .find(|line| line.as_slice().contains("ANativeActivity_onCreate")).is_none()
        {
            println!("Error: the output file doesn't contain ANativeActivity_onCreate");
            std::os::set_exit_status(1);
            return;
        }
    }*/

    // executing ant
    if Command::new("ant").arg("debug").stdout(std::old_io::process::InheritFd(1))
        .stderr(std::old_io::process::InheritFd(2)).cwd(directory.path())
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing ant debug");
        std::os::set_exit_status(1);
        return;
    }

    // copying apk file to the requested output
    fs::copy(&directory.path().join("bin").join("rust-android-debug.apk"),
        &args.output).unwrap();
}

struct Args {
    output: Path,
}

fn parse_arguments() -> (Args, Vec<String>) {
    let mut result_output = None;
    let mut result_passthrough = Vec::new();

    let args = std::os::args();
    let mut args = args.into_iter().skip(1);

    loop {
        let arg = match args.next() {
            None => return (
                Args {
                    output: result_output.expect("Could not find -o argument")
                },
                result_passthrough
            ),
            Some(arg) => arg
        };

        match arg.as_slice() {
            "-o" => {
                result_output = Some(Path::new(args.next().expect("-o must be followed by the output name")));
            },
            _ => result_passthrough.push(arg)
        };
    }
}

fn find_native_libs(args: &Args) -> HashMap<String, Path> {
    let base_path = args.output.dir_path().join("native");
    let mut native_shared_libs: HashMap<String, Path> = HashMap::new();

    fs::walk_dir(&base_path).and_then(|mut dirs| {
        for dir in dirs {
            fs::readdir(&dir).and_then(|paths| {
                for path in paths.iter() {
                    match (path.filename_str(), path.extension_str()) {
                        (Some(filename), Some(ext)) => {
                            if filename.starts_with("lib") && ext == "so" {
                                native_shared_libs.insert(filename.to_string(), path.clone());
                            }
                        }
                        _ => {}
                    }
                }
                Ok(())
            }).ok();
        }
        Ok(())
    }).ok();

    native_shared_libs
}

fn build_directory(sdk_dir: &Path, crate_name: &str, libs: &HashMap<String, Path>) -> TempDir {
    use std::old_io::fs;

    let build_directory = TempDir::new("android-rs-glue-rust-to-apk")
        .ok().expect("Could not create temporary build directory");

    let activity_name = if libs.len() > 0 {
        let src_path = build_directory.path().join("src/rust/glutin");
        fs::mkdir_recursive(&src_path, std::old_io::USER_RWX).unwrap();

        File::create(&src_path.join("MainActivity.java")).unwrap()
            .write_str(java_src(libs).as_slice())
            .unwrap();

        "rust.glutin.MainActivity"
    } else {
        "android.app.NativeActivity"
    };

    File::create(&build_directory.path().join("AndroidManifest.xml")).unwrap()
        .write_str(build_manifest(crate_name, activity_name).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("build.xml")).unwrap()
        .write_str(build_build_xml().as_slice())
        .unwrap();

    File::create(&build_directory.path().join("local.properties")).unwrap()
        .write_str(build_local_properties(sdk_dir).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("project.properties")).unwrap()
        .write_str(build_project_properties().as_slice())
        .unwrap();

    {
        let libs_path = build_directory.path().join("libs").join("armeabi");
        fs::mkdir_recursive(&libs_path, std::old_io::USER_RWX).unwrap();
    }

    build_directory
}

fn java_src(libs: &HashMap<String, Path>) -> String {
    let mut libs_string = "".to_string();

    for (name, _) in libs.iter() {
        // Strip off the 'lib' prefix and ".so" suffix. This is safe since libs only get added
        // to the hash map if they start with lib.
        let line = format!("        System.loadLibrary(\"{}\");\n", name.slice(3, name.len()-3));
        libs_string.push_str(line.as_slice());
    }

    format!(r#"package rust.glutin;

public class MainActivity extends android.app.NativeActivity {{
    static {{
        {0}
    }}
}}"#, libs_string)
}

fn build_manifest(crate_name: &str, activity_name: &str) -> String {
    format!(r#"<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.native_activity"
        android:versionCode="1"
        android:versionName="1.0">

    <uses-sdk android:minSdkVersion="9" />

    <uses-feature android:glEsVersion="0x00020000" android:required="true"></uses-feature>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application android:label="{0}">
        <activity android:name="{1}"
                android:label="{0}"
                android:configChanges="orientation|keyboardHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
<!-- END_INCLUDE(manifest) -->
"#, crate_name, activity_name)
}

fn build_build_xml() -> String {
    format!(r#"<?xml version="1.0" encoding="UTF-8"?>
<project name="rust-android" default="help">
    <property file="local.properties" />
    <loadproperties srcFile="project.properties" />
    <import file="custom_rules.xml" optional="true" />
    <import file="${{sdk.dir}}/tools/ant/build.xml" />
</project>
"#)
}

fn build_local_properties(sdk_dir: &Path) -> String {
    use std::os;
    format!(r"sdk.dir={}", os::make_absolute(sdk_dir).unwrap().display())
}

fn build_project_properties() -> String {
    format!(r"target=android-18")
}
<commit_msg>Updated for Rust eaf4c5c78 2015-02-02 15:04:54<commit_after>#![feature(path, core, io, os, rustc_private)]

extern crate serialize;

use std::collections::HashMap;
use std::old_io::process::Command;
use std::old_io::{File, TempDir};
use std::old_io::fs;

fn main() {
    let (args, passthrough) = parse_arguments();

    // Find all the native shared libraries that exist in the target directory.
    let native_shared_libs = find_native_libs(&args);

    // getting the path from the ANDROID_HOME env
    let sdk_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "ANDROID_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the ANDROID_HOME environment variable");

    // hardcoding ndk path
    let ndk_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "NDK_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the NDK_HOME environment variable");

    // hardcoding ndk path
    let standalone_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "NDK_STANDALONE")
        .map(|(_, v)| Path::new(v)).unwrap_or(Path::new("/opt/ndk_standalone"));

    // creating the build directory that will contain all the necessary files to create teh apk
    let directory = build_directory(&sdk_path, args.output.filestem_str().unwrap(), &native_shared_libs);

    // Copy the additional native libs into the libs directory.
    for (name, path) in native_shared_libs.iter() {
        fs::copy(path, &directory.path().join("libs").join("armeabi").join(name)).unwrap();
    }

    // compiling android_native_app_glue.c
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .arg(ndk_path.join("sources").join("android").join("native_app_glue").join("android_native_app_glue.c"))
        .arg("-c")
        .arg("-o").arg(directory.path().join("android_native_app_glue.o"))
        .stdout(std::old_io::process::InheritFd(1)).stderr(std::old_io::process::InheritFd(2))
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling gcc to link to a shared object
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .args(passthrough.as_slice())
        .arg(directory.path().join("android_native_app_glue.o"))
        .arg("-o").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
        .arg("-shared")
        .arg("-Wl,-E")
        .stdout(std::old_io::process::InheritFd(1))
        .stderr(std::old_io::process::InheritFd(2))//.cwd(directory.path())
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling objdump to make sure that our object has `ANativeActivity_onCreate`
    // TODO: not working
    /*{
        let mut process =
            Command::new(standalone_path.join("bin").join("arm-linux-androideabi-objdump"))
            .arg("-x").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
            .stderr(std::old_io::process::InheritFd(2))
            .spawn().unwrap();

        // TODO: use UFCS instead
        fn by_ref<'a, T: Reader>(r: &'a mut T) -> std::old_io::RefReader<'a, T> { r.by_ref() };

        let stdout = process.stdout.as_mut().unwrap();
        let mut stdout = std::old_io::BufferedReader::new(by_ref(stdout));

        if stdout.lines().filter_map(|l| l.ok())
            .find(|line| line.as_slice().contains("ANativeActivity_onCreate")).is_none()
        {
            println!("Error: the output file doesn't contain ANativeActivity_onCreate");
            std::os::set_exit_status(1);
            return;
        }
    }*/

    // executing ant
    if Command::new("ant").arg("debug").stdout(std::old_io::process::InheritFd(1))
        .stderr(std::old_io::process::InheritFd(2)).cwd(directory.path())
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing ant debug");
        std::os::set_exit_status(1);
        return;
    }

    // copying apk file to the requested output
    fs::copy(&directory.path().join("bin").join("rust-android-debug.apk"),
        &args.output).unwrap();
}

struct Args {
    output: Path,
}

fn parse_arguments() -> (Args, Vec<String>) {
    let mut result_output = None;
    let mut result_passthrough = Vec::new();

    let args = std::os::args();
    let mut args = args.into_iter().skip(1);

    loop {
        let arg = match args.next() {
            None => return (
                Args {
                    output: result_output.expect("Could not find -o argument")
                },
                result_passthrough
            ),
            Some(arg) => arg
        };

        match arg.as_slice() {
            "-o" => {
                result_output = Some(Path::new(args.next().expect("-o must be followed by the output name")));
            },
            _ => result_passthrough.push(arg)
        };
    }
}

fn find_native_libs(args: &Args) -> HashMap<String, Path> {
    let base_path = args.output.dir_path().join("native");
    let mut native_shared_libs: HashMap<String, Path> = HashMap::new();

    fs::walk_dir(&base_path).and_then(|dirs| {
        for dir in dirs {
            fs::readdir(&dir).and_then(|paths| {
                for path in paths.iter() {
                    match (path.filename_str(), path.extension_str()) {
                        (Some(filename), Some(ext)) => {
                            if filename.starts_with("lib") && ext == "so" {
                                native_shared_libs.insert(filename.to_string(), path.clone());
                            }
                        }
                        _ => {}
                    }
                }
                Ok(())
            }).ok();
        }
        Ok(())
    }).ok();

    native_shared_libs
}

fn build_directory(sdk_dir: &Path, crate_name: &str, libs: &HashMap<String, Path>) -> TempDir {
    use std::old_io::fs;

    let build_directory = TempDir::new("android-rs-glue-rust-to-apk")
        .ok().expect("Could not create temporary build directory");

    let activity_name = if libs.len() > 0 {
        let src_path = build_directory.path().join("src/rust/glutin");
        fs::mkdir_recursive(&src_path, std::old_io::USER_RWX).unwrap();

        File::create(&src_path.join("MainActivity.java")).unwrap()
            .write_str(java_src(libs).as_slice())
            .unwrap();

        "rust.glutin.MainActivity"
    } else {
        "android.app.NativeActivity"
    };

    File::create(&build_directory.path().join("AndroidManifest.xml")).unwrap()
        .write_str(build_manifest(crate_name, activity_name).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("build.xml")).unwrap()
        .write_str(build_build_xml().as_slice())
        .unwrap();

    File::create(&build_directory.path().join("local.properties")).unwrap()
        .write_str(build_local_properties(sdk_dir).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("project.properties")).unwrap()
        .write_str(build_project_properties().as_slice())
        .unwrap();

    {
        let libs_path = build_directory.path().join("libs").join("armeabi");
        fs::mkdir_recursive(&libs_path, std::old_io::USER_RWX).unwrap();
    }

    build_directory
}

fn java_src(libs: &HashMap<String, Path>) -> String {
    let mut libs_string = "".to_string();

    for (name, _) in libs.iter() {
        // Strip off the 'lib' prefix and ".so" suffix. This is safe since libs only get added
        // to the hash map if they start with lib.
        let line = format!("        System.loadLibrary(\"{}\");\n", name.slice(3, name.len()-3));
        libs_string.push_str(line.as_slice());
    }

    format!(r#"package rust.glutin;

public class MainActivity extends android.app.NativeActivity {{
    static {{
        {0}
    }}
}}"#, libs_string)
}

fn build_manifest(crate_name: &str, activity_name: &str) -> String {
    format!(r#"<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.native_activity"
        android:versionCode="1"
        android:versionName="1.0">

    <uses-sdk android:minSdkVersion="9" />

    <uses-feature android:glEsVersion="0x00020000" android:required="true"></uses-feature>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application android:label="{0}">
        <activity android:name="{1}"
                android:label="{0}"
                android:configChanges="orientation|keyboardHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
<!-- END_INCLUDE(manifest) -->
"#, crate_name, activity_name)
}

fn build_build_xml() -> String {
    format!(r#"<?xml version="1.0" encoding="UTF-8"?>
<project name="rust-android" default="help">
    <property file="local.properties" />
    <loadproperties srcFile="project.properties" />
    <import file="custom_rules.xml" optional="true" />
    <import file="${{sdk.dir}}/tools/ant/build.xml" />
</project>
"#)
}

fn build_local_properties(sdk_dir: &Path) -> String {
    use std::os;
    format!(r"sdk.dir={}", os::make_absolute(sdk_dir).unwrap().display())
}

fn build_project_properties() -> String {
    format!(r"target=android-18")
}
<|endoftext|>