waf/demos/fortran/typemap/wscript

192 lines
5.4 KiB
Python

#! /usr/bin/env python
top = '.'
out = 'build'
def options(opt):
opt.load('compiler_c')
opt.load('compiler_fc')
def configure(conf):
conf.load('compiler_c')
conf.load('compiler_fc')
conf.check_fortran()
# configuration tests that may be totally irrelevant
conf.check_fortran_dummy_main()
if not conf.env.IFORT_WIN32:
conf.check_fortran_verbose_flag()
conf.check_fortran_clib()
conf.check_fortran_mangling()
def build(bld):
bld(
features = 'fc typemap fcshlib',
source = 'fsrc.f90 basetypes.f90',
defs = 'fsrc.def',
target = 'foo',
)
import os
from waflib import Logs, Build, Utils
from waflib import TaskGen, Task
from waflib.ConfigSet import ConfigSet
@TaskGen.feature('typemap')
@TaskGen.after('process_source')
@TaskGen.before('apply_link')
def process_typemaps(self):
"""
modmap: *.f90 + foo.in -> foo.h + foo.f90
compile foo.f90 like the others
"""
node = self.path.find_resource(getattr(self, 'typemap', 'fwrap_ktp.in'))
if not node:
raise self.bld.errors.WafError('no typemap file declared for %r' % self)
f90out = node.change_ext('.f90')
lst = [node]
for x in self.tasks:
if x.inputs and x.inputs[0].name.endswith('.f90'):
lst.append(x.inputs[0])
tmtsk = self.typemap_task = self.create_task('modmap', lst, [f90out, node.change_ext('.h')])
for x in self.tasks:
if x.inputs and x.inputs[0].name.endswith('.f90'):
tmtsk.set_run_after(x)
tsk = self.create_compiled_task('fc', f90out)
tsk.nomod = True # the fortran files won't compile unless all the .mod files are set, ick
class modmap(Task.Task):
"""
create .h and .f90 files, so this must run be executed before any c task
"""
ext_out = ['.h'] # before any c task is not mandatory since #732 but i want to be sure (ita)
def run(self):
"""
we need another build context, because we cannot really disable the logger here
"""
obld = self.generator.bld
bld = Build.BuildContext(top_dir=obld.srcnode.abspath(), out_dir=obld.bldnode.abspath())
bld.init_dirs()
bld.in_msg = 1 # disable all that comes from bld.msg(..), bld.start_msg(..) and bld.end_msg(...)
bld.env = self.env.derive()
node = self.inputs[0]
bld.logger = Logs.make_logger(node.parent.get_bld().abspath() + os.sep + node.name + '.log', 'build')
gen_type_map_files(bld, self.inputs, self.outputs)
class ctp(object):
def __init__(self, name, basetype, odecl, use):
self.name = name
self.basetype = basetype
self.odecl = odecl
self.use = use
self.fc_type = None
def gen_type_map_files(bld, inputs, outputs):
# The ctp properties (name, basetype, odecl, etc.) would be listed in a
# configuration file and the ctp list below would be loaded from that file.
# But the type resolution must be determined at *compile time* (i.e. build
# time), and can't be determined by static analysis. This is because each
# fortran compiler can have a different value for the type resolution.
# Moreover, the type resolution can depend on an arbitrary number of .mod
# files and integer expressions.
ktp_in = [ip for ip in inputs if ip.name.endswith('.in')][0]
env = ConfigSet()
env.load(ktp_in.abspath())
ctps = []
for ctp_ in env.CTPS:
ctps.append(ctp(**ctp_))
# the 'use' attribute of the ctp instances above uses the .mod file created
# after the compilation of fsrc.f90. The type mapping depends on the .mod
# file generated, and thus the mapping needs to be discovered during the
# build stage, not the configuration stage.
find_types(bld, ctps)
# write fortran -> C mapping to file.
fort_file = [ff for ff in outputs if ff.name.endswith('.f90')][0]
c_header = [ch for ch in outputs if ch.name.endswith('.h')][0]
write_type_map(bld, ctps, fort_file, c_header)
def find_types(bld, ctps):
for ctp in ctps:
fc_type = None
fc_type = find_fc_type(bld, ctp.basetype,
ctp.odecl, ctp.use)
if not fc_type:
raise bld.errors.WafError(
"unable to find C type for type %s" % ctp.odecl)
ctp.fc_type = fc_type
type_dict = {'integer' : ['c_signed_char', 'c_short', 'c_int', 'c_long', 'c_long_long']}
def find_fc_type(bld, base_type, decl, use):
fsrc_tmpl = '''\
subroutine outer(a)
use, intrinsic :: iso_c_binding
implicit none
%(TEST_DECL)s, intent(inout) :: a
interface
subroutine inner(a)
use, intrinsic :: iso_c_binding
use %(USE)s
implicit none
%(TYPE_DECL)s, intent(inout) :: a
end subroutine inner
end interface
call inner(a)
end subroutine outer
'''
for ctype in type_dict[base_type]:
test_decl = '%s(kind=%s)' % (base_type, ctype)
fsrc = fsrc_tmpl % {'TYPE_DECL' : decl,
'TEST_DECL' : test_decl,
'USE' : use}
try:
bld.check_cc(
fragment=fsrc,
compile_filename='test.f90',
features='fc',
includes = bld.bldnode.abspath(),
)
except bld.errors.ConfigurationError:
pass
else:
res = ctype
break
else:
res = ''
return res
def write_type_map(bld, ctps, fort_file, c_header):
buf = ['''\
module type_maps
use, intrinsic :: iso_c_binding
implicit none
''']
for ctp in ctps:
buf.append('integer, parameter :: %s = %s' % (ctp.name, ctp.fc_type))
buf.append('end module type_maps\n')
fort_file.write('\n'.join(buf))
cap_name = '%s__' % c_header.name.upper().replace('.', '_')
buf = ['''\
#ifndef %s
#define %s
''' % (cap_name, cap_name)]
for ctp in ctps:
# This is just an example, so this would be customized. The 'long long'
# would correspond to the actual C type...
buf.append('typedef long long %s\n' % ctp.name)
buf.append('#endif\n')
c_header.write('\n'.join(buf))
# vim:ft=python:noet