# ex:ts=4:sw=4:sts=4:et # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- """ BitBake 'Fetch' implementation for crates.io """ # Copyright (C) 2016 Doug Goldstein # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Based on functions from the base bb module, Copyright 2003 Holger Schurig import os import shutil import subprocess import bb from bb.fetch2 import logger, subprocess_setup, UnpackError from bb.fetch2.wget import Wget class Crate(Wget): """Class to fetch crates via wget""" def _cargo_path(self, rootdir, component): # TODO: make this less brittle # This can go away entirely once we can build a cargo that supports source-replacement repo = "github.com-1ecc6299db9ec823" return os.path.join(rootdir, "cargo_home", "registry", component, repo) def _cargo_src_path(self, rootdir): return self._cargo_path(rootdir, "src") def _cargo_index_path(self, rootdir): return self._cargo_path(rootdir, "index") def _cargo_cache_path(self, rootdir): return self._cargo_path(rootdir, "cache") def _cargo_registry_path(self, rootdir, component=""): return os.path.join(rootdir, "cargo_registry", component) def supports(self, ud, d): """ Check to see if a given url is for this fetcher """ return ud.type in ['crate', 'crate-index'] def recommends_checksum(self, urldata): return False def urldata_init(self, ud, d): """ Sets up to download the respective crate from crates.io """ if ud.type == 'crate': self._crate_urldata_init(ud, d) elif ud.type == 'crate-index': self._index_urldata_init(ud, d) super(Crate, self).urldata_init(ud, d) def _crate_urldata_init(self, ud, d): """ Sets up the download for a crate """ # URL syntax is: crate://NAME/VERSION # break the URL apart by / parts = ud.url.split('/') if len(parts) < 5: raise bb.fetch2.ParameterError("Invalid URL: Must be crate://HOST/NAME/VERSION", ud.url) # last field is version version = parts[len(parts) - 1] # second to last field is name name = parts[len(parts) - 2] # host (this is to allow custom crate registries to be specified host = '/'.join(parts[2:len(parts) - 2]) # if using upstream just fix it up nicely if host == 'crates.io': host = 'crates.io/api/v1/crates' ud.url = "https://%s/%s/%s/download" % (host, name, version) ud.parm['downloadfilename'] = "%s-%s.crate" % (name, version) ud.parm['name'] = name logger.debug(2, "Fetching %s to %s" % (ud.url, ud.parm['downloadfilename'])) def _index_urldata_init(self, ud, d): """ Sets up the download for the cargo index """ # URL syntax is: crate-index://REV # break the URL apart by / parts = ud.url.split('/') if len(parts) != 4: raise bb.fetch2.ParameterError("Invalid URL: Must be crate-index://HOST/REV", ud.url) # last field is the rev rev = parts[3] host = parts[2] if host == 'crates.io': host = 'github.com/rust-lang/crates.io-index' ud.url = "https://%s/archive/%s.tar.gz" % (host, rev) ud.parm['downloadfilename'] = 'cargo-index-%s.tar.gz' % rev ud.parm['name'] = "index" logger.debug(2, "Fetching crate index %s" % ud.url) def unpack(self, ud, rootdir, d): """ Uses the crate to build the necessary paths for cargo to utilize it """ if ud.type == 'crate-index': return self._index_unpack(ud, rootdir, d) elif ud.type == 'crate': return self._crate_unpack(ud, rootdir, d) else: super(Crate, self).unpack(ud, rootdir, d) def _index_unpack(self, ud, rootdir, d): cargo_index = self._cargo_index_path(rootdir) self._index_unpack_to(ud, rootdir, d, cargo_index) cargo_registry_index = self._cargo_registry_path(rootdir, "index") self._index_unpack_to(ud, rootdir, d, cargo_registry_index) def _index_unpack_to(self, ud, rootdir, d, cargo_index): """ Unpacks the index """ thefile = ud.localpath cmd = "tar -xz --no-same-owner --strip-components 1 -f %s -C %s" % (thefile, cargo_index) # change to the rootdir to unpack but save the old working dir save_cwd = os.getcwd() os.chdir(rootdir) # ensure we've got these paths made bb.utils.mkdirhier(cargo_index) # path it path = d.getVar('PATH', True) if path: cmd = "PATH=\"%s\" %s" % (path, cmd) bb.note("Unpacking %s to %s/" % (thefile, cargo_index)) ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True) os.chdir(save_cwd) if ret != 0: raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), ud.url) def _crate_unpack(self, ud, rootdir, d): """ Unpacks a crate """ thefile = ud.localpath # change to the rootdir to unpack but save the old working dir save_cwd = os.getcwd() os.chdir(rootdir) pn = d.getVar('PN', True) if pn == ud.parm.get('name'): cmd = "tar -xz --no-same-owner -f %s" % thefile else: cargo_src = self._cargo_src_path(rootdir) cargo_cache = self._cargo_cache_path(rootdir) cargo_registry = self._cargo_registry_path(rootdir) cmd = "tar -xz --no-same-owner -f %s -C %s" % (thefile, cargo_src) # ensure we've got these paths made bb.utils.mkdirhier(cargo_cache) bb.utils.mkdirhier(cargo_registry) bb.utils.mkdirhier(cargo_src) bb.note("Copying %s to %s/" % (thefile, cargo_cache)) shutil.copy(thefile, cargo_cache) bb.note("Copying %s to %s/" % (thefile, cargo_registry)) shutil.copy(thefile, cargo_registry) # path it path = d.getVar('PATH', True) if path: cmd = "PATH=\"%s\" %s" % (path, cmd) bb.note("Unpacking %s to %s/" % (thefile, os.getcwd())) ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True) os.chdir(save_cwd) if ret != 0: raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), ud.url)