<commit_before>#![allow(dead_code)]

#[cfg(any(feature = "nfc", feature = "nfd", feature = "nfkc", feature = "nfkd"))]
extern crate unicode_normalization;
#[cfg(feature = "embed_all")] extern crate pocket_resources;

extern crate bincode;
extern crate fst;
extern crate hyphenation_commons;
extern crate serde;

use fst::Map;
use bincode as bin;
use serde::ser;
use std::collections::HashMap;
use std::hash::Hash;
use std::env;
use std::error;
use std::fmt;
use std::fs::{self, File};
use std::io;
use std::io::prelude::*;
use std::iter::{self, FromIterator};
use std::path::{Path, PathBuf};

use hyphenation_commons::dictionary::*;
use hyphenation_commons::dictionary::trie::Trie;
use hyphenation_commons::dictionary::extended as ext;
use hyphenation_commons::Language;
use hyphenation_commons::Language::*;
use hyphenation_commons::parse::*;

// Configuration of exclusive optional features

use configuration::*;
mod configuration {
    // In service of configurable normalization forms, a type alias and a function
    // are defined via conditional compilation.
    //
    // If no feature is explicitly set, normalization is avoided altogether.

    // Neither Cargo nor rustc allows us to set exclusive features; we must indulge
    // them with this clumsy branle of cfg declarations.
    #[cfg(not(any(feature = "nfc", feature = "nfd", feature = "nfkc", feature = "nfkd")))]
    pub fn normalize(s : &str) -> String { s.to_owned() }

    #[cfg(any(feature = "nfc", feature = "nfd", feature = "nfkc", feature = "nfkd"))]
    use unicode_normalization::*;

    #[cfg(feature = "nfc")]  pub fn normalize(s : &str) -> String { s.nfc().collect() }
    #[cfg(feature = "nfd")]  pub fn normalize(s : &str) -> String { s.nfd().collect() }
    #[cfg(feature = "nfkc")] pub fn normalize(s : &str) -> String { s.nfkc().collect() }
    #[cfg(feature = "nfkd")] pub fn normalize(s : &str) -> String { s.nfkd().collect() }
}

trait TryFromIterator<Tally> : Sized {
    fn try_from_iter<I>(iter : I) -> Result<Self, Error>
    where I : IntoIterator<Item = (String, Tally)>
            + ExactSizeIterator;
}

fn uniques<I, T>(iter : I) -> (Vec<(String, u64)>, Vec<T>)
where T : Eq + Clone + Hash
    , I : IntoIterator<Item = (String, T)>
        + ExactSizeIterator
{
    let mut pairs = Vec::with_capacity(iter.len());
    let mut tally_ids = HashMap::with_capacity(iter.len());
    let mut tallies : Vec<T> = Vec::with_capacity(256);
    for (pattern, tally) in iter {
        match tally_ids.get(&tally) {
            Some(&id) => pairs.push((pattern, id)),
            None => {
                let id = tallies.len() as u64;
                tallies.push(tally.clone());
                tally_ids.insert(tally, id);
                pairs.push((pattern, id));
            }
        }
    }
    pairs.sort_by(|a, b| a.0.cmp(&b.0));
    pairs.dedup_by(|a, b| a.0 == b.0);
    (pairs, tallies)
}

impl TryFromIterator<<Patterns as Parse>::Tally> for Patterns {
    fn try_from_iter<I>(iter : I) -> Result<Self, Error>
    where I : IntoIterator<Item = (String, <Patterns as Parse>::Tally)>
            + ExactSizeIterator
    {
        let (kvs, tallies) = uniques(iter);
        let map = Map::from_iter(kvs.into_iter()) ?;
        let automaton = Trie(map);
        Ok(Patterns { tallies, automaton })
    }
}

impl TryFromIterator<<Exceptions as Parse>::Tally> for Exceptions {
    fn try_from_iter<I>(iter : I) -> Result<Self, Error>
    where I : IntoIterator<Item = (String, <Exceptions as Parse>::Tally)>
            + ExactSizeIterator
    {
        Ok(Exceptions(HashMap::from_iter(iter)))
    }
}

impl TryFromIterator<<ext::Patterns as Parse>::Tally> for ext::Patterns {
    fn try_from_iter<I>(iter : I) -> Result<Self, Error>
    where I : IntoIterator<Item = (String, <ext::Patterns as Parse>::Tally)>
            + ExactSizeIterator
    {
        let (kvs, tallies) = uniques(iter);
        let map = Map::from_iter(kvs.into_iter()) ?;
        let automaton = Trie(map);
        Ok(ext::Patterns { tallies, automaton })
    }
}

// Dictionary building and serialization

#[derive(Clone, Debug)]
struct Paths {
    source : PathBuf,
    out : PathBuf
}

impl Paths {
    fn new() -> Result<Self, Error> {
        let source = env::var("CARGO_MANIFEST_DIR").map(|p| PathBuf::from(p)) ?;
        let out = env::var("OUT_DIR").map(|p| PathBuf::from(p)) ?;

        Ok(Paths { source, out })
    }

    fn destine_item<P : AsRef<Path>>(&self, p : P) -> PathBuf { self.out.join(p.as_ref()) }
    fn source_item<P : AsRef<Path>>(&self, p : P) -> PathBuf { self.source.join(p.as_ref()) }

    fn source_pattern(&self, lang : Language, suffix : &str) -> PathBuf {
        let fname = format!("hyph-{}.{}.txt", lang.code(), suffix);
        self.source_item("patterns").join(fname)
    }

    fn destine_dict(&self, lang : Language, suffix : &str) -> PathBuf {
        self.destine_item("dictionaries").join(Self::dict_name(lang, suffix))
    }

    fn dict_name(lang : Language, suffix : &str) -> String {
        format!("{}.{}.bincode", lang.code(), suffix)
    }
}

trait Build : Sized + Parse + TryFromIterator<<Self as Parse>::Tally> {
    fn suffix() -> &'static str;

    fn sourcepath(lang : Language, paths : &Paths) -> PathBuf {
        paths.source_pattern(lang, Self::suffix())
    }

    fn build(lang : Language, paths : &Paths) -> Result<Self, Error> {
        let file = File::open(Self::sourcepath(lang, paths)) ?;
        let by_line = io::BufReader::new(file).lines();
        let pairs : Vec<_> = by_line.map(|res| Self::pair(&res.unwrap(), normalize)).collect();

        Self::try_from_iter(pairs.into_iter())
    }
}

impl Build for Patterns   { fn suffix() -> &'static str { "pat" } }
impl Build for Exceptions { fn suffix() -> &'static str { "hyp" } }
impl Build for ext::Patterns { fn suffix() -> &'static str { "ext" } }

fn write<T>(item : &T, path : &Path) -> Result<(), Error> where T : ser::Serialize {
    let mut buffer = File::create(&path).map(|f| io::BufWriter::new(f)) ?;
    bin::config().limit(5_000_000).serialize_into(&mut buffer, item) ?;
    Ok(())
}

fn copy_dir(from : &Path, to : &Path) -> Result<(), Error> {
    for entry in fs::read_dir(from) ? {
        entry.and_then(|e| fs::copy(e.path(), to.join(e.file_name()))) ?;
    }

    Ok(())
}

fn main() {
    let dict_folder = Path::new("dictionaries");
    let _std_out = "standard";
    let _ext_out = "extended";
    let paths = Paths::new().unwrap();
    let dict_source = paths.source_item(dict_folder);
    let dict_out = paths.destine_item(dict_folder);

    let ext_langs = vec![Catalan, Hungarian];
    let std_langs =
        vec![ Afrikaans, Armenian, Assamese, Basque, Belarusian, Bengali, Bulgarian, Catalan,
              Chinese, Coptic, Croatian, Czech, Danish, Dutch, EnglishGB, EnglishUS, Esperanto,
              Estonian, Ethiopic, Finnish, French, Friulan, Galician, Georgian, German1901,
              German1996, GermanSwiss, GreekAncient, GreekMono, GreekPoly, Gujarati, Hindi,
              Hungarian, Icelandic, Indonesian, Interlingua, Irish, Italian, Kannada, Kurmanji,
              Latin, LatinClassic, LatinLiturgical, Latvian, Lithuanian, Malayalam, Marathi,
              Mongolian, NorwegianBokmal, NorwegianNynorsk, Occitan, Oriya, Pali, Panjabi,
              Piedmontese, Polish, Portuguese, Romanian, Romansh, Russian, Sanskrit,
              SerbianCyrillic, SerbocroatianCyrillic, SerbocroatianLatin, SlavonicChurch, Slovak,
              Slovenian, Spanish, Swedish, Tamil, Telugu, Thai, Turkish, Turkmen, Ukrainian,
              Uppersorbian, Welsh ];

    fs::create_dir_all(&dict_out).unwrap();

    // If no dictionary is to be rebuilt, copy the bundled ones into the `target`
    // folder.
    #[cfg(not(any(feature = "build_dictionaries", feature = "nfc", feature = "nfd",
                  feature = "nfkc", feature = "nfkd")))]
    {
        copy_dir(dict_source.as_path(), dict_out.as_path()).unwrap();
    }

    // Otherwise, process the bundled patterns into new dictionaries and similarly
    // bundle them.
    #[cfg(any(feature = "build_dictionaries", feature = "nfc", feature = "nfd",
              feature = "nfkc", feature = "nfkd"))]
    {
        println!("Building `Standard` dictionaries:");
        for &language in std_langs.iter() {
            println!("  - {:?}", language);
            let dict = Standard {
                language,
                patterns : Patterns::build(language, &paths).unwrap(),
                exceptions : Exceptions::build(language, &paths).unwrap(),
                minima : language.minima()
            };

            write(&dict, &paths.destine_dict(language, _std_out)).unwrap();
        }

        println!("Building `Extended` dictionaries:");
        for &language in ext_langs.iter() {
            println!("  - {:?}", language);
            let dict = ext::Extended {
                language,
                patterns : ext::Patterns::build(language, &paths).unwrap(),
                exceptions : ext::Exceptions::default(),
                minima : language.minima()
            };

            write(&dict, &paths.destine_dict(language, _ext_out)).unwrap();
        }
    }

    #[cfg(all(feature = "embed_en-us", not(feature = "embed_all")))] {
        let dict = (&dict_folder, Paths::dict_name(EnglishUS, _std_out));
        pocket_resources::package(iter::once(&dict)).unwrap();
    }

    #[cfg(feature = "embed_all")] {
        // HEED: `pocket_resources` requires paths to be relative
        let std_p = std_langs.iter().map(|&l| (&dict_folder, Paths::dict_name(l, _std_out)));
        let ext_p = ext_langs.iter().map(|&l| (&dict_folder, Paths::dict_name(l, _ext_out)));
        let all_paths : Vec<_> = std_p.chain(ext_p).collect();
        pocket_resources::package(all_paths.iter()).unwrap();
    }

    println!("cargo:rerun-if-changed=build.rs");
}

// Error type boilerplate

#[derive(Debug)]
pub enum Error {
    Build(fst::Error),
    Env(env::VarError),
    IO(io::Error),
    Serialization(bin::Error),
    Resource
    // TODO: Parsing
}

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match *self {
            Error::Build(ref e) => Some(e),
            Error::Env(ref e) => Some(e),
            Error::IO(ref e) => Some(e),
            Error::Serialization(ref e) => Some(e),
            _ => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Build(ref e) => e.fmt(f),
            Error::Env(ref e) => e.fmt(f),
            Error::IO(ref e) => e.fmt(f),
            Error::Serialization(ref e) => e.fmt(f),
            Error::Resource => f.write_str("dictionary could not be embedded")
        }
    }
}

