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