<commit_before>use super::{
    directory_stack::DirectoryStack,
    escape::{escape, unescape},
    variables::Variables,
};
use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use liner::Completer;
use smallvec::SmallVec;
use std::{iter, str};

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub(crate) struct IonFileCompleter {
    /// A pointer to the directory stack in the shell.
    dir_stack: *const DirectoryStack,
    /// A pointer to the variables map in the shell.
    vars: *const Variables,
    /// The directory the expansion takes place in
    path: String,
}

impl IonFileCompleter {
    pub(crate) fn new(
        path: Option<&str>,
        dir_stack: *const DirectoryStack,
        vars: *const Variables,
    ) -> IonFileCompleter {
        let mut path = path.unwrap_or("").to_string();
        if !path.is_empty() && !path.ends_with('/') {
            path.push('/');
        }
        IonFileCompleter { dir_stack, vars, path }
    }
}

impl Completer for IonFileCompleter {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        if let Some(expanded) = unsafe { (*self.vars).tilde_expansion(start, &*self.dir_stack) } {
            // Now we obtain completions for the `expanded` form of the `start` value.
            let iterator = filename_completion(&expanded, &self.path);

            // We can do that by obtaining the index position where the tilde character
            // ends. We don't search with `~` because we also want to
            // handle other tilde variants.
            let t_index = start.find('/').unwrap_or(1);
            // `tilde` is the tilde pattern, and `search` is the pattern that follows.
            let (tilde, search) = start.split_at(t_index);

            if search.len() < 2 {
                // If the length of the search pattern is less than 2, the search pattern is
                // empty, and thus the completions actually contain files and directories in
                // the home directory.

                // The tilde pattern will actually be our `start` command in itself,
                // and the completed form will be all of the characters beyond the length of
                // the expanded form of the tilde pattern.
                iterator.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
            // To save processing time, we should get obtain the index position where our
            // search pattern begins, and re-use that index to slice the completions so
            // that we may re-add the tilde character with the completion that follows.
            } else if let Some(e_index) = expanded.rfind(search) {
                // And then we will need to take those completions and remove the expanded form
                // of the tilde pattern and replace it with that pattern yet again.
                iterator
                    .map(|completion| escape(&[tilde, &completion[e_index..]].concat()))
                    .collect()
            } else {
                Vec::new()
            }
        } else {
            filename_completion(&start, &self.path).collect()
        }
    }
}

#[auto_enum]
fn filename_completion<'a, 'b>(start: &'a str, path: &'a str) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string: SmallVec<[u8; 128]> = SmallVec::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push(b'/');
    } else {
        string.extend_from_slice(path.as_bytes());
    }

    for element in split_start {
        string.extend_from_slice(element.as_bytes());
        if element != "." && element != ".." {
            string.push(b'*');
        }
        string.push(b'/');
    }

    string.pop(); // pop out the last '/' character
    let string = unsafe { &str::from_utf8_unchecked(&string) };

    let globs = glob_with(
        string,
        MatchOptions {
            case_sensitive:              true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .and_then(|completions| {
        let mut completions = completions
            .filter_map(Result::ok)
            .filter_map(move |file| {
                let out = file.to_str()?.trim_start_matches(&path);
                let mut joined = String::with_capacity(out.len() + 3); // worst case senario
                if unescaped_start.starts_with("./") {
                    joined.push_str("./");
                }
                joined.push_str(out);
                if file.is_dir() {
                    joined.push('/');
                }
                Some(joined)
            })
            .peekable();

        if completions.peek().is_some() {
            Some(completions)
        } else {
            None
        }
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    a: Vec<A>,
    b: B,
}

impl<A, B> MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    pub(crate) fn new(a: Vec<A>, b: B) -> MultiCompleter<A, B> { MultiCompleter { a, b } }
}

impl<A, B> Completer for MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    fn completions(&self, start: &str) -> Vec<String> {
        let mut completions = self.b.completions(start);
        for x in &self.a {
            completions.extend_from_slice(&x.completions(start));
        }
        completions
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn filename_completion() {
        let completer = IonFileCompleter::new(None, &DirectoryStack::new(), &Variables::default());
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);

        assert_eq!(completer.completions("~"), vec!["~/"]);

        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}
<commit_msg>Don't suggest current with current buffer<commit_after>use super::{
    directory_stack::DirectoryStack,
    escape::{escape, unescape},
    variables::Variables,
};
use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use liner::Completer;
use smallvec::SmallVec;
use std::{iter, str};

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub(crate) struct IonFileCompleter {
    /// A pointer to the directory stack in the shell.
    dir_stack: *const DirectoryStack,
    /// A pointer to the variables map in the shell.
    vars: *const Variables,
    /// The directory the expansion takes place in
    path: String,
}

impl IonFileCompleter {
    pub(crate) fn new(
        path: Option<&str>,
        dir_stack: *const DirectoryStack,
        vars: *const Variables,
    ) -> IonFileCompleter {
        let mut path = path.unwrap_or("").to_string();
        if !path.is_empty() && !path.ends_with('/') {
            path.push('/');
        }
        IonFileCompleter { dir_stack, vars, path }
    }
}

impl Completer for IonFileCompleter {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        if let Some(expanded) = unsafe { (*self.vars).tilde_expansion(start, &*self.dir_stack) } {
            // Now we obtain completions for the `expanded` form of the `start` value.
            let iterator = filename_completion(&expanded, &self.path);

            // We can do that by obtaining the index position where the tilde character
            // ends. We don't search with `~` because we also want to
            // handle other tilde variants.
            let t_index = start.find('/').unwrap_or(1);
            // `tilde` is the tilde pattern, and `search` is the pattern that follows.
            let (tilde, search) = start.split_at(t_index);

            if search.len() < 2 {
                // If the length of the search pattern is less than 2, the search pattern is
                // empty, and thus the completions actually contain files and directories in
                // the home directory.

                // The tilde pattern will actually be our `start` command in itself,
                // and the completed form will be all of the characters beyond the length of
                // the expanded form of the tilde pattern.
                iterator.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
            // To save processing time, we should get obtain the index position where our
            // search pattern begins, and re-use that index to slice the completions so
            // that we may re-add the tilde character with the completion that follows.
            } else if let Some(e_index) = expanded.rfind(search) {
                // And then we will need to take those completions and remove the expanded form
                // of the tilde pattern and replace it with that pattern yet again.
                iterator
                    .map(|completion| escape(&[tilde, &completion[e_index..]].concat()))
                    .collect()
            } else {
                Vec::new()
            }
        } else {
            filename_completion(&start, &self.path).collect()
        }
    }
}