impl From<io::Error> for Error {
    fn from(err : io::Error) -> Error { Error::IO(err) }
}

impl From<env::VarError> for Error {
    fn from(err : env::VarError) -> Error { Error::Env(err) }
}

impl From<bin::Error> for Error {
    fn from(err : bin::Error) -> Error { Error::Serialization(err) }
}

impl From<fst::Error> for Error {
    fn from(err : fst::Error) -> Error { Error::Build(err) }
}
<commit_msg>Avoid warnings in featureless builds<commit_after>#![allow(dead_code)]

#[cfg(any(feature = "nfc", feature = "nfd", feature = "nfkc", feature = "nfkd"))]
extern crate unicode_normalization;
#[cfg(feature = "embed_all")] extern crate pocket_resources;

extern crate bincode;
extern crate fst;
extern crate hyphenation_commons;
extern crate serde;

use fst::Map;
use bincode as bin;
use serde::ser;
use std::collections::HashMap;
use std::hash::Hash;
use std::env;
use std::error;
use std::fmt;
use std::fs::{self, File};
use std::io;
use std::io::prelude::*;
use std::iter::{FromIterator};
use std::path::{Path, PathBuf};

use hyphenation_commons::dictionary::*;
use hyphenation_commons::dictionary::trie::Trie;
use hyphenation_commons::dictionary::extended as ext;
use hyphenation_commons::Language;
use hyphenation_commons::Language::*;
use hyphenation_commons::parse::*;

// Configuration of exclusive optional features

use configuration::*;
mod configuration {
    // In service of configurable normalization forms, a type alias and a function
    // are defined via conditional compilation.
    //
    // If no feature is explicitly set, normalization is avoided altogether.

    // Neither Cargo nor rustc allows us to set exclusive features; we must indulge
    // them with this clumsy branle of cfg declarations.
    #[cfg(not(any(feature = "nfc", feature = "nfd", feature = "nfkc", feature = "nfkd")))]
    pub fn normalize(s : &str) -> String { s.to_owned() }

    #[cfg(any(feature = "nfc", feature = "nfd", feature = "nfkc", feature = "nfkd"))]
    use unicode_normalization::*;

    #[cfg(feature = "nfc")]  pub fn normalize(s : &str) -> String { s.nfc().collect() }
    #[cfg(feature = "nfd")]  pub fn normalize(s : &str) -> String { s.nfd().collect() }
    #[cfg(feature = "nfkc")] pub fn normalize(s : &str) -> String { s.nfkc().collect() }
    #[cfg(feature = "nfkd")] pub fn normalize(s : &str) -> String { s.nfkd().collect() }
}

trait TryFromIterator<Tally> : Sized {
    fn try_from_iter<I>(iter : I) -> Result<Self, Error>
    where I : IntoIterator<Item = (String, Tally)>
            + ExactSizeIterator;
}

fn uniques<I, T>(iter : I) -> (Vec<(String, u64)>, Vec<T>)
where T : Eq + Clone + Hash
    , I : IntoIterator<Item = (String, T)>
        + ExactSizeIterator
{
    let mut pairs = Vec::with_capacity(iter.len());
    let mut tally_ids = HashMap::with_capacity(iter.len());
    let mut tallies : Vec<T> = Vec::with_capacity(256);
    for (pattern, tally) in iter {
        match tally_ids.get(&tally) {
            Some(&id) => pairs.push((pattern, id)),
            None => {
                let id = tallies.len() as u64;
                tallies.push(tally.clone());
                tally_ids.insert(tally, id);
                pairs.push((pattern, id));
            }
        }
    }
    pairs.sort_by(|a, b| a.0.cmp(&b.0));
    pairs.dedup_by(|a, b| a.0 == b.0);
    (pairs, tallies)
}

impl TryFromIterator<<Patterns as Parse>::Tally> for Patterns {
    fn try_from_iter<I>(iter : I) -> Result<Self, Error>
    where I : IntoIterator<Item = (String, <Patterns as Parse>::Tally)>
            + ExactSizeIterator
    {
        let (kvs, tallies) = uniques(iter);
        let map = Map::from_iter(kvs.into_iter()) ?;
        let automaton = Trie(map);
        Ok(Patterns { tallies, automaton })
    }
}

impl TryFromIterator<<Exceptions as Parse>::Tally> for Exceptions {
    fn try_from_iter<I>(iter : I) -> Result<Self, Error>
    where I : IntoIterator<Item = (String, <Exceptions as Parse>::Tally)>
            + ExactSizeIterator
    {
        Ok(Exceptions(HashMap::from_iter(iter)))
    }
}

impl TryFromIterator<<ext::Patterns as Parse>::Tally> for ext::Patterns {
    fn try_from_iter<I>(iter : I) -> Result<Self, Error>
    where I : IntoIterator<Item = (String, <ext::Patterns as Parse>::Tally)>
            + ExactSizeIterator
    {
        let (kvs, tallies) = uniques(iter);
        let map = Map::from_iter(kvs.into_iter()) ?;
        let automaton = Trie(map);
        Ok(ext::Patterns { tallies, automaton })
    }
}

// Dictionary building and serialization

#[derive(Clone, Debug)]
struct Paths {
    source : PathBuf,
    out : PathBuf
}

impl Paths {
    fn new() -> Result<Self, Error> {
        let source = env::var("CARGO_MANIFEST_DIR").map(|p| PathBuf::from(p)) ?;
        let out = env::var("OUT_DIR").map(|p| PathBuf::from(p)) ?;

        Ok(Paths { source, out })
    }

    fn destine_item<P : AsRef<Path>>(&self, p : P) -> PathBuf { self.out.join(p.as_ref()) }
    fn source_item<P : AsRef<Path>>(&self, p : P) -> PathBuf { self.source.join(p.as_ref()) }

    fn source_pattern(&self, lang : Language, suffix : &str) -> PathBuf {
        let fname = format!("hyph-{}.{}.txt", lang.code(), suffix);
        self.source_item("patterns").join(fname)
    }

    fn destine_dict(&self, lang : Language, suffix : &str) -> PathBuf {
        self.destine_item("dictionaries").join(Self::dict_name(lang, suffix))
    }

    fn dict_name(lang : Language, suffix : &str) -> String {
        format!("{}.{}.bincode", lang.code(), suffix)
    }
}

trait Build : Sized + Parse + TryFromIterator<<Self as Parse>::Tally> {
    fn suffix() -> &'static str;

    fn sourcepath(lang : Language, paths : &Paths) -> PathBuf {
        paths.source_pattern(lang, Self::suffix())
    }

    fn build(lang : Language, paths : &Paths) -> Result<Self, Error> {
        let file = File::open(Self::sourcepath(lang, paths)) ?;
        let by_line = io::BufReader::new(file).lines();
        let pairs : Vec<_> = by_line.map(|res| Self::pair(&res.unwrap(), normalize)).collect();

        Self::try_from_iter(pairs.into_iter())
    }
}

impl Build for Patterns   { fn suffix() -> &'static str { "pat" } }
impl Build for Exceptions { fn suffix() -> &'static str { "hyp" } }
impl Build for ext::Patterns { fn suffix() -> &'static str { "ext" } }

fn write<T>(item : &T, path : &Path) -> Result<(), Error> where T : ser::Serialize {
    let mut buffer = File::create(&path).map(|f| io::BufWriter::new(f)) ?;
    bin::config().limit(5_000_000).serialize_into(&mut buffer, item) ?;
    Ok(())
}

fn copy_dir(from : &Path, to : &Path) -> Result<(), Error> {
    for entry in fs::read_dir(from) ? {
        entry.and_then(|e| fs::copy(e.path(), to.join(e.file_name()))) ?;
    }

    Ok(())
}

fn main() {
    let dict_folder = Path::new("dictionaries");
    let _std_out = "standard";
    let _ext_out = "extended";
    let paths = Paths::new().unwrap();
    let dict_source = paths.source_item(dict_folder);
    let dict_out = paths.destine_item(dict_folder);

    let _ext_langs = vec![Catalan, Hungarian];
    let _std_langs =
        vec![ Afrikaans, Armenian, Assamese, Basque, Belarusian, Bengali, Bulgarian, Catalan,
              Chinese, Coptic, Croatian, Czech, Danish, Dutch, EnglishGB, EnglishUS, Esperanto,
              Estonian, Ethiopic, Finnish, French, Friulan, Galician, Georgian, German1901,
              German1996, GermanSwiss, GreekAncient, GreekMono, GreekPoly, Gujarati, Hindi,
              Hungarian, Icelandic, Indonesian, Interlingua, Irish, Italian, Kannada, Kurmanji,
              Latin, LatinClassic, LatinLiturgical, Latvian, Lithuanian, Malayalam, Marathi,
              Mongolian, NorwegianBokmal, NorwegianNynorsk, Occitan, Oriya, Pali, Panjabi,
              Piedmontese, Polish, Portuguese, Romanian, Romansh, Russian, Sanskrit,
              SerbianCyrillic, SerbocroatianCyrillic, SerbocroatianLatin, SlavonicChurch, Slovak,
              Slovenian, Spanish, Swedish, Tamil, Telugu, Thai, Turkish, Turkmen, Ukrainian,
              Uppersorbian, Welsh ];

    fs::create_dir_all(&dict_out).unwrap();

    // If no dictionary is to be rebuilt, copy the bundled ones into the `target`
    // folder.
    #[cfg(not(any(feature = "build_dictionaries", feature = "nfc", feature = "nfd",
                  feature = "nfkc", feature = "nfkd")))]
    {
        copy_dir(dict_source.as_path(), dict_out.as_path()).unwrap();
    }

    // Otherwise, process the bundled patterns into new dictionaries and similarly
    // bundle them.
    #[cfg(any(feature = "build_dictionaries", feature = "nfc", feature = "nfd",
              feature = "nfkc", feature = "nfkd"))]
    {
        println!("Building `Standard` dictionaries:");
        for &language in _std_langs.iter() {
            println!("  - {:?}", language);
            let dict = Standard {
                language,
                patterns : Patterns::build(language, &paths).unwrap(),
                exceptions : Exceptions::build(language, &paths).unwrap(),
                minima : language.minima()
            };

            write(&dict, &paths.destine_dict(language, _std_out)).unwrap();
        }

        println!("Building `Extended` dictionaries:");
        for &language in _ext_langs.iter() {
            println!("  - {:?}", language);
            let dict = ext::Extended {
                language,
                patterns : ext::Patterns::build(language, &paths).unwrap(),
                exceptions : ext::Exceptions::default(),
                minima : language.minima()
            };

            write(&dict, &paths.destine_dict(language, _ext_out)).unwrap();
        }
    }

    #[cfg(all(feature = "embed_en-us", not(feature = "embed_all")))] {
        use std::iter;

        let dict = (&dict_folder, Paths::dict_name(EnglishUS, _std_out));
        pocket_resources::package(iter::once(&dict)).unwrap();
    }

    #[cfg(feature = "embed_all")] {
        // HEED: `pocket_resources` requires paths to be relative
        let std_p = _std_langs.iter().map(|&l| (&dict_folder, Paths::dict_name(l, _std_out)));
        let ext_p = _ext_langs.iter().map(|&l| (&dict_folder, Paths::dict_name(l, _ext_out)));
        let all_paths : Vec<_> = std_p.chain(ext_p).collect();
        pocket_resources::package(all_paths.iter()).unwrap();
    }

    println!("cargo:rerun-if-changed=build.rs");
}

// Error type boilerplate

