Skip to content

Commit

Permalink
rustpkg: Compute hash to find crate
Browse files Browse the repository at this point in the history
Previously rustpkg tried to parse filenames to find crate. Now ue use
deterministic hashes, so it becomes possible to directly construct
filename and check if the file exists.
  • Loading branch information
klutzy committed Jan 22, 2014
1 parent 655433e commit df9067c
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 199 deletions.
35 changes: 18 additions & 17 deletions src/librustpkg/crate_id.rs
Expand Up @@ -9,8 +9,9 @@
// except according to those terms.

use std::hash::Streaming;
use std::hash;
use syntax::crateid;
use extra::hex::ToHex;
use rustc::util::sha2::{Digest, Sha256};

/// Path-fragment identifier of a package such as
/// 'github.com/graydon/test'; path must be a relative
Expand Down Expand Up @@ -41,7 +42,7 @@ impl Eq for CrateId {
}

impl CrateId {
pub fn get_version<'a>(&'a self) -> &'a str {
pub fn version_or_default<'a>(&'a self) -> &'a str {
match self.version {
Some(ref ver) => ver.as_slice(),
None => "0.0"
Expand All @@ -66,16 +67,24 @@ impl CrateId {
}
}

pub fn to_crate_id_str(&self) -> ~str {
format!("{}\\#{}", self.path.as_str().unwrap(), self.version_or_default())
}

pub fn to_lib_name(&self) -> ~str {
format!("{}-{}-{}", self.short_name, self.hash(), self.version_or_default())
}

pub fn hash(&self) -> ~str {
// FIXME (#9639): hash should take a &[u8] so we can hash the real path
self.path.display().with_str(|s| {
let vers = self.get_version();
format!("{}-{}-{}", s, hash(s + vers), vers)
})
let mut hasher = Sha256::new();
hasher.reset();
hasher.input_str(self.to_crate_id_str());
let hash = hasher.result_bytes().to_hex();
hash.slice_chars(0, 8).to_owned()
}

pub fn short_name_with_version(&self) -> ~str {
format!("{}-{}", self.short_name, self.get_version())
format!("{}-{}", self.short_name, self.version_or_default())
}

/// True if the ID has multiple components
Expand Down Expand Up @@ -126,18 +135,10 @@ impl Iterator<(Path, Path)> for Prefixes {
impl ToStr for CrateId {
fn to_str(&self) -> ~str {
// should probably use the filestem and not the whole path
format!("{}-{}", self.path.as_str().unwrap(), self.get_version())
format!("{}-{}", self.path.as_str().unwrap(), self.version_or_default())
}
}


pub fn write<W: Writer>(writer: &mut W, string: &str) {
writer.write(string.as_bytes());
}

pub fn hash(data: ~str) -> ~str {
let hasher = &mut hash::default_state();
write(hasher, data);
hasher.result_str()
}

2 changes: 1 addition & 1 deletion src/librustpkg/package_source.rs
Expand Up @@ -287,7 +287,7 @@ impl PkgSrc {
// FIXME (#9639): This needs to handle non-utf8 paths
let url = format!("https://{}", crateid.path.as_str().unwrap());
debug!("Fetching package: git clone {} {} [version={}]",
url, clone_target.display(), crateid.get_version());
url, clone_target.display(), crateid.version_or_default());

let mut failed = false;

Expand Down
155 changes: 30 additions & 125 deletions src/librustpkg/path_util.rs
Expand Up @@ -14,17 +14,16 @@

pub use crate_id::CrateId;
pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install};
pub use version::{Version, split_version, split_version_general,
try_parsing_version};
pub use version::{Version, split_version, split_version_general, try_parsing_version};
pub use rustc::metadata::filesearch::rust_path;
use rustc::metadata::filesearch::{libdir, relative_target_lib_path};
use rustc::driver::driver::host_triple;

use std::libc;
use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
use std::os;
use std::io;
use std::io::fs;
use rustc::metadata::filesearch::{libdir, relative_target_lib_path};
use rustc::driver::driver::host_triple;
use messages::*;

