about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorJack Moffitt <jack@metajack.im>2013-12-09 14:56:53 -0700
committerJack Moffitt <jack@metajack.im>2013-12-10 17:04:24 -0700
commitb349036e5f4f5f42e34ae9dd7859f3dc7a79de94 (patch)
treec11b022d85120eddff8bdf1be2fd36fe3939369f /src/libsyntax
parent29ca4350c8d64facb39311660e8ee919766f481a (diff)
downloadrust-b349036e5f4f5f42e34ae9dd7859f3dc7a79de94.tar.gz
rust-b349036e5f4f5f42e34ae9dd7859f3dc7a79de94.zip
Make crate hash stable and externally computable.
This replaces the link meta attributes with a pkgid attribute and uses a hash
of this as the crate hash. This makes the crate hash computable by things
other than the Rust compiler. It also switches the hash function ot SHA1 since
that is much more likely to be available in shell, Python, etc than SipHash.

Fixes #10188, #8523.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/attr.rs8
-rw-r--r--src/libsyntax/lib.rs3
-rw-r--r--src/libsyntax/pkgid.rs160
3 files changed, 171 insertions, 0 deletions
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 05a65de16b8..ac5254f1aba 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -18,6 +18,7 @@ use codemap::{Span, Spanned, spanned, dummy_spanned};
 use codemap::BytePos;
 use diagnostic::span_handler;
 use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
+use pkgid::PkgId;
 
 use std::hashmap::HashSet;
 
@@ -235,6 +236,13 @@ pub fn find_linkage_metas(attrs: &[Attribute]) -> ~[@MetaItem] {
     result
 }
 
+pub fn find_pkgid(attrs: &[Attribute]) -> Option<PkgId> {
+    match first_attr_value_str_by_name(attrs, "pkgid") {
+        None => None,
+        Some(id) => from_str::<PkgId>(id),
+    }
+}
+
 #[deriving(Eq)]
 pub enum InlineAttr {
     InlineNone,
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index c9b3ba9d460..4382e6d67b8 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -13,6 +13,8 @@
  *  macros.
  */
 
+#[pkgid="syntax#0.9-pre"];
+// NOTE: remove after the next snapshot
 #[link(name = "syntax",
        package_id = "syntax",
        vers = "0.9-pre",
@@ -51,6 +53,7 @@ pub mod fold;
 
 
 pub mod parse;
+pub mod pkgid;
 
 pub mod print {
     pub mod pp;
diff --git a/src/libsyntax/pkgid.rs b/src/libsyntax/pkgid.rs
new file mode 100644
index 00000000000..1e840ca700b
--- /dev/null
+++ b/src/libsyntax/pkgid.rs
@@ -0,0 +1,160 @@
+// Copyright 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.
+
+#[deriving(Clone, Eq)]
+pub struct PkgId {
+    path: ~str,
+    name: ~str,
+    version: Option<~str>,
+}
+
+impl ToStr for PkgId {
+    fn to_str(&self) -> ~str {
+        let version = match self.version {
+            None => "0.0",
+            Some(ref version) => version.as_slice(),
+        };
+        if self.path.is_empty() {
+            format!("{}\\#{}", self.name, version)
+        } else {
+            format!("{}/{}\\#{}", self.path, self.name, version)
+        }
+    }
+}
+
+impl FromStr for PkgId {
+    fn from_str(s: &str) -> Option<PkgId> {
+        let hash_idx = match s.find('#') {
+            None => s.len(),
+            Some(idx) => idx,
+        };
+        let prefix = s.slice_to(hash_idx);
+        let name_idx = match prefix.rfind('/') {
+            None => 0,
+            Some(idx) => idx + 1,
+        };
+        if name_idx >= prefix.len() {
+            return None;
+        }
+        let name = prefix.slice_from(name_idx);
+        if name.len() <= 0 {
+            return None;
+        }
+
+        let path = if name_idx == 0 {
+            ""
+        } else {
+            prefix.slice_to(name_idx - 1)
+        };
+        let check_path = Path::new(path);
+        if !check_path.is_relative() {
+            return None;
+        }
+
+        let version = match s.find('#') {
+            None => None,
+            Some(idx) => {
+                if idx >= s.len() {
+                    None
+                } else {
+                    let v = s.slice_from(idx + 1);
+                    if v.is_empty() {
+                        None
+                    } else {
+                        Some(v.to_owned())
+                    }
+                }
+            }
+        };
+
+        Some(PkgId{
+            path: path.to_owned(),
+            name: name.to_owned(),
+            version: version,
+        })
+    }
+}
+
+impl PkgId {
+    pub fn version_or_default<'a>(&'a self) -> &'a str {
+        match self.version {
+            None => "0.0",
+            Some(ref version) => version.as_slice(),
+        }
+    }
+}
+
+#[test]
+fn bare_name() {
+    let pkgid: PkgId = from_str("foo").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"foo");
+    assert_eq!(pkgid.version, None);
+    assert_eq!(pkgid.path, ~"");
+}
+
+#[test]
+fn bare_name_single_char() {
+    let pkgid: PkgId = from_str("f").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"f");
+    assert_eq!(pkgid.version, None);
+    assert_eq!(pkgid.path, ~"");
+}
+
+#[test]
+fn empty_pkgid() {
+    let pkgid: Option<PkgId> = from_str("");
+    assert!(pkgid.is_none());
+}
+
+#[test]
+fn simple_path() {
+    let pkgid: PkgId = from_str("example.com/foo/bar").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"bar");
+    assert_eq!(pkgid.version, None);
+    assert_eq!(pkgid.path, ~"example.com/foo");
+}
+
+#[test]
+fn simple_version() {
+    let pkgid: PkgId = from_str("foo#1.0").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"foo");
+    assert_eq!(pkgid.version, Some(~"1.0"));
+    assert_eq!(pkgid.path, ~"");
+}
+
+#[test]
+fn absolute_path() {
+    let pkgid: Option<PkgId> = from_str("/foo/bar");
+    assert!(pkgid.is_none());
+}
+
+#[test]
+fn path_and_version() {
+    let pkgid: PkgId = from_str("example.com/foo/bar#1.0").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"bar");
+    assert_eq!(pkgid.version, Some(~"1.0"));
+    assert_eq!(pkgid.path, ~"example.com/foo");
+}
+
+#[test]
+fn single_chars() {
+    let pkgid: PkgId = from_str("a/b#1").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"b");
+    assert_eq!(pkgid.version, Some(~"1"));
+    assert_eq!(pkgid.path, ~"a");
+}
+
+#[test]
+fn missing_version() {
+    let pkgid: PkgId = from_str("foo#").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"foo");
+    assert_eq!(pkgid.version, None);
+    assert_eq!(pkgid.path, ~"");
+}
\ No newline at end of file