summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2020-11-18 12:32:15 (GMT)
committerKitware Robot <kwrobot@kitware.com>2020-11-18 12:32:21 (GMT)
commit04b6de78a7937222856bd81a1f1d347324241123 (patch)
tree61da73778025cdf1a8d34235e92d419a95cdfe23 /Source
parentac4deef27139a2365d156f1eab8c62a407a3992a (diff)
parentf3eed2c49d38ef95261f687c8c0c549990d37501 (diff)
downloadCMake-04b6de78a7937222856bd81a1f1d347324241123.zip
CMake-04b6de78a7937222856bd81a1f1d347324241123.tar.gz
CMake-04b6de78a7937222856bd81a1f1d347324241123.tar.bz2
Merge topic 'ninja-fortran-ddi-format'
f3eed2c49d cmGlobalNinjaGenerator: use P1689 dependency file format for Fortran a02c4ccabc cmScanDepFormat: add reader and writer for the format from P1689R2 fa18069ebd Ninja: Exclude unused dyndep features during CMake bootstrap Acked-by: Kitware Robot <kwrobot@kitware.com> Acked-by: Michael Hirsch, Ph.D. <michael@scivision.dev> Merge-request: !5485
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt1
-rw-r--r--Source/cmGlobalNinjaGenerator.cxx135
-rw-r--r--Source/cmNinjaTargetGenerator.cxx2
-rw-r--r--Source/cmScanDepFormat.cxx289
-rw-r--r--Source/cmScanDepFormat.h30
-rw-r--r--Source/cmcmd.cxx2
6 files changed, 378 insertions, 81 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index cb954e5..d1616ad 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -834,6 +834,7 @@ endif()
# Ninja support
set(SRCS ${SRCS}
+ cmScanDepFormat.cxx
cmGlobalNinjaGenerator.cxx
cmGlobalNinjaGenerator.h
cmNinjaTypes.h
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 83d15ab..7ef69f4 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -34,6 +34,7 @@
#include "cmOutputConverter.h"
#include "cmProperty.h"
#include "cmRange.h"
+#include "cmScanDepFormat.h"
#include "cmState.h"
#include "cmStateDirectory.h"
#include "cmStateSnapshot.h"
@@ -2024,6 +2025,8 @@ void cmGlobalNinjaGenerator::StripNinjaOutputPathPrefixAsSuffix(
cmStripSuffixIfExists(path, this->OutputPathPrefix);
}
+#if !defined(CMAKE_BOOTSTRAP)
+
/*
We use the following approach to support Fortran. Each target already
@@ -2103,16 +2106,6 @@ Compilation of source files within a target is split into the following steps:
(because the latter consumes the module).
*/
-struct cmSourceInfo
-{
- // Set of provided and required modules.
- std::set<std::string> Provides;
- std::set<std::string> Requires;
-
- // Set of files included in the translation unit.
- std::set<std::string> Includes;
-};
-
static std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
std::string const& arg_tdi, std::string const& arg_pp);
@@ -2120,6 +2113,7 @@ 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_src;
std::string arg_pp;
std::string arg_dep;
std::string arg_obj;
@@ -2128,6 +2122,8 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
for (std::string const& arg : cmMakeRange(argBeg, argEnd)) {
if (cmHasLiteralPrefix(arg, "--tdi=")) {
arg_tdi = arg.substr(6);
+ } else if (cmHasLiteralPrefix(arg, "--src=")) {
+ arg_src = arg.substr(6);
} else if (cmHasLiteralPrefix(arg, "--pp=")) {
arg_pp = arg.substr(5);
} else if (cmHasLiteralPrefix(arg, "--dep=")) {
@@ -2168,6 +2164,9 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
cmSystemTools::Error("-E cmake_ninja_depends requires value for --lang=");
return 1;
}
+ if (arg_src.empty()) {
+ arg_src = cmStrCat("<", arg_obj, " input file>");
+ }
std::unique_ptr<cmSourceInfo> info;
if (arg_lang == "Fortran") {
@@ -2184,6 +2183,8 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
return 1;
}
+ info->PrimaryOutput = arg_obj;
+
{
cmGeneratedFileStream depfile(arg_dep);
depfile << cmSystemTools::ConvertToUnixOutputPath(arg_pp) << ":";
@@ -2193,24 +2194,7 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
depfile << "\n";
}
- Json::Value ddi(Json::objectValue);
- ddi["object"] = arg_obj;
-
- Json::Value& ddi_provides = ddi["provides"] = Json::arrayValue;
- for (std::string const& provide : info->Provides) {
- ddi_provides.append(provide);
- }
- Json::Value& ddi_requires = ddi["requires"] = Json::arrayValue;
- for (std::string const& r : info->Requires) {
- // Require modules not provided in the same source.
- if (!info->Provides.count(r)) {
- ddi_requires.append(r);
- }
- }
-
- cmGeneratedFileStream ddif(arg_ddi);
- ddif << ddi;
- if (!ddif) {
+ if (!cmScanDepFormat_P1689_Write(arg_ddi, arg_src, *info)) {
cmSystemTools::Error(
cmStrCat("-E cmake_ninja_depends failed to write ", arg_ddi));
return 1;
@@ -2268,19 +2252,28 @@ std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
}
auto info = cm::make_unique<cmSourceInfo>();
- info->Provides = finfo.Provides;
- info->Requires = finfo.Requires;
- info->Includes = finfo.Includes;
+ for (std::string const& provide : finfo.Provides) {
+ cmSourceReqInfo src_info;
+ src_info.LogicalName = provide;
+ src_info.CompiledModulePath = provide;
+ info->Provides.emplace_back(src_info);
+ }
+ for (std::string const& require : finfo.Requires) {
+ // Require modules not provided in the same source.
+ if (finfo.Provides.count(require)) {
+ continue;
+ }
+ cmSourceReqInfo src_info;
+ src_info.LogicalName = require;
+ src_info.CompiledModulePath = require;
+ info->Requires.emplace_back(src_info);
+ }
+ for (std::string const& include : finfo.Includes) {
+ info->Includes.push_back(include);
+ }
return info;
}
-struct cmDyndepObjectInfo
-{
- std::string Object;
- std::vector<std::string> Provides;
- std::vector<std::string> Requires;
-};
-
bool cmGlobalNinjaGenerator::WriteDyndepFile(
std::string const& dir_top_src, std::string const& dir_top_bld,
std::string const& dir_cur_src, std::string const& dir_cur_bld,
@@ -2302,34 +2295,14 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
this->LocalGenerators.push_back(std::move(lgd));
}
- std::vector<cmDyndepObjectInfo> objects;
+ std::vector<cmSourceInfo> objects;
for (std::string const& arg_ddi : arg_ddis) {
- // Load the ddi file and compute the module file paths it provides.
- Json::Value ddio;
- Json::Value const& ddi = ddio;
- cmsys::ifstream ddif(arg_ddi.c_str(), std::ios::in | std::ios::binary);
- Json::Reader reader;
- if (!reader.parse(ddif, ddio, false)) {
- cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
- arg_ddi,
- reader.getFormattedErrorMessages()));
+ cmSourceInfo info;
+ if (!cmScanDepFormat_P1689_Parse(arg_ddi, &info)) {
+ cmSystemTools::Error(
+ cmStrCat("-E cmake_ninja_dyndep failed to parse ddi file ", arg_ddi));
return false;
}
-
- cmDyndepObjectInfo info;
- info.Object = ddi["object"].asString();
- Json::Value const& ddi_provides = ddi["provides"];
- if (ddi_provides.isArray()) {
- for (auto const& ddi_provide : ddi_provides) {
- info.Provides.push_back(ddi_provide.asString());
- }
- }
- Json::Value const& ddi_requires = ddi["requires"];
- if (ddi_requires.isArray()) {
- for (auto const& ddi_require : ddi_requires) {
- info.Requires.push_back(ddi_require.asString());
- }
- }
objects.push_back(std::move(info));
}
@@ -2360,11 +2333,12 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
// We do this after loading the modules provided by linked targets
// in case we have one of the same name that must be preferred.
Json::Value tm = Json::objectValue;
- for (cmDyndepObjectInfo const& object : objects) {
- for (std::string const& p : object.Provides) {
- std::string const mod = cmStrCat(module_dir, p);
- mod_files[p] = mod;
- tm[p] = mod;
+ for (cmSourceInfo const& object : objects) {
+ for (auto const& p : object.Provides) {
+ std::string const mod = cmStrCat(
+ module_dir, cmSystemTools::GetFilenameName(p.CompiledModulePath));
+ mod_files[p.LogicalName] = mod;
+ tm[p.LogicalName] = mod;
}
}
@@ -2374,15 +2348,16 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
{
cmNinjaBuild build("dyndep");
build.Outputs.emplace_back("");
- for (cmDyndepObjectInfo const& object : objects) {
- build.Outputs[0] = object.Object;
+ for (cmSourceInfo const& object : objects) {
+ build.Outputs[0] = this->ConvertToNinjaPath(object.PrimaryOutput);
build.ImplicitOuts.clear();
- for (std::string const& p : object.Provides) {
- build.ImplicitOuts.push_back(this->ConvertToNinjaPath(mod_files[p]));
+ for (auto const& p : object.Provides) {
+ build.ImplicitOuts.push_back(
+ this->ConvertToNinjaPath(mod_files[p.LogicalName]));
}
build.ImplicitDeps.clear();
- for (std::string const& r : object.Requires) {
- auto mit = mod_files.find(r);
+ for (auto const& r : object.Requires) {
+ auto mit = mod_files.find(r.LogicalName);
if (mit != mod_files.end()) {
build.ImplicitDeps.push_back(this->ConvertToNinjaPath(mit->second));
}
@@ -2406,11 +2381,6 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
return true;
}
-bool cmGlobalNinjaGenerator::EnableCrossConfigBuild() const
-{
- return !this->CrossConfigs.empty();
-}
-
int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
std::vector<std::string>::const_iterator argEnd)
{
@@ -2492,6 +2462,13 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
return 0;
}
+#endif
+
+bool cmGlobalNinjaGenerator::EnableCrossConfigBuild() const
+{
+ return !this->CrossConfigs.empty();
+}
+
void cmGlobalNinjaGenerator::AppendDirectoryForConfig(
const std::string& prefix, const std::string& config,
const std::string& suffix, std::string& dir)
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index f2bec8c..17b7efc 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -532,7 +532,7 @@ std::string GetScanCommand(const std::string& cmakeCmd, const std::string& tdi,
const std::string& ddiFile)
{
return cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi,
- " --lang=", lang, " --pp=", ppFile,
+ " --lang=", lang, " --src=$in", " --pp=", ppFile,
" --dep=$DEP_FILE --obj=$OBJ_FILE --ddi=", ddiFile);
}
diff --git a/Source/cmScanDepFormat.cxx b/Source/cmScanDepFormat.cxx
new file mode 100644
index 0000000..b157a8b
--- /dev/null
+++ b/Source/cmScanDepFormat.cxx
@@ -0,0 +1,289 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmScanDepFormat.h"
+
+#include <cctype>
+#include <cstdio>
+
+#include <cm3p/json/reader.h>
+#include <cm3p/json/value.h>
+#include <cm3p/json/writer.h>
+
+#include "cmsys/FStream.hxx"
+
+#include "cm_utf8.h"
+
+#include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+static bool ParseFilename(Json::Value const& val, std::string& result)
+{
+ if (val.isObject()) {
+ Json::Value const& format = val["format"];
+ if (format == "raw8") {
+ Json::Value const& data = val["data"];
+ for (auto const& byte : data) {
+ result.push_back(static_cast<char>(byte.asUInt()));
+ }
+ } else /* TODO: if (format == "raw16") */ {
+ return false;
+ }
+ } else if (val.isString()) {
+ result = val.asString();
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static Json::Value EncodeFilename(std::string const& path)
+{
+ if (cm_utf8_is_valid(path.c_str())) {
+ std::string valid_data;
+ valid_data.reserve(path.size());
+
+ for (auto const& byte : path) {
+ if (std::iscntrl(byte)) {
+ // Control characters.
+ valid_data.append("\\u");
+ char buf[5];
+ std::snprintf(buf, sizeof(buf), "%04x", byte);
+ valid_data.append(buf);
+ } else if (byte == '"' || byte == '\\') {
+ // Special JSON characters.
+ valid_data.push_back('\\');
+ valid_data.push_back(byte);
+ } else {
+ // Other data.
+ valid_data.push_back(byte);
+ }
+ }
+
+ return valid_data;
+ }
+
+ Json::Value data;
+ data["format"] = "raw8";
+ Json::Value& code_units = data["code-units"];
+ for (auto const& code_unit : path) {
+ code_units.append(static_cast<int>(code_unit));
+ }
+
+ return data;
+}
+
+#define PARSE_BLOB(val, res) \
+ do { \
+ if (!ParseFilename(val, res)) { \
+ cmSystemTools::Error( \
+ cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp, \
+ ": invalid blob")); \
+ return false; \
+ } \
+ } while (0)
+
+#define PARSE_FILENAME(val, res) \
+ do { \
+ if (!ParseFilename(val, res)) { \
+ cmSystemTools::Error( \
+ cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp, \
+ ": invalid filename")); \
+ return false; \
+ } \
+ \
+ if (!cmSystemTools::FileIsFullPath(res)) { \
+ res = cmStrCat(work_directory, '/', res); \
+ } \
+ } while (0)
+
+bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, cmSourceInfo* info)
+{
+ Json::Value ppio;
+ Json::Value const& ppi = ppio;
+ cmsys::ifstream ppf(arg_pp.c_str(), std::ios::in | std::ios::binary);
+ {
+ Json::Reader reader;
+ if (!reader.parse(ppf, ppio, false)) {
+ cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ",
+ arg_pp,
+ reader.getFormattedErrorMessages()));
+ return false;
+ }
+ }
+
+ Json::Value const& version = ppi["version"];
+ if (version.asUInt() != 0) {
+ cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ",
+ arg_pp, ": version ", version.asString()));
+ return false;
+ }
+
+ Json::Value const& workdir = ppi["work-directory"];
+ if (!workdir.isString()) {
+ cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ",
+ arg_pp, ": work-directory is not a string"));
+ return false;
+ }
+ std::string work_directory;
+ PARSE_BLOB(workdir, work_directory);
+
+ Json::Value const& rules = ppi["rules"];
+ if (rules.isArray()) {
+ if (rules.size() != 1) {
+ cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ",
+ arg_pp, ": expected 1 source entry"));
+ return false;
+ }
+
+ for (auto const& rule : rules) {
+ Json::Value const& depends = rule["depends"];
+ if (depends.isArray()) {
+ std::string depend_filename;
+ for (auto const& depend : depends) {
+ PARSE_FILENAME(depend, depend_filename);
+ info->Includes.push_back(depend_filename);
+ }
+ }
+
+ if (rule.isMember("future-compile")) {
+ Json::Value const& future_compile = rule["future-compile"];
+
+ if (future_compile.isMember("outputs")) {
+ Json::Value const& outputs = future_compile["outputs"];
+ if (outputs.isArray()) {
+ if (outputs.empty()) {
+ cmSystemTools::Error(
+ cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp,
+ ": expected at least one 1 output"));
+ return false;
+ }
+
+ PARSE_FILENAME(outputs[0], info->PrimaryOutput);
+ }
+ }
+
+ if (future_compile.isMember("provides")) {
+ Json::Value const& provides = future_compile["provides"];
+ if (provides.isArray()) {
+ for (auto const& provide : provides) {
+ cmSourceReqInfo provide_info;
+
+ Json::Value const& logical_name = provide["logical-name"];
+ PARSE_BLOB(logical_name, provide_info.LogicalName);
+
+ if (provide.isMember("compiled-module-path")) {
+ Json::Value const& compiled_module_path =
+ provide["compiled-module-path"];
+ PARSE_FILENAME(compiled_module_path,
+ provide_info.CompiledModulePath);
+ } else {
+ provide_info.CompiledModulePath =
+ cmStrCat(provide_info.LogicalName, ".mod");
+ }
+
+ info->Provides.push_back(provide_info);
+ }
+ }
+ }
+
+ if (future_compile.isMember("requires")) {
+ Json::Value const& reqs = future_compile["requires"];
+ if (reqs.isArray()) {
+ for (auto const& require : reqs) {
+ cmSourceReqInfo require_info;
+
+ Json::Value const& logical_name = require["logical-name"];
+ PARSE_BLOB(logical_name, require_info.LogicalName);
+
+ if (require.isMember("compiled-module-path")) {
+ Json::Value const& compiled_module_path =
+ require["compiled-module-path"];
+ PARSE_FILENAME(compiled_module_path,
+ require_info.CompiledModulePath);
+ }
+
+ info->Requires.push_back(require_info);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool cmScanDepFormat_P1689_Write(std::string const& path,
+ std::string const& input,
+ cmSourceInfo const& info)
+{
+ Json::Value ddi(Json::objectValue);
+ ddi["version"] = 0;
+ ddi["revision"] = 0;
+ ddi["work-directory"] =
+ EncodeFilename(cmSystemTools::GetCurrentWorkingDirectory());
+
+ Json::Value& rules = ddi["rules"] = Json::arrayValue;
+
+ Json::Value rule(Json::objectValue);
+ Json::Value& inputs = rule["inputs"] = Json::arrayValue;
+ inputs.append(EncodeFilename(input));
+
+ Json::Value& rule_outputs = rule["outputs"] = Json::arrayValue;
+ rule_outputs.append(EncodeFilename(path));
+
+ Json::Value& depends = rule["depends"] = Json::arrayValue;
+ for (auto const& include : info.Includes) {
+ depends.append(EncodeFilename(include));
+ }
+
+ Json::Value& future_compile = rule["future-compile"] = Json::objectValue;
+
+ Json::Value& outputs = future_compile["outputs"] = Json::arrayValue;
+ outputs.append(info.PrimaryOutput);
+
+ Json::Value& provides = future_compile["provides"] = Json::arrayValue;
+ for (auto const& provide : info.Provides) {
+ Json::Value provide_obj(Json::objectValue);
+ auto const encoded = EncodeFilename(provide.LogicalName);
+ provide_obj["logical-name"] = encoded;
+ if (provide.CompiledModulePath.empty()) {
+ provide_obj["compiled-module-path"] = encoded;
+ } else {
+ provide_obj["compiled-module-path"] =
+ EncodeFilename(provide.CompiledModulePath);
+ }
+
+ // TODO: Source file tracking. See below.
+
+ provides.append(provide_obj);
+ }
+
+ Json::Value& reqs = future_compile["requires"] = Json::arrayValue;
+ for (auto const& require : info.Requires) {
+ Json::Value require_obj(Json::objectValue);
+ auto const encoded = EncodeFilename(require.LogicalName);
+ require_obj["logical-name"] = encoded;
+ if (require.CompiledModulePath.empty()) {
+ require_obj["compiled-module-path"] = encoded;
+ } else {
+ require_obj["compiled-module-path"] =
+ EncodeFilename(require.CompiledModulePath);
+ }
+
+ // TODO: Source filename inclusion. Requires collating with the provides
+ // filenames (as a sanity check if available on both sides).
+
+ reqs.append(require_obj);
+ }
+
+ rules.append(rule);
+
+ cmGeneratedFileStream ddif(path);
+ ddif << ddi;
+
+ return !!ddif;
+}
diff --git a/Source/cmScanDepFormat.h b/Source/cmScanDepFormat.h
new file mode 100644
index 0000000..1ad0ecf
--- /dev/null
+++ b/Source/cmScanDepFormat.h
@@ -0,0 +1,30 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <string>
+#include <vector>
+
+struct cmSourceReqInfo
+{
+ std::string LogicalName;
+ std::string CompiledModulePath;
+};
+
+struct cmSourceInfo
+{
+ std::string PrimaryOutput;
+
+ // Set of provided and required modules.
+ std::vector<cmSourceReqInfo> Provides;
+ std::vector<cmSourceReqInfo> Requires;
+
+ // Set of files included in the translation unit.
+ std::vector<std::string> Includes;
+};
+
+bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp,
+ cmSourceInfo* info);
+bool cmScanDepFormat_P1689_Write(std::string const& path,
+ std::string const& input,
+ cmSourceInfo const& info);
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index a611dd7..81374a1 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -1167,7 +1167,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
return cmcmd::ExecuteLinkScript(args);
}
-#if !defined(CMAKE_BOOTSTRAP) || defined(CMAKE_BOOTSTRAP_NINJA)
+#if !defined(CMAKE_BOOTSTRAP)
// Internal CMake ninja dependency scanning support.
if (args[1] == "cmake_ninja_depends") {
return cmcmd_cmake_ninja_depends(args.begin() + 2, args.end());