#[derive(Debug)]
pub enum Error {
    Build(fst::Error),
    Env(env::VarError),
    IO(io::Error),
    Serialization(bin::Error),
    Resource
    // TODO: Parsing
}

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match *self {
            Error::Build(ref e) => Some(e),
            Error::Env(ref e) => Some(e),
            Error::IO(ref e) => Some(e),
            Error::Serialization(ref e) => Some(e),
            _ => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Build(ref e) => e.fmt(f),
            Error::Env(ref e) => e.fmt(f),
            Error::IO(ref e) => e.fmt(f),
            Error::Serialization(ref e) => e.fmt(f),
            Error::Resource => f.write_str("dictionary could not be embedded")
        }
    }
}

impl From<io::Error> for Error {
    fn from(err : io::Error) -> Error { Error::IO(err) }
}

impl From<env::VarError> for Error {
    fn from(err : env::VarError) -> Error { Error::Env(err) }
}

impl From<bin::Error> for Error {
    fn from(err : bin::Error) -> Error { Error::Serialization(err) }
}

impl From<fst::Error> for Error {
    fn from(err : fst::Error) -> Error { Error::Build(err) }
}
<|endoftext|>


<commit_before>extern crate cc;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate semver;

use regex::Regex;
use semver::Version;
use std::env;
use std::ffi::OsStr;
use std::io::{self, ErrorKind};
use std::path::PathBuf;
use std::process::Command;

// Environment variables that can guide compilation
//
// When adding new ones, they should also be added to main() to force a
// rebuild if they are changed.
lazy_static! {
    /// A single path to search for LLVM in (containing bin/llvm-config)
    static ref ENV_LLVM_PREFIX: String =
        format!("LLVM_SYS_{}_PREFIX", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If exactly "YES", ignore the version blacklist
    static ref ENV_IGNORE_BLACKLIST: String =
        format!("LLVM_SYS_{}_IGNORE_BLACKLIST", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, enforce precise correspondence between crate and binary versions.
    static ref ENV_STRICT_VERSIONING: String =
        format!("LLVM_SYS_{}_STRICT_VERSIONING", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, do not attempt to strip irrelevant options for llvm-config --cflags
    static ref ENV_NO_CLEAN_CFLAGS: String =
        format!("LLVM_SYS_{}_NO_CLEAN_CFLAGS", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set and targeting MSVC, force the debug runtime library
    static ref ENV_USE_DEBUG_MSVCRT: String =
        format!("LLVM_SYS_{}_USE_DEBUG_MSVCRT", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, always link against libffi
    static ref ENV_FORCE_FFI: String =
        format!("LLVM_SYS_{}_FFI_WORKAROUND", env!("CARGO_PKG_VERSION_MAJOR"));
}

lazy_static! {
    /// LLVM version used by this version of the crate.
    static ref CRATE_VERSION: Version = {
        let crate_version = Version::parse(env!("CARGO_PKG_VERSION"))
            .expect("Crate version is somehow not valid semver");
        Version {
            major: crate_version.major / 10,
            minor: crate_version.major % 10,
            .. crate_version
        }
    };

    static ref LLVM_CONFIG_BINARY_NAMES: Vec<String> = {
        vec![
            "llvm-config".into(),
            format!("llvm-config-{}", CRATE_VERSION.major),
            format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor),
            format!("llvm-config{}{}", CRATE_VERSION.major, CRATE_VERSION.minor),
        ]
    };

    /// Filesystem path to an llvm-config binary for the correct version.
    static ref LLVM_CONFIG_PATH: Option<PathBuf> = locate_llvm_config();
}

/// Try to find a version of llvm-config that is compatible with this crate.
///
/// If $LLVM_SYS_<VERSION>_PREFIX is set, look for llvm-config ONLY in there. The assumption is
/// that the user know best, and they want to link to a specific build or fork of LLVM.
///
/// If $LLVM_SYS_<VERSION>_PREFIX is NOT set, then look for llvm-config in $PATH.
///
/// Returns None on failure.
fn locate_llvm_config() -> Option<PathBuf> {
    let prefix = env::var_os(&*ENV_LLVM_PREFIX)
        .map(|p| PathBuf::from(p).join("bin"))
        .unwrap_or_else(PathBuf::new);
    for binary_name in &*LLVM_CONFIG_BINARY_NAMES {
        let binary_name = prefix.join(binary_name);
        match llvm_version(&binary_name) {
            Ok(ref version) if is_compatible_llvm(version) => {
                // Compatible version found. Nice.
                return Some(binary_name);
            }
            Ok(version) => {
                // Version mismatch. Will try further searches, but warn that
                // we're not using the system one.
                println!(
                    "Found LLVM version {} on PATH, but need {}.",
                    version, *CRATE_VERSION
                );
            }
            Err(ref e) if e.kind() == ErrorKind::NotFound => {
                // Looks like we failed to execute any llvm-config. Keep
                // searching.
            }
            // Some other error, probably a weird failure. Give up.
            Err(e) => panic!("Failed to search PATH for llvm-config: {}", e),
        }
    }

    None
}

/// Check whether the given version of LLVM is blacklisted,
/// returning `Some(reason)` if it is.
fn is_blacklisted_llvm(llvm_version: &Version) -> Option<&'static str> {
    static BLACKLIST: &'static [(u64, u64, u64, &'static str)] = &[];

    if let Some(x) = env::var_os(&*ENV_IGNORE_BLACKLIST) {
        if &x == "YES" {
            println!(
                "cargo:warning=Ignoring blacklist entry for LLVM {}",
                llvm_version
            );
            return None;
        } else {
            println!(
                "cargo:warning={} is set but not exactly \"YES\"; blacklist is still honored.",
                *ENV_IGNORE_BLACKLIST
            );
        }
    }

    for &(major, minor, patch, reason) in BLACKLIST.iter() {
        let bad_version = Version {
            major: major,
            minor: minor,
            patch: patch,
            pre: vec![],
            build: vec![],
        };

        if &bad_version == llvm_version {
            return Some(reason);
        }
    }
    None
}

/// Check whether the given LLVM version is compatible with this version of
/// the crate.
fn is_compatible_llvm(llvm_version: &Version) -> bool {
    if let Some(reason) = is_blacklisted_llvm(llvm_version) {
        println!(
            "Found LLVM {}, which is blacklisted: {}",
            llvm_version, reason
        );
        return false;
    }

    let strict =
        env::var_os(&*ENV_STRICT_VERSIONING).is_some() || cfg!(feature = "strict-versioning");
    if strict {
        llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor
    } else {
        llvm_version.major >= CRATE_VERSION.major
            || (llvm_version.major == CRATE_VERSION.major
                && llvm_version.minor >= CRATE_VERSION.minor)
    }
}

/// Get the output from running `llvm-config` with the given argument.
///
/// Lazily searches for or compiles LLVM as configured by the environment
/// variables.
fn llvm_config(arg: &str) -> String {
    llvm_config_ex(&*LLVM_CONFIG_PATH.clone().unwrap(), arg)
        .expect("Surprising failure from llvm-config")
}

/// Invoke the specified binary as llvm-config.
///
/// Explicit version of the `llvm_config` function that bubbles errors
/// up.
fn llvm_config_ex<S: AsRef<OsStr>>(binary: S, arg: &str) -> io::Result<String> {
    Command::new(binary)
        .arg(arg)
        .arg("--link-static") // Don't use dylib for >= 3.9
        .output()
        .map(|output| {
            String::from_utf8(output.stdout).expect("Output from llvm-config was not valid UTF-8")
        })
}

/// Get the LLVM version using llvm-config.
fn llvm_version<S: AsRef<OsStr>>(binary: &S) -> io::Result<Version> {
    let version_str = llvm_config_ex(binary.as_ref(), "--version")?;

    // LLVM isn't really semver and uses version suffixes to build
    // version strings like '3.8.0svn', so limit what we try to parse
    // to only the numeric bits.
    let re = Regex::new(r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))??").unwrap();
    let c = re
        .captures(&version_str)
        .expect("Could not determine LLVM version from llvm-config.");

    // some systems don't have a patch number but Version wants it so we just append .0 if it isn't
    // there
    let s = match c.name("patch") {
        None => format!("{}.0", &c[0]),
        Some(_) => c[0].to_string(),
    };
    Ok(Version::parse(&s).unwrap())
}

/// Get the names of the dylibs required by LLVM, including the C++ standard
/// library.
fn get_system_libraries() -> Vec<String> {
    llvm_config("--system-libs")
        .split(&[' ', '\n'] as &[char])
        .filter(|s| !s.is_empty())
        .map(|flag| {
            if cfg!(target_env = "msvc") {
                // Same as --libnames, foo.lib
                assert!(flag.ends_with(".lib"));
                &flag[..flag.len() - 4]
            } else if cfg!(target_os = "macos") {
                // Linker flags style, -lfoo
                assert!(flag.starts_with("-l"));
                if flag.ends_with(".tbd") && flag.starts_with("-llib") {
                    &flag[5..flag.len() - 4]
                } else {
                    &flag[2..]
                }
            } else {
                // Linker flags style, -lfoo
                assert!(flag.starts_with("-l"));
                &flag[2..]
            }
        })
        .chain(get_system_libcpp())
        .map(str::to_owned)
        .collect::<Vec<String>>()
}

/// Get the library that must be linked for C++, if any.
fn get_system_libcpp() -> Option<&'static str> {
    if cfg!(target_env = "msvc") {
        // MSVC doesn't need an explicit one.
        None
    } else if cfg!(target_os = "macos") {
        // On OS X 10.9 and later, LLVM's libc++ is the default. On earlier
        // releases GCC's libstdc++ is default. Unfortunately we can't
        // reasonably detect which one we need (on older ones libc++ is
        // available and can be selected with -stdlib=lib++), so assume the
        // latest, at the cost of breaking the build on older OS releases
        // when LLVM was built against libstdc++.
        Some("c++")
    } else if cfg!(target_os = "freebsd") {
        Some("c++")
    } else {
        // Otherwise assume GCC's libstdc++.
        // This assumption is probably wrong on some platforms, but would need
        // testing on them.
        Some("stdc++")
    }
}

/// Get the names of libraries to link against.
fn get_link_libraries() -> Vec<String> {
    // Using --libnames in conjunction with --libdir is particularly important
    // for MSVC when LLVM is in a path with spaces, but it is generally less of
    // a hack than parsing linker flags output from --libs and --ldflags.
    llvm_config("--libnames")
        .split(&[' ', '\n'] as &[char])
        .filter(|s| !s.is_empty())
        .map(|name| {
            // --libnames gives library filenames. Extract only the name that
            // we need to pass to the linker.
            if cfg!(target_env = "msvc") {
                // LLVMfoo.lib
                assert!(name.ends_with(".lib"));
                &name[..name.len() - 4]
            } else {
                // libLLVMfoo.a
                assert!(name.starts_with("lib") && name.ends_with(".a"));
                &name[3..name.len() - 2]
            }
        })
        .map(str::to_owned)
        .collect::<Vec<String>>()
}

fn get_llvm_cflags() -> String {
    let output = llvm_config("--cflags");

    // llvm-config includes cflags from its own compilation with --cflags that
    // may not be relevant to us. In particularly annoying cases, these might
    // include flags that aren't understood by the default compiler we're
    // using. Unless requested otherwise, clean CFLAGS of options that are
    // known to be possibly-harmful.
    let no_clean = env::var_os(&*ENV_NO_CLEAN_CFLAGS).is_some();
    if no_clean || cfg!(target_env = "msvc") {
        // MSVC doesn't accept -W... options, so don't try to strip them and
        // possibly strip something that should be retained. Also do nothing if
        // the user requests it.
        return output;
    }

    llvm_config("--cflags")
        .split(&[' ', '\n'][..])
        .filter(|word| !word.starts_with("-W"))
        .collect::<Vec<_>>()
        .join(" ")
}