pub fn default_workspace() -> Path {
Expand Down Expand Up @@ -173,151 +172,57 @@ fn output_in_workspace(crateid: &CrateId, workspace: &Path, what: OutputType) ->
/// Figure out what the library name for <crateid> in <workspace>'s build
/// directory is, and if the file exists, return it.
pub fn built_library_in_workspace(crateid: &CrateId, workspace: &Path) -> Option<Path> {
library_in_workspace(&crateid.path, crateid.short_name, Build, workspace, "build",
&crateid.version)
library_in_workspace(crateid, Build, workspace)
}

/// Does the actual searching stuff
pub fn installed_library_in_workspace(pkg_path: &Path, workspace: &Path) -> Option<Path> {
pub fn installed_library_in_workspace(crate_id: &CrateId, workspace: &Path) -> Option<Path> {
// This could break once we're handling multiple versions better -- I should add a test for it
// FIXME (#9639): This needs to handle non-utf8 paths
match pkg_path.filename_str() {
match crate_id.path.filename_str() {
None => None,
Some(short_name) => library_in_workspace(pkg_path,
short_name,
Install,
workspace,
libdir(),
&None)
Some(_short_name) => library_in_workspace(crate_id, Install, workspace)
}
}

/// `workspace` is used to figure out the directory to search.
/// `short_name` is taken as the link name of the library.
pub fn library_in_workspace(path: &Path, short_name: &str, where: Target,
workspace: &Path, prefix: &str, version: &Version) -> Option<Path> {
pub fn library_in_workspace(crate_id: &CrateId, where: Target, workspace: &Path) -> Option<Path> {
debug!("library_in_workspace: checking whether a library named {} exists",
short_name);

// We don't know what the hash is, so we have to search through the directory
// contents

debug!("short_name = {} where = {:?} workspace = {} \
prefix = {}", short_name, where, workspace.display(), prefix);
crate_id.short_name);

let dir_to_search = match where {
Build => target_build_dir(workspace).join(path),
Build => target_build_dir(workspace).join(&crate_id.path),
Install => target_lib_dir(workspace)
};

library_in(short_name, version, &dir_to_search)
library_in(crate_id, &dir_to_search)
}

pub fn system_library(sysroot: &Path, crate_id: &str) -> Option<Path> {
let (lib_name, version) = split_crate_id(crate_id);
library_in(lib_name, &version, &sysroot.join(relative_target_lib_path(host_triple())))
}

fn library_in(short_name: &str, version: &Version, dir_to_search: &Path) -> Option<Path> {
debug!("Listing directory {}", dir_to_search.display());
let dir_contents = {
let _guard = io::ignore_io_error();
fs::readdir(dir_to_search)
};
debug!("dir has {:?} entries", dir_contents.len());

let dll_prefix = format!("{}{}", os::consts::DLL_PREFIX, short_name);
let dll_filetype = os::consts::DLL_EXTENSION;
let rlib_prefix = format!("{}{}", "lib", short_name);
let rlib_filetype = "rlib";

debug!("dll_prefix = {} and dll_filetype = {}", dll_prefix, dll_filetype);
debug!("rlib_prefix = {} and rlib_filetype = {}", rlib_prefix, rlib_filetype);

// Find a filename that matches the pattern:
// (lib_prefix)-hash-(version)(lib_suffix)
let mut libraries = dir_contents.iter().filter(|p| {
let extension = p.extension_str();
debug!("p = {}, p's extension is {:?}", p.display(), extension);
match extension {
None => false,
Some(ref s) => dll_filetype == *s || rlib_filetype == *s,
library_in(&CrateId::new(crate_id), &sysroot.join(relative_target_lib_path(host_triple())))
}

fn library_in(crate_id: &CrateId, dir_to_search: &Path) -> Option<Path> {
let lib_name = crate_id.to_lib_name();
let filenames = [
format!("{}{}.{}", "lib", lib_name, "rlib"),
format!("{}{}{}", os::consts::DLL_PREFIX, lib_name, os::consts::DLL_SUFFIX),
];

for filename in filenames.iter() {
debug!("filename = {}", filename.as_slice());
let path = dir_to_search.join(filename.as_slice());
if path.exists() {
debug!("found: {}", path.display());
return Some(path);
}
});

let mut result_filename = None;
for p_path in libraries {
// Find a filename that matches the pattern: (lib_prefix)-hash-(version)(lib_suffix)
// and remember what the hash was
let mut f_name = match p_path.filestem_str() {
Some(s) => s, None => continue
};
// Already checked the filetype above

// This is complicated because library names and versions can both contain dashes
loop {
if f_name.is_empty() { break; }
match f_name.rfind('-') {
Some(i) => {
debug!("Maybe {} is a version", f_name.slice(i + 1, f_name.len()));
match try_parsing_version(f_name.slice(i + 1, f_name.len())) {
Some(ref found_vers) if version == &Some(found_vers.to_owned()) ||
version == &None => {
match f_name.slice(0, i).rfind('-') {
Some(j) => {
let lib_prefix = match p_path.extension_str() {
Some(ref s) if dll_filetype == *s => &dll_prefix,
_ => &rlib_prefix,
};
debug!("Maybe {} equals {}", f_name.slice(0, j), *lib_prefix);
if f_name.slice(0, j) == *lib_prefix {
result_filename = Some(p_path.clone());
}
break;
}
None => break
}
}
_ => { f_name = f_name.slice(0, i); }
}
}
None => break
} // match
} // loop
} // for

if result_filename.is_none() {
debug!("warning: library_in_workspace didn't find a library in {} for {}",
dir_to_search.display(), short_name);
}

// Return the filename that matches, which we now know exists
// (if result_filename != None)
let abs_path = result_filename.map(|result_filename| {
let absolute_path = dir_to_search.join(&result_filename);
debug!("result_filename = {}", absolute_path.display());
absolute_path
});

abs_path
}

fn split_crate_id<'a>(crate_id: &'a str) -> (&'a str, Version) {
match split_version(crate_id) {
Some((name, vers)) =>
match vers {
Some(ref v) => match v.find('-') {
Some(pos) => (name, Some(v.slice(0, pos).to_owned())),
None => (name, Some(v.to_owned()))
},
_ => (name, vers)
},
None => (crate_id, None)
}
debug!("warning: library_in_workspace didn't find a library in {} for {}",
dir_to_search.display(), crate_id.short_name);
return None;
}



/// Returns the executable that would be installed for <crateid>
/// in <workspace>
/// As a side effect, creates the bin-dir if it doesn't exist
Expand Down

0 comments on commit df9067c

Please sign in to comment.