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,
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,
152 // Add include directory flags.
154 std::vector<std::string> includes;
155 this->LocalGenerator->GetIncludeDirectories(includes, this->Target,
157 std::string includeFlags =
158 this->LocalGenerator->GetIncludeFlags(includes, language.c_str(),
159 language == "RC" ? true : false); // full include paths for RC
160 // needed by cmcldeps
161 if(cmGlobalNinjaGenerator::IsMinGW())
162 cmSystemTools::ReplaceString(includeFlags, "\\", "/");
164 this->LocalGenerator->AppendFlags(flags, includeFlags.c_str());
167 // Append old-style preprocessor definition flags.
168 this->LocalGenerator->AppendFlags(flags, this->Makefile->GetDefineFlags());
170 // Add target-specific and source-specific flags.
171 this->LocalGenerator->AppendFlags(flags,
172 this->Target->GetProperty("COMPILE_FLAGS"));
173 this->LocalGenerator->AppendFlags(flags,
174 source->GetProperty("COMPILE_FLAGS"));
176 // TODO: Handle Apple frameworks.
181 // TODO: Refactor with
182 // void cmMakefileTargetGenerator::WriteTargetLanguageFlags().
184 cmNinjaTargetGenerator::
185 ComputeDefines(cmSourceFile *source, const std::string& language)
189 // Add the export symbol definition for shared library objects.
190 if(const char* exportMacro = this->Target->GetExportMacro())
192 this->LocalGenerator->AppendDefines(defines, exportMacro,
196 // Add preprocessor definitions for this target and configuration.
197 this->LocalGenerator->AppendDefines
199 this->Makefile->GetProperty("COMPILE_DEFINITIONS"),
201 this->LocalGenerator->AppendDefines
203 this->Target->GetProperty("COMPILE_DEFINITIONS"),
205 this->LocalGenerator->AppendDefines
207 source->GetProperty("COMPILE_DEFINITIONS"),
210 std::string defPropName = "COMPILE_DEFINITIONS_";
211 defPropName += cmSystemTools::UpperCase(this->GetConfigName());
212 this->LocalGenerator->AppendDefines
214 this->Makefile->GetProperty(defPropName.c_str()),
216 this->LocalGenerator->AppendDefines
218 this->Target->GetProperty(defPropName.c_str()),
220 this->LocalGenerator->AppendDefines
222 source->GetProperty(defPropName.c_str()),
229 cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const
231 // Static libraries never depend on other targets for linking.
232 if (this->Target->GetType() == cmTarget::STATIC_LIBRARY ||
233 this->Target->GetType() == cmTarget::OBJECT_LIBRARY)
234 return cmNinjaDeps();
236 cmComputeLinkInformation* cli =
237 this->Target->GetLinkInformation(this->GetConfigName());
239 return cmNinjaDeps();
241 const std::vector<std::string> &deps = cli->GetDepends();
242 cmNinjaDeps result(deps.size());
243 std::transform(deps.begin(), deps.end(), result.begin(), MapToNinjaPath());
245 // Add a dependency on the link definitions file, if any.
246 if(!this->ModuleDefinitionFile.empty())
248 result.push_back(this->ModuleDefinitionFile);
255 cmNinjaTargetGenerator
256 ::GetSourceFilePath(cmSourceFile* source) const
258 return ConvertToNinjaPath(source->GetFullPath().c_str());
262 cmNinjaTargetGenerator
263 ::GetObjectFilePath(cmSourceFile* source) const
265 std::string path = this->LocalGenerator->GetHomeRelativeOutputPath();
268 std::string const& objectName = this->GeneratorTarget->Objects[source];
269 path += this->LocalGenerator->GetTargetDirectory(*this->Target);
275 std::string cmNinjaTargetGenerator::GetTargetOutputDir() const
277 std::string dir = this->Target->GetDirectory(this->GetConfigName());
278 return ConvertToNinjaPath(dir.c_str());
282 cmNinjaTargetGenerator
283 ::GetTargetFilePath(const std::string& name) const
285 std::string path = this->GetTargetOutputDir();
286 if (path.empty() || path == ".")
293 std::string cmNinjaTargetGenerator::GetTargetName() const
295 return this->Target->GetName();
298 std::string cmNinjaTargetGenerator::GetTargetPDB() const
300 std::string targetFullPathPDB;
301 if(this->Target->GetType() == cmTarget::EXECUTABLE ||
302 this->Target->GetType() == cmTarget::STATIC_LIBRARY ||
303 this->Target->GetType() == cmTarget::SHARED_LIBRARY ||
304 this->Target->GetType() == cmTarget::MODULE_LIBRARY)
306 targetFullPathPDB = this->Target->GetDirectory(this->GetConfigName());
307 targetFullPathPDB += "/";
308 targetFullPathPDB += this->Target->GetPDBName(this->GetConfigName());
311 return targetFullPathPDB.c_str();
316 cmNinjaTargetGenerator
317 ::WriteLanguageRules(const std::string& language)
319 #ifdef NINJA_GEN_VERBOSE_FILES
320 this->GetRulesFileStream()
321 << "# Rules for language " << language << "\n\n";
323 this->WriteCompileRule(language);
327 cmNinjaTargetGenerator
328 ::WriteCompileRule(const std::string& language)
330 cmLocalGenerator::RuleVariables vars;
331 vars.RuleLauncher = "RULE_LAUNCH_COMPILE";
332 vars.CMTarget = this->GetTarget();
333 std::string lang = language;
334 vars.Language = lang.c_str();
336 vars.Object = "$out";
337 std::string flags = "$FLAGS";
338 vars.Defines = "$DEFINES";
339 vars.TargetPDB = "$TARGET_PDB";
342 cmMakefile* mf = this->GetMakefile();
344 bool useClDeps = false;
345 std::string clDepsBinary;
346 std::string clShowPrefix;
347 if (lang == "C" || lang == "CXX" || lang == "RC")
349 const char* depsPtr = mf->GetDefinition("CMAKE_CMCLDEPS_EXECUTABLE");
350 const char* showPtr = mf->GetDefinition("CMAKE_CL_SHOWINCLUDE_PREFIX");
351 if (depsPtr && showPtr)
353 // don't wrap for try_compile,
354 // TODO but why doesn't it work with cmcldeps?
355 const std::string projectName = mf->GetProjectName() ?
356 mf->GetProjectName() : "";
357 if (projectName != "CMAKE_TRY_COMPILE")
360 std::string qu = "\"";
361 clDepsBinary = qu + depsPtr + qu;
362 clShowPrefix = qu + showPtr + qu;
363 vars.DependencyFile = "$DEP_FILE";
370 std::string depfileFlagsName = "CMAKE_DEPFILE_FLAGS_" + language;
371 const char *depfileFlags = mf->GetDefinition(depfileFlagsName.c_str());
372 if (depfileFlags || useClDeps) {
373 std::string depFlagsStr = depfileFlags ? depfileFlags : "";
374 depfile = "$DEP_FILE";
375 cmSystemTools::ReplaceString(depFlagsStr, "<DEPFILE>", "\"$DEP_FILE\"");
376 cmSystemTools::ReplaceString(depFlagsStr, "<OBJECT>", "$out");
377 cmSystemTools::ReplaceString(depFlagsStr, "<CMAKE_C_COMPILER>",
378 mf->GetDefinition("CMAKE_C_COMPILER"));
379 flags += " " + depFlagsStr;
381 vars.Flags = flags.c_str();
384 // Rule for compiling object file.
385 std::string compileCmdVar = "CMAKE_";
386 compileCmdVar += language;
387 compileCmdVar += "_COMPILE_OBJECT";
388 std::string compileCmd = mf->GetRequiredDefinition(compileCmdVar.c_str());
389 std::vector<std::string> compileCmds;
390 cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
392 for (std::vector<std::string>::iterator i = compileCmds.begin();
393 i != compileCmds.end(); ++i)
394 this->GetLocalGenerator()->ExpandRuleVariables(*i, vars);
396 std::string cmdLine =
397 this->GetLocalGenerator()->BuildCommandLine(compileCmds);
401 std::string cl = mf->GetDefinition("CMAKE_C_COMPILER");
402 cl = "\"" + cl + "\" ";
403 cmdLine = clDepsBinary + " " + lang + " $in \"$DEP_FILE\" $out "
404 + clShowPrefix + " " + cl + cmdLine;
407 // Write the rule for compiling file of the given language.
408 cmOStringStream comment;
409 comment << "Rule for compiling " << language << " files.";
410 cmOStringStream description;
411 description << "Building " << language << " object $out";
412 this->GetGlobalGenerator()->AddRule(this->LanguageCompilerRule(language),
420 cmNinjaTargetGenerator
421 ::WriteObjectBuildStatements()
424 cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
425 this->GetBuildFileStream()
426 << "# Object build statements for "
427 << cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
429 << this->GetTargetName()
432 for(std::vector<cmSourceFile*>::const_iterator
433 si = this->GeneratorTarget->CustomCommands.begin();
434 si != this->GeneratorTarget->CustomCommands.end(); ++si)
436 cmCustomCommand const* cc = (*si)->GetCustomCommand();
437 this->GetLocalGenerator()->AddCustomCommandTarget(cc, this->GetTarget());
439 this->OSXBundleGenerator->GenerateMacOSXContentStatements(
440 this->GeneratorTarget->HeaderSources,
441 this->MacOSXContentGenerator);
442 this->OSXBundleGenerator->GenerateMacOSXContentStatements(
443 this->GeneratorTarget->ExtraSources,
444 this->MacOSXContentGenerator);
445 for(std::vector<cmSourceFile*>::const_iterator
446 si = this->GeneratorTarget->ExternalObjects.begin();
447 si != this->GeneratorTarget->ExternalObjects.end(); ++si)
449 this->Objects.push_back(this->GetSourceFilePath(*si));
451 for(std::vector<cmSourceFile*>::const_iterator
452 si = this->GeneratorTarget->ObjectSources.begin();
453 si != this->GeneratorTarget->ObjectSources.end(); ++si)
455 this->WriteObjectBuildStatement(*si);
457 if(!this->GeneratorTarget->ModuleDefinitionFile.empty())
459 this->ModuleDefinitionFile = this->ConvertToNinjaPath(
460 this->GeneratorTarget->ModuleDefinitionFile.c_str());
464 // Add object library contents as external objects.
465 std::vector<std::string> objs;
466 this->GeneratorTarget->UseObjectLibraries(objs);
467 for(std::vector<std::string>::iterator oi = objs.begin();
468 oi != objs.end(); ++oi)
470 this->Objects.push_back(ConvertToNinjaPath(oi->c_str()));
474 this->GetBuildFileStream() << "\n";
478 cmNinjaTargetGenerator
479 ::WriteObjectBuildStatement(cmSourceFile* source)
481 cmNinjaDeps emptyDeps;
484 const std::string language = source->GetLanguage();
485 std::string rule = this->LanguageCompilerRule(language);
488 std::string objectFileName = this->GetObjectFilePath(source);
489 outputs.push_back(objectFileName);
490 // Add this object to the list of object files.
491 this->Objects.push_back(objectFileName);
493 cmNinjaDeps explicitDeps;
494 std::string sourceFileName;
495 if (language == "RC")
496 sourceFileName = source->GetFullPath();
498 sourceFileName = this->GetSourceFilePath(source);
499 explicitDeps.push_back(sourceFileName);
501 // Ensure that the target dependencies are built before any source file in
502 // the target, using order-only dependencies.
503 cmNinjaDeps orderOnlyDeps;
504 this->GetLocalGenerator()->AppendTargetDepends(this->Target, orderOnlyDeps);
506 if(const char* objectDeps = source->GetProperty("OBJECT_DEPENDS")) {
507 std::vector<std::string> depList;
508 cmSystemTools::ExpandListArgument(objectDeps, depList);
509 std::transform(depList.begin(), depList.end(),
510 std::back_inserter(orderOnlyDeps), MapToNinjaPath());
513 // Add order-only dependencies on custom command outputs.
514 for(std::vector<cmSourceFile*>::const_iterator
515 si = this->GeneratorTarget->CustomCommands.begin();
516 si != this->GeneratorTarget->CustomCommands.end(); ++si)
518 cmCustomCommand const* cc = (*si)->GetCustomCommand();
519 const std::vector<std::string>& ccoutputs = cc->GetOutputs();
520 std::transform(ccoutputs.begin(), ccoutputs.end(),
521 std::back_inserter(orderOnlyDeps), MapToNinjaPath());
524 // If the source file is GENERATED and does not have a custom command
525 // (either attached to this source file or another one), assume that one of
526 // the target dependencies, OBJECT_DEPENDS or header file custom commands
527 // will rebuild the file.
528 if (source->GetPropertyAsBool("GENERATED") && !source->GetCustomCommand() &&
529 !this->GetGlobalGenerator()->HasCustomCommandOutput(sourceFileName)) {
530 this->GetGlobalGenerator()->AddAssumedSourceDependencies(sourceFileName,
535 vars["FLAGS"] = this->ComputeFlagsForObject(source, language);
536 vars["DEFINES"] = this->ComputeDefines(source, language);
537 vars["DEP_FILE"] = objectFileName + ".d";;
538 EnsureParentDirectoryExists(objectFileName);
540 // TODO move to GetTargetPDB
541 cmMakefile* mf = this->GetMakefile();
542 if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") ||
543 mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID"))
545 vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
546 ConvertToNinjaPath(GetTargetPDB().c_str()).c_str(),
547 cmLocalGenerator::SHELL);
550 if(this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS"))
552 cmLocalGenerator::RuleVariables compileObjectVars;
553 std::string lang = language;
554 compileObjectVars.Language = lang.c_str();
556 std::string escapedSourceFileName = sourceFileName;
558 if (!cmSystemTools::FileIsFullPath(sourceFileName.c_str()))
560 escapedSourceFileName = cmSystemTools::CollapseFullPath(
561 escapedSourceFileName.c_str(),
562 this->GetGlobalGenerator()->GetCMakeInstance()->
563 GetHomeOutputDirectory());
566 escapedSourceFileName =
567 this->LocalGenerator->ConvertToOutputFormat(
568 escapedSourceFileName.c_str(), cmLocalGenerator::SHELL);
570 compileObjectVars.Source = escapedSourceFileName.c_str();
571 compileObjectVars.Object = objectFileName.c_str();
572 compileObjectVars.Flags = vars["FLAGS"].c_str();
573 compileObjectVars.Defines = vars["DEFINES"].c_str();
575 // Rule for compiling object file.
576 std::string compileCmdVar = "CMAKE_";
577 compileCmdVar += language;
578 compileCmdVar += "_COMPILE_OBJECT";
579 std::string compileCmd =
580 this->GetMakefile()->GetRequiredDefinition(compileCmdVar.c_str());
581 std::vector<std::string> compileCmds;
582 cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
584 for (std::vector<std::string>::iterator i = compileCmds.begin();
585 i != compileCmds.end(); ++i)
586 this->GetLocalGenerator()->ExpandRuleVariables(*i, compileObjectVars);
588 std::string cmdLine =
589 this->GetLocalGenerator()->BuildCommandLine(compileCmds);
591 this->GetGlobalGenerator()->AddCXXCompileCommand(cmdLine,
595 cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
604 if(const char* objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) {
605 std::vector<std::string> outputList;
606 cmSystemTools::ExpandListArgument(objectOutputs, outputList);
607 std::transform(outputList.begin(), outputList.end(), outputList.begin(),
609 cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
610 "Additional output files.",
616 //----------------------------------------------------------------------------
618 cmNinjaTargetGenerator
619 ::AddModuleDefinitionFlag(std::string& flags)
621 if(this->ModuleDefinitionFile.empty())
626 // TODO: Create a per-language flag variable.
627 const char* defFileFlag =
628 this->Makefile->GetDefinition("CMAKE_LINK_DEF_FILE_FLAG");
634 // Append the flag and value. Use ConvertToLinkReference to help
635 // vs6's "cl -link" pass it to the linker.
636 std::string flag = defFileFlag;
637 flag += (this->LocalGenerator->ConvertToLinkReference(
638 this->ModuleDefinitionFile.c_str()));
639 this->LocalGenerator->AppendFlags(flags, flag.c_str());
643 cmNinjaTargetGenerator
644 ::EnsureDirectoryExists(const std::string& dir)
646 cmSystemTools::MakeDirectory(dir.c_str());
650 cmNinjaTargetGenerator
651 ::EnsureParentDirectoryExists(const std::string& path)
653 EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path.c_str()));
657 //----------------------------------------------------------------------------
659 cmNinjaTargetGenerator::MacOSXContentGeneratorType::operator()(
660 cmSourceFile& source, const char* pkgloc)
662 // Skip OS X content when not building a Framework or Bundle.
663 if(this->Generator->OSXBundleGenerator->GetMacContentDirectory().empty())
669 this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc);
671 // Get the input file location.
672 std::string input = source.GetFullPath();
674 this->Generator->GetLocalGenerator()->ConvertToNinjaPath(input.c_str());
676 // Get the output file location.
677 std::string output = macdir;
679 output += cmSystemTools::GetFilenameName(input);
681 this->Generator->GetLocalGenerator()->ConvertToNinjaPath(output.c_str());
683 // Write a build statement to copy the content into the bundle.
684 this->Generator->GetGlobalGenerator()->WriteMacOSXContentBuild(input,
687 // Add as a dependency of all target so that it gets called.
688 this->Generator->GetGlobalGenerator()->AddDependencyToAll(output);