waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

wscript (5549B)


      1 #! /usr/bin/env python
      2 
      3 top = '.'
      4 out = 'build'
      5 
      6 def options(opt):
      7 	opt.load('compiler_c')
      8 	opt.load('compiler_fc')
      9 
     10 def configure(conf):
     11 	conf.load('compiler_c')
     12 	conf.load('compiler_fc')
     13 	conf.check_fortran()
     14 	# configuration tests that may be totally irrelevant
     15 	conf.check_fortran_dummy_main()
     16 	if not conf.env.IFORT_WIN32:
     17 		conf.check_fortran_verbose_flag()
     18 		conf.check_fortran_clib()
     19 		conf.check_fortran_mangling()
     20 
     21 def build(bld):
     22 
     23 	bld(
     24 		features = 'fc typemap fcshlib',
     25 		source = 'fsrc.f90 basetypes.f90',
     26 		defs   = 'fsrc.def',
     27 		target = 'foo',
     28 		)
     29 
     30 import os
     31 from waflib import Logs, Build, Utils
     32 
     33 from waflib import TaskGen, Task
     34 from waflib.ConfigSet import ConfigSet
     35 
     36 @TaskGen.feature('typemap')
     37 @TaskGen.after('process_source')
     38 @TaskGen.before('apply_link')
     39 def process_typemaps(self):
     40 	"""
     41 	modmap: *.f90 + foo.in -> foo.h + foo.f90
     42 	compile foo.f90 like the others
     43 	"""
     44 	node = self.path.find_resource(getattr(self, 'typemap', 'fwrap_ktp.in'))
     45 	if not node:
     46 		raise self.bld.errors.WafError('no typemap file declared for %r' % self)
     47 
     48 	f90out = node.change_ext('.f90')
     49 	lst = [node]
     50 	for x in self.tasks:
     51 		if x.inputs and x.inputs[0].name.endswith('.f90'):
     52 			lst.append(x.inputs[0])
     53 	tmtsk = self.typemap_task = self.create_task('modmap', lst, [f90out, node.change_ext('.h')])
     54 
     55 	for x in self.tasks:
     56 		if x.inputs and x.inputs[0].name.endswith('.f90'):
     57 			tmtsk.set_run_after(x)
     58 
     59 	tsk = self.create_compiled_task('fc', f90out)
     60 	tsk.nomod = True # the fortran files won't compile unless all the .mod files are set, ick
     61 
     62 class modmap(Task.Task):
     63 	"""
     64 	create .h and .f90 files, so this must run be executed before any c task
     65 	"""
     66 	ext_out = ['.h'] # before any c task is not mandatory since #732 but i want to be sure (ita)
     67 	def run(self):
     68 		"""
     69 		we need another build context, because we cannot really disable the logger here
     70 		"""
     71 		obld = self.generator.bld
     72 		bld = Build.BuildContext(top_dir=obld.srcnode.abspath(), out_dir=obld.bldnode.abspath())
     73 		bld.init_dirs()
     74 		bld.in_msg = 1 # disable all that comes from bld.msg(..), bld.start_msg(..) and bld.end_msg(...)
     75 		bld.env = self.env.derive()
     76 		node = self.inputs[0]
     77 		bld.logger = Logs.make_logger(node.parent.get_bld().abspath() + os.sep + node.name + '.log', 'build')
     78 
     79 		gen_type_map_files(bld, self.inputs, self.outputs)
     80 
     81 class ctp(object):
     82 	def __init__(self, name, basetype, odecl, use):
     83 		self.name = name
     84 		self.basetype = basetype
     85 		self.odecl = odecl
     86 		self.use = use
     87 		self.fc_type = None
     88 
     89 def gen_type_map_files(bld, inputs, outputs):
     90 
     91 	# The ctp properties (name, basetype, odecl, etc.) would be listed in a
     92 	# configuration file and the ctp list below would be loaded from that file.
     93 	# But the type resolution must be determined at *compile time* (i.e. build
     94 	# time), and can't be determined by static analysis.  This is because each
     95 	# fortran compiler can have a different value for the type resolution.
     96 	# Moreover, the type resolution can depend on an arbitrary number of .mod
     97 	# files and integer expressions.
     98 
     99 	ktp_in = [ip for ip in inputs if ip.name.endswith('.in')][0]
    100 	env = ConfigSet()
    101 	env.load(ktp_in.abspath())
    102 	ctps = []
    103 	for ctp_ in env.CTPS:
    104 		ctps.append(ctp(**ctp_))
    105 
    106 	# the 'use' attribute of the ctp instances above uses the .mod file created
    107 	# after the compilation of fsrc.f90.  The type mapping depends on the .mod
    108 	# file generated, and thus the mapping needs to be discovered during the
    109 	# build stage, not the configuration stage.
    110 
    111 	find_types(bld, ctps)
    112 
    113 	# write fortran -> C mapping to file.
    114 	fort_file = [ff for ff in outputs if ff.name.endswith('.f90')][0]
    115 	c_header = [ch for ch in outputs if ch.name.endswith('.h')][0]
    116 	write_type_map(bld, ctps, fort_file, c_header)
    117 
    118 def find_types(bld, ctps):
    119 	for ctp in ctps:
    120 		fc_type = None
    121 		fc_type = find_fc_type(bld, ctp.basetype,
    122 					ctp.odecl, ctp.use)
    123 		if not fc_type:
    124 			raise bld.errors.WafError(
    125 					"unable to find C type for type %s" % ctp.odecl)
    126 		ctp.fc_type = fc_type
    127 
    128 type_dict = {'integer' : ['c_signed_char', 'c_short', 'c_int', 'c_long', 'c_long_long']}
    129 
    130 def find_fc_type(bld, base_type, decl, use):
    131 	fsrc_tmpl = '''\
    132 subroutine outer(a)
    133   use, intrinsic :: iso_c_binding
    134   implicit none
    135   %(TEST_DECL)s, intent(inout) :: a
    136   interface
    137 	subroutine inner(a)
    138 	  use, intrinsic :: iso_c_binding
    139 	  use %(USE)s
    140 	  implicit none
    141 	  %(TYPE_DECL)s, intent(inout) :: a
    142 	end subroutine inner
    143   end interface
    144   call inner(a)
    145 end subroutine outer
    146 '''
    147 	for ctype in type_dict[base_type]:
    148 		test_decl = '%s(kind=%s)' % (base_type, ctype)
    149 		fsrc = fsrc_tmpl % {'TYPE_DECL' : decl,
    150 							'TEST_DECL' : test_decl,
    151 							'USE' : use}
    152 		try:
    153 			bld.check_cc(
    154 					fragment=fsrc,
    155 					compile_filename='test.f90',
    156 					features='fc',
    157 					includes = bld.bldnode.abspath(),
    158 					)
    159 		except bld.errors.ConfigurationError:
    160 			pass
    161 		else:
    162 			res = ctype
    163 			break
    164 	else:
    165 		res = ''
    166 	return res
    167 
    168 def write_type_map(bld, ctps, fort_file, c_header):
    169 	buf = ['''\
    170 module type_maps
    171 use, intrinsic :: iso_c_binding
    172 implicit none
    173 ''']
    174 	for ctp in ctps:
    175 		buf.append('integer, parameter :: %s = %s' % (ctp.name, ctp.fc_type))
    176 	buf.append('end module type_maps\n')
    177 	fort_file.write('\n'.join(buf))
    178 
    179 	cap_name = '%s__' % c_header.name.upper().replace('.', '_')
    180 	buf = ['''\
    181 #ifndef %s
    182 #define %s
    183 ''' % (cap_name, cap_name)]
    184 	for ctp in ctps:
    185 		# This is just an example, so this would be customized. The 'long long'
    186 		# would correspond to the actual C type...
    187 		buf.append('typedef long long %s\n' % ctp.name)
    188 	buf.append('#endif\n')
    189 	c_header.write('\n'.join(buf))
    190 
    191 # vim:ft=python:noet