|
1
|
+#
|
|
2
|
+# Copyright (C) 2018 Codethink Limited
|
|
3
|
+#
|
|
4
|
+# This program is free software; you can redistribute it and/or
|
|
5
|
+# modify it under the terms of the GNU Lesser General Public
|
|
6
|
+# License as published by the Free Software Foundation; either
|
|
7
|
+# version 2 of the License, or (at your option) any later version.
|
|
8
|
+#
|
|
9
|
+# This library is distributed in the hope that it will be useful,
|
|
10
|
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
12
|
+# Lesser General Public License for more details.
|
|
13
|
+#
|
|
14
|
+# You should have received a copy of the GNU Lesser General Public
|
|
15
|
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+#
|
|
17
|
+# Authors:
|
|
18
|
+# Valentin David <valentin david codethink co uk>
|
|
19
|
+
|
|
20
|
+"""
|
|
21
|
+cargo - stage files from cargo manifest
|
|
22
|
+=======================================
|
|
23
|
+
|
|
24
|
+`cargo` downloads and stages cargo crates based on a `Cargo.toml`
|
|
25
|
+manifest provided by a previous source.
|
|
26
|
+
|
|
27
|
+`ref` will contain the `Cargo.lock` file. `bst track` should be used
|
|
28
|
+to set it.
|
|
29
|
+
|
|
30
|
+When `keep-lock` is true, tracking will store the current `Cargo.lock`
|
|
31
|
+provided by previous sources. in the `ref`. If `keep-lock` is false or
|
|
32
|
+absent, then `ref` will be created for the latest available crates.
|
|
33
|
+
|
|
34
|
+**Host dependencies:**
|
|
35
|
+
|
|
36
|
+ * cargo
|
|
37
|
+ * cargo-vendor (can be installed with `cargo install cargo-vendor`).
|
|
38
|
+
|
|
39
|
+**Usage:**
|
|
40
|
+
|
|
41
|
+.. code:: yaml
|
|
42
|
+
|
|
43
|
+ # Specify the cargo source kind
|
|
44
|
+ kind: cargo
|
|
45
|
+
|
|
46
|
+ # Optionally give the subdirectory where the `Cargo.toml` manifest
|
|
47
|
+ # can be found.
|
|
48
|
+ subdir: subproject
|
|
49
|
+
|
|
50
|
+ # Optionally disallow rewriting `Cargo.lock`. In this case tracking
|
|
51
|
+ # will just read the existing file. If not used, then tracking
|
|
52
|
+ # will create `Cargo.lock`.
|
|
53
|
+ keep-lock: true
|
|
54
|
+"""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+import hashlib
|
|
58
|
+import os
|
|
59
|
+
|
|
60
|
+from buildstream import Consistency, Source, utils
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+class CargoSource(Source):
|
|
64
|
+
|
|
65
|
+ BST_REQUIRES_PREVIOUS_SOURCES_TRACK = True
|
|
66
|
+ BST_REQUIRES_PREVIOUS_SOURCES_FETCH = True
|
|
67
|
+
|
|
68
|
+ def configure(self, node):
|
|
69
|
+ self.node_validate(node, ['ref', 'subdir', 'keep-lock'] + Source.COMMON_CONFIG_KEYS)
|
|
70
|
+ self.ref = self.node_get_member(node, str, 'ref', None)
|
|
71
|
+ self.subdir = self.node_get_member(node, str, 'subdir', '.')
|
|
72
|
+ self.keeplock = self.node_get_member(node, bool, 'keep-lock', False)
|
|
73
|
+ self.extra_path = None
|
|
74
|
+
|
|
75
|
+ def preflight(self):
|
|
76
|
+ self.host_cargo = utils.get_host_tool('cargo')
|
|
77
|
+
|
|
78
|
+ try:
|
|
79
|
+ utils.get_host_tool('cargo-vendor')
|
|
80
|
+ except utils.ProgramNotFoundError:
|
|
81
|
+ cargo_home = os.environ.get('CARGO_HOME', os.path.expanduser('~/.cargo'))
|
|
82
|
+ self.extra_path = os.path.join(cargo_home, 'bin')
|
|
83
|
+
|
|
84
|
+ self.call([self.host_cargo, 'vendor', '-V'],
|
|
85
|
+ env=self._environment(),
|
|
86
|
+ fail='Cannot find "cargo vendor". Please install it with "cargo install cargo-vendor".')
|
|
87
|
+
|
|
88
|
+ def get_unique_key(self):
|
|
89
|
+ return [self.subdir, self.ref]
|
|
90
|
+
|
|
91
|
+ def get_ref(self):
|
|
92
|
+ return self.ref
|
|
93
|
+
|
|
94
|
+ def load_ref(self, node):
|
|
95
|
+ self.ref = self.node_get_member(node, str, 'ref', None)
|
|
96
|
+
|
|
97
|
+ def set_ref(self, ref, node):
|
|
98
|
+ node['ref'] = self.ref = ref
|
|
99
|
+
|
|
100
|
+ def _environment(self, *, set_home=False):
|
|
101
|
+ env = {}
|
|
102
|
+ env.update(os.environ)
|
|
103
|
+ if self.extra_path:
|
|
104
|
+ path = env.get('PATH', '').split(':')
|
|
105
|
+ path.append(self.extra_path)
|
|
106
|
+ env['PATH'] = ':'.join(path)
|
|
107
|
+ if set_home:
|
|
108
|
+ home = os.path.join(self.get_mirror_directory(), 'home')
|
|
109
|
+ os.makedirs(home, exist_ok=True)
|
|
110
|
+ env['CARGO_HOME'] = home
|
|
111
|
+ return env
|
|
112
|
+
|
|
113
|
+ def _get_manifest(self, directory):
|
|
114
|
+ projectdir = os.path.join(directory, self.subdir)
|
|
115
|
+ manifest = os.path.join(projectdir, 'Cargo.toml')
|
|
116
|
+ lockfile = os.path.join(projectdir, 'Cargo.lock')
|
|
117
|
+ return manifest, lockfile
|
|
118
|
+
|
|
119
|
+ def track(self, previous_sources_dir):
|
|
120
|
+ manifest, lockfile = self._get_manifest(previous_sources_dir)
|
|
121
|
+
|
|
122
|
+ if not self.keeplock:
|
|
123
|
+ self.call([self.host_cargo, 'generate-lockfile', '--manifest-path', manifest],
|
|
124
|
+ env=self._environment(set_home=True),
|
|
125
|
+ fail="Failed to track cargo packages")
|
|
126
|
+ try:
|
|
127
|
+ with open(lockfile, 'rb') as f:
|
|
128
|
+ lockcontent = f.read().decode('utf-8')
|
|
129
|
+ except OSError as e:
|
|
130
|
+ if self.keeplock and e.errno == errno.ENOENT:
|
|
131
|
+ raise SourceError("{}: Cannot find Cargo.lock".format(self))
|
|
132
|
+ else:
|
|
133
|
+ raise
|
|
134
|
+
|
|
135
|
+ return lockcontent
|
|
136
|
+
|
|
137
|
+ def _get_stamp(self):
|
|
138
|
+ h = hashlib.sha256()
|
|
139
|
+ h.update(self.get_ref().encode('utf-8'))
|
|
140
|
+ return os.path.join(self.get_mirror_directory(), 'stamps', h.hexdigest())
|
|
141
|
+
|
|
142
|
+ def get_consistency(self):
|
|
143
|
+ if not self.ref:
|
|
144
|
+ return Consistency.INCONSISTENT
|
|
145
|
+ if os.path.exists(self._get_stamp()):
|
|
146
|
+ return Consistency.CACHED
|
|
147
|
+ return Consistency.RESOLVED
|
|
148
|
+
|
|
149
|
+ def fetch(self, previous_sources_dir):
|
|
150
|
+ manifest, lockfile = self._get_manifest(previous_sources_dir)
|
|
151
|
+ if not self.keeplock:
|
|
152
|
+ with open(lockfile, 'wb') as f:
|
|
153
|
+ f.write(self.get_ref().encode('utf-8'))
|
|
154
|
+
|
|
155
|
+ self.call([self.host_cargo, 'fetch', '--manifest-path', manifest, '--locked'],
|
|
156
|
+ env=self._environment(set_home=True),
|
|
157
|
+ fail="Failed to fetch cargo packages")
|
|
158
|
+ stamp = self._get_stamp()
|
|
159
|
+ os.makedirs(os.path.dirname(stamp), exist_ok=True)
|
|
160
|
+ with open(stamp, 'w'):
|
|
161
|
+ pass
|
|
162
|
+
|
|
163
|
+ def stage(self, directory):
|
|
164
|
+ manifest, lockfile = self._get_manifest(directory)
|
|
165
|
+ if not self.keeplock:
|
|
166
|
+ with open(lockfile, 'wb') as f:
|
|
167
|
+ f.write(self.ref.encode('utf-8'))
|
|
168
|
+
|
|
169
|
+ config = os.path.join(os.path.dirname(manifest), '.cargo', 'config')
|
|
170
|
+ os.makedirs(os.path.dirname(config), exist_ok=True)
|
|
171
|
+
|
|
172
|
+ vendordir = os.path.join(directory, 'vendor')
|
|
173
|
+ relvendordir = os.path.relpath(vendordir, os.path.dirname(manifest))
|
|
174
|
+
|
|
175
|
+ with utils.save_file_atomic(config, 'wb') as f:
|
|
176
|
+ self.call([self.host_cargo, 'vendor', '--frozen', '--relative-path', relvendordir],
|
|
177
|
+ env=self._environment(set_home=True),
|
|
178
|
+ cwd=os.path.dirname(manifest),
|
|
179
|
+ stdout=f,
|
|
180
|
+ fail="Failed to stage cargo packages")
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+def setup():
|
|
184
|
+ return CargoSource
|