Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/planemo/shed2tap/base.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 from __future__ import print_function | |
2 | |
3 import os | |
4 import subprocess | |
5 import sys | |
6 import tarfile | |
7 import zipfile | |
8 from ftplib import all_errors as FTPErrors # tuple of exceptions | |
9 from typing import List | |
10 from xml.etree import ElementTree | |
11 | |
12 from galaxy.util import unicodify | |
13 from six import iteritems | |
14 from six import string_types | |
15 from six.moves import map as imap | |
16 from six.moves.urllib.error import URLError | |
17 from six.moves.urllib.request import urlretrieve | |
18 | |
19 | |
20 TOOLSHED_MAP = { | |
21 "toolshed": "https://toolshed.g2.bx.psu.edu", | |
22 "testtoolshed": "https://testtoolshed.g2.bx.psu.edu", | |
23 } | |
24 | |
25 | |
26 class Dependencies(object): | |
27 """ Base class for parsing Tool Shed dependency files. | |
28 """ | |
29 | |
30 def __init__( | |
31 self, | |
32 dependencies_file, | |
33 repo=None, | |
34 package_factory=None, | |
35 ): | |
36 if package_factory is None: | |
37 package_factory = BasePackage | |
38 self.repo = repo | |
39 self.root = ElementTree.parse(dependencies_file).getroot() | |
40 packages = [] | |
41 dependencies = [] | |
42 package_els = self.root.findall("package") | |
43 assert package_els is not None | |
44 for package_el in package_els: | |
45 install_els = package_el.findall("install") | |
46 readme_els = package_el.findall("readme") | |
47 if len(readme_els) > 0: | |
48 readme = readme_els[0].text | |
49 else: | |
50 readme = None | |
51 assert len(install_els) in (0, 1) | |
52 if len(install_els) == 1: | |
53 install_el = install_els[0] | |
54 package = package_factory( | |
55 self, | |
56 package_el, | |
57 install_el, | |
58 readme=readme | |
59 ) | |
60 packages.append(package) | |
61 else: | |
62 repository_el = package_el.find("repository") | |
63 if repository_el is None: | |
64 message = "no repository in package el for %s" % repo | |
65 raise AssertionError(message) | |
66 dependency = Dependency(self, package_el, repository_el) | |
67 dependencies.append(dependency) | |
68 | |
69 self.packages = packages | |
70 self.dependencies = dependencies | |
71 | |
72 def single_package(self): | |
73 return len(self.packages) == 1 | |
74 | |
75 def __repr__(self): | |
76 return "Dependencies[for_repo=%s]" % self.repo | |
77 | |
78 | |
79 class Repo(object): | |
80 | |
81 def __init__(self, **kwds): | |
82 for key, value in iteritems(kwds): | |
83 setattr(self, key, value) | |
84 | |
85 def recipe_base_name(self): | |
86 owner = self.owner.replace("-", "") | |
87 name = self.name | |
88 name = name.replace("_", "").replace("-", "") | |
89 base = "%s_%s" % (owner, name) | |
90 return base | |
91 | |
92 @staticmethod | |
93 def from_xml(elem): | |
94 tool_shed_url = elem.attrib.get("toolshed", None) | |
95 if tool_shed_url and ("testtoolshed" in tool_shed_url): | |
96 prefix = "testtoolshed" | |
97 else: | |
98 prefix = "toolshed" | |
99 prior = elem.attrib.get("prior_installation_required", False) | |
100 return Repo( | |
101 prefix=prefix, | |
102 name=elem.attrib["name"], | |
103 owner=elem.attrib["owner"], | |
104 tool_shed_url=tool_shed_url, | |
105 changeset_revision=elem.attrib.get("changeset_revision", None), | |
106 prior_installation_required=prior, | |
107 ) | |
108 | |
109 @staticmethod | |
110 def from_api(prefix, repo_json): | |
111 return Repo( | |
112 prefix=prefix, | |
113 name=repo_json["name"], | |
114 owner=repo_json["owner"], | |
115 tool_shed_url=TOOLSHED_MAP[prefix], | |
116 ) | |
117 | |
118 def get_file(self, path): | |
119 try: | |
120 url_template = "%s/repos/%s/%s/raw-file/tip/%s" | |
121 url = url_template % ( | |
122 self.tool_shed_url, | |
123 self.owner, | |
124 self.name, | |
125 path | |
126 ) | |
127 path, headers = urlretrieve(url) | |
128 return path | |
129 except Exception as e: | |
130 print(e) | |
131 return None | |
132 | |
133 def __repr__(self): | |
134 return "Repository[name=%s,owner=%s]" % (self.name, self.owner) | |
135 | |
136 | |
137 class Dependency(object): | |
138 | |
139 def __init__(self, dependencies, package_el, repository_el): | |
140 self.dependencies = dependencies | |
141 self.package_el = package_el | |
142 self.repository_el = repository_el | |
143 self.repo = Repo.from_xml(repository_el) | |
144 | |
145 def __repr__(self): | |
146 temp = "Dependency[package_name=%s,version=%s,dependent_package=%s]" | |
147 return temp % ( | |
148 self.package_el.attrib["name"], | |
149 self.package_el.attrib["version"], | |
150 self.repository_el.attrib["name"] | |
151 ) | |
152 | |
153 | |
154 class BasePackage(object): | |
155 | |
156 def __init__(self, dependencies, package_el, install_el, readme): | |
157 self.dependencies = dependencies | |
158 self.package_el = package_el | |
159 self.install_el = install_el | |
160 self.readme = readme | |
161 self.all_actions = self.get_all_actions() | |
162 self.no_arch_option = self.has_no_achitecture_install() | |
163 | |
164 def get_all_actions(self): | |
165 action_or_group = self.install_el[0] | |
166 parsed_actions = [] | |
167 if action_or_group.tag == "actions": | |
168 parsed_actions.append(self.parse_actions(action_or_group)) | |
169 elif action_or_group.tag == "actions_group": | |
170 actions_els = action_or_group.findall("actions") | |
171 assert actions_els is not None | |
172 for actions in actions_els: | |
173 parsed_actions.append(self.parse_actions(actions)) | |
174 action_els = action_or_group.findall("action") | |
175 assert action_els is not None | |
176 for action in action_els: | |
177 for parsed_a in parsed_actions: | |
178 parsed_a.actions.append(self.parse_action(action)) | |
179 return parsed_actions | |
180 | |
181 def has_no_achitecture_install(self): | |
182 all_actions = self.all_actions | |
183 if len(all_actions) < 2: | |
184 return False | |
185 else: | |
186 last_action = all_actions[-1] | |
187 return (not last_action.architecture) and (not last_action.os) | |
188 | |
189 def has_explicit_set_environments(self): | |
190 all_actions = self.all_actions | |
191 for actions in all_actions: | |
192 for action in actions.actions: | |
193 if action.explicit_variables: | |
194 return True | |
195 return False | |
196 | |
197 def has_multiple_set_environments(self): | |
198 all_actions = self.all_actions | |
199 for actions in all_actions: | |
200 count = 0 | |
201 for action in actions.actions: | |
202 if action.explicit_variables: | |
203 count += 1 | |
204 if count > 1: | |
205 return True | |
206 return False | |
207 | |
208 def parse_actions(self, actions): | |
209 os = actions.attrib.get("os", None) | |
210 architecture = actions.get("architecture", None) | |
211 action_els = actions.findall("action") | |
212 assert action_els is not None | |
213 parsed_actions = list(imap(self.parse_action, action_els)) | |
214 action_packages = [] | |
215 for package in actions.findall("package"): | |
216 action_packages.append(self.parse_action_package(package)) | |
217 return Actions(parsed_actions, os, architecture, action_packages) | |
218 | |
219 def parse_action_package(self, elem): | |
220 name = elem.attrib["name"] | |
221 version = elem.attrib["version"] | |
222 repo = Repo.from_xml(elem.find("repository")) | |
223 return ActionPackage(name, version, repo) | |
224 | |
225 def parse_action(self, action): | |
226 return BaseAction.from_elem(action, package=self) | |
227 | |
228 def __repr__(self): | |
229 actions = self.all_actions | |
230 parts = ( | |
231 self.package_el.attrib["name"], | |
232 self.package_el.attrib["version"], | |
233 self.dependencies, | |
234 actions | |
235 ) | |
236 template = "Install[name=%s,version=%s,dependencies=%s,actions=%s]" | |
237 return template % parts | |
238 | |
239 | |
240 class Actions(object): | |
241 | |
242 def __init__( | |
243 self, | |
244 actions, | |
245 os=None, | |
246 architecture=None, | |
247 action_packages=[] | |
248 ): | |
249 self.os = os | |
250 self.architecture = architecture | |
251 self.actions = actions or [] | |
252 self.action_packages = action_packages | |
253 | |
254 def first_download(self): | |
255 for action in self.actions: | |
256 if action.action_type in ["download_by_url", "download_file"]: | |
257 return action | |
258 return None | |
259 | |
260 def downloads(self): | |
261 actions = [] | |
262 for action in self.actions: | |
263 if action.action_type in ["download_by_url", "download_file"]: | |
264 actions.append(action) | |
265 return actions | |
266 | |
267 def __repr__(self): | |
268 platform = "" | |
269 if self.os or self.architecture: | |
270 platform = "os=%s,arch=%s," % (self.os, self.architecture) | |
271 return "Actions[%s%s]" % (platform, map(str, self.actions)) | |
272 | |
273 def _indent_extend(self, target, new_entries, indent=" "): | |
274 for line in new_entries: | |
275 target.append(indent + line) | |
276 | |
277 def to_bash(self): | |
278 # Use self.os.title() to match "Linux" or "Darwin" in bash where case matters: | |
279 if self.os and self.architecture: | |
280 condition = '("%s" == `uname`) && ("%s" == `arch`)' % (self.os.title(), self.architecture) | |
281 elif self.os: | |
282 condition = '"%s" == `uname`' % self.os.title() | |
283 elif self.architecture: | |
284 condition = '"%s" == `arch`' % self.architecture | |
285 else: | |
286 condition = None | |
287 | |
288 install_cmds = [] | |
289 env_cmds = [] | |
290 | |
291 if condition: | |
292 # Conditional actions block | |
293 install_cmds = [ | |
294 '#' + '-' * 60, | |
295 'if [[ $specifc_action_done == 0 && %s ]]' % condition, | |
296 'then', | |
297 ' echo "Platform-specific action for os=%s, arch=%s"' % (self.os, self.architecture)] | |
298 env_cmds = install_cmds[:] | |
299 # TODO - Refactor block indentation? | |
300 for action in self.actions: | |
301 i_cmds, e_cmds = action.to_bash() | |
302 self._indent_extend(install_cmds, i_cmds) | |
303 self._indent_extend(env_cmds, e_cmds) | |
304 # If we run the action, do not want to run any later actions! | |
305 install_cmds.extend([' specifc_action_done=1', 'fi']) | |
306 env_cmds.extend([' specifc_action_done=1', 'fi']) | |
307 else: | |
308 # Non-specific default action... | |
309 install_cmds = [ | |
310 '#' + '-' * 60, | |
311 'if [[ $specifc_action_done == 0 ]]', | |
312 'then', | |
313 ' echo "Non-platform-specific actions"'] | |
314 env_cmds = install_cmds[:] | |
315 for action in self.actions: | |
316 i_cmds, e_cmds = action.to_bash() | |
317 self._indent_extend(install_cmds, i_cmds) | |
318 self._indent_extend(env_cmds, e_cmds) | |
319 install_cmds.append('fi') | |
320 env_cmds.append('fi') | |
321 return install_cmds, env_cmds | |
322 | |
323 | |
324 class ActionPackage(object): | |
325 | |
326 def __init__(self, name, version, repo): | |
327 self.name = name | |
328 self.version = version | |
329 self.repo = repo | |
330 | |
331 | |
332 class BaseAction(object): | |
333 _keys = [] # type: List[str] | |
334 action_type = None # type: str | |
335 | |
336 def __repr__(self): | |
337 return "Action[type=%s]" % self.action_type | |
338 | |
339 def same_as(self, other): | |
340 if self._keys != other._keys: | |
341 return False | |
342 else: | |
343 for key in self._keys: | |
344 if getattr(self, key) != getattr(other, key): | |
345 return False | |
346 | |
347 return True | |
348 | |
349 def parse_action_repo(self, elem): | |
350 repo_elem = elem.find("repository") | |
351 repo = Repo.from_xml(repo_elem) | |
352 self.repo = repo | |
353 | |
354 def parse_package_elems(self, elem): | |
355 package_els = elem.findall("package") | |
356 packages = [] | |
357 assert package_els is not None | |
358 for package_el in package_els: | |
359 packages.append(package_el.text) | |
360 self.packages = packages | |
361 | |
362 @classmethod | |
363 def from_elem(cls, elem, package): | |
364 type = elem.attrib["type"] | |
365 action_class = actions_by_type[type] | |
366 return action_class(elem) | |
367 | |
368 def to_bash(self): | |
369 """Return lists of bash shell commands to execute this action. | |
370 | |
371 This method is be implemented by each sub-class, and will | |
372 return two list of strings (for ``dep_install.sh`` and | |
373 ``env.sh`` respectively). | |
374 """ | |
375 raise NotImplementedError("No to_bash defined for %r" % self) | |
376 | |
377 | |
378 def _tar_folders(filename): | |
379 with tarfile.open(filename, "r", errorlevel=0) as archive: | |
380 folders = set() | |
381 for i in archive.getmembers(): | |
382 if i.isdir(): | |
383 folders.add(i.name.rstrip("/")) | |
384 else: | |
385 folders.add(os.path.split(i.name)[0]) | |
386 return list(folders) | |
387 | |
388 | |
389 def _zip_folders(filename): | |
390 archive = zipfile.ZipFile(filename, "r") | |
391 return list(set(i.filename.rstrip("/") for i in archive.infolist() if i.filename.endswith("/"))) | |
392 | |
393 | |
394 def _common_prefix(folders): | |
395 common_prefix = "" | |
396 if len(folders) == 1: | |
397 common_prefix = list(folders)[0] | |
398 else: | |
399 common_prefix = os.path.commonprefix(folders) | |
400 assert not os.path.isabs(common_prefix), folders | |
401 return common_prefix | |
402 | |
403 | |
404 def _cache_download(url, filename, sha256sum=None): | |
405 """Returns local path to cached copy of URL using given filename.""" | |
406 cache = os.environ.get("DOWNLOAD_CACHE", "./download_cache/") | |
407 # TODO - expose this as a command line option | |
408 | |
409 if not os.path.isdir(cache): | |
410 os.mkdir(cache) | |
411 | |
412 local = os.path.join(cache, filename) | |
413 | |
414 if not os.path.isfile(local): | |
415 # Must download it... | |
416 try: | |
417 # TODO - log this nicely... | |
418 sys.stderr.write("Downloading %s to %r\n" % (url, local)) | |
419 urlretrieve(url, local) | |
420 except URLError: | |
421 # Most likely server is down, could be bad URL in XML action: | |
422 raise RuntimeError("Unable to download %s" % url) | |
423 except FTPErrors: | |
424 # Most likely server is down, could be bad URL in XML action: | |
425 raise RuntimeError("Unable to download %s" % url) | |
426 | |
427 # Verifying the checksum is slow, only do this on a fresh | |
428 # download. Assume locally cached files are already OK. | |
429 if sha256sum: | |
430 # TODO - log this nicely... | |
431 sys.stderr.write("Verifying checksum for %s\n" % filename) | |
432 filehash = subprocess.check_output(['shasum', '-a', '256', local])[0:64].strip() | |
433 filehash = unicodify(filehash) | |
434 if filehash != sha256sum: | |
435 raise RuntimeError("Checksum failure for %s, got %r but wanted %r" % (local, filehash, sha256sum)) | |
436 | |
437 return local | |
438 | |
439 | |
440 def _determine_compressed_file_folder(url, downloaded_filename, target_filename=None, sha256sum=None): | |
441 """Determine how to decompress the file & its directory structure. | |
442 | |
443 Returns a list of shell commands. Consider this example where the | |
444 folder to change to cannot be guessed from the tar-ball filename: | |
445 | |
446 $ curl -o "ncbi-blast-2.2.30+-ia32-linux.tar.gz" \ | |
447 "ftp://ftp.ncbi.nlm.nih.gov/blast/executables/blast+/2.2.30/ncbi-blast-2.2.30+-ia32-linux.tar.gz" | |
448 $ tar -zxvf ncbi-blast-2.2.30+-ia32-linux.tar.gz | |
449 $ cd ncbi-blast-2.2.30+ | |
450 | |
451 Here it would return: | |
452 | |
453 ['tar -zxvf ncbi-blast-2.2.30+-ia32-linux.tar.gz', 'cd ncbi-blast-2.2.30+'] | |
454 | |
455 If not cached, this function will download the file to the | |
456 $DOWNLOAD_CACHE folder, and then open it / decompress it in | |
457 order to find common folder prefix used. This will also verify | |
458 how to decompress the file, and the checksum if given. | |
459 """ | |
460 answer = [] | |
461 | |
462 local = _cache_download(url, downloaded_filename, sha256sum) | |
463 | |
464 if not target_filename: | |
465 target_filename = downloaded_filename | |
466 | |
467 if tarfile.is_tarfile(local): | |
468 folders = _tar_folders(local) | |
469 if target_filename.endswith((".tar.gz", ".tgz")): | |
470 answer.append('tar -zxvf %s' % target_filename) | |
471 elif target_filename.endswith(".tar.bz2"): | |
472 answer.append('tar -jxvf %s' % target_filename) | |
473 elif target_filename.endswith(".tar"): | |
474 answer.extend('tar -xvf %s' % target_filename) | |
475 else: | |
476 # Quite possibly this file doesn't need decompressing, | |
477 # but until we've tested lots of real world tool_dependencies.xml | |
478 # files I'd like to check these cases to confirm this. | |
479 raise NotImplementedError("How to decompress tar file %s?" % target_filename) | |
480 elif zipfile.is_zipfile(local): | |
481 if target_filename.endswith(".jar"): | |
482 # Do not decompress! | |
483 return answer | |
484 folders = _zip_folders(local) | |
485 answer.append('unzip %s' % target_filename) | |
486 elif target_filename.endswith(".dmg"): | |
487 # Do not decompress! | |
488 return answer | |
489 else: | |
490 # No compression? Leave as it is? | |
491 raise NotImplementedError("What kind of compression is %s using?" % local) | |
492 | |
493 common_prefix = _common_prefix(folders) | |
494 if common_prefix: | |
495 answer.append('cd "%s"' % common_prefix) | |
496 | |
497 return answer | |
498 | |
499 | |
500 def _commands_and_downloaded_file(url, target_filename=None, sha256sum=None): | |
501 # We preserve the filename from the URL in the cache. | |
502 # i.e. We do NOT use the target_filename in the cache. | |
503 # This because some Galaxy recipes normalise platform specific downloads | |
504 # to use a single target filename, which would therefore break checksums etc | |
505 # e.g. tests/data/repos/package_1/tool_dependencies.xml | |
506 downloaded_filename = os.path.split(url)[-1] | |
507 if "?" in downloaded_filename: | |
508 downloaded_filename = downloaded_filename[:downloaded_filename.index("?")] | |
509 if "#" in downloaded_filename: | |
510 downloaded_filename = downloaded_filename[:downloaded_filename.index("#")] | |
511 | |
512 if not target_filename: | |
513 target_filename = downloaded_filename | |
514 | |
515 # Curl is present on Mac OS X, can we assume it will be on Linux? | |
516 # Cannot assume that wget will be on Mac OS X. | |
517 answer = [ | |
518 'if [[ -f "%s" ]]' % target_filename, | |
519 'then', | |
520 ' echo "Reusing existing %s"' % target_filename, | |
521 'elif [[ -f "$DOWNLOAD_CACHE/%s" ]]' % downloaded_filename, | |
522 'then', | |
523 ' echo "Reusing cached %s"' % downloaded_filename, | |
524 ' cp "$DOWNLOAD_CACHE/%s" "%s"' % (downloaded_filename, target_filename), | |
525 'else', | |
526 ' echo "Downloading %s"' % downloaded_filename, | |
527 ' curl -L -o "$DOWNLOAD_CACHE/%s" "%s"' % (downloaded_filename, url), | |
528 ' cp "$DOWNLOAD_CACHE/%s" "%s"' % (downloaded_filename, target_filename), | |
529 ] | |
530 if sha256sum: | |
531 # This is inserted into the if-else for a fresh download only. | |
532 # Note double space between checksum and filename: | |
533 answer.append(' echo "%s %s" | shasum -a 256 -c -' % (sha256sum, target_filename)) | |
534 answer.append('fi') | |
535 | |
536 return answer, downloaded_filename | |
537 | |
538 | |
539 def _commands_to_download_and_extract(url, target_filename=None, sha256sum=None): | |
540 answer, downloaded_filename = _commands_and_downloaded_file(url, target_filename, sha256sum) | |
541 # Now should we unpack the tar-ball etc? | |
542 answer.extend(_determine_compressed_file_folder(url, downloaded_filename, target_filename, sha256sum)) | |
543 return answer, [] | |
544 | |
545 | |
546 class DownloadByUrlAction(BaseAction): | |
547 action_type = "download_by_url" | |
548 _keys = ["url"] | |
549 | |
550 def __init__(self, elem): | |
551 self.url = elem.text.strip() | |
552 assert self.url | |
553 self.sha256sum = elem.attrib.get("sha256sum", None) | |
554 self.target_filename = elem.attrib.get("target_filename", None) | |
555 | |
556 def to_bash(self): | |
557 # See class DownloadByUrl in Galaxy, | |
558 # lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py | |
559 return _commands_to_download_and_extract(self.url, self.target_filename, self.sha256sum) | |
560 | |
561 | |
562 class DownloadFileAction(BaseAction): | |
563 action_type = "download_file" | |
564 _keys = ["url", "extract"] | |
565 | |
566 def __init__(self, elem): | |
567 self.url = elem.text.strip() | |
568 self.extract = asbool(elem.attrib.get("extract", False)) | |
569 self.sha256sum = elem.attrib.get("sha256sum", None) | |
570 self.target_filename = elem.attrib.get("target_filename", None) | |
571 | |
572 def to_bash(self): | |
573 if self.extract: | |
574 return _commands_to_download_and_extract(self.url, self.target_filename, self.sha256sum) | |
575 else: | |
576 commands, downloaded_file = _commands_and_downloaded_file(self.url, self.target_filename, self.sha256sum) | |
577 return commands, [] | |
578 | |
579 | |
580 class DownloadBinary(BaseAction): | |
581 action_type = "download_binary" | |
582 _keys = ["url_template", "target_directory"] | |
583 | |
584 def __init__(self, elem): | |
585 self.url_template = elem.text | |
586 assert self.url_template | |
587 self.target_directory = elem.get('target_directory', None) | |
588 | |
589 | |
590 class ShellCommandAction(BaseAction): | |
591 action_type = "shell_command" | |
592 _keys = ["command"] | |
593 | |
594 def __init__(self, elem): | |
595 self.command = elem.text | |
596 | |
597 def to_bash(self): | |
598 # Galaxy would run each action from the same temp | |
599 # working directory - possible that tool_dependencies.xml | |
600 # shell_command could change $PWD so reset this: | |
601 return ["pushd . > /dev/null", self.command, "popd > /dev/null"], [] | |
602 | |
603 | |
604 class TemplateShellCommandAction(BaseAction): | |
605 action_type = "template_command" | |
606 _keys = ["language", "command"] | |
607 | |
608 def __init__(self, elem): | |
609 self.command = elem.text | |
610 self.language = elem.get('language', 'cheetah').lower() | |
611 assert self.command | |
612 assert self.language == "cheetah" | |
613 | |
614 | |
615 class MoveFileAction(BaseAction): | |
616 action_type = "move_file" | |
617 _keys = ["move_file"] | |
618 | |
619 def __init__(self, elem): | |
620 self.source = elem.find("source").text | |
621 self.destination = elem.find("destination").text | |
622 | |
623 def to_bash(self): | |
624 return ["mv %s %s" % (self.source, self.destination)], [] | |
625 | |
626 | |
627 class MoveDirectoryFilesAction(BaseAction): | |
628 action_type = "move_directory_files" | |
629 _keys = ["source_directory", "destination_directory"] | |
630 | |
631 def __init__(self, elem): | |
632 source_directory = elem.find("source_directory").text | |
633 destination_directory = elem.find("destination_directory").text | |
634 self.source_directory = source_directory | |
635 self.destination_directory = destination_directory | |
636 | |
637 def to_bash(self): | |
638 return ["mv %s/* %s/" % (self.source_directory, self.destination_directory)], [] | |
639 | |
640 | |
641 class SetEnvironmentAction(BaseAction): | |
642 action_type = "set_environment" | |
643 _keys = ["variables"] | |
644 | |
645 def __init__(self, elem): | |
646 variables = [] | |
647 var_els = elem.findall("environment_variable") | |
648 assert var_els is not None | |
649 for ev_elem in var_els: | |
650 var = SetVariable(ev_elem) | |
651 variables.append(var) | |
652 self.variables = variables | |
653 assert self.variables | |
654 | |
655 def to_bash(self): | |
656 answer = [] | |
657 for var in self.variables: | |
658 # Expand $INSTALL_DIR here? | |
659 if var.action == "set_to": | |
660 answer.append('export %s=%s' % (var.name, var.raw_value)) | |
661 elif var.action == "prepend_to": | |
662 answer.append('export %s=%s:$%s' % (var.name, var.raw_value, var.name)) | |
663 elif var.action == "append_to": | |
664 answer.append('export %s=$%s:%s' % (var.name, var.name, var.raw_value)) | |
665 else: | |
666 raise ValueError("Undefined environment variable action %r" % var.action) | |
667 return answer, answer # Actions needed in env.sh here! | |
668 | |
669 | |
670 class ChmodAction(BaseAction): | |
671 action_type = "chmod" | |
672 _keys = ["mods"] | |
673 | |
674 def __init__(self, elem): | |
675 mods = [] | |
676 file_els = elem.findall("file") | |
677 assert file_els is not None | |
678 for mod_elem in file_els: | |
679 mod = {} | |
680 mod["mode"] = mod_elem.attrib["mode"] | |
681 mod["target"] = mod_elem.text | |
682 mods.append(mod) | |
683 self.mods = mods | |
684 assert self.mods | |
685 | |
686 def to_bash(self): | |
687 return ["chmod %s %s" % (m["mode"], m["target"]) for m in self.mods], [] | |
688 | |
689 | |
690 class MakeInstallAction(BaseAction): | |
691 action_type = "make_install" | |
692 _keys = [] # type: List[str] | |
693 | |
694 def __init__(self, elem): | |
695 pass | |
696 | |
697 def to_bash(self): | |
698 return ["make install"], [] | |
699 | |
700 | |
701 class AutoconfAction(BaseAction): | |
702 action_type = "autoconf" | |
703 _keys = ["options"] | |
704 | |
705 def __init__(self, elem): | |
706 self.options = elem.text | |
707 | |
708 def to_bash(self): | |
709 if self.options: | |
710 raise NotImplementedError("Options with action autoconf not implemented yet.") | |
711 return ['./configure', 'make', 'make install'], [] | |
712 | |
713 | |
714 class ChangeDirectoryAction(BaseAction): | |
715 action_type = "change_directory" | |
716 _keys = ["directory"] | |
717 | |
718 def __init__(self, elem): | |
719 self.directory = elem.text | |
720 assert self.directory | |
721 | |
722 def to_bash(self): | |
723 return ["cd %s" % self.directory], [] | |
724 | |
725 | |
726 class MakeDirectoryAction(BaseAction): | |
727 action_type = "make_directory" | |
728 _keys = ["directory"] | |
729 | |
730 def __init__(self, elem): | |
731 self.directory = elem.text | |
732 | |
733 def to_bash(self): | |
734 return ["mkdir -p %s" % self.directory], [] | |
735 | |
736 | |
737 class SetupPerlEnvironmentAction(BaseAction): | |
738 action_type = "setup_perl_environment" | |
739 _keys = ["repo", "packages"] | |
740 | |
741 def __init__(self, elem): | |
742 self.parse_action_repo(elem) | |
743 self.parse_package_elems(elem) | |
744 | |
745 | |
746 class SetupRubyEnvironmentAction(BaseAction): | |
747 action_type = "setup_ruby_environment" | |
748 _keys = ["repo", "packages"] | |
749 | |
750 def __init__(self, elem): | |
751 self.parse_action_repo(elem) | |
752 self.parse_package_elems(elem) | |
753 | |
754 | |
755 class SetupPythonEnvironmentAction(BaseAction): | |
756 action_type = "setup_python_environment" | |
757 _keys = ["repo", "packages"] | |
758 | |
759 def __init__(self, elem): | |
760 self.parse_action_repo(elem) | |
761 self.parse_package_elems(elem) | |
762 | |
763 | |
764 class SetupVirtualenvAction(BaseAction): | |
765 action_type = "setup_virtualenv" | |
766 _keys = ["use_requirements_file", "python", "requirements"] | |
767 | |
768 def __init__(self, elem): | |
769 use_reqs = elem.attrib.get("use_requirements_file", "True") | |
770 self.use_requirements_file = asbool(use_reqs) | |
771 self.python = elem.get('python', 'python') | |
772 self.requirements = elem.text or 'requirements.txt' | |
773 | |
774 | |
775 class SetupREnvironmentAction(BaseAction): | |
776 action_type = "setup_r_environment" | |
777 _keys = ["repo", "packages"] | |
778 | |
779 def __init__(self, elem): | |
780 self.parse_action_repo(elem) | |
781 self.parse_package_elems(elem) | |
782 | |
783 | |
784 class SetEnvironmentForInstallAction(BaseAction): | |
785 action_type = "set_environment_for_install" | |
786 | |
787 def __init__(self, elem): | |
788 pass | |
789 | |
790 def to_bash(self): | |
791 # TODO - How could we resolve/check the dependencies? | |
792 return ['echo "WARNING: Assuming packages already installed!"'], [] | |
793 | |
794 | |
795 class SetVariable(object): | |
796 | |
797 def __init__(self, elem): | |
798 self.action = elem.attrib["action"] | |
799 self.name = elem.attrib["name"] | |
800 self.raw_value = elem.text | |
801 | |
802 | |
803 truthy = frozenset(['true', 'yes', 'on', 'y', 't', '1']) | |
804 falsy = frozenset(['false', 'no', 'off', 'n', 'f', '0']) | |
805 | |
806 | |
807 def asbool(obj): | |
808 if isinstance(obj, string_types): | |
809 obj = obj.strip().lower() | |
810 if obj in truthy: | |
811 return True | |
812 elif obj in falsy: | |
813 return False | |
814 else: | |
815 raise ValueError("String is not true/false: %r" % obj) | |
816 return bool(obj) | |
817 | |
818 | |
819 action_classes = [ | |
820 DownloadByUrlAction, | |
821 DownloadFileAction, | |
822 DownloadBinary, | |
823 ShellCommandAction, | |
824 TemplateShellCommandAction, | |
825 MoveFileAction, | |
826 MoveDirectoryFilesAction, | |
827 SetEnvironmentAction, | |
828 ChmodAction, | |
829 MakeInstallAction, | |
830 AutoconfAction, | |
831 ChangeDirectoryAction, | |
832 MakeDirectoryAction, | |
833 SetupPerlEnvironmentAction, | |
834 SetupRubyEnvironmentAction, | |
835 SetupPythonEnvironmentAction, | |
836 SetupVirtualenvAction, | |
837 SetupREnvironmentAction, | |
838 SetEnvironmentForInstallAction, | |
839 ] | |
840 | |
841 actions_by_type = dict(map(lambda c: (c.action_type, c), action_classes)) |