Ways to suppress Circularity

I am seeking existing discussions about living with processes that call each other.

I have 2 processes that call each other that have not ever caused recursion in the 14 years of its executable existence. I am having severe problems updating them to gfortran 12+, because MODULEs must not call each other.

This project has 700+ files, many with more than 1 routine in them.
Making a static library of a “service routines subset” was straight forward. But upgrading to “modern” fortran that could attract programmers has introduced administrative problems. I embraced MODULEs and, indeed, doing so revealed coding problems that were (are) resolved that demonstrated its sensibility. It is greatly welcomed.

After I got to having converted 200+ of them to MODULEs and thought I would get aid on how to order compilation for “make” by adding each (or many at least) MODULE’s requirements. And doing the same for those files not yet converted to MODULEs.

It seems this last paragraph is what has brought me Circularity issues. I fought them by rewriting processes to make them go away. But, it was easy to see that Circularity just got “deeper” and difficult to locate the processes that constituted that “deeper”.

Trying to find help has wasted a lot of time. No “standards” references can be expected to go into this issue. Help with this is not going to be found via StackOverflow (easily, that is for sure) because those 2 processes are not simple, and SO wants a best answer for a single question.

Hence this “topic” to which I am clueless to add anything to (yet).

What type of “circularity” are we talking of?

1: actual circular dependencies among modules; these are not allowed AFAIK, but submodules can be used to bypass the issue in practice

module a
  use b
end module

module b
  use a
end module

2: build dependencies as required by (GNU) Make. For these you need a tool (let’s call it the module scanner) which determines the dependencies between modules and expresses them in the Make syntax:

target: ... dependencies...
    rule

For the second type of “circularity” you may want to review the following threads:

Edit: some compilers come with a built-in module scanner you can use

however, the output slightly differs which makes it harder to write a generic Makefile supporting multiple compilers.

For external scanners you could check the one at the links below (both are in Awk and use “dependency” files):

Since GNU Make 4.3 it is also possible define grouped targets with the &: syntax, which could be used instead of dependency files.

2 Likes

I suppose my elaboration was TL;DR.

I have zero problem with gFortran and linux “make” telling me there is Circularity. I believe it. I have not heard of any other type.

I do have many <object_needed>.o list_of_prerequisites

And “make” will tell me “<one_of_prerequisites> needed by <some_object_needed>.o. Prerequisite dropped”.

Then, not surprisingly, “no rule of how make (that) one_of_prerequisites”.

I read once about “Grouped targets” and decided I did not need yet more “convenience” help.

I have SUBMODULEs, but have yet to find a place in these numerous old files still yet to be converted to MODULE-ness where that SUBMODULE “B” and SUBMODULE “C” of MODULE “A” allows “B” to call a process in “C” and “C” can do the same in “B” is helpful when it comes to a practical situation where the code of “B” is so different from the code in “C”.

