cmGlobalNinjaGenerator.cxx 42.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
  Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>

  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.

  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
Brad King's avatar
Brad King committed
13 14
#include "cmGlobalNinjaGenerator.h"

15
#include "cmAlgorithms.h"
16 17 18
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpressionEvaluationFile.h"
#include "cmGeneratorTarget.h"
19 20 21 22
#include "cmLocalNinjaGenerator.h"
#include "cmMakefile.h"
#include "cmVersion.h"

23
#include <algorithm>
24
#include <assert.h>
25
#include <ctype.h>
26

27 28 29 30 31 32
const char* cmGlobalNinjaGenerator::NINJA_BUILD_FILE = "build.ninja";
const char* cmGlobalNinjaGenerator::NINJA_RULES_FILE = "rules.ninja";
const char* cmGlobalNinjaGenerator::INDENT = "  ";

void cmGlobalNinjaGenerator::Indent(std::ostream& os, int count)
{
33
  for (int i = 0; i < count; ++i)
34 35 36 37 38
    os << cmGlobalNinjaGenerator::INDENT;
}

void cmGlobalNinjaGenerator::WriteDivider(std::ostream& os)
{
39 40
  os << "# ======================================"
     << "=======================================\n";
41 42 43 44 45 46 47 48 49 50 51
}

void cmGlobalNinjaGenerator::WriteComment(std::ostream& os,
                                          const std::string& comment)
{
  if (comment.empty())
    return;

  std::string replace = comment;
  std::string::size_type lpos = 0;
  std::string::size_type rpos;
52
  os << "\n#############################################\n";
53
  while ((rpos = replace.find('\n', lpos)) != std::string::npos) {
54 55
    os << "# " << replace.substr(lpos, rpos - lpos) << "\n";
    lpos = rpos + 1;
56
  }
57
  os << "# " << replace.substr(lpos) << "\n\n";
58 59
}

60 61 62 63 64
std::string cmGlobalNinjaGenerator::EncodeRuleName(std::string const& name)
{
  // Ninja rule names must match "[a-zA-Z0-9_.-]+".  Use ".xx" to encode
  // "." and all invalid characters as hexadecimal.
  std::string encoded;
65 66
  for (std::string::const_iterator i = name.begin(); i != name.end(); ++i) {
    if (isalnum(*i) || *i == '_' || *i == '-') {
67
      encoded += *i;
68
    } else {
69 70 71 72
      char buf[16];
      sprintf(buf, ".%02x", static_cast<unsigned int>(*i));
      encoded += buf;
    }
73
  }
74 75 76
  return encoded;
}

77 78
static bool IsIdentChar(char c)
{
79 80 81
  return ('a' <= c && c <= 'z') ||
    ('+' <= c && c <= '9') || // +,-./ and numbers
    ('A' <= c && c <= 'Z') || (c == '_') || (c == '$') || (c == '\\') ||
82
    (c == ' ') || (c == ':');
83 84
}

85 86 87
std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string& ident,
                                                std::ostream& vars)
{
88 89 90
  if (std::find_if(ident.begin(), ident.end(),
                   std::not1(std::ptr_fun(IsIdentChar))) != ident.end()) {
    static unsigned VarNum = 0;
91
    std::ostringstream names;
92 93 94 95
    names << "ident" << VarNum++;
    vars << names.str() << " = " << ident << "\n";
    return "$" + names.str();
  } else {
96 97 98 99
    std::string result = ident;
    cmSystemTools::ReplaceString(result, " ", "$ ");
    cmSystemTools::ReplaceString(result, ":", "$:");
    return result;
100 101 102
  }
}

103
std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string& lit)
104 105 106
{
  std::string result = lit;
  cmSystemTools::ReplaceString(result, "$", "$$");
107
  cmSystemTools::ReplaceString(result, "\n", "$\n");
108 109 110
  return result;
}

