Skip to content

Commit e738f36

Browse files
committed
Convert Fortran interface binding file to JSON
Signed-off-by: Jake Tronge <jtronge@lanl.gov>
1 parent 8695511 commit e738f36

File tree

9 files changed

+450
-167
lines changed

9 files changed

+450
-167
lines changed

docs/developers/bindings.rst

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,47 @@ generated file name must be of the form ``generated_${basename}.c``, where
6969
Fortran Bindings
7070
----------------
7171

72-
Adding a new Fortran binding may be as simple as adding an additional prototype
73-
to ``ompi/mpi/fortran/use-mpi-f08/interface.in``, as long as all the required
74-
types are already supported by the binding code. Below is an example prototype
75-
for ``MPI_Waitall``:
72+
To add a new Fortran binding, or update an existing one, one will need to
73+
modify the ``ompi/mpi/fortran/use-mpi-f08/interface.json`` file; this JSON file
74+
contains a list of prototype objects, including information about their name
75+
and each parameter passed. Below is an example for ``MPI_Waitall``:
7676

7777
.. code-block::
7878
79-
.waitall(SHORTCUT_COUNT count, REQUEST_ARRAY array_of_requests[count=count],
80-
STATUS_ARRAY array_of_statuses[count=count])
79+
{
80+
"name": "waitall",
81+
"parameters": [
82+
{
83+
"type": "SHORTCUT_COUNT",
84+
"name": "count"
85+
},
86+
{
87+
"type": "REQUEST_ARRAY",
88+
"name": "array_of_requests",
89+
"dep_params": {
90+
"count": "count"
91+
}
92+
},
93+
{
94+
"type": "STATUS_ARRAY",
95+
"name": "array_of_statuses",
96+
"dep_params": {
97+
"count": "count"
98+
}
99+
}
100+
]
101+
}
81102
82-
First, notice that the function name is listed with the ``MPI_`` prefix removed
83-
and in all lowercase along with a ``.`` before the name---this makes it easier
84-
to delimit prototypes across multiple lines. Parameters are listed in a
85-
simplified ``TYPE NAME`` form, with an optional ``[key=value;...]`` attribute
86-
coming after if required for the specific type. This key-value attribute is
87-
used to specify dependencies betwen parameters, where the key is validated by
88-
the type and the value must be the name of another parameter.
103+
This object includes two properties: the ``name`` property holding the
104+
subroutine name, converted to lowercase and the ``mpi_`` prefix removed; and
105+
the ``parameters`` property describing all parameters, their types and
106+
dependencies. Some parameters may depend on other types and this is listed in
107+
the ``dep_params`` field. An example of this can be seen with
108+
``array_of_requests`` above, in which ``dep_params`` holds a key-value pair
109+
``"count": "count"``, where the key ``count`` corresponds to a key required by
110+
the ``REQUEST_ARRAY`` type and the value ``count`` to the name of another
111+
parameter. These parameter dependencies are specific to the types used and are
112+
validated by the binding scripts.
89113

90114
The Fortran binding code not only generates Fortran, but also additional
91115
wrapping C code that calls into the C bindings, making conversions and checking

ompi/mpi/bindings/bindings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def main():
3232
# Fortran set up code
3333
parser_fortran = subparsers.add_parser('fortran', help='subcommand for generating Fortran code')
3434
parser_fortran.add_argument('--ts', action='store_true', help='generate bindings w/o TS 29113 support')
35-
parser_fortran.add_argument('--template', required=True, help='template file to use')
35+
parser_fortran.add_argument('--prototypes', required=True, help='JSON prototype file to use')
3636
subparsers_fortran = parser_fortran.add_subparsers()
3737
# Handler for generating actual code
3838
parser_code = subparsers_fortran.add_parser('code', help='generate binding code')

ompi/mpi/bindings/ompi_bindings/fortran.py

