]> git.proxmox.com Git - cargo.git/commitdiff
update docs and drop old scripts
authorXimin Luo <infinity0@debian.org>
Sat, 23 Oct 2021 23:15:33 +0000 (00:15 +0100)
committerXimin Luo <infinity0@debian.org>
Sat, 23 Oct 2021 23:15:33 +0000 (00:15 +0100)
debian/README.source
debian/bootstrap.py [deleted file]
debian/make_orig_multi.sh

index db09af40623e67928bfc05fbfd96e6a408f27886..5bf7949e2180018caa0819c8b00b2402fde66864 100644 (file)
@@ -16,7 +16,8 @@ Updating the package
 3. $ git fetch upstream
    You might have to first run:
    $ git remote add upstream https://github.com/rust-lang/cargo
-4. $ gbp import-orig ../cargo_<version>.orig.tar.gz
+4. $ git checkout debian/experimental
+   $ gbp import-orig ../cargo_<version>.orig.tar.gz
    If you get errors, check the extra default flags in d/gbp.conf
 5. Check that no old versions remain in vendor/. If there are, then your git
    repo was messed up when you ran (4). Rewind the debian/sid, upstream, and
@@ -27,35 +28,17 @@ Updating the package
 General info
 ============
 
-Current packaging of cargo is sub-optimal due to the fact that
-both the language (Rust) and its package manager (Cargo)
-are involved into self-dependency loops to bootstrap.
+Cargo depends on a few hundred other crates (which themselves need cargo to
+build). To avoid an insane bootstrapping loop, this package therefore embeds
+all dependency crates. This is made convenient by the "vendor" subcommand.
 
-Moreover, the current approach to modules and registry by cargo is
-biased towards a developer-friendly always-online use.
-
-This package currently resort to several workarounds to build cargo:
- 1. we use a custom script (debian/bootstrap.py) to build a local
-    stage0, instead of downloading/embedding a snapshotted binary.
- 2. we embed all dependencies crates, because cargo needs external
-    modules (which need cargo themself to build).
- 4. we generate a .cargo/config at build-time, to override paths and
-    registry.
- 5. we create a temporary git repository at build-time for the
-    registry, as this is needed by cargo.
+(The alternative method, that expresses the true upstream dependencies more
+faithfully, can be found in the "rust-cargo" crate, which is packaged just like
+any other Debian Rust package. That is used as a library by the Debian packages
+of various Rust ecosystem tools that build on top of cargo, such as our very
+own debcargo. By contrast, this package is used as the actual executable
+package manager, with a smaller bootstrapping requirement.)
 
 As such, the original source is composed by two tarballs:
  * cargo source
