1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
4 Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
6 Distributed under the OSI-approved BSD License (the "License");
7 see accompanying file Copyright.txt for details.
9 This software is distributed WITHOUT ANY WARRANTY; without even the
10 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 See the License for more information.
12 ============================================================================*/
13 #include "cmGeneratedFileStream.h"
14 #include "cmGeneratorExpressionEvaluationFile.h"
15 #include "cmGeneratorTarget.h"
16 #include "cmGlobalNinjaGenerator.h"
17 #include "cmLocalNinjaGenerator.h"
18 #include "cmMakefile.h"
19 #include "cmVersion.h"
23 const char* cmGlobalNinjaGenerator::NINJA_BUILD_FILE = "build.ninja";
24 const char* cmGlobalNinjaGenerator::NINJA_RULES_FILE = "rules.ninja";
25 const char* cmGlobalNinjaGenerator::INDENT = " ";
27 void cmGlobalNinjaGenerator::Indent(std::ostream& os, int count)
29 for(int i = 0; i < count; ++i)
30 os << cmGlobalNinjaGenerator::INDENT;
33 void cmGlobalNinjaGenerator::WriteDivider(std::ostream& os)
36 << "# ======================================"
37 << "=======================================\n";
40 void cmGlobalNinjaGenerator::WriteComment(std::ostream& os,
41 const std::string& comment)
46 std::string replace = comment;
47 std::string::size_type lpos = 0;
48 std::string::size_type rpos;
49 os << "\n#############################################\n";
50 while((rpos = replace.find('\n', lpos)) != std::string::npos)
52 os << "# " << replace.substr(lpos, rpos - lpos) << "\n";
55 os << "# " << replace.substr(lpos) << "\n\n";
58 static bool IsIdentChar(char c)
61 ('a' <= c && c <= 'z') ||
62 ('+' <= c && c <= '9') || // +,-./ and numbers
63 ('A' <= c && c <= 'Z') ||
64 (c == '_') || (c == '$') || (c == '\\') ||
65 (c == ' ') || (c == ':');
68 std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string &ident,
70 if (std::find_if(ident.begin(), ident.end(),
71 std::not1(std::ptr_fun(IsIdentChar))) != ident.end()) {
72 static unsigned VarNum = 0;
73 cmOStringStream names;
74 names << "ident" << VarNum++;
75 vars << names.str() << " = " << ident << "\n";
76 return "$" + names.str();
78 std::string result = ident;
79 cmSystemTools::ReplaceString(result, " ", "$ ");
80 cmSystemTools::ReplaceString(result, ":", "$:");
85 std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string &lit)
87 std::string result = lit;
88 cmSystemTools::ReplaceString(result, "$", "$$");
89 cmSystemTools::ReplaceString(result, "\n", "$\n");
93 std::string cmGlobalNinjaGenerator::EncodePath(const std::string &path)
95 std::string result = path;
98 cmSystemTools::ReplaceString(result, "\\", "/");
100 cmSystemTools::ReplaceString(result, "/", "\\");
102 return EncodeLiteral(result);
105 void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os,
106 const std::string& comment,
107 const std::string& rule,
108 const cmNinjaDeps& outputs,
109 const cmNinjaDeps& explicitDeps,
110 const cmNinjaDeps& implicitDeps,
111 const cmNinjaDeps& orderOnlyDeps,
112 const cmNinjaVars& variables,
113 const std::string& rspfile,
116 // Make sure there is a rule.
119 cmSystemTools::Error("No rule for WriteBuildStatement! called "
125 // Make sure there is at least one output file.
128 cmSystemTools::Error("No output files for WriteBuildStatement! called "
134 cmGlobalNinjaGenerator::WriteComment(os, comment);
136 cmOStringStream arguments;
138 // TODO: Better formatting for when there are multiple input/output files.
140 // Write explicit dependencies.
141 for(cmNinjaDeps::const_iterator i = explicitDeps.begin();
142 i != explicitDeps.end();
145 arguments << " " << EncodeIdent(EncodePath(*i), os);
147 //we need to track every dependency that comes in, since we are trying
148 //to find dependencies that are side effects of build commands
150 this->CombinedBuildExplicitDependencies.insert( EncodePath(*i) );
153 // Write implicit dependencies.
154 if(!implicitDeps.empty())
157 for(cmNinjaDeps::const_iterator i = implicitDeps.begin();
158 i != implicitDeps.end();
160 arguments << " " << EncodeIdent(EncodePath(*i), os);
163 // Write order-only dependencies.
164 if(!orderOnlyDeps.empty())
167 for(cmNinjaDeps::const_iterator i = orderOnlyDeps.begin();
168 i != orderOnlyDeps.end();
170 arguments << " " << EncodeIdent(EncodePath(*i), os);
175 cmOStringStream build;
177 // Write outputs files.
179 for(cmNinjaDeps::const_iterator i = outputs.begin();
180 i != outputs.end(); ++i)
182 build << " " << EncodeIdent(EncodePath(*i), os);
183 this->CombinedBuildOutputs.insert( EncodePath(*i) );
188 build << " " << rule;
190 // Write the variables bound to this build statement.
191 cmOStringStream variable_assignments;
192 for(cmNinjaVars::const_iterator i = variables.begin();
193 i != variables.end(); ++i)
194 cmGlobalNinjaGenerator::WriteVariable(variable_assignments,
195 i->first, i->second, "", 1);
197 // check if a response file rule should be used
198 std::string buildstr = build.str();
199 std::string assignments = variable_assignments.str();
200 const std::string args = arguments.str();
202 && args.size() + buildstr.size() + assignments.size()
203 > (size_t) cmdLineLimit) {
204 buildstr += "_RSP_FILE";
205 variable_assignments.clear();
206 cmGlobalNinjaGenerator::WriteVariable(variable_assignments,
207 "RSP_FILE", rspfile, "", 1);
208 assignments += variable_assignments.str();
211 os << buildstr << args << assignments;
214 void cmGlobalNinjaGenerator::WritePhonyBuild(std::ostream& os,
215 const std::string& comment,
216 const cmNinjaDeps& outputs,
217 const cmNinjaDeps& explicitDeps,
218 const cmNinjaDeps& implicitDeps,
219 const cmNinjaDeps& orderOnlyDeps,
220 const cmNinjaVars& variables)
232 void cmGlobalNinjaGenerator::AddCustomCommandRule()
234 this->AddRule("CUSTOM_COMMAND",
237 "Rule for running custom commands.",
245 cmGlobalNinjaGenerator::WriteCustomCommandBuild(const std::string& command,
246 const std::string& description,
247 const std::string& comment,
248 const cmNinjaDeps& outputs,
249 const cmNinjaDeps& deps,
250 const cmNinjaDeps& orderOnlyDeps)
252 std::string cmd = command;
255 // TODO Shouldn't an empty command be handled by ninja?
259 this->AddCustomCommandRule();
262 vars["COMMAND"] = cmd;
263 vars["DESC"] = EncodeLiteral(description);
265 this->WriteBuild(*this->BuildFileStream,
276 cmGlobalNinjaGenerator::AddMacOSXContentRule()
278 cmLocalGenerator *lg = this->LocalGenerators[0];
279 cmMakefile* mfRoot = lg->GetMakefile();
282 cmd << lg->ConvertToOutputFormat(
283 mfRoot->GetRequiredDefinition("CMAKE_COMMAND"),
284 cmLocalGenerator::SHELL)
285 << " -E copy $in $out";
287 this->AddRule("COPY_OSX_CONTENT",
289 "Copying OS X Content $out",
290 "Rule for copying OS X bundle content file."
296 cmGlobalNinjaGenerator::WriteMacOSXContentBuild(const std::string& input,
297 const std::string& output)
299 this->AddMacOSXContentRule();
302 outputs.push_back(output);
304 deps.push_back(input);
307 this->WriteBuild(*this->BuildFileStream,
317 void cmGlobalNinjaGenerator::WriteRule(std::ostream& os,
318 const std::string& name,
319 const std::string& command,
320 const std::string& description,
321 const std::string& comment,
322 const std::string& depfile,
323 const std::string& rspfile,
324 const std::string& rspcontent,
328 // Make sure the rule has a name.
331 cmSystemTools::Error("No name given for WriteRuleStatement! called "
337 // Make sure a command is given.
340 cmSystemTools::Error("No command given for WriteRuleStatement! called "
346 cmGlobalNinjaGenerator::WriteComment(os, comment);
349 os << "rule " << name << "\n";
351 // Write the depfile if any.
354 cmGlobalNinjaGenerator::Indent(os, 1);
355 os << "depfile = " << depfile << "\n";
358 // Write the command.
359 cmGlobalNinjaGenerator::Indent(os, 1);
360 os << "command = " << command << "\n";
362 // Write the description if any.
363 if(!description.empty())
365 cmGlobalNinjaGenerator::Indent(os, 1);
366 os << "description = " << description << "\n";
371 if (rspcontent.empty())
373 cmSystemTools::Error("No rspfile_content given!", comment.c_str());
376 cmGlobalNinjaGenerator::Indent(os, 1);
377 os << "rspfile = " << rspfile << "\n";
378 cmGlobalNinjaGenerator::Indent(os, 1);
379 os << "rspfile_content = " << rspcontent << "\n";
384 cmGlobalNinjaGenerator::Indent(os, 1);
385 os << "restat = 1\n";
390 cmGlobalNinjaGenerator::Indent(os, 1);
391 os << "generator = 1\n";
397 void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os,
398 const std::string& name,
399 const std::string& value,
400 const std::string& comment,
403 // Make sure we have a name.
406 cmSystemTools::Error("No name given for WriteVariable! called "
412 // Do not add a variable if the value is empty.
413 std::string val = cmSystemTools::TrimWhitespace(value);
419 cmGlobalNinjaGenerator::WriteComment(os, comment);
420 cmGlobalNinjaGenerator::Indent(os, indent);
421 os << name << " = " << val << "\n";
424 void cmGlobalNinjaGenerator::WriteInclude(std::ostream& os,
425 const std::string& filename,
426 const std::string& comment)
428 cmGlobalNinjaGenerator::WriteComment(os, comment);
429 os << "include " << filename << "\n";
432 void cmGlobalNinjaGenerator::WriteDefault(std::ostream& os,
433 const cmNinjaDeps& targets,
434 const std::string& comment)
436 cmGlobalNinjaGenerator::WriteComment(os, comment);
438 for(cmNinjaDeps::const_iterator i = targets.begin(); i != targets.end(); ++i)
444 cmGlobalNinjaGenerator::cmGlobalNinjaGenerator()
445 : cmGlobalGenerator()
448 , CompileCommandsStream(0)
452 // // Ninja is not ported to non-Unix OS yet.
453 // this->ForceUnixPaths = true;
454 this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake";
458 //----------------------------------------------------------------------------
459 // Virtual public methods.
461 cmLocalGenerator* cmGlobalNinjaGenerator::CreateLocalGenerator()
463 cmLocalGenerator* lg = new cmLocalNinjaGenerator;
464 lg->SetGlobalGenerator(this);
468 void cmGlobalNinjaGenerator
469 ::GetDocumentation(cmDocumentationEntry& entry)
471 entry.Name = cmGlobalNinjaGenerator::GetActualName();
472 entry.Brief = "Generates build.ninja files (experimental).";
474 "A build.ninja file is generated into the build tree. Recent "
475 "versions of the ninja program can build the project through the "
476 "\"all\" target. An \"install\" target is also provided.";
479 // Implemented in all cmGlobaleGenerator sub-classes.
481 // Source/cmLocalGenerator.cxx
483 void cmGlobalNinjaGenerator::Generate()
485 this->OpenBuildFileStream();
486 this->OpenRulesFileStream();
488 this->cmGlobalGenerator::Generate();
490 this->WriteAssumedSourceDependencies();
491 this->WriteTargetAliases(*this->BuildFileStream);
492 this->WriteUnknownExplicitDependencies(*this->BuildFileStream);
493 this->WriteBuiltinTargets(*this->BuildFileStream);
495 if (cmSystemTools::GetErrorOccuredFlag()) {
496 this->RulesFileStream->setstate(std::ios_base::failbit);
497 this->BuildFileStream->setstate(std::ios_base::failbit);
500 this->CloseCompileCommandsStream();
501 this->CloseRulesFileStream();
502 this->CloseBuildFileStream();
505 // Implemented in all cmGlobaleGenerator sub-classes.
507 // Source/cmMakefile.cxx:
508 void cmGlobalNinjaGenerator
509 ::EnableLanguage(std::vector<std::string>const& langs,
510 cmMakefile* makefile,
513 if (makefile->IsOn("CMAKE_COMPILER_IS_MINGW"))
516 this->EnableMinGWLanguage(makefile);
518 if (std::find(langs.begin(), langs.end(), "Fortran") != langs.end())
520 cmSystemTools::Error("The Ninja generator does not support Fortran yet.");
522 this->cmGlobalGenerator::EnableLanguage(langs, makefile, optional);
525 bool cmGlobalNinjaGenerator::UsingMinGW = false;
528 // cmGlobalUnixMakefileGenerator3
529 // cmGlobalVisualStudio10Generator
530 // cmGlobalVisualStudio6Generator
531 // cmGlobalVisualStudio7Generator
532 // cmGlobalXCodeGenerator
534 // cmGlobalGenerator::Build()
535 std::string cmGlobalNinjaGenerator
536 ::GenerateBuildCommand(const char* makeProgram,
537 const char* projectName,
538 const char* projectDir,
539 const char* additionalOptions,
540 const char* targetName,
545 // Project name & dir and config are not used yet.
549 // Ninja does not have -i equivalent option yet.
551 // We do not handle fast build yet.
554 std::string makeCommand =
555 cmSystemTools::ConvertToUnixOutputPath(makeProgram);
557 if(additionalOptions)
560 makeCommand += additionalOptions;
564 if(strcmp(targetName, "clean") == 0)
566 makeCommand += " -t clean";
571 makeCommand += targetName;
578 //----------------------------------------------------------------------------
579 // Non-virtual public methods.
581 void cmGlobalNinjaGenerator::AddRule(const std::string& name,
582 const std::string& command,
583 const std::string& description,
584 const std::string& comment,
585 const std::string& depfile,
586 const std::string& rspfile,
587 const std::string& rspcontent,
591 // Do not add the same rule twice.
592 if (this->HasRule(name))
597 this->Rules.insert(name);
598 cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream,
609 this->RuleCmdLength[name] = (int) command.size();
612 bool cmGlobalNinjaGenerator::HasRule(const std::string &name)
614 RulesSetType::const_iterator rule = this->Rules.find(name);
615 return (rule != this->Rules.end());
618 //----------------------------------------------------------------------------
619 // Private virtual overrides
621 // TODO: Refactor to combine with cmGlobalUnixMakefileGenerator3 impl.
622 void cmGlobalNinjaGenerator::ComputeTargetObjects(cmGeneratorTarget* gt) const
624 cmTarget* target = gt->Target;
626 // Compute full path to object file directory for this target.
628 dir_max += gt->Makefile->GetCurrentOutputDirectory();
630 dir_max += gt->LocalGenerator->GetTargetDirectory(*target);
632 gt->ObjectDirectory = dir_max;
634 // Compute the name of each object file.
635 for(std::vector<cmSourceFile*>::iterator
636 si = gt->ObjectSources.begin();
637 si != gt->ObjectSources.end(); ++si)
639 cmSourceFile* sf = *si;
640 std::string objectName = gt->LocalGenerator
641 ->GetObjectFileNameWithoutTarget(*sf, dir_max);
642 gt->Objects[sf] = objectName;
646 //----------------------------------------------------------------------------
649 void cmGlobalNinjaGenerator::OpenBuildFileStream()
651 // Compute Ninja's build file path.
652 std::string buildFilePath =
653 this->GetCMakeInstance()->GetHomeOutputDirectory();
654 buildFilePath += "/";
655 buildFilePath += cmGlobalNinjaGenerator::NINJA_BUILD_FILE;
657 // Get a stream where to generate things.
658 if (!this->BuildFileStream)
660 this->BuildFileStream = new cmGeneratedFileStream(buildFilePath.c_str());
661 if (!this->BuildFileStream)
663 // An error message is generated by the constructor if it cannot
669 // Write the do not edit header.
670 this->WriteDisclaimer(*this->BuildFileStream);
672 // Write a comment about this file.
673 *this->BuildFileStream
674 << "# This file contains all the build statements describing the\n"
675 << "# compilation DAG.\n\n"
679 void cmGlobalNinjaGenerator::CloseBuildFileStream()
681 if (this->BuildFileStream)
683 delete this->BuildFileStream;
684 this->BuildFileStream = 0;
688 cmSystemTools::Error("Build file stream was not open.");
692 void cmGlobalNinjaGenerator::OpenRulesFileStream()
694 // Compute Ninja's build file path.
695 std::string rulesFilePath =
696 this->GetCMakeInstance()->GetHomeOutputDirectory();
697 rulesFilePath += "/";
698 rulesFilePath += cmGlobalNinjaGenerator::NINJA_RULES_FILE;
700 // Get a stream where to generate things.
701 if (!this->RulesFileStream)
703 this->RulesFileStream = new cmGeneratedFileStream(rulesFilePath.c_str());
704 if (!this->RulesFileStream)
706 // An error message is generated by the constructor if it cannot
712 // Write the do not edit header.
713 this->WriteDisclaimer(*this->RulesFileStream);
715 // Write comment about this file.
716 *this->RulesFileStream
717 << "# This file contains all the rules used to get the outputs files\n"
718 << "# built from the input files.\n"
719 << "# It is included in the main '" << NINJA_BUILD_FILE << "'.\n\n"
723 void cmGlobalNinjaGenerator::CloseRulesFileStream()
725 if (this->RulesFileStream)
727 delete this->RulesFileStream;
728 this->RulesFileStream = 0;
732 cmSystemTools::Error("Rules file stream was not open.");
736 void cmGlobalNinjaGenerator::AddCXXCompileCommand(
737 const std::string &commandLine,
738 const std::string &sourceFile)
740 // Compute Ninja's build file path.
741 std::string buildFileDir =
742 this->GetCMakeInstance()->GetHomeOutputDirectory();
743 if (!this->CompileCommandsStream)
745 std::string buildFilePath = buildFileDir + "/compile_commands.json";
747 // Get a stream where to generate things.
748 this->CompileCommandsStream =
749 new cmGeneratedFileStream(buildFilePath.c_str());
750 *this->CompileCommandsStream << "[";
752 *this->CompileCommandsStream << "," << std::endl;
755 std::string sourceFileName = sourceFile;
756 if (!cmSystemTools::FileIsFullPath(sourceFileName.c_str()))
758 sourceFileName = cmSystemTools::CollapseFullPath(
759 sourceFileName.c_str(),
760 this->GetCMakeInstance()->GetHomeOutputDirectory());
764 *this->CompileCommandsStream << "\n{\n"
765 << " \"directory\": \""
766 << cmGlobalGenerator::EscapeJSON(buildFileDir) << "\",\n"
767 << " \"command\": \""
768 << cmGlobalGenerator::EscapeJSON(commandLine) << "\",\n"
770 << cmGlobalGenerator::EscapeJSON(sourceFileName) << "\"\n"
774 void cmGlobalNinjaGenerator::CloseCompileCommandsStream()
776 if (this->CompileCommandsStream)
778 *this->CompileCommandsStream << "\n]";
779 delete this->CompileCommandsStream;
780 this->CompileCommandsStream = 0;
785 void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os)
788 << "# CMAKE generated file: DO NOT EDIT!\n"
789 << "# Generated by \"" << this->GetName() << "\""
790 << " Generator, CMake Version "
791 << cmVersion::GetMajorVersion() << "."
792 << cmVersion::GetMinorVersion() << "\n\n";
795 void cmGlobalNinjaGenerator::AddDependencyToAll(cmTarget* target)
797 this->AppendTargetOutputs(target, this->AllDependencies);
800 void cmGlobalNinjaGenerator::AddDependencyToAll(const std::string& input)
802 this->AllDependencies.push_back(input);
805 void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()
807 for (std::map<std::string, std::set<std::string> >::iterator
808 i = this->AssumedSourceDependencies.begin();
809 i != this->AssumedSourceDependencies.end(); ++i) {
811 std::copy(i->second.begin(), i->second.end(), std::back_inserter(deps));
812 WriteCustomCommandBuild(/*command=*/"", /*description=*/"",
813 "Assume dependencies for generated source file.",
814 cmNinjaDeps(1, i->first), deps);
819 cmGlobalNinjaGenerator
820 ::AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs)
822 const char* configName =
823 target->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE");
824 cmLocalNinjaGenerator *ng =
825 static_cast<cmLocalNinjaGenerator *>(this->LocalGenerators[0]);
827 // for frameworks, we want the real name, not smple name
828 // frameworks always appear versioned, and the build.ninja
829 // will always attempt to manage symbolic links instead
830 // of letting cmOSXBundleGenerator do it.
831 bool realname = target->IsFrameworkOnApple();
833 switch (target->GetType()) {
834 case cmTarget::EXECUTABLE:
835 case cmTarget::SHARED_LIBRARY:
836 case cmTarget::STATIC_LIBRARY:
837 case cmTarget::MODULE_LIBRARY:
838 outputs.push_back(ng->ConvertToNinjaPath(
839 target->GetFullPath(configName, false, realname).c_str()));
842 case cmTarget::OBJECT_LIBRARY:
843 case cmTarget::UTILITY: {
844 std::string path = ng->ConvertToNinjaPath(
845 target->GetMakefile()->GetStartOutputDirectory());
846 if (path.empty() || path == ".")
847 outputs.push_back(target->GetName());
850 path += target->GetName();
851 outputs.push_back(path);
856 case cmTarget::GLOBAL_TARGET:
857 // Always use the target in HOME instead of an unused duplicate in a
859 outputs.push_back(target->GetName());
868 cmGlobalNinjaGenerator
869 ::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs)
871 if (target->GetType() == cmTarget::GLOBAL_TARGET) {
872 // Global targets only depend on other utilities, which may not appear in
873 // the TargetDepends set (e.g. "all").
874 std::set<cmStdString> const& utils = target->GetUtilities();
875 std::copy(utils.begin(), utils.end(), std::back_inserter(outputs));
877 cmTargetDependSet const& targetDeps =
878 this->GetTargetDirectDepends(*target);
879 for (cmTargetDependSet::const_iterator i = targetDeps.begin();
880 i != targetDeps.end(); ++i) {
881 this->AppendTargetOutputs(*i, outputs);
886 void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias,
889 this->AppendTargetOutputs(target, outputs);
890 // Mark the target's outputs as ambiguous to ensure that no other target uses
891 // the output as an alias.
892 for (cmNinjaDeps::iterator i = outputs.begin(); i != outputs.end(); ++i)
893 TargetAliases[*i] = 0;
895 // Insert the alias into the map. If the alias was already present in the
896 // map and referred to another target, mark it as ambiguous.
897 std::pair<TargetAliasMap::iterator, bool> newAlias =
898 TargetAliases.insert(std::make_pair(alias, target));
899 if (newAlias.second && newAlias.first->second != target)
900 newAlias.first->second = 0;
903 void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os)
905 cmGlobalNinjaGenerator::WriteDivider(os);
906 os << "# Target aliases.\n\n";
908 for (TargetAliasMap::const_iterator i = TargetAliases.begin();
909 i != TargetAliases.end(); ++i) {
910 // Don't write ambiguous aliases.
915 this->AppendTargetOutputs(i->second, deps);
917 this->WritePhonyBuild(os,
919 cmNinjaDeps(1, i->first),
924 void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os)
926 //now write out the unknown explicit dependencies.
928 //union the configured files, evaluations files and the CombinedBuildOutputs,
929 //and then difference with CombinedExplicitDependencies to find the explicit
930 //dependencies that we have no rule for
932 cmGlobalNinjaGenerator::WriteDivider(os);
933 os << "# Unknown Build Time Dependencies.\n"
934 << "# Tell Ninja that they may appear as side effects of build rules\n"
935 << "# otherwise ordered by order-only dependencies.\n\n";
937 //get the list of files that cmake itself has generated as a
938 //product of configuration.
939 cmLocalNinjaGenerator *ng =
940 static_cast<cmLocalNinjaGenerator *>(this->LocalGenerators[0]);
942 std::set<std::string> knownDependencies;
943 for (std::vector<cmLocalGenerator *>::const_iterator i =
944 this->LocalGenerators.begin(); i != this->LocalGenerators.end(); ++i)
946 //get the vector of files created by this makefile and convert them
947 //to ninja paths, which are all relative in respect to the build directory
948 const std::vector<std::string>& files =
949 (*i)->GetMakefile()->GetOutputFiles();
950 typedef std::vector<std::string>::const_iterator vect_it;
951 for(vect_it j = files.begin(); j != files.end(); ++j)
953 knownDependencies.insert( ng->ConvertToNinjaPath( j->c_str() ) );
957 for(std::vector<cmGeneratorExpressionEvaluationFile*>::const_iterator
958 li = this->EvaluationFiles.begin();
959 li != this->EvaluationFiles.end();
962 //get all the files created by generator expressions and convert them
964 std::vector<std::string> files = (*li)->GetFiles();
965 typedef std::vector<std::string>::const_iterator vect_it;
966 for(vect_it j = files.begin(); j != files.end(); ++j)
968 knownDependencies.insert( ng->ConvertToNinjaPath( j->c_str() ) );
972 for(TargetAliasMap::const_iterator i= this->TargetAliases.begin();
973 i != this->TargetAliases.end();
976 knownDependencies.insert( ng->ConvertToNinjaPath(i->first.c_str()) );
979 //remove all source files we know will exist.
980 typedef std::map<std::string, std::set<std::string> >::const_iterator map_it;
981 for(map_it i = this->AssumedSourceDependencies.begin();
982 i != this->AssumedSourceDependencies.end();
985 knownDependencies.insert( ng->ConvertToNinjaPath(i->first.c_str()) );
988 //insert outputs from all WirteBuild commands
989 for(std::set<std::string>::iterator i = this->CombinedBuildOutputs.begin();
990 i != this->CombinedBuildOutputs.end(); ++i)
992 //these paths have already be encoded when added to CombinedBuildOutputs
993 knownDependencies.insert(*i);
996 //after we have combined the data into knownDependencies we have no need
997 //to keep this data around
998 this->CombinedBuildOutputs.clear();
1000 //now we difference with CombinedBuildExplicitDependencies to find
1001 //the list of items we know nothing about.
1002 //We have encoded all the paths in CombinedBuildExplicitDependencies
1003 //and knownDependencies so no matter if unix or windows paths they
1004 //should all match now.
1006 std::vector<std::string> unkownExplicitDepends;
1007 this->CombinedBuildExplicitDependencies.erase("all");
1009 std::set_difference(this->CombinedBuildExplicitDependencies.begin(),
1010 this->CombinedBuildExplicitDependencies.end(),
1011 knownDependencies.begin(),
1012 knownDependencies.end(),
1013 std::back_inserter(unkownExplicitDepends));
1016 std::string const rootBuildDirectory =
1017 this->GetCMakeInstance()->GetHomeOutputDirectory();
1018 for (std::vector<std::string>::const_iterator
1019 i = unkownExplicitDepends.begin();
1020 i != unkownExplicitDepends.end();
1023 //verify the file is in the build directory
1024 std::string const absDepPath = cmSystemTools::CollapseFullPath(
1025 i->c_str(), rootBuildDirectory.c_str());
1026 bool const inBuildDir = cmSystemTools::IsSubDirectory(absDepPath.c_str(),
1027 rootBuildDirectory.c_str());
1030 cmNinjaDeps deps(1,*i);
1031 this->WritePhonyBuild(os,
1039 void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os)
1042 cmGlobalNinjaGenerator::WriteDivider(os);
1043 os << "# Built-in targets\n\n";
1045 this->WriteTargetAll(os);
1046 this->WriteTargetRebuildManifest(os);
1047 this->WriteTargetClean(os);
1048 this->WriteTargetHelp(os);
1051 void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os)
1053 cmNinjaDeps outputs;
1054 outputs.push_back("all");
1056 this->WritePhonyBuild(os,
1057 "The main all target.",
1059 this->AllDependencies);
1061 cmGlobalNinjaGenerator::WriteDefault(os,
1063 "Make the all target the default.");
1066 void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
1068 cmLocalGenerator *lg = this->LocalGenerators[0];
1069 cmMakefile* mfRoot = lg->GetMakefile();
1071 cmOStringStream cmd;
1072 cmd << lg->ConvertToOutputFormat(
1073 mfRoot->GetRequiredDefinition("CMAKE_COMMAND"),
1074 cmLocalGenerator::SHELL)
1076 << lg->ConvertToOutputFormat(mfRoot->GetHomeDirectory(),
1077 cmLocalGenerator::SHELL)
1079 << lg->ConvertToOutputFormat(mfRoot->GetHomeOutputDirectory(),
1080 cmLocalGenerator::SHELL);
1081 WriteRule(*this->RulesFileStream,
1084 "Re-running CMake...",
1085 "Rule for re-running cmake.",
1090 /*generator=*/ true);
1092 cmNinjaDeps implicitDeps;
1093 for (std::vector<cmLocalGenerator *>::const_iterator i =
1094 this->LocalGenerators.begin(); i != this->LocalGenerators.end(); ++i) {
1095 const std::vector<std::string>& lf = (*i)->GetMakefile()->GetListFiles();
1096 implicitDeps.insert(implicitDeps.end(), lf.begin(), lf.end());
1098 std::sort(implicitDeps.begin(), implicitDeps.end());
1099 implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()),
1100 implicitDeps.end());
1101 implicitDeps.push_back("CMakeCache.txt");
1103 this->WriteBuild(os,
1104 "Re-run CMake if any of its inputs changed.",
1106 /*outputs=*/ cmNinjaDeps(1, NINJA_BUILD_FILE),
1107 /*explicitDeps=*/ cmNinjaDeps(),
1109 /*orderOnlyDeps=*/ cmNinjaDeps(),
1110 /*variables=*/ cmNinjaVars());
1112 this->WritePhonyBuild(os,
1113 "A missing CMake input file is not an error.",
1118 std::string cmGlobalNinjaGenerator::ninjaCmd() const
1120 cmLocalGenerator* lgen = this->LocalGenerators[0];
1122 return lgen->ConvertToOutputFormat(
1123 lgen->GetMakefile()->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"),
1124 cmLocalGenerator::SHELL);
1129 void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os)
1131 WriteRule(*this->RulesFileStream,
1133 (ninjaCmd() + " -t clean").c_str(),
1134 "Cleaning all built files...",
1135 "Rule for cleaning all built files.",
1140 /*generator=*/ false);
1142 "Clean all the built files.",
1144 /*outputs=*/ cmNinjaDeps(1, "clean"),
1145 /*explicitDeps=*/ cmNinjaDeps(),
1146 /*implicitDeps=*/ cmNinjaDeps(),
1147 /*orderOnlyDeps=*/ cmNinjaDeps(),
1148 /*variables=*/ cmNinjaVars());
1151 void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os)
1153 WriteRule(*this->RulesFileStream,
1155 (ninjaCmd() + " -t targets").c_str(),
1156 "All primary targets available:",
1157 "Rule for printing all primary targets available.",
1162 /*generator=*/ false);
1164 "Print all primary targets available.",
1166 /*outputs=*/ cmNinjaDeps(1, "help"),
1167 /*explicitDeps=*/ cmNinjaDeps(),
1168 /*implicitDeps=*/ cmNinjaDeps(),
1169 /*orderOnlyDeps=*/ cmNinjaDeps(),
1170 /*variables=*/ cmNinjaVars());