From d3e0b64b1432700a71a10ddf06084f7bb8500694 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 22 Sep 2016 10:14:44 -0400 Subject: Ninja: Add internal tool to scan Fortran code for module dependencies Create an internal `cmake -E cmake_ninja_depends` tool to scan an already-preprocessed Fortran translation unit for modules that it provides or requires. Save the information in a "ddi" file with a CMake-private format for intermediate dynamic dependency information. This file may the be loaded by another tool to be added later. --- Source/cmGlobalNinjaGenerator.cxx | 125 ++++++++++++++++++++++++++++++++++++++ Source/cmcmd.cxx | 10 +++ 2 files changed, 135 insertions(+) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 6da6617..e6babe6 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -14,6 +14,7 @@ #include "cmAlgorithms.h" #include "cmDocumentationEntry.h" +#include "cmFortranParser.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpressionEvaluationFile.h" #include "cmGeneratorTarget.h" @@ -28,6 +29,9 @@ #include "cmVersion.h" #include "cmake.h" +#include "cm_jsoncpp_reader.h" +#include "cm_jsoncpp_writer.h" + #include #include #include @@ -1486,3 +1490,124 @@ Compilation of source files within a target is split into the following steps: ``mod1.mod`` will always be up to date before ``src2.f90.o`` is built (because the latter consumes the module). */ + +int cmcmd_cmake_ninja_depends(std::vector::const_iterator argBeg, + std::vector::const_iterator argEnd) +{ + std::string arg_tdi; + std::string arg_pp; + std::string arg_dep; + std::string arg_obj; + std::string arg_ddi; + for (std::vector::const_iterator a = argBeg; a != argEnd; ++a) { + std::string const& arg = *a; + if (cmHasLiteralPrefix(arg, "--tdi=")) { + arg_tdi = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--pp=")) { + arg_pp = arg.substr(5); + } else if (cmHasLiteralPrefix(arg, "--dep=")) { + arg_dep = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--obj=")) { + arg_obj = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--ddi=")) { + arg_ddi = arg.substr(6); + } else { + cmSystemTools::Error("-E cmake_ninja_depends unknown argument: ", + arg.c_str()); + return 1; + } + } + if (arg_tdi.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --tdi="); + return 1; + } + if (arg_pp.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --pp="); + return 1; + } + if (arg_dep.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --dep="); + return 1; + } + if (arg_obj.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --obj="); + return 1; + } + if (arg_ddi.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --ddi="); + return 1; + } + + std::vector includes; + { + Json::Value tdio; + Json::Value const& tdi = tdio; + { + cmsys::ifstream tdif(arg_tdi.c_str(), std::ios::in | std::ios::binary); + Json::Reader reader; + if (!reader.parse(tdif, tdio, false)) { + cmSystemTools::Error("-E cmake_ninja_depends failed to parse ", + arg_tdi.c_str(), + reader.getFormattedErrorMessages().c_str()); + return 1; + } + } + + Json::Value const& tdi_include_dirs = tdi["include-dirs"]; + if (tdi_include_dirs.isArray()) { + for (Json::Value::const_iterator i = tdi_include_dirs.begin(); + i != tdi_include_dirs.end(); ++i) { + includes.push_back(i->asString()); + } + } + } + + cmFortranSourceInfo info; + std::set defines; + cmFortranParser parser(includes, defines, info); + if (!cmFortranParser_FilePush(&parser, arg_pp.c_str())) { + cmSystemTools::Error("-E cmake_ninja_depends failed to open ", + arg_pp.c_str()); + return 1; + } + if (cmFortran_yyparse(parser.Scanner) != 0) { + // Failed to parse the file. + return 1; + } + + { + cmGeneratedFileStream depfile(arg_dep.c_str()); + depfile << cmSystemTools::ConvertToUnixOutputPath(arg_pp) << ":"; + for (std::set::iterator i = info.Includes.begin(); + i != info.Includes.end(); ++i) { + depfile << " \\\n " << cmSystemTools::ConvertToUnixOutputPath(*i); + } + depfile << "\n"; + } + + Json::Value ddi(Json::objectValue); + ddi["object"] = arg_obj; + + Json::Value& ddi_provides = ddi["provides"] = Json::arrayValue; + for (std::set::iterator i = info.Provides.begin(); + i != info.Provides.end(); ++i) { + ddi_provides.append(*i); + } + Json::Value& ddi_requires = ddi["requires"] = Json::arrayValue; + for (std::set::iterator i = info.Requires.begin(); + i != info.Requires.end(); ++i) { + // Require modules not provided in the same source. + if (!info.Provides.count(*i)) { + ddi_requires.append(*i); + } + } + + cmGeneratedFileStream ddif(arg_ddi.c_str()); + ddif << ddi; + if (!ddif) { + cmSystemTools::Error("-E cmake_ninja_depends failed to write ", + arg_ddi.c_str()); + return 1; + } + return 0; +} diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 9daed4b..998a7ad 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -52,6 +52,9 @@ #include #include +int cmcmd_cmake_ninja_depends(std::vector::const_iterator argBeg, + std::vector::const_iterator argEnd); + void CMakeCommandUsage(const char* program) { std::ostringstream errorStream; @@ -782,6 +785,13 @@ int cmcmd::ExecuteCMakeCommand(std::vector& args) return cmcmd::ExecuteLinkScript(args); } +#ifdef CMAKE_BUILD_WITH_CMAKE + // Internal CMake ninja dependency scanning support. + else if (args[1] == "cmake_ninja_depends") { + return cmcmd_cmake_ninja_depends(args.begin() + 2, args.end()); + } +#endif + // Internal CMake unimplemented feature notification. else if (args[1] == "cmake_unimplemented_variable") { std::cerr << "Feature not implemented for this platform."; -- cgit v0.12