Lines changed: 28 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -15,104 +15,36 @@
1515
listed.
1616
"""
1717
from collections import namedtuple
18+
import json
1819
import re
1920
from ompi_bindings import compiler, consts, util
2021
from ompi_bindings.fortran_type import FortranType
2122

2223

2324
FortranParameter = namedtuple('FortranParameter', ['type_name', 'name', 'dep_params'])
24-
FortranPrototype = namedtuple('FortranPrototype', ['fn_name', 'lno', 'parameters'])
25-
26-
27-
def parse_prototype(lno, line):
28-
"""Parse a prototype for the given line and string."""
29-
if consts.PROTOTYPE_RE.match(line) is None:
30-
raise util.BindingError(
31-
f'Invalid function prototype for Fortran interface starting on line {lno}'
32-
)
33-
34-
start = line.index('(')
35-
end = line.index(')')
36-
fn_name = line[1:start].strip()
37-
parameters = line[start+1:end].split(',')
38-
39-
# Attempt to parse each parameter
40-
parsed_parameters = []
41-
try:
42-
for param in parameters:
43-
param = param.strip()
44-
param_parts = param.split()
45-
type_name = param_parts[0]
46-
name = ''.join(param_parts[1:])
47-
type_ = FortranType.get(type_name)
48-
dep_params = None
49-
50-
# Check for 'param[name=param(;name=param)]' parameters,
51-
# indicating a dependency on that other parameter
52-
if '[' in name:
53-
idx = name.index('[')
54-
dep_params = [part.split('=') for part in name[idx+1:-1].split(';')]
55-
dep_params = dict(dep_params)
56-
name = name[:idx]
57-
58-
# Validate the parameter key values (or an empty list if not found)
59-
type_.validate_dep_param_keys(name, [] if dep_params is None else dep_params.keys())
60-
61-
parsed_parameters.append(FortranParameter(type_name, name, dep_params))
62-
return FortranPrototype(fn_name, lno, parsed_parameters)
63-
except util.BindingError as err:
64-
raise util.BindingError(
65-
f'Failed to parse prototype starting on line {lno}: {err}'
66-
) from None
67-
except KeyError as err:
68-
raise util.BindingError(
69-
f'Found invalid type starting on line {lno}: {err}'
70-
) from None
25+
FortranPrototype = namedtuple('FortranPrototype', ['fn_name', 'parameters'])
7126

7227

7328
def load_prototypes(fname):
74-
"""Load the prototypes from a file."""
29+
"""Load the prototypes from a JSON file."""
7530
with open(fname) as fp:
76-
tmp_proto_string = []
77-
proto_strings = []
78-
cur_lno = 0
79-
reading_proto = False
80-
81-
# The loop below is designed to read in each prototype, each delimited
82-
# by a '.' preceding the name of the subroutine, allowing for the
83-
# prototype to run across multiple lines.
84-
for i, line in enumerate(fp):
85-
lno = i + 1
86-
line = line.strip()
87-
88-
# First strip comments
89-
comm_idx = line.find('#')
90-
if comm_idx != -1:
91-
line = line[:comm_idx]
92-
if not line:
93-
continue
94-
95-
# Set the current line for the prototype
96-
if not tmp_proto_string:
97-
cur_lno = lno
98-
99-
# Check for initial '.' character, indicating the start of a prototype
100-
reading_proto = reading_proto or line[0] == '.'
101-
if line[0] == '.' and tmp_proto_string:
102-
# If the buffer is not empty, then the previous prototype
103-
# string is complete and can be saved
104-
proto_strings.append((cur_lno, ' '.join(tmp_proto_string)))
105-
cur_lno = lno
106-
tmp_proto_string = []
107-
# Only add the line to the current buffer if we already encountered
108-
# a '.' at the start of this or a previous line
109-
if reading_proto:
110-
tmp_proto_string.append(line)
111-
112-
if tmp_proto_string:
113-
proto_strings.append((cur_lno, ' '.join(tmp_proto_string)))
114-
115-
return [parse_prototype(lno, proto_string) for lno, proto_string in proto_strings]
31+
data = json.load(fp)
32+
prototypes = []
33+
for proto in data:
34+
fn_name = proto['name']
35+
parameters = []
36+
for param in proto['parameters']:
37+
type_name = param['type']
38+
type_ = FortranType.get(type_name)
39+
param_name = param['name']
40+
dep_params = param['dep_params'] if 'dep_params' in param else None
41+
try:
42+
type_.validate_dep_param_keys(param_name, [] if dep_params is None else dep_params.keys())
43+
except util.BindingError as err:
44+
raise util.BindingError(f'Invalid prototype "{fn_name}": {err}') from None
45+
parameters.append(FortranParameter(type_name, param_name, dep_params))
46+
prototypes.append(FortranPrototype(fn_name, parameters))
47+
return prototypes
11648

11749

11850
class FortranBinding:
@@ -136,13 +68,13 @@ def __init__(self, prototype, out, bigcount=False, ts=False):
13668
if param.dep_params is not None:
13769
dep_params[param.name] = param.dep_params
13870
# Set dependent parameters for those that need them
139-
try:
140-
for name, deps in dep_params.items():
71+
for name, deps in dep_params.items():
72+
try:
14173
param_map[name].dep_params = {key: param_map[dep_name] for key, dep_name in deps.items()}
142-
except KeyError as err:
143-
raise util.BindingError(
144-
f'Invalid dependent type in prototype starting on line {prototype.lno}: {err}'
145-
)
74+
except KeyError as err:
75+
raise util.BindingError(
76+
f'Invalid dependent type for parameter "{name}" (prototype "{prototype.fn_name}"): {err}'
77+
) from None
14678

14779
def dump(self, *pargs, **kwargs):
14880
"""Write to the output file."""
@@ -370,7 +302,7 @@ def print_binding(prototype, lang, out, bigcount=False, ts=False):
370302

371303
def generate_code(args, out):
372304
"""Generate binding code based on arguments."""
373-
prototypes = load_prototypes(args.template)
305+
prototypes = load_prototypes(args.prototypes)
374306
if args.lang == 'fortran':
375307
print_f_source_header(out)
376308
out.dump()
@@ -388,7 +320,7 @@ def generate_code(args, out):
388320

389321
def generate_interface(args, out):
390322
"""Generate the Fortran interface files."""
391-
prototypes = load_prototypes(args.template)
323+
prototypes = load_prototypes(args.prototypes)
392324
out.dump(f'! {consts.GENERATED_MESSAGE}')
393325
for prototype in prototypes:
394326
ext_name = util.ext_api_func_name(prototype.fn_name)

ompi/mpi/bindings/ompi_bindings/fortran_type.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ def validate_dep_param_keys(param_name, keys):
4949
"""Validate the keys that are allowed to be used for a dependent param."""
5050
# No dependent parameters allowed by default
5151
if keys:
52-
raise util.FortranBindingError(
53-
f'Invalid keys found for parameter {param_name}: {list(keys)}'
52+
raise util.BindingError(
53+
f'Invalid keys found for parameter "{param_name}": {list(keys)}'
5454
)
5555

5656
@property

ompi/mpi/fortran/use-mpi-f08/Makefile.am

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -565,23 +565,23 @@ lib@OMPI_LIBMPI_NAME@_usempif08_profile_la_FCFLAGS = \
565565
-DOMPI_BUILD_MPI_PROFILING=1
566566

567567

568-
# Include script and template files for ABI generation from a distribution
568+
# Include script and JSON file for ABI generation from a distribution
569569
# tarball
570-
EXTRA_DIST = interface.in
570+
EXTRA_DIST = interface.json
571571

572572
#
573573
# Generate the Fortran bindings and C wrapper functions for bindings with a
574574
# *.in template.
575575
#
576576

577577
if OMPI_GENERATE_BINDINGS
578-
api_f08_generated.F90: interface.in
578+
api_f08_generated.F90: interface.json
579579
$(OMPI_V_GEN) $(PYTHON) $(top_srcdir)/ompi/mpi/bindings/bindings.py \
580580
--builddir $(abs_top_builddir) \
581581
--srcdir $(abs_top_srcdir) \
582582
--output $(abs_builddir)/$@ \
583583
fortran \
584-
--template $(abs_srcdir)/$< \
584+
--prototypes $(abs_srcdir)/$< \
585585
code \
586586
fortran
587587

ompi/mpi/fortran/use-mpi-f08/base/Makefile.am

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,24 +49,24 @@ endif
4949
# binding scripts in order to generate the specific version for their compiler.
5050
#
5151
if OMPI_GENERATE_BINDINGS
52-
api_f08_generated.c: ../interface.in
52+
api_f08_generated.c: ../interface.json
5353
$(OMPI_V_GEN) $(PYTHON) $(top_srcdir)/ompi/mpi/bindings/bindings.py \
5454
--builddir $(abs_top_builddir) \
5555
--srcdir $(abs_top_srcdir) \
5656
--output $(abs_builddir)/$@ \
5757
fortran \
58-
--template $(abs_srcdir)/$< \
58+
--prototypes $(abs_srcdir)/$< \
5959
code \
6060
c
6161

62-
api_f08_ts_generated.c: ../interface.in
62+
api_f08_ts_generated.c: ../interface.json
6363
$(OMPI_V_GEN) $(PYTHON) $(top_srcdir)/ompi/mpi/bindings/bindings.py \
6464
--builddir $(abs_top_builddir) \
6565
--srcdir $(abs_top_srcdir) \
6666
--output $(abs_builddir)/$@ \
6767
fortran \
6868
--ts \
69-
--template $(abs_srcdir)/$< \
69+
--prototypes $(abs_srcdir)/$< \
7070
code \
7171
c
7272

ompi/mpi/fortran/use-mpi-f08/interface.in

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)