fn is_llvm_debug() -> bool {
    // Has to be either Debug or Release
    llvm_config("--build-mode").contains("Debug")
}

fn main() {
    // Behavior can be significantly affected by these vars.
    println!("cargo:rerun-if-env-changed={}", &*ENV_LLVM_PREFIX);
    println!("cargo:rerun-if-env-changed={}", &*ENV_IGNORE_BLACKLIST);
    println!("cargo:rerun-if-env-changed={}", &*ENV_STRICT_VERSIONING);
    println!("cargo:rerun-if-env-changed={}", &*ENV_NO_CLEAN_CFLAGS);
    println!("cargo:rerun-if-env-changed={}", &*ENV_USE_DEBUG_MSVCRT);
    println!("cargo:rerun-if-env-changed={}", &*ENV_FORCE_FFI);

    if LLVM_CONFIG_PATH.is_none() {
        println!("cargo:rustc-cfg=LLVM_SYS_NOT_FOUND");
        return;
    }

    // Build the extra wrapper functions.
    if !cfg!(feature = "disable-alltargets-init") {
        std::env::set_var("CFLAGS", get_llvm_cflags());
        cc::Build::new()
            .file("wrappers/target.c")
            .compile("targetwrappers");
    }

    if cfg!(feature = "no-llvm-linking") {
        return;
    }

    let libdir = llvm_config("--libdir");

    // Export information to other crates
    println!(
        "cargo:config_path={}",
        LLVM_CONFIG_PATH.clone().unwrap().display()
    ); // will be DEP_LLVM_CONFIG_PATH
    println!("cargo:libdir={}", libdir); // DEP_LLVM_LIBDIR

    // Link LLVM libraries
    println!("cargo:rustc-link-search=native={}", libdir);
    for name in get_link_libraries() {
        println!("cargo:rustc-link-lib=static={}", name);
    }

    // Link system libraries
    for name in get_system_libraries() {
        println!("cargo:rustc-link-lib=dylib={}", name);
    }

    let use_debug_msvcrt = env::var_os(&*ENV_USE_DEBUG_MSVCRT).is_some();
    if cfg!(target_env = "msvc") && (use_debug_msvcrt || is_llvm_debug()) {
        println!("cargo:rustc-link-lib={}", "msvcrtd");
    }

    // Link libffi if the user requested this workaround.
    // See https://bitbucket.org/tari/llvm-sys.rs/issues/12/
    let force_ffi = env::var_os(&*ENV_FORCE_FFI).is_some();
    if force_ffi {
        println!("cargo:rustc-link-lib=dylib={}", "ffi");
    }
}
<commit_msg>build.rs: don't error with LLVM_SYS_NOT_FOUND when llvm-config is not needed<commit_after>extern crate cc;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate semver;

use regex::Regex;
use semver::Version;
use std::env;
use std::ffi::OsStr;
use std::io::{self, ErrorKind};
use std::path::PathBuf;
use std::process::Command;

// Environment variables that can guide compilation
//
// When adding new ones, they should also be added to main() to force a
// rebuild if they are changed.
lazy_static! {
    /// A single path to search for LLVM in (containing bin/llvm-config)
    static ref ENV_LLVM_PREFIX: String =
        format!("LLVM_SYS_{}_PREFIX", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If exactly "YES", ignore the version blacklist
    static ref ENV_IGNORE_BLACKLIST: String =
        format!("LLVM_SYS_{}_IGNORE_BLACKLIST", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, enforce precise correspondence between crate and binary versions.
    static ref ENV_STRICT_VERSIONING: String =
        format!("LLVM_SYS_{}_STRICT_VERSIONING", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, do not attempt to strip irrelevant options for llvm-config --cflags
    static ref ENV_NO_CLEAN_CFLAGS: String =
        format!("LLVM_SYS_{}_NO_CLEAN_CFLAGS", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set and targeting MSVC, force the debug runtime library
    static ref ENV_USE_DEBUG_MSVCRT: String =
        format!("LLVM_SYS_{}_USE_DEBUG_MSVCRT", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, always link against libffi
    static ref ENV_FORCE_FFI: String =
        format!("LLVM_SYS_{}_FFI_WORKAROUND", env!("CARGO_PKG_VERSION_MAJOR"));
}

lazy_static! {
    /// LLVM version used by this version of the crate.
    static ref CRATE_VERSION: Version = {
        let crate_version = Version::parse(env!("CARGO_PKG_VERSION"))
            .expect("Crate version is somehow not valid semver");
        Version {
            major: crate_version.major / 10,
            minor: crate_version.major % 10,
            .. crate_version
        }
    };

    static ref LLVM_CONFIG_BINARY_NAMES: Vec<String> = {
        vec![
            "llvm-config".into(),
            format!("llvm-config-{}", CRATE_VERSION.major),
            format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor),
            format!("llvm-config{}{}", CRATE_VERSION.major, CRATE_VERSION.minor),
        ]
    };

    /// Filesystem path to an llvm-config binary for the correct version.
    static ref LLVM_CONFIG_PATH: Option<PathBuf> = locate_llvm_config();
}

/// Try to find a version of llvm-config that is compatible with this crate.
///
/// If $LLVM_SYS_<VERSION>_PREFIX is set, look for llvm-config ONLY in there. The assumption is
/// that the user know best, and they want to link to a specific build or fork of LLVM.
///
/// If $LLVM_SYS_<VERSION>_PREFIX is NOT set, then look for llvm-config in $PATH.
///
/// Returns None on failure.
fn locate_llvm_config() -> Option<PathBuf> {
    let prefix = env::var_os(&*ENV_LLVM_PREFIX)
        .map(|p| PathBuf::from(p).join("bin"))
        .unwrap_or_else(PathBuf::new);
    for binary_name in &*LLVM_CONFIG_BINARY_NAMES {
        let binary_name = prefix.join(binary_name);
        match llvm_version(&binary_name) {
            Ok(ref version) if is_compatible_llvm(version) => {
                // Compatible version found. Nice.
                return Some(binary_name);
            }
            Ok(version) => {
                // Version mismatch. Will try further searches, but warn that
                // we're not using the system one.
                println!(
                    "Found LLVM version {} on PATH, but need {}.",
                    version, *CRATE_VERSION
                );
            }
            Err(ref e) if e.kind() == ErrorKind::NotFound => {
                // Looks like we failed to execute any llvm-config. Keep
                // searching.
            }
            // Some other error, probably a weird failure. Give up.
            Err(e) => panic!("Failed to search PATH for llvm-config: {}", e),
        }
    }

    None
}

/// Check whether the given version of LLVM is blacklisted,
/// returning `Some(reason)` if it is.
fn is_blacklisted_llvm(llvm_version: &Version) -> Option<&'static str> {
    static BLACKLIST: &'static [(u64, u64, u64, &'static str)] = &[];

    if let Some(x) = env::var_os(&*ENV_IGNORE_BLACKLIST) {
        if &x == "YES" {
            println!(
                "cargo:warning=Ignoring blacklist entry for LLVM {}",
                llvm_version
            );
            return None;
        } else {
            println!(
                "cargo:warning={} is set but not exactly \"YES\"; blacklist is still honored.",
                *ENV_IGNORE_BLACKLIST
            );
        }
    }

    for &(major, minor, patch, reason) in BLACKLIST.iter() {
        let bad_version = Version {
            major: major,
            minor: minor,
            patch: patch,
            pre: vec![],
            build: vec![],
        };

        if &bad_version == llvm_version {
            return Some(reason);
        }
    }
    None
}

/// Check whether the given LLVM version is compatible with this version of
/// the crate.
fn is_compatible_llvm(llvm_version: &Version) -> bool {
    if let Some(reason) = is_blacklisted_llvm(llvm_version) {
        println!(
            "Found LLVM {}, which is blacklisted: {}",
            llvm_version, reason
        );
        return false;
    }

    let strict =
        env::var_os(&*ENV_STRICT_VERSIONING).is_some() || cfg!(feature = "strict-versioning");
    if strict {
        llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor
    } else {
        llvm_version.major >= CRATE_VERSION.major
            || (llvm_version.major == CRATE_VERSION.major
                && llvm_version.minor >= CRATE_VERSION.minor)
    }
}

/// Get the output from running `llvm-config` with the given argument.
///
/// Lazily searches for or compiles LLVM as configured by the environment
/// variables.
fn llvm_config(arg: &str) -> String {
    llvm_config_ex(&*LLVM_CONFIG_PATH.clone().unwrap(), arg)
        .expect("Surprising failure from llvm-config")
}

/// Invoke the specified binary as llvm-config.
///
/// Explicit version of the `llvm_config` function that bubbles errors
/// up.
fn llvm_config_ex<S: AsRef<OsStr>>(binary: S, arg: &str) -> io::Result<String> {
    Command::new(binary)
        .arg(arg)
        .arg("--link-static") // Don't use dylib for >= 3.9
        .output()
        .map(|output| {
            String::from_utf8(output.stdout).expect("Output from llvm-config was not valid UTF-8")
        })
}

/// Get the LLVM version using llvm-config.
fn llvm_version<S: AsRef<OsStr>>(binary: &S) -> io::Result<Version> {
    let version_str = llvm_config_ex(binary.as_ref(), "--version")?;

    // LLVM isn't really semver and uses version suffixes to build
    // version strings like '3.8.0svn', so limit what we try to parse
    // to only the numeric bits.
    let re = Regex::new(r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))??").unwrap();
    let c = re
        .captures(&version_str)
        .expect("Could not determine LLVM version from llvm-config.");

    // some systems don't have a patch number but Version wants it so we just append .0 if it isn't
    // there
    let s = match c.name("patch") {
        None => format!("{}.0", &c[0]),
        Some(_) => c[0].to_string(),
    };
    Ok(Version::parse(&s).unwrap())
}

/// Get the names of the dylibs required by LLVM, including the C++ standard
/// library.
fn get_system_libraries() -> Vec<String> {
    llvm_config("--system-libs")
        .split(&[' ', '\n'] as &[char])
        .filter(|s| !s.is_empty())
        .map(|flag| {
            if cfg!(target_env = "msvc") {
                // Same as --libnames, foo.lib
                assert!(flag.ends_with(".lib"));
                &flag[..flag.len() - 4]
            } else if cfg!(target_os = "macos") {
                // Linker flags style, -lfoo
                assert!(flag.starts_with("-l"));
                if flag.ends_with(".tbd") && flag.starts_with("-llib") {
                    &flag[5..flag.len() - 4]
                } else {
                    &flag[2..]
                }
            } else {
                // Linker flags style, -lfoo
                assert!(flag.starts_with("-l"));
                &flag[2..]
            }
        })
        .chain(get_system_libcpp())
        .map(str::to_owned)
        .collect::<Vec<String>>()
}

/// Get the library that must be linked for C++, if any.
fn get_system_libcpp() -> Option<&'static str> {
    if cfg!(target_env = "msvc") {
        // MSVC doesn't need an explicit one.
        None
    } else if cfg!(target_os = "macos") {
        // On OS X 10.9 and later, LLVM's libc++ is the default. On earlier
        // releases GCC's libstdc++ is default. Unfortunately we can't
        // reasonably detect which one we need (on older ones libc++ is
        // available and can be selected with -stdlib=lib++), so assume the
        // latest, at the cost of breaking the build on older OS releases
        // when LLVM was built against libstdc++.
        Some("c++")
    } else if cfg!(target_os = "freebsd") {
        Some("c++")
    } else {
        // Otherwise assume GCC's libstdc++.
        // This assumption is probably wrong on some platforms, but would need
        // testing on them.
        Some("stdc++")
    }
}

