jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
6.3KB

  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. # DC 2008
  4. # Thomas Nagy 2010 (ita)
  5. """
  6. fortran support
  7. """
  8. from waflib import Utils, Task, Logs
  9. from waflib.Tools import ccroot, fc_config, fc_scan
  10. from waflib.TaskGen import feature, extension
  11. from waflib.Configure import conf
  12. ccroot.USELIB_VARS['fc'] = set(['FCFLAGS', 'DEFINES', 'INCLUDES'])
  13. ccroot.USELIB_VARS['fcprogram_test'] = ccroot.USELIB_VARS['fcprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
  14. ccroot.USELIB_VARS['fcshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
  15. ccroot.USELIB_VARS['fcstlib'] = set(['ARFLAGS', 'LINKDEPS'])
  16. @feature('fcprogram', 'fcshlib', 'fcstlib', 'fcprogram_test')
  17. def dummy(self):
  18. """
  19. Unused function that does nothing (TODO: remove in waf 1.9)
  20. """
  21. pass
  22. @extension('.f', '.f90', '.F', '.F90', '.for', '.FOR')
  23. def fc_hook(self, node):
  24. "Bind the typical Fortran file extensions to the creation of a :py:class:`waflib.Tools.fc.fc` instance"
  25. return self.create_compiled_task('fc', node)
  26. @conf
  27. def modfile(conf, name):
  28. """
  29. Turn a module name into the right module file name.
  30. Defaults to all lower case.
  31. """
  32. return {'lower' :name.lower() + '.mod',
  33. 'lower.MOD' :name.upper() + '.MOD',
  34. 'UPPER.mod' :name.upper() + '.mod',
  35. 'UPPER' :name.upper() + '.MOD'}[conf.env.FC_MOD_CAPITALIZATION or 'lower']
  36. def get_fortran_tasks(tsk):
  37. """
  38. Obtain all other fortran tasks from the same build group. Those tasks must not have
  39. the attribute 'nomod' or 'mod_fortran_done'
  40. """
  41. bld = tsk.generator.bld
  42. tasks = bld.get_tasks_group(bld.get_group_idx(tsk.generator))
  43. return [x for x in tasks if isinstance(x, fc) and not getattr(x, 'nomod', None) and not getattr(x, 'mod_fortran_done', None)]
  44. class fc(Task.Task):
  45. """
  46. The fortran tasks can only run when all fortran tasks in the current group are ready to be executed
  47. This may cause a deadlock if another fortran task is waiting for something that cannot happen (circular dependency)
  48. in this case, set the 'nomod=True' on those tasks instances to break the loop
  49. """
  50. color = 'GREEN'
  51. run_str = '${FC} ${FCFLAGS} ${FCINCPATH_ST:INCPATHS} ${FCDEFINES_ST:DEFINES} ${_FCMODOUTFLAGS} ${FC_TGT_F}${TGT[0].abspath()} ${FC_SRC_F}${SRC[0].abspath()}'
  52. vars = ["FORTRANMODPATHFLAG"]
  53. def scan(self):
  54. """scanner for fortran dependencies"""
  55. tmp = fc_scan.fortran_parser(self.generator.includes_nodes)
  56. tmp.task = self
  57. tmp.start(self.inputs[0])
  58. if Logs.verbose:
  59. Logs.debug('deps: deps for %r: %r; unresolved %r' % (self.inputs, tmp.nodes, tmp.names))
  60. return (tmp.nodes, tmp.names)
  61. def runnable_status(self):
  62. """
  63. Set the mod file outputs and the dependencies on the mod files over all the fortran tasks
  64. executed by the main thread so there are no concurrency issues
  65. """
  66. if getattr(self, 'mod_fortran_done', None):
  67. return super(fc, self).runnable_status()
  68. # now, if we reach this part it is because this fortran task is the first in the list
  69. bld = self.generator.bld
  70. # obtain the fortran tasks
  71. lst = get_fortran_tasks(self)
  72. # disable this method for other tasks
  73. for tsk in lst:
  74. tsk.mod_fortran_done = True
  75. # wait for all the .f tasks to be ready for execution
  76. # and ensure that the scanners are called at least once
  77. for tsk in lst:
  78. ret = tsk.runnable_status()
  79. if ret == Task.ASK_LATER:
  80. # we have to wait for one of the other fortran tasks to be ready
  81. # this may deadlock if there are dependencies between the fortran tasks
  82. # but this should not happen (we are setting them here!)
  83. for x in lst:
  84. x.mod_fortran_done = None
  85. # TODO sort the list of tasks in bld.producer.outstanding to put all fortran tasks at the end
  86. return Task.ASK_LATER
  87. ins = Utils.defaultdict(set)
  88. outs = Utils.defaultdict(set)
  89. # the .mod files to create
  90. for tsk in lst:
  91. key = tsk.uid()
  92. for x in bld.raw_deps[key]:
  93. if x.startswith('MOD@'):
  94. name = bld.modfile(x.replace('MOD@', ''))
  95. node = bld.srcnode.find_or_declare(name)
  96. if not hasattr(node, 'sig'):
  97. node.sig = Utils.SIG_NIL
  98. tsk.set_outputs(node)
  99. outs[id(node)].add(tsk)
  100. # the .mod files to use
  101. for tsk in lst:
  102. key = tsk.uid()
  103. for x in bld.raw_deps[key]:
  104. if x.startswith('USE@'):
  105. name = bld.modfile(x.replace('USE@', ''))
  106. node = bld.srcnode.find_resource(name)
  107. if node and node not in tsk.outputs:
  108. if not node in bld.node_deps[key]:
  109. bld.node_deps[key].append(node)
  110. ins[id(node)].add(tsk)
  111. # if the intersection matches, set the order
  112. for k in ins.keys():
  113. for a in ins[k]:
  114. a.run_after.update(outs[k])
  115. # the scanner cannot output nodes, so we have to set them
  116. # ourselves as task.dep_nodes (additional input nodes)
  117. tmp = []
  118. for t in outs[k]:
  119. tmp.extend(t.outputs)
  120. a.dep_nodes.extend(tmp)
  121. a.dep_nodes.sort(key=lambda x: x.abspath())
  122. # the task objects have changed: clear the signature cache
  123. for tsk in lst:
  124. try:
  125. delattr(tsk, 'cache_sig')
  126. except AttributeError:
  127. pass
  128. return super(fc, self).runnable_status()
  129. class fcprogram(ccroot.link_task):
  130. """Link fortran programs"""
  131. color = 'YELLOW'
  132. run_str = '${FC} ${LINKFLAGS} ${FCLNK_SRC_F}${SRC} ${FCLNK_TGT_F}${TGT[0].abspath()} ${RPATH_ST:RPATH} ${FCSTLIB_MARKER} ${FCSTLIBPATH_ST:STLIBPATH} ${FCSTLIB_ST:STLIB} ${FCSHLIB_MARKER} ${FCLIBPATH_ST:LIBPATH} ${FCLIB_ST:LIB} ${LDFLAGS}'
  133. inst_to = '${BINDIR}'
  134. class fcshlib(fcprogram):
  135. """Link fortran libraries"""
  136. inst_to = '${LIBDIR}'
  137. class fcprogram_test(fcprogram):
  138. """Custom link task to obtain the compiler outputs for fortran configuration tests"""
  139. def runnable_status(self):
  140. """This task is always executed"""
  141. ret = super(fcprogram_test, self).runnable_status()
  142. if ret == Task.SKIP_ME:
  143. ret = Task.RUN_ME
  144. return ret
  145. def exec_command(self, cmd, **kw):
  146. """Store the compiler std our/err onto the build context, to bld.out + bld.err"""
  147. bld = self.generator.bld
  148. kw['shell'] = isinstance(cmd, str)
  149. kw['stdout'] = kw['stderr'] = Utils.subprocess.PIPE
  150. kw['cwd'] = bld.variant_dir
  151. bld.out = bld.err = ''
  152. bld.to_log('command: %s\n' % cmd)
  153. kw['output'] = 0
  154. try:
  155. (bld.out, bld.err) = bld.cmd_and_log(cmd, **kw)
  156. except Exception:
  157. return -1
  158. if bld.out:
  159. bld.to_log("out: %s\n" % bld.out)
  160. if bld.err:
  161. bld.to_log("err: %s\n" % bld.err)
  162. class fcstlib(ccroot.stlink_task):
  163. """Link fortran static libraries (uses ar by default)"""
  164. pass # do not remove the pass statement