I am working on a fortran codebase (monolithic model for weather/climate simulations) for which I wish to refactor a part of its source code to a fully external library called libfeature
. The reason I want to do this is so that the institute I am working at can make consolidated code contributions to libfeature
rather than having a bunch of forks of the main model codebase. The model codebase currently has an externals/
directory with many externals (e.g., libmodelmath
) and would be an appropriate location for libfeature
. Note that the model can be configured to enable or disable such externals with
./configure --enable-feature
However, libfeature
is dependent on a lot of core source code in the model like e.g., mo_grid.f90
. This ends up creating a circular dependency when trying to extract feature
. An example source tree of the current model is in graphic (a) below.
If I attempt to extract feature
into a new library and then place it in the externals
folder as libfeature
, I would inevitably have a dependency with the model codebase (now named desired-model
). So if the model codebase source tree now looks graphic (b) above, it is clear that a circular dependency arises in
desired-model/externals/libfeature/dependencies
since a dependency of libfeature
is desired-model
.
Are there any Fortran (or C/C++ for that matter) codebases that encounter and solve a similar issue that I might use as a reference to inform my refactoring? I’m finding this circular dependency problem to be challenging to reason about and plan, so any other advice would be very much appreciated!
I list here some things I have also considered about this refactoring process:
(1) For the Release
build of libfeature
, dependencies/desired-model
should not be compiled since this would be redundant. The parent desired-model
will already compile e.g., src/mo_grid.f90
so it wouldn’t make sense to also have externals/libfeature/dependencies/desired-model/src/mo_grid.f90
be compiled.
(2) For the Debug
build of libfeature
, system tests would (I think) necessitate running dependency/desired-model
since one would want to verify that a full simulation works once one makes changes to libfeature
. Under such a test, dependency/desired-model/libfeature
should point to the in-development version of libfeature
(i.e., the one in the current directory) instead of using the usual .gitmodules
logic to download a different version of libfeature
(i.e., one that is stable and in a remote repo).
(3) libfeature
developers might need to make changes to desired-model/src
by adding statements like
#ifdef LIBFEATURE
CALL foo()
#endif`
This does not seem like something to be too concerned about because (a) pushing to submodule repositories is streamlined (see this stackoverflow), and (b) there is precedent in the desired-model
codebase where such preprocessor directives are used to add other enabled features but not libfeature
yet. For example, in the actual codebase, there is an external library called ART
and its functionality is enabled via
#ifdef __ICON_ART
CALL art_rad_aero_interface( .... )
#endif
This is what I mean by precedent. Obviously, this is not necessarily a good practice in terms of maintainability, but so it goes.
Note, the above simplified problem is based on a real problem with the publicly available icon-model codebase. For reference, libfeature
corresponds to icon-model/src/upper_atmosphere
and grid
corresponds to icon-model/src/shr_horizontal