Skip to content

Commit f1d7cba

Browse files
authored
Merge pull request #613 from jkloetzke/url-improvements
Url SCM improvements
2 parents f36400b + 4f15722 commit f1d7cba

File tree

6 files changed

+87
-57
lines changed

6 files changed

+87
-57
lines changed

doc/manual/configuration.rst

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,17 +1152,22 @@ url
11521152
is performed. This replaces the initial ``~`` or ``~user`` by the *user*’s
11531153
home directory.
11541154

1155-
If a SHA digest is
1156-
given with ``digestSHA1``, ``digestSHA256`` and/or ``digestSHA512``, the
1157-
downloaded file will be checked for a matching hash sum. This also makes the
1158-
URL deterministic for Bob. Otherwise the URL will be checked in each build
1159-
for updates. Based on the file name ending, Bob will try to extract the
1160-
downloaded file. You may prevent this by setting the ``extract`` attribute
1161-
to ``no`` or ``False``. If the heuristic fails, the extraction tool may be
1162-
specified as ``tar``, ``gzip``, ``xz``, ``7z`` or ``zip`` directly. For
1163-
``tar`` files it is possible to strip a configurable number of leading
1164-
components from file names on extraction by the ``stripComponents``
1165-
attribute.
1155+
If a SHA digest is given with ``digestSHA1``, ``digestSHA256`` and/or
1156+
``digestSHA512``, the downloaded file will be checked for a matching hash
1157+
sum. This also makes the URL deterministic for Bob. Otherwise, the URL will
1158+
be checked in each build for updates.
1159+
1160+
Based on the file name ending, Bob will try to extract the downloaded file.
1161+
You may prevent this by setting the ``extract`` attribute to ``no`` or
1162+
``False``. If the heuristic fails, the extraction tool may be specified as
1163+
``tar``, ``gzip``, ``xz``, ``7z`` or ``zip`` directly. For ``tar`` files it
1164+
is possible to strip a configurable number of leading components from file
1165+
names on extraction by the ``stripComponents`` attribute.
1166+
1167+
If the file is extracted, the original file will be kept next to the
1168+
``workspace`` to keep the directory clean. If the
1169+
:ref:`policies-urlScmSeparateDownload` policy is on the old behavour, the
1170+
downloaded file will still be in put the workspace, though.
11661171

11671172
.. note::
11681173
Starting with Bob 0.14 (see :ref:`policies-tidyUrlScm` policy) the whole

doc/manual/policies.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,13 +344,15 @@ New behavior
344344

345345
Unmanaged layers are expected in the same directory.
346346

347+
.. _policies-urlScmSeparateDownload:
348+
347349
urlScmSeparateDownload
348350
~~~~~~~~~~~~~~~~~~~~~~
349351

350352
Introduced in: 1.0
351353

352-
This policy controls where bob places downloaded files of UrlScms if extraction is
353-
used.
354+
This policy controls where Bob places downloaded files of ``url`` SCMs if
355+
extraction is used.
354356

355357
Old behavior
356358
The downloaded file could be found in the workspace next to the extracted files.