111
std::string cmGlobalNinjaGenerator::EncodePath(const std::string& path)
112 113 114
{
  std::string result = path;
#ifdef _WIN32
115
  if (this->IsGCCOnWindows())
116 117 118
    cmSystemTools::ReplaceString(result, "\\", "/");
  else
    cmSystemTools::ReplaceString(result, "/", "\\");
119 120 121 122
#endif
  return EncodeLiteral(result);
}

123
std::string cmGlobalNinjaGenerator::EncodeDepfileSpace(const std::string& path)
124 125 126 127 128 129
{
  std::string result = path;
  cmSystemTools::ReplaceString(result, " ", "\\ ");
  return result;
}

130 131 132 133 134 135
void cmGlobalNinjaGenerator::WriteBuild(
  std::ostream& os, const std::string& comment, const std::string& rule,
  const cmNinjaDeps& outputs, const cmNinjaDeps& explicitDeps,
  const cmNinjaDeps& implicitDeps, const cmNinjaDeps& orderOnlyDeps,
  const cmNinjaVars& variables, const std::string& rspfile, int cmdLineLimit,
  bool* usedResponseFile)
136 137
{
  // Make sure there is a rule.
138
  if (rule.empty()) {
139 140 141 142
    cmSystemTools::Error("No rule for WriteBuildStatement! called "
                         "with comment: ",
                         comment.c_str());
    return;
143
  }
144 145

  // Make sure there is at least one output file.
146
  if (outputs.empty()) {
147 148 149 150
    cmSystemTools::Error("No output files for WriteBuildStatement! called "
                         "with comment: ",
                         comment.c_str());
    return;
151
  }
152 153 154

  cmGlobalNinjaGenerator::WriteComment(os, comment);

155
  std::string arguments;
156 157 158 159

  // TODO: Better formatting for when there are multiple input/output files.

  // Write explicit dependencies.
160 161
  for (cmNinjaDeps::const_iterator i = explicitDeps.begin();
       i != explicitDeps.end(); ++i) {
162
    arguments += " " + EncodeIdent(EncodePath(*i), os);
163
  }
164

165
  // Write implicit dependencies.
166
  if (!implicitDeps.empty()) {
167
    arguments += " |";
168 169
    for (cmNinjaDeps::const_iterator i = implicitDeps.begin();
         i != implicitDeps.end(); ++i)
170
      arguments += " " + EncodeIdent(EncodePath(*i), os);
171
  }
172 173

  // Write order-only dependencies.
174
  if (!orderOnlyDeps.empty()) {
175
    arguments += " ||";
176 177
    for (cmNinjaDeps::const_iterator i = orderOnlyDeps.begin();
         i != orderOnlyDeps.end(); ++i)
178
      arguments += " " + EncodeIdent(EncodePath(*i), os);
179
  }
180

181
  arguments += "\n";
182

183
  std::string build;
184 185

  // Write outputs files.
186
  build += "build";
187 188
  for (cmNinjaDeps::const_iterator i = outputs.begin(); i != outputs.end();
       ++i) {
189
    build += " " + EncodeIdent(EncodePath(*i), os);
190 191
    if (this->ComputingUnknownDependencies) {
      this->CombinedBuildOutputs.insert(EncodePath(*i));
192
    }
193
  }
194
  build += ":";
195

196
  // Write the rule.
197
  build += " " + rule;
198 199

  // Write the variables bound to this build statement.
200
  std::ostringstream variable_assignments;
201 202 203 204
  for (cmNinjaVars::const_iterator i = variables.begin(); i != variables.end();
       ++i)
    cmGlobalNinjaGenerator::WriteVariable(variable_assignments, i->first,
                                          i->second, "", 1);
205 206

  // check if a response file rule should be used
207
  std::string buildstr = build;
208
  std::string assignments = variable_assignments.str();
209
  const std::string& args = arguments;
210
  bool useResponseFile = false;
211 212
  if (cmdLineLimit < 0 ||
      (cmdLineLimit > 0 &&
213 214
       (args.size() + buildstr.size() + assignments.size()) >
         static_cast<size_t>(cmdLineLimit))) {
215
    variable_assignments.str(std::string());
216 217
    cmGlobalNinjaGenerator::WriteVariable(variable_assignments, "RSP_FILE",
                                          rspfile, "", 1);
218
    assignments += variable_assignments.str();
219
    useResponseFile = true;
220
  }
221
  if (usedResponseFile) {
222
    *usedResponseFile = useResponseFile;
223
  }
224

225
  os << buildstr << args << assignments;
226 227
}

