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 "cmNinjaTargetGenerator.h"
14 #include "cmGlobalNinjaGenerator.h"
15 #include "cmLocalNinjaGenerator.h"
16 #include "cmGeneratedFileStream.h"
17 #include "cmGeneratorTarget.h"
18 #include "cmNinjaNormalTargetGenerator.h"
19 #include "cmNinjaUtilityTargetGenerator.h"
20 #include "cmSystemTools.h"
21 #include "cmMakefile.h"
22 #include "cmComputeLinkInformation.h"
23 #include "cmSourceFile.h"
24 #include "cmCustomCommandGenerator.h"
28 cmNinjaTargetGenerator *
29 cmNinjaTargetGenerator::New(cmTarget* target)
31 switch (target->GetType())
33 case cmTarget::EXECUTABLE:
34 case cmTarget::SHARED_LIBRARY:
35 case cmTarget::STATIC_LIBRARY:
36 case cmTarget::MODULE_LIBRARY:
37 case cmTarget::OBJECT_LIBRARY:
38 return new cmNinjaNormalTargetGenerator(target);
40 case cmTarget::UTILITY:
41 return new cmNinjaUtilityTargetGenerator(target);;
43 case cmTarget::GLOBAL_TARGET: {
44 // We only want to process global targets that live in the home
45 // (i.e. top-level) directory. CMake creates copies of these targets
46 // in every directory, which we don't need.
47 cmMakefile *mf = target->GetMakefile();
48 if (strcmp(mf->GetStartDirectory(), mf->GetHomeDirectory()) == 0)
49 return new cmNinjaUtilityTargetGenerator(target);
58 cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmTarget* target)
60 MacOSXContentGenerator(0),
61 OSXBundleGenerator(0),
64 Makefile(target->GetMakefile()),
66 static_cast<cmLocalNinjaGenerator*>(Makefile->GetLocalGenerator())),
69 this->GeneratorTarget =
70 this->GetGlobalGenerator()->GetGeneratorTarget(target);
71 MacOSXContentGenerator = new MacOSXContentGeneratorType(this);
74 cmNinjaTargetGenerator::~cmNinjaTargetGenerator()
76 delete this->MacOSXContentGenerator;
79 cmGeneratedFileStream& cmNinjaTargetGenerator::GetBuildFileStream() const
81 return *this->GetGlobalGenerator()->GetBuildFileStream();
84 cmGeneratedFileStream& cmNinjaTargetGenerator::GetRulesFileStream() const
86 return *this->GetGlobalGenerator()->GetRulesFileStream();
89 cmGlobalNinjaGenerator* cmNinjaTargetGenerator::GetGlobalGenerator() const
91 return this->LocalGenerator->GetGlobalNinjaGenerator();
94 const char* cmNinjaTargetGenerator::GetConfigName() const
96 return this->LocalGenerator->GetConfigName();
99 // TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
100 const char* cmNinjaTargetGenerator::GetFeature(const char* feature)
102 return this->Target->GetFeature(feature, this->GetConfigName());
105 // TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
106 bool cmNinjaTargetGenerator::GetFeatureAsBool(const char* feature)
108 return cmSystemTools::IsOn(this->GetFeature(feature));
111 // TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
112 void cmNinjaTargetGenerator::AddFeatureFlags(std::string& flags,
115 // Add language-specific flags.
116 this->LocalGenerator->AddLanguageFlags(flags, lang, this->GetConfigName());
118 if(this->GetFeatureAsBool("INTERPROCEDURAL_OPTIMIZATION"))
120 this->LocalGenerator->AppendFeatureOptions(flags, lang, "IPO");
124 // TODO: Most of the code is picked up from
125 // void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink),
126 // void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
129 cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source,
130 const std::string& language)
134 this->AddFeatureFlags(flags, language.c_str());
136 this->GetLocalGenerator()->AddArchitectureFlags(flags,
137 this->GeneratorTarget,
139 this->GetConfigName());
141 // TODO: Fortran support.
142 // // Fortran-specific flags computed for this target.
143 // if(*l == "Fortran")
145 // this->AddFortranFlags(flags);
148 // Add shared-library flags if needed.
149 this->LocalGenerator->AddCMP0018Flags(flags, this->Target,
151 this->GetConfigName());
153 this->LocalGenerator->AddVisibilityPresetFlags(flags, this->Target,
156 // Add include directory flags.
157 const char *config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE");
159 std::vector<std::string> includes;
160 this->LocalGenerator->GetIncludeDirectories(includes,
161 this->GeneratorTarget,
162 language.c_str(), config);
163 std::string includeFlags =
164 this->LocalGenerator->GetIncludeFlags(includes, this->GeneratorTarget,
166 language == "RC" ? true : false); // full include paths for RC
167 // needed by cmcldeps
168 if(cmGlobalNinjaGenerator::IsMinGW())
169 cmSystemTools::ReplaceString(includeFlags, "\\", "/");
171 this->LocalGenerator->AppendFlags(flags, includeFlags.c_str());
174 // Append old-style preprocessor definition flags.
175 this->LocalGenerator->AppendFlags(flags, this->Makefile->GetDefineFlags());
177 // Add target-specific flags.
178 this->LocalGenerator->AddCompileOptions(flags, this->Target,
179 language.c_str(), config);
181 // Add source file specific flags.
182 this->LocalGenerator->AppendFlags(flags,
183 source->GetProperty("COMPILE_FLAGS"));
185 // TODO: Handle Apple frameworks.
190 // TODO: Refactor with
191 // void cmMakefileTargetGenerator::WriteTargetLanguageFlags().
193 cmNinjaTargetGenerator::
194 ComputeDefines(cmSourceFile *source, const std::string& language)
196 std::set<std::string> defines;
198 // Add the export symbol definition for shared library objects.
199 if(const char* exportMacro = this->Target->GetExportMacro())
201 this->LocalGenerator->AppendDefines(defines, exportMacro);
204 // Add preprocessor definitions for this target and configuration.
205 this->LocalGenerator->AddCompileDefinitions(defines, this->Target,
206 this->GetConfigName());
207 this->LocalGenerator->AppendDefines
209 source->GetProperty("COMPILE_DEFINITIONS"));
211 std::string defPropName = "COMPILE_DEFINITIONS_";
212 defPropName += cmSystemTools::UpperCase(this->GetConfigName());
213 this->LocalGenerator->AppendDefines
215 source->GetProperty(defPropName.c_str()));
218 std::string definesString;
219 this->LocalGenerator->JoinDefines(defines, definesString,
222 return definesString;
225 cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const
227 // Static libraries never depend on other targets for linking.
228 if (this->Target->GetType() == cmTarget::STATIC_LIBRARY ||
229 this->Target->GetType() == cmTarget::OBJECT_LIBRARY)
230 return cmNinjaDeps();
232 cmComputeLinkInformation* cli =
233 this->Target->GetLinkInformation(this->GetConfigName());
235 return cmNinjaDeps();
237 const std::vector<std::string> &deps = cli->GetDepends();
238 cmNinjaDeps result(deps.size());
239 std::transform(deps.begin(), deps.end(), result.begin(), MapToNinjaPath());
241 // Add a dependency on the link definitions file, if any.
242 if(!this->ModuleDefinitionFile.empty())
244 result.push_back(this->ModuleDefinitionFile);
251 cmNinjaTargetGenerator
252 ::GetSourceFilePath(cmSourceFile* source) const
254 return ConvertToNinjaPath(source->GetFullPath().c_str());
258 cmNinjaTargetGenerator
259 ::GetObjectFilePath(cmSourceFile* source) const
261 std::string path = this->LocalGenerator->GetHomeRelativeOutputPath();
264 std::string const& objectName = this->GeneratorTarget->Objects[source];
265 path += this->LocalGenerator->GetTargetDirectory(*this->Target);
271 std::string cmNinjaTargetGenerator::GetTargetOutputDir() const
273 std::string dir = this->Target->GetDirectory(this->GetConfigName());
274 return ConvertToNinjaPath(dir.c_str());
278 cmNinjaTargetGenerator
279 ::GetTargetFilePath(const std::string& name) const
281 std::string path = this->GetTargetOutputDir();
282 if (path.empty() || path == ".")
289 std::string cmNinjaTargetGenerator::GetTargetName() const
291 return this->Target->GetName();
295 bool cmNinjaTargetGenerator::SetMsvcTargetPdbVariable(cmNinjaVars& vars) const
297 cmMakefile* mf = this->GetMakefile();
298 if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") ||
299 mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID"))
302 if(this->Target->GetType() == cmTarget::EXECUTABLE ||
303 this->Target->GetType() == cmTarget::STATIC_LIBRARY ||
304 this->Target->GetType() == cmTarget::SHARED_LIBRARY ||
305 this->Target->GetType() == cmTarget::MODULE_LIBRARY)
307 pdbPath = this->Target->GetPDBDirectory(this->GetConfigName());
309 pdbPath += this->Target->GetPDBName(this->GetConfigName());
312 vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
313 ConvertToNinjaPath(pdbPath.c_str()).c_str(),
314 cmLocalGenerator::SHELL);
315 EnsureParentDirectoryExists(pdbPath);
322 cmNinjaTargetGenerator
323 ::WriteLanguageRules(const std::string& language)
325 #ifdef NINJA_GEN_VERBOSE_FILES
326 this->GetRulesFileStream()
327 << "# Rules for language " << language << "\n\n";
329 this->WriteCompileRule(language);
333 cmNinjaTargetGenerator
334 ::WriteCompileRule(const std::string& language)
336 cmLocalGenerator::RuleVariables vars;
337 vars.RuleLauncher = "RULE_LAUNCH_COMPILE";
338 vars.CMTarget = this->GetTarget();
339 std::string lang = language;
340 vars.Language = lang.c_str();
342 vars.Object = "$out";
343 std::string flags = "$FLAGS";
344 vars.Defines = "$DEFINES";
345 vars.TargetPDB = "$TARGET_PDB";
346 vars.ObjectDir = "$OBJECT_DIR";
348 cmMakefile* mf = this->GetMakefile();
350 bool useClDeps = false;
351 std::string clBinary;
352 std::string clDepsBinary;
353 std::string clShowPrefix;
354 if (lang == "C" || lang == "CXX" || lang == "RC")
356 clDepsBinary = mf->GetSafeDefinition("CMAKE_CMCLDEPS_EXECUTABLE");
357 if (!clDepsBinary.empty() && !mf->GetIsSourceFileTryCompile())
359 clShowPrefix = mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDE_PREFIX");
360 clBinary = mf->GetDefinition("CMAKE_C_COMPILER") ?
361 mf->GetSafeDefinition("CMAKE_C_COMPILER") :
362 mf->GetSafeDefinition("CMAKE_CXX_COMPILER");
363 if (!clBinary.empty() && !clShowPrefix.empty())
366 const std::string quote = " \"";
367 clBinary = quote + clBinary + "\" ";
368 clDepsBinary = quote + clDepsBinary + "\" ";
369 clShowPrefix = quote + clShowPrefix + "\" ";
370 vars.DependencyFile = "$DEP_FILE";
377 std::string depfileFlagsName = "CMAKE_DEPFILE_FLAGS_" + language;
378 const char *depfileFlags = mf->GetDefinition(depfileFlagsName.c_str());
379 if (depfileFlags || useClDeps) {
380 std::string depFlagsStr = depfileFlags ? depfileFlags : "";
381 depfile = "$DEP_FILE";
382 cmSystemTools::ReplaceString(depFlagsStr, "<DEPFILE>", "\"$DEP_FILE\"");
383 cmSystemTools::ReplaceString(depFlagsStr, "<OBJECT>", "$out");
384 cmSystemTools::ReplaceString(depFlagsStr, "<CMAKE_C_COMPILER>",
385 mf->GetDefinition("CMAKE_C_COMPILER"));
386 flags += " " + depFlagsStr;
388 vars.Flags = flags.c_str();
391 // Rule for compiling object file.
392 std::string compileCmdVar = "CMAKE_";
393 compileCmdVar += language;
394 compileCmdVar += "_COMPILE_OBJECT";
395 std::string compileCmd = mf->GetRequiredDefinition(compileCmdVar.c_str());
396 std::vector<std::string> compileCmds;
397 cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
401 std::string cmdPrefix = clDepsBinary + lang + " $in \"$DEP_FILE\" $out " +
402 clShowPrefix + clBinary;
403 compileCmds.front().insert(0, cmdPrefix);
406 for (std::vector<std::string>::iterator i = compileCmds.begin();
407 i != compileCmds.end(); ++i)
408 this->GetLocalGenerator()->ExpandRuleVariables(*i, vars);
410 std::string cmdLine =
411 this->GetLocalGenerator()->BuildCommandLine(compileCmds);
414 // Write the rule for compiling file of the given language.
415 cmOStringStream comment;
416 comment << "Rule for compiling " << language << " files.";
417 cmOStringStream description;
418 description << "Building " << language << " object $out";
419 this->GetGlobalGenerator()->AddRule(this->LanguageCompilerRule(language),
427 cmNinjaTargetGenerator
428 ::WriteObjectBuildStatements()
431 cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
432 this->GetBuildFileStream()
433 << "# Object build statements for "
434 << cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
436 << this->GetTargetName()
439 for(std::vector<cmSourceFile*>::const_iterator
440 si = this->GeneratorTarget->CustomCommands.begin();
441 si != this->GeneratorTarget->CustomCommands.end(); ++si)
443 cmCustomCommand const* cc = (*si)->GetCustomCommand();
444 this->GetLocalGenerator()->AddCustomCommandTarget(cc, this->GetTarget());
446 this->OSXBundleGenerator->GenerateMacOSXContentStatements(
447 this->GeneratorTarget->HeaderSources,
448 this->MacOSXContentGenerator);
449 this->OSXBundleGenerator->GenerateMacOSXContentStatements(
450 this->GeneratorTarget->ExtraSources,
451 this->MacOSXContentGenerator);
452 for(std::vector<cmSourceFile*>::const_iterator
453 si = this->GeneratorTarget->ExternalObjects.begin();
454 si != this->GeneratorTarget->ExternalObjects.end(); ++si)
456 this->Objects.push_back(this->GetSourceFilePath(*si));
458 for(std::vector<cmSourceFile*>::const_iterator
459 si = this->GeneratorTarget->ObjectSources.begin();
460 si != this->GeneratorTarget->ObjectSources.end(); ++si)
462 this->WriteObjectBuildStatement(*si);
464 if(!this->GeneratorTarget->ModuleDefinitionFile.empty())
466 this->ModuleDefinitionFile = this->ConvertToNinjaPath(
467 this->GeneratorTarget->ModuleDefinitionFile.c_str());
471 // Add object library contents as external objects.
472 std::vector<std::string> objs;
473 this->GeneratorTarget->UseObjectLibraries(objs);
474 for(std::vector<std::string>::iterator oi = objs.begin();
475 oi != objs.end(); ++oi)
477 this->Objects.push_back(ConvertToNinjaPath(oi->c_str()));
481 this->GetBuildFileStream() << "\n";
485 cmNinjaTargetGenerator
486 ::WriteObjectBuildStatement(cmSourceFile* source)
489 const std::string language = source->GetLanguage();
490 std::string rule = this->LanguageCompilerRule(language);
493 std::string objectFileName = this->GetObjectFilePath(source);
494 outputs.push_back(objectFileName);
495 // Add this object to the list of object files.
496 this->Objects.push_back(objectFileName);
498 cmNinjaDeps explicitDeps;
499 std::string sourceFileName;
500 if (language == "RC")
501 sourceFileName = source->GetFullPath();
503 sourceFileName = this->GetSourceFilePath(source);
504 explicitDeps.push_back(sourceFileName);
506 // Ensure that the target dependencies are built before any source file in
507 // the target, using order-only dependencies.
508 cmNinjaDeps orderOnlyDeps;
509 this->GetLocalGenerator()->AppendTargetDepends(this->Target, orderOnlyDeps);
511 cmNinjaDeps implicitDeps;
512 if(const char* objectDeps = source->GetProperty("OBJECT_DEPENDS")) {
513 std::vector<std::string> depList;
514 cmSystemTools::ExpandListArgument(objectDeps, depList);
515 std::transform(depList.begin(), depList.end(),
516 std::back_inserter(implicitDeps), MapToNinjaPath());
519 // Add order-only dependencies on custom command outputs.
520 for(std::vector<cmSourceFile*>::const_iterator
521 si = this->GeneratorTarget->CustomCommands.begin();
522 si != this->GeneratorTarget->CustomCommands.end(); ++si)
524 cmCustomCommand const* cc = (*si)->GetCustomCommand();
525 const std::vector<std::string>& ccoutputs = cc->GetOutputs();
526 std::transform(ccoutputs.begin(), ccoutputs.end(),
527 std::back_inserter(orderOnlyDeps), MapToNinjaPath());
530 // If the source file is GENERATED and does not have a custom command
531 // (either attached to this source file or another one), assume that one of
532 // the target dependencies, OBJECT_DEPENDS or header file custom commands
533 // will rebuild the file.
534 if (source->GetPropertyAsBool("GENERATED") && !source->GetCustomCommand() &&
535 !this->GetGlobalGenerator()->HasCustomCommandOutput(sourceFileName)) {
536 this->GetGlobalGenerator()->AddAssumedSourceDependencies(sourceFileName,
541 vars["FLAGS"] = this->ComputeFlagsForObject(source, language);
542 vars["DEFINES"] = this->ComputeDefines(source, language);
543 vars["DEP_FILE"] = objectFileName + ".d";;
544 EnsureParentDirectoryExists(objectFileName);
546 std::string objectDir = this->Target->GetSupportDirectory();
547 vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
548 ConvertToNinjaPath(objectDir.c_str()).c_str(),
549 cmLocalGenerator::SHELL);
551 this->SetMsvcTargetPdbVariable(vars);
553 if(this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS"))
555 cmLocalGenerator::RuleVariables compileObjectVars;
556 std::string lang = language;
557 compileObjectVars.Language = lang.c_str();
559 std::string escapedSourceFileName = sourceFileName;
561 if (!cmSystemTools::FileIsFullPath(sourceFileName.c_str()))
563 escapedSourceFileName = cmSystemTools::CollapseFullPath(
564 escapedSourceFileName.c_str(),
565 this->GetGlobalGenerator()->GetCMakeInstance()->
566 GetHomeOutputDirectory());
569 escapedSourceFileName =
570 this->LocalGenerator->ConvertToOutputFormat(
571 escapedSourceFileName.c_str(), cmLocalGenerator::SHELL);
573 compileObjectVars.Source = escapedSourceFileName.c_str();
574 compileObjectVars.Object = objectFileName.c_str();
575 compileObjectVars.ObjectDir = objectDir.c_str();
576 compileObjectVars.Flags = vars["FLAGS"].c_str();
577 compileObjectVars.Defines = vars["DEFINES"].c_str();
579 // Rule for compiling object file.
580 std::string compileCmdVar = "CMAKE_";
581 compileCmdVar += language;
582 compileCmdVar += "_COMPILE_OBJECT";
583 std::string compileCmd =
584 this->GetMakefile()->GetRequiredDefinition(compileCmdVar.c_str());
585 std::vector<std::string> compileCmds;
586 cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
588 for (std::vector<std::string>::iterator i = compileCmds.begin();
589 i != compileCmds.end(); ++i)
590 this->GetLocalGenerator()->ExpandRuleVariables(*i, compileObjectVars);
592 std::string cmdLine =
593 this->GetLocalGenerator()->BuildCommandLine(compileCmds);
595 this->GetGlobalGenerator()->AddCXXCompileCommand(cmdLine,
599 this->GetGlobalGenerator()->WriteBuild(this->GetBuildFileStream(),
608 if(const char* objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) {
609 std::vector<std::string> outputList;
610 cmSystemTools::ExpandListArgument(objectOutputs, outputList);
611 std::transform(outputList.begin(), outputList.end(), outputList.begin(),
613 this->GetGlobalGenerator()->WritePhonyBuild(this->GetBuildFileStream(),
614 "Additional output files.",
620 //----------------------------------------------------------------------------
622 cmNinjaTargetGenerator
623 ::AddModuleDefinitionFlag(std::string& flags)
625 if(this->ModuleDefinitionFile.empty())
630 // TODO: Create a per-language flag variable.
631 const char* defFileFlag =
632 this->Makefile->GetDefinition("CMAKE_LINK_DEF_FILE_FLAG");
638 // Append the flag and value. Use ConvertToLinkReference to help
639 // vs6's "cl -link" pass it to the linker.
640 std::string flag = defFileFlag;
641 flag += (this->LocalGenerator->ConvertToLinkReference(
642 this->ModuleDefinitionFile.c_str()));
643 this->LocalGenerator->AppendFlags(flags, flag.c_str());
647 cmNinjaTargetGenerator
648 ::EnsureDirectoryExists(const std::string& path) const
650 if (cmSystemTools::FileIsFullPath(path.c_str()))
652 cmSystemTools::MakeDirectory(path.c_str());
656 const std::string fullPath = std::string(this->GetGlobalGenerator()->
657 GetCMakeInstance()->GetHomeOutputDirectory())
659 cmSystemTools::MakeDirectory(fullPath.c_str());
664 cmNinjaTargetGenerator
665 ::EnsureParentDirectoryExists(const std::string& path) const
667 EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path.c_str()));
671 //----------------------------------------------------------------------------
673 cmNinjaTargetGenerator::MacOSXContentGeneratorType::operator()(
674 cmSourceFile& source, const char* pkgloc)
676 // Skip OS X content when not building a Framework or Bundle.
677 if(!this->Generator->GetTarget()->IsBundleOnApple())
683 this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc);
685 // Get the input file location.
686 std::string input = source.GetFullPath();
688 this->Generator->GetLocalGenerator()->ConvertToNinjaPath(input.c_str());
690 // Get the output file location.
691 std::string output = macdir;
693 output += cmSystemTools::GetFilenameName(input);
695 this->Generator->GetLocalGenerator()->ConvertToNinjaPath(output.c_str());
697 // Write a build statement to copy the content into the bundle.
698 this->Generator->GetGlobalGenerator()->WriteMacOSXContentBuild(input,
701 // Add as a dependency of all target so that it gets called.
702 this->Generator->GetGlobalGenerator()->AddDependencyToAll(output);