Skip to content

Commit

Permalink
Clarify that common blocks don't actually work
Browse files Browse the repository at this point in the history
  • Loading branch information
rjfarmer committed Mar 11, 2024
1 parent e167a2f commit 4cc7906
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 28 deletions.
23 changes: 3 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ To run unit tests
- [x] Allocatable arrays of strings
- [ ] Classes
- [ ] Abstract interfaces
- [x] Common blocks (partial)
- [ ] Common blocks
- [ ] Equivalences
- [ ] Namelists
- [ ] Quad precision variables
Expand Down Expand Up @@ -347,28 +347,11 @@ To run unit tests
- [x] Unary operations (arguments that involve an expression to evaluate like dimension(n+1) or dimension((2*n)+1))
- [x] Functions returning an explicit array as their result

### Accessing common block elements
### Common block elements

There's no direct way to access the common block elements, but if you declare the common block as a module variable you may access the elements by their name:
There is no way currently to access components of a common block.


````fortran
module my_mod
implicit none
integer :: a,b,c
common /comm1/ a,b,c
````

Elements in the common block can thus be accessed as:

````python
x.a
x.b
x.c
````

## Accessing module file data

For those wanting to explore the module file format, there is a routine ``mod_info`` available from the top-level ``gfort2py`` module:
Expand Down
35 changes: 35 additions & 0 deletions gfort2py/fCompile.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,41 @@ def compile_and_load(
return os.path.join(output_dir, lib_name), module_filename(mname, output_dir)


def common_compile(
string=None,
gfort=None,
FC=None,
FFLAGS="-O2",
LDLIBS="",
LDFLAGS="",
output=None,
):
if "\n" in string:
string = string.split("\n")

name = "c" + hashlib.md5(b"".join([i.encode() for i in string])).hexdigest()

string = "\n".join([f"module {name}", *string, "contains", "end module"])

lib_path = Path(os.path.realpath(gfort._lib._name))

LDLIBS = (
LDLIBS + f"-l:{lib_path.name}"
) # colon is needed to search for exact name not lib{name}
LDFLAGS = LDFLAGS + f"-L{lib_path.parent}"

FFLAGS += f" -Wl,-rpath='.',-rpath='{lib_path.parent}' -Wl,--no-undefined"

return compile_and_load(
string=string,
FC=FC,
FFLAGS=FFLAGS,
LDLIBS=LDLIBS,
LDFLAGS=LDFLAGS,
output=output,
)


class CompileError(Exception):
pass

Expand Down
3 changes: 3 additions & 0 deletions gfort2py/fVar.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ def __new__(cls, obj, *args, **kwargs):
we are currently dealing with.
"""
if obj.in_common_block():
raise AttributeError("Can not access variables defined in common blocks")

if obj.is_derived():
if obj.is_array():
if obj.is_explicit():
Expand Down
74 changes: 69 additions & 5 deletions gfort2py/gfort2py.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .fVar import fVar
from .fProc import fProc
from .fParameters import fParam
from .fCompile import compile_and_load
from .fCompile import compile_and_load, common_compile
from .utils import library_ext

_TEST_FLAG = os.environ.get("_GFORT2PY_TEST_FLAG") is not None
Expand Down Expand Up @@ -174,17 +174,17 @@ def compile(
This code will then be converted into a Fortran module and
compiled.
FC specifies the Fortran compilier to be used. This
FC specifies the Fortran compiler to be used. This
must be some version of gfortran
FFLAGS specifies Fortran compile options. This defaults
to -O2. We will additionaly insert flags for buidling
to -O2. We will additionally insert flags for building
shared libraries on the current platform.
LDLIBS specifies any libraries to link agaisnt
LDLIBS specifies any libraries to link against
LDFLAGS specifies extra argument to pass to the linker
(usally this is specifing the directory of where libraies are
(usually this is specifying the directory of where libraries are
stored and passed with the -L option)
output Path to store intermediate files. Defaults to None
Expand All @@ -207,3 +207,67 @@ def compile(
)

return fFort(library, mod_file, cache_folder=cache_folder)


# Does not work needs https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47030 applied

# def common(
# string=None,
# gfort=None,
# *,
# FC=None,
# FFLAGS="-O2",
# LDLIBS="",
# LDFLAGS="",
# output=None,
# cache_folder=None,
# ):
# """
# Compiles and loads a snippet of Fortran code for accessing common blocks.

# Code must be provided as a valid bit of Fortran that declares the variables
# and declares the common block, eg:

# fstr = "
# integer :: a_int,b_int,c_int
# common /common_name/ a_int,b_int,c_int
# "

# This code will then be converted into a Fortran module and
# compiled.

# gfort specifies an already loaded instance of fFort contains the common block

# FC specifies the Fortran compiler to be used. This
# must be some version of gfortran

# FFLAGS specifies Fortran compile options. This defaults
# to -O2. We will additionally insert flags for building
# shared libraries on the current platform.

# LDLIBS specifies any libraries to link against

# LDFLAGS specifies extra argument to pass to the linker
# (usually this is specifying the directory of where libraries are
# stored and passed with the -L option)

# output Path to store intermediate files. Defaults to None
# where files are stored in a temp folder. Otherwise
# stored in ``output`` folder.

# cache_folder same as for fFort, specifies location to save cached
# mod data to.

# """

# library, mod_file = common_compile(
# string=string,
# gfort=gfort,
# FC=FC,
# FFLAGS=FFLAGS,
# LDLIBS=LDLIBS,
# LDFLAGS=LDFLAGS,
# output=output,
# )

# return fFort(library, mod_file, cache_folder=cache_folder)
2 changes: 1 addition & 1 deletion gfort2py/module_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def is_pdt_def(self):
return "PDT_TEMPLATE" in self.sym.attr.attributes

def in_common_block(self):
return "IN_CMMMON" in self.sym.attr.attributes
return "IN_COMMON" in self.sym.attr.attributes

@property
def strlen(self):
Expand Down
5 changes: 3 additions & 2 deletions tests/common.py → tests/common_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@

class TestCommonBlocks:
def test_set_values(self):
x.x_int = 1
assert x.x_int == 1
with pytest.raises(AttributeError) as cm:
x.x_int = 1
assert x.x_int == 1

def test_get_comm(self):
assert len(x._module.common)

0 comments on commit 4cc7906

Please sign in to comment.