228 229 230 231
void cmGlobalNinjaGenerator::WritePhonyBuild(
  std::ostream& os, const std::string& comment, const cmNinjaDeps& outputs,
  const cmNinjaDeps& explicitDeps, const cmNinjaDeps& implicitDeps,
  const cmNinjaDeps& orderOnlyDeps, const cmNinjaVars& variables)
232
{
233 234
  this->WriteBuild(os, comment, "phony", outputs, explicitDeps, implicitDeps,
                   orderOnlyDeps, variables);
235 236 237 238
}

void cmGlobalNinjaGenerator::AddCustomCommandRule()
{
239
  this->AddRule("CUSTOM_COMMAND", "$COMMAND", "$DESC",
240 241
                "Rule for running custom commands.",
                /*depfile*/ "",
242
                /*deptype*/ "",
243
                /*rspfile*/ "",
244
                /*rspcontent*/ "",
245
                /*restat*/ "", // bound on each build statement as needed
246
                /*generator*/ false);
247 248
}

249 250 251 252 253
void cmGlobalNinjaGenerator::WriteCustomCommandBuild(
  const std::string& command, const std::string& description,
  const std::string& comment, bool uses_terminal, bool restat,
  const cmNinjaDeps& outputs, const cmNinjaDeps& deps,
  const cmNinjaDeps& orderOnly)
254
{
255 256
  std::string cmd = command;
#ifdef _WIN32
257 258 259
  if (cmd.empty())
    // TODO Shouldn't an empty command be handled by ninja?
    cmd = "cmd.exe /c";
260 261
#endif

262 263 264
  this->AddCustomCommandRule();

  cmNinjaVars vars;
265
  vars["COMMAND"] = cmd;
266
  vars["DESC"] = EncodeLiteral(description);
267
  if (restat) {
268
    vars["restat"] = "1";
269 270
  }
  if (uses_terminal && SupportsConsolePool()) {
271
    vars["pool"] = "console";
272
  }
273

274 275 276 277 278 279 280
  this->WriteBuild(*this->BuildFileStream, comment, "CUSTOM_COMMAND", outputs,
                   deps, cmNinjaDeps(), orderOnly, vars);

  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 (cmNinjaDeps::const_iterator i = deps.begin(); i != deps.end(); ++i) {
281
      this->CombinedCustomCommandExplicitDependencies.insert(EncodePath(*i));
282
    }
283
  }
284 285
}

286
void cmGlobalNinjaGenerator::AddMacOSXContentRule()
287
{
288
  cmLocalGenerator* lg = this->LocalGenerators[0];
289

290
  std::ostringstream cmd;
291
  cmd << lg->ConvertToOutputFormat(cmSystemTools::GetCMakeCommand(),
292
                                   cmOutputConverter::SHELL)
293 294
      << " -E copy $in $out";

295
  this->AddRule("COPY_OSX_CONTENT", cmd.str(), "Copying OS X Content $out",
296
                "Rule for copying OS X bundle content file.",
297
                /*depfile*/ "",
298 299 300
                /*deptype*/ "",
                /*rspfile*/ "",
                /*rspcontent*/ "",
301
                /*restat*/ "",
302
                /*generator*/ false);
303 304
}

305 306
void cmGlobalNinjaGenerator::WriteMacOSXContentBuild(const std::string& input,
                                                     const std::string& output)