#[auto_enum]
fn filename_completion<'a, 'b>(start: &'a str, path: &'a str) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string: SmallVec<[u8; 128]> = SmallVec::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push(b'/');
    } else {
        string.extend_from_slice(path.as_bytes());
    }

    for element in split_start {
        string.extend_from_slice(element.as_bytes());
        if element != "." && element != ".." {
            string.push(b'*');
        }
        string.push(b'/');
    }

    string.pop(); // pop out the last '/' character
    let string = unsafe { &str::from_utf8_unchecked(&string) };

    let globs = glob_with(
        string,
        MatchOptions {
            case_sensitive:              true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .map(|completions| {
        completions.filter_map(Result::ok).filter_map(move |file| {
            let out = file.to_str()?.trim_start_matches(&path);
            let mut joined = String::with_capacity(out.len() + 3); // worst case senario
            if unescaped_start.starts_with("./") {
                joined.push_str("./");
            }
            joined.push_str(out);
            if file.is_dir() {
                joined.push('/');
            }
            Some(joined)
        })
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    a: Vec<A>,
    b: B,
}

impl<A, B> MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    pub(crate) fn new(a: Vec<A>, b: B) -> MultiCompleter<A, B> { MultiCompleter { a, b } }
}

impl<A, B> Completer for MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    fn completions(&self, start: &str) -> Vec<String> {
        let mut completions = self.b.completions(start);
        for x in &self.a {
            completions.extend_from_slice(&x.completions(start));
        }
        completions
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn filename_completion() {
        let completer = IonFileCompleter::new(None, &DirectoryStack::new(), &Variables::default());
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);

        assert_eq!(completer.completions("~"), vec!["~/"]);

        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}
<|endoftext|>


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

use driver::session;
use metadata::cstore;
use metadata::filesearch;

use std::hashmap::HashSet;
use std::{os, vec};

fn not_win32(os: session::Os) -> bool {
  os != session::OsWin32
}

pub fn get_rpath_flags(sess: session::Session, out_filename: &Path)
                    -> ~[~str] {
    let os = sess.targ_cfg.os;

    // No rpath on windows
    if os == session::OsWin32 {
        return ~[];
    }

    debug!("preparing the RPATH!");

    let sysroot = sess.filesearch.sysroot();
    let output = out_filename;
    let libs = cstore::get_used_crate_files(sess.cstore);
    // We don't currently rpath extern libraries, but we know
    // where rustrt is and we know every rust program needs it
    let libs = vec::append_one(libs, get_sysroot_absolute_rt_lib(sess));

    let rpaths = get_rpaths(os, sysroot, output, libs,
                            sess.opts.target_triple);
    rpaths_to_flags(rpaths)
}

fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path {
    let r = filesearch::relative_target_lib_path(sess.opts.target_triple);
    let mut p = sess.filesearch.sysroot().join(&r);
    p.push(os::dll_filename("rustrt"));
    p
}

pub fn rpaths_to_flags(rpaths: &[~str]) -> ~[~str] {
    // FIXME (#9639): This needs to handle non-utf8 paths
    rpaths.iter().map(|rpath| format!("-Wl,-rpath,{}",*rpath)).collect()
}

fn get_rpaths(os: session::Os,
              sysroot: &Path,
              output: &Path,
              libs: &[Path],
              target_triple: &str) -> ~[~str] {
    debug!("sysroot: {}", sysroot.display());
    debug!("output: {}", output.display());
    debug!("libs:");
    for libpath in libs.iter() {
        debug!("    {}", libpath.display());
    }
    debug!("target_triple: {}", target_triple);

    // Use relative paths to the libraries. Binaries can be moved
    // as long as they maintain the relative relationship to the
    // crates they depend on.
    let rel_rpaths = get_rpaths_relative_to_output(os, output, libs);

    // Make backup absolute paths to the libraries. Binaries can
    // be moved as long as the crates they link against don't move.
    let abs_rpaths = get_absolute_rpaths(libs);

    // And a final backup rpath to the global library location.
    let fallback_rpaths = ~[get_install_prefix_rpath(target_triple)];

    fn log_rpaths(desc: &str, rpaths: &[~str]) {
        debug!("{} rpaths:", desc);
        for rpath in rpaths.iter() {
            debug!("    {}", *rpath);
        }
    }

    log_rpaths("relative", rel_rpaths);
    log_rpaths("absolute", abs_rpaths);
    log_rpaths("fallback", fallback_rpaths);

    let mut rpaths = rel_rpaths;
    rpaths.push_all(abs_rpaths);
    rpaths.push_all(fallback_rpaths);

    // Remove duplicates
    let rpaths = minimize_rpaths(rpaths);
    return rpaths;
}

fn get_rpaths_relative_to_output(os: session::Os,
                                 output: &Path,
                                 libs: &[Path]) -> ~[~str] {
    libs.iter().map(|a| get_rpath_relative_to_output(os, output, a)).collect()
}

pub fn get_rpath_relative_to_output(os: session::Os,
                                    output: &Path,
                                    lib: &Path)
                                 -> ~str {
    use std::os;

    assert!(not_win32(os));

    // Mac doesn't appear to support $ORIGIN
    let prefix = match os {
        session::OsAndroid | session::OsLinux | session::OsFreebsd
                          => "$ORIGIN",
        session::OsMacos => "@executable_path",
        session::OsWin32 => unreachable!()
    };

    let mut lib = os::make_absolute(lib);
    lib.pop();
    let mut output = os::make_absolute(output);
    output.pop();
    let relative = lib.path_relative_from(&output);
    let relative = relative.expect("could not create rpath relative to output");
    // FIXME (#9639): This needs to handle non-utf8 paths
    prefix+"/"+relative.as_str().expect("non-utf8 component in path")
}

fn get_absolute_rpaths(libs: &[Path]) -> ~[~str] {
    libs.iter().map(|a| get_absolute_rpath(a)).collect()
}

pub fn get_absolute_rpath(lib: &Path) -> ~str {
    let mut p = os::make_absolute(lib);
    p.pop();
    // FIXME (#9639): This needs to handle non-utf8 paths
    p.as_str().expect("non-utf8 component in rpath").to_owned()
}

pub fn get_install_prefix_rpath(target_triple: &str) -> ~str {
    let install_prefix = env!("CFG_PREFIX");

    let tlib = filesearch::relative_target_lib_path(target_triple);
    let mut path = Path::new(install_prefix);
    path.push(&tlib);
    let path = os::make_absolute(&path);
    // FIXME (#9639): This needs to handle non-utf8 paths
    path.as_str().expect("non-utf8 component in rpath").to_owned()
}

pub fn minimize_rpaths(rpaths: &[~str]) -> ~[~str] {
    let mut set = HashSet::new();
    let mut minimized = ~[];
    for rpath in rpaths.iter() {
        if set.insert(rpath.as_slice()) {
            minimized.push(rpath.clone());
        }
    }
    minimized
}

#[cfg(unix, test)]
mod test {
    use std::os;

    // FIXME(#2119): the outer attribute should be #[cfg(unix, test)], then
    // these redundant #[cfg(test)] blocks can be removed
    #[cfg(test)]
    #[cfg(test)]
    use back::rpath::{get_absolute_rpath, get_install_prefix_rpath};
    use back::rpath::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
    use driver::session;

    #[test]
    fn test_rpaths_to_flags() {
        let flags = rpaths_to_flags([~"path1", ~"path2"]);
        assert_eq!(flags, ~[~"-Wl,-rpath,path1", ~"-Wl,-rpath,path2"]);
    }

    #[test]
    fn test_prefix_rpath() {
        let res = get_install_prefix_rpath("triple");
        let mut d = Path::new(env!("CFG_PREFIX"));
        d.push("lib/rustc/triple/lib");
        debug!("test_prefix_path: {} vs. {}",
               res,
               d.display());
        assert!(res.as_bytes().ends_with(d.as_vec()));
    }

    #[test]
    fn test_prefix_rpath_abs() {
        let res = get_install_prefix_rpath("triple");
        assert!(Path::new(res).is_absolute());
    }

    #[test]
    fn test_minimize1() {
        let res = minimize_rpaths([~"rpath1", ~"rpath2", ~"rpath1"]);
        assert_eq!(res.as_slice(), [~"rpath1", ~"rpath2"]);
    }

    #[test]
    fn test_minimize2() {
        let res = minimize_rpaths([~"1a", ~"2",  ~"2",
                                   ~"1a", ~"4a", ~"1a",
                                   ~"2",  ~"3",  ~"4a",
                                   ~"3"]);
        assert_eq!(res.as_slice(), [~"1a", ~"2", ~"4a", ~"3"]);
    }

    #[test]
    #[cfg(target_os = "linux")]
    #[cfg(target_os = "android")]
    fn test_rpath_relative() {
      let o = session::OsLinux;
      let res = get_rpath_relative_to_output(o,
            &Path::new("bin/rustc"), &Path::new("lib/libstd.so"));
      assert_eq!(res.as_slice(), "$ORIGIN/../lib");
    }

    #[test]
    #[cfg(target_os = "freebsd")]
    fn test_rpath_relative() {
        let o = session::OsFreebsd;
        let res = get_rpath_relative_to_output(o,
            &Path::new("bin/rustc"), &Path::new("lib/libstd.so"));
        assert_eq!(res.as_slice(), "$ORIGIN/../lib");
    }

    #[test]
    #[cfg(target_os = "macos")]
    fn test_rpath_relative() {
        let o = session::OsMacos;
        let res = get_rpath_relative_to_output(o,
                                               &Path::new("bin/rustc"),
                                               &Path::new("lib/libstd.so"));
        assert_eq!(res.as_slice(), "@executable_path/../lib");
    }

    #[test]
    fn test_get_absolute_rpath() {
        let res = get_absolute_rpath(&Path::new("lib/libstd.so"));
        let lib = os::make_absolute(&Path::new("lib"));
        debug!("test_get_absolute_rpath: {} vs. {}",
               res.to_str(), lib.display());

        // FIXME (#9639): This needs to handle non-utf8 paths
        assert_eq!(res.as_slice(), lib.as_str().expect("non-utf8 component in path"));
    }
}
<commit_msg>auto merge of #10299 : alexcrichton/rust/osx-loader-path-fix, r=brson<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use driver::session;
use metadata::cstore;
use metadata::filesearch;

use std::hashmap::HashSet;
use std::{os, vec};

fn not_win32(os: session::Os) -> bool {
  os != session::OsWin32
}

pub fn get_rpath_flags(sess: session::Session, out_filename: &Path)
                    -> ~[~str] {
    let os = sess.targ_cfg.os;

    // No rpath on windows
    if os == session::OsWin32 {
        return ~[];
    }

    debug!("preparing the RPATH!");

    let sysroot = sess.filesearch.sysroot();
    let output = out_filename;
    let libs = cstore::get_used_crate_files(sess.cstore);
    // We don't currently rpath extern libraries, but we know
    // where rustrt is and we know every rust program needs it
    let libs = vec::append_one(libs, get_sysroot_absolute_rt_lib(sess));

    let rpaths = get_rpaths(os, sysroot, output, libs,
                            sess.opts.target_triple);
    rpaths_to_flags(rpaths)
}

fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path {
    let r = filesearch::relative_target_lib_path(sess.opts.target_triple);
    let mut p = sess.filesearch.sysroot().join(&r);
    p.push(os::dll_filename("rustrt"));
    p
}

pub fn rpaths_to_flags(rpaths: &[~str]) -> ~[~str] {
    // FIXME (#9639): This needs to handle non-utf8 paths
    rpaths.iter().map(|rpath| format!("-Wl,-rpath,{}",*rpath)).collect()
}

fn get_rpaths(os: session::Os,
              sysroot: &Path,
              output: &Path,
              libs: &[Path],
              target_triple: &str) -> ~[~str] {
    debug!("sysroot: {}", sysroot.display());
    debug!("output: {}", output.display());
    debug!("libs:");
    for libpath in libs.iter() {
        debug!("    {}", libpath.display());
    }
    debug!("target_triple: {}", target_triple);

    // Use relative paths to the libraries. Binaries can be moved
    // as long as they maintain the relative relationship to the
    // crates they depend on.
    let rel_rpaths = get_rpaths_relative_to_output(os, output, libs);

    // Make backup absolute paths to the libraries. Binaries can
    // be moved as long as the crates they link against don't move.
    let abs_rpaths = get_absolute_rpaths(libs);

    // And a final backup rpath to the global library location.
    let fallback_rpaths = ~[get_install_prefix_rpath(target_triple)];

    fn log_rpaths(desc: &str, rpaths: &[~str]) {
        debug!("{} rpaths:", desc);
        for rpath in rpaths.iter() {
            debug!("    {}", *rpath);
        }
    }

    log_rpaths("relative", rel_rpaths);
    log_rpaths("absolute", abs_rpaths);
    log_rpaths("fallback", fallback_rpaths);

    let mut rpaths = rel_rpaths;
    rpaths.push_all(abs_rpaths);
    rpaths.push_all(fallback_rpaths);

    // Remove duplicates
    let rpaths = minimize_rpaths(rpaths);
    return rpaths;
}

fn get_rpaths_relative_to_output(os: session::Os,
                                 output: &Path,
                                 libs: &[Path]) -> ~[~str] {
    libs.iter().map(|a| get_rpath_relative_to_output(os, output, a)).collect()
}

pub fn get_rpath_relative_to_output(os: session::Os,
                                    output: &Path,
                                    lib: &Path)
                                 -> ~str {
    use std::os;

    assert!(not_win32(os));

    // Mac doesn't appear to support $ORIGIN
    let prefix = match os {
        session::OsAndroid | session::OsLinux | session::OsFreebsd
                          => "$ORIGIN",
        session::OsMacos => "@loader_path",
        session::OsWin32 => unreachable!()
    };

    let mut lib = os::make_absolute(lib);
    lib.pop();
    let mut output = os::make_absolute(output);
    output.pop();
    let relative = lib.path_relative_from(&output);
    let relative = relative.expect("could not create rpath relative to output");
    // FIXME (#9639): This needs to handle non-utf8 paths
    prefix+"/"+relative.as_str().expect("non-utf8 component in path")
}

fn get_absolute_rpaths(libs: &[Path]) -> ~[~str] {
    libs.iter().map(|a| get_absolute_rpath(a)).collect()
}

pub fn get_absolute_rpath(lib: &Path) -> ~str {
    let mut p = os::make_absolute(lib);
    p.pop();
    // FIXME (#9639): This needs to handle non-utf8 paths
    p.as_str().expect("non-utf8 component in rpath").to_owned()
}

pub fn get_install_prefix_rpath(target_triple: &str) -> ~str {
    let install_prefix = env!("CFG_PREFIX");

    let tlib = filesearch::relative_target_lib_path(target_triple);
    let mut path = Path::new(install_prefix);
    path.push(&tlib);
    let path = os::make_absolute(&path);
    // FIXME (#9639): This needs to handle non-utf8 paths
    path.as_str().expect("non-utf8 component in rpath").to_owned()
}

pub fn minimize_rpaths(rpaths: &[~str]) -> ~[~str] {
    let mut set = HashSet::new();
    let mut minimized = ~[];
    for rpath in rpaths.iter() {
        if set.insert(rpath.as_slice()) {
            minimized.push(rpath.clone());
        }
    }
    minimized
}

#[cfg(unix, test)]
mod test {
    use std::os;

    // FIXME(#2119): the outer attribute should be #[cfg(unix, test)], then
    // these redundant #[cfg(test)] blocks can be removed
    #[cfg(test)]
    #[cfg(test)]
    use back::rpath::{get_absolute_rpath, get_install_prefix_rpath};
    use back::rpath::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
    use driver::session;

    #[test]
    fn test_rpaths_to_flags() {
        let flags = rpaths_to_flags([~"path1", ~"path2"]);
        assert_eq!(flags, ~[~"-Wl,-rpath,path1", ~"-Wl,-rpath,path2"]);
    }

    #[test]
    fn test_prefix_rpath() {
        let res = get_install_prefix_rpath("triple");
        let mut d = Path::new(env!("CFG_PREFIX"));
        d.push("lib/rustc/triple/lib");
        debug!("test_prefix_path: {} vs. {}",
               res,
               d.display());
        assert!(res.as_bytes().ends_with(d.as_vec()));
    }

    #[test]
    fn test_prefix_rpath_abs() {
        let res = get_install_prefix_rpath("triple");
        assert!(Path::new(res).is_absolute());
    }

    #[test]
    fn test_minimize1() {
        let res = minimize_rpaths([~"rpath1", ~"rpath2", ~"rpath1"]);
        assert_eq!(res.as_slice(), [~"rpath1", ~"rpath2"]);
    }

    #[test]
    fn test_minimize2() {
        let res = minimize_rpaths([~"1a", ~"2",  ~"2",
                                   ~"1a", ~"4a", ~"1a",
                                   ~"2",  ~"3",  ~"4a",
                                   ~"3"]);
        assert_eq!(res.as_slice(), [~"1a", ~"2", ~"4a", ~"3"]);
    }

    #[test]
    #[cfg(target_os = "linux")]
    #[cfg(target_os = "android")]
    fn test_rpath_relative() {
      let o = session::OsLinux;
      let res = get_rpath_relative_to_output(o,
            &Path::new("bin/rustc"), &Path::new("lib/libstd.so"));
      assert_eq!(res.as_slice(), "$ORIGIN/../lib");
    }

    #[test]
    #[cfg(target_os = "freebsd")]
    fn test_rpath_relative() {
        let o = session::OsFreebsd;
        let res = get_rpath_relative_to_output(o,
            &Path::new("bin/rustc"), &Path::new("lib/libstd.so"));
        assert_eq!(res.as_slice(), "$ORIGIN/../lib");
    }

    #[test]
    #[cfg(target_os = "macos")]
    fn test_rpath_relative() {
        let o = session::OsMacos;
        let res = get_rpath_relative_to_output(o,
                                               &Path::new("bin/rustc"),
                                               &Path::new("lib/libstd.so"));
        assert_eq!(res.as_slice(), "@loader_path/../lib");
    }

    #[test]
    fn test_get_absolute_rpath() {
        let res = get_absolute_rpath(&Path::new("lib/libstd.so"));
        let lib = os::make_absolute(&Path::new("lib"));
        debug!("test_get_absolute_rpath: {} vs. {}",
               res.to_str(), lib.display());

        // FIXME (#9639): This needs to handle non-utf8 paths
        assert_eq!(res.as_slice(), lib.as_str().expect("non-utf8 component in path"));
    }
}
<|endoftext|>


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

extern crate gcc;
extern crate build_helper;

use std::process::Command;
use std::env;
use std::path::{PathBuf, Path};

use build_helper::output;

fn detect_llvm_link(llvm_config: &Path) -> (&'static str, Option<&'static str>) {
    let mut version_cmd = Command::new(llvm_config);
    version_cmd.arg("--version");
    let version_output = output(&mut version_cmd);
    let mut parts = version_output.split('.').take(2)
        .filter_map(|s| s.parse::<u32>().ok());
    if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
        if major > 3 || (major == 3 && minor >= 9) {
            // Force the link mode we want, preferring static by default, but
            // possibly overridden by `configure --enable-llvm-link-shared`.
            if env::var_os("LLVM_LINK_SHARED").is_some() {
                return ("dylib", Some("--link-shared"));
            } else {
                return ("static", Some("--link-static"));
            }
        } else if major == 3 && minor == 8 {
            // Find out LLVM's default linking mode.
            let mut mode_cmd = Command::new(llvm_config);
            mode_cmd.arg("--shared-mode");
            if output(&mut mode_cmd).trim() == "shared" {
                return ("dylib", None);
            } else {
                return ("static", None);
            }
        }
    }
    ("static", None)
}

fn main() {
    let target = env::var("TARGET").expect("TARGET was not set");
    let llvm_config = env::var_os("LLVM_CONFIG")
        .map(PathBuf::from)
        .unwrap_or_else(|| {
            if let Some(dir) = env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
                let to_test = dir.parent()
                    .unwrap()
                    .parent()
                    .unwrap()
                    .join(&target)
                    .join("llvm/bin/llvm-config");
                if Command::new(&to_test).output().is_ok() {
                    return to_test;
                }
            }
            PathBuf::from("llvm-config")
        });

    println!("cargo:rerun-if-changed={}", llvm_config.display());

    // Test whether we're cross-compiling LLVM. This is a pretty rare case
    // currently where we're producing an LLVM for a different platform than
    // what this build script is currently running on.
    //
    // In that case, there's no guarantee that we can actually run the target,
    // so the build system works around this by giving us the LLVM_CONFIG for
    // the host platform. This only really works if the host LLVM and target
    // LLVM are compiled the same way, but for us that's typically the case.
    //
    // We *want* detect this cross compiling situation by asking llvm-config
    // what it's host-target is. If that's not the TARGET, then we're cross
    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
    // tricks us into thinking we're doing a cross build when we aren't, so
    // havoc ensues.
    //
    // In any case, if we're cross compiling, this generally just means that we
    // can't trust all the output of llvm-config becaues it might be targeted
    // for the host rather than the target. As a result a bunch of blocks below
    // are gated on `if !is_crossed`
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let is_crossed = target != host;

    let optional_components =
        ["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", "systemz", "jsbackend", "msp430",
         "sparc", "nvptx", "hexagon"];

    // FIXME: surely we don't need all these components, right? Stuff like mcjit
    //        or interpreter the compiler itself never uses.
    let required_components = &["ipo",
                                "bitreader",
                                "bitwriter",
                                "linker",
                                "asmparser",
                                "mcjit",
                                "interpreter",
                                "instrumentation"];

    let components = output(Command::new(&llvm_config).arg("--components"));
    let mut components = components.split_whitespace().collect::<Vec<_>>();
    components.retain(|c| optional_components.contains(c) || required_components.contains(c));

    for component in required_components {
        if !components.contains(component) {
            panic!("require llvm component {} but wasn't found", component);
        }
    }

    for component in components.iter() {
        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
    }

    // Link in our own LLVM shims, compiled with the same flags as LLVM
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--cxxflags");
    let cxxflags = output(&mut cmd);
    let mut cfg = gcc::Config::new();
    for flag in cxxflags.split_whitespace() {
        // Ignore flags like `-m64` when we're doing a cross build
        if is_crossed && flag.starts_with("-m") {
            continue;
        }

        // -Wdate-time is not supported by the netbsd cross compiler
        if is_crossed && target.contains("netbsd") && flag.contains("date-time") {
            continue;
        }

        cfg.flag(flag);
    }

    for component in &components {
        let mut flag = String::from("-DLLVM_COMPONENT_");
        flag.push_str(&component.to_uppercase());
        cfg.flag(&flag);
    }

    if env::var_os("LLVM_RUSTLLVM").is_some() {
        cfg.flag("-DLLVM_RUSTLLVM");
    }

    build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm"));
    cfg.file("../rustllvm/PassWrapper.cpp")
       .file("../rustllvm/RustWrapper.cpp")
       .file("../rustllvm/ArchiveWrapper.cpp")
       .cpp(true)
       .cpp_link_stdlib(None) // we handle this below
       .compile("librustllvm.a");

    let (llvm_kind, llvm_link_arg) = detect_llvm_link(&llvm_config);

    // Link in all LLVM libraries, if we're uwring the "wrong" llvm-config then
    // we don't pick up system libs because unfortunately they're for the host
    // of llvm-config, not the target that we're attempting to link.
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--libs");

    if let Some(link_arg) = llvm_link_arg {
        cmd.arg(link_arg);
    }

    if !is_crossed {
        cmd.arg("--system-libs");
    }
    cmd.args(&components);

    for lib in output(&mut cmd).split_whitespace() {
        let name = if lib.starts_with("-l") {
            &lib[2..]
        } else if lib.starts_with("-") {
            &lib[1..]
        } else if Path::new(lib).exists() {
            // On MSVC llvm-config will print the full name to libraries, but
            // we're only interested in the name part
            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
            name.trim_right_matches(".lib")
        } else if lib.ends_with(".lib") {
            // Some MSVC libraries just come up with `.lib` tacked on, so chop
            // that off
            lib.trim_right_matches(".lib")
        } else {
            continue;
        };

        // Don't need or want this library, but LLVM's CMake build system
        // doesn't provide a way to disable it, so filter it here even though we
        // may or may not have built it. We don't reference anything from this
        // library and it otherwise may just pull in extra dependencies on
        // libedit which we don't want
        if name == "LLVMLineEditor" {
            continue;
        }

        let kind = if name.starts_with("LLVM") {
            llvm_kind
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, name);
    }

    // LLVM ldflags
    //
    // If we're a cross-compile of LLVM then unfortunately we can't trust these
    // ldflags (largely where all the LLVM libs are located). Currently just
    // hack around this by replacing the host triple with the target and pray
    // that those -L directories are the same!
    let mut cmd = Command::new(&llvm_config);
    if let Some(link_arg) = llvm_link_arg {
        cmd.arg(link_arg);
    }
    cmd.arg("--ldflags");
    for lib in output(&mut cmd).split_whitespace() {
        if lib.starts_with("-LIBPATH:") {
            println!("cargo:rustc-link-search=native={}", &lib[9..]);
        } else if is_crossed {
            if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}",
                         lib[2..].replace(&host, &target));
            }
        } else if lib.starts_with("-l") {
            println!("cargo:rustc-link-lib={}", &lib[2..]);
        } else if lib.starts_with("-L") {
            println!("cargo:rustc-link-search=native={}", &lib[2..]);
        }
    }

    let llvm_static_stdcpp = env::var_os("LLVM_STATIC_STDCPP");

    let stdcppname = if target.contains("openbsd") {
        // OpenBSD has a particular C++ runtime library name
        "estdc++"
    } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
        // NetBSD uses a separate library when relocation is required
        "stdc++_pic"
    } else {
        "stdc++"
    };

    // C++ runtime library
    if !target.contains("msvc") {
        if let Some(s) = llvm_static_stdcpp {
            assert!(!cxxflags.contains("stdlib=libc++"));
            let path = PathBuf::from(s);
            println!("cargo:rustc-link-search=native={}",
                     path.parent().unwrap().display());
            println!("cargo:rustc-link-lib=static={}", stdcppname);
        } else if cxxflags.contains("stdlib=libc++") {
            println!("cargo:rustc-link-lib=c++");
        } else {
            println!("cargo:rustc-link-lib={}", stdcppname);
        }
    }

    // LLVM requires symbols from this library, but apparently they're not printeds
    // during llvm-config?
    if target.contains("windows") {
        println!("cargo:rustc-link-lib=ole32");
    }
    if target.contains("windows-gnu") {
        println!("cargo:rustc-link-lib=static-nobundle=gcc_s");
        println!("cargo:rustc-link-lib=static-nobundle=pthread");
    }
}
<commit_msg>Avoid the hexagon backend on old versions of LLVM<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate gcc;
extern crate build_helper;

