xref: /openbmc/openbmc/poky/bitbake/lib/bb/fetch2/crate.py (revision 595f6308)
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