307 308 309 310 311 312 313 314 315
{
  this->AddMacOSXContentRule();

  cmNinjaDeps outputs;
  outputs.push_back(output);
  cmNinjaDeps deps;
  deps.push_back(input);
  cmNinjaVars vars;

316 317
  this->WriteBuild(*this->BuildFileStream, "", "COPY_OSX_CONTENT", outputs,
                   deps, cmNinjaDeps(), cmNinjaDeps(), cmNinjaVars());
318 319
}

320 321 322 323 324 325
void cmGlobalNinjaGenerator::WriteRule(
  std::ostream& os, const std::string& name, const std::string& command,
  const std::string& description, const std::string& comment,
  const std::string& depfile, const std::string& deptype,
  const std::string& rspfile, const std::string& rspcontent,
  const std::string& restat, bool generator)
326 327
{
  // Make sure the rule has a name.
328
  if (name.empty()) {
329 330 331 332
    cmSystemTools::Error("No name given for WriteRuleStatement! called "
                         "with comment: ",
                         comment.c_str());
    return;
333
  }
334 335

  // Make sure a command is given.
336
  if (command.empty()) {
337 338 339 340
    cmSystemTools::Error("No command given for WriteRuleStatement! called "
                         "with comment: ",
                         comment.c_str());
    return;
341
  }
342 343 344 345 346 347 348

  cmGlobalNinjaGenerator::WriteComment(os, comment);

  // Write the rule.
  os << "rule " << name << "\n";

  // Write the depfile if any.
349
  if (!depfile.empty()) {
350 351
    cmGlobalNinjaGenerator::Indent(os, 1);
    os << "depfile = " << depfile << "\n";
352
  }
353

354
  // Write the deptype if any.
355
  if (!deptype.empty()) {
356 357
    cmGlobalNinjaGenerator::Indent(os, 1);
    os << "deps = " << deptype << "\n";
358
  }
359

360 361 362 363 364
  // Write the command.
  cmGlobalNinjaGenerator::Indent(os, 1);
  os << "command = " << command << "\n";

  // Write the description if any.
365
  if (!description.empty()) {
366 367
    cmGlobalNinjaGenerator::Indent(os, 1);
    os << "description = " << description << "\n";
368
  }
369

370 371
  if (!rspfile.empty()) {
    if (rspcontent.empty()) {
372 373
      cmSystemTools::Error("No rspfile_content given!", comment.c_str());
      return;
374
    }
375 376 377 378
    cmGlobalNinjaGenerator::Indent(os, 1);
    os << "rspfile = " << rspfile << "\n";
    cmGlobalNinjaGenerator::Indent(os, 1);
    os << "rspfile_content = " << rspcontent << "\n";
379
  }
380

381
  if (!restat.empty()) {
382
    cmGlobalNinjaGenerator::Indent(os, 1);
383
    os << "restat = " << restat << "\n";
384
  }
385

386
  if (generator) {
387 388
    cmGlobalNinjaGenerator::Indent(os, 1);
    os << "generator = 1\n";
389
  }
390 391

  os << "\n";
392 393 394 395 396 397 398 399 400
}

void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os,
                                           const std::string& name,
                                           const std::string& value,
                                           const std::string& comment,
                                           int indent)
{
  // Make sure we have a name.
401
  if (name.empty()) {
402 403 404 405
    cmSystemTools::Error("No name given for WriteVariable! called "
                         "with comment: ",
                         comment.c_str());
    return;
406
  }
407 408 409

  // Do not add a variable if the value is empty.
  std::string val = cmSystemTools::TrimWhitespace(value);
410
  if (val.empty()) {
411
    return;
412
  }
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432

  cmGlobalNinjaGenerator::WriteComment(os, comment);
  cmGlobalNinjaGenerator::Indent(os, indent);
  os << name << " = " << val << "\n";
}

void cmGlobalNinjaGenerator::WriteInclude(std::ostream& os,
                                          const std::string& filename,
                                          const std::string& comment)
{
  cmGlobalNinjaGenerator::WriteComment(os, comment);
  os << "include " << filename << "\n";
}

