CMake Fortran Issues: Difference between revisions

From KitwarePublic
Jump to navigationJump to search
Line 163: Line 163:
You'll see that a.f90 is recompiled and prog.dir/prog  is linked again. But main.f90 has to recompiled too, since a module dependency is a compile time dependency like an include.
You'll see that a.f90 is recompiled and prog.dir/prog  is linked again. But main.f90 has to recompiled too, since a module dependency is a compile time dependency like an include.


=== Rules similar to those that should be generated by CMake ===
=== Rules like those that should be generated by CMake ===


Makefile:
Makefile:

Revision as of 15:51, 8 October 2007

Introduction

CMake has a number of Fortran issues that have been discussed many different times on list and duplicated a fair number of times in the bug tracker as well.

Maik Beckmann is trying to make sense of all the confusion by collecting information on all Fortran issues at http://www.cmake.org/Bug/view.php?id=5809

Please join the work there by

  • Contributing patches.
  • Testing the patches that already exist there.
  • Reporting things that don't work.
  • Sending simplified examples of things which don't work.
  • Sharing your expert knowledge of CMake.

Concepts expressed using Makefiles

This section is intended to discuss the Makefile rules which CMake has to generate. All examples are fully working. You can download them as tarball examples_using_Makefiles.tar.gz at http://www.cmake.org/Bug/view.php?id=5809. To build an example, change into the corresponding build directory and run the

$ make

command. After this initial build, check dependencies by touching source files of your choice and running the

$ make

command again.

Note: For examples which show how an external library providing modules is handled, the external library which resides at directory extLib for each of these examples has to be built and installed by changing into the corresponding extLib directory and running the

$ make install && make clean

command.

A simple program

A f9x program which is build by compiling in linking two source files a.f90 and main.f90. The tree structure is:

  • example_simpleProgram
    • build
      • Makefile
      • prog.dir
        • build.make
    • main.f90
    • a.f90

a.f90:

SUBROUTINE printHello
    WRITE(*,*) "Hello f9x world"	
END SUBROUTINE

main.f90:

PROGRAM hello 
    CALL printHello
END PROGRAM

Makefile:

all: prog.dir/all

prog.dir/all:
	$(MAKE) -f prog.dir/build.make prog.dir/all

clean:
	$(MAKE) -f prog.dir/build.make prog.dir/clean

build.make:

prog.dir/all: prog.dir/prog

prog.dir/prog:  prog.dir/a.o prog.dir/main.o
	gfortran -o prog.dir/prog  prog.dir/a.o prog.dir/main.o
	
prog.dir/a.o: ../a.f90
	gfortran -o prog.dir/a.o  -c ../a.f90
	
prog.dir/main.o: ../main.f90 
	gfortran -o prog.dir/main.o  -c ../main.f90 
	
prog.dir/clean:
	rm prog.dir/a.o prog.dir/main.o prog.dir/prog

The rules generated by the current CMake covers all dependencies which can occur as long as no modules are used.

You can download this example as tarball example_simpleProgram.tar.gz at http://www.cmake.org/Bug/view.php?id=5809

A simple program with module

The same as before, but now a.f90 provides a module which main.f90 uses. The tree structure is:

  • example_simpleProgram_withModule
    • build
      • Makefile
      • prog.dir
        • build.make
    • main.f90
    • a.f90

a.f90:

MODULE localMod 
!
CONTAINS
    SUBROUTINE printHello
        WRITE(*,*) "Hello f9x world"
    END SUBROUTINE
END MODULE

main.f90:

PROGRAM hello 
    USE localMod
    CALL printHello
END PROGRAM

Rules like those generated by current CMake

Makefile:

all: prog.dir/all

prog.dir/all:
	$(MAKE) -f prog.dir/build.make prog.dir/requires
	$(MAKE) -f prog.dir/build.make prog.dir/all

clean:
	$(MAKE) -f prog.dir/build.make prog.dir/clean

build.make:

prog.dir/all: prog.dir/prog


prog.dir/prog:  prog.dir/a.o prog.dir/main.o
	gfortran -o prog.dir/prog  prog.dir/a.o prog.dir/main.o 

prog.dir/a.o: ../a.f90
	gfortran -o prog.dir/a.o  -c ../a.f90 -M prog.dir
	
prog.dir/localmod.mod: prog.dir/a.o	
	
prog.dir/main.o: ../main.f90 
	gfortran -o prog.dir/main.o  -c ../main.f90 -I prog.dir 
	
prog.dir/clean:
	rm prog.dir/localmod.mod prog.dir/a.o prog.dir/main.o prog.dir/prog
	
	
