mirror of https://gitlab.com/ita1024/waf.git
192 lines
5.4 KiB
Python
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
|