void cmGlobalNinjaGenerator::WriteDefault(std::ostream& os,
                                          const cmNinjaDeps& targets,
                                          const std::string& comment)
{
  cmGlobalNinjaGenerator::WriteComment(os, comment);
  os << "default";
433 434
  for (cmNinjaDeps::const_iterator i = targets.begin(); i != targets.end();
       ++i)
435 436 437 438
    os << " " << *i;
  os << "\n";
}

439
cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm)
440
  : cmGlobalCommonGenerator(cm)
441 442
  , BuildFileStream(0)
  , RulesFileStream(0)
443
  , CompileCommandsStream(0)
444 445
  , Rules()
  , AllDependencies()
446
  , UsingGCCOnWindows(false)
447 448
  , ComputingUnknownDependencies(false)
  , PolicyCMP0058(cmPolicies::WARN)
449
{
450
#ifdef _WIN32
451
  cm->GetState()->SetWindowsShell(true);
452
#endif
453 454 455 456 457 458 459
  // // Ninja is not ported to non-Unix OS yet.
  // this->ForceUnixPaths = true;
  this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake";
}

// Virtual public methods.

460
cmLocalGenerator* cmGlobalNinjaGenerator::CreateLocalGenerator(cmMakefile* mf)
461
{
462
  return new cmLocalNinjaGenerator(this, mf);
463 464
}

465
void cmGlobalNinjaGenerator::GetDocumentation(cmDocumentationEntry& entry)
466
{
467
  entry.Name = cmGlobalNinjaGenerator::GetActualName();
468
  entry.Brief = "Generates build.ninja files.";
469 470 471 472 473 474 475 476
}

// Implemented in all cmGlobaleGenerator sub-classes.
// Used in:
//   Source/cmLocalGenerator.cxx
//   Source/cmake.cxx
void cmGlobalNinjaGenerator::Generate()
{
477 478
  // Check minimum Ninja version.
  if (cmSystemTools::VersionCompare(cmSystemTools::OP_LESS,
479
                                    this->NinjaVersion.c_str(),
480
                                    RequiredNinjaVersion().c_str())) {
481
    std::ostringstream msg;
482
    msg << "The detected version of Ninja (" << this->NinjaVersion;
483 484 485 486
    msg << ") is less than the version of Ninja required by CMake (";
    msg << this->RequiredNinjaVersion() << ").";
    this->GetCMakeInstance()->IssueMessage(cmake::FATAL_ERROR, msg.str());
    return;
487
  }
488 489 490
  this->OpenBuildFileStream();
  this->OpenRulesFileStream();

491
  this->InitOutputPathPrefix();
492 493
  this->TargetAll = this->NinjaOutputPath("all");
  this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt");
494

495
  this->PolicyCMP0058 =
496 497
    this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus(
      cmPolicies::CMP0058);
498 499 500 501
  this->ComputingUnknownDependencies =
    (this->PolicyCMP0058 == cmPolicies::OLD ||
     this->PolicyCMP0058 == cmPolicies::WARN);

502 503
  this->cmGlobalGenerator::Generate();

504
  this->WriteAssumedSourceDependencies();
505
  this->WriteTargetAliases(*this->BuildFileStream);
506
  this->WriteFolderTargets(*this->BuildFileStream);
507
  this->WriteUnknownExplicitDependencies(*this->BuildFileStream);
508 509
  this->WriteBuiltinTargets(*this->BuildFileStream);

510 511 512 513 514
  if (cmSystemTools::GetErrorOccuredFlag()) {
    this->RulesFileStream->setstate(std::ios_base::failbit);
    this->BuildFileStream->setstate(std::ios_base::failbit);
  }

515
  this->CloseCompileCommandsStream();
516 517 518 519
  this->CloseRulesFileStream();
  this->CloseBuildFileStream();
}

