CMake Fortran Issues: Difference between revisions
Maikbeckmann (talk | contribs) |
Maikbeckmann (talk | contribs) |
||
Line 227: | Line 227: | ||
*** main.f90 | *** main.f90 | ||
Contents of myproject... | |||
a.f90: | a.f90: |
Revision as of 14:59, 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 one change into the corresponding build folder and enter
- $ make
Simply building isn't the fancy part. Now try to touch a sourcefile of your choise and check if the build order is right.
Note: There are examples which want to show how an external libray providing modules is handled. The corresponding external lib which resided at folder extLib for each of these examples has to be build and installed (just into an include and lib subdir) by typing
- $ make install && make clean
inside folder extLib.
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
- build
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
- build
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 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: 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 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: 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
- provides a module
- uses the provided module
- uses a module of a external library
structure:
- example_dependingOn_externalLib
- extLib
- include
- externalmod.mod
- lib
- libmyextlib.a
- include
- myproject
- build
- Makefile
- prog.dir
- build.make
- a.f90
- main.f90
- build
- extLib
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
- build
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
- include
- 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
- build
- extLib
You can download this example as tarball example_final.tar.gz at http://www.cmake.org/Bug/view.php?id=5809 . The extlib folder contains the source and a Makefile to build and install the lib by typing
- $ make install
Content...
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?
- 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.
- 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.
- In case a required module mymod isn't provided by the same target
- search the build-tree for mymod.mod.stamp
- 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!