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.
This commit is contained in:
Brad King 2016-09-22 10:14:44 -04:00
parent 8eca59a175
commit d3e0b64b14
2 changed files with 135 additions and 0 deletions

View File

@ -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 <algorithm>
#include <ctype.h>
#include <functional>
@ -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<std::string>::const_iterator argBeg,
std::vector<std::string>::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<std::string>::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<std::string> 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<std::string> 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<std::string>::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<std::string>::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<std::string>::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;
}

View File

@ -52,6 +52,9 @@
#include <stdlib.h>
#include <time.h>
int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
std::vector<std::string>::const_iterator argEnd);
void CMakeCommandUsage(const char* program)
{
std::ostringstream errorStream;
@ -782,6 +785,13 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& 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.";