520 521 522
void cmGlobalNinjaGenerator::FindMakeProgram(cmMakefile* mf)
{
  this->cmGlobalGenerator::FindMakeProgram(mf);
523
  if (const char* ninjaCommand = mf->GetDefinition("CMAKE_MAKE_PROGRAM")) {
524
    this->NinjaCommand = ninjaCommand;
525 526 527 528
    std::vector<std::string> command;
    command.push_back(this->NinjaCommand);
    command.push_back("--version");
    std::string version;
529
    cmSystemTools::RunSingleCommand(command, &version, 0, 0, 0,
530 531
                                    cmSystemTools::OUTPUT_NONE);
    this->NinjaVersion = cmSystemTools::TrimWhitespace(version);
532
  }
533 534
}

535 536
void cmGlobalNinjaGenerator::EnableLanguage(
  std::vector<std::string> const& langs, cmMakefile* mf, bool optional)
537
{
538
  if (std::find(langs.begin(), langs.end(), "Fortran") != langs.end()) {
539
    cmSystemTools::Error("The Ninja generator does not support Fortran yet.");
540
  }
541
  this->cmGlobalGenerator::EnableLanguage(langs, mf, optional);
542 543 544
  for (std::vector<std::string>::const_iterator l = langs.begin();
       l != langs.end(); ++l) {
    if (*l == "NONE") {
545 546
      continue;
    }
547 548
    this->ResolveLanguageCompiler(*l, mf, optional);
  }
549
#ifdef _WIN32
550 551 552 553
  if (mf->IsOn("CMAKE_COMPILER_IS_MINGW") ||
      strcmp(mf->GetSafeDefinition("CMAKE_C_COMPILER_ID"), "GNU") == 0 ||
      strcmp(mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID"), "GNU") == 0 ||
      strcmp(mf->GetSafeDefinition("CMAKE_C_SIMULATE_ID"), "GNU") == 0 ||
554
      strcmp(mf->GetSafeDefinition("CMAKE_CXX_SIMULATE_ID"), "GNU") == 0) {
555
    this->UsingGCCOnWindows = true;
556
  }
557
#endif
558 559 560 561
}

// Implemented by:
//   cmGlobalUnixMakefileGenerator3
562
//   cmGlobalGhsMultiGenerator
563 564 565 566 567
//   cmGlobalVisualStudio10Generator
//   cmGlobalVisualStudio7Generator
//   cmGlobalXCodeGenerator
// Called by:
//   cmGlobalGenerator::Build()
568 569 570 571 572
void cmGlobalNinjaGenerator::GenerateBuildCommand(
  std::vector<std::string>& makeCommand, const std::string& makeProgram,
  const std::string& /*projectName*/, const std::string& /*projectDir*/,
  const std::string& targetName, const std::string& /*config*/, bool /*fast*/,
  bool verbose, std::vector<std::string> const& makeOptions)
573
{
574
  makeCommand.push_back(this->SelectMakeProgram(makeProgram));
575

576
  if (verbose) {
577
    makeCommand.push_back("-v");
578
  }
579

580 581 582 583
  makeCommand.insert(makeCommand.end(), makeOptions.begin(),
                     makeOptions.end());
  if (!targetName.empty()) {
    if (targetName == "clean") {
584 585
      makeCommand.push_back("-t");
      makeCommand.push_back("clean");
586
    } else {
587
      makeCommand.push_back(targetName);
588
    }
589
  }
590 591 592 593
}

// Non-virtual public methods.

594 595 596 597 598 599
void cmGlobalNinjaGenerator::AddRule(
  const std::string& name, const std::string& command,
  const std::string& description, const std::string& comment,
  const std::string& depfile, const std::string& deptype,
  const std::string& rspfile, const std::string& rspcontent,
  const std::string& restat, bool generator)
600 601
{
  // Do not add the same rule twice.
602
  if (this->HasRule(name)) {
603
    return;
604
  }
605 606

  this->Rules.insert(name);
607 608 609 610 611
  cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream, name, command,
                                    description, comment, depfile, deptype,
                                    rspfile, rspcontent, restat, generator);

  this->RuleCmdLength[name] = (int)command.size();
612 613
}