use std::process::Command;
use std::env;
use std::path::{PathBuf, Path};

use build_helper::output;

fn detect_llvm_link(major: u32, minor: u32, llvm_config: &Path)
    -> (&'static str, Option<&'static str>) {
    if major > 3 || (major == 3 && minor >= 9) {
        // Force the link mode we want, preferring static by default, but
        // possibly overridden by `configure --enable-llvm-link-shared`.
        if env::var_os("LLVM_LINK_SHARED").is_some() {
            return ("dylib", Some("--link-shared"));
        } else {
            return ("static", Some("--link-static"));
        }
    } else if major == 3 && minor == 8 {
        // Find out LLVM's default linking mode.
        let mut mode_cmd = Command::new(llvm_config);
        mode_cmd.arg("--shared-mode");
        if output(&mut mode_cmd).trim() == "shared" {
            return ("dylib", None);
        } else {
            return ("static", None);
        }
    }
    ("static", None)
}

fn main() {
    let target = env::var("TARGET").expect("TARGET was not set");
    let llvm_config = env::var_os("LLVM_CONFIG")
        .map(PathBuf::from)
        .unwrap_or_else(|| {
            if let Some(dir) = env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
                let to_test = dir.parent()
                    .unwrap()
                    .parent()
                    .unwrap()
                    .join(&target)
                    .join("llvm/bin/llvm-config");
                if Command::new(&to_test).output().is_ok() {
                    return to_test;
                }
            }
            PathBuf::from("llvm-config")
        });

    println!("cargo:rerun-if-changed={}", llvm_config.display());

    // Test whether we're cross-compiling LLVM. This is a pretty rare case
    // currently where we're producing an LLVM for a different platform than
    // what this build script is currently running on.
    //
    // In that case, there's no guarantee that we can actually run the target,
    // so the build system works around this by giving us the LLVM_CONFIG for
    // the host platform. This only really works if the host LLVM and target
    // LLVM are compiled the same way, but for us that's typically the case.
    //
    // We *want* detect this cross compiling situation by asking llvm-config
    // what it's host-target is. If that's not the TARGET, then we're cross
    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
    // tricks us into thinking we're doing a cross build when we aren't, so
    // havoc ensues.
    //
    // In any case, if we're cross compiling, this generally just means that we
    // can't trust all the output of llvm-config becaues it might be targeted
    // for the host rather than the target. As a result a bunch of blocks below
    // are gated on `if !is_crossed`
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let is_crossed = target != host;

    let mut optional_components =
        vec!["x86", "arm", "aarch64", "mips", "powerpc", "pnacl",
             "systemz", "jsbackend", "msp430", "sparc", "nvptx"];

    let mut version_cmd = Command::new(&llvm_config);
    version_cmd.arg("--version");
    let version_output = output(&mut version_cmd);
    let mut parts = version_output.split('.').take(2)
        .filter_map(|s| s.parse::<u32>().ok());
    let (major, minor) =
        if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
            (major, minor)
        } else {
            (3, 7)
        };

    if major > 3 {
        optional_components.push("hexagon");
    }

    // FIXME: surely we don't need all these components, right? Stuff like mcjit
    //        or interpreter the compiler itself never uses.
    let required_components = &["ipo",
                                "bitreader",
                                "bitwriter",
                                "linker",
                                "asmparser",
                                "mcjit",
                                "interpreter",
                                "instrumentation"];

    let components = output(Command::new(&llvm_config).arg("--components"));
    let mut components = components.split_whitespace().collect::<Vec<_>>();
    components.retain(|c| optional_components.contains(c) || required_components.contains(c));

    for component in required_components {
        if !components.contains(component) {
            panic!("require llvm component {} but wasn't found", component);
        }
    }

    for component in components.iter() {
        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
    }

    // Link in our own LLVM shims, compiled with the same flags as LLVM
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--cxxflags");
    let cxxflags = output(&mut cmd);
    let mut cfg = gcc::Config::new();
    for flag in cxxflags.split_whitespace() {
        // Ignore flags like `-m64` when we're doing a cross build
        if is_crossed && flag.starts_with("-m") {
            continue;
        }

        // -Wdate-time is not supported by the netbsd cross compiler
        if is_crossed && target.contains("netbsd") && flag.contains("date-time") {
            continue;
        }

        cfg.flag(flag);
    }

    for component in &components {
        let mut flag = String::from("-DLLVM_COMPONENT_");
        flag.push_str(&component.to_uppercase());
        cfg.flag(&flag);
    }

    if env::var_os("LLVM_RUSTLLVM").is_some() {
        cfg.flag("-DLLVM_RUSTLLVM");
    }

    build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm"));
    cfg.file("../rustllvm/PassWrapper.cpp")
       .file("../rustllvm/RustWrapper.cpp")
       .file("../rustllvm/ArchiveWrapper.cpp")
       .cpp(true)
       .cpp_link_stdlib(None) // we handle this below
       .compile("librustllvm.a");

    let (llvm_kind, llvm_link_arg) = detect_llvm_link(major, minor, &llvm_config);

    // Link in all LLVM libraries, if we're uwring the "wrong" llvm-config then
    // we don't pick up system libs because unfortunately they're for the host
    // of llvm-config, not the target that we're attempting to link.
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--libs");

    if let Some(link_arg) = llvm_link_arg {
        cmd.arg(link_arg);
    }

    if !is_crossed {
        cmd.arg("--system-libs");
    }
    cmd.args(&components);

    for lib in output(&mut cmd).split_whitespace() {
        let name = if lib.starts_with("-l") {
            &lib[2..]
        } else if lib.starts_with("-") {
            &lib[1..]
        } else if Path::new(lib).exists() {
            // On MSVC llvm-config will print the full name to libraries, but
            // we're only interested in the name part
            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
            name.trim_right_matches(".lib")
        } else if lib.ends_with(".lib") {
            // Some MSVC libraries just come up with `.lib` tacked on, so chop
            // that off
            lib.trim_right_matches(".lib")
        } else {
            continue;
        };

        // Don't need or want this library, but LLVM's CMake build system
        // doesn't provide a way to disable it, so filter it here even though we
        // may or may not have built it. We don't reference anything from this
        // library and it otherwise may just pull in extra dependencies on
        // libedit which we don't want
        if name == "LLVMLineEditor" {
            continue;
        }

        let kind = if name.starts_with("LLVM") {
            llvm_kind
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, name);
    }

    // LLVM ldflags
    //
    // If we're a cross-compile of LLVM then unfortunately we can't trust these
    // ldflags (largely where all the LLVM libs are located). Currently just
    // hack around this by replacing the host triple with the target and pray
    // that those -L directories are the same!
    let mut cmd = Command::new(&llvm_config);
    if let Some(link_arg) = llvm_link_arg {
        cmd.arg(link_arg);
    }
    cmd.arg("--ldflags");
    for lib in output(&mut cmd).split_whitespace() {
        if lib.starts_with("-LIBPATH:") {
            println!("cargo:rustc-link-search=native={}", &lib[9..]);
        } else if is_crossed {
            if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}",
                         lib[2..].replace(&host, &target));
            }
        } else if lib.starts_with("-l") {
            println!("cargo:rustc-link-lib={}", &lib[2..]);
        } else if lib.starts_with("-L") {
            println!("cargo:rustc-link-search=native={}", &lib[2..]);
        }
    }

    let llvm_static_stdcpp = env::var_os("LLVM_STATIC_STDCPP");

    let stdcppname = if target.contains("openbsd") {
        // OpenBSD has a particular C++ runtime library name
        "estdc++"
    } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
        // NetBSD uses a separate library when relocation is required
        "stdc++_pic"
    } else {
        "stdc++"
    };

    // C++ runtime library
    if !target.contains("msvc") {
        if let Some(s) = llvm_static_stdcpp {
            assert!(!cxxflags.contains("stdlib=libc++"));
            let path = PathBuf::from(s);
            println!("cargo:rustc-link-search=native={}",
                     path.parent().unwrap().display());
            println!("cargo:rustc-link-lib=static={}", stdcppname);
        } else if cxxflags.contains("stdlib=libc++") {
            println!("cargo:rustc-link-lib=c++");
        } else {
            println!("cargo:rustc-link-lib={}", stdcppname);
        }
    }

    // LLVM requires symbols from this library, but apparently they're not printeds
    // during llvm-config?
    if target.contains("windows") {
        println!("cargo:rustc-link-lib=ole32");
    }
    if target.contains("windows-gnu") {
        println!("cargo:rustc-link-lib=static-nobundle=gcc_s");
        println!("cargo:rustc-link-lib=static-nobundle=pthread");
    }
}
<|endoftext|>


