comparison venv/lib/python2.7/site-packages/planemo/shed2tap/base.py @ 0:d67268158946 draft

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