614
bool cmGlobalNinjaGenerator::HasRule(const std::string& name)
615 616 617 618 619
{
  RulesSetType::const_iterator rule = this->Rules.find(name);
  return (rule != this->Rules.end());
}

620 621
// Private virtual overrides

622 623 624 625 626 627 628
std::string cmGlobalNinjaGenerator::GetEditCacheCommand() const
{
  // Ninja by design does not run interactive tools in the terminal,
  // so our only choice is cmake-gui.
  return cmSystemTools::GetCMakeGUICommand();
}

629 630
void cmGlobalNinjaGenerator::ComputeTargetObjectDirectory(
  cmGeneratorTarget* gt) const
631 632
{
  // Compute full path to object file directory for this target.
633
  std::string dir;
634
  dir += gt->LocalGenerator->GetCurrentBinaryDirectory();
635
  dir += "/";
636
  dir += gt->LocalGenerator->GetTargetDirectory(gt);
637 638
  dir += "/";
  gt->ObjectDirectory = dir;
639 640
}

641 642 643 644 645 646 647 648 649 650 651
// Private methods

void cmGlobalNinjaGenerator::OpenBuildFileStream()
{
  // Compute Ninja's build file path.
  std::string buildFilePath =
    this->GetCMakeInstance()->GetHomeOutputDirectory();
  buildFilePath += "/";
  buildFilePath += cmGlobalNinjaGenerator::NINJA_BUILD_FILE;

  // Get a stream where to generate things.
652
  if (!this->BuildFileStream) {
653
    this->BuildFileStream = new cmGeneratedFileStream(buildFilePath.c_str());
654
    if (!this->BuildFileStream) {
655 656 657 658
      // An error message is generated by the constructor if it cannot
      // open the file.
      return;
    }
659
  }
660 661 662 663 664 665 666

  // Write the do not edit header.
  this->WriteDisclaimer(*this->BuildFileStream);

  // Write a comment about this file.
  *this->BuildFileStream
    << "# This file contains all the build statements describing the\n"
667
    << "# compilation DAG.\n\n";
668 669 670 671
}

void cmGlobalNinjaGenerator::CloseBuildFileStream()
{
672
  if (this->BuildFileStream) {
673 674
    delete this->BuildFileStream;
    this->BuildFileStream = 0;
675
  } else {
676
    cmSystemTools::Error("Build file stream was not open.");
677
  }
678 679 680 681 682 683 684 685 686 687 688
}

void cmGlobalNinjaGenerator::OpenRulesFileStream()
{
  // Compute Ninja's build file path.
  std::string rulesFilePath =
    this->GetCMakeInstance()->GetHomeOutputDirectory();
  rulesFilePath += "/";
  rulesFilePath += cmGlobalNinjaGenerator::NINJA_RULES_FILE;

  // Get a stream where to generate things.
689
  if (!this->RulesFileStream) {
690
    this->RulesFileStream = new cmGeneratedFileStream(rulesFilePath.c_str());
691
    if (!this->RulesFileStream) {
692 693 694 695
      // An error message is generated by the constructor if it cannot
      // open the file.
      return;
    }
696
  }
697 698 699 700 701

  // Write the do not edit header.
  this->WriteDisclaimer(*this->RulesFileStream);

  // Write comment about this file.
702
  /* clang-format off */
703 704 705 706 707
  *this->RulesFileStream
    << "# This file contains all the rules used to get the outputs files\n"
    << "# built from the input files.\n"
    << "# It is included in the main '" << NINJA_BUILD_FILE << "'.\n\n"
    ;
708
  /* clang-format on */
709 710 711 712
}

void cmGlobalNinjaGenerator::CloseRulesFileStream()
{
713
  if (this->RulesFileStream) {
714 715
    delete this->RulesFileStream;
    this->RulesFileStream = 0;
716
  } else {
717
    cmSystemTools::Error("Rules file stream was not open.");
718
  }
719 720
}

