[CMake] Fortran 90 modules not rebuilt

Brad King brad.king at kitware.com
Wed Nov 17 13:18:40 EST 2010


On 11/17/2010 12:15 PM, K. Noel Belcourt wrote:
> On Nov 17, 2010, at 10:02 AM, Brad King wrote:
>> This tells CMake to print some verbose target dependency
>> information.  I bet it will report "modules" in a non-trivial
>> connected component with a bunch of other libraries.  Please
>> send me the output.
>
> It's attached.

The key output is this:

--------------------------------------------------
Component (1):
  contains target 5 [bonus]
  contains target 9 [bur]
  contains target 13 [cav]
  contains target 17 [cftf]
  contains target 21 [cor]
  contains target 25 [cvh]
  contains target 29 [dch]
  contains target 33 [edf]
  contains target 38 [eos]
  contains target 42 [esf]
  contains target 46 [exec]
  contains target 50 [fdi]
  contains target 54 [fp]
  contains target 58 [h2c]
  contains target 62 [hs]
  contains target 69 [modules]
  contains target 73 [mp]
  contains target 82 [rn1]
  contains target 86 [rn2]
  contains target 90 [spr]
  contains target 95 [tp]
  contains target 99 [util]
--------------------------------------------------

showing that indeed "modules" is in the middle of a big cycle
of the dependency graph.  Later we see

--------------------------------------------------
The final target dependency graph is:
...
target 69 is [modules]
  depends on target 73 [mp]
...
target 73 is [mp]
  depends on target 82 [rn1]
...
target 82 is [rn1]
  depends on target 86 [rn2]
...
target 86 is [rn2]
  depends on target 90 [spr]
...
target 90 is [spr]
  depends on target 95 [tp]
...
target 95 is [tp]
  depends on target 99 [util]
...
target 99 is [util]
--------------------------------------------------

which is exactly the dependency order you see at "make" time.
Here is what is going on...

Given the original global target dependency graph CMake needs
to compute a final dependency graph to give to the make tool.
Normally the final graph is the same as the original graph
when there are no circular dependencies.  Most target types
(shared libs, executables, module libs, custom targets) are
never involved in dependency cycles because each needs to
link to its dependencies and thus there is a natural lack of
cycles.

Static libraries are really just archives of object files and
as such do not link.  Therefore they can be built in any order
and circular dependencies are allowed.  However, CMake cannot
just hand the original dependencies over to the make tool
because it will warn and randomly drop dependencies to break
cycles.  Instead CMake must compute a new final dependency
graph that has no cycles.

CMake solves this problem by first converting the original
directed graph into a directed *acyclic* graph in which
each node of the new graph is a strongly connected component
of the original graph.  In this intermediate form, all the
normal targets appear in trivial (size 1) components but
static libraries may appear in larger components.

The final graph is computed from the intermediate form by
choosing for each non-trivial component one member as its
"head" and another as its "tail".  In trivial (size 1)
components the head and tail are the same.  Inter-component
dependencies are output in the final graph as a dependency
of the tail of one component on the head of the other.
That leaves only the problem of choosing the proper head
and tail of each non-trivial component and some set of
intra-component dependencies between them.

Intra-component dependencies are computed by essentially
throwing out all the "link" dependencies among the static
libraries within the component.  Then a topological sort
on remaining "util" dependencies, which come from commands
like add_dependencies and can never have cycles, chooses
a safe order in which to build the static libraries.
The final intra-component dependencies are merely a
linked-list between a head and tail, plus the "util"
dependencies.

In pure C and C++ libraries there are normally no "util"
dependencies among any static library cycles because all the
objects can build in any order.  This is not the case for
Fortran 90 though.  Module producers must compile before
module consumers.  This is what you are trying to enforce
by moving all the module producers into a single target.

The problem is that nothing has told CMake about these
module-induced dependencies.  CMake's Fortran dep scanning
does not occur until build time which is too late to affect the
target build order (also it only orders files, not targets).
The solution is to add a line like

  add_dependencies(mylib modules)

for all the libraries whose source files depend on any modules.
This can probably just be done for all the libraries.

You'll also need to use CMake 2.8.3 because 2.8.2 and lower did
not honor "util" dependencies during intra-component dependency
selection.  Fortunately I discovered the bug by accident while
working on something else before 2.8.3 and fixed it.

-Brad


More information about the CMake mailing list