<commit_before>import comm::{port, chan};
import html::html_methods;
import css::css_methods;
import dom::style;

enum parse_state {
    ps_html_normal,
    ps_html_tag,
    ps_css_elmt,
    ps_css_relation,
    ps_css_desc,
    ps_css_attribute
}

type parser = {
    mut lookahead: option<char_or_eof>,
    mut state: parse_state,
    reader: io::reader
};

enum char_or_eof {
    coe_char(u8),
    coe_eof
}

impl u8_methods for u8 {
    fn is_whitespace() -> bool {
        ret self == ' ' as u8 || self == '\n' as u8
            || self == '\t' as u8;
    }

    fn is_alpha() -> bool {
        ret (self >= ('A' as u8) && self <= ('Z' as u8)) ||
            (self >= ('a' as u8) && self <= ('z' as u8));
    }
}

impl u8_vec_methods for [u8] {
    fn to_str() -> str { ret str::from_bytes(self); }
    fn to_html_token() -> html::token { ret html::to_text(self.to_str()); }
    fn to_css_token() -> html::token { ret html::to_text(self.to_str()); }
}

impl util_methods for parser {
    fn get() -> char_or_eof {
        alt self.lookahead {
            some(coe) {
                let rv = coe;
                self.lookahead = none;
                ret rv;
            }
            none {
                /* fall through */
            }
        }

        if self.reader.eof() { ret coe_eof; }
        ret coe_char(self.reader.read_byte() as u8);
    }