localmod.mod.proxy: prog.dir/a.o

prog.dir/main.o.requires: localmod.mod.proxy
	
prog.dir/requires: prog.dir/main.o.requires

After you build prog using this set of Makefiles do (you're at the build directory)

 $ touch ../a.f90

and enter

 $ make

You'll see that a.f90 is recompiled and prog.dir/prog is linked again. But main.f90 has to recompiled too, since a module dependency is a compile time dependency like an include.

Rules like those that should be generated by CMake

Makefile:

all: prog.dir/all

prog.dir/all:
	$(MAKE) -f prog.dir/build.make prog.dir/all

clean:
	$(MAKE) -f prog.dir/build.make prog.dir/clean

build.make:

prog.dir/all: prog.dir/prog


prog.dir/prog:  prog.dir/a.o prog.dir/main.o
	gfortran -o prog.dir/prog  prog.dir/a.o prog.dir/main.o 

prog.dir/a.o: ../a.f90
	gfortran -o prog.dir/a.o  -c ../a.f90 -M prog.dir
	
prog.dir/localmod.mod: prog.dir/a.o	
	
prog.dir/main.o: ../main.f90 prog.dir/localmod.mod
	gfortran -o prog.dir/main.o  -c ../main.f90 -I prog.dir 
	
prog.dir/clean:
	rm prog.dir/localmod.mod prog.dir/a.o prog.dir/main.o prog.dir/prog

After you build prog using this set of Makefiles do (you're at the build directory)

 $ touch ../a.f90

and enter

 $ make

You'll see that a.f90 is recompiled, like the current CMake does, but main.f90 is recompiled too, as it should be.

Executable depending on external lib

This example build a executable target which

  1. provides a module
  2. uses the provided module
  3. uses a module of a external library

structure:

  • example_dependingOn_externalLib
    • extLib
      • include
        • externalmod.mod
      • lib
        • libmyextlib.a
    • myproject
      • build
        • Makefile
        • prog.dir
          • build.make
      • a.f90
      • main.f90

Contents of myproject...

a.f90:

MODULE localMod 
!
CONTAINS
    SUBROUTINE printLocalModGreeting
        WRITE(*,*) "Greetings from Module localMod"
    END SUBROUTINE
END MODULE

main.f90:

PROGRAM hello 
    USE localMod
    USE externalMod
    CALL printLocalModGreeting
    CALL printExtModGreeting
END PROGRAM

rules like they are generated by current CMake

Makefile:

all: prog.dir/all

prog.dir/all:
	$(MAKE) -f prog.dir/build.make prog.dir/requires
	$(MAKE) -f prog.dir/build.make prog.dir/all

clean:
	$(MAKE) -f prog.dir/build.make prog.dir/clean

build.make:

prog.dir/all: prog.dir/prog


prog.dir/prog: ../../extLib/lib/libmyextlib.a
prog.dir/prog: prog.dir/a.o prog.dir/main.o
	gfortran -o prog.dir/prog  prog.dir/a.o prog.dir/main.o ../../extLib/lib/libmyextlib.a

prog.dir/a.o: ../a.f90
	gfortran -o prog.dir/a.o  -c ../a.f90 -M prog.dir
	
prog.dir/main.o: ../main.f90 
	gfortran -o prog.dir/main.o	 -c ../main.f90 -I prog.dir -I ../../extLib/include
	
prog.dir/clean:
	rm prog.dir/localmod.mod prog.dir/a.o prog.dir/main.o prog.dir/prog
	
	
externalmod.mod.proxy: # dummy 

localmod.mod.proxy: prog.dir/a.o

prog.dir/main.o.requires: localmod.mod.proxy externalmod.mod.proxy
	
prog.dir/requires: prog.dir/main.o.requires

This rules got the same problem as the example above (simple Program with module) plus it doesn't recognizes if the external modules got updated.

rules like they could be generated

Makefile:

all: prog.dir/all

prog.dir/all:
	$(MAKE) -f prog.dir/build.make prog.dir/all

clean:
	$(MAKE) -f prog.dir/build.make prog.dir/clean

build.make:

prog.dir/all: prog.dir/prog


prog.dir/prog: ../../extLib/lib/libmyextlib.a
prog.dir/prog: prog.dir/a.o prog.dir/main.o
	gfortran -o prog.dir/prog  prog.dir/a.o prog.dir/main.o ../../extLib/lib/libmyextlib.a

prog.dir/a.o: ../a.f90
	gfortran -o prog.dir/a.o  -c ../a.f90 -M prog.dir
	
prog.dir/localmod.mod: prog.dir/a.o	
	
prog.dir/main.o: ../main.f90 prog.dir/localmod.mod
	gfortran -o prog.dir/main.o	 -c ../main.f90 -I prog.dir -I ../../extLib/include
	
prog.dir/clean:
	rm prog.dir/localmod.mod prog.dir/a.o prog.dir/main.o prog.dir/prog

These rules build everything in proper order and consider the timestamp of externalmod.mod.

Executable target depending on lib target

structure:

  • example_depending_libTarget
    • build
      • Makefile
      • lib.dir
        • build.make
        • libmodx.mod.stamp
        • libmody.mod.stamp
      • prog.dir
        • build.make
    • lib
      • a.f90
      • b.f90
    • prog
      • a.f90
      • main.f90

contents...

lib/a.f90:

MODULE libModX 
    USE libModY
END MODULE

lib/b.f90:

MODULE libModY
END MODULE

prog/a.f90:

MODULE localMod
END MODULE

prog/main.f90:

PROGRAM hello
    USE localMod
    USE libModX

    WRITE(*,*) 'Hello, F90 world.'
END PROGRAM

rules like they are generated by current CMake

build/Makefile:

all: lib.dir/all prog.dir/all
	

lib.dir/all:
	$(MAKE) -f lib.dir/build.make lib.dir/requires
	$(MAKE) -f lib.dir/build.make lib.dir/all

prog.dir/all: lib.dir/all
	$(MAKE) -f prog.dir/build.make  prog.dir/requires
	$(MAKE) -f prog.dir/build.make  prog.dir/all

	
clean:
	$(MAKE) -f prog.dir/build.make prog.dir/clean
	$(MAKE) -f lib.dir/build.make lib.dir/clean

build/lib.dir/build.make:

lib.dir/all: lib.dir/mylib

lib.dir/mylib: lib.dir/libmylib.a

lib.dir/libmylib.a: lib.dir/a.o lib.dir/b.o 
	ar rc lib.dir/libmylib.a lib.dir/a.o lib.dir/b.o  
	ranlib lib.dir/libmylib.a

lib.dir/a.o: ../lib/a.f90 
	gfortran -o lib.dir/a.o -c ../lib/a.f90 -M lib.dir

	
lib.dir/b.o: ../lib/b.f90 
	gfortran -o lib.dir/b.o -c ../lib/b.f90  -M lib.dir

 
libmody.mod.proxy: lib.dir/b.o

lib.dir/a.o.requires: libmody.mod.proxy
	
lib.dir/requires:  lib.dir/a.o.requires  


lib.dir/clean:
	rm lib.dir/a.o lib.dir/b.o lib.dir/libmylib.a
	rm lib.dir/libmodx.mod lib.dir/libmody.mod

build/prog.dir/build.make:

prog.dir/all: prog.dir/prog


prog.dir/prog: prog.dir/main.o prog.dir/a.o
	gfortran -o prog.dir/prog  prog.dir/main.o prog.dir/a.o  lib.dir/libmylib.a 

prog.dir/a.o: ../prog/a.f90
	gfortran -o prog.dir/a.o -c ../prog/a.f90 -M prog.dir
	
prog.dir/localmod.mod: prog.dir/a.o


prog.dir/main.o: ../prog/main.f90 
	gfortran -o prog.dir/main.o -c ../prog/main.f90 -I lib.dir -I prog.dir


localmod.mod.proxy: prog.dir/a.o
libmodx.mod.proxy: # dummy 

prog.dir/main.o.requires: localmod.mod.proxy libmodx.mod.proxy
	
prog.dir/requires: prog.dir/main.o.requires	


prog.dir/clean:
	rm prog.dir/a.o prog.dir/main.o prog.dir/prog prog.dir/localmod.mod

Again everything is build, but isn't updated proper.

rules like they could be generated

build/Makefile:

all: lib.dir/all prog.dir/all
	

lib.dir/all:
	$(MAKE) -f lib.dir/build.make lib.dir/all

prog.dir/all: lib.dir/all
	$(MAKE) -f prog.dir/build.make prog.dir/all

build/lib.dir/build.make:

lib.dir/all: lib.dir/mylib

lib.dir/mylib: lib.dir/libmylib.a

lib.dir/libmylib.a: lib.dir/a.o lib.dir/b.o 
	ar rc lib.dir/libmylib.a lib.dir/a.o lib.dir/b.o  
	ranlib lib.dir/libmylib.a

lib.dir/a.o: ../lib/a.f90 lib.dir/libmody.mod 
	gfortran -o lib.dir/a.o -c ../lib/a.f90 -M lib.dir
	touch lib.dir/libmodx.mod.stamp
	
lib.dir/b.o: ../lib/b.f90 
	gfortran -o lib.dir/b.o -c ../lib/b.f90  -M lib.dir
	touch lib.dir/libmody.mod.stamp
	

lib.dir/libmodx.mod: lib.dir/a.o
lib.dir/libmody.mod: lib.dir/b.o


lib.dir/clean:
	rm lib.dir/a.o lib.dir/b.o lib.dir/libmylib.a
	rm lib.dir/libmodx.mod lib.dir/libmody.mod

build/prog.dir/build.make:

prog.dir/all: prog.dir/prog


prog.dir/prog: prog.dir/main.o prog.dir/a.o
	gfortran -o prog.dir/prog  prog.dir/main.o prog.dir/a.o  lib.dir/libmylib.a 

prog.dir/a.o: ../prog/a.f90
	gfortran -o prog.dir/a.o -c ../prog/a.f90 -M prog.dir
	
prog.dir/localmod.mod: prog.dir/a.o

prog.dir/main.o: lib.dir/libmodx.mod.stamp
prog.dir/main.o: ../prog/main.f90 prog.dir/localmod.mod 
	gfortran -o prog.dir/main.o -c ../prog/main.f90 -I lib.dir -I prog.dir



prog.dir/clean:
	rm prog.dir/a.o prog.dir/main.o prog.dir/prog prog.dir/localmod.mod

Finally: Executable target depending on lib target and external lib

structure:

  • example_final
    • extLib
      • include
        • externalmod.mod
      • lib
        • libmyextlib.a
    • myproject
      • build
        • Makefile
        • lib.dir
          • build.make
          • libmodx.mod.stamp
          • libmody.mod.stamp
        • prog.dir
          • build.make
      • lib
        • a.f90
        • b.f90
      • prog
        • a.f90
        • main.f90


Contents...

lib/a.f90:

MODULE libModX 
    USE libModY
END MODULE

lib/b.f90:

MODULE libModY
END MODULE

prog/a.f90:

MODULE localMod
END MODULE


prog/b.f90:

PROGRAM hello
    USE localMod
    USE libModX
    USE externalMod

    WRITE(*,*) 'Hello, F90 world.'
    CALL printExtModGreeting
END PROGRAM


rules like they are generated by current CMake

build/Makefile:

all: lib.dir/all prog.dir/all
	

lib.dir/all:
	$(MAKE) -f lib.dir/build.make lib.dir/requires
	$(MAKE) -f lib.dir/build.make lib.dir/all

prog.dir/all: lib.dir/all
	$(MAKE) -f prog.dir/build.make prog.dir/requires
	$(MAKE) -f prog.dir/build.make prog.dir/all

	
clean:
	$(MAKE) -f prog.dir/build.make prog.dir/clean
	$(MAKE) -f lib.dir/build.make lib.dir/clean

build/lib.dir/build.make:

lib.dir/all: lib.dir/mylib

lib.dir/mylib: lib.dir/libmylib.a

lib.dir/libmylib.a: lib.dir/a.o lib.dir/b.o 
	ar rc lib.dir/libmylib.a lib.dir/a.o lib.dir/b.o  
	ranlib lib.dir/libmylib.a

lib.dir/a.o: ../lib/a.f90 
	gfortran -o lib.dir/a.o -c ../lib/a.f90 -M lib.dir
	
lib.dir/b.o: ../lib/b.f90 
	gfortran -o lib.dir/b.o -c ../lib/b.f90  -M lib.dir
	

libmody.mod.proxy: lib.dir/b.o

lib.dir/b.o.requires: libmody.mod.proxy

lib.dir/requires: lib.dir/b.o.requires


lib.dir/clean:
	rm lib.dir/a.o lib.dir/b.o lib.dir/libmylib.a
	rm lib.dir/libmodx.mod lib.dir/libmody.mod

build/prog.dir/build.make:

prog.dir/all: prog.dir/prog

prog.dir/prog: ../../extLib/lib/libmyextlib.a
prog.dir/prog: prog.dir/main.o prog.dir/a.o ../../extLib/lib/libmyextlib.a
	gfortran -o prog.dir/prog  prog.dir/main.o prog.dir/a.o  lib.dir/libmylib.a ../../extLib/lib/libmyextlib.a

prog.dir/a.o: ../prog/a.f90
	gfortran -o prog.dir/a.o -c ../prog/a.f90 -M prog.dir
	
prog.dir/main.o: 
prog.dir/main.o: ../prog/main.f90 
	gfortran -o prog.dir/main.o -c ../prog/main.f90 -I lib.dir -I prog.dir -I ../../extLib/include


localmod.mod.proxy: prog.dir/a.o
libmodx.mod.proxy: # dummy
externalmod.mod.proxy: # dummy

prog.dir/main.o.requires: localmod.mod.proxy libmodx.mod.proxy externalmod.mod.proxy

prog.dir/requires: prog.dir/main.o.requires

prog.dir/clean:
	rm prog.dir/a.o prog.dir/main.o prog.dir/prog prog.dir/localmod.mod

rules like they could be generated

build/Makefile:

all: lib.dir/all prog.dir/all
	

lib.dir/all:
	$(MAKE) -f lib.dir/build.make lib.dir/all

prog.dir/all: lib.dir/all
	$(MAKE) -f prog.dir/build.make prog.dir/all

	
clean:
	$(MAKE) -f prog.dir/build.make prog.dir/clean
	$(MAKE) -f lib.dir/build.make lib.dir/clean

build/lib.dir/build.make:

lib.dir/all: lib.dir/mylib

lib.dir/mylib: lib.dir/libmylib.a

lib.dir/libmylib.a: lib.dir/a.o lib.dir/b.o 
	ar rc lib.dir/libmylib.a lib.dir/a.o lib.dir/b.o  
	ranlib lib.dir/libmylib.a

lib.dir/a.o: ../lib/a.f90 lib.dir/libmody.mod 
	gfortran -o lib.dir/a.o -c ../lib/a.f90 -M lib.dir
	touch lib.dir/libmodx.mod.stamp
	
lib.dir/b.o: ../lib/b.f90 
	gfortran -o lib.dir/b.o -c ../lib/b.f90  -M lib.dir
	touch lib.dir/libmody.mod.stamp
	

lib.dir/libmodx.mod: lib.dir/a.o
lib.dir/libmody.mod: lib.dir/b.o


lib.dir/clean:
	rm lib.dir/a.o lib.dir/b.o lib.dir/libmylib.a
	rm lib.dir/libmodx.mod lib.dir/libmody.mod

build/prog.dir/build.make:

prog.dir/all: prog.dir/prog

prog.dir/prog: ../../extLib/lib/libmyextlib.a
prog.dir/prog: prog.dir/main.o prog.dir/a.o ../../extLib/lib/libmyextlib.a
	gfortran -o prog.dir/prog  prog.dir/main.o prog.dir/a.o  lib.dir/libmylib.a ../../extLib/lib/libmyextlib.a

prog.dir/a.o: ../prog/a.f90
	gfortran -o prog.dir/a.o -c ../prog/a.f90 -M prog.dir
	
prog.dir/localmod.mod: prog.dir/a.o

prog.dir/main.o: lib.dir/libmodx.mod.stamp
prog.dir/main.o: ../../extLib/include/externalmod.mod
prog.dir/main.o: ../prog/main.f90 prog.dir/localmod.mod 
	gfortran -o prog.dir/main.o -c ../prog/main.f90 -I lib.dir -I prog.dir -I ../../extLib/include



prog.dir/clean:
	rm prog.dir/a.o prog.dir/main.o prog.dir/prog prog.dir/localmod.mod

Conclusion

What keeps CMake from doing it like could be done shown above?

For each target CMake parses the source files and writes the dependencies one by one. This is ok for includes. But doing it this way CMake cannot determine if a required module of source file a.f90 is provided by a source file b.f90 of the same target or not. This is IMHO the reason why the CMake developer droped a direct dependency of the source file to a required module. As shown in the rules like they are generated by current CMak sections an extra step called required was introduced. This works out if one want just to build, but isn't enought for developer needs (because recompilation isn't done as expected).

What kind of changes have to be done to make it happen?

  1. Before the actually dependency tracking starts, CMake has to parse all fortran sources and to create a corresponding file i.e. mymodule.mod.stamp for mymodule. This can be done soucre by source. Now CMake is able to search the build tree for a module.
  2. Rather than doing it one by one, all sources of a target have to be parsed before starting to write dependencies. This way CMake knows if a required modules is provided by itself or not.
  3. In case a required module mymod isn't provided by the same target
    1. search the build-tree for mymod.mod.stamp
    2. if not found search it at the include paths


Module dependency tracking superseeds include dependency tracking but

Note: IMHO the code responsible for C/C++/Java dependency generation shouldn't be touched, since speed is a imporant advantage of CMake for developers!