Ability to list versions from web page with spack list -v PACKAGE
Experimental feature automatically parses versions out of web pages and prints what it thinks are avaialble versions of a package, e.g.: $ spack list -v libunwind 1.1 1.0 0.98.6 0.98.4 0.98.2 0.98 0.96 0.93 0.91 0.1 1.0.1 0.99 0.98.5 0.98.3 0.98.1 0.97 0.95 0.92 0.9 0.0
This commit is contained in:
parent
6e557798e8
commit
57ef3b8a80
@ -1,10 +1,19 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
import spack.packages as packages
|
import spack.packages as packages
|
||||||
|
from spack.version import ver
|
||||||
from spack.colify import colify
|
from spack.colify import colify
|
||||||
|
import spack.url as url
|
||||||
|
import spack.tty as tty
|
||||||
|
|
||||||
|
|
||||||
description ="List spack packages"
|
description ="List spack packages"
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
subparser.add_argument('-v', '--versions', metavar="PACKAGE", dest='version_package',
|
||||||
|
help='List available versions of a package (experimental).')
|
||||||
subparser.add_argument('-i', '--installed', action='store_true', dest='installed',
|
subparser.add_argument('-i', '--installed', action='store_true', dest='installed',
|
||||||
help='List installed packages for each platform along with versions.')
|
help='List installed packages for each platform along with versions.')
|
||||||
|
|
||||||
@ -16,8 +25,33 @@ def list(parser, args):
|
|||||||
print "%s:" % sys_type
|
print "%s:" % sys_type
|
||||||
package_vers = []
|
package_vers = []
|
||||||
for pkg in pkgs[sys_type]:
|
for pkg in pkgs[sys_type]:
|
||||||
pv = [pkg.name + "/" + v for v in pkg.installed_versions]
|
pv = [pkg.name + "@" + v for v in pkg.installed_versions]
|
||||||
package_vers.extend(pv)
|
package_vers.extend(pv)
|
||||||
colify(sorted(package_vers), indent=4)
|
colify(sorted(package_vers), indent=4)
|
||||||
|
|
||||||
|
elif args.version_package:
|
||||||
|
pkg = packages.get(args.version_package)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Run curl but grab the mime type from the http headers
|
||||||
|
listing = spack.curl('-s', '-L', pkg.list_url, return_output=True)
|
||||||
|
url_regex = os.path.basename(url.wildcard_version(pkg.url))
|
||||||
|
strings = re.findall(url_regex, listing)
|
||||||
|
|
||||||
|
versions = []
|
||||||
|
wildcard = pkg.version.wildcard()
|
||||||
|
for s in strings:
|
||||||
|
match = re.search(wildcard, s)
|
||||||
|
if match:
|
||||||
|
versions.append(ver(match.group(0)))
|
||||||
|
|
||||||
|
colify(str(v) for v in reversed(sorted(set(versions))))
|
||||||
|
|
||||||
|
except:
|
||||||
|
tty.die("Listing versions for %s failed" % pkg.name,
|
||||||
|
"Listing versions is experimental. You may need to add the list_url",
|
||||||
|
"attribute to the package to tell Spack where to look for versions.")
|
||||||
|
raise
|
||||||
|
|
||||||
else:
|
else:
|
||||||
colify(packages.all_package_names())
|
colify(packages.all_package_names())
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
#
|
|
||||||
# colify
|
# colify
|
||||||
# By Todd Gamblin, tgamblin@llnl.gov
|
# By Todd Gamblin, tgamblin@llnl.gov
|
||||||
#
|
#
|
||||||
@ -100,6 +98,11 @@ def colify(elts, **options):
|
|||||||
if not type(elts) == list:
|
if not type(elts) == list:
|
||||||
elts = list(elts)
|
elts = list(elts)
|
||||||
|
|
||||||
|
if not output.isatty():
|
||||||
|
for elt in elts:
|
||||||
|
output.write("%s\n" % elt)
|
||||||
|
return
|
||||||
|
|
||||||
console_cols = options.get("cols", None)
|
console_cols = options.get("cols", None)
|
||||||
if not console_cols:
|
if not console_cols:
|
||||||
console_cols, console_rows = get_terminal_size()
|
console_cols, console_rows = get_terminal_size()
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
import url
|
import url
|
||||||
import arch
|
import arch
|
||||||
|
|
||||||
|
from spec import Compiler
|
||||||
|
from version import Version
|
||||||
from multi_function import platform
|
from multi_function import platform
|
||||||
from stage import Stage
|
from stage import Stage
|
||||||
from dependency import *
|
from dependency import *
|
||||||
@ -241,6 +243,11 @@ class SomePackage(Package):
|
|||||||
"""Controls whether install and uninstall check deps before running."""
|
"""Controls whether install and uninstall check deps before running."""
|
||||||
ignore_dependencies = False
|
ignore_dependencies = False
|
||||||
|
|
||||||
|
# TODO: multi-compiler support
|
||||||
|
"""Default compiler for this package"""
|
||||||
|
compiler = Compiler('gcc')
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, sys_type = arch.sys_type()):
|
def __init__(self, sys_type = arch.sys_type()):
|
||||||
# Check for attributes that derived classes must set.
|
# Check for attributes that derived classes must set.
|
||||||
attr.required(self, 'homepage')
|
attr.required(self, 'homepage')
|
||||||
@ -261,10 +268,14 @@ def __init__(self, sys_type = arch.sys_type()):
|
|||||||
validate.url(self.url)
|
validate.url(self.url)
|
||||||
|
|
||||||
# Set up version
|
# Set up version
|
||||||
attr.setdefault(self, 'version', url.parse_version(self.url))
|
if not hasattr(self, 'version'):
|
||||||
if not self.version:
|
try:
|
||||||
tty.die("Couldn't extract version from %s. " +
|
self.version = url.parse_version(self.url)
|
||||||
"You must specify it explicitly for this URL." % self.url)
|
except UndetectableVersionError:
|
||||||
|
tty.die("Couldn't extract a default version from %s. You " +
|
||||||
|
"must specify it explicitly in the package." % self.url)
|
||||||
|
elif type(self.version) == string:
|
||||||
|
self.version = Version(self.version)
|
||||||
|
|
||||||
# This adds a bunch of convenience commands to the package's module scope.
|
# This adds a bunch of convenience commands to the package's module scope.
|
||||||
self.add_commands_to_module()
|
self.add_commands_to_module()
|
||||||
@ -275,6 +286,10 @@ def __init__(self, sys_type = arch.sys_type()):
|
|||||||
# stage used to build this package.
|
# stage used to build this package.
|
||||||
self.stage = Stage(self.stage_name, self.url)
|
self.stage = Stage(self.stage_name, self.url)
|
||||||
|
|
||||||
|
# Set a default list URL (place to find lots of versions)
|
||||||
|
if not hasattr(self, 'list_url'):
|
||||||
|
self.list_url = os.path.dirname(self.url)
|
||||||
|
|
||||||
|
|
||||||
def add_commands_to_module(self):
|
def add_commands_to_module(self):
|
||||||
"""Populate the module scope of install() with some useful functions.
|
"""Populate the module scope of install() with some useful functions.
|
||||||
@ -395,6 +410,16 @@ def prefix(self):
|
|||||||
return new_path(self.package_path, self.version)
|
return new_path(self.package_path, self.version)
|
||||||
|
|
||||||
|
|
||||||
|
def url_version(self, version):
|
||||||
|
"""Given a version, this returns a string that should be substituted into the
|
||||||
|
package's URL to download that version.
|
||||||
|
By default, this just returns the version string. Subclasses may need to
|
||||||
|
override this, e.g. for boost versions where you need to ensure that there
|
||||||
|
are _'s in the download URL.
|
||||||
|
"""
|
||||||
|
return version.string
|
||||||
|
|
||||||
|
|
||||||
def remove_prefix(self):
|
def remove_prefix(self):
|
||||||
"""Removes the prefix for a package along with any empty parent directories."""
|
"""Removes the prefix for a package along with any empty parent directories."""
|
||||||
if self.dirty:
|
if self.dirty:
|
||||||
|
@ -6,11 +6,10 @@
|
|||||||
import glob
|
import glob
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
|
import spack.error
|
||||||
from spack.utils import *
|
from spack.utils import *
|
||||||
import spack.arch as arch
|
import spack.arch as arch
|
||||||
import spack.version as version
|
|
||||||
import spack.attr as attr
|
|
||||||
import spack.error as serr
|
|
||||||
|
|
||||||
# Valid package names -- can contain - but can't start with it.
|
# Valid package names -- can contain - but can't start with it.
|
||||||
valid_package = r'^\w[\w-]*$'
|
valid_package = r'^\w[\w-]*$'
|
||||||
@ -20,7 +19,16 @@
|
|||||||
|
|
||||||
instances = {}
|
instances = {}
|
||||||
|
|
||||||
class InvalidPackageNameError(serr.SpackError):
|
|
||||||
|
def get(pkg, arch=arch.sys_type()):
|
||||||
|
key = (pkg, arch)
|
||||||
|
if not key in instances:
|
||||||
|
package_class = get_class(pkg)
|
||||||
|
instances[key] = package_class(arch)
|
||||||
|
return instances[key]
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidPackageNameError(spack.error.SpackError):
|
||||||
"""Raised when we encounter a bad package name."""
|
"""Raised when we encounter a bad package name."""
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
super(InvalidPackageNameError, self).__init__(
|
super(InvalidPackageNameError, self).__init__(
|
||||||
@ -34,7 +42,7 @@ def valid_name(pkg):
|
|||||||
|
|
||||||
def validate_name(pkg):
|
def validate_name(pkg):
|
||||||
if not valid_name(pkg):
|
if not valid_name(pkg):
|
||||||
raise spack.InvalidPackageNameError(pkg)
|
raise InvalidPackageNameError(pkg)
|
||||||
|
|
||||||
|
|
||||||
def filename_for(pkg):
|
def filename_for(pkg):
|
||||||
@ -45,8 +53,6 @@ def filename_for(pkg):
|
|||||||
|
|
||||||
def installed_packages(**kwargs):
|
def installed_packages(**kwargs):
|
||||||
"""Returns a dict from systype strings to lists of Package objects."""
|
"""Returns a dict from systype strings to lists of Package objects."""
|
||||||
list_installed = kwargs.get('installed', False)
|
|
||||||
|
|
||||||
pkgs = {}
|
pkgs = {}
|
||||||
if not os.path.isdir(spack.install_path):
|
if not os.path.isdir(spack.install_path):
|
||||||
return pkgs
|
return pkgs
|
||||||
@ -108,14 +114,6 @@ def get_class(pkg):
|
|||||||
return klass
|
return klass
|
||||||
|
|
||||||
|
|
||||||
def get(pkg, arch=arch.sys_type()):
|
|
||||||
key = (pkg, arch)
|
|
||||||
if not key in instances:
|
|
||||||
package_class = get_class(pkg)
|
|
||||||
instances[key] = package_class(arch)
|
|
||||||
return instances[key]
|
|
||||||
|
|
||||||
|
|
||||||
def compute_dependents():
|
def compute_dependents():
|
||||||
"""Reads in all package files and sets dependence information on
|
"""Reads in all package files and sets dependence information on
|
||||||
Package objects in memory.
|
Package objects in memory.
|
||||||
|
@ -9,6 +9,8 @@ class Libdwarf(Package):
|
|||||||
url = "http://reality.sgiweb.org/davea/libdwarf-20130207.tar.gz"
|
url = "http://reality.sgiweb.org/davea/libdwarf-20130207.tar.gz"
|
||||||
md5 = "64b42692e947d5180e162e46c689dfbf"
|
md5 = "64b42692e947d5180e162e46c689dfbf"
|
||||||
|
|
||||||
|
list_url = "http://reality.sgiweb.org/davea/dwarf.html"
|
||||||
|
|
||||||
depends_on("libelf")
|
depends_on("libelf")
|
||||||
|
|
||||||
|
|
||||||
|
@ -163,9 +163,19 @@ def parse_name_and_version(path):
|
|||||||
return (name, ver)
|
return (name, ver)
|
||||||
|
|
||||||
|
|
||||||
def version_format(path):
|
def substitute_version(path, new_version):
|
||||||
"""Given a URL or archive name, find the version and create a format string
|
"""Given a URL or archive name, find the version in the path and substitute
|
||||||
that will allow another version to be substituted.
|
the new version for it.
|
||||||
"""
|
"""
|
||||||
ver, start, end = parse_version_string_with_indices(path)
|
ver, start, end = parse_version_string_with_indices(path)
|
||||||
return path[:start] + '%s' + path[end:]
|
return path[:start] + new_version + path[end:]
|
||||||
|
|
||||||
|
|
||||||
|
def wildcard_version(path):
|
||||||
|
"""Find the version in the supplied path, and return a regular expression
|
||||||
|
that will match this path with any version in its place.
|
||||||
|
"""
|
||||||
|
ver, start, end = parse_version_string_with_indices(path)
|
||||||
|
v = Version(ver)
|
||||||
|
|
||||||
|
return re.escape(path[:start]) + v.wildcard() + re.escape(path[end:])
|
||||||
|
@ -41,6 +41,10 @@ def __init__(self, string):
|
|||||||
segments = re.findall(segment_regex, string)
|
segments = re.findall(segment_regex, string)
|
||||||
self.version = tuple(int_if_int(seg) for seg in segments)
|
self.version = tuple(int_if_int(seg) for seg in segments)
|
||||||
|
|
||||||
|
# Store the separators from the original version string as well.
|
||||||
|
# last element of separators is ''
|
||||||
|
self.separators = tuple(re.split(segment_regex, string)[1:-1])
|
||||||
|
|
||||||
|
|
||||||
def up_to(self, index):
|
def up_to(self, index):
|
||||||
"""Return a version string up to the specified component, exclusive.
|
"""Return a version string up to the specified component, exclusive.
|
||||||
@ -48,6 +52,29 @@ def up_to(self, index):
|
|||||||
"""
|
"""
|
||||||
return '.'.join(str(x) for x in self[:index])
|
return '.'.join(str(x) for x in self[:index])
|
||||||
|
|
||||||
|
def wildcard(self):
|
||||||
|
"""Create a regex that will match variants of this version string."""
|
||||||
|
def a_or_n(seg):
|
||||||
|
if type(seg) == int:
|
||||||
|
return r'[0-9]+'
|
||||||
|
else:
|
||||||
|
return r'[a-zA-Z]+'
|
||||||
|
|
||||||
|
version = self.version
|
||||||
|
separators = ('',) + self.separators
|
||||||
|
|
||||||
|
version += (version[-1],) * 2
|
||||||
|
separators += (separators[-1],) * 2
|
||||||
|
|
||||||
|
sep_res = [re.escape(sep) for sep in separators]
|
||||||
|
seg_res = [a_or_n(seg) for seg in version]
|
||||||
|
|
||||||
|
wc = seg_res[0]
|
||||||
|
for i in xrange(1, len(sep_res)):
|
||||||
|
wc += '(?:' + sep_res[i] + seg_res[i]
|
||||||
|
wc += ')?' * (len(seg_res) - 1)
|
||||||
|
return wc
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for v in self.version:
|
for v in self.version:
|
||||||
yield v
|
yield v
|
||||||
@ -96,13 +123,16 @@ def __lt__(self, other):
|
|||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""Implemented to match __lt__. See __lt__."""
|
"""Implemented to match __lt__. See __lt__."""
|
||||||
if type(other) == VersionRange:
|
if type(other) != Version:
|
||||||
return False
|
return False
|
||||||
return self.version == other.version
|
return self.version == other.version
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not (self == other)
|
return not (self == other)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.version)
|
||||||
|
|
||||||
|
|
||||||
@total_ordering
|
@total_ordering
|
||||||
class VersionRange(object):
|
class VersionRange(object):
|
||||||
|
Loading…
Reference in New Issue
Block a user