    fn unget(ch: u8) {
        assert self.lookahead.is_none();
        self.lookahead = some(coe_char(ch));
    }

    fn parse_err(err: str) -> ! {
        fail err
    }

    fn expect(ch: u8) {
        alt self.get() {
            coe_char(c) {
                if c != ch {
                    self.parse_err(#fmt("expected '%c'", ch as char));
                }
            }
            coe_eof {
                self.parse_err(#fmt("expected '%c' at eof", ch as char));
            }
        }
    }

    fn parse_ident() -> str {
        let mut result: [u8] = [];
        loop {
            alt self.get() {
                coe_char(c) {
                    if (c.is_alpha()) {
                        result += [c];
                    } else if result.len() == 0u {
                        self.parse_err("expected ident");
                    } else {
                        self.unget(c);
                        break;
                    }
                }
                coe_eof {
                    self.parse_err("expected ident");
                }
            }
        }
        ret str::from_bytes(result);
    }

    fn expect_ident(expected: str) {
        let actual = self.parse_ident();
        if expected != actual {
            self.parse_err(#fmt("expected '%s' but found '%s'",
                                expected, actual));
        }
    }

    fn eat_whitespace() {
        loop {
            alt self.get() {
                coe_char(c) {
                  if c.is_whitespace() {
                        self.unget(c);
                        ret;
                    }
                }
                coe_eof {
                    ret;
                }
            }
        }
    }

    fn parse_html() -> html::token {
        let mut ch: u8;
        alt self.get() {
            coe_char(c) { ch = c; }
            coe_eof { ret html::to_eof; }
        }

        let token = alt self.state {
          ps_html_normal   { self.parse_in_normal_state(ch) }
          ps_html_tag      { self.parse_in_tag_state(ch) }
          _                { fail "Parsing in html mode when not in " +
                                "an html state" }
        };

        #debug["token=%?", token];
        ret token;
    }

    fn parse_css() -> css::token {
        let mut ch: u8;
        alt self.get() {
            coe_char(c) { ch = c; }
            coe_eof { ret css::to_eof; }
        }

        let token = alt self.state {
          ps_css_desc        { self.parse_css_description(ch) }
          ps_css_attribute   { self.parse_css_attribute(ch) }
          ps_css_elmt        { self.parse_css_element(ch) }
          ps_css_relation    { self.parse_css_relation(ch) }
          _                  { fail "Parsing in css mode when not in " +
                                  "a css state" }
        };

        #debug["token=%?", token];
        ret token;
    }
}

mod html {
    enum token {
        to_start_opening_tag(str),
        to_end_opening_tag,
        to_end_tag(str),
        to_self_close_tag,
        to_text(str),
        to_attr(str, str),
        to_doctype,
        to_eof
    }

