/*============================================================================ CMake - Cross Platform Makefile Generator Copyright 2011 Peter Collingbourne Copyright 2011 Nicolas Despres 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. ============================================================================*/ #include "cmNinjaNormalTargetGenerator.h" #include "cmAlgorithms.h" #include "cmCustomCommandGenerator.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGlobalNinjaGenerator.h" #include "cmLocalNinjaGenerator.h" #include "cmMakefile.h" #include "cmOSXBundleGenerator.h" #include "cmSourceFile.h" #include #include #include #ifndef _WIN32 #include #endif cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator( cmGeneratorTarget* target) : cmNinjaTargetGenerator(target) , TargetNameOut() , TargetNameSO() , TargetNameReal() , TargetNameImport() , TargetNamePDB() , TargetLinkLanguage("") { this->TargetLinkLanguage = target->GetLinkerLanguage(this->GetConfigName()); if (target->GetType() == cmState::EXECUTABLE) this->GetGeneratorTarget()->GetExecutableNames( this->TargetNameOut, this->TargetNameReal, this->TargetNameImport, this->TargetNamePDB, GetLocalGenerator()->GetConfigName()); else this->GetGeneratorTarget()->GetLibraryNames( this->TargetNameOut, this->TargetNameSO, this->TargetNameReal, this->TargetNameImport, this->TargetNamePDB, GetLocalGenerator()->GetConfigName()); if (target->GetType() != cmState::OBJECT_LIBRARY) { // on Windows the output dir is already needed at compile time // ensure the directory exists (OutDir test) EnsureDirectoryExists(target->GetDirectory(this->GetConfigName())); } this->OSXBundleGenerator = new cmOSXBundleGenerator(target, this->GetConfigName()); this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders); } cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() { delete this->OSXBundleGenerator; } void cmNinjaNormalTargetGenerator::Generate() { if (this->TargetLinkLanguage.empty()) { cmSystemTools::Error("CMake can not determine linker language for " "target: ", this->GetGeneratorTarget()->GetName().c_str()); return; } // Write the rules for each language. this->WriteLanguagesRules(); // Write the build statements this->WriteObjectBuildStatements(); if (this->GetGeneratorTarget()->GetType() == cmState::OBJECT_LIBRARY) { this->WriteObjectLibStatement(); } else { this->WriteLinkStatement(); } } void cmNinjaNormalTargetGenerator::WriteLanguagesRules() { #ifdef NINJA_GEN_VERBOSE_FILES cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream()); this->GetRulesFileStream() << "# Rules for each languages for " << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()) << " target " << this->GetTargetName() << "\n\n"; #endif // Write rules for languages compiled in this target. std::set languages; std::vector sourceFiles; this->GetGeneratorTarget()->GetSourceFiles( sourceFiles, this->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE")); for (std::vector::const_iterator i = sourceFiles.begin(); i != sourceFiles.end(); ++i) { const std::string& lang = (*i)->GetLanguage(); if (!lang.empty()) { languages.insert(lang); } } for (std::set::const_iterator l = languages.begin(); l != languages.end(); ++l) { this->WriteLanguageRules(*l); } } const char* cmNinjaNormalTargetGenerator::GetVisibleTypeName() const { switch (this->GetGeneratorTarget()->GetType()) { case cmState::STATIC_LIBRARY: return "static library"; case cmState::SHARED_LIBRARY: return "shared library"; case cmState::MODULE_LIBRARY: if (this->GetGeneratorTarget()->IsCFBundleOnApple()) return "CFBundle shared module"; else return "shared module"; case cmState::EXECUTABLE: return "executable"; default: return 0; } } std::string cmNinjaNormalTargetGenerator::LanguageLinkerRule() const { return this->TargetLinkLanguage + "_" + cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()) + "_LINKER__" + cmGlobalNinjaGenerator::EncodeRuleName( this->GetGeneratorTarget()->GetName()); } void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile) { cmState::TargetType targetType = this->GetGeneratorTarget()->GetType(); std::string ruleName = this->LanguageLinkerRule(); // Select whether to use a response file for objects. std::string rspfile; std::string rspcontent; if (!this->GetGlobalGenerator()->HasRule(ruleName)) { cmLocalGenerator::RuleVariables vars; vars.RuleLauncher = "RULE_LAUNCH_LINK"; vars.CMTarget = this->GetGeneratorTarget(); vars.Language = this->TargetLinkLanguage.c_str(); std::string responseFlag; if (!useResponseFile) { vars.Objects = "$in"; vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES"; } else { std::string cmakeVarLang = "CMAKE_"; cmakeVarLang += this->TargetLinkLanguage; // build response file name std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar); if (flag) { responseFlag = flag; } else { responseFlag = "@"; } rspfile = "$RSP_FILE"; responseFlag += rspfile; // build response file content if (this->GetGlobalGenerator()->IsGCCOnWindows()) { rspcontent = "$in"; } else { rspcontent = "$in_newline"; } rspcontent += " $LINK_PATH $LINK_LIBRARIES"; vars.Objects = responseFlag.c_str(); vars.LinkLibraries = ""; } vars.ObjectDir = "$OBJECT_DIR"; vars.Target = "$TARGET_FILE"; vars.SONameFlag = "$SONAME_FLAG"; vars.TargetSOName = "$SONAME"; vars.TargetInstallNameDir = "$INSTALLNAME_DIR"; vars.TargetPDB = "$TARGET_PDB"; // Setup the target version. std::string targetVersionMajor; std::string targetVersionMinor; { std::ostringstream majorStream; std::ostringstream minorStream; int major; int minor; this->GetGeneratorTarget()->GetTargetVersion(major, minor); majorStream << major; minorStream << minor; targetVersionMajor = majorStream.str(); targetVersionMinor = minorStream.str(); } vars.TargetVersionMajor = targetVersionMajor.c_str(); vars.TargetVersionMinor = targetVersionMinor.c_str(); vars.Flags = "$FLAGS"; vars.LinkFlags = "$LINK_FLAGS"; vars.Manifests = "$MANIFESTS"; std::string langFlags; if (targetType != cmState::EXECUTABLE) { langFlags += "$LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS"; vars.LanguageCompileFlags = langFlags.c_str(); } // Rule for linking library/executable. std::vector linkCmds = this->ComputeLinkCmd(); for (std::vector::iterator i = linkCmds.begin(); i != linkCmds.end(); ++i) { this->GetLocalGenerator()->ExpandRuleVariables(*i, vars); } linkCmds.insert(linkCmds.begin(), "$PRE_LINK"); linkCmds.push_back("$POST_BUILD"); std::string linkCmd = this->GetLocalGenerator()->BuildCommandLine(linkCmds); // Write the linker rule with response file if needed. std::ostringstream comment; comment << "Rule for linking " << this->TargetLinkLanguage << " " << this->GetVisibleTypeName() << "."; std::ostringstream description; description << "Linking " << this->TargetLinkLanguage << " " << this->GetVisibleTypeName() << " $TARGET_FILE"; this->GetGlobalGenerator()->AddRule(ruleName, linkCmd, description.str(), comment.str(), /*depfile*/ "", /*deptype*/ "", rspfile, rspcontent, /*restat*/ "$RESTAT", /*generator*/ false); } if (this->TargetNameOut != this->TargetNameReal && !this->GetGeneratorTarget()->IsFrameworkOnApple()) { std::string cmakeCommand = this->GetLocalGenerator()->ConvertToOutputFormat( cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); if (targetType == cmState::EXECUTABLE) this->GetGlobalGenerator()->AddRule( "CMAKE_SYMLINK_EXECUTABLE", cmakeCommand + " -E cmake_symlink_executable" " $in $out && $POST_BUILD", "Creating executable symlink $out", "Rule for creating " "executable symlink.", /*depfile*/ "", /*deptype*/ "", /*rspfile*/ "", /*rspcontent*/ "", /*restat*/ "", /*generator*/ false); else this->GetGlobalGenerator()->AddRule( "CMAKE_SYMLINK_LIBRARY", cmakeCommand + " -E cmake_symlink_library" " $in $SONAME $out && $POST_BUILD", "Creating library symlink $out", "Rule for creating " "library symlink.", /*depfile*/ "", /*deptype*/ "", /*rspfile*/ "", /*rspcontent*/ "", /*restat*/ "", /*generator*/ false); } } std::vector cmNinjaNormalTargetGenerator::ComputeLinkCmd() { std::vector linkCmds; cmMakefile* mf = this->GetMakefile(); { std::string linkCmdVar = this->GetGeneratorTarget()->GetCreateRuleVariable( this->TargetLinkLanguage, this->GetConfigName()); const char* linkCmd = mf->GetDefinition(linkCmdVar); if (linkCmd) { cmSystemTools::ExpandListArgument(linkCmd, linkCmds); return linkCmds; } } switch (this->GetGeneratorTarget()->GetType()) { case cmState::STATIC_LIBRARY: { // We have archive link commands set. First, delete the existing archive. { std::string cmakeCommand = this->GetLocalGenerator()->ConvertToOutputFormat( cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); linkCmds.push_back(cmakeCommand + " -E remove $TARGET_FILE"); } // TODO: Use ARCHIVE_APPEND for archives over a certain size. { std::string linkCmdVar = "CMAKE_"; linkCmdVar += this->TargetLinkLanguage; linkCmdVar += "_ARCHIVE_CREATE"; const char* linkCmd = mf->GetRequiredDefinition(linkCmdVar); cmSystemTools::ExpandListArgument(linkCmd, linkCmds); } { std::string linkCmdVar = "CMAKE_"; linkCmdVar += this->TargetLinkLanguage; linkCmdVar += "_ARCHIVE_FINISH"; const char* linkCmd = mf->GetRequiredDefinition(linkCmdVar); cmSystemTools::ExpandListArgument(linkCmd, linkCmds); } return linkCmds; } case cmState::SHARED_LIBRARY: case cmState::MODULE_LIBRARY: case cmState::EXECUTABLE: break; default: assert(0 && "Unexpected target type"); } return std::vector(); } static int calculateCommandLineLengthLimit(int linkRuleLength) { static int const limits[] = { #ifdef _WIN32 8000, #endif #if defined(__APPLE__) || defined(__HAIKU__) || defined(__linux) // for instance ARG_MAX is 2096152 on Ubuntu or 262144 on Mac ((int)sysconf(_SC_ARG_MAX)) - 1000, #endif #if defined(__linux) // #define MAX_ARG_STRLEN (PAGE_SIZE * 32) in Linux's binfmts.h ((int)sysconf(_SC_PAGESIZE) * 32) - 1000, #endif std::numeric_limits::max() }; size_t const arrSz = cmArraySize(limits); int const sz = *std::min_element(limits, limits + arrSz); if (sz == std::numeric_limits::max()) { return -1; } return sz - linkRuleLength; } void cmNinjaNormalTargetGenerator::WriteLinkStatement() { cmGeneratorTarget& gt = *this->GetGeneratorTarget(); const std::string cfgName = this->GetConfigName(); std::string targetOutput = ConvertToNinjaPath(gt.GetFullPath(cfgName)); std::string targetOutputReal = ConvertToNinjaPath(gt.GetFullPath(cfgName, /*implib=*/false, /*realpath=*/true)); std::string targetOutputImplib = ConvertToNinjaPath(gt.GetFullPath(cfgName, /*implib=*/true)); if (gt.IsAppBundleOnApple()) { // Create the app bundle std::string outpath = gt.GetDirectory(cfgName); this->OSXBundleGenerator->CreateAppBundle(this->TargetNameOut, outpath); // Calculate the output path targetOutput = outpath; targetOutput += "/"; targetOutput += this->TargetNameOut; targetOutput = this->ConvertToNinjaPath(targetOutput); targetOutputReal = outpath; targetOutputReal += "/"; targetOutputReal += this->TargetNameReal; targetOutputReal = this->ConvertToNinjaPath(targetOutputReal); } else if (gt.IsFrameworkOnApple()) { // Create the library framework. this->OSXBundleGenerator->CreateFramework(this->TargetNameOut, gt.GetDirectory(cfgName)); } else if (gt.IsCFBundleOnApple()) { // Create the core foundation bundle. this->OSXBundleGenerator->CreateCFBundle(this->TargetNameOut, gt.GetDirectory(cfgName)); } // Write comments. cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream()); const cmState::TargetType targetType = gt.GetType(); this->GetBuildFileStream() << "# Link build statements for " << cmState::GetTargetTypeName(targetType) << " target " << this->GetTargetName() << "\n\n"; cmNinjaDeps emptyDeps; cmNinjaVars vars; // Compute the comment. std::ostringstream comment; comment << "Link the " << this->GetVisibleTypeName() << " " << targetOutputReal; // Compute outputs. cmNinjaDeps outputs; outputs.push_back(targetOutputReal); // Compute specific libraries to link with. cmNinjaDeps explicitDeps = this->GetObjects(); cmNinjaDeps implicitDeps = this->ComputeLinkDeps(); cmMakefile* mf = this->GetMakefile(); std::string frameworkPath; std::string linkPath; cmGeneratorTarget& genTarget = *this->GetGeneratorTarget(); std::string createRule = genTarget.GetCreateRuleVariable( this->TargetLinkLanguage, this->GetConfigName()); bool useWatcomQuote = mf->IsOn(createRule + "_USE_WATCOM_QUOTE"); cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator(); vars["TARGET_FILE"] = localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL); localGen.GetTargetFlags(vars["LINK_LIBRARIES"], vars["FLAGS"], vars["LINK_FLAGS"], frameworkPath, linkPath, &genTarget, useWatcomQuote); if (this->GetMakefile()->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS") && gt.GetType() == cmState::SHARED_LIBRARY) { if (gt.GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS")) { std::string name_of_def_file = gt.GetSupportDirectory(); name_of_def_file += "/" + gt.GetName(); name_of_def_file += ".def "; vars["LINK_FLAGS"] += " /DEF:"; vars["LINK_FLAGS"] += this->GetLocalGenerator()->ConvertToOutputFormat( name_of_def_file.c_str(), cmOutputConverter::SHELL); } } // Add OS X version flags, if any. if (this->GeneratorTarget->GetType() == cmState::SHARED_LIBRARY || this->GeneratorTarget->GetType() == cmState::MODULE_LIBRARY) { this->AppendOSXVerFlag(vars["LINK_FLAGS"], this->TargetLinkLanguage, "COMPATIBILITY", true); this->AppendOSXVerFlag(vars["LINK_FLAGS"], this->TargetLinkLanguage, "CURRENT", false); } this->addPoolNinjaVariable("JOB_POOL_LINK", >, vars); this->AddModuleDefinitionFlag(vars["LINK_FLAGS"]); vars["LINK_FLAGS"] = cmGlobalNinjaGenerator::EncodeLiteral(vars["LINK_FLAGS"]); vars["MANIFESTS"] = this->GetManifests(); vars["LINK_PATH"] = frameworkPath + linkPath; // Compute architecture specific link flags. Yes, these go into a different // variable for executables, probably due to a mistake made when duplicating // code between the Makefile executable and library generators. if (targetType == cmState::EXECUTABLE) { std::string t = vars["FLAGS"]; localGen.AddArchitectureFlags(t, &genTarget, TargetLinkLanguage, cfgName); vars["FLAGS"] = t; } else { std::string t = vars["ARCH_FLAGS"]; localGen.AddArchitectureFlags(t, &genTarget, TargetLinkLanguage, cfgName); vars["ARCH_FLAGS"] = t; t = ""; localGen.AddLanguageFlags(t, TargetLinkLanguage, cfgName); vars["LANGUAGE_COMPILE_FLAGS"] = t; } if (this->GetGeneratorTarget()->HasSOName(cfgName)) { vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage); vars["SONAME"] = this->TargetNameSO; if (targetType == cmState::SHARED_LIBRARY) { std::string install_dir = this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(cfgName); if (!install_dir.empty()) { vars["INSTALLNAME_DIR"] = localGen.Convert( install_dir, cmOutputConverter::NONE, cmOutputConverter::SHELL); } } } cmNinjaDeps byproducts; if (!this->TargetNameImport.empty()) { const std::string impLibPath = localGen.ConvertToOutputFormat( targetOutputImplib, cmOutputConverter::SHELL); vars["TARGET_IMPLIB"] = impLibPath; EnsureParentDirectoryExists(impLibPath); if (genTarget.HasImportLibrary()) { byproducts.push_back(targetOutputImplib); } } if (!this->SetMsvcTargetPdbVariable(vars)) { // It is common to place debug symbols at a specific place, // so we need a plain target name in the rule available. std::string prefix; std::string base; std::string suffix; this->GetGeneratorTarget()->GetFullNameComponents(prefix, base, suffix); std::string dbg_suffix = ".dbg"; // TODO: Where to document? if (mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) { dbg_suffix = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX"); } vars["TARGET_PDB"] = base + suffix + dbg_suffix; } const std::string objPath = GetGeneratorTarget()->GetSupportDirectory(); vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL); EnsureDirectoryExists(objPath); if (this->GetGlobalGenerator()->IsGCCOnWindows()) { // ar.exe can't handle backslashes in rsp files (implicitly used by gcc) std::string& linkLibraries = vars["LINK_LIBRARIES"]; std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/'); std::string& link_path = vars["LINK_PATH"]; std::replace(link_path.begin(), link_path.end(), '\\', '/'); } const std::vector* cmdLists[3] = { >.GetPreBuildCommands(), >.GetPreLinkCommands(), >.GetPostBuildCommands() }; std::vector preLinkCmdLines, postBuildCmdLines; std::vector* cmdLineLists[3] = { &preLinkCmdLines, &preLinkCmdLines, &postBuildCmdLines }; for (unsigned i = 0; i != 3; ++i) { for (std::vector::const_iterator ci = cmdLists[i]->begin(); ci != cmdLists[i]->end(); ++ci) { cmCustomCommandGenerator ccg(*ci, cfgName, this->GetLocalGenerator()); localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]); std::vector const& ccByproducts = ccg.GetByproducts(); std::transform(ccByproducts.begin(), ccByproducts.end(), std::back_inserter(byproducts), MapToNinjaPath()); } } // maybe create .def file from list of objects if (gt.GetType() == cmState::SHARED_LIBRARY && this->GetMakefile()->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS")) { if (gt.GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS")) { std::string cmakeCommand = this->GetLocalGenerator()->ConvertToOutputFormat( cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); std::string name_of_def_file = gt.GetSupportDirectory(); name_of_def_file += "/" + gt.GetName(); name_of_def_file += ".def"; std::string cmd = cmakeCommand; cmd += " -E __create_def "; cmd += this->GetLocalGenerator()->ConvertToOutputFormat( name_of_def_file.c_str(), cmOutputConverter::SHELL); cmd += " "; cmNinjaDeps objs = this->GetObjects(); std::string obj_list_file = name_of_def_file; obj_list_file += ".objs"; cmd += this->GetLocalGenerator()->ConvertToOutputFormat( obj_list_file.c_str(), cmOutputConverter::SHELL); preLinkCmdLines.push_back(cmd); // create a list of obj files for the -E __create_def to read cmGeneratedFileStream fout(obj_list_file.c_str()); for (cmNinjaDeps::iterator i = objs.begin(); i != objs.end(); ++i) { if (cmHasLiteralSuffix(*i, ".obj")) { fout << *i << "\n"; } } } } // If we have any PRE_LINK commands, we need to go back to HOME_OUTPUT for // the link commands. if (!preLinkCmdLines.empty()) { const std::string homeOutDir = localGen.ConvertToOutputFormat( localGen.GetBinaryDirectory(), cmOutputConverter::SHELL); preLinkCmdLines.push_back("cd " + homeOutDir); } vars["PRE_LINK"] = localGen.BuildCommandLine(preLinkCmdLines); std::string postBuildCmdLine = localGen.BuildCommandLine(postBuildCmdLines); cmNinjaVars symlinkVars; if (targetOutput == targetOutputReal) { vars["POST_BUILD"] = postBuildCmdLine; } else { vars["POST_BUILD"] = ":"; symlinkVars["POST_BUILD"] = postBuildCmdLine; } cmGlobalNinjaGenerator& globalGen = *this->GetGlobalGenerator(); int commandLineLengthLimit = -1; if (!this->ForceResponseFile()) { commandLineLengthLimit = calculateCommandLineLengthLimit( globalGen.GetRuleCmdLength(this->LanguageLinkerRule())); } const std::string rspfile = std::string(cmake::GetCMakeFilesDirectoryPostSlash()) + gt.GetName() + ".rsp"; // Gather order-only dependencies. cmNinjaDeps orderOnlyDeps; this->GetLocalGenerator()->AppendTargetDepends(this->GetGeneratorTarget(), orderOnlyDeps); // Ninja should restat after linking if and only if there are byproducts. vars["RESTAT"] = byproducts.empty() ? "" : "1"; for (cmNinjaDeps::const_iterator oi = byproducts.begin(), oe = byproducts.end(); oi != oe; ++oi) { this->GetGlobalGenerator()->SeenCustomCommandOutput(*oi); outputs.push_back(*oi); } // Write the build statement for this target. bool usedResponseFile = false; globalGen.WriteBuild(this->GetBuildFileStream(), comment.str(), this->LanguageLinkerRule(), outputs, explicitDeps, implicitDeps, orderOnlyDeps, vars, rspfile, commandLineLengthLimit, &usedResponseFile); this->WriteLinkRule(usedResponseFile); if (targetOutput != targetOutputReal && !gt.IsFrameworkOnApple()) { if (targetType == cmState::EXECUTABLE) { globalGen.WriteBuild( this->GetBuildFileStream(), "Create executable symlink " + targetOutput, "CMAKE_SYMLINK_EXECUTABLE", cmNinjaDeps(1, targetOutput), cmNinjaDeps(1, targetOutputReal), emptyDeps, emptyDeps, symlinkVars); } else { cmNinjaDeps symlinks; std::string const soName = this->ConvertToNinjaPath(this->GetTargetFilePath(this->TargetNameSO)); // If one link has to be created. if (targetOutputReal == soName || targetOutput == soName) { symlinkVars["SONAME"] = soName; } else { symlinkVars["SONAME"] = ""; symlinks.push_back(soName); } symlinks.push_back(targetOutput); globalGen.WriteBuild( this->GetBuildFileStream(), "Create library symlink " + targetOutput, "CMAKE_SYMLINK_LIBRARY", symlinks, cmNinjaDeps(1, targetOutputReal), emptyDeps, emptyDeps, symlinkVars); } } // Add aliases for the file name and the target name. globalGen.AddTargetAlias(this->TargetNameOut, >); globalGen.AddTargetAlias(this->GetTargetName(), >); } void cmNinjaNormalTargetGenerator::WriteObjectLibStatement() { // Write a phony output that depends on all object files. cmNinjaDeps outputs; this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(), outputs); cmNinjaDeps depends = this->GetObjects(); this->GetGlobalGenerator()->WritePhonyBuild( this->GetBuildFileStream(), "Object library " + this->GetTargetName(), outputs, depends); // Add aliases for the target name. this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(), this->GetGeneratorTarget()); }