/// Get the names of libraries to link against.
fn get_link_libraries() -> Vec<String> {
    // Using --libnames in conjunction with --libdir is particularly important
    // for MSVC when LLVM is in a path with spaces, but it is generally less of
    // a hack than parsing linker flags output from --libs and --ldflags.
    llvm_config("--libnames")
        .split(&[' ', '\n'] as &[char])
        .filter(|s| !s.is_empty())
        .map(|name| {
            // --libnames gives library filenames. Extract only the name that
            // we need to pass to the linker.
            if cfg!(target_env = "msvc") {
                // LLVMfoo.lib
                assert!(name.ends_with(".lib"));
                &name[..name.len() - 4]
            } else {
                // libLLVMfoo.a
                assert!(name.starts_with("lib") && name.ends_with(".a"));
                &name[3..name.len() - 2]
            }
        })
        .map(str::to_owned)
        .collect::<Vec<String>>()
}

fn get_llvm_cflags() -> String {
    let output = llvm_config("--cflags");

    // llvm-config includes cflags from its own compilation with --cflags that
    // may not be relevant to us. In particularly annoying cases, these might
    // include flags that aren't understood by the default compiler we're
    // using. Unless requested otherwise, clean CFLAGS of options that are
    // known to be possibly-harmful.
    let no_clean = env::var_os(&*ENV_NO_CLEAN_CFLAGS).is_some();
    if no_clean || cfg!(target_env = "msvc") {
        // MSVC doesn't accept -W... options, so don't try to strip them and
        // possibly strip something that should be retained. Also do nothing if
        // the user requests it.
        return output;
    }

    llvm_config("--cflags")
        .split(&[' ', '\n'][..])
        .filter(|word| !word.starts_with("-W"))
        .collect::<Vec<_>>()
        .join(" ")
}

fn is_llvm_debug() -> bool {
    // Has to be either Debug or Release
    llvm_config("--build-mode").contains("Debug")
}

fn main() {
    // Behavior can be significantly affected by these vars.
    println!("cargo:rerun-if-env-changed={}", &*ENV_LLVM_PREFIX);
    println!("cargo:rerun-if-env-changed={}", &*ENV_IGNORE_BLACKLIST);
    println!("cargo:rerun-if-env-changed={}", &*ENV_STRICT_VERSIONING);
    println!("cargo:rerun-if-env-changed={}", &*ENV_NO_CLEAN_CFLAGS);
    println!("cargo:rerun-if-env-changed={}", &*ENV_USE_DEBUG_MSVCRT);
    println!("cargo:rerun-if-env-changed={}", &*ENV_FORCE_FFI);

    if cfg!(feature = "no-llvm-linking") && cfg!(feature = "disable-alltargets-init") {
        // exit early as we don't need to do anything and llvm-config isn't needed at all
        return;
    }

    if LLVM_CONFIG_PATH.is_none() {
        println!("cargo:rustc-cfg=LLVM_SYS_NOT_FOUND");
        return;
    }

    // Build the extra wrapper functions.
    if !cfg!(feature = "disable-alltargets-init") {
        std::env::set_var("CFLAGS", get_llvm_cflags());
        cc::Build::new()
            .file("wrappers/target.c")
            .compile("targetwrappers");
    }

    if cfg!(feature = "no-llvm-linking") {
        return;
    }

    let libdir = llvm_config("--libdir");

    // Export information to other crates
    println!(
        "cargo:config_path={}",
        LLVM_CONFIG_PATH.clone().unwrap().display()
    ); // will be DEP_LLVM_CONFIG_PATH
    println!("cargo:libdir={}", libdir); // DEP_LLVM_LIBDIR

    // Link LLVM libraries
    println!("cargo:rustc-link-search=native={}", libdir);
    for name in get_link_libraries() {
        println!("cargo:rustc-link-lib=static={}", name);
    }

    // Link system libraries
    for name in get_system_libraries() {
        println!("cargo:rustc-link-lib=dylib={}", name);
    }

    let use_debug_msvcrt = env::var_os(&*ENV_USE_DEBUG_MSVCRT).is_some();
    if cfg!(target_env = "msvc") && (use_debug_msvcrt || is_llvm_debug()) {
        println!("cargo:rustc-link-lib={}", "msvcrtd");
    }

    // Link libffi if the user requested this workaround.
    // See https://bitbucket.org/tari/llvm-sys.rs/issues/12/
    let force_ffi = env::var_os(&*ENV_FORCE_FFI).is_some();
    if force_ffi {
        println!("cargo:rustc-link-lib=dylib={}", "ffi");
    }
}
<|endoftext|>


<commit_before>use std::os;
use std::io::Command;
use std::io::process::InheritFd;

