summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2013-05-12 14:17:38 -0700
committerTodd Gamblin <tgamblin@llnl.gov>2013-05-12 14:17:38 -0700
commitb2a5fef6ad11c9f975d3ddb4a6e34b03bb8051f5 (patch)
tree955aa1028d0bd52e55173d281ef143873df8237e
parentad4411bc9e8de6d13edcf3be78d78a989475b1ec (diff)
downloadspack-b2a5fef6ad11c9f975d3ddb4a6e34b03bb8051f5.tar.gz
spack-b2a5fef6ad11c9f975d3ddb4a6e34b03bb8051f5.tar.bz2
spack-b2a5fef6ad11c9f975d3ddb4a6e34b03bb8051f5.tar.xz
spack-b2a5fef6ad11c9f975d3ddb4a6e34b03bb8051f5.zip
Commands take specs as input instead of names.
modified clean, create, fetch, install, and uninstall
-rw-r--r--lib/spack/spack/cmd/__init__.py27
-rw-r--r--lib/spack/spack/cmd/clean.py14
-rw-r--r--lib/spack/spack/cmd/create.py4
-rw-r--r--lib/spack/spack/cmd/fetch.py12
-rw-r--r--lib/spack/spack/cmd/install.py15
-rw-r--r--lib/spack/spack/cmd/uninstall.py11
-rw-r--r--lib/spack/spack/dependency.py2
-rw-r--r--lib/spack/spack/package.py4
-rw-r--r--lib/spack/spack/parse.py7
-rw-r--r--lib/spack/spack/relations.py1
-rw-r--r--lib/spack/spack/stage.py3
-rw-r--r--lib/spack/spack/test/url_parse.py9
-rw-r--r--lib/spack/spack/test/versions.py (renamed from lib/spack/spack/test/compare_versions.py)5
-rw-r--r--lib/spack/spack/url.py166
-rw-r--r--lib/spack/spack/version.py165
15 files changed, 265 insertions, 180 deletions
diff --git a/lib/spack/spack/cmd/__init__.py b/lib/spack/spack/cmd/__init__.py
index a175ce7cfb..9b034b2a60 100644
--- a/lib/spack/spack/cmd/__init__.py
+++ b/lib/spack/spack/cmd/__init__.py
@@ -1,7 +1,9 @@
import os
import re
+import sys
import spack
+import spack.spec
import spack.tty as tty
import spack.attr as attr
@@ -21,10 +23,6 @@ for file in os.listdir(command_path):
commands.sort()
-def null_op(*args):
- pass
-
-
def get_cmd_function_name(name):
return name.replace("-", "_")
@@ -36,7 +34,7 @@ def get_module(name):
module_name, fromlist=[name, SETUP_PARSER, DESCRIPTION],
level=0)
- attr.setdefault(module, SETUP_PARSER, null_op)
+ attr.setdefault(module, SETUP_PARSER, lambda *args: None) # null-op
attr.setdefault(module, DESCRIPTION, "")
fn_name = get_cmd_function_name(name)
@@ -50,3 +48,22 @@ def get_module(name):
def get_command(name):
"""Imports the command's function from a module and returns it."""
return getattr(get_module(name), get_cmd_function_name(name))
+
+
+def parse_specs(args):
+ """Convenience function for parsing arguments from specs. Handles common
+ exceptions and dies if there are errors.
+ """
+ if type(args) == list:
+ args = " ".join(args)
+
+ try:
+ return spack.spec.parse(" ".join(args))
+
+ except spack.parse.ParseError, e:
+ e.print_error(sys.stdout)
+ sys.exit(1)
+
+ except spack.spec.SpecError, e:
+ tty.error(e.message)
+ sys.exit(1)
diff --git a/lib/spack/spack/cmd/clean.py b/lib/spack/spack/cmd/clean.py
index 33f1d68a46..d827768e6e 100644
--- a/lib/spack/spack/cmd/clean.py
+++ b/lib/spack/spack/cmd/clean.py
@@ -1,3 +1,6 @@
+import argparse
+
+import spack.cmd
import spack.packages as packages
import spack.tty as tty
import spack.stage as stage
@@ -5,21 +8,22 @@ import spack.stage as stage
description = "Remove staged files for packages"
def setup_parser(subparser):
- subparser.add_argument('names', nargs='+', help="name(s) of package(s) to clean")
subparser.add_argument('-c', "--clean", action="store_true", dest='clean',
help="run make clean in the stage directory (default)")
subparser.add_argument('-w', "--work", action="store_true", dest='work',
help="delete and re-expand the entire stage directory")
subparser.add_argument('-d', "--dist", action="store_true", dest='dist',
help="delete the downloaded archive.")
+ subparser.add_argument('packages', nargs=argparse.REMAINDER, help="specs of packages to clean")
def clean(parser, args):
- if not args.names:
- tty.die("spack clean requires at least one package name.")
+ if not args.packages:
+ tty.die("spack clean requires at least one package argument")
- for name in args.names:
- package = packages.get(name)
+ specs = spack.cmd.parse_specs(args.packages)
+ for spec in specs:
+ package = packages.get(spec.name)
if args.dist:
package.do_clean_dist()
elif args.work:
diff --git a/lib/spack/spack/cmd/create.py b/lib/spack/spack/cmd/create.py
index 36b6d0e571..e0d087bb9d 100644
--- a/lib/spack/spack/cmd/create.py
+++ b/lib/spack/spack/cmd/create.py
@@ -4,7 +4,7 @@ import os
import spack
import spack.packages as packages
import spack.tty as tty
-import spack.version
+import spack.url
from spack.stage import Stage
from contextlib import closing
@@ -38,7 +38,7 @@ def create(parser, args):
url = args.url
# Try to deduce name and version of the new package from the URL
- name, version = spack.version.parse(url)
+ name, version = spack.url.parse_name_and_version(url)
if not name:
print "Couldn't guess a name for this package."
while not name:
diff --git a/lib/spack/spack/cmd/fetch.py b/lib/spack/spack/cmd/fetch.py
index df5173fdaa..2c8098b673 100644
--- a/lib/spack/spack/cmd/fetch.py
+++ b/lib/spack/spack/cmd/fetch.py
@@ -1,12 +1,18 @@
+import argparse
+import spack.cmd
import spack.packages as packages
description = "Fetch archives for packages"
def setup_parser(subparser):
- subparser.add_argument('names', nargs='+', help="names of packages to fetch")
+ subparser.add_argument('packages', nargs=argparse.REMAINDER, help="specs of packages to fetch")
def fetch(parser, args):
- for name in args.names:
- package = packages.get(name)
+ if not args.packages:
+ tty.die("fetch requires at least one package argument")
+
+ specs = spack.cmd.parse_specs(args.packages)
+ for spec in specs:
+ package = packages.get(spec.name)
package.do_fetch()
diff --git a/lib/spack/spack/cmd/install.py b/lib/spack/spack/cmd/install.py
index 766be9a3ea..1af74e0f60 100644
--- a/lib/spack/spack/cmd/install.py
+++ b/lib/spack/spack/cmd/install.py
@@ -1,19 +1,28 @@
+import sys
+import argparse
+
import spack
import spack.packages as packages
+import spack.cmd
description = "Build and install packages"
def setup_parser(subparser):
- subparser.add_argument('names', nargs='+', help="names of packages to install")
subparser.add_argument('-i', '--ignore-dependencies',
action='store_true', dest='ignore_dependencies',
help="Do not try to install dependencies of requested packages.")
subparser.add_argument('-d', '--dirty', action='store_true', dest='dirty',
help="Don't clean up partially completed build/installation on error.")
+ subparser.add_argument('packages', nargs=argparse.REMAINDER, help="specs of packages to install")
+
def install(parser, args):
+ if not args.packages:
+ tty.die("install requires at least one package argument")
+
spack.ignore_dependencies = args.ignore_dependencies
- for name in args.names:
- package = packages.get(name)
+ specs = spack.cmd.parse_specs(args.packages)
+ for spec in specs:
+ package = packages.get(spec.name)
package.dirty = args.dirty
package.do_install()
diff --git a/lib/spack/spack/cmd/uninstall.py b/lib/spack/spack/cmd/uninstall.py
index 2cc5aeb2c8..e76d91f123 100644
--- a/lib/spack/spack/cmd/uninstall.py
+++ b/lib/spack/spack/cmd/uninstall.py
@@ -1,15 +1,22 @@
+import spack.cmd
import spack.packages as packages
+import argparse
description="Remove an installed package"
def setup_parser(subparser):
- subparser.add_argument('names', nargs='+', help="name(s) of package(s) to uninstall")
subparser.add_argument('-f', '--force', action='store_true', dest='force',
help="Ignore installed packages that depend on this one and remove it anyway.")
+ subparser.add_argument('packages', nargs=argparse.REMAINDER, help="specs of packages to uninstall")
def uninstall(parser, args):
+ if not args.packages:
+ tty.die("uninstall requires at least one package argument.")
+
+ specs = spack.cmd.parse_specs(args.packages)
+
# get packages to uninstall as a list.
- pkgs = [packages.get(name) for name in args.names]
+ pkgs = [packages.get(spec.name) for spec in specs]
# Sort packages to be uninstalled by the number of installed dependents
# This ensures we do things in the right order
diff --git a/lib/spack/spack/dependency.py b/lib/spack/spack/dependency.py
index ef87de7ce1..7b8517b035 100644
--- a/lib/spack/spack/dependency.py
+++ b/lib/spack/spack/dependency.py
@@ -9,7 +9,7 @@ import packages
class Dependency(object):
"""Represents a dependency from one package to another.
"""
- def __init__(self, name, version):
+ def __init__(self, name):
self.name = name
@property
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 13da76c934..81b64ccc4e 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -22,7 +22,7 @@ import packages
import tty
import attr
import validate
-import version
+import url
import arch
from multi_function import platform
@@ -261,7 +261,7 @@ class Package(object):
validate.url(self.url)
# Set up version
- attr.setdefault(self, 'version', version.parse_version(self.url))
+ attr.setdefault(self, 'version', url.parse_version(self.url))
if not self.version:
tty.die("Couldn't extract version from %s. " +
"You must specify it explicitly for this URL." % self.url)
diff --git a/lib/spack/spack/parse.py b/lib/spack/spack/parse.py
index 1d10bda6af..e34f833bf6 100644
--- a/lib/spack/spack/parse.py
+++ b/lib/spack/spack/parse.py
@@ -1,5 +1,6 @@
import re
import spack.error as err
+import spack.tty as tty
import itertools
@@ -11,9 +12,7 @@ class ParseError(err.SpackError):
self.pos = pos
def print_error(self, out):
- out.write(self.message + ":\n\n")
- out.write(" " + self.string + "\n")
- out.write(" " + self.pos * " " + "^\n\n")
+ tty.error(self.message, self.string, self.pos * " " + "^")
class LexError(ParseError):
@@ -107,7 +106,7 @@ class Parser(object):
if self.next:
self.unexpected_token()
else:
- self.next_token_error("Unexpected end of file")
+ self.next_token_error("Unexpected end of input")
sys.exit(1)
def parse(self, text):
diff --git a/lib/spack/spack/relations.py b/lib/spack/spack/relations.py
index 052a509cb7..0442314e5e 100644
--- a/lib/spack/spack/relations.py
+++ b/lib/spack/spack/relations.py
@@ -44,6 +44,7 @@ provides
spack install mpileaks ^mvapich
spack install mpileaks ^mpich
"""
+import sys
from dependency import Dependency
diff --git a/lib/spack/spack/stage.py b/lib/spack/spack/stage.py
index c8ffa915bc..54d5eb7e6a 100644
--- a/lib/spack/spack/stage.py
+++ b/lib/spack/spack/stage.py
@@ -126,6 +126,9 @@ class Stage(object):
"""Returns the path to the expanded archive directory if it's expanded;
None if the archive hasn't been expanded.
"""
+ if not self.archive_file:
+ return None
+
for file in os.listdir(self.path):
archive_path = spack.new_path(self.path, file)
if os.path.isdir(archive_path):
diff --git a/lib/spack/spack/test/url_parse.py b/lib/spack/spack/test/url_parse.py
index 73fe5796ed..819e6fefd2 100644
--- a/lib/spack/spack/test/url_parse.py
+++ b/lib/spack/spack/test/url_parse.py
@@ -3,18 +3,19 @@ This file has a bunch of versions tests taken from the excellent version
detection in Homebrew.
"""
import unittest
-import spack.version as ver
+import spack.url as url
from pprint import pprint
class UrlParseTest(unittest.TestCase):
def assert_not_detected(self, string):
- self.assertRaises(ver.UndetectableVersionError, ver.parse, string)
+ self.assertRaises(
+ url.UndetectableVersionError, url.parse_name_and_version, string)
def assert_detected(self, name, v, string):
- parsed_name, parsed_v = ver.parse(string)
+ parsed_name, parsed_v = url.parse_name_and_version(string)
self.assertEqual(parsed_name, name)
- self.assertEqual(parsed_v, ver.Version(v))
+ self.assertEqual(parsed_v, url.Version(v))
def test_wwwoffle_version(self):
self.assert_detected(
diff --git a/lib/spack/spack/test/compare_versions.py b/lib/spack/spack/test/versions.py
index 5513da6915..96666b36db 100644
--- a/lib/spack/spack/test/compare_versions.py
+++ b/lib/spack/spack/test/versions.py
@@ -7,7 +7,7 @@ import unittest
from spack.version import *
-class CompareVersionsTest(unittest.TestCase):
+class VersionsTest(unittest.TestCase):
def assert_ver_lt(self, a, b):
a, b = ver(a), ver(b)
@@ -129,7 +129,8 @@ class CompareVersionsTest(unittest.TestCase):
self.assert_ver_lt('1.fc17', '1g.fc17')
- # Stuff below here is not taken from RPM's tests.
+ # Stuff below here is not taken from RPM's tests and is
+ # unique to spack
def test_version_ranges(self):
self.assert_ver_lt('1.2:1.4', '1.6')
self.assert_ver_gt('1.6', '1.2:1.4')
diff --git a/lib/spack/spack/url.py b/lib/spack/spack/url.py
new file mode 100644
index 0000000000..3a6c9a5158
--- /dev/null
+++ b/lib/spack/spack/url.py
@@ -0,0 +1,166 @@
+"""
+This module has methods for parsing names and versions of packages from URLs.
+The idea is to allow package creators to supply nothing more than the
+download location of the package, and figure out version and name information
+from there.
+
+Example: when spack is given the following URL:
+
+ ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p243.tar.gz
+
+It can figure out that the package name is ruby, and that it is at version
+1.9.1-p243. This is useful for making the creation of packages simple: a user
+just supplies a URL and skeleton code is generated automatically.
+
+Spack can also figure out that it can most likely download 1.8.1 at this URL:
+
+ ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.8.1.tar.gz
+
+This is useful if a user asks for a package at a particular version number;
+spack doesn't need anyone to tell it where to get the tarball even though
+it's never been told about that version before.
+"""
+import os
+import re
+
+import spack.error
+import spack.utils
+from spack.version import Version
+
+
+class UrlParseError(spack.error.SpackError):
+ """Raised when the URL module can't parse something correctly."""
+ def __init__(self, msg, spec):
+ super(UrlParseError, self).__init__(msg)
+ self.spec = spec
+
+
+class UndetectableVersionError(UrlParseError):
+ """Raised when we can't parse a version from a string."""
+ def __init__(self, spec):
+ super(UndetectableVersionError, self).__init__(
+ "Couldn't detect version in: " + spec, spec)
+
+
+class UndetectableNameError(UrlParseError):
+ """Raised when we can't parse a package name from a string."""
+ def __init__(self, spec):
+ super(UndetectableNameError, self).__init__(
+ "Couldn't parse package name in: " + spec)
+
+
+def parse_version_string_with_indices(spec):
+ """Try to extract a version string from a filename or URL. This is taken
+ largely from Homebrew's Version class."""
+
+ if os.path.isdir(spec):
+ stem = os.path.basename(spec)
+ elif re.search(r'((?:sourceforge.net|sf.net)/.*)/download$', spec):
+ stem = spack.utils.stem(os.path.dirname(spec))
+ else:
+ stem = spack.utils.stem(spec)
+
+ version_types = [
+ # GitHub tarballs, e.g. v1.2.3
+ (r'github.com/.+/(?:zip|tar)ball/v?((\d+\.)+\d+)$', spec),
+
+ # e.g. https://github.com/sam-github/libnet/tarball/libnet-1.1.4
+ (r'github.com/.+/(?:zip|tar)ball/.*-((\d+\.)+\d+)$', spec),
+
+ # e.g. https://github.com/isaacs/npm/tarball/v0.2.5-1
+ (r'github.com/.+/(?:zip|tar)ball/v?((\d+\.)+\d+-(\d+))$', spec),
+
+ # e.g. https://github.com/petdance/ack/tarball/1.93_02
+ (r'github.com/.+/(?:zip|tar)ball/v?((\d+\.)+\d+_(\d+))$', spec),
+
+ # e.g. https://github.com/erlang/otp/tarball/OTP_R15B01 (erlang style)
+ (r'[-_](R\d+[AB]\d*(-\d+)?)', spec),
+
+ # e.g. boost_1_39_0
+ (r'((\d+_)+\d+)$', stem),
+
+ # e.g. foobar-4.5.1-1
+ # e.g. ruby-1.9.1-p243
+ (r'-((\d+\.)*\d\.\d+-(p|rc|RC)?\d+)(?:[-._](?:bin|dist|stable|src|sources))?$', stem),
+
+ # e.g. lame-398-1
+ (r'-((\d)+-\d)', stem),
+
+ # e.g. foobar-4.5.1
+ (r'-((\d+\.)*\d+)$', stem),
+
+ # e.g. foobar-4.5.1b
+ (r'-((\d+\.)*\d+([a-z]|rc|RC)\d*)$', stem),
+
+ # e.g. foobar-4.5.0-beta1, or foobar-4.50-beta
+ (r'-((\d+\.)*\d+-beta(\d+)?)$', stem),
+
+ # e.g. foobar4.5.1
+ (r'((\d+\.)*\d+)$', stem),
+
+ # e.g. foobar-4.5.0-bin
+ (r'-((\d+\.)+\d+[a-z]?)[-._](bin|dist|stable|src|sources?)$', stem),
+
+ # e.g. dash_0.5.5.1.orig.tar.gz (Debian style)
+ (r'_((\d+\.)+\d+[a-z]?)[.]orig$', stem),
+
+ # e.g. http://www.openssl.org/source/openssl-0.9.8s.tar.gz
+ (r'-([^-]+)', stem),
+
+ # e.g. astyle_1.23_macosx.tar.gz
+ (r'_([^_]+)', stem),
+
+ # e.g. http://mirrors.jenkins-ci.org/war/1.486/jenkins.war
+ (r'\/(\d\.\d+)\/', spec),
+
+ # e.g. http://www.ijg.org/files/jpegsrc.v8d.tar.gz
+ (r'\.v(\d+[a-z]?)', stem)]
+
+ for vtype in version_types:
+ regex, match_string = vtype[:2]
+ match = re.search(regex, match_string)
+ if match and match.group(1) is not None:
+ return match.group(1), match.start(1), match.end(1)
+
+ raise UndetectableVersionError(spec)
+
+
+def parse_version(spec):
+ """Given a URL or archive name, extract a version from it and return
+ a version object.
+ """
+ ver, start, end = parse_version_string_with_indices(spec)
+ return Version(ver)
+
+
+def parse_name(spec, ver=None):
+ if ver is None:
+ ver = parse_version(spec)
+
+ ntypes = (r'/sourceforge/([^/]+)/',
+ r'/([^/]+)/(tarball|zipball)/',
+ r'/([^/]+)[_.-](bin|dist|stable|src|sources)[_.-]%s' % ver,
+ r'/([^/]+)[_.-]v?%s' % ver,
+ r'/([^/]+)%s' % ver,
+ r'^([^/]+)[_.-]v?%s' % ver,
+ r'^([^/]+)%s' % ver)
+
+ for nt in ntypes:
+ match = re.search(nt, spec)
+ if match:
+ return match.group(1)
+ raise UndetectableNameError(spec)
+
+
+def parse_name_and_version(spec):
+ ver = parse_version(spec)
+ name = parse_name(spec, ver)
+ return (name, ver)
+
+
+def create_version_format(spec):
+ """Given a URL or archive name, find the version and create a format string
+ that will allow another version to be substituted.
+ """
+ ver, start, end = parse_version_string_with_indices(spec)
+ return spec[:start] + '%s' + spec[end:]
diff --git a/lib/spack/spack/version.py b/lib/spack/spack/version.py
index 9a0390451c..1dfd3718b5 100644
--- a/lib/spack/spack/version.py
+++ b/lib/spack/spack/version.py
@@ -3,7 +3,10 @@ import re
from functools import total_ordering
import utils
-import spack.error as serr
+import spack.error
+
+# Valid version characters
+VALID_VERSION = r'[A-Za-z0-9_.-]'
def int_if_int(string):
@@ -26,28 +29,37 @@ def ver(string):
@total_ordering
class Version(object):
"""Class to represent versions"""
- def __init__(self, version_string):
+ def __init__(self, string):
+ if not re.match(VALID_VERSION, string):
+ raise ValueError("Bad characters in version string: %s" % string)
+
# preserve the original string
- self.version_string = version_string
+ self.string = string
# Split version into alphabetical and numeric segments
- segments = re.findall(r'[a-zA-Z]+|[0-9]+', version_string)
+ segment_regex = r'[a-zA-Z]+|[0-9]+'
+ segments = re.findall(segment_regex, string)
self.version = tuple(int_if_int(seg) for seg in segments)
+
def up_to(self, index):
"""Return a version string up to the specified component, exclusive.
e.g., if this is 10.8.2, self.up_to(2) will return '10.8'.
"""
return '.'.join(str(x) for x in self[:index])
+ def __iter__(self):
+ for v in self.version:
+ yield v
+
def __getitem__(self, idx):
return tuple(self.version[idx])
def __repr__(self):
- return self.version_string
+ return self.string
def __str__(self):
- return self.version_string
+ return self.string
def __lt__(self, other):
"""Version comparison is designed for consistency with the way RPM
@@ -145,144 +157,3 @@ class VersionRange(object):
if self.end:
out += str(self.end)
return out
-
-
-class VersionParseError(serr.SpackError):
- """Raised when the version module can't parse something."""
- def __init__(self, msg, spec):
- super(VersionParseError, self).__init__(msg)
- self.spec = spec
-
-
-class UndetectableVersionError(VersionParseError):
- """Raised when we can't parse a version from a string."""
- def __init__(self, spec):
- super(UndetectableVersionError, self).__init__(
- "Couldn't detect version in: " + spec, spec)
-
-
-class UndetectableNameError(VersionParseError):
- """Raised when we can't parse a package name from a string."""
- def __init__(self, spec):
- super(UndetectableNameError, self).__init__(
- "Couldn't parse package name in: " + spec)
-
-
-def parse_version_string_with_indices(spec):
- """Try to extract a version string from a filename or URL. This is taken
- largely from Homebrew's Version class."""
-
- if os.path.isdir(spec):
- stem = os.path.basename(spec)
- elif re.search(r'((?:sourceforge.net|sf.net)/.*)/download$', spec):
- stem = utils.stem(os.path.dirname(spec))
- else:
- stem = utils.stem(spec)
-
- version_types = [
- # GitHub tarballs, e.g. v1.2.3
- (r'github.com/.+/(?:zip|tar)ball/v?((\d+\.)+\d+)$', spec),
-
- # e.g. https://github.com/sam-github/libnet/tarball/libnet-1.1.4
- (r'github.com/.+/(?:zip|tar)ball/.*-((\d+\.)+\d+)$', spec),
-
- # e.g. https://github.com/isaacs/npm/tarball/v0.2.5-1
- (r'github.com/.+/(?:zip|tar)ball/v?((\d+\.)+\d+-(\d+))$', spec),
-
- # e.g. https://github.com/petdance/ack/tarball/1.93_02
- (r'github.com/.+/(?:zip|tar)ball/v?((\d+\.)+\d+_(\d+))$', spec),
-
- # e.g. https://github.com/erlang/otp/tarball/OTP_R15B01 (erlang style)
- (r'[-_](R\d+[AB]\d*(-\d+)?)', spec),
-
- # e.g. boost_1_39_0
- (r'((\d+_)+\d+)$', stem),
-
- # e.g. foobar-4.5.1-1
- # e.g. ruby-1.9.1-p243
- (r'-((\d+\.)*\d\.\d+-(p|rc|RC)?\d+)(?:[-._](?:bin|dist|stable|src|sources))?$', stem),
-
- # e.g. lame-398-1
- (r'-((\d)+-\d)', stem),
-
- # e.g. foobar-4.5.1
- (r'-((\d+\.)*\d+)$', stem),
-
- # e.g. foobar-4.5.1b
- (r'-((\d+\.)*\d+([a-z]|rc|RC)\d*)$', stem),
-
- # e.g. foobar-4.5.0-beta1, or foobar-4.50-beta
- (r'-((\d+\.)*\d+-beta(\d+)?)$', stem),
-
- # e.g. foobar4.5.1
- (r'((\d+\.)*\d+)$', stem),
-
- # e.g. foobar-4.5.0-bin
- (r'-((\d+\.)+\d+[a-z]?)[-._](bin|dist|stable|src|sources?)$', stem),
-
- # e.g. dash_0.5.5.1.orig.tar.gz (Debian style)
- (r'_((\d+\.)+\d+[a-z]?)[.]orig$', stem),
-
- # e.g. http://www.openssl.org/source/openssl-0.9.8s.tar.gz
- (r'-([^-]+)', stem),
-
- # e.g. astyle_1.23_macosx.tar.gz
- (r'_([^_]+)', stem),
-
- # e.g. http://mirrors.jenkins-ci.org/war/1.486/jenkins.war
- (r'\/(\d\.\d+)\/', spec),
-
- # e.g. http://www.ijg.org/files/jpegsrc.v8d.tar.gz
- (r'\.v(\d+[a-z]?)', stem)]
-
- for vtype in version_types:
- regex, match_string = vtype[:2]
- match = re.search(regex, match_string)
- if match and match.group(1) is not None:
- return match.group(1), match.start(1), match.end(1)
-
- raise UndetectableVersionError(spec)
-
-
-def parse_version(spec):
- """Given a URL or archive name, extract a version from it and return
- a version object.
- """
- ver, start, end = parse_version_string_with_indices(spec)
- return Version(ver)
-
-
-def create_version_format(spec):
- """Given a URL or archive name, find the version and create a format string
- that will allow another version to be substituted.
- """
- ver, start, end = parse_version_string_with_indices(spec)
- return spec[:start] + '%s' + spec[end:]
-
-
-def replace_version(spec, new_version):
- version = create_version_format(spec)
- # TODO: finish this function.
-
-def parse_name(spec, ver=None):
- if ver is None:
- ver = parse_version(spec)
-
- ntypes = (r'/sourceforge/([^/]+)/',
- r'/([^/]+)/(tarball|zipball)/',
- r'/([^/]+)[_.-](bin|dist|stable|src|sources)[_.-]%s' % ver,
- r'/([^/]+)[_.-]v?%s' % ver,
- r'/([^/]+)%s' % ver,
- r'^([^/]+)[_.-]v?%s' % ver,
- r'^([^/]+)%s' % ver)
-
- for nt in ntypes:
- match = re.search(nt, spec)
- if match:
- return match.group(1)
- raise UndetectableNameError(spec)
-
-def parse(spec):
- ver = parse_version(spec)
- name = parse_name(spec, ver)
- return (name, ver)