From 4cc790633d585d3253916ecb00f9378a7c35d0d4 Mon Sep 17 00:00:00 2001 From: Robert Farmer Date: Mon, 11 Mar 2024 21:33:51 +0000 Subject: [PATCH] Clarify that common blocks don't actually work --- README.md | 23 ++------- gfort2py/fCompile.py | 35 ++++++++++++++ gfort2py/fVar.py | 3 ++ gfort2py/gfort2py.py | 74 +++++++++++++++++++++++++++-- gfort2py/module_parse.py | 2 +- tests/{common.py => common_test.py} | 5 +- 6 files changed, 114 insertions(+), 28 deletions(-) rename tests/{common.py => common_test.py} (79%) diff --git a/README.md b/README.md index c751d72..810ecfb 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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: diff --git a/gfort2py/fCompile.py b/gfort2py/fCompile.py index 95aed5d..c15f243 100644 --- a/gfort2py/fCompile.py +++ b/gfort2py/fCompile.py @@ -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 diff --git a/gfort2py/fVar.py b/gfort2py/fVar.py index 7f75ec6..d1f6e30 100644 --- a/gfort2py/fVar.py +++ b/gfort2py/fVar.py @@ -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(): diff --git a/gfort2py/gfort2py.py b/gfort2py/gfort2py.py index fc67003..502f193 100644 --- a/gfort2py/gfort2py.py +++ b/gfort2py/gfort2py.py @@ -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 @@ -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 @@ -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) diff --git a/gfort2py/module_parse.py b/gfort2py/module_parse.py index e14bf71..3c5004b 100644 --- a/gfort2py/module_parse.py +++ b/gfort2py/module_parse.py @@ -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): diff --git a/tests/common.py b/tests/common_test.py similarity index 79% rename from tests/common.py rename to tests/common_test.py index f987044..4a14c2b 100644 --- a/tests/common.py +++ b/tests/common_test.py @@ -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)