fn main() {
    let dst = Path::new(os::getenv("OUT_DIR").unwrap());

    configure();
    build();
    install(&dst);
    println!("cargo:rustc-flags=-L {} -l termbox:static", dst.join("lib").display());
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = os::getenv("TARGET").unwrap();
    let mut cflags;
    if target.as_slice().contains("i686") {
        cflags = "-m32"
    } else if target.as_slice().contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = ""
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    os::setenv("CFLAGS", cflags);

    run(&mut cmd);
    os::unsetenv("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let termbox_dir = cargo_dir.join("termbox");
    let mut cmd = Command::new("./waf");
    cmd.cwd(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {}", cmd);
    assert!(cmd.stdout(InheritFd(1))
                .stderr(InheritFd(2))
                .status()
                .unwrap()
                .success());
}
<commit_msg>Add git submodule commands to build script<commit_after>use std::os;
use std::io::Command;
use std::io::process::InheritFd;

fn main() {
    let dst = Path::new(os::getenv("OUT_DIR").unwrap());

    submodule_init();
    submodule_update();
    configure();
    build();
    install(&dst);
    println!("cargo:rustc-flags=-L {} -l termbox:static", dst.join("lib").display());
}

fn submodule_init() {
    let mut cmd = git();
    cmd.arg("submodule");
    cmd.arg("init");
    run(&mut cmd);
}

fn submodule_update() {
    let mut cmd = git();
    cmd.arg("submodule");
    cmd.arg("update");
    run(&mut cmd);
}

fn git() -> Command {
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let mut cmd = Command::new("git");
    cmd.cwd(&cargo_dir);
    cmd
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = os::getenv("TARGET").unwrap();
    let mut cflags;
    if target.as_slice().contains("i686") {
        cflags = "-m32"
    } else if target.as_slice().contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = ""
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    os::setenv("CFLAGS", cflags);

    run(&mut cmd);
    os::unsetenv("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let termbox_dir = cargo_dir.join("termbox");
    let mut cmd = Command::new("./waf");
    cmd.cwd(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {}", cmd);
    assert!(cmd.stdout(InheritFd(1))
                .stderr(InheritFd(2))
                .status()
                .unwrap()
                .success());
}
<|endoftext|>


<commit_before>use std::{
    env,
    error::Error,
    fs::{self, File},
    io::Write,
    path::Path,
};

const SOURCE_DIR: &str = "resources/templates";

fn main() -> Result<(), Box<dyn Error>> {
    let out_dir = env::var("OUT_DIR")?;
    let root_dir = env::var("CARGO_MANIFEST_DIR")?;
    let dest_path = Path::new(&out_dir).join("templates.rs");
    let mut all_the_files = File::create(&dest_path)?;

    writeln!(&all_the_files, "{{")?;
    for f in fs::read_dir(SOURCE_DIR)? {
        let f = f?;

        if !f.file_type()?.is_file() {
            continue;
        }

        writeln!(
            &mut all_the_files,
            r#"m.insert("{name}", include_str!("{root_dir}/{name}"));"#,
            name = f.path().display(),
            root_dir = root_dir
        )?;
    }
    writeln!(&all_the_files, "}}")?;

    Ok(())
}
<commit_msg>Redo build.rs in a bazel-compatible way<commit_after>use std::{
    env,
    error::Error,
    fs::{self, File},
    io::Write,
    path::Path,
};

const SOURCE_DIR: &str = "resources/templates";

fn main() -> Result<(), Box<dyn Error>> {
    let out_dir = env::var("OUT_DIR")?;
    let dest_path = Path::new(&out_dir).join("templates.rs");
    let mut all_the_files = File::create(&dest_path)?;

    writeln!(&all_the_files, "{{")?;
    for f in fs::read_dir(SOURCE_DIR)? {
        let f = f?;

        if f.file_type()?.is_dir() {
            continue;
        }

        writeln!(
            &mut all_the_files,
            r#"m.insert("{name}", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/{name}")));"#,
            name = f.path().display()
        )?;
    }
    writeln!(&all_the_files, "}}")?;

    Ok(())
}
<|endoftext|>


<commit_before>extern crate gcc;

fn main() {
    let mut cfg = gcc::Config::new()
                      .include("decNumber")
                      .file("decNumber/decContext.c")
                      .file("decNumber/decDouble.c")
                      .file("decNumber/decNumber.c")
                      .file("decNumber/decPacked.c")
                      .file("decNumber/decQuad.c")
                      .file("decNumber/decSingle.c")
                      .file("decNumber/decimal128.c")
                      .file("decNumber/decimal32.c")
                      .file("decNumber/decimal64.c")
                      .compile("libdecNumber.a");
}
<commit_msg>Rustfmt.<commit_after>extern crate gcc;

fn main() {
    gcc::Config::new()
        .include("decNumber")
        .file("decNumber/decContext.c")
        .file("decNumber/decDouble.c")
        .file("decNumber/decNumber.c")
        .file("decNumber/decPacked.c")
        .file("decNumber/decQuad.c")
        .file("decNumber/decSingle.c")
        .file("decNumber/decimal128.c")
        .file("decNumber/decimal32.c")
        .file("decNumber/decimal64.c")
        .compile("libdecNumber.a");
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate phf_codegen;

use std::env;
use std::fs::File;
use std::io::{BufWriter, Write, Read};
use std::path::Path;

#[derive(Deserialize)]
struct Emoji {
    #[serde(rename(deserialize = "char"))]
    character: String,
    name: String,
}

fn main() {
    let path = Path::new(&env::var("OUT_DIR").unwrap()).join("emojis.rs");
    let mut file = BufWriter::new(File::create(&path).unwrap());

    let mut emoji_file = File::open("emojis.json").expect("Can't open emoji file");
    let mut buffer = String::new();
    emoji_file
        .read_to_string(&mut buffer)
        .expect("Can't read emoji file");

    let emojis: Vec<Emoji> = serde_json::from_str(&buffer).expect("Invalid JSON im emoji file");
    assert!(emojis.len() == 256);

    generate_standard_map(&mut file, &emojis);
    write!(&mut file, "\n").unwrap();
    generate_reverse_map(&mut file, &emojis);
}

fn generate_standard_map(file: &mut BufWriter<File>, emojis: &Vec<Emoji>) {
    write!(
        file,
        "/// Compile time generated lookup table for Emojis.\n"
    ).unwrap();
    write!(file, "/// \n").unwrap();
    write!(
        file,
        "static EMOJIS: phf::Map<u8, (&'static str, &'static str)> = "
    ).unwrap();

    let mut m = phf_codegen::Map::new();
    for (idx, emoji) in emojis.iter().enumerate().take(256) {
        m.entry(
            idx as u8,
            &format!("({:?}, {:?})", emoji.character, emoji.name),
        );
    }
    m.build(file).unwrap();
    write!(file, ";\n").unwrap();
}

fn generate_reverse_map(file: &mut BufWriter<File>, emojis: &Vec<Emoji>) {
    write!(
        file,
        "/// Compile time generated reverse lookup table for Emojis.\n"
    ).unwrap();
    write!(file, "/// \n").unwrap();
    write!(file, "static EMOJIS_REV: phf::Map<&'static str, u8> = ").unwrap();

    let mut m = phf_codegen::Map::new();
    for (idx, emoji) in emojis.iter().enumerate().take(256) {
        m.entry(emoji.character.clone(), &format!("{}", idx as u8));
    }
    m.build(file).unwrap();
    write!(file, ";\n").unwrap();
}
<commit_msg>Fix rustfmt error<commit_after>#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate phf_codegen;

use std::env;
use std::fs::File;
use std::io::{BufWriter, Write, Read};
use std::path::Path;

#[derive(Deserialize)]
struct Emoji {
    #[serde(rename(deserialize = "char"))]
    character: String,
    name: String,
}

fn main() {
    let path = Path::new(&env::var("OUT_DIR").unwrap()).join("emojis.rs");
    let mut file = BufWriter::new(File::create(&path).unwrap());

    let mut emoji_file = File::open("emojis.json").expect("Can't open emoji file");
    let mut buffer = String::new();
    emoji_file.read_to_string(&mut buffer).expect(
        "Can't read emoji file",
    );

    let emojis: Vec<Emoji> = serde_json::from_str(&buffer).expect("Invalid JSON im emoji file");
    assert!(emojis.len() == 256);

    generate_standard_map(&mut file, &emojis);
    write!(&mut file, "\n").unwrap();
    generate_reverse_map(&mut file, &emojis);
}

fn generate_standard_map(file: &mut BufWriter<File>, emojis: &Vec<Emoji>) {
    write!(
        file,
        "/// Compile time generated lookup table for Emojis.\n"
    ).unwrap();
    write!(file, "/// \n").unwrap();
    write!(
        file,
        "static EMOJIS: phf::Map<u8, (&'static str, &'static str)> = "
    ).unwrap();

    let mut m = phf_codegen::Map::new();
    for (idx, emoji) in emojis.iter().enumerate().take(256) {
        m.entry(
            idx as u8,
            &format!("({:?}, {:?})", emoji.character, emoji.name),
        );
    }
    m.build(file).unwrap();
    write!(file, ";\n").unwrap();
}

fn generate_reverse_map(file: &mut BufWriter<File>, emojis: &Vec<Emoji>) {
    write!(
        file,
        "/// Compile time generated reverse lookup table for Emojis.\n"
    ).unwrap();
    write!(file, "/// \n").unwrap();
    write!(file, "static EMOJIS_REV: phf::Map<&'static str, u8> = ").unwrap();

    let mut m = phf_codegen::Map::new();
    for (idx, emoji) in emojis.iter().enumerate().take(256) {
        m.entry(emoji.character.clone(), &format!("{}", idx as u8));
    }
    m.build(file).unwrap();
    write!(file, ";\n").unwrap();
}
<|endoftext|>


<commit_before>/* build assembly language files and glue them to rust code
 *
 * Reminder: this runs on the host build system using the host's architecture.
 * Thus, a Rust toolchain that can build executables for the host arch must be installed, and
 * the host architecture must be the default toolchain target - or this script will fail.
 * For example: building a RISC-V kernel on an X86-64 server requires a toolchain that
 * can output code for both architectures, and outputs X86-64 by default.
 *
 * (c) Chris Williams, 2018.
 *
 * See LICENSE for usage and copying.
 */

/* acceptable targets (selected using --target to cargo):
   riscv32imac-unknown-none-elf

   acceptable machines (selected as --features to cargo):
   sifive_u34 (SiFive E-series boards)
   qemu32_virt (32-bit Qemu Virt hardware environment)

   eg: cargo build --target riscv32imac-unknown-none-elf --features sifive_u34
*/

use std::process::Command;
use std::process::exit;
use std::env;
use std::fs;

extern crate regex;
use regex::Regex;

fn main()
{
  /* first, determine which CPU we're building for from target triple */
  let target_triple = env::var("TARGET").unwrap();
  let mut target_cpu = String::new();
  let mut target_arch = String::new();
  let mut target_abi = String::new();

  if target_triple.starts_with("riscv32") == true
  {
    target_cpu.push_str("riscv32");
    target_arch.push_str("rv32imac");
    target_abi.push_str("ilp32");
  }
  else
  {
    println!("Unknown target {}. Use --target to select a CPU type", target_triple);
    exit(1);
  }

  /* generate filenames for as and ar from CPU target */
  let gnu_as = String::from(format!("{}-elf-as", target_cpu));
  let gnu_ar = String::from(format!("{}-elf-ar", target_cpu));

  /* determine machine target from build system's environment variables */
  let mut target_machine = String::new();
  if env::var("CARGO_FEATURE_SIFIVE_U34").is_ok() == true
  {
    target_machine.push_str("sifive_u34");
  }
  else if env::var("CARGO_FEATURE_QEMU32_VIRT").is_ok() == true
  {
    target_machine.push_str("qemu32_virt");
  }
  else
  {
    println!("Cannot determine target machine. Use --features to select a device");
    exit(1);
  }

  let output_dir = env::var("OUT_DIR").unwrap();

  /* tell cargo to rebuild just these files change:
     linker scripts and any files in the platform's assembly code directory */
  println!("cargo:rerun-if-changed=src/platform/{}/{}/link.ld", target_cpu, target_machine);
  match fs::read_dir(format!("src/platform/{}/{}/asm", target_cpu, target_machine))
  {
    Ok(directory) => for file in directory
    {
      match file
      {
        Ok(file) =>
        {
          /* assume everything in the asm directory can be assembled if it is a file */
          if let Ok(metadata) = file.metadata()
          {
            if metadata.is_file() == true
            {
              println!("cargo:rerun-if-changed={}",file.path().to_str().unwrap());
              assemble(&output_dir, file.path().to_str().unwrap(),
                       &gnu_as, &gnu_ar, &target_arch, &target_abi);
            }
          }
        },
        _ => {} /* ignore empty/inaccessible directories */
      }
    },
    _ => {} /* ignore empty/inaccessible directories */
  }
}

/* assemble
   Attempt to assemble a given source file, which must be a .s file
   => output_dir = where to assemble our code
      path = path to .s file to assemble
      as_exec, ar_exec = assember and archive executable names,
      arch, abi = CPU architecture and ABI strings to pass to assembler
*/
fn assemble(output_dir: &String, path: &str,
            as_exec: &String, ar_exec: &String, arch: &String, abi: &String)
{
  /* create name from .s source file's path - extract just the leafname and drop the
     file extension. so extract 'start' from 'src/platform/blah/asm/start.s' */
  let re = Regex::new(r"(([A-Za-z0-9_]+)(/))+(?P<leaf>[A-Za-z0-9]+)(\.s)").unwrap();
  let matches = re.captures(&path);
  if matches.is_none() == true
  {
    return; /* skip non-conformant files */
  }

  /* extract leafname (sans .s extension) from the path */
  let leafname = &(matches.unwrap())["leaf"];

  /* build pathnames for the assembled .o and .a files */
  let mut object_file = output_dir.clone();
  object_file.push_str("/");
  object_file.push_str(leafname);
  object_file.push_str(".o");

  let mut archive_file = output_dir.clone();
  archive_file.push_str("/lib");
  archive_file.push_str(leafname);
  archive_file.push_str(".a");

  /* now let's try to assemble the thing - this is where errors become fatal */
  Command::new(as_exec).arg("-march").arg(arch).arg("-mabi").arg(abi)
                       .arg("-o").arg(&object_file).arg(path)
                       .status().expect(format!("Failed to assemble {}", path).as_str());
  Command::new(ar_exec).arg("crus").arg(archive_file).arg(object_file)
                       .status().expect(format!("Failed to archive {}", path).as_str());

  /* tell cargo where to find the goodies */
  println!("cargo:rustc-link-search=native={}", output_dir);
  println!("cargo:rustc-link-lib=static={}", leafname);
}
<commit_msg>Detect and fail on assembler and archiver errors. So far, the build script catches situations when the assembler or archiver fails to run, such as if the executable is missing. Now we catch failures during runtime of the assembler and archiver, such as fatal syntax errors in source code.<commit_after>/* build assembly language files and glue them to rust code
 *
 * Reminder: this runs on the host build system using the host's architecture.
 * Thus, a Rust toolchain that can build executables for the host arch must be installed, and
 * the host architecture must be the default toolchain target - or this script will fail.
 * For example: building a RISC-V kernel on an X86-64 server requires a toolchain that
 * can output code for both architectures, and outputs X86-64 by default.
 *
 * (c) Chris Williams, 2018.
 *
 * See LICENSE for usage and copying.
 */

/* acceptable targets (selected using --target to cargo):
   riscv32imac-unknown-none-elf

   acceptable machines (selected as --features to cargo):
   sifive_u34 (SiFive E-series boards)
   qemu32_virt (32-bit Qemu Virt hardware environment)

   eg: cargo build --target riscv32imac-unknown-none-elf --features sifive_u34
*/

use std::process::Command;
use std::process::exit;
use std::env;
use std::fs;

extern crate regex;
use regex::Regex;

fn main()
{
  /* first, determine which CPU we're building for from target triple */
  let target_triple = env::var("TARGET").unwrap();
  let mut target_cpu = String::new();
  let mut target_arch = String::new();
  let mut target_abi = String::new();

  if target_triple.starts_with("riscv32") == true
  {
    target_cpu.push_str("riscv32");
    target_arch.push_str("rv32imac");
    target_abi.push_str("ilp32");
  }
  else
  {
    println!("Unknown target {}. Use --target to select a CPU type", target_triple);
    exit(1);
  }

  /* generate filenames for as and ar from CPU target */
  let gnu_as = String::from(format!("{}-elf-as", target_cpu));
  let gnu_ar = String::from(format!("{}-elf-ar", target_cpu));

  /* determine machine target from build system's environment variables */
  let mut target_machine = String::new();
  if env::var("CARGO_FEATURE_SIFIVE_U34").is_ok() == true
  {
    target_machine.push_str("sifive_u34");
  }
  else if env::var("CARGO_FEATURE_QEMU32_VIRT").is_ok() == true
  {
    target_machine.push_str("qemu32_virt");
  }
  else
  {
    println!("Cannot determine target machine. Use --features to select a device");
    exit(1);
  }

  let output_dir = env::var("OUT_DIR").unwrap();

  /* tell cargo to rebuild just these files change:
     linker scripts and any files in the platform's assembly code directory */
  println!("cargo:rerun-if-changed=src/platform/{}/{}/link.ld", target_cpu, target_machine);
  match fs::read_dir(format!("src/platform/{}/{}/asm", target_cpu, target_machine))
  {
    Ok(directory) => for file in directory
    {
      match file
      {
        Ok(file) =>
        {
          /* assume everything in the asm directory can be assembled if it is a file */
          if let Ok(metadata) = file.metadata()
          {
            if metadata.is_file() == true
            {
              println!("cargo:rerun-if-changed={}",file.path().to_str().unwrap());
              assemble(&output_dir, file.path().to_str().unwrap(),
                       &gnu_as, &gnu_ar, &target_arch, &target_abi);
            }
          }
        },
        _ => {} /* ignore empty/inaccessible directories */
      }
    },
    _ => {} /* ignore empty/inaccessible directories */
  }
}

/* assemble
   Attempt to assemble a given source file, which must be a .s file
   => output_dir = where to assemble our code
      path = path to .s file to assemble
      as_exec, ar_exec = assember and archive executable names,
      arch, abi = CPU architecture and ABI strings to pass to assembler
*/
fn assemble(output_dir: &String, path: &str,
            as_exec: &String, ar_exec: &String, arch: &String, abi: &String)
{
  /* create name from .s source file's path - extract just the leafname and drop the
     file extension. so extract 'start' from 'src/platform/blah/asm/start.s' */
  let re = Regex::new(r"(([A-Za-z0-9_]+)(/))+(?P<leaf>[A-Za-z0-9]+)(\.s)").unwrap();
  let matches = re.captures(&path);
  if matches.is_none() == true
  {
    return; /* skip non-conformant files */
  }

  /* extract leafname (sans .s extension) from the path */
  let leafname = &(matches.unwrap())["leaf"];

  /* build pathnames for the assembled .o and .a files */
  let mut object_file = output_dir.clone();
  object_file.push_str("/");
  object_file.push_str(leafname);
  object_file.push_str(".o");

  let mut archive_file = output_dir.clone();
  archive_file.push_str("/lib");
  archive_file.push_str(leafname);
  archive_file.push_str(".a");

  /* now let's try to assemble the thing - this is where errors become fatal */
  if Command::new(as_exec).arg("-march").arg(arch).arg("-mabi").arg(abi)
                       .arg("-o").arg(&object_file).arg(path)
                       .status().expect(format!("Failed to assemble {}", path).as_str())
                       .code() != Some(0)
  {
    panic!("Assembler rejected {}", path);
  }

  if Command::new(ar_exec).arg("crus").arg(archive_file).arg(object_file)
                       .status().expect(format!("Failed to archive {}", path).as_str())
                       .code() != Some(0)
  {
    panic!("Archiver rejected {}", path);
  }

  /* tell cargo where to find the goodies */
  println!("cargo:rustc-link-search=native={}", output_dir);
  println!("cargo:rustc-link-lib=static={}", leafname);
}
<|endoftext|>


<commit_before>#![allow(clippy::option_if_let_else)]

use std::env;
use std::fs;
use std::path::Path;
use std::process::{Command, ExitStatus, Stdio};
use std::str;

#[cfg(all(feature = "backtrace", not(feature = "std")))]
compile_error! {
    "`backtrace` feature without `std` feature is not supported"
}

// This code exercises the surface area that we expect of the std Backtrace
// type. If the current toolchain is able to compile it, we go ahead and use
// backtrace in anyhow.
const PROBE: &str = r#"
    #![feature(backtrace)]
    #![allow(dead_code)]

    use std::backtrace::{Backtrace, BacktraceStatus};
    use std::error::Error;
    use std::fmt::{self, Display};

    #[derive(Debug)]
    struct E;

    impl Display for E {
        fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
            unimplemented!()
        }
    }

    impl Error for E {
        fn backtrace(&self) -> Option<&Backtrace> {
            let backtrace = Backtrace::capture();
            match backtrace.status() {
                BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {}
            }
            unimplemented!()
        }
    }
"#;

fn main() {
    if cfg!(feature = "std") {
        match compile_probe() {
            Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
            _ => {}
        }
    }

    let rustc = match rustc_minor_version() {
        Some(rustc) => rustc,
        None => return,
    };

    if rustc < 51 {
        println!("cargo:rustc-cfg=anyhow_no_ptr_addr_of");
    }

    if rustc < 52 {
        println!("cargo:rustc-cfg=anyhow_no_fmt_arguments_as_str");
    }
}

fn compile_probe() -> Option<ExitStatus> {
    let rustc = env::var_os("RUSTC")?;
    let out_dir = env::var_os("OUT_DIR")?;
    let probefile = Path::new(&out_dir).join("probe.rs");
    fs::write(&probefile, PROBE).ok()?;

    // Make sure to pick up Cargo rustc configuration.
    let mut cmd = if let Some(wrapper) = env::var_os("CARGO_RUSTC_WRAPPER") {
        let mut cmd = Command::new(wrapper);
        // The wrapper's first argument is supposed to be the path to rustc.
        cmd.arg(rustc);
        cmd
    } else {
        Command::new(rustc)
    };

    cmd.stderr(Stdio::null())
        .arg("--edition=2018")
        .arg("--crate-name=anyhow_build")
        .arg("--crate-type=lib")
        .arg("--emit=metadata")
        .arg("--out-dir")
        .arg(out_dir)
        .arg(probefile);

    if let Some(target) = env::var_os("TARGET") {
        cmd.arg("--target").arg(target);
    }

    // If Cargo wants to set RUSTFLAGS, use that.
    if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
        if !rustflags.is_empty() {
            for arg in rustflags.split('\x1f') {
                cmd.arg(arg);
            }
        }
    }

    cmd.status().ok()
}

fn rustc_minor_version() -> Option<u32> {
    let rustc = env::var_os("RUSTC")?;
    let output = Command::new(rustc).arg("--version").output().ok()?;
    let version = str::from_utf8(&output.stdout).ok()?;
    let mut pieces = version.split('.');
    if pieces.next() != Some("rustc 1") {
        return None;
    }
    pieces.next()?.parse().ok()
}
<commit_msg>fix RUSTC_WRAPPER env var<commit_after>#![allow(clippy::option_if_let_else)]

use std::env;
use std::fs;
use std::path::Path;
use std::process::{Command, ExitStatus, Stdio};
use std::str;

#[cfg(all(feature = "backtrace", not(feature = "std")))]
compile_error! {
    "`backtrace` feature without `std` feature is not supported"
}

// This code exercises the surface area that we expect of the std Backtrace
// type. If the current toolchain is able to compile it, we go ahead and use
// backtrace in anyhow.
const PROBE: &str = r#"
    #![feature(backtrace)]
    #![allow(dead_code)]

    use std::backtrace::{Backtrace, BacktraceStatus};
    use std::error::Error;
    use std::fmt::{self, Display};

    #[derive(Debug)]
    struct E;

    impl Display for E {
        fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
            unimplemented!()
        }
    }

    impl Error for E {
        fn backtrace(&self) -> Option<&Backtrace> {
            let backtrace = Backtrace::capture();
            match backtrace.status() {
                BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {}
            }
            unimplemented!()
        }
    }
"#;

fn main() {
    if cfg!(feature = "std") {
        match compile_probe() {
            Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
            _ => {}
        }
    }

    let rustc = match rustc_minor_version() {
        Some(rustc) => rustc,
        None => return,
    };

    if rustc < 51 {
        println!("cargo:rustc-cfg=anyhow_no_ptr_addr_of");
    }

    if rustc < 52 {
        println!("cargo:rustc-cfg=anyhow_no_fmt_arguments_as_str");
    }
}

fn compile_probe() -> Option<ExitStatus> {
    let rustc = env::var_os("RUSTC")?;
    let out_dir = env::var_os("OUT_DIR")?;
    let probefile = Path::new(&out_dir).join("probe.rs");
    fs::write(&probefile, PROBE).ok()?;

    // Make sure to pick up Cargo rustc configuration.
    let mut cmd = if let Some(wrapper) = env::var_os("RUSTC_WRAPPER") {
        let mut cmd = Command::new(wrapper);
        // The wrapper's first argument is supposed to be the path to rustc.
        cmd.arg(rustc);
        cmd
    } else {
        Command::new(rustc)
    };

    cmd.stderr(Stdio::null())
        .arg("--edition=2018")
        .arg("--crate-name=anyhow_build")
        .arg("--crate-type=lib")
        .arg("--emit=metadata")
        .arg("--out-dir")
        .arg(out_dir)
        .arg(probefile);

    if let Some(target) = env::var_os("TARGET") {
        cmd.arg("--target").arg(target);
    }

    // If Cargo wants to set RUSTFLAGS, use that.
    if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
        if !rustflags.is_empty() {
            for arg in rustflags.split('\x1f') {
                cmd.arg(arg);
            }
        }
    }

    cmd.status().ok()
}

fn rustc_minor_version() -> Option<u32> {
    let rustc = env::var_os("RUSTC")?;
    let output = Command::new(rustc).arg("--version").output().ok()?;
    let version = str::from_utf8(&output.stdout).ok()?;
    let mut pieces = version.split('.');
    if pieces.next() != Some("rustc 1") {
        return None;
    }
    pieces.next()?.parse().ok()
}
<|endoftext|>


<commit_before>extern crate gl_generator;

use gl_generator::{Api, Fallbacks, Profile, Registry};
use std::{env, fs::File, path::PathBuf};

fn main() {
    let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());

    println!("cargo:rerun-if-changed=build.rs");

    if env::var_os("CARGO_FEATURE_BACKEND_EGL").is_some() {
        let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
        Registry::new(
            Api::Egl,
            (1, 5),
            Profile::Core,
            Fallbacks::All,
            [
                "EGL_KHR_create_context",
                "EGL_EXT_create_context_robustness",
                "EGL_KHR_create_context_no_error",
                "EGL_KHR_platform_x11",
                "EGL_KHR_platform_android",
                "EGL_KHR_platform_wayland",
                "EGL_KHR_platform_gbm",
                "EGL_EXT_platform_base",
                "EGL_EXT_platform_x11",
                "EGL_MESA_platform_gbm",
                "EGL_EXT_platform_wayland",
                "EGL_EXT_platform_device",
                "EGL_KHR_image_base",
            ],
        ).write_bindings(gl_generator::GlobalGenerator, &mut file)
        .unwrap();
    }

    if env::var_os("CARGO_FEATURE_RENDERER_GL").is_some() {
        let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
        Registry::new(
            Api::Gles2,
            (3, 2),
            Profile::Compatibility,
            Fallbacks::None,
            ["GL_OES_EGL_image"],
        ).write_bindings(gl_generator::StructGenerator, &mut file)
        .unwrap();
    }
}
<commit_msg>build: fix build without gl-features<commit_after>#[cfg(any(feature = "backend_egl", feature = "backend_gl"))]
extern crate gl_generator;

