diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py
index c8f63c93908..08defe4a00c 100755
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -17,6 +17,7 @@ from galaxy.util.bunch import Bunch
from galaxy.util.template import fill_template
from galaxy import util, jobs, model
from galaxy.jobs import ParallelismInfo
+from copy import deepcopy
from elementtree import ElementTree
from parameters import *
from parameters.grouping import *
@@ -520,7 +521,7 @@ class ToolBox( object ):
def load_tool( self, config_file, guid=None, **kwds ):
"""Load a single tool from the file named by `config_file` and return an instance of `Tool`."""
# Parse XML configuration file and get the root element
- tree = util.parse_xml( config_file )
+ tree = self._load_and_preprocess_tool_xml( config_file )
root = tree.getroot()
# Allow specifying a different tool subclass to instantiate
if root.find( "type" ) is not None:
@@ -704,7 +705,104 @@ class ToolBox( object ):
rval = tools
return rval
-
+
+ def _load_and_preprocess_tool_xml(self, config_file):
+ tree = util.parse_xml(config_file)
+ root = tree.getroot()
+ macros_el = root.find('macros')
+ if not macros_el:
+ return tree
+ tool_dir = os.path.dirname(config_file)
+ macros = self._load_macros(macros_el, tool_dir)
+
+ self._expand_macros([root], macros)
+ return tree
+
+ def _expand_macros(self, elements, macros):
+ for element in elements:
+ # HACK for elementtree, newer implementations (etree/lxml) won't
+ # require this parent_map data structure but elementtree does not
+ # track parents or recongnize .find('..').
+ parent_map = dict((c, p) for p in element.getiterator() for c in p)
+ for expand_el in element.findall('.//expand'):
+ macro_name = expand_el.get('macro')
+ macro_def = deepcopy(macros[macro_name]) # deepcopy needed?
+
+ yield_els = [yield_el for macro_def_el in macro_def for yield_el in macro_def_el.findall('.//yield')]
+
+ expand_el_children = expand_el.getchildren()
+ macro_def_parent_map = \
+ dict((c, p) for macro_def_el in macro_def for p in macro_def_el.getiterator() for c in p)
+
+ for yield_el in yield_els:
+ self._xml_replace(yield_el, expand_el_children, macro_def_parent_map)
+
+ # Recursively expand contained macros.
+ self._expand_macros(macro_def, macros)
+ self._xml_replace(expand_el, macro_def, parent_map)
+
+ def _load_macros(self, macros_el, tool_dir):
+ macros = {}
+ # Import macros from external files.
+ macros.update(self._load_imported_macros(macros_el, tool_dir))
+ # Load all directly defined macros.
+ macros.update(self._load_embedded_macros(macros_el, tool_dir))
+ return macros
+
+ def _load_embedded_macros(self, macros_el, tool_dir):
+ macros = {}
+
+ macro_els = []
+ if macros_el:
+ macro_els = macros_el.findall("macro")
+ for macro in macro_els:
+ macro_name = macro.get("name")
+ macros[macro_name] = self._load_macro_def(macro)
+
+ return macros
+
+ def _load_imported_macros(self, macros_el, tool_dir):
+ macros = {}
+
+ macro_import_els = []
+ if macros_el:
+ macro_import_els = macros_el.findall("import")
+ for macro_import_el in macro_import_els:
+ raw_import_path = macro_import_el.text
+ tool_relative_import_path = \
+ os.path.basename(raw_import_path) # Sanitize this
+ import_path = \
+ os.path.join(tool_dir, tool_relative_import_path)
+ file_macros = self._load_macro_file(import_path, tool_dir)
+ macros.update(file_macros)
+
+ return macros
+
+ def _load_macro_file(self, path, tool_dir):
+ tree = util.parse_xml(path)
+ root = tree.getroot()
+ return self._load_macros(root, tool_dir)
+
+ def _load_macro_def(self, macro):
+ return list(macro.getchildren())
+
+ def _xml_replace(self, query, targets, parent_map):
+ #parent_el = query.find('..') ## Something like this would be better with newer xml library
+ parent_el = parent_map[query]
+ matching_index = -1
+ #for index, el in enumerate(parent_el.iter('.')): ## Something like this for newer implementation
+ for index, el in enumerate(parent_el.getchildren()):
+ if el == query:
+ matching_index = index
+ break
+ assert matching_index >= 0
+ current_index = matching_index
+ for target in targets:
+ current_index += 1
+ parent_el.insert(current_index, deepcopy(target))
+ parent_el.remove(query)
+
+
class ToolSection( object ):
"""
A group of tools with similar type/purpose that will be displayed as a
diff --git a/tools/ngs_rna/tophat2_wrapper.xml b/tools/ngs_rna/tophat2_wrapper.xml
index 5733bbe4a6e..fb0f1d19bca 100644
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -143,23 +143,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
@@ -182,17 +171,7 @@
-
-
-
-
-
-
-
-
-
-
-
+
alignments (number of reads divided by average depth of coverage)" help="0.0 to 1.0 (0 to turn off)" />
@@ -201,40 +180,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -319,83 +265,40 @@
(params['settingsType'] == 'full' and params['fusion_search']['do_search'] == 'Yes')
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
+ tophat_macros.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/ngs_rna/tophat_color_wrapper.xml b/tools/ngs_rna/tophat_color_wrapper.xml
index 72c0a277b29..399d2c9c46c 100644
--- a/tools/ngs_rna/tophat_color_wrapper.xml
+++ b/tools/ngs_rna/tophat_color_wrapper.xml
@@ -154,23 +154,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
@@ -194,17 +183,7 @@
-
-
-
-
-
-
-
-
-
-
-
+
@@ -230,20 +209,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -301,17 +268,7 @@
-
-
-
-
-
-
-
-
-
-
-
+
@@ -399,90 +356,50 @@
( singlePaired['pParams']['indel_search']['allow_indel_search'] == 'Yes' ) )
)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+ tophat_macros.xml
+
+
(
( ( 'sParams' in singlePaired ) and ( 'indel_search' in singlePaired['sParams'] ) and
( singlePaired['sParams']['indel_search']['allow_indel_search'] == 'Yes' ) ) or
( ( 'pParams' in singlePaired ) and ( 'indel_search' in singlePaired['pParams'] ) and
( singlePaired['pParams']['indel_search']['allow_indel_search'] == 'Yes' ) )
)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/ngs_rna/tophat_macros.xml b/tools/ngs_rna/tophat_macros.xml
new file mode 100644
index 00000000000..51ebec16d9a
--- /dev/null
+++ b/tools/ngs_rna/tophat_macros.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/ngs_rna/tophat_wrapper.xml b/tools/ngs_rna/tophat_wrapper.xml
index 234040bc664..83321cdb9d5 100644
--- a/tools/ngs_rna/tophat_wrapper.xml
+++ b/tools/ngs_rna/tophat_wrapper.xml
@@ -151,23 +151,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
@@ -298,17 +287,7 @@ alignments (number of reads divided by average depth of coverage)" help="0.0 to
-
-
-
-
-
-
-
-
-
-
-
+
@@ -316,40 +295,7 @@ alignments (number of reads divided by average depth of coverage)" help="0.0 to
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -387,83 +333,40 @@ alignments (number of reads divided by average depth of coverage)" help="0.0 to
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
+ tophat_macros.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+