diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 42ce6227a..6da6617eb 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -1408,3 +1408,81 @@ void cmGlobalNinjaGenerator::StripNinjaOutputPathPrefixAsSuffix( EnsureTrailingSlash(path); cmStripSuffixIfExists(path, this->OutputPathPrefix); } + +/* + +We use the following approach to support Fortran. Each target already +has a .dir/ directory used to hold intermediate files for CMake. +For each target, a FortranDependInfo.json file is generated by CMake with +information about include directories, module directories, and the locations +the per-target directories for target dependencies. + +Compilation of source files within a target is split into the following steps: + +1. Preprocess all sources, scan preprocessed output for module dependencies. + This step is done with independent build statements for each source, + and can therefore be done in parallel. + + rule Fortran_PREPROCESS + depfile = $DEP_FILE + command = gfortran -cpp $DEFINES $INCLUDES $FLAGS -E $in -o $out && + cmake -E cmake_ninja_depends \ + --tdi=FortranDependInfo.json --pp=$out --dep=$DEP_FILE \ + --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE + + build src.f90-pp.f90 | src.f90-pp.f90.ddi: Fortran_PREPROCESS src.f90 + OBJ_FILE = src.f90.o + DEP_FILE = src.f90-pp.f90.d + DYNDEP_INTERMEDIATE_FILE = src.f90-pp.f90.ddi + + The ``cmake -E cmake_ninja_depends`` tool reads the preprocessed output + and generates the ninja depfile for preprocessor dependencies. It also + generates a "ddi" file (in a format private to CMake) that lists the + object file that compilation will produce along with the module names + it provides and/or requires. The "ddi" file is an implicit output + because it should not appear in "$out" but is generated by the rule. + +2. Consolidate the per-source module dependencies saved in the "ddi" + files from all sources to produce a ninja "dyndep" file, ``Fortran.dd``. + + rule Fortran_DYNDEP + command = cmake -E cmake_ninja_dyndep \ + --tdi=FortranDependInfo.json --dd=$out $in + + build Fortran.dd: Fortran_DYNDEP src1.f90-pp.f90.ddi src2.f90-pp.f90.ddi + + The ``cmake -E cmake_ninja_dyndep`` tool reads the "ddi" files from all + sources in the target and the ``FortranModules.json`` files from targets + on which the target depends. It computes dependency edges on compilations + that require modules to those that provide the modules. This information + is placed in the ``Fortran.dd`` file for ninja to load later. It also + writes the expected location of modules provided by this target into + ``FortranModules.json`` for use by dependent targets. + +3. Compile all sources after loading dynamically discovered dependencies + of the compilation build statements from their ``dyndep`` bindings. + + rule Fortran_COMPILE + command = gfortran $INCLUDES $FLAGS -c $in -o $out + + build src1.f90.o: Fortran_COMPILE src1.f90-pp.f90 || Fortran.dd + dyndep = Fortran.dd + + The "dyndep" binding tells ninja to load dynamically discovered + dependency information from ``Fortran.dd``. This adds information + such as: + + build src1.f90.o | mod1.mod: dyndep + restat = 1 + + This tells ninja that ``mod1.mod`` is an implicit output of compiling + the object file ``src1.f90.o``. The ``restat`` binding tells it that + the timestamp of the output may not always change. Additionally: + + build src2.f90.o: dyndep | mod1.mod + + This tells ninja that ``mod1.mod`` is a dependency of compiling the + object file ``src2.f90.o``. This ensures that ``src1.f90.o`` and + ``mod1.mod`` will always be up to date before ``src2.f90.o`` is built + (because the latter consumes the module). +*/