#[cfg(any(feature = "backend_egl", feature = "backend_gl"))]
use gl_generator::{Api, Fallbacks, Profile, Registry};
use std::{env, fs::File, path::PathBuf};

#[cfg(any(feature = "backend_egl", feature = "backend_gl"))]
fn main() {
    let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());

    println!("cargo:rerun-if-changed=build.rs");

    if env::var_os("CARGO_FEATURE_BACKEND_EGL").is_some() {
        let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
        Registry::new(
            Api::Egl,
            (1, 5),
            Profile::Core,
            Fallbacks::All,
            [
                "EGL_KHR_create_context",
                "EGL_EXT_create_context_robustness",
                "EGL_KHR_create_context_no_error",
                "EGL_KHR_platform_x11",
                "EGL_KHR_platform_android",
                "EGL_KHR_platform_wayland",
                "EGL_KHR_platform_gbm",
                "EGL_EXT_platform_base",
                "EGL_EXT_platform_x11",
                "EGL_MESA_platform_gbm",
                "EGL_EXT_platform_wayland",
                "EGL_EXT_platform_device",
                "EGL_KHR_image_base",
            ],
        ).write_bindings(gl_generator::GlobalGenerator, &mut file)
        .unwrap();
    }

    if env::var_os("CARGO_FEATURE_RENDERER_GL").is_some() {
        let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
        Registry::new(
            Api::Gles2,
            (3, 2),
            Profile::Compatibility,
            Fallbacks::None,
            ["GL_OES_EGL_image"],
        ).write_bindings(gl_generator::StructGenerator, &mut file)
        .unwrap();
    }
}

#[cfg(not(any(feature = "backend_egl", feature = "backend_gl")))]
fn main() {}<|endoftext|>


<commit_before>#[cfg(feature = "serialization-protobuf")]
extern crate protoc_rust;

#[cfg(feature = "serialization-protobuf")]
fn assert_protobuf_version(version: String) {
    use std::process::{Command, Stdio};
    let protoc = Command::new("protoc")
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .args(&["--version"])
        .spawn()
        .unwrap();
    let version_output = protoc.wait_with_output().unwrap();
    assert!(version_output.status.success());
    assert_eq!(
        String::from_utf8(version_output.stdout).unwrap().trim(),
        version.trim()
    );
}

