1# ex:ts=4:sw=4:sts=4:et 2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- 3""" 4BitBake 'Fetch' implementation for crates.io 5""" 6 7# Copyright (C) 2016 Doug Goldstein 8# 9# SPDX-License-Identifier: GPL-2.0-only 10# 11# Based on functions from the base bb module, Copyright 2003 Holger Schurig 12 13import hashlib 14import json 15import os 16import shutil 17import subprocess 18import bb 19from bb.fetch2 import logger, subprocess_setup, UnpackError 20from bb.fetch2.wget import Wget 21 22 23class Crate(Wget): 24 25 """Class to fetch crates via wget""" 26 27 def _cargo_bitbake_path(self, rootdir): 28 return os.path.join(rootdir, "cargo_home", "bitbake") 29 30 def supports(self, ud, d): 31 """ 32 Check to see if a given url is for this fetcher 33 """ 34 return ud.type in ['crate'] 35 36 def recommends_checksum(self, urldata): 37 return False 38 39 def urldata_init(self, ud, d): 40 """ 41 Sets up to download the respective crate from crates.io 42 """ 43 44 if ud.type == 'crate': 45 self._crate_urldata_init(ud, d) 46 47 super(Crate, self).urldata_init(ud, d) 48 49 def _crate_urldata_init(self, ud, d): 50 """ 51 Sets up the download for a crate 52 """ 53 54 # URL syntax is: crate://NAME/VERSION 55 # break the URL apart by / 56 parts = ud.url.split('/') 57 if len(parts) < 5: 58 raise bb.fetch2.ParameterError("Invalid URL: Must be crate://HOST/NAME/VERSION", ud.url) 59 60 # last field is version 61 version = parts[len(parts) - 1] 62 # second to last field is name 63 name = parts[len(parts) - 2] 64 # host (this is to allow custom crate registries to be specified 65 host = '/'.join(parts[2:len(parts) - 2]) 66 67 # if using upstream just fix it up nicely 68 if host == 'crates.io': 69 host = 'crates.io/api/v1/crates' 70 71 ud.url = "https://%s/%s/%s/download" % (host, name, version) 72 ud.parm['downloadfilename'] = "%s-%s.crate" % (name, version) 73 ud.parm['name'] = name 74 75 logger.debug(2, "Fetching %s to %s" % (ud.url, ud.parm['downloadfilename'])) 76 77 def unpack(self, ud, rootdir, d): 78 """ 79 Uses the crate to build the necessary paths for cargo to utilize it 80 """ 81 if ud.type == 'crate': 82 return self._crate_unpack(ud, rootdir, d) 83 else: 84 super(Crate, self).unpack(ud, rootdir, d) 85 86 def _crate_unpack(self, ud, rootdir, d): 87 """ 88 Unpacks a crate 89 """ 90 thefile = ud.localpath 91 92 # possible metadata we need to write out 93 metadata = {} 94 95 # change to the rootdir to unpack but save the old working dir 96 save_cwd = os.getcwd() 97 os.chdir(rootdir) 98 99 pn = d.getVar('BPN') 100 if pn == ud.parm.get('name'): 101 cmd = "tar -xz --no-same-owner -f %s" % thefile 102 else: 103 cargo_bitbake = self._cargo_bitbake_path(rootdir) 104 105 cmd = "tar -xz --no-same-owner -f %s -C %s" % (thefile, cargo_bitbake) 106 107 # ensure we've got these paths made 108 bb.utils.mkdirhier(cargo_bitbake) 109 110 # generate metadata necessary 111 with open(thefile, 'rb') as f: 112 # get the SHA256 of the original tarball 113 tarhash = hashlib.sha256(f.read()).hexdigest() 114 115 metadata['files'] = {} 116 metadata['package'] = tarhash 117 118 path = d.getVar('PATH') 119 if path: 120 cmd = "PATH=\"%s\" %s" % (path, cmd) 121 bb.note("Unpacking %s to %s/" % (thefile, os.getcwd())) 122 123 ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True) 124 125 os.chdir(save_cwd) 126 127 if ret != 0: 128 raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), ud.url) 129 130 # if we have metadata to write out.. 131 if len(metadata) > 0: 132 cratepath = os.path.splitext(os.path.basename(thefile))[0] 133 bbpath = self._cargo_bitbake_path(rootdir) 134 mdfile = '.cargo-checksum.json' 135 mdpath = os.path.join(bbpath, cratepath, mdfile) 136 with open(mdpath, "w") as f: 137 json.dump(metadata, f) 138