summaryrefslogtreecommitdiffstats
path: root/Source/cmGlobalNinjaGenerator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmGlobalNinjaGenerator.cxx')
-rw-r--r--Source/cmGlobalNinjaGenerator.cxx255
1 files changed, 193 insertions, 62 deletions
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index c502fb8..963118f 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -6,9 +6,12 @@
#include <cctype>
#include <cstdio>
#include <sstream>
+#include <utility>
#include <cm/iterator>
#include <cm/memory>
+#include <cm/optional>
+#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/memory>
@@ -214,22 +217,41 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os,
{
// Write explicit outputs
for (std::string const& output : build.Outputs) {
- buildStr += cmStrCat(' ', this->EncodePath(output));
+ buildStr = cmStrCat(buildStr, ' ', this->EncodePath(output));
if (this->ComputingUnknownDependencies) {
this->CombinedBuildOutputs.insert(output);
}
}
// Write implicit outputs
if (!build.ImplicitOuts.empty()) {
- buildStr += " |";
+ // Assume Ninja is new enough to support implicit outputs.
+ // Callers should not populate this field otherwise.
+ buildStr = cmStrCat(buildStr, " |");
for (std::string const& implicitOut : build.ImplicitOuts) {
- buildStr += cmStrCat(' ', this->EncodePath(implicitOut));
+ buildStr = cmStrCat(buildStr, ' ', this->EncodePath(implicitOut));
+ if (this->ComputingUnknownDependencies) {
+ this->CombinedBuildOutputs.insert(implicitOut);
+ }
+ }
+ }
+
+ // Repeat some outputs, but expressed as absolute paths.
+ // This helps Ninja handle absolute paths found in a depfile.
+ // FIXME: Unfortunately this causes Ninja to stat the file twice.
+ // We could avoid this if Ninja Issue 1251 were fixed.
+ if (!build.WorkDirOuts.empty()) {
+ if (this->SupportsImplicitOuts() && build.ImplicitOuts.empty()) {
+ // Make them implicit outputs if supported by this version of Ninja.
+ buildStr = cmStrCat(buildStr, " |");
+ }
+ for (std::string const& workdirOut : build.WorkDirOuts) {
+ buildStr = cmStrCat(buildStr, " ${cmake_ninja_workdir}",
+ this->EncodePath(workdirOut));
}
}
- buildStr += ':';
// Write the rule.
- buildStr += cmStrCat(' ', build.Rule);
+ buildStr = cmStrCat(buildStr, ": ", build.Rule);
}
std::string arguments;
@@ -305,21 +327,46 @@ void cmGlobalNinjaGenerator::AddCustomCommandRule()
this->AddRule(rule);
}
+void cmGlobalNinjaGenerator::CCOutputs::Add(
+ std::vector<std::string> const& paths)
+{
+ for (std::string const& path : paths) {
+ std::string out = this->GG->ConvertToNinjaPath(path);
+ if (!cmSystemTools::FileIsFullPath(out)) {
+ // This output is expressed as a relative path. Repeat it,
+ // but expressed as an absolute path for Ninja Issue 1251.
+ this->WorkDirOuts.emplace_back(out);
+ this->GG->SeenCustomCommandOutput(this->GG->ConvertToNinjaAbsPath(path));
+ }
+ this->GG->SeenCustomCommandOutput(out);
+ this->ExplicitOuts.emplace_back(std::move(out));
+ }
+}
+
void cmGlobalNinjaGenerator::WriteCustomCommandBuild(
- const std::string& command, const std::string& description,
- const std::string& comment, const std::string& depfile,
- const std::string& job_pool, bool uses_terminal, bool restat,
- const cmNinjaDeps& outputs, const std::string& config,
- const cmNinjaDeps& explicitDeps, const cmNinjaDeps& orderOnlyDeps)
+ std::string const& command, std::string const& description,
+ std::string const& comment, std::string const& depfile,
+ std::string const& job_pool, bool uses_terminal, bool restat,
+ std::string const& config, CCOutputs outputs, cmNinjaDeps explicitDeps,
+ cmNinjaDeps orderOnlyDeps)
{
this->AddCustomCommandRule();
+ if (this->ComputingUnknownDependencies) {
+ // we need to track every dependency that comes in, since we are trying
+ // to find dependencies that are side effects of build commands
+ for (std::string const& dep : explicitDeps) {
+ this->CombinedCustomCommandExplicitDependencies.insert(dep);
+ }
+ }
+
{
cmNinjaBuild build("CUSTOM_COMMAND");
build.Comment = comment;
- build.Outputs = outputs;
- build.ExplicitDeps = explicitDeps;
- build.OrderOnlyDeps = orderOnlyDeps;
+ build.Outputs = std::move(outputs.ExplicitOuts);
+ build.WorkDirOuts = std::move(outputs.WorkDirOuts);
+ build.ExplicitDeps = std::move(explicitDeps);
+ build.OrderOnlyDeps = std::move(orderOnlyDeps);
cmNinjaVars& vars = build.Variables;
{
@@ -349,14 +396,6 @@ void cmGlobalNinjaGenerator::WriteCustomCommandBuild(
this->WriteBuild(*this->GetImplFileStream(config), build);
}
}
-
- if (this->ComputingUnknownDependencies) {
- // we need to track every dependency that comes in, since we are trying
- // to find dependencies that are side effects of build commands
- for (std::string const& dep : explicitDeps) {
- this->CombinedCustomCommandExplicitDependencies.insert(dep);
- }
- }
}
void cmGlobalNinjaGenerator::AddMacOSXContentRule()
@@ -503,14 +542,7 @@ std::unique_ptr<cmLocalGenerator> cmGlobalNinjaGenerator::CreateLocalGenerator(
codecvt::Encoding cmGlobalNinjaGenerator::GetMakefileEncoding() const
{
-#ifdef _WIN32
- // Ninja on Windows does not support non-ANSI characters.
- // https://github.com/ninja-build/ninja/issues/1195
- return codecvt::ANSI;
-#else
- // No encoding conversion needed on other platforms.
- return codecvt::None;
-#endif
+ return this->NinjaExpectedEncoding;
}
void cmGlobalNinjaGenerator::GetDocumentation(cmDocumentationEntry& entry)
@@ -732,6 +764,61 @@ void cmGlobalNinjaGenerator::CheckNinjaFeatures()
this->NinjaSupportsMetadataOnRegeneration = !cmSystemTools::VersionCompare(
cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
RequiredNinjaVersionForMetadataOnRegeneration().c_str());
+#ifdef _WIN32
+ this->NinjaSupportsCodePage = !cmSystemTools::VersionCompare(
+ cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
+ RequiredNinjaVersionForCodePage().c_str());
+ if (this->NinjaSupportsCodePage) {
+ this->CheckNinjaCodePage();
+ } else {
+ this->NinjaExpectedEncoding = codecvt::ANSI;
+ }
+#endif
+}
+
+void cmGlobalNinjaGenerator::CheckNinjaCodePage()
+{
+ std::vector<std::string> command{ this->NinjaCommand, "-t", "wincodepage" };
+ std::string output;
+ std::string error;
+ int result;
+ if (!cmSystemTools::RunSingleCommand(command, &output, &error, &result,
+ nullptr, cmSystemTools::OUTPUT_NONE)) {
+ this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
+ cmStrCat("Running\n '",
+ cmJoin(command, "' '"),
+ "'\n"
+ "failed with:\n ",
+ error));
+ cmSystemTools::SetFatalErrorOccured();
+ } else if (result == 0) {
+ std::istringstream outputStream(output);
+ std::string line;
+ bool found = false;
+ while (cmSystemTools::GetLineFromStream(outputStream, line)) {
+ if (cmHasLiteralPrefix(line, "Build file encoding: ")) {
+ cm::string_view lineView(line);
+ cm::string_view encoding =
+ lineView.substr(cmStrLen("Build file encoding: "));
+ if (encoding == "UTF-8") {
+ // Ninja expects UTF-8. We use that internally. No conversion needed.
+ this->NinjaExpectedEncoding = codecvt::None;
+ } else {
+ this->NinjaExpectedEncoding = codecvt::ANSI;
+ }
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ this->GetCMakeInstance()->IssueMessage(
+ MessageType::WARNING,
+ "Could not determine Ninja's code page, defaulting to UTF-8");
+ this->NinjaExpectedEncoding = codecvt::None;
+ }
+ } else {
+ this->NinjaExpectedEncoding = codecvt::ANSI;
+ }
}
bool cmGlobalNinjaGenerator::CheckLanguages(
@@ -1073,10 +1160,8 @@ std::string const& cmGlobalNinjaGenerator::ConvertToNinjaPath(
return f->second;
}
- const auto& ng =
- cm::static_reference_cast<cmLocalNinjaGenerator>(this->LocalGenerators[0]);
- std::string const& bin_dir = ng.GetState()->GetBinaryDirectory();
- std::string convPath = ng.MaybeConvertToRelativePath(bin_dir, path);
+ std::string convPath =
+ this->LocalGenerators[0]->MaybeRelativeToTopBinDir(path);
convPath = this->NinjaOutputPath(convPath);
#ifdef _WIN32
std::replace(convPath.begin(), convPath.end(), '/', '\\');
@@ -1085,6 +1170,15 @@ std::string const& cmGlobalNinjaGenerator::ConvertToNinjaPath(
.first->second;
}
+std::string cmGlobalNinjaGenerator::ConvertToNinjaAbsPath(
+ std::string path) const
+{
+#ifdef _WIN32
+ std::replace(path.begin(), path.end(), '/', '\\');
+#endif
+ return path;
+}
+
void cmGlobalNinjaGenerator::AddAdditionalCleanFile(std::string fileName,
const std::string& config)
{
@@ -1150,6 +1244,8 @@ void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os) const
void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()
{
for (auto const& asd : this->AssumedSourceDependencies) {
+ CCOutputs outputs(this);
+ outputs.ExplicitOuts.emplace_back(asd.first);
cmNinjaDeps orderOnlyDeps;
std::copy(asd.second.begin(), asd.second.end(),
std::back_inserter(orderOnlyDeps));
@@ -1158,8 +1254,8 @@ void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()
"Assume dependencies for generated source file.",
/*depfile*/ "", /*job_pool*/ "",
/*uses_terminal*/ false,
- /*restat*/ true, cmNinjaDeps(1, asd.first), "", cmNinjaDeps(),
- orderOnlyDeps);
+ /*restat*/ true, std::string(), outputs, cmNinjaDeps(),
+ std::move(orderOnlyDeps));
}
}
@@ -2198,14 +2294,22 @@ Compilation of source files within a target is split into the following steps:
(because the latter consumes the module).
*/
-static std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
+namespace {
+
+struct cmSourceInfo
+{
+ cmScanDepInfo ScanDep;
+ std::vector<std::string> Includes;
+};
+
+cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
std::string const& arg_tdi, std::string const& arg_pp);
+}
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;
@@ -2214,8 +2318,6 @@ 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=")) {
@@ -2256,11 +2358,8 @@ 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;
+ cm::optional<cmSourceInfo> info;
if (arg_lang == "Fortran") {
info = cmcmd_cmake_ninja_depends_fortran(arg_tdi, arg_pp);
} else {
@@ -2275,7 +2374,7 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
return 1;
}
- info->PrimaryOutput = arg_obj;
+ info->ScanDep.PrimaryOutput = arg_obj;
{
cmGeneratedFileStream depfile(arg_dep);
@@ -2286,7 +2385,7 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
depfile << "\n";
}
- if (!cmScanDepFormat_P1689_Write(arg_ddi, arg_src, *info)) {
+ if (!cmScanDepFormat_P1689_Write(arg_ddi, info->ScanDep)) {
cmSystemTools::Error(
cmStrCat("-E cmake_ninja_depends failed to write ", arg_ddi));
return 1;
@@ -2294,11 +2393,16 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
return 0;
}
-std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
+namespace {
+
+cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
std::string const& arg_tdi, std::string const& arg_pp)
{
+ cm::optional<cmSourceInfo> info;
cmFortranCompiler fc;
std::vector<std::string> includes;
+ std::string dir_top_bld;
+ std::string module_dir;
{
Json::Value tdio;
Json::Value const& tdi = tdio;
@@ -2309,10 +2413,15 @@ std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
cmSystemTools::Error(
cmStrCat("-E cmake_ninja_depends failed to parse ", arg_tdi,
reader.getFormattedErrorMessages()));
- return nullptr;
+ return info;
}
}
+ dir_top_bld = tdi["dir-top-bld"].asString();
+ if (!dir_top_bld.empty() && !cmHasLiteralSuffix(dir_top_bld, "/")) {
+ dir_top_bld += '/';
+ }
+
Json::Value const& tdi_include_dirs = tdi["include-dirs"];
if (tdi_include_dirs.isArray()) {
for (auto const& tdi_include_dir : tdi_include_dirs) {
@@ -2320,6 +2429,12 @@ std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
}
}
+ Json::Value const& tdi_module_dir = tdi["module-dir"];
+ module_dir = tdi_module_dir.asString();
+ if (!module_dir.empty() && !cmHasLiteralSuffix(module_dir, "/")) {
+ module_dir += '/';
+ }
+
Json::Value const& tdi_compiler_id = tdi["compiler-id"];
fc.Id = tdi_compiler_id.asString();
@@ -2336,19 +2451,25 @@ std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
if (!cmFortranParser_FilePush(&parser, arg_pp.c_str())) {
cmSystemTools::Error(
cmStrCat("-E cmake_ninja_depends failed to open ", arg_pp));
- return nullptr;
+ return info;
}
if (cmFortran_yyparse(parser.Scanner) != 0) {
// Failed to parse the file.
- return nullptr;
+ return info;
}
- auto info = cm::make_unique<cmSourceInfo>();
+ info = cmSourceInfo();
for (std::string const& provide : finfo.Provides) {
cmSourceReqInfo src_info;
src_info.LogicalName = provide;
- src_info.CompiledModulePath = provide;
- info->Provides.emplace_back(src_info);
+ if (!module_dir.empty()) {
+ std::string mod = cmStrCat(module_dir, provide);
+ if (!dir_top_bld.empty() && cmHasPrefix(mod, dir_top_bld)) {
+ mod = mod.substr(dir_top_bld.size());
+ }
+ src_info.CompiledModulePath = std::move(mod);
+ }
+ info->ScanDep.Provides.emplace_back(src_info);
}
for (std::string const& require : finfo.Requires) {
// Require modules not provided in the same source.
@@ -2357,14 +2478,14 @@ std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
}
cmSourceReqInfo src_info;
src_info.LogicalName = require;
- src_info.CompiledModulePath = require;
- info->Requires.emplace_back(src_info);
+ info->ScanDep.Requires.emplace_back(src_info);
}
for (std::string const& include : finfo.Includes) {
info->Includes.push_back(include);
}
return info;
}
+}
bool cmGlobalNinjaGenerator::WriteDyndepFile(
std::string const& dir_top_src, std::string const& dir_top_bld,
@@ -2379,17 +2500,17 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
cmStateSnapshot snapshot = this->GetCMakeInstance()->GetCurrentSnapshot();
snapshot.GetDirectory().SetCurrentSource(dir_cur_src);
snapshot.GetDirectory().SetCurrentBinary(dir_cur_bld);
- snapshot.GetDirectory().SetRelativePathTopSource(dir_top_src.c_str());
- snapshot.GetDirectory().SetRelativePathTopBinary(dir_top_bld.c_str());
auto mfd = cm::make_unique<cmMakefile>(this, snapshot);
auto lgd = this->CreateLocalGenerator(mfd.get());
+ lgd->SetRelativePathTopSource(dir_top_src);
+ lgd->SetRelativePathTopBinary(dir_top_bld);
this->Makefiles.push_back(std::move(mfd));
this->LocalGenerators.push_back(std::move(lgd));
}
- std::vector<cmSourceInfo> objects;
+ std::vector<cmScanDepInfo> objects;
for (std::string const& arg_ddi : arg_ddis) {
- cmSourceInfo info;
+ cmScanDepInfo info;
if (!cmScanDepFormat_P1689_Parse(arg_ddi, &info)) {
cmSystemTools::Error(
cmStrCat("-E cmake_ninja_dyndep failed to parse ddi file ", arg_ddi));
@@ -2425,10 +2546,20 @@ 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 (cmSourceInfo const& object : objects) {
+ for (cmScanDepInfo const& object : objects) {
for (auto const& p : object.Provides) {
- std::string const mod = cmStrCat(
- module_dir, cmSystemTools::GetFilenameName(p.CompiledModulePath));
+ std::string mod;
+ if (!p.CompiledModulePath.empty()) {
+ // The scanner provided the path to the module file.
+ mod = p.CompiledModulePath;
+ if (!cmSystemTools::FileIsFullPath(mod)) {
+ // Treat relative to work directory (top of build tree).
+ mod = cmSystemTools::CollapseFullPath(mod, dir_top_bld);
+ }
+ } else {
+ // Assume the module file path matches the logical module name.
+ mod = cmStrCat(module_dir, p.LogicalName);
+ }
mod_files[p.LogicalName] = mod;
tm[p.LogicalName] = mod;
}
@@ -2440,7 +2571,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
{
cmNinjaBuild build("dyndep");
build.Outputs.emplace_back("");
- for (cmSourceInfo const& object : objects) {
+ for (cmScanDepInfo const& object : objects) {
build.Outputs[0] = this->ConvertToNinjaPath(object.PrimaryOutput);
build.ImplicitOuts.clear();
for (auto const& p : object.Provides) {