#[cfg(feature = "serialization-protobuf")]
fn build_protobuf(out_dir: &str, input: &[&str], includes: &[&str]) {
    use self::protoc_rust::{run, Args};
    run(Args {
        out_dir,
        input,
        includes,
        customize: Default::default(),
    }).expect("protoc");
}

#[cfg(feature = "serialization-protobuf")]
fn build_protobuf_schemata() {
    use std::fs::File;
    use std::io::Read;
    let mut version_string = String::new();
    let mut version_pin =
        File::open("PROTOC_VERSION").expect("protoc version pin `PROTOC_VERSION` file is missing");
    version_pin.read_to_string(&mut version_string).expect("cannot read protoc pin file");
    assert_protobuf_version(version_string);
    build_protobuf("src/proto", &["protobuf/proof.proto"], &[]);
}

fn main() {
    #[cfg(feature = "serialization-protobuf")]
    build_protobuf_schemata();
}
<commit_msg>rustfmt<commit_after>#[cfg(feature = "serialization-protobuf")]
extern crate protoc_rust;

#[cfg(feature = "serialization-protobuf")]
fn assert_protobuf_version(version: String) {
    use std::process::{Command, Stdio};
    let protoc = Command::new("protoc")
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .args(&["--version"])
        .spawn()
        .unwrap();
    let version_output = protoc.wait_with_output().unwrap();
    assert!(version_output.status.success());
    assert_eq!(
        String::from_utf8(version_output.stdout).unwrap().trim(),
        version.trim()
    );
}

#[cfg(feature = "serialization-protobuf")]
fn build_protobuf(out_dir: &str, input: &[&str], includes: &[&str]) {
    use self::protoc_rust::{run, Args};
    run(Args {
        out_dir,
        input,
        includes,
        customize: Default::default(),
    }).expect("protoc");
}

#[cfg(feature = "serialization-protobuf")]
fn build_protobuf_schemata() {
    use std::fs::File;
    use std::io::Read;
    let mut version_string = String::new();
    let mut version_pin =
        File::open("PROTOC_VERSION").expect("protoc version pin `PROTOC_VERSION` file is missing");
    version_pin
        .read_to_string(&mut version_string)
        .expect("cannot read protoc pin file");
    assert_protobuf_version(version_string);
    build_protobuf("src/proto", &["protobuf/proof.proto"], &[]);
}

fn main() {
    #[cfg(feature = "serialization-protobuf")]
    build_protobuf_schemata();
}
<|endoftext|>


<commit_before>extern crate cc;

fn main() {
    let mut build = cc::Build::new();
    build
        .file("src/PDCurses/pdcurses/addch.c") //Common PDCurses files
        .file("src/PDCurses/pdcurses/addchstr.c")
        .file("src/PDCurses/pdcurses/addstr.c")
        .file("src/PDCurses/pdcurses/attr.c")
        .file("src/PDCurses/pdcurses/beep.c")
        .file("src/PDCurses/pdcurses/bkgd.c")
        .file("src/PDCurses/pdcurses/border.c")
        .file("src/PDCurses/pdcurses/clear.c")
        .file("src/PDCurses/pdcurses/color.c")
        .file("src/PDCurses/pdcurses/debug.c")
        .file("src/PDCurses/pdcurses/delch.c")
        .file("src/PDCurses/pdcurses/deleteln.c")
        .file("src/PDCurses/pdcurses/deprec.c")
        .file("src/PDCurses/pdcurses/getch.c")
        .file("src/PDCurses/pdcurses/getstr.c")
        .file("src/PDCurses/pdcurses/getyx.c")
        .file("src/PDCurses/pdcurses/inch.c")
        .file("src/PDCurses/pdcurses/inchstr.c")
        .file("src/PDCurses/pdcurses/initscr.c")
        .file("src/PDCurses/pdcurses/inopts.c")
        .file("src/PDCurses/pdcurses/insch.c")
        .file("src/PDCurses/pdcurses/insstr.c")
        .file("src/PDCurses/pdcurses/instr.c")
        .file("src/PDCurses/pdcurses/kernel.c")
        .file("src/PDCurses/pdcurses/keyname.c")
        .file("src/PDCurses/pdcurses/mouse.c")
        .file("src/PDCurses/pdcurses/move.c")
        .file("src/PDCurses/pdcurses/outopts.c")
        .file("src/PDCurses/pdcurses/overlay.c")
        .file("src/PDCurses/pdcurses/pad.c")
        .file("src/PDCurses/pdcurses/panel.c")
        .file("src/PDCurses/pdcurses/printw.c")
        .file("src/PDCurses/pdcurses/refresh.c")
        .file("src/PDCurses/pdcurses/scanw.c")
        .file("src/PDCurses/pdcurses/scr_dump.c")
        .file("src/PDCurses/pdcurses/scroll.c")
        .file("src/PDCurses/pdcurses/slk.c")
        .file("src/PDCurses/pdcurses/termattr.c")
        .file("src/PDCurses/pdcurses/terminfo.c")
        .file("src/PDCurses/pdcurses/touch.c")
        .file("src/PDCurses/pdcurses/util.c")
        .file("src/PDCurses/pdcurses/window.c")
        .include("src/PDCurses")
        .define("PDC_WIDE", Some("Y")) // Build with wide-character (Unicode) support
        .define("PDC_FORCE_UTF8", Some("Y")) // Makes PDCurses ignore the system locale, and treat all narrow-character strings as UTF-8
        .define("PDC_RGB", Some("Y")); // Use RGB colors, it's what most people expect them to be

    flavor_specifics(&mut build);

    build.compile("libpdcurses.a");
}

// Use win32a if it's chosen, or if no flavor is chosen.
#[cfg(any(feature = "win32a", all(not(feature="win32"), not(feature="win32a"))))]
fn flavor_specifics(build: &mut cc::Build) {
    println!("cargo:rustc-link-lib=dylib=user32");
    println!("cargo:rustc-link-lib=dylib=gdi32");
    println!("cargo:rustc-link-lib=dylib=comdlg32");

    build
        .file("src/PDCurses/win32a/pdcclip.c")
        .file("src/PDCurses/win32a/pdcdisp.c")
        .file("src/PDCurses/win32a/pdcgetsc.c")
        .file("src/PDCurses/win32a/pdckbd.c")
        .file("src/PDCurses/win32a/pdcscrn.c")
        .file("src/PDCurses/win32a/pdcsetsc.c")
        .file("src/PDCurses/win32a/pdcutil.c");
}

#[cfg(feature = "win32")]
fn flavor_specifics(build: &mut cc::Build) {
    println!("cargo:rustc-link-lib=dylib=user32");

    build
        .file("src/PDCurses/win32/pdcclip.c")
        .file("src/PDCurses/win32/pdcdisp.c")
        .file("src/PDCurses/win32/pdcgetsc.c")
        .file("src/PDCurses/win32/pdckbd.c")
        .file("src/PDCurses/win32/pdcscrn.c")
        .file("src/PDCurses/win32/pdcsetsc.c")
        .file("src/PDCurses/win32/pdcutil.c");
}<commit_msg>Explicitly link the shell32.<commit_after>extern crate cc;

fn main() {
    let mut build = cc::Build::new();
    build
        .file("src/PDCurses/pdcurses/addch.c") //Common PDCurses files
        .file("src/PDCurses/pdcurses/addchstr.c")
        .file("src/PDCurses/pdcurses/addstr.c")
        .file("src/PDCurses/pdcurses/attr.c")
        .file("src/PDCurses/pdcurses/beep.c")
        .file("src/PDCurses/pdcurses/bkgd.c")
        .file("src/PDCurses/pdcurses/border.c")
        .file("src/PDCurses/pdcurses/clear.c")
        .file("src/PDCurses/pdcurses/color.c")
        .file("src/PDCurses/pdcurses/debug.c")
        .file("src/PDCurses/pdcurses/delch.c")
        .file("src/PDCurses/pdcurses/deleteln.c")
        .file("src/PDCurses/pdcurses/deprec.c")
        .file("src/PDCurses/pdcurses/getch.c")
        .file("src/PDCurses/pdcurses/getstr.c")
        .file("src/PDCurses/pdcurses/getyx.c")
        .file("src/PDCurses/pdcurses/inch.c")
        .file("src/PDCurses/pdcurses/inchstr.c")
        .file("src/PDCurses/pdcurses/initscr.c")
        .file("src/PDCurses/pdcurses/inopts.c")
        .file("src/PDCurses/pdcurses/insch.c")
        .file("src/PDCurses/pdcurses/insstr.c")
        .file("src/PDCurses/pdcurses/instr.c")
        .file("src/PDCurses/pdcurses/kernel.c")
        .file("src/PDCurses/pdcurses/keyname.c")
        .file("src/PDCurses/pdcurses/mouse.c")
        .file("src/PDCurses/pdcurses/move.c")
        .file("src/PDCurses/pdcurses/outopts.c")
        .file("src/PDCurses/pdcurses/overlay.c")
        .file("src/PDCurses/pdcurses/pad.c")
        .file("src/PDCurses/pdcurses/panel.c")
        .file("src/PDCurses/pdcurses/printw.c")
        .file("src/PDCurses/pdcurses/refresh.c")
        .file("src/PDCurses/pdcurses/scanw.c")
        .file("src/PDCurses/pdcurses/scr_dump.c")
        .file("src/PDCurses/pdcurses/scroll.c")
        .file("src/PDCurses/pdcurses/slk.c")
        .file("src/PDCurses/pdcurses/termattr.c")
        .file("src/PDCurses/pdcurses/terminfo.c")
        .file("src/PDCurses/pdcurses/touch.c")
        .file("src/PDCurses/pdcurses/util.c")
        .file("src/PDCurses/pdcurses/window.c")
        .include("src/PDCurses")
        .define("PDC_WIDE", Some("Y")) // Build with wide-character (Unicode) support
        .define("PDC_FORCE_UTF8", Some("Y")) // Makes PDCurses ignore the system locale, and treat all narrow-character strings as UTF-8
        .define("PDC_RGB", Some("Y")); // Use RGB colors, it's what most people expect them to be

    flavor_specifics(&mut build);

    build.compile("libpdcurses.a");
}

// Use win32a if it's chosen, or if no flavor is chosen.
#[cfg(any(feature = "win32a", all(not(feature="win32"), not(feature="win32a"))))]
fn flavor_specifics(build: &mut cc::Build) {
    println!("cargo:rustc-link-lib=dylib=user32");
    println!("cargo:rustc-link-lib=dylib=gdi32");
    println!("cargo:rustc-link-lib=dylib=comdlg32");
    println!("cargo:rustc-link-lib=dylib=shell32");

    build
        .file("src/PDCurses/win32a/pdcclip.c")
        .file("src/PDCurses/win32a/pdcdisp.c")
        .file("src/PDCurses/win32a/pdcgetsc.c")
        .file("src/PDCurses/win32a/pdckbd.c")
        .file("src/PDCurses/win32a/pdcscrn.c")
        .file("src/PDCurses/win32a/pdcsetsc.c")
        .file("src/PDCurses/win32a/pdcutil.c");
}

#[cfg(feature = "win32")]
fn flavor_specifics(build: &mut cc::Build) {
    println!("cargo:rustc-link-lib=dylib=user32");

    build
        .file("src/PDCurses/win32/pdcclip.c")
        .file("src/PDCurses/win32/pdcdisp.c")
        .file("src/PDCurses/win32/pdcgetsc.c")
        .file("src/PDCurses/win32/pdckbd.c")
        .file("src/PDCurses/win32/pdcscrn.c")
        .file("src/PDCurses/win32/pdcsetsc.c")
        .file("src/PDCurses/win32/pdcutil.c");
}<|endoftext|>