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 "cmNinjaNormalTargetGenerator.h"
14 #include "cmLocalNinjaGenerator.h"
15 #include "cmGlobalNinjaGenerator.h"
16 #include "cmSourceFile.h"
17 #include "cmGeneratedFileStream.h"
18 #include "cmMakefile.h"
19 #include "cmOSXBundleGenerator.h"
29 cmNinjaNormalTargetGenerator::
30 cmNinjaNormalTargetGenerator(cmTarget* target)
31 : cmNinjaTargetGenerator(target)
37 , TargetLinkLanguage(0)
39 this->TargetLinkLanguage = target->GetLinkerLanguage(this->GetConfigName());
40 if (target->GetType() == cmTarget::EXECUTABLE)
41 target->GetExecutableNames(this->TargetNameOut,
43 this->TargetNameImport,
45 GetLocalGenerator()->GetConfigName());
47 target->GetLibraryNames(this->TargetNameOut,
50 this->TargetNameImport,
52 GetLocalGenerator()->GetConfigName());
54 if(target->GetType() != cmTarget::OBJECT_LIBRARY)
56 // on Windows the output dir is already needed at compile time
57 // ensure the directory exists (OutDir test)
58 EnsureDirectoryExists(target->GetDirectory(this->GetConfigName()));
61 this->OSXBundleGenerator = new cmOSXBundleGenerator(target,
62 this->GetConfigName());
63 this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
66 cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator()
68 delete this->OSXBundleGenerator;
71 void cmNinjaNormalTargetGenerator::Generate()
73 if (!this->TargetLinkLanguage) {
74 cmSystemTools::Error("CMake can not determine linker language for "
76 this->GetTarget()->GetName());
80 // Write the rules for each language.
81 this->WriteLanguagesRules();
83 // Write the build statements
84 this->WriteObjectBuildStatements();
86 if(this->GetTarget()->GetType() == cmTarget::OBJECT_LIBRARY)
88 this->WriteObjectLibStatement();
92 this->WriteLinkRule(false); // write rule without rspfile support
93 this->WriteLinkRule(true); // write rule with rspfile support
94 this->WriteLinkStatement();
98 void cmNinjaNormalTargetGenerator::WriteLanguagesRules()
100 #ifdef NINJA_GEN_VERBOSE_FILES
101 cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream());
102 this->GetRulesFileStream()
103 << "# Rules for each languages for "
104 << cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
106 << this->GetTargetName()
110 std::set<cmStdString> languages;
111 this->GetTarget()->GetLanguages(languages);
112 for(std::set<cmStdString>::const_iterator l = languages.begin();
113 l != languages.end();
115 this->WriteLanguageRules(*l);
118 const char *cmNinjaNormalTargetGenerator::GetVisibleTypeName() const
120 switch (this->GetTarget()->GetType()) {
121 case cmTarget::STATIC_LIBRARY:
122 return "static library";
123 case cmTarget::SHARED_LIBRARY:
124 return "shared library";
125 case cmTarget::MODULE_LIBRARY:
126 if (this->GetTarget()->IsCFBundleOnApple())
127 return "CFBundle shared module";
129 return "shared module";
130 case cmTarget::EXECUTABLE:
138 cmNinjaNormalTargetGenerator
139 ::LanguageLinkerRule() const
141 return std::string(this->TargetLinkLanguage)
143 + cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
148 cmNinjaNormalTargetGenerator
149 ::WriteLinkRule(bool useResponseFile)
151 cmTarget::TargetType targetType = this->GetTarget()->GetType();
152 std::string ruleName = this->LanguageLinkerRule();
154 ruleName += "_RSP_FILE";
156 // Select whether to use a response file for objects.
158 std::string rspcontent;
160 if (!this->GetGlobalGenerator()->HasRule(ruleName)) {
161 cmLocalGenerator::RuleVariables vars;
162 vars.RuleLauncher = "RULE_LAUNCH_LINK";
163 vars.CMTarget = this->GetTarget();
164 vars.Language = this->TargetLinkLanguage;
166 std::string responseFlag;
167 if (!useResponseFile) {
168 vars.Objects = "$in";
169 vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
171 std::string cmakeVarLang = "CMAKE_";
172 cmakeVarLang += this->TargetLinkLanguage;
174 // build response file name
175 std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
176 const char * flag = GetMakefile()->GetDefinition(cmakeLinkVar.c_str());
182 rspfile = "$RSP_FILE";
183 responseFlag += rspfile;
185 // build response file content
186 std::string linkOptionVar = cmakeVarLang;
187 linkOptionVar += "_COMPILER_LINKER_OPTION_FLAG_";
188 linkOptionVar += cmTarget::GetTargetTypeName(targetType);
189 const std::string linkOption =
190 GetMakefile()->GetSafeDefinition(linkOptionVar.c_str());
191 rspcontent = "$in_newline "+linkOption+" $LINK_PATH $LINK_LIBRARIES";
192 vars.Objects = responseFlag.c_str();
193 vars.LinkLibraries = "";
196 vars.ObjectDir = "$OBJECT_DIR";
199 // Makefile generator expands <TARGET> to the plain target name
200 // with suffix. $out expands to a relative path. This difference
201 // could make trouble when switching to Ninja generator. Maybe
202 // using TARGET_NAME and RuleVariables::TargetName is a fix.
203 vars.Target = "$out";
205 vars.SONameFlag = "$SONAME_FLAG";
206 vars.TargetSOName = "$SONAME";
207 vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
208 vars.TargetPDB = "$TARGET_PDB";
210 // Setup the target version.
211 std::string targetVersionMajor;
212 std::string targetVersionMinor;
214 cmOStringStream majorStream;
215 cmOStringStream minorStream;
218 this->GetTarget()->GetTargetVersion(major, minor);
219 majorStream << major;
220 minorStream << minor;
221 targetVersionMajor = majorStream.str();
222 targetVersionMinor = minorStream.str();
224 vars.TargetVersionMajor = targetVersionMajor.c_str();
225 vars.TargetVersionMinor = targetVersionMinor.c_str();
227 vars.Flags = "$FLAGS";
228 vars.LinkFlags = "$LINK_FLAGS";
230 std::string langFlags;
231 if (targetType != cmTarget::EXECUTABLE) {
232 this->GetLocalGenerator()->AddLanguageFlags(langFlags,
233 this->TargetLinkLanguage,
234 this->GetConfigName());
235 langFlags += " $ARCH_FLAGS";
236 vars.LanguageCompileFlags = langFlags.c_str();
239 // Rule for linking library/executable.
240 std::vector<std::string> linkCmds = this->ComputeLinkCmd();
241 for(std::vector<std::string>::iterator i = linkCmds.begin();
245 this->GetLocalGenerator()->ExpandRuleVariables(*i, vars);
247 linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
248 linkCmds.push_back("$POST_BUILD");
249 std::string linkCmd =
250 this->GetLocalGenerator()->BuildCommandLine(linkCmds);
252 // Write the linker rule with response file if needed.
253 cmOStringStream comment;
254 comment << "Rule for linking " << this->TargetLinkLanguage << " "
255 << this->GetVisibleTypeName() << ".";
256 cmOStringStream description;
257 description << "Linking " << this->TargetLinkLanguage << " "
258 << this->GetVisibleTypeName() << " $out";
259 this->GetGlobalGenerator()->AddRule(ruleName,
268 if (this->TargetNameOut != this->TargetNameReal &&
269 !this->GetTarget()->IsFrameworkOnApple()) {
270 std::string cmakeCommand =
271 this->GetLocalGenerator()->ConvertToOutputFormat(
272 this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"),
273 cmLocalGenerator::SHELL);
274 if (targetType == cmTarget::EXECUTABLE)
275 this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_EXECUTABLE",
277 " -E cmake_symlink_executable"
278 " $in $out && $POST_BUILD",
279 "Creating executable symlink $out",
280 "Rule for creating executable symlink.");
282 this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_LIBRARY",
284 " -E cmake_symlink_library"
285 " $in $SONAME $out && $POST_BUILD",
286 "Creating library symlink $out",
287 "Rule for creating library symlink.");
291 std::vector<std::string>
292 cmNinjaNormalTargetGenerator
295 std::vector<std::string> linkCmds;
296 cmTarget::TargetType targetType = this->GetTarget()->GetType();
297 switch (targetType) {
298 case cmTarget::STATIC_LIBRARY: {
299 // Check if you have a non archive way to create the static library.
301 std::string linkCmdVar = "CMAKE_";
302 linkCmdVar += this->TargetLinkLanguage;
303 linkCmdVar += "_CREATE_STATIC_LIBRARY";
304 if (const char *linkCmd =
305 this->GetMakefile()->GetDefinition(linkCmdVar.c_str()))
307 cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
312 // We have archive link commands set. First, delete the existing archive.
313 std::string cmakeCommand =
314 this->GetLocalGenerator()->ConvertToOutputFormat(
315 this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"),
316 cmLocalGenerator::SHELL);
317 linkCmds.push_back(cmakeCommand + " -E remove $out");
319 // TODO: Use ARCHIVE_APPEND for archives over a certain size.
321 std::string linkCmdVar = "CMAKE_";
322 linkCmdVar += this->TargetLinkLanguage;
323 linkCmdVar += "_ARCHIVE_CREATE";
324 const char *linkCmd =
325 this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
326 cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
329 std::string linkCmdVar = "CMAKE_";
330 linkCmdVar += this->TargetLinkLanguage;
331 linkCmdVar += "_ARCHIVE_FINISH";
332 const char *linkCmd =
333 this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
334 cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
338 case cmTarget::SHARED_LIBRARY:
339 case cmTarget::MODULE_LIBRARY:
340 case cmTarget::EXECUTABLE: {
341 std::string linkCmdVar = "CMAKE_";
342 linkCmdVar += this->TargetLinkLanguage;
343 switch (targetType) {
344 case cmTarget::SHARED_LIBRARY:
345 linkCmdVar += "_CREATE_SHARED_LIBRARY";
347 case cmTarget::MODULE_LIBRARY:
348 linkCmdVar += "_CREATE_SHARED_MODULE";
350 case cmTarget::EXECUTABLE:
351 linkCmdVar += "_LINK_EXECUTABLE";
354 assert(0 && "Unexpected target type");
357 const char *linkCmd =
358 this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
359 cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
363 assert(0 && "Unexpected target type");
365 return std::vector<std::string>();
368 void cmNinjaNormalTargetGenerator::WriteLinkStatement()
370 cmTarget::TargetType targetType = this->GetTarget()->GetType();
372 std::string targetOutput = ConvertToNinjaPath(
373 this->GetTarget()->GetFullPath(this->GetConfigName()).c_str());
374 std::string targetOutputReal = ConvertToNinjaPath(
375 this->GetTarget()->GetFullPath(this->GetConfigName(),
377 /*realpath=*/true).c_str());
378 std::string targetOutputImplib = ConvertToNinjaPath(
379 this->GetTarget()->GetFullPath(this->GetConfigName(),
380 /*implib=*/true).c_str());
382 if (this->GetTarget()->IsAppBundleOnApple())
384 // Create the app bundle
385 std::string outpath =
386 this->GetTarget()->GetDirectory(this->GetConfigName());
387 this->OSXBundleGenerator->CreateAppBundle(this->TargetNameOut, outpath);
389 // Calculate the output path
390 targetOutput = outpath;
392 targetOutput += this->TargetNameOut;
393 targetOutput = this->ConvertToNinjaPath(targetOutput.c_str());
394 targetOutputReal = outpath;
395 targetOutputReal += "/";
396 targetOutputReal += this->TargetNameReal;
397 targetOutputReal = this->ConvertToNinjaPath(targetOutputReal.c_str());
399 else if (this->GetTarget()->IsFrameworkOnApple())
401 // Create the library framework.
402 std::string outpath =
403 this->GetTarget()->GetDirectory(this->GetConfigName());
404 this->OSXBundleGenerator->CreateFramework(this->TargetNameOut, outpath);
406 else if(this->GetTarget()->IsCFBundleOnApple())
408 // Create the core foundation bundle.
409 std::string outpath =
410 this->GetTarget()->GetDirectory(this->GetConfigName());
411 this->OSXBundleGenerator->CreateCFBundle(this->TargetNameOut, outpath);
415 cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
416 this->GetBuildFileStream()
417 << "# Link build statements for "
418 << cmTarget::GetTargetTypeName(targetType)
420 << this->GetTargetName()
423 cmNinjaDeps emptyDeps;
426 // Compute the comment.
427 cmOStringStream comment;
428 comment << "Link the " << this->GetVisibleTypeName() << " "
433 outputs.push_back(targetOutputReal);
435 // Compute specific libraries to link with.
436 cmNinjaDeps explicitDeps = this->GetObjects();
437 cmNinjaDeps implicitDeps = this->ComputeLinkDeps();
439 std::string frameworkPath;
440 std::string linkPath;
441 this->GetLocalGenerator()->GetTargetFlags(vars["LINK_LIBRARIES"],
446 this->GetGeneratorTarget());
448 this->AddModuleDefinitionFlag(vars["LINK_FLAGS"]);
449 vars["LINK_FLAGS"] = cmGlobalNinjaGenerator
450 ::EncodeLiteral(vars["LINK_FLAGS"]);
452 vars["LINK_PATH"] = frameworkPath + linkPath;
454 // Compute architecture specific link flags. Yes, these go into a different
455 // variable for executables, probably due to a mistake made when duplicating
456 // code between the Makefile executable and library generators.
457 std::string flags = (targetType == cmTarget::EXECUTABLE
459 : vars["ARCH_FLAGS"]);
460 this->GetLocalGenerator()->AddArchitectureFlags(flags,
461 this->GetGeneratorTarget(),
462 this->TargetLinkLanguage,
463 this->GetConfigName());
464 if (targetType == cmTarget::EXECUTABLE) {
465 vars["FLAGS"] = flags;
467 vars["ARCH_FLAGS"] = flags;
469 if (this->GetTarget()->HasSOName(this->GetConfigName())) {
470 vars["SONAME_FLAG"] =
471 this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage);
472 vars["SONAME"] = this->TargetNameSO;
473 if (targetType == cmTarget::SHARED_LIBRARY) {
474 std::string install_name_dir = this->GetTarget()
475 ->GetInstallNameDirForBuildTree(this->GetConfigName());
477 if (!install_name_dir.empty()) {
478 vars["INSTALLNAME_DIR"] =
479 this->GetLocalGenerator()->Convert(install_name_dir.c_str(),
480 cmLocalGenerator::NONE,
481 cmLocalGenerator::SHELL, false);
486 if (!this->TargetNameImport.empty()) {
487 const std::string impLibPath = this->GetLocalGenerator()
488 ->ConvertToOutputFormat(targetOutputImplib.c_str(),
489 cmLocalGenerator::SHELL);
490 vars["TARGET_IMPLIB"] = impLibPath;
491 EnsureParentDirectoryExists(impLibPath);
494 cmMakefile* mf = this->GetMakefile();
495 if (!this->SetMsvcTargetPdbVariable(vars))
497 // It is common to place debug symbols at a specific place,
498 // so we need a plain target name in the rule available.
502 this->GetTarget()->GetFullNameComponents(prefix, base, suffix);
503 std::string dbg_suffix = ".dbg";
504 // TODO: Where to document?
505 if (mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX"))
506 dbg_suffix = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX");
507 vars["TARGET_PDB"] = base + suffix + dbg_suffix;
510 if (mf->IsOn("CMAKE_COMPILER_IS_MINGW"))
512 const std::string objPath = GetTarget()->GetSupportDirectory();
513 vars["OBJECT_DIR"] = ConvertToNinjaPath(objPath.c_str());
514 EnsureDirectoryExists(objPath);
515 // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
516 std::string& linkLibraries = vars["LINK_LIBRARIES"];
517 std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
520 std::vector<cmCustomCommand> *cmdLists[3] = {
521 &this->GetTarget()->GetPreBuildCommands(),
522 &this->GetTarget()->GetPreLinkCommands(),
523 &this->GetTarget()->GetPostBuildCommands()
526 std::vector<std::string> preLinkCmdLines, postBuildCmdLines;
527 std::vector<std::string> *cmdLineLists[3] = {
533 for (unsigned i = 0; i != 3; ++i) {
534 for (std::vector<cmCustomCommand>::const_iterator
535 ci = cmdLists[i]->begin();
536 ci != cmdLists[i]->end(); ++ci) {
537 this->GetLocalGenerator()->AppendCustomCommandLines(&*ci,
542 // If we have any PRE_LINK commands, we need to go back to HOME_OUTPUT for
543 // the link commands.
544 if (!preLinkCmdLines.empty()) {
545 const std::string homeOutDir = this->GetLocalGenerator()
546 ->ConvertToOutputFormat(this->GetMakefile()->GetHomeOutputDirectory(),
547 cmLocalGenerator::SHELL);
548 preLinkCmdLines.push_back("cd " + homeOutDir);
552 this->GetLocalGenerator()->BuildCommandLine(preLinkCmdLines);
553 std::string postBuildCmdLine =
554 this->GetLocalGenerator()->BuildCommandLine(postBuildCmdLines);
556 cmNinjaVars symlinkVars;
557 if (targetOutput == targetOutputReal) {
558 vars["POST_BUILD"] = postBuildCmdLine;
560 vars["POST_BUILD"] = ":";
561 symlinkVars["POST_BUILD"] = postBuildCmdLine;
564 int linkRuleLength = this->GetGlobalGenerator()->
565 GetRuleCmdLength(this->LanguageLinkerRule());
567 int commandLineLengthLimit = 1;
568 const char* forceRspFile = "CMAKE_NINJA_FORCE_RESPONSE_FILE";
569 if (!this->GetMakefile()->IsDefinitionSet(forceRspFile) &&
570 cmSystemTools::GetEnv(forceRspFile) == 0) {
572 commandLineLengthLimit = 8000 - linkRuleLength;
573 #elif defined(__linux) || defined(__APPLE__)
574 // for instance ARG_MAX is 2096152 on Ubuntu or 262144 on Mac
575 commandLineLengthLimit = ((int)sysconf(_SC_ARG_MAX))-linkRuleLength-1000;
577 (void)linkRuleLength;
578 commandLineLengthLimit = -1;
582 //Get the global generator as we are going to be call WriteBuild numerous
583 //times in the following section
584 cmGlobalNinjaGenerator* globalGenerator = this->GetGlobalGenerator();
587 const std::string rspfile = std::string
588 (cmake::GetCMakeFilesDirectoryPostSlash()) +
589 this->GetTarget()->GetName() + ".rsp";
591 // Write the build statement for this target.
592 globalGenerator->WriteBuild(this->GetBuildFileStream(),
594 this->LanguageLinkerRule(),
601 commandLineLengthLimit);
603 if (targetOutput != targetOutputReal &&
604 !this->GetTarget()->IsFrameworkOnApple()) {
605 if (targetType == cmTarget::EXECUTABLE) {
606 globalGenerator->WriteBuild(this->GetBuildFileStream(),
607 "Create executable symlink " + targetOutput,
608 "CMAKE_SYMLINK_EXECUTABLE",
609 cmNinjaDeps(1, targetOutput),
610 cmNinjaDeps(1, targetOutputReal),
615 cmNinjaDeps symlinks;
616 const std::string soName = this->GetTargetFilePath(this->TargetNameSO);
617 // If one link has to be created.
618 if (targetOutputReal == soName || targetOutput == soName) {
619 symlinkVars["SONAME"] = soName;
621 symlinkVars["SONAME"] = "";
622 symlinks.push_back(soName);
624 symlinks.push_back(targetOutput);
625 globalGenerator->WriteBuild(this->GetBuildFileStream(),
626 "Create library symlink " + targetOutput,
627 "CMAKE_SYMLINK_LIBRARY",
629 cmNinjaDeps(1, targetOutputReal),
636 if (!this->TargetNameImport.empty()) {
637 // Since using multiple outputs would mess up the $out variable, use an
638 // alias for the import library.
639 globalGenerator->WritePhonyBuild(this->GetBuildFileStream(),
640 "Alias for import library.",
641 cmNinjaDeps(1, targetOutputImplib),
642 cmNinjaDeps(1, targetOutputReal));
645 // Add aliases for the file name and the target name.
646 globalGenerator->AddTargetAlias(this->TargetNameOut,
648 globalGenerator->AddTargetAlias(this->GetTargetName(),
652 //----------------------------------------------------------------------------
653 void cmNinjaNormalTargetGenerator::WriteObjectLibStatement()
655 // Write a phony output that depends on all object files.
657 this->GetLocalGenerator()->AppendTargetOutputs(this->GetTarget(), outputs);
658 cmNinjaDeps depends = this->GetObjects();
659 this->GetGlobalGenerator()->WritePhonyBuild(this->GetBuildFileStream(),
661 + this->GetTargetName(),
665 // Add aliases for the target name.
666 this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),