<commit_before>use std::os;
use std::collections::{HashSet, HashMap};
use std::env;
use std::fs;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use std::path::Path;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use cargo::core::Workspace;
use cargo::util::errors::CargoError;
use cargo::util::errors::human;
use cargo::util::process_builder::process;

use config::Config;

pub struct BuildResult {
    /// The absolute path where the apk is located.
    pub apk_path: PathBuf,
}

pub fn build(workspace: &Workspace, config: &Config) -> Result<BuildResult, Box<CargoError>> {
    // First we detect whether `ant` works.
    match Command::new(&config.ant_command).arg("-version").stdout(Stdio::null()).status() {
        Ok(s) if s.success() => (),
        _ => {
            return Err(human("Could not execute `ant`. Did you install it?").into());
        }
    }

    // Building the `android-artifacts` directory that will contain all the artifacts.
    // FIXME: don't use into_path_unlocked() but pass a Cargo::Filesystem everywhere
    let android_artifacts_dir = workspace.target_dir().join("android-artifacts").into_path_unlocked();
    build_android_artifacts_dir(&android_artifacts_dir, &config);

    let mut abi_libs: HashMap<&str, Vec<String>> = HashMap::new();

    for build_target in config.build_targets.iter() {
        let build_target_dir = android_artifacts_dir.join(build_target);

        // Finding the tools in the NDK.
        let (gcc_path, ar_path) = {
            let host_os = if cfg!(target_os = "windows") { "windows" }
                       else if cfg!(target_os = "linux") { "linux" }
                       else if cfg!(target_os = "macos") { "darwin" }
                       else { panic!("Unknown or incompatible host OS") };

            let target_arch = if build_target.starts_with("arm") { "arm-linux-androideabi" }
                       else if build_target.starts_with("aarch64") { "aarch64-linux-android" }
                       else if build_target.starts_with("i") { "x86" }
                       else if build_target.starts_with("x86_64") { "x86_64" }
                       else if build_target.starts_with("mipsel") { "mipsel-linux-android" }
                       // TODO: mips64
                       else { panic!("Unknown or incompatible build target: {}", build_target) };

            // Looks like the tools don't always share the prefix of the target arch
            // Just a macos issue?
            let tool_prefix = if build_target.starts_with("arm") { "arm-linux-androideabi" }
                       else if build_target.starts_with("aarch64") { "aarch64-linux-android" }
                       else if build_target.starts_with("i") { "i686-linux-android" }
                       else if build_target.starts_with("x86_64") { "x86_64-linux-android" }
                       else if build_target.starts_with("mipsel") { "mipsel-linux-android" }
                       // TODO: mips64
                       else { panic!("Unknown or incompatible build target: {}", build_target) };

            let base = config.ndk_path.join(format!("toolchains/{}-4.9/prebuilt/{}-x86_64", target_arch, host_os));
            (base.join(format!("bin/{}-gcc", tool_prefix)), base.join(format!("bin/{}-ar", tool_prefix)))
        };

        let gcc_sysroot = {
            let arch = if build_target.starts_with("arm") { "arm" }
                       else if build_target.starts_with("aarch64") { "arm64" }
                       else if build_target.starts_with("i") { "x86" }
                       else if build_target.starts_with("x86_64") { "x86_64" }
                       else if build_target.starts_with("mips") { "mips" }
                       // TODO: mips64
                       else { panic!("Unknown or incompatible build target: {}", build_target) };
            config.ndk_path.join(format!("platforms/android-{v}/arch-{a}",
                                         v = config.android_version, a = arch))
        };

        // Create android cpu abi name
        let abi = if build_target.starts_with("arm") { "armeabi" }
                  // TODO: armeabi-v7a
                  else if build_target.starts_with("aarch64") { "arm64-v8a" }
                  else if build_target.starts_with("i") { "x86" }
                  else if build_target.starts_with("x86_64") { "x86_64" }
                  else if build_target.starts_with("mips") { "mips" }
                  // TODO: mips64
                  else { panic!("Unknown or incompatible build target: {}", build_target) };

        // Compiling android_native_app_glue.c
        {
            workspace.config().shell().say("Compiling android_native_app_glue.c", 10);
            let mut cmd = process(&gcc_path);
            cmd.arg(config.ndk_path.join("sources/android/native_app_glue/android_native_app_glue.c"))
               .arg("-c");
            if config.release {
                cmd.arg("-O3");
            }
            cmd.arg("-o").arg(build_target_dir.join("android_native_app_glue.o"))
               .arg("--sysroot").arg(&gcc_sysroot)
               .exec()?;
        }

        // Compiling injected-glue
        let injected_glue_lib = {
            workspace.config().shell().say("Compiling injected-glue", 10);
            let mut cmd = process("rustc");
            cmd.arg(android_artifacts_dir.join("injected-glue/lib.rs"))
               .arg("--crate-type").arg("rlib");
            if config.release {
                cmd.arg("-C")
                   .arg("opt-level=3");
            }
            cmd.arg("--crate-name").arg("cargo_apk_injected_glue")
               .arg("--target").arg(build_target)
               .arg("--out-dir").arg(&build_target_dir);

            cmd.exec()?;

            let stdout = cmd.arg("--print").arg("file-names")
                            .exec_with_output()?;
            let stdout = String::from_utf8(stdout.stdout).unwrap();

            build_target_dir.join(stdout.lines().next().unwrap())
        };

        // Compiling glue_obj.rs
        {
            let mut file = File::create(build_target_dir.join("glue_obj.rs")).unwrap();
            file.write_all(&include_bytes!("../../glue_obj.rs")[..]).unwrap();
        }
        
        {
            workspace.config().shell().say("Compiling glue_obj", 10);
            let mut cmd = process("rustc");
            cmd.arg(build_target_dir.join("glue_obj.rs"))
               .arg("--crate-type").arg("staticlib");
            if config.release {
                cmd.arg("-C")
                   .arg("opt-level=3");
            }
            cmd.arg("--target").arg(build_target)
               .arg("--extern").arg(format!("cargo_apk_injected_glue={}", injected_glue_lib.to_string_lossy()))
               .arg("--emit").arg("obj")
               .arg("-o").arg(build_target_dir.join("glue_obj.o"))
               .exec()?;
        }

        // Directory where we will put the native libraries for ant to pick them up.
        let native_libraries_dir = android_artifacts_dir.join(format!("build/libs/{}", abi));

        if fs::metadata(&native_libraries_dir).is_err() {
            fs::DirBuilder::new().recursive(true).create(&native_libraries_dir).unwrap();<commit_msg>Use the configured rustc instead of rustc in path<commit_after>use std::os;
use std::collections::{HashSet, HashMap};
use std::env;
use std::fs;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use std::path::Path;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use cargo::core::Workspace;
use cargo::util::errors::CargoError;
use cargo::util::errors::human;
use cargo::util::process_builder::process;

use config::Config;

pub struct BuildResult {
    /// The absolute path where the apk is located.
    pub apk_path: PathBuf,
}

pub fn build(workspace: &Workspace, config: &Config) -> Result<BuildResult, Box<CargoError>> {
    // First we detect whether `ant` works.
    match Command::new(&config.ant_command).arg("-version").stdout(Stdio::null()).status() {
        Ok(s) if s.success() => (),
        _ => {
            return Err(human("Could not execute `ant`. Did you install it?").into());
        }
    }

    // Building the `android-artifacts` directory that will contain all the artifacts.
    // FIXME: don't use into_path_unlocked() but pass a Cargo::Filesystem everywhere
    let android_artifacts_dir = workspace.target_dir().join("android-artifacts").into_path_unlocked();
    build_android_artifacts_dir(&android_artifacts_dir, &config);

    let mut abi_libs: HashMap<&str, Vec<String>> = HashMap::new();

    for build_target in config.build_targets.iter() {
        let build_target_dir = android_artifacts_dir.join(build_target);

        // Finding the tools in the NDK.
        let (gcc_path, ar_path) = {
            let host_os = if cfg!(target_os = "windows") { "windows" }
                       else if cfg!(target_os = "linux") { "linux" }
                       else if cfg!(target_os = "macos") { "darwin" }
                       else { panic!("Unknown or incompatible host OS") };

            let target_arch = if build_target.starts_with("arm") { "arm-linux-androideabi" }
                       else if build_target.starts_with("aarch64") { "aarch64-linux-android" }
                       else if build_target.starts_with("i") { "x86" }
                       else if build_target.starts_with("x86_64") { "x86_64" }
                       else if build_target.starts_with("mipsel") { "mipsel-linux-android" }
                       // TODO: mips64
                       else { panic!("Unknown or incompatible build target: {}", build_target) };

            // Looks like the tools don't always share the prefix of the target arch
            // Just a macos issue?
            let tool_prefix = if build_target.starts_with("arm") { "arm-linux-androideabi" }
                       else if build_target.starts_with("aarch64") { "aarch64-linux-android" }
                       else if build_target.starts_with("i") { "i686-linux-android" }
                       else if build_target.starts_with("x86_64") { "x86_64-linux-android" }
                       else if build_target.starts_with("mipsel") { "mipsel-linux-android" }
                       // TODO: mips64
                       else { panic!("Unknown or incompatible build target: {}", build_target) };

            let base = config.ndk_path.join(format!("toolchains/{}-4.9/prebuilt/{}-x86_64", target_arch, host_os));
            (base.join(format!("bin/{}-gcc", tool_prefix)), base.join(format!("bin/{}-ar", tool_prefix)))
        };

        let gcc_sysroot = {
            let arch = if build_target.starts_with("arm") { "arm" }
                       else if build_target.starts_with("aarch64") { "arm64" }
                       else if build_target.starts_with("i") { "x86" }
                       else if build_target.starts_with("x86_64") { "x86_64" }
                       else if build_target.starts_with("mips") { "mips" }
                       // TODO: mips64
                       else { panic!("Unknown or incompatible build target: {}", build_target) };
            config.ndk_path.join(format!("platforms/android-{v}/arch-{a}",
                                         v = config.android_version, a = arch))
        };

        // Create android cpu abi name
        let abi = if build_target.starts_with("arm") { "armeabi" }
                  // TODO: armeabi-v7a
                  else if build_target.starts_with("aarch64") { "arm64-v8a" }
                  else if build_target.starts_with("i") { "x86" }
                  else if build_target.starts_with("x86_64") { "x86_64" }
                  else if build_target.starts_with("mips") { "mips" }
                  // TODO: mips64
                  else { panic!("Unknown or incompatible build target: {}", build_target) };

        // Compiling android_native_app_glue.c
        {
            workspace.config().shell().say("Compiling android_native_app_glue.c", 10);
            let mut cmd = process(&gcc_path);
            cmd.arg(config.ndk_path.join("sources/android/native_app_glue/android_native_app_glue.c"))
               .arg("-c");
            if config.release {
                cmd.arg("-O3");
            }
            cmd.arg("-o").arg(build_target_dir.join("android_native_app_glue.o"))
               .arg("--sysroot").arg(&gcc_sysroot)
               .exec()?;
        }

        // Compiling injected-glue
        let injected_glue_lib = {
            workspace.config().shell().say("Compiling injected-glue", 10);
            let mut cmd = workspace.config().rustc()?.process();
            cmd.arg(android_artifacts_dir.join("injected-glue/lib.rs"))
               .arg("--crate-type").arg("rlib");
            if config.release {
                cmd.arg("-C")
                   .arg("opt-level=3");
            }
            cmd.arg("--crate-name").arg("cargo_apk_injected_glue")
               .arg("--target").arg(build_target)
               .arg("--out-dir").arg(&build_target_dir);

            cmd.exec()?;

            let stdout = cmd.arg("--print").arg("file-names")
                            .exec_with_output()?;
            let stdout = String::from_utf8(stdout.stdout).unwrap();

            build_target_dir.join(stdout.lines().next().unwrap())
        };

        // Compiling glue_obj.rs
        {
            let mut file = File::create(build_target_dir.join("glue_obj.rs")).unwrap();
            file.write_all(&include_bytes!("../../glue_obj.rs")[..]).unwrap();
        }
        
        {
            workspace.config().shell().say("Compiling glue_obj", 10);
            let mut cmd = workspace.config().rustc()?.process();
            cmd.arg(build_target_dir.join("glue_obj.rs"))
               .arg("--crate-type").arg("staticlib");
            if config.release {
                cmd.arg("-C")
                   .arg("opt-level=3");
            }
            cmd.arg("--target").arg(build_target)
               .arg("--extern").arg(format!("cargo_apk_injected_glue={}", injected_glue_lib.to_string_lossy()))
               .arg("--emit").arg("obj")
               .arg("-o").arg(build_target_dir.join("glue_obj.o"))
               .exec()?;
        }

        // Directory where we will put the native libraries for ant to pick them up.
        let native_libraries_dir = android_artifacts_dir.join(format!("build/libs/{}", abi));

        if fs::metadata(&native_libraries_dir).is_err() {
            fs::DirBuilder::new().recursive(true).create(&native_libraries_dir).unwrap();
{'fraction_non_alphanumeric': 0.14704271631982474, 'fraction_numerical': 0.011363636363636364, 'mean_word_length': 2.037845705967977, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3277347', 'n_tokens_mistral': 4495, 'n_tokens_neox': 4322, 'n_words': 799}