    impl html_methods for parser {
        fn parse_in_normal_state(c: u8) -> token {
            let mut ch = c;
            if ch == ('<' as u8) {
                alt self.get() {
                  coe_char(c) { ch = c; }
                  coe_eof { self.parse_err("eof after '<'") }
                }

                if ch == ('!' as u8) {
                    self.eat_whitespace();
                    self.expect_ident("DOCTYPE");
                    self.eat_whitespace();
                    self.expect_ident("html");
                    self.eat_whitespace();
                    self.expect('>' as u8);
                    ret to_doctype;
                }

                if ch == ('/' as u8) {
                    let ident = self.parse_ident();
                    self.expect('>' as u8);
                    ret to_end_tag(ident);
                }

                self.unget(ch);

                self.eat_whitespace();
                let ident = self.parse_ident();
                self.eat_whitespace();

                self.state = ps_html_tag;
                ret to_start_opening_tag(ident);
            }

            // Make a text node.
            let mut s: [u8] = [ch];
            loop {
                alt self.get() {
                  coe_char(c) {
                    if c == ('<' as u8) {
                        self.unget(c);
                        ret s.to_html_token();
                    }
                    s += [c];
                  }
                  coe_eof { ret s.to_html_token(); }
                }
            }
        }

        fn parse_in_tag_state(c: u8) -> token {
            let mut ch = c;

            if ch == ('>' as u8) {
                self.state = ps_html_normal;
                ret to_end_opening_tag;
            }

            if ch == ('/' as u8) {
                self.state = ps_html_normal;
                ret to_self_close_tag;
            }

            if !ch.is_alpha() {
                fail #fmt("expected alphabetical in tag but found %c",
                          ch as char);
            }

            // Parse an attribute.
            let mut attribute_name = [ch];
            loop {
                alt self.get() {
                  coe_char(c) {
                    if c == ('=' as u8) { break; }
                    attribute_name += [c];
                  }
                  coe_eof {
                    ret to_attr(attribute_name.to_str(),
                                attribute_name.to_str()); }
                }
            }

            // Parse the attribute value.
            self.expect('"' as u8);
            let mut attribute_value = [];
            loop {
                alt self.get() {
                  coe_char(c) {
                    if c == ('"' as u8) { break; }
                    attribute_value += [c];
                  }
                  coe_eof {
                    ret to_attr(attribute_name.to_str(),
                                attribute_value.to_str());
                  }
                }
            }

            // Eat whitespacpe.
            self.eat_whitespace();

            ret to_attr(attribute_name.to_str(), attribute_value.to_str());
        }
    }
}

mod css {
    enum token {
        to_start_desc,
        to_end_desc,
        to_descendant,
        to_child,
        to_sibling,
        to_comma,
        to_elmt(str),
        to_attr(style::attr),
        to_desc(str, str),
        to_eof
    }

    impl css_methods for parser {
        fn parse_css_relation(c : u8) -> token {
            self.state = ps_css_elmt;

            let token = alt c {
              '{' as u8  { self.state = ps_css_desc; to_start_desc }
              '>' as u8  { to_child }
              '+' as u8  { to_sibling }
              ',' as u8  { to_comma }
              _          { to_descendant }
            };

            self.eat_whitespace();

            ret token;
        }

        fn parse_css_element(c : u8) -> token {
            /* Check for special attributes with an implied element.*/
            if c == '.' as u8 || c == '#' as u8 {
                self.state = ps_css_attribute;
                self.unget(c);
                ret to_elmt("*");
            }

            let element = self.parse_ident();
            self.state = ps_css_attribute;

            ret to_elmt(element);
        }

        fn parse_css_attribute(c : u8) -> token {
            let mut ch = c;

            /* If we've reached the end of this list of attributes,
            look for the relation to the next element.*/
            if c.is_whitespace() {
                self.state = ps_css_relation;
                self.eat_whitespace();

                alt self.get() {
                  coe_char(c)  { ch = c }
                  coe_eof      { fail "File ended before description " +
                                    "of style" }
                }

                ret self.parse_css_relation(ch);
            }

            alt ch {
              '.' as u8 { ret to_attr(
                  style::includes("class", self.parse_ident())); }
              '#' as u8 { ret to_attr(
                  style::includes("id", self.parse_ident())); }
              '[' as u8 {
                let attr_name = self.parse_ident();

                alt self.get() {
                  coe_char(c)    { ch = c; }
                  coe_eof        { fail "File ended before " +
                                      "description finished"; }
                }

                if ch == ']' as u8 {
                    ret to_attr(style::exists(attr_name));
                } else if ch == '=' as u8 {
                    let attr_val = self.parse_ident();
                    self.expect(']' as u8);
                    ret to_attr(style::exact(attr_name, attr_val));
                } else if ch == '~' as u8 {
                    self.expect('=' as u8);
                    let attr_val = self.parse_ident();
                    self.expect(']' as u8);
                    ret to_attr(style::includes(attr_name, attr_val));
                } else if ch == '|' as u8 {
                    self.expect('=' as u8);
                    let attr_val = self.parse_ident();
                    self.expect(']' as u8);
                    ret to_attr(style::starts_with(attr_name, attr_val));
                }

                fail #fmt("Unexpected symbol %c in attribute", ch as char);
              }
              _   { fail #fmt("Unexpected symbol %c in attribute",
                              ch as char); }
            }
        }

        fn parse_css_description(c: u8) -> token {
            let mut ch = c;

            if ch.is_whitespace() {
                self.eat_whitespace();

                alt self.get() {
                  coe_char(c)  { ch = c }
                  coe_eof      { fail "Reached end of file " +
                                    "in CSS description" }
                }
            }

            let mut desc_name = [];

            // Get the name of the descriptor
            loop {
                if ch.is_whitespace() {
                    self.eat_whitespace();
                } else if ch == ':' as u8 {
                    if desc_name.len() == 0u {
                        fail "Expected descriptor name";
                    } else {
                        break;
                    }
                } else {
                    desc_name += [ch];
                }

                alt self.get() {
                  coe_char(c)  { ch = c }
                  coe_eof      { fail "Reached end of file " +
                                    "in CSS description" }
                }
            }

            self.eat_whitespace();
            let mut desc_val = [];

            // Get the value of the descriptor
            loop {
                alt self.get() {
                  coe_char(c)  { ch = c }
                  coe_eof      { fail "Reached end of file " +
                                    "in CSS description" }
                }

                if ch.is_whitespace() {
                    self.eat_whitespace();
                } else if ch == '}' as u8 {
                    if desc_val.len() == 0u {
                        fail "Expected descriptor value";
                    } else {
                        self.state = ps_css_elmt;
                        break;
                    }
                } else if ch == ';' as u8 {
                    if desc_val.len() == 0u {
                        fail "Expected descriptor value";
                    } else {
                        break;
                    }
                } else {
                    desc_val += [ch];
                }
            }

            ret to_desc(desc_name.to_str(), desc_val.to_str());
        }
    }
}

fn parser(reader: io::reader, state : parse_state) -> parser {
    ret { mut lookahead: none, mut state: state, reader: reader };
}

fn spawn_html_parser_task(filename: str) -> port<html::token> {
    let result_port = port();
    let result_chan = chan(result_port);
    task::spawn {||
        let file_data = io::read_whole_file(filename).get();
        let reader = io::bytes_reader(file_data);

        assert filename.ends_with(".html");
        let parser = parser(reader, ps_html_normal);

        loop {
            let token = parser.parse_html();
            result_chan.send(token);
            if token == html::to_eof { break; }
        }
    };
    ret result_port;
}

