3333* Toon Willems (Ghent University)
3434* Kenneth Hoste (Ghent University)
3535* Stijn De Weirdt (Ghent University)
36+ * Bart Oldeman (McGill University, Calcul Quebec, Digital Research Alliance of Canada)
3637"""
3738import math
3839import os
4546from easybuild .tools .config import build_option , get_repository , get_repositorypath
4647from easybuild .tools .filetools import get_cwd
4748from easybuild .tools .module_naming_scheme .utilities import det_full_ec_version
48- from easybuild .tools .job .backend import job_backend
49+ from easybuild .tools .job .backend import job_backend , JobBackend
4950from easybuild .tools .repository .repository import init_repository
5051
5152
@@ -57,7 +58,8 @@ def _to_key(dep):
5758 return ActiveMNS ().det_full_module_name (dep )
5859
5960
60- def build_easyconfigs_in_parallel (build_command , easyconfigs , output_dir = 'easybuild-build' , prepare_first = True ):
61+ def build_easyconfigs_in_parallel (build_command , easyconfigs , output_dir = 'easybuild-build' , testing = False ,
62+ prepare_first = True , tweak_map = None , try_opts = '' ):
6163 """
6264 Build easyconfigs in parallel by submitting jobs to a batch-queuing system.
6365 Return list of jobs submitted.
@@ -69,11 +71,14 @@ def build_easyconfigs_in_parallel(build_command, easyconfigs, output_dir='easybu
6971 :param build_command: build command to use
7072 :param easyconfigs: list of easyconfig files
7173 :param output_dir: output directory
74+ :param testing: If `True`, skip actual job submission
7275 :param prepare_first: prepare by runnning fetch step first for each easyconfig
76+ :param tweak_map: Mapping from tweaked to original easyconfigs
77+ :param try_opts: --try-* options to pass if the easyconfig is tweaked
7378 """
7479 _log .info ("going to build these easyconfigs in parallel: %s" , [os .path .basename (ec ['spec' ]) for ec in easyconfigs ])
7580
76- active_job_backend = job_backend ()
81+ active_job_backend = JobBackend () if testing else job_backend ()
7782 if active_job_backend is None :
7883 raise EasyBuildError ("Can not use --job if no job backend is available." )
7984
@@ -93,12 +98,17 @@ def build_easyconfigs_in_parallel(build_command, easyconfigs, output_dir='easybu
9398 # this is very important, otherwise we might have race conditions
9499 # e.g. GCC-4.5.3 finds cloog.tar.gz but it was incorrectly downloaded by GCC-4.6.3
95100 # running this step here, prevents this
96- if prepare_first :
101+ if prepare_first and not testing :
97102 prepare_easyconfig (easyconfig )
98103
104+ # convert <tweaked easyconfig.eb> to <original-easyconfig.eb --try-xxx> to avoid needing a shared tmpdir
105+ spec = easyconfig ['spec' ]
106+ if spec in (tweak_map or {}):
107+ spec = tweak_map [spec ] + try_opts
108+
99109 # the new job will only depend on already submitted jobs
100- _log .info ("creating job for ec: %s" % os .path .basename (easyconfig ['spec' ]))
101- new_job = create_job (active_job_backend , build_command , easyconfig , output_dir = output_dir )
110+ _log .info ("creating job for ec: %s using %s " % ( os .path .basename (easyconfig ['spec' ]), spec ))
111+ new_job = create_job (active_job_backend , build_command , easyconfig , output_dir = output_dir , spec = spec )
102112
103113 # filter out dependencies marked as external modules
104114 deps = [d for d in easyconfig ['ec' ].all_dependencies if not d .get ('external_module' , False )]
@@ -116,24 +126,27 @@ def build_easyconfigs_in_parallel(build_command, easyconfigs, output_dir='easybu
116126
117127 active_job_backend .complete ()
118128
119- return jobs
129+ return build_command if testing else jobs
120130
121131
122- def submit_jobs (ordered_ecs , cmd_line_opts , testing = False , prepare_first = True ):
132+ def submit_jobs (ordered_ecs , cmd_line_opts , testing = False , prepare_first = True , tweak_map = None ):
123133 """
124134 Submit jobs.
125135 :param ordered_ecs: list of easyconfigs, in the order they should be processed
126136 :param cmd_line_opts: list of command line options (in 'longopt=value' form)
127137 :param testing: If `True`, skip actual job submission
128138 :param prepare_first: prepare by runnning fetch step first for each easyconfig
139+ :param tweak_map: Mapping from tweaked to original easyconfigs
129140 """
130141 curdir = get_cwd ()
131142
132- # regex pattern for options to ignore (help options can't reach here)
143+ # regex patterns for options to ignore and tweak options (help options can't reach here)
133144 ignore_opts = re .compile ('^--robot$|^--job|^--try-.*$|^--easystack$' )
145+ try_opts_re = re .compile ('^--try-.*$' )
134146
135147 # generate_cmd_line returns the options in form --longopt=value
136148 opts = [o for o in cmd_line_opts if not ignore_opts .match (o .split ('=' )[0 ])]
149+ try_opts = [o for o in cmd_line_opts if try_opts_re .match (o .split ('=' )[0 ])]
137150
138151 # add --disable-job to make sure the submitted job doesn't submit a job itself,
139152 # resulting in an infinite cycle of jobs;
@@ -143,6 +156,7 @@ def submit_jobs(ordered_ecs, cmd_line_opts, testing=False, prepare_first=True):
143156
144157 # compose string with command line options, properly quoted and with '%' characters escaped
145158 opts_str = ' ' .join (opts ).replace ('%' , '%%' )
159+ try_opts_str = ' ' + ' ' .join (try_opts ).replace ('%' , '%%' )
146160
147161 eb_cmd = build_option ('job_eb_cmd' )
148162
@@ -154,19 +168,19 @@ def submit_jobs(ordered_ecs, cmd_line_opts, testing=False, prepare_first=True):
154168 _log .info ("Command template for jobs: %s" , command )
155169 if testing :
156170 _log .debug ("Skipping actual submission of jobs since testing mode is enabled" )
157- return command
158- else :
159- return build_easyconfigs_in_parallel (command , ordered_ecs , prepare_first = prepare_first )
171+ return build_easyconfigs_in_parallel (command , ordered_ecs , testing = testing , prepare_first = prepare_first ,
172+ tweak_map = tweak_map , try_opts = try_opts_str )
160173
161174
162- def create_job (job_backend , build_command , easyconfig , output_dir = 'easybuild-build' ):
175+ def create_job (job_backend , build_command , easyconfig , output_dir = 'easybuild-build' , spec = '' ):
163176 """
164177 Creates a job to build a *single* easyconfig.
165178
166179 :param job_backend: A factory object for querying server parameters and creating actual job objects
167180 :param build_command: format string for command, full path to an easyconfig file will be substituted in it
168181 :param easyconfig: easyconfig as processed by process_easyconfig
169182 :param output_dir: optional output path; --regtest-output-dir will be used inside the job with this variable
183+ :param spec: untweaked easyconfig name with optional --try-* options
170184
171185 returns the job
172186 """
@@ -183,7 +197,7 @@ def create_job(job_backend, build_command, easyconfig, output_dir='easybuild-bui
183197 command = build_command % {
184198 'add_opts' : add_opts ,
185199 'output_dir' : os .path .join (os .path .abspath (output_dir ), name ),
186- 'spec' : easyconfig ['spec' ],
200+ 'spec' : spec or easyconfig ['spec' ],
187201 }
188202
189203 # just use latest build stats
0 commit comments