The “service” routine pair I have “Circularity” trouble with will probably not be the only pair. This particular service routines pair appears in most of the files. That is what finally has me seeking help. I really do not want to modify a hundred files only to discover that my change was NOT the answer. Right now I get an idea (like programming "more’ to seemingly solve the Circularity issue) only to discover it is still there… only “deeper”.

Most of my MODULEs do work with other MODULEs just fine. It was the <needed_module>.o: <prerequisite_“one_of_a_few”> that “got me” as the number of such lines I created became numerous. Adding even more of them is not going to get rid of the “make”-voiced complaint.

Taking a step back, I suppose your 700+ files were not module-based.

If so, you can avoid the circular dependency by using just ONE module, with multiple submodules.

You probably already know which procedures must be made public, but exposing some interfaces from one submodule to another might require some trial-and-error (since the one usually complaining about missing symbols is the linker).

For example:

module bigmod
    implicit none
    private

    ! public interfaces
    interface
        module subroutine a()
        end subroutine
        module function calc() result(x)
            ...
        end subroutine
        ...
    end interface
    public a
    public calc
    ...

    ! private interfaces exposed among submodules
    interface
        module subroutine c()
        end subroutine
        module function proc() result(y)
            ...
        end function
        ...
    end interface
end module

submodule (bigmod) sm_public
    implicit none
contains
    module subroutine a()
        ...
    end subroutine
    module function calc() result(x)
        ...
    end function
    ...
end submodule

submodule (bigmod) sm_private1
    implicit none
contains
    module subroutine c()
        print *, proc()
    end subroutine
    ...
end submodule

submodule (bigmod) sm_private2
    implicit none
contains
    module function proc() result(y)
        ...
        y = calc()
    end function
    ...
end submodule

submodule (bigmod) sm_private3
    ...
end submodule
...

With a simple module-submodules hierarchy, complilation is pretty straightforward —compile module first, then all submodules in any order.

1 Like

If your Makefile doesn’t capture the module dependencies across files correctly, it’s always at risk of breaking, especially in parallel builds with -jN.

The introduction of modules forces builds to follow a particular order, in contrast to the “flat” compilation model of F77 where suffix rules like .f.o (obsolete) or pattern rules %.o : %.f are sufficient. Unfortunately these rules can’t distinguish if a file actually contains modules or just a bunch of external procedures.

People started to tackle this problem as soon as Fortran 90 modules became available. There are numerous threads about this issue at comp.lang.fortran:

@ jwmwalrus

True enough. When I got ahold of the project it was all Solaris F77 + a some c routines. I broke that thing into 2 big pieces. I nursed it to compile as legacy – no small feat. THEN created a 20 line “2nd part” to substitute for the 2nd big piece. And got that combination to breathe. THEN I started on updating the1st piece concentrating on the intertwined, unapologetic spaghetti code. ALL of it is public.

Your suggestion of 1 MODULE and the rest SUBMODULEs would NEVER have occurred to me. I will give that a try using your example as a guide. First, I will read carefully about private | public. I did not suspect I might use it to my advantage as a programmer updating 100% public code.

I will report back in a few days.

1 Like

My searches never took me to comp.lang.fortran
There is a lot there it seems for updating.

I am exhausted searching. Fortran-lang.discourse is giving me the help I thirst for. Mr. Walrus’ 1st.

When using modules (as opposed to the F77-style procedures), you get explicit interfaces —i.e., the procedure signatures and their invocations get proper/better checking from the compiler.

But in Fortran, modules also provide encapsulation (public | private and protected).

Of the 700+ procedures you have, probably only a handful need to be exposed for public USE, while the rest can remain private. If that’s the case, then the single-module-multiple-submodules approach works for you.

(As of now, gfortran has a submodule-related bug when submodules try to access symbols declared in the parent module)

Otherwise, if most or all of the 700+ procedures need to be made public, then you’re better off keeping them in separate files and maybe providing modules with just the interface blocks. This is what LAPACK95 does, btw.

1 Like

When I have unexpected circular module dependencies, I try to resolve them in two seemingly contradictory ways.

One way is to combine the two (or more) modules into a single module. This allows the contained routines to call each other while eliminating the circular logic problem.

The other way, ironically, is to reorganize the two modules and create a third module that includes only the interdependent routines. Say the original modules A and B have a circular dependence because there is a routine in module A that is referenced by module B. Then create a new C with just that routine. The new modules A and B must then have USE C statements in the appropriate contained subprograms, and of course the makefile (or whatever build system you have) must be updated to reflect the new dependencies.

Of course, sometimes this is more complicated than just a single shared subprogram, and sometimes it involves also organizing shared data which presumably began its life in legacy common blocks.

I did try that, but only halfheartedly, because name changes would affect most of the project files. Making THAT just running a sed program would lead down a rabbit hole of assuring that each file’s references to such a prospective change would need to be marked in some fashion to surely should be changed, e. g. NOT in the !History: section and similar. I have been through similar and it just eats on me that I am not directly headed toward my goal for this project when I descend into the warren.

I respect and read nearly everything you post that I run across, so I will spend the time to make your suggestion a member of my ready at hand schemes when getting surprises as I update (and update the update, sigh) to an acceptable, working whole.