fn spawn_css_parser_task(filename: str) -> port<css::token> {
    let result_port = port();
    let result_chan = chan(result_port);
    task::spawn {||
        let file_data = io::read_whole_file(filename).get();
        let reader = io::bytes_reader(file_data);

        assert filename.ends_with(".css");
        let parser : parser = parser(reader, ps_css_elmt);

        loop {
            let token = parser.parse_css();
            result_chan.send(token);
            if token == css::to_eof { break; }
        }
    };
    ret result_port;
}
<commit_msg>Fixed parsing crash caused by refactored html parsing.<commit_after>import comm::{port, chan};
import html::html_methods;
import css::css_methods;
import dom::style;

enum parse_state {
    ps_html_normal,
    ps_html_tag,
    ps_css_elmt,
    ps_css_relation,
    ps_css_desc,
    ps_css_attribute
}

type parser = {
    mut lookahead: option<char_or_eof>,
    mut state: parse_state,
    reader: io::reader
};

enum char_or_eof {
    coe_char(u8),
    coe_eof
}

impl u8_methods for u8 {
    fn is_whitespace() -> bool {
        ret self == ' ' as u8 || self == '\n' as u8
            || self == '\t' as u8;
    }

    fn is_alpha() -> bool {
        ret (self >= ('A' as u8) && self <= ('Z' as u8)) ||
            (self >= ('a' as u8) && self <= ('z' as u8));
    }
}

impl u8_vec_methods for [u8] {
    fn to_str() -> str { ret str::from_bytes(self); }
    fn to_html_token() -> html::token { ret html::to_text(self.to_str()); }
    fn to_css_token() -> html::token { ret html::to_text(self.to_str()); }
}

impl util_methods for parser {
    fn get() -> char_or_eof {
        alt self.lookahead {
            some(coe) {
                let rv = coe;
                self.lookahead = none;
                ret rv;
            }
            none {
                /* fall through */
            }
        }

        if self.reader.eof() { ret coe_eof; }
        ret coe_char(self.reader.read_byte() as u8);
    }

    fn unget(ch: u8) {
        assert self.lookahead.is_none();
        self.lookahead = some(coe_char(ch));
    }

    fn parse_err(err: str) -> ! {
        fail err
    }

    fn expect(ch: u8) {
        alt self.get() {
            coe_char(c) {
                if c != ch {
                    self.parse_err(#fmt("expected '%c'", ch as char));
                }
            }
            coe_eof {
                self.parse_err(#fmt("expected '%c' at eof", ch as char));
            }
        }
    }

    fn parse_ident() -> str {
        let mut result: [u8] = [];
        loop {
            alt self.get() {
                coe_char(c) {
                    if (c.is_alpha()) {
                        result += [c];
                    } else if result.len() == 0u {
                        self.parse_err("expected ident");
                    } else {
                        self.unget(c);
                        break;
                    }
                }
                coe_eof {
                    self.parse_err("expected ident");
                }
            }
        }
        ret str::from_bytes(result);
    }

    fn expect_ident(expected: str) {
        let actual = self.parse_ident();
        if expected != actual {
            self.parse_err(#fmt("expected '%s' but found '%s'",
                                expected, actual));
        }
    }

    fn eat_whitespace() {
        loop {
            alt self.get() {
                coe_char(c) {
                  if !c.is_whitespace() {
                        self.unget(c);
                        ret;
                    }
                }
                coe_eof {
                    ret;
                }
            }
        }
    }

    fn parse_html() -> html::token {
        let mut ch: u8;
        alt self.get() {
            coe_char(c) { ch = c; }
            coe_eof { ret html::to_eof; }
        }

        let token = alt self.state {
          ps_html_normal   { self.parse_in_normal_state(ch) }
          ps_html_tag      { self.parse_in_tag_state(ch) }
          _                { fail "Parsing in html mode when not in " +
                                "an html state" }
        };

        #debug["token=%?", token];
        ret token;
    }

    fn parse_css() -> css::token {
        let mut ch: u8;
        alt self.get() {
            coe_char(c) { ch = c; }
            coe_eof { ret css::to_eof; }
        }

        let token = alt self.state {
          ps_css_desc        { self.parse_css_description(ch) }
          ps_css_attribute   { self.parse_css_attribute(ch) }
          ps_css_elmt        { self.parse_css_element(ch) }
          ps_css_relation    { self.parse_css_relation(ch) }
          _                  { fail "Parsing in css mode when not in " +
                                  "a css state" }
        };

        #debug["token=%?", token];
        ret token;
    }
}

mod html {
    enum token {
        to_start_opening_tag(str),
        to_end_opening_tag,
        to_end_tag(str),
        to_self_close_tag,
        to_text(str),
        to_attr(str, str),
        to_doctype,
        to_eof
    }

    impl html_methods for parser {
        fn parse_in_normal_state(c: u8) -> token {
            let mut ch = c;
            if ch == ('<' as u8) {
                alt self.get() {
                  coe_char(c) { ch = c; }
                  coe_eof { self.parse_err("eof after '<'") }
                }

                if ch == ('!' as u8) {
                    self.eat_whitespace();
                    self.expect_ident("DOCTYPE");
                    self.eat_whitespace();
                    self.expect_ident("html");
                    self.eat_whitespace();
                    self.expect('>' as u8);
                    ret to_doctype;
                }

                if ch == ('/' as u8) {
                    let ident = self.parse_ident();
                    self.expect('>' as u8);
                    ret to_end_tag(ident);
                }

                self.unget(ch);

                self.eat_whitespace();
                let ident = self.parse_ident();
                self.eat_whitespace();

                self.state = ps_html_tag;
                ret to_start_opening_tag(ident);
            }

            // Make a text node.
            let mut s: [u8] = [ch];
            loop {
                alt self.get() {
                  coe_char(c) {
                    if c == ('<' as u8) {
                        self.unget(c);
                        ret s.to_html_token();
                    }
                    s += [c];
                  }
                  coe_eof { ret s.to_html_token(); }
                }
            }
        }

        fn parse_in_tag_state(c: u8) -> token {
            let mut ch = c;

            if ch == ('>' as u8) {
                self.state = ps_html_normal;
                ret to_end_opening_tag;
            }

            if ch == ('/' as u8) {
                self.state = ps_html_normal;
                ret to_self_close_tag;
            }

            if !ch.is_alpha() {
                fail #fmt("expected alphabetical in tag but found %c",
                          ch as char);
            }

            // Parse an attribute.
            let mut attribute_name = [ch];
            loop {
                alt self.get() {
                  coe_char(c) {
                    if c == ('=' as u8) { break; }
                    attribute_name += [c];
                  }
                  coe_eof {
                    ret to_attr(attribute_name.to_str(),
                                attribute_name.to_str()); }
                }
            }

            // Parse the attribute value.
            self.expect('"' as u8);
            let mut attribute_value = [];
            loop {
                alt self.get() {
                  coe_char(c) {
                    if c == ('"' as u8) { break; }
                    attribute_value += [c];
                  }
                  coe_eof {
                    ret to_attr(attribute_name.to_str(),
                                attribute_value.to_str());
                  }
                }
            }

            // Eat whitespacpe.
            self.eat_whitespace();

            ret to_attr(attribute_name.to_str(), attribute_value.to_str());
        }
    }
}

mod css {
    enum token {
        to_start_desc,
        to_end_desc,
        to_descendant,
        to_child,
        to_sibling,
        to_comma,
        to_elmt(str),
        to_attr(style::attr),
        to_desc(str, str),
        to_eof
    }

    impl css_methods for parser {
        fn parse_css_relation(c : u8) -> token {
            self.state = ps_css_elmt;

            let token = alt c {
              '{' as u8  { self.state = ps_css_desc; to_start_desc }
              '>' as u8  { to_child }
              '+' as u8  { to_sibling }
              ',' as u8  { to_comma }
              _          { to_descendant }
            };

            self.eat_whitespace();

            ret token;
        }

        fn parse_css_element(c : u8) -> token {
            /* Check for special attributes with an implied element.*/
            if c == '.' as u8 || c == '#' as u8 {
                self.state = ps_css_attribute;
                self.unget(c);
                ret to_elmt("*");
            }

            let element = self.parse_ident();
            self.state = ps_css_attribute;

            ret to_elmt(element);
        }