721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
static void EnsureTrailingSlash(std::string& path)
{
  if (path.empty()) {
    return;
  }
  std::string::value_type last = path[path.size() - 1];
#ifdef _WIN32
  if (last != '\\') {
    path += '\\';
  }
#else
  if (last != '/') {
    path += '/';
  }
#endif
}

738 739
std::string cmGlobalNinjaGenerator::ConvertToNinjaPath(const std::string& path)
{
740 741
  cmLocalNinjaGenerator* ng =
    static_cast<cmLocalNinjaGenerator*>(this->LocalGenerators[0]);
742
  std::string convPath = ng->Convert(path, cmOutputConverter::HOME_OUTPUT);
743
  convPath = this->NinjaOutputPath(convPath);
744 745 746 747 748 749
#ifdef _WIN32
  cmSystemTools::ReplaceString(convPath, "/", "\\");
#endif
  return convPath;
}

750 751
std::string cmGlobalNinjaGenerator::ConvertToNinjaFolderRule(
  const std::string& path)
752
{
753 754 755
  cmLocalNinjaGenerator* ng =
    static_cast<cmLocalNinjaGenerator*>(this->LocalGenerators[0]);
  std::string convPath = ng->Convert(path + "/all", cmOutputConverter::HOME);
756
  convPath = this->NinjaOutputPath(convPath);
757 758 759 760 761 762
#ifdef _WIN32
  cmSystemTools::ReplaceString(convPath, "/", "\\");
#endif
  return convPath;
}

763
void cmGlobalNinjaGenerator::AddCXXCompileCommand(
764
  const std::string& commandLine, const std::string& sourceFile)
765 766 767 768
{
  // Compute Ninja's build file path.
  std::string buildFileDir =
    this->GetCMakeInstance()->GetHomeOutputDirectory();
769
  if (!this->CompileCommandsStream) {
770 771 772 773 774 775
    std::string buildFilePath = buildFileDir + "/compile_commands.json";

    // Get a stream where to generate things.
    this->CompileCommandsStream =
      new cmGeneratedFileStream(buildFilePath.c_str());
    *this->CompileCommandsStream << "[";
776
  } else {
777
    *this->CompileCommandsStream << "," << std::endl;
778
  }
779

780
  std::string sourceFileName = sourceFile;
781
  if (!cmSystemTools::FileIsFullPath(sourceFileName.c_str())) {
782
    sourceFileName = cmSystemTools::CollapseFullPath(
783 784
      sourceFileName, this->GetCMakeInstance()->GetHomeOutputDirectory());
  }
785

786
  /* clang-format off */
787 788 789 790 791 792
  *this->CompileCommandsStream << "\n{\n"
     << "  \"directory\": \""
     << cmGlobalGenerator::EscapeJSON(buildFileDir) << "\",\n"
     << "  \"command\": \""
     << cmGlobalGenerator::EscapeJSON(commandLine) << "\",\n"
     << "  \"file\": \""
793
     << cmGlobalGenerator::EscapeJSON(sourceFileName) << "\"\n"
794
     << "}";
795
  /* clang-format on */
796 797 798 799
}

void cmGlobalNinjaGenerator::CloseCompileCommandsStream()
{
800
  if (this->CompileCommandsStream) {
801 802 803
    *this->CompileCommandsStream << "\n]";
    delete this->CompileCommandsStream;
    this->CompileCommandsStream = 0;
804
  }
805 806
}

807 808
void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os)
{
809 810 811 812
  os << "# CMAKE generated file: DO NOT EDIT!\n"
     << "# Generated by \"" << this->GetName() << "\""
     << " Generator, CMake Version " << cmVersion::GetMajorVersion() << "."
     << cmVersion::GetMinorVersion() << "\n\n";
813 814
}

815
void cmGlobalNinjaGenerator::AddDependencyToAll(cmGeneratorTarget* target)
816 817 818 819
{
  this->AppendTargetOutputs(target, this->AllDependencies);
}

820 821 822 823 824
void cmGlobalNinjaGenerator::AddDependencyToAll(const std::string& input)
{
  this->AllDependencies.push_back(input);
}

825
void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()