- * dependencies crates (under vendor/), stripped of unused embedded
-   C libraries
-
-Uploading Package
-=================
-
-Please use dgit push-source command to upload new version of the package to
-archive. Command by itself creates a source only upload uploads the package to
-ftpmaster and creates version history on dgit.
-
-    dgit push-source --gbp
-
- -- Vasudev Kamath <vasudev@copyninja.info>, Sat,  9 Jun 2018 14:41:17 +0530
+ * dependencies crates (under vendor/), stripped of unused embedded C libraries
diff --git a/debian/bootstrap.py b/debian/bootstrap.py
deleted file mode 100755 (executable)
index fafd958..0000000
+++ /dev/null
@@ -1,1465 +0,0 @@
-#!/usr/bin/env python
-"""
-NOTE: This script has not been used for a very long time and very likely won't
-work. Please read the code before attempting to run it and hoping that "just
-fixing the errors" will work. -- infinity0
-
-About
-=====
-
-This python script is design to do the bare minimum to compile and link the
-Cargo binary for the purposes of bootstrapping itself on a new platform for
-which cross-compiling isn't possible.  I wrote this specifically to bootstrap
-Cargo on [Bitrig](https://bitrig.org).  Bitrig is a fork of OpenBSD that uses
-clang/clang++ and other BSD licensed tools instead of GNU licensed software.
-Cross compiling from another platform is extremely difficult because of the
-alternative toolchain Bitrig uses.
-
-With this script, all that should be necessary to run this is a working Rust
-toolchain, Python, and Git.
-
-This script will not set up a full cargo cache or anything.  It works by
-cloning the cargo index and then starting with the cargo dependencies, it
-recursively builds the dependency tree.  Once it has the dependency tree, it
-starts with the leaves of the tree, doing a breadth first traversal and for
-each dependency, it clones the repo, sets the repo's head to the correct
-revision and then executes the build command specified in the cargo config.
-
-This bootstrap script uses a temporary directory to store the built dependency
-libraries and uses that as a link path when linking dependencies and the
-cargo binary.  The goal is to create a statically linked cargo binary that is
-capable of being used as a "local cargo" when running the main cargo Makefiles.
-
-Dependencies
-============
-
-* pytoml -- used for parsing toml files.
-  https://github.com/avakar/pytoml
-
-* dulwich -- used for working with git repos.
-  https://git.samba.org/?p=jelmer/dulwich.git;a=summary
-
-Both can be installed via the pip tool:
-
-```sh
-sudo pip install pytoml dulwich
-```
-
-Command Line Options
-====================
-
-```
---cargo-root <path>    specify the path to the cargo repo root.
---target-dir <path>    specify the location to store build results.
---crate-index <path>   path to where crates.io index shoudl be cloned
---no-clone             don't clone crates.io index, --crate-index must point to existing clone.
---no-clean             don't remove the folders created during bootstrapping.
---download             only download the crates needed to bootstrap cargo.
---graph                output dot format graph of dependencies.
---target <triple>      build target: e.g. x86_64-unknown-bitrig
---host <triple>        host machine: e.g. x86_64-unknown-linux-gnu
---urls-file <file>     file to write crate URLs to
---blacklist <crates>   list of blacklisted crates to skip
---include-optional <crates> list of optional crates to include
---patchdir <dir>       directory containing patches to apply to crates after fetching them
---save-crate           if set, save .crate file when downloading
-```
-
-The `--cargo-root` option defaults to the current directory if unspecified.  The
-target directory defaults to Python equivilent of `mktemp -d` if unspecified.
-The `--crate-index` option specifies where the crates.io index will be cloned.  Or,
-if you already have a clone of the index, the crates index should point there
-and you should also specify `--no-clone`.  The `--target` option is used to
-specify which platform you are bootstrapping for.  The `--host` option defaults
-to the value of the `--target` option when not specified.
-
-Examples
-========
-
-To bootstrap Cargo on (Bitrig)[https://bitrig.org] I followed these steps:
-
-* Cloned this [bootstrap script repo](https://github.com/dhuseby/cargo-bootstra)
-to `/tmp/bootstrap`.
-* Cloned the [crates.io index](https://github.com/rust-lang/crates.io-index)
-to `/tmp/index`.
-* Created a target folder, `/tmp/out`, for the output.
-* Cloned the (Cargo)[https://github.com/rust-lang/cargo] repo to `/tmp/cargo`.
-* Copied the bootstrap.py script to the cargo repo root.
-* Ran the bootstrap.py script like so:
-```sh
-./bootstrap.py --crate-index /tmp/index --target-dir /tmp/out --no-clone --no-clean --target x86_64-unknown-bitrig
-```
-
-After the script completed, there is a Cargo executable named `cargo-0_2_0` in
-`/tmp/out`.  That executable can then be used to bootstrap Cargo from source by
-specifying it as the `--local-cargo` option to Cargo's `./configure` script.
-"""
-
-import argparse
-import cStringIO
-import hashlib
-import inspect
-import json
-import os
-import re
-import shutil
-import subprocess
-import sys
-import tarfile
-import tempfile
-import urlparse
-import socket
-# In Debian crates are already downloaded when we bootstrap cargo.
-# import requests
-import pytoml as toml
-import dulwich.porcelain as git
-from glob import glob
-
-
-TARGET = None
-HOST = None
-GRAPH = None
-URLS_FILE = None
-CRATE_CACHE = None
-CRATES_INDEX = 'git://github.com/rust-lang/crates.io-index.git'
-CARGO_REPO = 'git://github.com/rust-lang/cargo.git'
-CRATE_API_DL = 'https://crates.io/api/v1/crates/%s/%s/download'
-SV_RANGE = re.compile(r'^(?P<op>(?:\<=|\>=|=|\<|\>|\^|\~))?\s*'
-                      r'(?P<major>(?:\*|0|[1-9][0-9]*))'
-                      r'(\.(?P<minor>(?:\*|0|[1-9][0-9]*)))?'
-                      r'(\.(?P<patch>(?:\*|0|[1-9][0-9]*)))?'
-                      r'(\-(?P<prerelease>[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?'
-                      r'(\+(?P<build>[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$')
-SEMVER = re.compile(r'^\s*(?P<major>(?:0|[1-9][0-9]*))'
-                    r'(\.(?P<minor>(?:0|[1-9][0-9]*)))?'
-                    r'(\.(?P<patch>(?:0|[1-9][0-9]*)))?'
-                    r'(\-(?P<prerelease>[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?'
-                    r'(\+(?P<build>[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$')
-BSCRIPT = re.compile(r'^cargo:(?P<key>([^\s=]+))(=(?P<value>.+))?$')
-BNAME = re.compile('^(lib)?(?P<name>([^_]+))(_.*)?$')
-BUILT = {}
-CRATES = {}
-CVER = re.compile("-([^-]+)$")
-UNRESOLVED = []
-PFX = []
-BLACKLIST = []
-INCLUDE_OPTIONAL = []
-
-def dbgCtx(f):
-    def do_dbg(self, *cargs):
-        PFX.append(self.name())
-        ret = f(self, *cargs)
-        PFX.pop()
-        return ret
-    return do_dbg
-
-def dbg(s):
-    print '%s: %s' % (':'.join(PFX), s)
-
-
-class PreRelease(object):
-
-    def __init__(self, pr):
-        self._container = []
-        if pr is not None:
-            self._container += str(pr).split('.')
-
-    def __str__(self):
-        return '.'.join(self._container)
-
-    def __repr__(self):
-        return self._container
-
-    def __getitem__(self, key):
-        return self._container[key]
-
-    def __len__(self):
-        return len(self._container)
-
-    def __gt__(self, rhs):
-        return not ((self < rhs) or (self == rhs))
-
-    def __ge__(self, rhs):
-        return not (self < rhs)
-
-    def __le__(self, rhs):
-        return not (self > rhs)
-
-    def __eq__(self, rhs):
-        return self._container == rhs._container
-
-    def __ne__(self, rhs):
-        return not (self == rhs)
-
-    def __lt__(self, rhs):
-        if self == rhs:
-            return False
-
-        # not having a pre-release is higher precedence
-        if len(self) == 0:
-            if len(rhs) == 0:
-                return False
-            else:
-                # 1.0.0 > 1.0.0-alpha
-                return False
-        else:
-            if len(rhs) is None:
-                # 1.0.0-alpha < 1.0.0
-                return True
-
-        # if both have one, then longer pre-releases are higher precedence
-        if len(self) > len(rhs):
-            # 1.0.0-alpha.1 > 1.0.0-alpha
-            return False
-        elif len(self) < len(rhs):
-            # 1.0.0-alpha < 1.0.0-alpha.1
-            return True
-
-        # if both have the same length pre-release, must check each piece
-        # numeric sub-parts have lower precedence than non-numeric sub-parts
-        # non-numeric sub-parts are compared lexically in ASCII sort order
-        for l,r in zip(self, rhs):
-            if l.isdigit():
-                if r.isdigit():
-                    if int(l) < int(r):
-                        # 2 > 1
-                        return True
-                    elif int(l) > int(r):
-                        # 1 < 2
-                        return False
-                    else:
-                        # 1 == 1
-                        continue
-                else:
-                    # 1 < 'foo'
-                    return True
-            else:
-                if r.isdigit():
-                    # 'foo' > 1
-                    return False
-
-            # both are non-numeric
-            if l < r:
-                return True
-            elif l > r:
-                return False
-
-        raise RuntimeError('PreRelease __lt__ failed')
-
-
-class Semver(dict):
-
-    def __init__(self, sv):
-        match = SEMVER.match(str(sv))
-        if match is None:
-            raise ValueError('%s is not a valid semver string' % sv)
-
-        self._input = sv
-        self.update(match.groupdict())
-        self.prerelease = PreRelease(self['prerelease'])
-
-    def __str__(self):
-        major, minor, patch, prerelease, build = self.parts_raw()
-        s = ''
-        if major is None:
-            s += '0'
-        else:
-            s += major
-        s += '.'
-        if minor is None:
-            s += '0'
-        else:
-            s += minor
-        s += '.'
-        if patch is None:
-            s += '0'
-        else:
-            s += patch
-        if len(self.prerelease):
-            s += '-' + str(self.prerelease)
-        if build is not None:
-            s += '+' + build
-        return s
-
-    def __hash__(self):
-        return hash(str(self))
-
-    def as_range(self):
-        return SemverRange('=%s' % self)
-
-    def parts(self):
-        major, minor, patch, prerelease, build = self.parts_raw()
-        if major is None:
-            major = '0'
-        if minor is None:
-            minor = '0'
-        if patch is None:
-            patch = '0'
-        return (int(major),int(minor),int(patch),prerelease,build)
-
-    def parts_raw(self):
-        return (self['major'],self['minor'],self['patch'],self['prerelease'],self['build'])
-
-    def __lt__(self, rhs):
-        lmaj,lmin,lpat,lpre,_ = self.parts()
-        rmaj,rmin,rpat,rpre,_ = rhs.parts()
-        if lmaj < rmaj:
-            return True
-        if lmaj > rmaj:
-            return False
-        if lmin < rmin:
-            return True
-        if lmin > rmin:
-            return False
-        if lpat < rpat:
-            return True
-        if lpat > rpat:
-            return False
-        if lpre is not None and rpre is None:
-            return True
-        if lpre is not None and rpre is not None:
-            if self.prerelease < rhs.prerelease:
-                return True
-        return False
-
-    def __le__(self, rhs):
-        return not (self > rhs)
-
-    def __gt__(self, rhs):
-        return not ((self < rhs) or (self == rhs))
-
-    def __ge__(self, rhs):
-        return not (self < rhs)
-
-    def __eq__(self, rhs):
-        # build metadata is only considered for equality
-        lmaj,lmin,lpat,lpre,lbld = self.parts()
-        rmaj,rmin,rpat,rpre,rbld = rhs.parts()
-        return lmaj == rmaj and \
-               lmin == rmin and \
-               lpat == rpat and \
-               lpre == rpre and \
-               lbld == rbld
-
-    def __ne__(self, rhs):
-        return not (self == rhs)
-
-
-class SemverRange(object):
-
-    def __init__(self, sv):
-        self._input = sv
-        self._lower = None
-        self._upper = None
-        self._op = None
-        self._semver = None
-
-        sv = str(sv)
-        svs = [x.strip() for x in sv.split(',')]
-
-        if len(svs) > 1:
-            self._op = '^'
-            for sr in svs:
-                rang = SemverRange(sr)
-                if rang.lower() is not None:
-                    if self._lower is None or rang.lower() < self._lower:
-                        self._lower = rang.lower()
-                if rang.upper() is not None:
-                    if self._upper is None or rang.upper() > self._upper:
-                        self._upper = rang.upper()
-                op, semver = rang.op_semver()
-                if semver is not None:
-                    if op == '>=':
-                        if self._lower is None or semver < self._lower:
-                            self._lower = semver
-                    if op == '<':
-                        if self._upper is None or semver > self._upper:
-                            self._upper = semver
-            return
-
-        match = SV_RANGE.match(sv)
-        if match is None:
-            raise ValueError('%s is not a valid semver range string' % sv)
-
-        svm = match.groupdict()
-        op, major, minor, patch, prerelease, build = svm['op'], svm['major'], svm['minor'], svm['patch'], svm['prerelease'], svm['build']
-        prerelease = PreRelease(prerelease)
-
-        # fix up the op
-        if op is None:
-            if major == '*' or minor == '*' or patch == '*':
-                op = '*'
-            else:
-                # if no op was specified and there are no wildcards, then op
-                # defaults to '^'
-                op = '^'
-        else:
-            self._semver = Semver(sv[len(op):])
-
-        if op not in ('<=', '>=', '<', '>', '=', '^', '~', '*'):
-            raise ValueError('%s is not a valid semver operator' % op)
-
-        self._op = op
-
-        # lower bound
-        def find_lower():
-            if op in ('<=', '<', '=', '>', '>='):
-                return None
-
-            if op == '*':
-                # wildcards specify a range
-                if major == '*':
-                    return Semver('0.0.0')
-                elif minor == '*':
-                    return Semver(major + '.0.0')
-                elif patch == '*':
-                    return Semver(major + '.' + minor + '.0')
-            elif op == '^':
-                # caret specifies a range
-                if patch is None:
-                    if minor is None:
-                        # ^0 means >=0.0.0 and <1.0.0
-                        return Semver(major + '.0.0')
-                    else:
-                        # ^0.0 means >=0.0.0 and <0.1.0
-                        return Semver(major + '.' + minor + '.0')
-                else:
-                    # ^0.0.1 means >=0.0.1 and <0.0.2
-                    # ^0.1.2 means >=0.1.2 and <0.2.0
-                    # ^1.2.3 means >=1.2.3 and <2.0.0
-                    if int(major) == 0:
-                        if int(minor) == 0:
-                            # ^0.0.1
-                            return Semver('0.0.' + patch)
-                        else:
-                            # ^0.1.2
-                            return Semver('0.' + minor + '.' + patch)
-                    else:
-                        # ^1.2.3
-                        return Semver(major + '.' + minor + '.' + patch)
-            elif op == '~':
-                # tilde specifies a minimal range
-                if patch is None:
-                    if minor is None:
-                        # ~0 means >=0.0.0 and <1.0.0
-                        return Semver(major + '.0.0')
-                    else:
-                        # ~0.0 means >=0.0.0 and <0.1.0
-                        return Semver(major + '.' + minor + '.0')
-                else:
-                    # ~0.0.1 means >=0.0.1 and <0.1.0
-                    # ~0.1.2 means >=0.1.2 and <0.2.0
-                    # ~1.2.3 means >=1.2.3 and <1.3.0
-                    return Semver(major + '.' + minor + '.' + patch)
-
-            raise RuntimeError('No lower bound')
-        self._lower = find_lower()
-
-        def find_upper():
-            if op in ('<=', '<', '=', '>', '>='):
-                return None
-
-            if op == '*':
-                # wildcards specify a range
-                if major == '*':
-                    return None
-                elif minor == '*':
-                    return Semver(str(int(major) + 1) + '.0.0')
-                elif patch == '*':
-                    return Semver(major + '.' + str(int(minor) + 1) + '.0')
-            elif op == '^':
-                # caret specifies a range
-                if patch is None:
-                    if minor is None:
-                        # ^0 means >=0.0.0 and <1.0.0
-                        return Semver(str(int(major) + 1) + '.0.0')
-                    else:
-                        # ^0.0 means >=0.0.0 and <0.1.0
-                        return Semver(major + '.' + str(int(minor) + 1) + '.0')
-                else:
-                    # ^0.0.1 means >=0.0.1 and <0.0.2
-                    # ^0.1.2 means >=0.1.2 and <0.2.0
-                    # ^1.2.3 means >=1.2.3 and <2.0.0
-                    if int(major) == 0:
-                        if int(minor) == 0:
-                            # ^0.0.1
-                            return Semver('0.0.' + str(int(patch) + 1))
-                        else:
-                            # ^0.1.2
-                            return Semver('0.' + str(int(minor) + 1) + '.0')
-                    else:
-                        # ^1.2.3
-                        return Semver(str(int(major) + 1) + '.0.0')
-            elif op == '~':
-                # tilde specifies a minimal range
-                if patch is None:
-                    if minor is None:
-                        # ~0 means >=0.0.0 and <1.0.0
-                        return Semver(str(int(major) + 1) + '.0.0')
-                    else:
-                        # ~0.0 means >=0.0.0 and <0.1.0
-                        return Semver(major + '.' + str(int(minor) + 1) + '.0')
-                else:
-                    # ~0.0.1 means >=0.0.1 and <0.1.0
-                    # ~0.1.2 means >=0.1.2 and <0.2.0
-                    # ~1.2.3 means >=1.2.3 and <1.3.0
-                    return Semver(major + '.' + str(int(minor) + 1) + '.0')
-
-            raise RuntimeError('No upper bound')
-        self._upper = find_upper()
-
-    def __repr__(self):
-        return "SemverRange(%s, op=%s, semver=%s, lower=%s, upper=%s)" % (repr(self._input), self._op, self._semver, self._lower, self._upper)
-
-    def __str__(self):
-        return self._input
-
-    def lower(self):
-        return self._lower
-
-    def upper(self):
-        return self._upper
-
-    def op_semver(self):
-        return self._op, self._semver
-
-    def compare(self, sv):
-        if not isinstance(sv, Semver):
-            sv = Semver(sv)
-
-        op = self._op
-        if op == '*':
-            if self._semver is not None and self._semver['major'] == '*':
-                return sv >= Semver('0.0.0')
-            if self._lower is not None and sv < self._lower:
-                return False
-            if self._upper is not None and sv >= self._upper:
-                return False
-            return True
-        elif op == '^':
-            return (sv >= self._lower) and (sv < self._upper)
-        elif op == '~':
-            return (sv >= self._lower) and (sv < self._upper)
-        elif op == '<=':
-            return sv <= self._semver
-        elif op == '>=':
-            return sv >= self._semver
-        elif op == '<':
-            return sv < self._semver
-        elif op == '>':
-            return sv > self._semver
-        elif op == '=':
-            return sv == self._semver
-
-        raise RuntimeError('Semver comparison failed to find a matching op')
-
-
-def test_semver():
-    """
-    Tests for Semver parsing. Run using py.test: py.test bootstrap.py
-    """
-    assert str(Semver("1")) == "1.0.0"
-    assert str(Semver("1.1")) == "1.1.0"
-    assert str(Semver("1.1.1")) == "1.1.1"
-    assert str(Semver("1.1.1-alpha")) == "1.1.1-alpha"
-    assert str(Semver("1.1.1-alpha.1")) == "1.1.1-alpha.1"
-    assert str(Semver("1.1.1-alpha+beta")) == "1.1.1-alpha+beta"
-    assert str(Semver("1.1.1-alpha+beta.1")) == "1.1.1-alpha+beta.1"
-
-def test_semver_eq():
-    assert Semver("1") == Semver("1.0.0")
-    assert Semver("1.1") == Semver("1.1.0")
-    assert Semver("1.1.1") == Semver("1.1.1")
-    assert Semver("1.1.1-alpha") == Semver("1.1.1-alpha")
-    assert Semver("1.1.1-alpha.1") == Semver("1.1.1-alpha.1")
-    assert Semver("1.1.1-alpha+beta") == Semver("1.1.1-alpha+beta")
-    assert Semver("1.1.1-alpha.1+beta") == Semver("1.1.1-alpha.1+beta")
-    assert Semver("1.1.1-alpha.1+beta.1") == Semver("1.1.1-alpha.1+beta.1")
-
-def test_semver_comparison():
-    assert Semver("1") < Semver("2.0.0")
-    assert Semver("1.1") < Semver("1.2.0")
-    assert Semver("1.1.1") < Semver("1.1.2")
-    assert Semver("1.1.1-alpha") < Semver("1.1.1")
-    assert Semver("1.1.1-alpha") < Semver("1.1.1-beta")
-    assert Semver("1.1.1-alpha") < Semver("1.1.1-beta")
-    assert Semver("1.1.1-alpha") < Semver("1.1.1-alpha.1")
-    assert Semver("1.1.1-alpha.1") < Semver("1.1.1-alpha.2")
-    assert Semver("1.1.1-alpha+beta") < Semver("1.1.1+beta")
-    assert Semver("1.1.1-alpha+beta") < Semver("1.1.1-beta+beta")
-    assert Semver("1.1.1-alpha+beta") < Semver("1.1.1-beta+beta")
-    assert Semver("1.1.1-alpha+beta") < Semver("1.1.1-alpha.1+beta")
-    assert Semver("1.1.1-alpha.1+beta") < Semver("1.1.1-alpha.2+beta")
-    assert Semver("0.5") < Semver("2.0")
-    assert not (Semver("2.0") < Semver("0.5"))
-    assert not (Semver("0.5") > Semver("2.0"))
-    assert not (Semver("0.5") >= Semver("2.0"))
-    assert Semver("2.0") >= Semver("0.5")
-    assert Semver("2.0") > Semver("0.5")
-    assert not (Semver("2.0") > Semver("2.0"))
-    assert not (Semver("2.0") < Semver("2.0"))
-
-def test_semver_range():
-    def bounds(spec, lowe, high):
-        lowe = Semver(lowe) if lowe is not None else lowe
-        high = Semver(high) if high is not None else high
-        assert SemverRange(spec).lower() == lowe and SemverRange(spec).upper() == high
-    bounds('0',      '0.0.0', '1.0.0')
-    bounds('0.0',    '0.0.0', '0.1.0')
-    bounds('0.0.0',  '0.0.0', '0.0.1')
-    bounds('0.0.1',  '0.0.1', '0.0.2')
-    bounds('0.1.1',  '0.1.1', '0.2.0')
-    bounds('1.1.1',  '1.1.1', '2.0.0')
-    bounds('^0',     '0.0.0', '1.0.0')
-    bounds('^0.0',   '0.0.0', '0.1.0')
-    bounds('^0.0.0', '0.0.0', '0.0.1')
-    bounds('^0.0.1', '0.0.1', '0.0.2')
-    bounds('^0.1.1', '0.1.1', '0.2.0')
-    bounds('^1.1.1', '1.1.1', '2.0.0')
-    bounds('~0',     '0.0.0', '1.0.0')
-    bounds('~0.0',   '0.0.0', '0.1.0')
-    bounds('~0.0.0', '0.0.0', '0.1.0')
-    bounds('~0.0.1', '0.0.1', '0.1.0')
-    bounds('~0.1.1', '0.1.1', '0.2.0')
-    bounds('~1.1.1', '1.1.1', '1.2.0')
-    bounds('*',      '0.0.0', None)
-    bounds('0.*',    '0.0.0', '1.0.0')
-    bounds('0.0.*',  '0.0.0', '0.1.0')
-
-
-def test_semver_multirange():
-    assert SemverRange(">= 0.5, < 2.0").compare("1.0.0")
-    assert SemverRange("*").compare("0.2.7")
-
-
-class Runner(object):
-
-    def __init__(self, c, e, cwd=None):
-        self._cmd = c
-        if not isinstance(self._cmd, list):
-            self._cmd = [self._cmd]
-        self._env = e
-        self._stdout = []
-        self._stderr = []
-        self._returncode = 0
-        self._cwd = cwd
-
-    def __call__(self, c, e):
-        cmd = self._cmd + c
-        env = dict(self._env, **e)
-        #dbg(' env: %s' % env)
-        #dbg(' cwd: %s' % self._cwd)
-        envstr = ''
-        for k, v in env.iteritems():
-            envstr += ' %s="%s"' % (k, v)
-        if self._cwd is not None:
-            dbg('cd %s && %s %s' % (self._cwd, envstr, ' '.join(cmd)))
-        else:
-            dbg('%s %s' % (envstr, ' '.join(cmd)))
-
-        proc = subprocess.Popen(cmd, env=env,
-                                stdout=subprocess.PIPE, stderr=subprocess.PIPE,
-                                cwd=self._cwd)
-        out, err = proc.communicate()
-
-        for lo in out.split('\n'):
-            if len(lo) > 0:
-                self._stdout.append(lo)
-                #dbg('out: %s' % lo)
-
-        for le in err.split('\n'):
-            if len(le) > 0:
-                self._stderr.append(le)
-                dbg(le)
-
-        """
-        while proc.poll() is None:
-            lo = proc.stdout.readline().rstrip('\n')
-            le = proc.stderr.readline().rstrip('\n')
-            if len(lo) > 0:
-                self._stdout.append(lo)
-                dbg(lo)
-                sys.stdout.flush()
-            if len(le) > 0:
-                self._stderr.append(le)
-                dbg('err: %s', le)
-                sys.stdout.flush()
-        """
-        self._returncode = proc.wait()
-        #dbg(' ret: %s' % self._returncode)
-        return self._stdout
-
-    def output(self):
-        return self._stdout
-
-    def returncode(self):
-        return self._returncode
-
-class RustcRunner(Runner):
-
-    def __call__(self, c, e):
-        super(RustcRunner, self).__call__(c, e)
-        return ([], {}, {})
-
-class BuildScriptRunner(Runner):
-
-    def __call__(self, c, e):
-        #dbg('XXX Running build script:');
-        #dbg(' env: %s' % e)
-        #dbg(' '.join(self._cmd + c))
-        super(BuildScriptRunner, self).__call__(c, e)
-
-        # parse the output for cargo: lines
-        cmd = []
-        env = {}
-        denv = {}
-        for l in self.output():
-            match = BSCRIPT.match(str(l))
-            if match is None:
-                continue
-            pieces = match.groupdict()
-            k = pieces['key']
-            v = pieces['value']
-
-            if k == 'rustc-link-lib':
-                #dbg('YYYYYY: adding -l %s' % v)
-                cmd += ['-l', v]
-            elif k == 'rustc-link-search':
-                #dbg("adding link search path: %s" % v)
-                cmd += ['-L', v]
-            elif k == 'rustc-cfg':
-                cmd += ['--cfg', v]
-                env['CARGO_FEATURE_%s' % v.upper().replace('-', '_')] = 1
-            else:
-                #dbg("env[%s] = %s" % (k, v));
-                denv[k] = v
-        return (cmd, env, denv)
-
-class Crate(object):
-
-    def __init__(self, crate, ver, deps, cdir, build):
-        self._crate = str(crate)
-        self._version = Semver(ver)
-        self._dep_info = deps
-        self._dir = cdir
-        # put the build scripts first
-        self._build = [x for x in build if x.get('type') == 'build_script']
-        # then add the lib/bin builds
-        self._build += [x for x in build if x.get('type') != 'build_script']
-        self._resolved = False
-        self._deps = {}
-        self._refs = []
-        self._env = {}
-        self._dep_env = {}
-        self._extra_flags = []
-
-    def name(self):
-        return self._crate
-
-    def dep_info(self):
-        return self._dep_info
-
-    def version(self):
-        return self._version
-
-    def dir(self):
-        return self._dir
-
-    def __str__(self):
-        return '%s-%s' % (self.name(), self.version())
-
-    def add_dep(self, crate, features):
-        if str(crate) in self._deps:
-            return
-
-        features = [str(x) for x in features]
-        self._deps[str(crate)] = { 'features': features }
-        crate.add_ref(self)
-
-    def add_ref(self, crate):
-        if str(crate) not in self._refs:
-            self._refs.append(str(crate))
-
-    def resolved(self):
-        return self._resolved
-
-    @dbgCtx
-    def resolve(self, tdir, idir, nodl, graph=None):
-        if self._resolved:
-            return
-        if str(self) in CRATES:
-            return
-
-        if self._dep_info is not None:
-            print ''
-            dbg('Resolving dependencies for: %s' % str(self))
-            for d in self._dep_info:
-                kind = d.get('kind', 'normal')
-                if kind not in ('normal', 'build'):
-                    print ''
-                    dbg('Skipping %s dep %s' % (kind, d['name']))
-                    continue
-
-                optional = d.get('optional', False)
-                if optional and d['name'] not in INCLUDE_OPTIONAL:
-                    print ''
-                    dbg('Skipping optional dep %s' % d['name'])
-                    continue
-
-                svr = SemverRange(d['req'])
-                print ''
-                deps = []
-                dbg('Looking up info for %s %s' % (d['name'], str(svr)))
-                if d.get('local', None) is None:
-                    # go through crates first to see if the is satisfied already
-                    dcrate = find_crate_by_name_and_semver(d['name'], svr)
-                    if dcrate is not None:
-                        #import pdb; pdb.set_trace()
-                        svr = dcrate.version().as_range()
-                    name, ver, ideps, ftrs, cksum = crate_info_from_index(idir, d['name'], svr)
-                    if name in BLACKLIST:
-                        dbg('Found in blacklist, skipping %s' % (name))
-                    elif dcrate is None:
-                        if nodl:
-                            cdir = find_downloaded_crate(tdir, name, svr)
-                        else:
-                            cdir = dl_and_check_crate(tdir, name, ver, cksum)
-                        _, tver, tdeps, build = crate_info_from_toml(cdir)
-                        deps += ideps
-                        deps += tdeps
-                    else:
-                        dbg('Found crate already satisfying %s %s' % (d['name'], str(svr)))
-                        deps += dcrate.dep_info()
-                else:
-                    cdir = d['path']
-                    name, ver, ideps, build = crate_info_from_toml(cdir)
-                    deps += ideps
-
-                if name not in BLACKLIST:
-                    try:
-                        if dcrate is None:
-                            dcrate = Crate(name, ver, deps, cdir, build)
-                            if str(dcrate) in CRATES:
-                                dcrate = CRATES[str(dcrate)]
-                        UNRESOLVED.append(dcrate)
-                        if graph is not None:
-                            print >> graph, '"%s" -> "%s";' % (str(self), str(dcrate))
-
-                    except:
-                        dcrate = None
-
-                # clean up the list of features that are enabled
-                tftrs = d.get('features', [])
-                if isinstance(tftrs, dict):
-                    tftrs = tftrs.keys()
-                else:
-                    tftrs = [x for x in tftrs if len(x) > 0]
-
-                # add 'default' if default_features is true
-                if d.get('default_features', True):
-                    tftrs.append('default')
-
-                features = []
-                if isinstance(ftrs, dict):
-                    # add any available features that are activated by the
-                    # dependency entry in the parent's dependency record,
-                    # and any features they depend on recursively
-                    def add_features(f):
-                        if f in ftrs:
-                            for k in ftrs[f]:
-                                # guard against infinite recursion
-                                if not k in features:
-                                    features.append(k)
-                                    add_features(k)
-                    for k in tftrs:
-                        add_features(k)
-                else:
-                    features += [x for x in ftrs if (len(x) > 0) and (x in tftrs)]
-
-                if dcrate is not None:
-                    self.add_dep(dcrate, features)
-
-        self._resolved = True
-        CRATES[str(self)] = self
-
-    @dbgCtx
-    def build(self, by, out_dir, features=[]):
-        extra_filename = '-' + str(self.version()).replace('.','_')
-        output_name = self.name().replace('-','_')
-        output = os.path.join(out_dir, 'lib%s%s.rlib' % (output_name, extra_filename))
-
-        if str(self) in BUILT:
-            return ({'name':self.name(), 'lib':output}, self._env, self._extra_flags)
-
-        externs = []
-        extra_flags = []
-        for dep,info in self._deps.iteritems():
-            if dep in CRATES:
-                extern, env, extra_flags = CRATES[dep].build(self, out_dir, info['features'])
-                externs.append(extern)
-                self._dep_env[CRATES[dep].name()] = env
-                self._extra_flags += extra_flags
-
-        if os.path.isfile(output):
-            print ''
-            dbg('Skipping %s, already built (needed by: %s)' % (str(self), str(by)))
-            BUILT[str(self)] = str(by)
-            return ({'name':self.name(), 'lib':output}, self._env, self._extra_flags)
-
-        # build the environment for subcommands
-        tenv = dict(os.environ)
-        env = {}
-        env['PATH'] = tenv['PATH']
-        env['OUT_DIR'] = out_dir
-        env['TARGET'] = TARGET
-        env['HOST'] = HOST
-        env['NUM_JOBS'] = '1'
-        env['OPT_LEVEL'] = '0'
-        env['DEBUG'] = '0'
-        env['PROFILE'] = 'release'
-        env['CARGO_MANIFEST_DIR'] = self.dir()
-        env['CARGO_PKG_VERSION_MAJOR'] = self.version()['major']
-        env['CARGO_PKG_VERSION_MINOR'] = self.version()['minor']
-        env['CARGO_PKG_VERSION_PATCH'] = self.version()['patch']
-        pre = self.version()['prerelease']
-        if pre is None:
-            pre = ''
-        env['CARGO_PKG_VERSION_PRE'] = pre
-        env['CARGO_PKG_VERSION'] = str(self.version())
-        for f in features:
-            env['CARGO_FEATURE_%s' % f.upper().replace('-','_')] = '1'
-        for l,e in self._dep_env.iteritems():
-            for k,v in e.iteritems():
-                if type(v) is not str and type(v) is not unicode:
-                    v = str(v)
-                env['DEP_%s_%s' % (l.upper(), v.upper())] = v
-
-        # create the builders, build scrips are first
-        cmds = []
-        for b in self._build:
-            v = str(self._version).replace('.','_')
-            cmd = ['rustc']
-            cmd.append(os.path.join(self._dir, b['path']))
-            cmd.append('--crate-name')
-            if b['type'] == 'lib':
-                b.setdefault('name', self.name())
-                cmd.append(b['name'].replace('-','_'))
-                cmd.append('--crate-type')
-                cmd.append('lib')
-            elif b['type'] == 'build_script':
-                cmd.append('build_script_%s' % b['name'].replace('-','_'))
-                cmd.append('--crate-type')
-                cmd.append('bin')
-            else:
-                cmd.append(b['name'].replace('-','_'))
-                cmd.append('--crate-type')
-                cmd.append('bin')
-
-            for f in features:
-                cmd.append('--cfg')
-                cmd.append('feature=\"%s\"' % f)
-
-            cmd.append('-C')
-            cmd.append('extra-filename=' + extra_filename)
-
-            cmd.append('--out-dir')
-            cmd.append('%s' % out_dir)
-            cmd.append('--emit=dep-info,link')
-            cmd.append('--target')
-            cmd.append(TARGET)
-            cmd.append('-L')
-            cmd.append('%s' % out_dir)
-            cmd.append('-L')
-            cmd.append('%s/lib' % out_dir)
-
-
-            # add in the flags from dependencies
-            cmd += self._extra_flags
-
-            for e in externs:
-                cmd.append('--extern')
-                cmd.append('%s=%s' % (e['name'].replace('-','_'), e['lib']))
-
-            # get the pkg key name
-            match = BNAME.match(b['name'])
-            if match is not None:
-                match = match.groupdict()['name'].replace('-','_')
-
-            # queue up the runner
-            cmds.append({'name':b['name'], 'env_key':match, 'cmd':RustcRunner(cmd, env)})
-
-            # queue up the build script runner
-            if b['type'] == 'build_script':
-                bcmd = os.path.join(out_dir, 'build_script_%s-%s' % (b['name'], v))
-                cmds.append({'name':b['name'], 'env_key':match, 'cmd':BuildScriptRunner(bcmd, env, self._dir)})
-
-        print ''
-        dbg('Building %s (needed by: %s)' % (str(self), str(by)))
-
-        bcmd = []
-        benv = {}
-        for c in cmds:
-            runner = c['cmd']
-
-            (c1, e1, e2) = runner(bcmd, benv)
-
-            if runner.returncode() != 0:
-                raise RuntimeError('build command failed: %s' % runner.returncode())
-
-            bcmd += c1
-            benv = dict(benv, **e1)
-
-            key = c['env_key']
-            for k,v in e2.iteritems():
-                self._env['DEP_%s_%s' % (key.upper(), k.upper())] = v
-
-            #dbg('XXX  cmd: %s' % bcmd)
-            #dbg('XXX  env: %s' % benv)
-            #dbg('XXX denv: %s' % self._env)
-            #print ''
-
-        BUILT[str(self)] = str(by)
-        return ({'name':self.name(), 'lib':output}, self._env, bcmd)
-
-
-def dl_crate(url, depth=0):
-    if depth > 10:
-        raise RuntimeError('too many redirects')
-
-    r = requests.get(url)
-    try:
-        dbg('%sconnected to %s...%s' % ((' ' * depth), r.url, r.status_code))
-
-        if URLS_FILE is not None:
-            with open(URLS_FILE, "a") as f:
-                f.write(r.url + "\n")
-
-        return r.content
-    finally:
-        r.close()
-
-def dl_and_check_crate(tdir, name, ver, cksum):
-    cname = '%s-%s' % (name, ver)
-    cdir = os.path.join(tdir, cname)
-    if cname in CRATES:
-        dbg('skipping %s...already downloaded' % cname)
-        return cdir
-
-    def check_checksum(buf):
-        if (cksum is not None):
-            h = hashlib.sha256()
-            h.update(buf)
-            if h.hexdigest() == cksum:
-                dbg('Checksum is good...%s' % cksum)
-            else:
-                dbg('Checksum is BAD (%s != %s)' % (h.hexdigest(), cksum))
-
-    if CRATE_CACHE:
-        cachename = os.path.join(CRATE_CACHE, "%s.crate" % (cname))
-        if os.path.isfile(cachename):
-            dbg('found crate in cache...%s.crate' % (cname))
-            buf = open(cachename).read()
-            check_checksum(buf)
-            with tarfile.open(fileobj=cStringIO.StringIO(buf)) as tf:
-                dbg('unpacking result to %s...' % cdir)
-                tf.extractall(path=tdir)
-            return cdir
-
-    if not os.path.isdir(cdir):
-        dbg('Downloading %s source to %s' % (cname, cdir))
-        dl = CRATE_API_DL % (name, ver)
-        buf = dl_crate(dl)
-        check_checksum(buf)
-
-        if CRATE_CACHE:
-            dbg("saving crate to %s/%s.crate..." % (CRATE_CACHE, cname))
-            with open(os.path.join(CRATE_CACHE, "%s.crate" % (cname)), "wb") as f:
-                f.write(buf)
-
-        fbuf = cStringIO.StringIO(buf)
-        with tarfile.open(fileobj=fbuf) as tf:
-            dbg('unpacking result to %s...' % cdir)
-            tf.extractall(path=tdir)
-
-    return cdir
-
-
-def find_downloaded_crate(tdir, name, svr):
-    exists = glob("%s/%s-[0-9]*" % (tdir, name))
-    if not exists:
-        raise RuntimeError("crate does not exist and have --no-download: %s" % name)
-
-    # First, grok the available versions.
-    aver = sorted([Semver(CVER.search(x).group(1)) for x in exists])
-
-    # Now filter the "suitable" versions based on our version range.
-    sver = filter(svr.compare, aver)
-    if not sver:
-        raise RuntimeError("unable to satisfy dependency %s %s from %s; try running without --no-download" % (name, svr, map(str, aver)))
-
-    cver = sver[-1]
-    return "%s/%s-%s" % (tdir, name, cver)
-
-
-def crate_info_from_toml(cdir):
-    try:
-        with open(os.path.join(cdir, 'Cargo.toml'), 'rb') as ctoml:
-            #import pdb; pdb.set_trace()
-            cfg = toml.load(ctoml)
-            build = []
-            p = cfg.get('package',cfg.get('project', {}))
-            name = p.get('name', None)
-            #if name == 'num_cpus':
-            #    import pdb; pdb.set_trace()
-            ver = p.get('version', None)
-            if (name is None) or (ver is None):
-                import pdb; pdb.set_trace()
-                raise RuntimeError('invalid .toml file format')
-
-            # look for a "links" item
-            lnks = p.get('links', [])
-            if type(lnks) is not list:
-                lnks = [lnks]
-
-            # look for a "build" item
-            bf = p.get('build', None)
-
-            # if we have a 'links', there must be a 'build'
-            if len(lnks) > 0 and bf is None:
-                import pdb; pdb.set_trace()
-                raise RuntimeError('cargo requires a "build" item if "links" is specified')
-
-            # there can be target specific build script overrides
-            boverrides = {}
-            for lnk in lnks:
-                boverrides.update(cfg.get('target', {}).get(TARGET, {}).get(lnk, {}))
-
-            bmain = False
-            if bf is not None:
-                build.append({'type':'build_script', \
-                              'path':[ bf ], \
-                              'name':name.replace('-','_'), \
-                              'links': lnks, \
-                              'overrides': boverrides})
-
-            # look for libs array
-            libs = cfg.get('lib', [])
-            if type(libs) is not list:
-                libs = [libs]
-            for l in libs:
-                l['type'] = 'lib'
-                l['links'] = lnks
-                if l.get('path', None) is None:
-                    l['path'] = [ 'lib.rs' ]
-                build.append(l)
-                bmain = True
-
-            # look for bins array
-            bins = cfg.get('bin', [])
-            if type(bins) is not list:
-                bins = [bins]
-            for b in bins:
-                if b.get('path', None) is None:
-                    b['path'] = [ os.path.join('bin', '%s.rs' % b['name']), os.path.join('bin', 'main.rs'), '%s.rs' % b['name'], 'main.rs' ]
-                build.append({'type': 'bin', \
-                              'name':b['name'], \
-                              'path':b['path'], \
-                              'links': lnks})
-                bmain = True
-
-            # if no explicit directions on what to build, then add a default
-            if bmain == False:
-                build.append({'type':'lib', 'path':'lib.rs', 'name':name.replace('-','_')})
-
-            for b in build:
-                # make sure the path is a list of possible paths
-                if type(b['path']) is not list:
-                    b['path'] = [ b['path'] ]
-                bin_paths = []
-                for p in b['path']:
-                    bin_paths.append(os.path.join(cdir, p))
-                    bin_paths.append(os.path.join(cdir, 'src', p))
-
-                found_path = None
-                for p in bin_paths:
-                    if os.path.isfile(p):
-                        found_path = p
-                        break
-
-                if found_path == None:
-                    import pdb; pdb.set_trace()
-                    raise RuntimeError('could not find %s to build in %s', (build, cdir))
-                else:
-                    b['path'] = found_path
-
-            d = cfg.get('build-dependencies', {})
-            d.update(cfg.get('dependencies', {}))
-            d.update(cfg.get('target', {}).get(TARGET, {}).get('dependencies', {}))
-            deps = []
-            for k,v in d.iteritems():
-                if type(v) is not dict:
-                    deps.append({'name':k, 'req': v})
-                elif 'path' in v:
-                    if v.get('version', None) is None:
-                        deps.append({'name':k, 'path':os.path.join(cdir, v['path']), 'local':True, 'req':0})
-                    else:
-                        opts = v.get('optional',False)
-                        ftrs = v.get('features',[])
-                        deps.append({'name':k, 'path': v['path'], 'req':v['version'], 'features':ftrs, 'optional':opts})
-                else:
-                    opts = v.get('optional',False)
-                    ftrs = v.get('features',[])
-                    deps.append({'name':k, 'req':v['version'], 'features':ftrs, 'optional':opts})
-
-            return (name, ver, deps, build)
-
-    except Exception, e:
-        dbg('failed to load toml file for: %s (%s)' % (cdir, str(e)))
-        import pdb; pdb.set_trace()
-
-    return (None, None, [], 'lib.rs')
-
-
-def crate_info_from_index(idir, name, svr):
-    if len(name) == 1:
-        ipath = os.path.join(idir, '1', name)
-    elif len(name) == 2:
-        ipath = os.path.join(idir, '2', name)
-    elif len(name) == 3:
-        ipath = os.path.join(idir, '3', name[0:1], name)
-    else:
-        ipath = os.path.join(idir, name[0:2], name[2:4], name)
-
-    dbg('opening crate info: %s' % ipath)
-    dep_infos = []
-    with open(ipath, 'rb') as fin:
-        lines = fin.readlines()
-        for l in lines:
-            dep_infos.append(json.loads(l))
-
-    passed = {}
-    for info in dep_infos:
-        if 'vers' not in info:
-            continue
-        sv = Semver(info['vers'])
-        if svr.compare(sv):
-            passed[sv] = info
-
-    keys = sorted(passed.iterkeys())
-    best_match = keys.pop()
-    dbg('best match is %s-%s' % (name, best_match))
-    best_info = passed[best_match]
-    name = best_info.get('name', None)
-    ver = best_info.get('vers', None)
-    deps = best_info.get('deps', [])
-    ftrs = best_info.get('features', [])
-    cksum = best_info.get('cksum', None)
-
-    # only include deps without a 'target' or ones with matching 'target'
-    deps = [x for x in deps if x.get('target', TARGET) == TARGET]
-
-    return (name, ver, deps, ftrs, cksum)
-
-
-def find_crate_by_name_and_semver(name, svr):
-    for c in CRATES.itervalues():
-        if c.name() == name and svr.compare(c.version()):
-            return c
-    for c in UNRESOLVED:
-        if c.name() == name and svr.compare(c.version()):
-            return c
-    return None
-
-
-def args_parser():
-    parser = argparse.ArgumentParser(description='Cargo Bootstrap Tool')
-    parser.add_argument('--cargo-root', type=str,  default=os.getcwd(),
-                        help="specify the cargo repo root path")
-    parser.add_argument('--target-dir', type=str, default=tempfile.mkdtemp(),
-                        help="specify the path for storing built dependency libs")
-    parser.add_argument('--crate-index', type=str, default=None,
-                        help="path to where the crate index should be cloned")
-    parser.add_argument('--target', type=str, default=None,
-                        help="target triple for machine we're bootstrapping for")
-    parser.add_argument('--host', type=str, default=None,
-                        help="host triple for machine we're bootstrapping on")
-    parser.add_argument('--no-clone', action='store_true',
-                        help="skip cloning crates index, --crate-index must point to an existing clone of the crates index")
-    parser.add_argument('--no-git', action='store_true',
-                        help="don't assume that the crates index and cargo root are git repos; implies --no-clone")
-    parser.add_argument('--no-clean', action='store_true',
-                        help="don't delete the target dir and crate index")
-    parser.add_argument('--download', action='store_true',
-                        help="only download the crates needed to build cargo")
-    parser.add_argument('--no-download', action='store_true',
-                        help="don't download any crates (fail if any do not exist)")
-    parser.add_argument('--graph', action='store_true',
-                        help="output a dot graph of the dependencies")
-    parser.add_argument('--urls-file', type=str, default=None,
-                        help="file to write crate URLs to")
-    parser.add_argument('--blacklist', type=str, default="",
-                        help="space-separated list of crates to skip")
-    parser.add_argument('--include-optional', type=str, default="",
-                        help="space-separated list of optional crates to include")
-    parser.add_argument('--patchdir', type=str,
-                        help="directory with patches to apply after downloading crates. organized by crate/NNNN-description.patch")
-    parser.add_argument('--crate-cache', type=str,
-                        help="download and save crates to crate cache (directory)")
-    return parser
-
-
-def open_or_clone_repo(rdir, rurl, no_clone):
-    try:
-        repo = git.open_repo(rdir)
-        return repo
-    except:
-        repo = None
-
-    if repo is None and no_clone is False:
-        dbg('Cloning %s to %s' % (rurl, rdir))
-        return git.clone(rurl, rdir)
-
-    if repo is None and no_clone is True:
-        repo = rdir
-
-    return repo
-
-
-def patch_crates(targetdir, patchdir):
-    """
-    Apply patches in patchdir to downloaded crates
-    patchdir organization:
-
-    <patchdir>/
-      <crate>/
-        <patch>.patch
-    """
-    for patch in glob(os.path.join(patchdir, '*', '*.patch')):
-        crateid = os.path.basename(os.path.dirname(patch))
-        m = re.match(r'^([A-Za-z0-9_-]+?)(?:-([\d.]+))?$', crateid)
-        if m:
-            cratename = m.group(1)
-        else:
-            cratename = crateid
-        if cratename != crateid:
-            dirs = glob(os.path.join(targetdir, crateid))
-        else:
-            dirs = glob(os.path.join(targetdir, '%s-*' % (cratename)))
-        for cratedir in dirs:
-            # check if patch has been applied
-            patchpath = os.path.abspath(patch)
-            p = subprocess.Popen(['patch', '--dry-run', '-s', '-f', '-F', '10', '-p1', '-i', patchpath], cwd=cratedir)
-            rc = p.wait()
-            if rc == 0:
-                dbg("patching %s with patch %s" % (os.path.basename(cratedir), os.path.basename(patch)))
-                p = subprocess.Popen(['patch', '-s', '-F', '10', '-p1', '-i', patchpath], cwd=cratedir)
-                rc = p.wait()
-                if rc != 0:
-                    dbg("%s: failed to apply %s (rc=%s)" % (os.path.basename(cratedir), os.path.basename(patch), rc))
-            else:
-                dbg("%s: %s does not apply (rc=%s)" % (os.path.basename(cratedir), os.path.basename(patch), rc))
-
-
-if __name__ == "__main__":
-    try:
-        # parse args
-        parser = args_parser()
-        args = parser.parse_args()
-
-        # clone the cargo index
-        if args.crate_index is None:
-            args.crate_index = os.path.normpath(os.path.join(args.target_dir, 'index'))
-        dbg('cargo: %s, target: %s, index: %s' % \
-              (args.cargo_root, args.target_dir, args.crate_index))
-
-        TARGET = args.target
-        HOST = args.host
-        URLS_FILE = args.urls_file
-        BLACKLIST = args.blacklist.split()
-        INCLUDE_OPTIONAL = args.include_optional.split()
-        if args.crate_cache and os.path.isdir(args.crate_cache):
-            CRATE_CACHE = os.path.abspath(args.crate_cache)
-
-        if not args.no_git:
-            index = open_or_clone_repo(args.crate_index, CRATES_INDEX, args.no_clone)
-            cargo = open_or_clone_repo(args.cargo_root, CARGO_REPO, args.no_clone)
-
-            if index is None:
-                raise RuntimeError('You must have a local clone of the crates index, ' \
-                                   'omit --no-clone to allow this script to clone it for ' \
-                                   'you, or pass --no-git to bypass this check.')
-            if cargo is None:
-                raise RuntimeError('You must have a local clone of the cargo repo ' \
-                                   'so that this script can read the cargo toml file.')
-
-        if TARGET is None:
-            raise RuntimeError('You must specify the target triple of this machine')
-        if HOST is None:
-            HOST = TARGET
-
-    except Exception, e:
-        frame = inspect.trace()[-1]
-        print >> sys.stderr, "\nException:\n from %s, line %d:\n %s\n" % (frame[1], frame[2], e)
-        parser.print_help()
-        if not args.no_clean:
-            print "cleaning up %s" % (args.target_dir)
-            shutil.rmtree(args.target_dir)
-        sys.exit(1)
-
-    try:
-
-        # load cargo deps
-        name, ver, deps, build = crate_info_from_toml(args.cargo_root)
-        cargo_crate = Crate(name, ver, deps, args.cargo_root, build)
-        UNRESOLVED.append(cargo_crate)
-
-        if args.graph:
-            GRAPH = open(os.path.join(args.target_dir, 'deps.dot'), 'wb')
-            print >> GRAPH, "digraph %s {" % name
-
-        # resolve and download all of the dependencies
-        print ''
-        print '===================================='
-        print '===== DOWNLOADING DEPENDENCIES ====='
-        print '===================================='
-        while len(UNRESOLVED) > 0:
-            crate = UNRESOLVED.pop(0)
-            crate.resolve(args.target_dir, args.crate_index, args.no_download, GRAPH)
-
-        if args.graph:
-            print >> GRAPH, "}"
-            GRAPH.close()
-
-        if args.patchdir:
-            print ''
-            print '========================'
-            print '===== PATCH CRATES ====='
-            print '========================'
-            patch_crates(args.target_dir, args.patchdir)
-
-        if args.download:
-            print "done downloading..."
-            sys.exit(0)
-
-        # build cargo
-        print ''
-        print '=========================='
-        print '===== BUILDING CARGO ====='
-        print '=========================='
-        cargo_crate.build('bootstrap.py', args.target_dir)
-
-        # cleanup
-        if not args.no_clean:
-            print "cleaning up %s..." % (args.target_dir)
-            shutil.rmtree(args.target_dir)
-        print "done"
-
-    except Exception, e:
-        frame = inspect.trace()[-1]
-        print >> sys.stderr, "\nException:\n from %s, line %d:\n %s\n" % (frame[1], frame[2], e)
-        if not args.no_clean:
-            print "cleaning up %s..." % (args.target_dir)
-            shutil.rmtree(args.target_dir)
-        sys.exit(1)
-
-
index ec4dafe25104db1b92e9bb717828552caeb85ff3..05878e7325018bb395d9bd768bda7c95259aec12 100755 (executable)
@@ -1,8 +1,7 @@
 #!/bin/sh
 set -e
-echo ""
-echo "This needs a local copy of cargo-vendor, and the following packages:"
-echo "python-dulwich python-pytoml devscripts"
+echo "This needs the following packages:"
+echo "  python3-pytoml devscripts cargo"
 echo ""
 
 TMPDIR=`mktemp -d`