        fn parse_css_attribute(c : u8) -> token {
            let mut ch = c;

            /* If we've reached the end of this list of attributes,
            look for the relation to the next element.*/
            if c.is_whitespace() {
                self.state = ps_css_relation;
                self.eat_whitespace();

                alt self.get() {
                  coe_char(c)  { ch = c }
                  coe_eof      { fail "File ended before description " +
                                    "of style" }
                }

                ret self.parse_css_relation(ch);
            }

            alt ch {
              '.' as u8 { ret to_attr(
                  style::includes("class", self.parse_ident())); }
              '#' as u8 { ret to_attr(
                  style::includes("id", self.parse_ident())); }
              '[' as u8 {
                let attr_name = self.parse_ident();

                alt self.get() {
                  coe_char(c)    { ch = c; }
                  coe_eof        { fail "File ended before " +
                                      "description finished"; }
                }

                if ch == ']' as u8 {
                    ret to_attr(style::exists(attr_name));
                } else if ch == '=' as u8 {
                    let attr_val = self.parse_ident();
                    self.expect(']' as u8);
                    ret to_attr(style::exact(attr_name, attr_val));
                } else if ch == '~' as u8 {
                    self.expect('=' as u8);
                    let attr_val = self.parse_ident();
                    self.expect(']' as u8);
                    ret to_attr(style::includes(attr_name, attr_val));
                } else if ch == '|' as u8 {
                    self.expect('=' as u8);
                    let attr_val = self.parse_ident();
                    self.expect(']' as u8);
                    ret to_attr(style::starts_with(attr_name, attr_val));
                }

                fail #fmt("Unexpected symbol %c in attribute", ch as char);
              }
              _   { fail #fmt("Unexpected symbol %c in attribute",
                              ch as char); }
            }
        }

        fn parse_css_description(c: u8) -> token {
            let mut ch = c;

            if ch.is_whitespace() {
                self.eat_whitespace();

                alt self.get() {
                  coe_char(c)  { ch = c }
                  coe_eof      { fail "Reached end of file " +
                                    "in CSS description" }
                }
            }

            let mut desc_name = [];

            // Get the name of the descriptor
            loop {
                if ch.is_whitespace() {
                    self.eat_whitespace();
                } else if ch == ':' as u8 {
                    if desc_name.len() == 0u {
                        fail "Expected descriptor name";
                    } else {
                        break;
                    }
                } else {
                    desc_name += [ch];
                }

                alt self.get() {
                  coe_char(c)  { ch = c }
                  coe_eof      { fail "Reached end of file " +
                                    "in CSS description" }
                }
            }

            self.eat_whitespace();
            let mut desc_val = [];

            // Get the value of the descriptor
            loop {
                alt self.get() {
                  coe_char(c)  { ch = c }
                  coe_eof      { fail "Reached end of file " +
                                    "in CSS description" }
                }

                if ch.is_whitespace() {
                    self.eat_whitespace();
                } else if ch == '}' as u8 {
                    if desc_val.len() == 0u {
                        fail "Expected descriptor value";
                    } else {
                        self.state = ps_css_elmt;
                        break;
                    }
                } else if ch == ';' as u8 {
                    if desc_val.len() == 0u {
                        fail "Expected descriptor value";
                    } else {
                        break;
                    }
                } else {
                    desc_val += [ch];
                }
            }

            ret to_desc(desc_name.to_str(), desc_val.to_str());
        }
    }
}

fn parser(reader: io::reader, state : parse_state) -> parser {
    ret { mut lookahead: none, mut state: state, reader: reader };
}

fn spawn_html_parser_task(filename: str) -> port<html::token> {
    let result_port = port();
    let result_chan = chan(result_port);
    task::spawn {||
        let file_data = io::read_whole_file(filename).get();
        let reader = io::bytes_reader(file_data);

        assert filename.ends_with(".html");
        let parser = parser(reader, ps_html_normal);

        loop {
            let token = parser.parse_html();
            result_chan.send(token);
            if token == html::to_eof { break; }
        }
    };
    ret result_port;
}

fn spawn_css_parser_task(filename: str) -> port<css::token> {
    let result_port = port();
    let result_chan = chan(result_port);
    task::spawn {||
        let file_data = io::read_whole_file(filename).get();
        let reader = io::bytes_reader(file_data);

        assert filename.ends_with(".css");
        let parser : parser = parser(reader, ps_css_elmt);

        loop {
            let token = parser.parse_css();
            result_chan.send(token);
            if token == css::to_eof { break; }
        }
    };
    ret result_port;
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use mimir;
use serde_json;

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)/(?P<index>\w+)")
                 .unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap();
    let port = cap.name("port").unwrap().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        match doc_type.as_ref() {
            "addr" => serde_json::from_value::<mimir::Addr>(*v).ok().and_then(|o| Some(mimir::Place::Addr(o))),
            "street" => serde_json::from_value::<mimir::Street>(*v).ok().and_then(|o| Some(mimir::Place::Street(o))),
            "admin" => serde_json::from_value::<mimir::Admin>(*v).ok().and_then(|o| Some(mimir::Place::Admin(o))),
            _    => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

fn query(q: &String, cnx: &String) -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let sub_query = rs_q::build_bool()
                        .with_should(vec![
                       rs_q::build_term("_type","addr").with_boost(1000).build(),
                       rs_q::build_match("name.prefix", q.to_string())
                              .with_boost(100)
                              .build(),
                       rs_q::build_function_score()
                              .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                              .with_boost(30)
                              .with_query(rs_q::build_match_all().build())
			                  .with_function(
                          rs_es::query::functions::Function::build_field_value_factor("weight")
			                                  .with_factor(1)
                                      .with_modifier(rs_es::query::functions::Modifier::Log1p)
			                                                          .build())
                              .build()])
                        .build();
    let filter = rs_q::build_bool()
                     .with_should(vec![rs_q::build_bool()
                                           .with_must_not(rs_q::build_exists("house_number")
                                                              .build())
                                           .build(),
                                       rs_q::build_match("house_number", q.to_string()).build()])
                     .with_must(vec![rs_q::build_match("name.prefix", q.to_string())
             .with_minimum_should_match(rs_es::query::MinimumShouldMatch::from(100f64)).build()])
                     .build();

    let final_query = rs_q::build_bool()
                          .with_must(vec![sub_query])
                          .with_filter(filter)
                          .build();

    let mut client = build_rs_client(cnx);

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
                                                       .with_indexes(&["munin"])
                                                       .with_query(&final_query)
                                                       .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
             .hits
             .into_iter()
             .filter_map(|hit| make_place(hit.doc_type, hit.source))
             .collect())
}

fn query_location(_q: &String,
                  _coord: &model::Coord)
                  -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    panic!("todo!");
}

pub fn autocomplete(q: String,
                    coord: Option<model::Coord>,
                    cnx: &String)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    if let Some(ref coord) = coord {
        query_location(&q, coord)
    } else {
        query(&q, cnx)
    }
}
<commit_msg>bragi: A second search with match = name.ngram if no solution<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use mimir;
use serde_json;

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)/(?P<index>\w+)")
                 .unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap();
    let port = cap.name("port").unwrap().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        match doc_type.as_ref() {
            "addr" => serde_json::from_value::<mimir::Addr>(*v).ok().and_then(|o| Some(mimir::Place::Addr(o))),
            "street" => serde_json::from_value::<mimir::Street>(*v).ok().and_then(|o| Some(mimir::Place::Street(o))),
            "admin" => serde_json::from_value::<mimir::Admin>(*v).ok().and_then(|o| Some(mimir::Place::Admin(o))),
            _    => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

fn query(q: &String, cnx: &String, match_type: &str) -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let sub_query = rs_q::build_bool()
                        .with_should(vec![
                       rs_q::build_term("_type","addr").with_boost(1000).build(),
                       rs_q::build_match(match_type, q.to_string())
                              .with_boost(100)
                              .build(),
                       rs_q::build_function_score()
                              .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                              .with_boost(30)
                              .with_query(rs_q::build_match_all().build())
			                  .with_function(
                          rs_es::query::functions::Function::build_field_value_factor("weight")
			                                  .with_factor(1)
                                      .with_modifier(rs_es::query::functions::Modifier::Log1p)
			                                                          .build())
                              .build()])
                        .build();
    let filter = rs_q::build_bool()
                     .with_should(vec![rs_q::build_bool()
                                           .with_must_not(rs_q::build_exists("house_number")
                                                              .build())
                                           .build(),
                                       rs_q::build_match("house_number", q.to_string()).build()])
                     .with_must(vec![rs_q::build_match(match_type, q.to_string())
             .with_minimum_should_match(rs_es::query::MinimumShouldMatch::from(100f64)).build()])
                     .build();

    let final_query = rs_q::build_bool()
                          .with_must(vec![sub_query])
                          .with_filter(filter)
                          .build();

    let mut client = build_rs_client(cnx);

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
                                                       .with_indexes(&["munin"])
                                                       .with_query(&final_query)
                                                       .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
             .hits
             .into_iter()
             .filter_map(|hit| make_place(hit.doc_type, hit.source))
             .collect())
}

fn query_location(_q: &String,
                  _coord: &model::Coord)
                  -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    panic!("todo!");
}

pub fn autocomplete(q: String,
                    coord: Option<model::Coord>,
                    cnx: &String)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    if let Some(ref coord) = coord {
        query_location(&q, coord)
    } else {
    	//First search with match = "name.prefix".
    	//If no result then another search with match = "name.ngram"
    	let results = try!(query(&q, cnx, "name.prefix"));
        if results.is_empty() {
        	query(&q, cnx, "name.ngram")
        } else {
        	Ok(results)
        }
    }
}
<|endoftext|>