pym/bob/invoker.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,21 +192,27 @@ async def __runCommand(self, args, cwd, stdout=None, stderr=None,
192192
elif stdout == False:
193193
stdoutRedir = subprocess.DEVNULL
194194
stdoutStreams = []
195-
else:
195+
elif stdout is None:
196196
stdoutRedir = self.__stdout
197197
stdoutStreams = [self.__stdoutStream]
198+
else:
199+
stdoutRedir = stdout
200+
stdoutStreams = []
198201

199202
if stderr == True:
200203
# If stderr should be captured we use a dedicated buffer. This
201204
# buffer is then returned to the caller at child exit.
202205
stderrRedir = subprocess.PIPE
203206
stderrStreams = [io.BytesIO(), self.__stderrStream]
204-
elif stdout == False:
207+
elif stderr == False:
205208
stderrRedir = subprocess.DEVNULL
206209
stderrStreams = []
207-
else:
210+
elif stderr is None:
208211
stderrRedir = self.__stderr
209212
stderrStreams = [self.__stderrStream]
213+
else:
214+
stderrRedir = stderr
215+
stderrStreams = []
210216

211217
# Sanity check on Windows that there are no environment variables that
212218
# differ only in case. The Windows envrionment is used case insensitive

pym/bob/scm/url.py

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,23 @@ def dumpMode(mode):
158158

159159

160160
class Extractor():
161+
SUPPORT_STRIP = False
162+
161163
def __init__(self, dir, file, strip, separateDownload):
164+
if strip != 0 and not self.SUPPORT_STRIP:
165+
raise BuildError("Extractor does not support 'stripComponents'!")
162166
self.dir = dir
163167
self.file = file
164168
self.strip = strip
165169
self.separateDownload = separateDownload
166170

167-
async def _extract(self, cmds, invoker):
171+
async def _extract(self, cmds, invoker, stdout=None):
168172
destination = self.getCompressedFilePath(invoker)
169173
canary = destination+".extracted"
170174
if isYounger(destination, canary):
171175
for cmd in cmds:
172176
if shutil.which(cmd[0]) is None: continue
173-
await invoker.checkCommand(cmd, cwd=self.dir)
177+
await invoker.checkCommand(cmd, cwd=self.dir, stdout=stdout)
174178
invoker.trace("<touch>", canary)
175179
with open(canary, "wb") as f:
176180
pass
@@ -191,8 +195,7 @@ async def extract(self, invoker, destination, cwd):
191195
# case of tarfile broken in certain ways (e.g. tarfile will result in
192196
# different file modes!). But it shouldn't make a difference on Windows.
193197
class TarExtractor(Extractor):
194-
def __init__(self, dir, file, strip, separateDownload):
195-
super().__init__(dir, file, strip, separateDownload)
198+
SUPPORT_STRIP = True
196199

197200
async def extract(self, invoker):
198201
cmds = []
@@ -210,10 +213,6 @@ async def extract(self, invoker):
210213

211214

212215
class ZipExtractor(Extractor):
213-
def __init__(self, dir, file, strip, separateDownload):
214-
super().__init__(dir, file, strip, separateDownload)
215-
if strip != 0:
216-
raise BuildError("Extractor does not support 'stripComponents'!")
217216

218217
async def extract(self, invoker):
219218
cmds = []
@@ -226,47 +225,60 @@ async def extract(self, invoker):
226225
await self._extract(cmds, invoker)
227226

228227

229-
class GZipExtractor(Extractor):
230-
def __init__(self, dir, file, strip, separateDownload):
231-
super().__init__(dir, file, strip, separateDownload)
232-
if strip != 0:
233-
raise BuildError("Extractor does not support 'stripComponents'!")
228+
class SingleFileExtractor(Extractor):
234229

235230
async def extract(self, invoker):
236-
# gunzip extracts the file at the location of the input file. Copy the
237-
# downloaded file to the workspace directory prio to uncompressing it
238-
cmd = ["gunzip"]
239-
if self.separateDownload:
240-
shutil.copyfile(self.getCompressedFilePath(invoker),
241-
invoker.joinPath(self.dir, self.file))
231+
# The gunzip and unxz tools extracts the file at the location of the
232+
# input file. In case the separateDownload policy is active, the
233+
# destination might be in a separete folder.
234+
src = self.getCompressedFilePath(invoker)
235+
dst = invoker.joinPath(self.dir, self.file)
236+
237+
suffix = dst[-4:]
238+
if self.EXT_IGNORE_CASE:
239+
suffix = suffix.lower()
240+
241+
for ext, repl in self.EXT_MAP.items():
242+
if suffix.endswith(ext):
243+
dst = dst[:-len(ext)] + repl
244+
break
242245
else:
243-
cmd.append("-k")
244-
cmd.extend(["-f", self.file])
245-
await self._extract([cmd], invoker)
246-
246+
raise BuildError("unkown suffix")
247+
248+
with open(dst, 'wb') as f:
249+
cmd = [self.CMD, "-c", src]
250+
await self._extract([cmd], invoker, f)
251+
252+
shutil.copystat(src, dst)
253+
254+
255+
class GZipExtractor(SingleFileExtractor):
256+
CMD = "gunzip"
257+
EXT_IGNORE_CASE = True
258+
EXT_MAP = {
259+
".gz" : "",
260+
"-gz" : "",
261+
".z" : "",
262+
"-z" : "",
263+
"_z" : "",
264+
".tgz" : ".tar",
265+
".taz" : ".tar",
266+
}
247267

248-
class XZExtractor(Extractor):
249-
def __init__(self, dir, file, strip, separateDownload):
250-
super().__init__(dir, file, strip, separateDownload)
251-
if strip != 0:
252-
raise BuildError("Extractor does not support 'stripComponents'!")
253268

254-
async def extract(self, invoker):
255-
cmd = ["unxz"]
256-
if self.separateDownload:
257-
shutil.copyfile(self.getCompressedFilePath(invoker),
258-
invoker.joinPath(self.dir, self.file))
259-
else:
260-
cmd.append("-k")
261-
cmd.extend(["-f", self.file])
262-
await self._extract([cmd], invoker)
269+
class XZExtractor(SingleFileExtractor):
270+
CMD = "unxz"
271+
EXT_IGNORE_CASE = False
272+
EXT_MAP = {
273+
".xz" : "",
274+
".lzma" : "",
275+
".lz" : "",
276+
".txz" : ".tar",
277+
".tlz" : ".tar",
278+
}
263279

264280

265281
class SevenZipExtractor(Extractor):
266-
def __init__(self, dir, file, strip, separateDownload):
267-
super().__init__(dir, file, strip, separateDownload)
268-
if strip != 0:
269-
raise BuildError("Extractor does not support 'stripComponents'!")
270282

271283
async def extract(self, invoker):
272284
cmds = [["7z", "x", "-y", self.getCompressedFilePath(invoker)]]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
bobMinimumVersion: "0.25"
12
policies:
23
urlScmSeparateDownload: True

test/black-box/extractors/recipes/extract_test.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ buildScript: |
4040
for d in $(find $1/ -mindepth 1 -type d); do
4141
pushd $d
4242
sha256sum -c ${SHA256_FILE}
43+
if [[ $d = */gzip || $d = */xz ]] ; then
44+
# verify that extracted file retains mode
45+
test $(stat -c "%a" test.dat) = "600"
46+
fi
4347
popd
4448
done
4549

0 commit comments

Comments
 (0)