pymtt
 All Classes Namespaces Files Functions Variables Groups
Autotools.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2015-2018 Intel, Inc. All rights reserved.
4 # $COPYRIGHT$
5 #
6 # Additional copyrights may follow
7 #
8 # $HEADER$
9 #
10 
11 from __future__ import print_function
12 import os
13 import re
14 import string
15 import sys
16 import shlex
17 from BuildMTTTool import *
18 
19 ## @addtogroup Tools
20 # @{
21 # @addtogroup Build
22 # @section Autotools
23 # Run typical autotools commands to configure and build a software package
24 # @param middleware Middleware stage that these tests are to be built against
25 # @param parent Section that precedes this one in the dependency tree
26 # @param autogen_cmd Command to be executed to setup the configure script, usually called autogen.sh or autogen.pl
27 # @param configure_options Options to be passed to configure. Note that the prefix will be automatically set and need not be provided here
28 # @param make_options Options to be passed to the make command
29 # @param build_in_place Build tests in current location (no prefix or install)
30 # @param merge_stdout_stderr Merge stdout and stderr into one output stream
31 # @param stdout_save_lines Number of lines of stdout to save
32 # @param stderr_save_lines Number of lines of stderr to save
33 # @param modules Modules to load
34 # @param modules_unload Modules to unload
35 # @}
37  def __init__(self):
38  BuildMTTTool.__init__(self)
39  self.activated = False
40  self.options = {}
41  self.options['middleware'] = (None, "Middleware stage that these tests are to be built against")
42  self.options['parent'] = (None, "Section that precedes this one in the dependency tree")
43  self.options['autogen_cmd'] = (None, "Command to be executed to setup the configure script, usually called autogen.sh or autogen.pl")
44  self.options['configure_options'] = (None, "Options to be passed to configure. Note that the prefix will be automatically set and need not be provided here")
45  self.options['make_options'] = (None, "Options to be passed to the make command")
46  self.options['build_in_place'] = (False, "Build tests in current location (no prefix or install)")
47  self.options['merge_stdout_stderr'] = (False, "Merge stdout and stderr into one output stream")
48  self.options['stdout_save_lines'] = (-1, "Number of lines of stdout to save")
49  self.options['stderr_save_lines'] = (-1, "Number of lines of stderr to save")
50  self.options['modules'] = (None, "Modules to load")
51  self.options['modules_unload'] = (None, "Modules to unload")
52  self.exclude = set(string.punctuation)
53  return
54 
55  def activate(self):
56  if not self.activated:
57  # use the automatic procedure from IPlugin
58  IPlugin.activate(self)
59  self.activated = True
60  return
61 
62  def deactivate(self):
63  if self.activated:
64  IPlugin.deactivate(self)
65  self.activated = False
66  return
67 
68  def print_name(self):
69  return "Autotools"
70 
71  def print_options(self, testDef, prefix):
72  lines = testDef.printOptions(self.options)
73  for line in lines:
74  print(prefix + line)
75  return
76 
77  def execute(self, log, keyvals, testDef):
78 
79  testDef.logger.verbose_print("Autotools Execute")
80  # parse any provided options - these will override the defaults
81  cmds = {}
82  testDef.parseOptions(log, self.options, keyvals, cmds)
83  # get the location of the software we are to build
84  try:
85  if cmds['parent'] is not None:
86  # we have to retrieve the log entry from
87  # the parent section so we can get the
88  # location of the package. The logger
89  # can provide it for us
90  parentlog = testDef.logger.getLog(cmds['parent'])
91  if parentlog is None:
92  log['status'] = 1
93  log['stderr'] = "Parent",cmds['parent'],"log not found"
94  return
95  else:
96  log['status'] = 1
97  log['stderr'] = "Parent log not recorded"
98  return
99 
100  except KeyError:
101  log['status'] = 1
102  log['stderr'] = "Parent not specified"
103  return
104  try:
105  location = parentlog['location']
106  except KeyError:
107  log['status'] = 1
108  log['stderr'] = "Location of package to build was not specified in parent stage"
109  return
110  inPlace = False
111  # check to see if they specified a module to use
112  # where the autotools can be found
113  usedModuleUnload = False
114  try:
115  if cmds['modules_unload'] is not None:
116  status,stdout,stderr = testDef.modcmd.unloadModules(cmds['modules_unload'], testDef)
117  if 0 != status:
118  log['status'] = status
119  log['stderr'] = stderr
120  return
121  usedModuleUnload = True
122  except KeyError:
123  # not required to provide a module to unload
124  pass
125  usedModule = False
126  try:
127  if cmds['modules'] is not None:
128  status,stdout,stderr = testDef.modcmd.loadModules(cmds['modules'], testDef)
129  if 0 != status:
130  log['status'] = status
131  log['stderr'] = stderr
132  return
133  usedModule = True
134  except KeyError:
135  # not required to provide a module
136  pass
137 
138  # sense and record the compiler being used
139  plugin = None
140  availUtil = list(testDef.loader.utilities.keys())
141  for util in availUtil:
142  for pluginInfo in testDef.utilities.getPluginsOfCategory(util):
143  if "Compilers" == pluginInfo.plugin_object.print_name():
144  plugin = pluginInfo.plugin_object
145  break
146  if plugin is None:
147  log['compiler'] = {'status' : 1, 'family' : "unknown", 'version' : "unknown"}
148  else:
149  compilerLog = {}
150  plugin.execute(compilerLog, testDef)
151  log['compiler'] = compilerLog
152  testDef.logger.verbose_print(log['compiler'])
153 
154  # Find MPI information for IUDatabase plugin if
155  # mpi_info is not already set
156  fullLog = testDef.logger.getLog(None)
157  mpi_info_found = False
158  for lg in fullLog:
159  if 'mpi_info' in lg:
160  mpi_info_found = True
161  if mpi_info_found is False:
162  plugin = None
163  availUtil = list(testDef.loader.utilities.keys())
164  for util in availUtil:
165  for pluginInfo in testDef.utilities.getPluginsOfCategory(util):
166  if "MPIVersion" == pluginInfo.plugin_object.print_name():
167  plugin = pluginInfo.plugin_object
168  break
169  if plugin is None:
170  log['mpi_info'] = {'name' : 'unknown', 'version' : 'unknown'}
171  else:
172  mpi_info = {}
173  plugin.execute(mpi_info, testDef)
174  log['mpi_info'] = mpi_info
175  else:
176  testDef.logger.verbose_print("mpi_info already in log so skipping MPIVersion")
177 
178  # Add configure options to log for IUDatabase plugin
179  try:
180  log['configure_options'] = cmds['configure_options']
181  except KeyError:
182  log['configure_options'] = ''
183 
184  try:
185  if cmds['build_in_place']:
186  prefix = None
187  log['location'] = location
188  inPlace = True
189  else:
190  # create the prefix path where this build result will be placed
191  pfx = os.path.join(testDef.options['scratchdir'], log['section'].replace(':','_'))
192  # convert it to an absolute path
193  pfx = os.path.abspath(pfx)
194  # record this location for any follow-on steps
195  log['location'] = pfx
196  prefix = "--prefix={0}".format(pfx)
197  except KeyError:
198  # create the prefix path where this build result will be placed
199  pfx = os.path.join(testDef.options['scratchdir'], log['section'].replace(':','_'))
200  # convert it to an absolute path
201  pfx = os.path.abspath(pfx)
202  # record this location for any follow-on steps
203  log['location'] = pfx
204  prefix = "--prefix={0}".format(pfx)
205  # check to see if we are to leave things "as-is"
206  try:
207  if cmds['asis']:
208  if not inPlace:
209  # see if the build already exists - if
210  # it does, then we are done
211  if os.path.exists(pfx) and os.path.isdir(pfx):
212  testDef.logger.verbose_print("As-Is location " + pfx + " exists and is a directory")
213  # nothing further to do
214  log['status'] = 0
215  return
216  else:
217  # check if configure exists
218  cfg = os.path.join(location, "configure")
219  if os.path.exists(cfg):
220  # good enough
221  testDef.logger.verbose_print("As-Is location " + location + " has configure present")
222  log['status'] = 0
223  return
224  except KeyError:
225  pass
226  # check to see if this is a dryrun
227  if testDef.options['dryrun']:
228  # just log success and return
229  log['status'] = 0
230  return
231 
232  # check if we need to point to middleware
233  midpath = False
234  try:
235  if cmds['middleware'] is not None:
236  # pass it down
237  log['middleware'] = cmds['middleware']
238  # get the log entry of its location
239  midlog = testDef.logger.getLog(cmds['middleware'])
240  if midlog is not None:
241  # get the location of the middleware
242  try:
243  if midlog['location'] is not None:
244  # prepend that location to our paths
245  try:
246  oldbinpath = os.environ['PATH']
247  pieces = oldbinpath.split(':')
248  except KeyError:
249  oldbinpath = ""
250  pieces = []
251  bindir = os.path.join(midlog['location'], "bin")
252  pieces.insert(0, bindir)
253  newpath = ":".join(pieces)
254  os.environ['PATH'] = newpath
255  # prepend the loadable lib path
256  try:
257  oldldlibpath = os.environ['LD_LIBRARY_PATH']
258  pieces = oldldlibpath.split(':')
259  except KeyError:
260  oldldlibpath = ""
261  pieces = []
262  bindir = os.path.join(midlog['location'], "lib")
263  pieces.insert(0, bindir)
264  newpath = ":".join(pieces)
265  os.environ['LD_LIBRARY_PATH'] = newpath
266  # prepend the include path
267  try:
268  oldcpath = os.environ['CPATH']
269  pieces = oldcpath.split(':')
270  except KeyError:
271  oldcpath = ""
272  pieces = []
273  bindir = os.path.join(midlog['location'], "include")
274  pieces.insert(0, bindir)
275  newpath = ":".join(pieces)
276  os.environ['CPATH'] = newpath
277  # prepend the lib path
278  try:
279  oldlibpath = os.environ['LIBRARY_PATH']
280  pieces = oldlibpath.split(':')
281  except KeyError:
282  oldlibpath = ""
283  pieces = []
284  bindir = os.path.join(midlog['location'], "lib")
285  pieces.insert(0, bindir)
286  newpath = ":".join(pieces)
287  os.environ['LIBRARY_PATH'] = newpath
288 
289  # mark that this was done
290  midpath = True
291  except KeyError:
292  pass
293  # check for modules required by the middleware
294  try:
295  if midlog['parameters'] is not None:
296  for md in midlog['parameters']:
297  if "modules" == md[0]:
298  try:
299  if cmds['modules'] is not None:
300  # append these modules to those
301  mods = md[1].split(',')
302  newmods = modules.split(',')
303  for md in newmods:
304  mods.append(md)
305  cmds['modules'] = ','.join(mods)
306  except KeyError:
307  cmds['modules'] = md[1]
308  break
309  except KeyError:
310  pass
311  except KeyError:
312  pass
313 
314  # save the current directory so we can return to it
315  cwd = os.getcwd()
316  # now move to the package location
317  os.chdir(location)
318  # see if they want us to execute autogen
319  try:
320  if cmds['autogen_cmd'] is not None:
321  agargs = []
322  args = cmds['autogen_cmd'].split()
323  for arg in args:
324  agargs.append(arg.strip())
325  status, stdout, stderr, _ = testDef.execmd.execute(cmds, agargs, testDef)
326  if 0 != status:
327  log['status'] = status
328  log['stdout'] = stdout
329  log['stderr'] = stderr
330  if usedModule:
331  # unload the modules before returning
332  status,stdout,stderr = testDef.modcmd.unloadModules(cmds['modules'], testDef)
333  if 0 != status:
334  log['status'] = status
335  log['stderr'] = stderr
336  os.chdir(cwd)
337  return
338  if usedModuleUnload:
339  status,stdout,stderr = testDef.modcmd.loadModules(cmds['modules_unload'], testDef)
340  if 0 != status:
341  log['status'] = status
342  log['stderr'] = stderr
343  os.chdir(cwd)
344  return
345  # return to original location
346  os.chdir(cwd)
347  return
348  else:
349  # this is a multistep operation, and so we need to
350  # retain the output from each step in the log
351  log['autogen'] = (stdout, stderr)
352 
353  except KeyError:
354  # autogen phase is not required
355  pass
356  # we always have to run configure, but we first
357  # need to build a target prefix directory option based
358  # on the scratch directory and section name
359  cfgargs = ["./configure"]
360  if prefix is not None:
361  cfgargs.append(prefix)
362  # if they gave us any configure args, add them
363  try:
364  if cmds['configure_options'] is not None:
365  args = shlex.split(cmds['configure_options'])
366  for arg in args:
367  cfgargs.append(arg.strip())
368  except KeyError:
369  pass
370  status, stdout, stderr, _ = testDef.execmd.execute(cmds, cfgargs, testDef)
371  if 0 != status:
372  log['status'] = status
373  log['stdout'] = stdout
374  log['stderr'] = stderr
375  if usedModule:
376  # unload the modules before returning
377  status,stdout,stderr = testDef.modcmd.unloadModules(cmds['modules'], testDef)
378  if 0 != status:
379  log['status'] = status
380  log['stderr'] = stderr
381  # return to original location
382  os.chdir(cwd)
383  return
384  else:
385  # this is a multistep operation, and so we need to
386  # retain the output from each step in the log
387  log['configure'] = (stdout, stderr)
388  # next we do the build stage, using the custom build cmd
389  # if one is provided, or else defaulting to the testDef
390  # default
391  bldargs = ["make"]
392  try:
393  if cmds['make_options'] is not None:
394  args = cmds['make_options'].split()
395  for arg in args:
396  bldargs.append(arg.strip())
397  except KeyError:
398  # if they didn't provide it, then use the value in testDef
399  args = testDef.options.default_make_options.split()
400  for arg in args:
401  bldargs.append(arg.strip())
402  # step thru the process, starting with "clean"
403  bldargs.append("clean")
404  status, stdout, stderr, _ = testDef.execmd.execute(cmds, bldargs, testDef)
405  if 0 != status:
406  log['status'] = status
407  log['stdout'] = stdout
408  log['stderr'] = stderr
409  if usedModule:
410  # unload the modules before returning
411  status,stdout,stderr = testDef.modcmd.unloadModules(cmds['modules'], testDef)
412  if 0 != status:
413  log['status'] = status
414  log['stderr'] = stderr
415  # return to original location
416  os.chdir(cwd)
417  return
418  else:
419  # this is a multistep operation, and so we need to
420  # retain the output from each step in the log
421  log['make_clean'] = (stdout, stderr)
422  # now execute "make all"
423  bldargs = bldargs[0:-1]
424  bldargs.append("all")
425  status, stdout, stderr, _ = testDef.execmd.execute(cmds, bldargs, testDef)
426  if 0 != status:
427  log['status'] = status
428  log['stdout'] = stdout
429  log['stderr'] = stderr
430  if usedModule:
431  # unload the modules before returning
432  status,stdout,stderr = testDef.modcmd.unloadModules(cmds['modules'], testDef)
433  if 0 != status:
434  log['status'] = status
435  log['stderr'] = stderr
436  # return to original location
437  os.chdir(cwd)
438  return
439  else:
440  # this is a multistep operation, and so we need to
441  # retain the output from each step in the log
442  log['make_all'] = (stdout, stderr)
443  # and finally, execute "make install" if we have a prefix
444  if prefix is not None:
445  bldargs = bldargs[0:-1]
446  bldargs.append("install")
447  status, stdout, stderr, _ = testDef.execmd.execute(cmds, bldargs, testDef)
448  # this is the end of the operation, so the status is our
449  # overall status
450  log['status'] = status
451  log['stdout'] = stdout
452  log['stderr'] = stderr
453  if usedModule:
454  # unload the modules before returning
455  status,stdout,stderr = testDef.modcmd.unloadModules(cmds['modules'], testDef)
456  if 0 != status:
457  log['status'] = status
458  log['stderr'] = stderr
459 
460  # if we added middleware to the paths, remove it
461  if midpath:
462  os.environ['PATH'] = oldbinpath
463  os.environ['LD_LIBRARY_PATH'] = oldldlibpath
464  os.environ['CPATH'] = oldcpath
465  os.environ['LIBRARY_PATH'] = oldlibpath
466 
467  # return home
468  os.chdir(cwd)
469 
470  return