CMake/Source/cmNinjaNormalTargetGenerato...

794 lines
27 KiB
C++
Raw Normal View History

2011-11-11 09:00:49 +04:00
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
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 "cmLocalNinjaGenerator.h"
#include "cmGlobalNinjaGenerator.h"
#include "cmSourceFile.h"
#include "cmGeneratedFileStream.h"
#include "cmMakefile.h"
#include "cmOSXBundleGenerator.h"
#include "cmGeneratorTarget.h"
#include "cmCustomCommandGenerator.h"
2015-03-08 15:51:20 +03:00
#include "cmAlgorithms.h"
2011-11-11 09:00:49 +04:00
#include <assert.h>
#include <algorithm>
#include <limits>
2011-11-11 09:00:49 +04:00
#ifndef _WIN32
#include <unistd.h>
#endif
2011-11-11 09:00:49 +04:00
cmNinjaNormalTargetGenerator::
cmNinjaNormalTargetGenerator(cmGeneratorTarget* target)
: cmNinjaTargetGenerator(target)
2011-11-11 09:00:49 +04:00
, TargetNameOut()
, TargetNameSO()
, TargetNameReal()
, TargetNameImport()
, TargetNamePDB()
, TargetLinkLanguage("")
2011-11-11 09:00:49 +04:00
{
this->TargetLinkLanguage = target->GetLinkerLanguage(this->GetConfigName());
2011-11-11 09:00:49 +04:00
if (target->GetType() == cmTarget::EXECUTABLE)
this->GetGeneratorTarget()->GetExecutableNames(this->TargetNameOut,
2011-11-11 09:00:49 +04:00
this->TargetNameReal,
this->TargetNameImport,
this->TargetNamePDB,
GetLocalGenerator()->GetConfigName());
else
this->GetGeneratorTarget()->GetLibraryNames(this->TargetNameOut,
2011-11-11 09:00:49 +04:00
this->TargetNameSO,
this->TargetNameReal,
this->TargetNameImport,
this->TargetNamePDB,
GetLocalGenerator()->GetConfigName());
if(target->GetType() != cmTarget::OBJECT_LIBRARY)
{
// on Windows the output dir is already needed at compile time
// ensure the directory exists (OutDir test)
EnsureDirectoryExists(target->Target->GetDirectory(this->GetConfigName()));
}
this->OSXBundleGenerator = new cmOSXBundleGenerator(target,
this->GetConfigName());
this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
2011-11-11 09:00:49 +04:00
}
cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator()
{
delete this->OSXBundleGenerator;
2011-11-11 09:00:49 +04:00
}
void cmNinjaNormalTargetGenerator::Generate()
{
if (this->TargetLinkLanguage.empty()) {
cmSystemTools::Error("CMake can not determine linker language for "
"target: ",
2014-02-07 02:31:47 +04:00
this->GetTarget()->GetName().c_str());
2011-11-11 09:00:49 +04:00
return;
}
// Write the rules for each language.
this->WriteLanguagesRules();
// Write the build statements
this->WriteObjectBuildStatements();
if(this->GetTarget()->GetType() == cmTarget::OBJECT_LIBRARY)
{
this->WriteObjectLibStatement();
}
else
{
this->WriteLinkStatement();
}
2011-11-11 09:00:49 +04:00
}
void cmNinjaNormalTargetGenerator::WriteLanguagesRules()
{
#ifdef NINJA_GEN_VERBOSE_FILES
2011-11-11 09:00:49 +04:00
cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream());
this->GetRulesFileStream()
<< "# Rules for each languages for "
<< cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
<< " target "
<< this->GetTargetName()
<< "\n\n";
#endif
2011-11-11 09:00:49 +04:00
// Write rules for languages compiled in this target.
std::set<std::string> languages;
std::vector<cmSourceFile*> sourceFiles;
this->GetTarget()->GetSourceFiles(sourceFiles,
this->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE"));
for(std::vector<cmSourceFile*>::const_iterator
i = sourceFiles.begin(); i != sourceFiles.end(); ++i)
{
const std::string& lang = (*i)->GetLanguage();
if(!lang.empty())
{
languages.insert(lang);
}
}
for(std::set<std::string>::const_iterator l = languages.begin();
2011-11-11 09:00:49 +04:00
l != languages.end();
++l)
{
2011-11-11 09:00:49 +04:00
this->WriteLanguageRules(*l);
}
2011-11-11 09:00:49 +04:00
}
const char *cmNinjaNormalTargetGenerator::GetVisibleTypeName() const
{
switch (this->GetTarget()->GetType()) {
case cmTarget::STATIC_LIBRARY:
return "static library";
case cmTarget::SHARED_LIBRARY:
return "shared library";
case cmTarget::MODULE_LIBRARY:
if (this->GetTarget()->IsCFBundleOnApple())
return "CFBundle shared module";
else
return "shared module";
2011-11-11 09:00:49 +04:00
case cmTarget::EXECUTABLE:
return "executable";
default:
return 0;
}
}
std::string
cmNinjaNormalTargetGenerator
::LanguageLinkerRule() const
{
return this->TargetLinkLanguage
2011-11-11 09:00:49 +04:00
+ "_"
+ cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
+ "_LINKER__"
+ cmGlobalNinjaGenerator::EncodeRuleName(this->GetTarget()->GetName())
;
2011-11-11 09:00:49 +04:00
}
void
cmNinjaNormalTargetGenerator
::WriteLinkRule(bool useResponseFile)
2011-11-11 09:00:49 +04:00
{
cmTarget::TargetType targetType = this->GetTarget()->GetType();
std::string ruleName = this->LanguageLinkerRule();
// Select whether to use a response file for objects.
std::string rspfile;
std::string rspcontent;
2011-11-11 09:00:49 +04:00
if (!this->GetGlobalGenerator()->HasRule(ruleName)) {
cmLocalGenerator::RuleVariables vars;
vars.RuleLauncher = "RULE_LAUNCH_LINK";
vars.CMTarget = this->GetTarget();
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 = "";
}
2012-06-12 06:17:55 +04:00
vars.ObjectDir = "$OBJECT_DIR";
vars.Target = "$TARGET_FILE";
Support building shared libraries or modules without soname (#13155) Add a boolean target property NO_SONAME which may be used to disable soname for the specified shared library or module even if the platform supports it. This property should be useful for private shared libraries or various plugins which live in private directories and have not been designed to be found or loaded globally. Replace references to <CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG> and hard-coded -install_name flags with a conditional <SONAME_FLAG> which is expanded to the value of the CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG definition as long as soname supports is enabled for the target in question. Keep expanding CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG in rules in case third party projects still use it. Such projects would not yet use NO_SONAME so the adjacent <TARGET_SONAME> will always be expanded. Make <TARGET_INSTALLNAME_DIR> NO_SONAME aware as well. Since -install_name is soname on OS X, this should not be a problem if this variable is expanded only if soname is enabled. The Ninja generator performs rule variable substitution only once globally per rule to put its own placeholders. Final substitution is performed by ninja at build time. Therefore we cannot conditionally replace the soname placeholders on a per-target basis. Rather than omitting $SONAME from rules.ninja, simply do not write its contents for targets which have NO_SONAME. Since 3 variables are affected by NO_SONAME ($SONAME, $SONAME_FLAG, $INSTALLNAME_DIR), set them only if soname is enabled.
2012-04-22 17:42:55 +04:00
vars.SONameFlag = "$SONAME_FLAG";
2011-11-11 09:00:49 +04:00
vars.TargetSOName = "$SONAME";
vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
vars.TargetPDB = "$TARGET_PDB";
2011-11-11 09:00:49 +04:00
// Setup the target version.
std::string targetVersionMajor;
std::string targetVersionMinor;
{
std::ostringstream majorStream;
std::ostringstream minorStream;
2011-11-11 09:00:49 +04:00
int major;
int minor;
this->GetTarget()->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";
2011-11-11 09:00:49 +04:00
vars.LinkFlags = "$LINK_FLAGS";
vars.Manifests = "$MANIFESTS";
2011-11-11 09:00:49 +04:00
std::string langFlags;
if (targetType != cmTarget::EXECUTABLE)
{
langFlags += "$LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS";
vars.LanguageCompileFlags = langFlags.c_str();
}
2011-11-11 09:00:49 +04:00
// Rule for linking library/executable.
2011-11-11 09:00:49 +04:00
std::vector<std::string> linkCmds = this->ComputeLinkCmd();
for(std::vector<std::string>::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;
2011-11-11 09:00:49 +04:00
comment << "Rule for linking " << this->TargetLinkLanguage << " "
<< this->GetVisibleTypeName() << ".";
std::ostringstream description;
2011-11-11 09:00:49 +04:00
description << "Linking " << this->TargetLinkLanguage << " "
<< this->GetVisibleTypeName() << " $TARGET_FILE";
2011-11-11 09:00:49 +04:00
this->GetGlobalGenerator()->AddRule(ruleName,
linkCmd,
description.str(),
comment.str(),
/*depfile*/ "",
/*deptype*/ "",
rspfile,
rspcontent,
Add an option for explicit BYPRODUCTS of custom commands (#14963) A common idiom in CMake-based build systems is to have custom commands that generate files not listed explicitly as outputs so that these files do not have to be newer than the inputs. The file modification times of such "byproducts" are updated only when their content changes. Then other build rules can depend on the byproducts explicitly so that their dependents rebuild when the content of the original byproducts really does change. This "undeclared byproduct" approach is necessary for Makefile, VS, and Xcode build tools because if a byproduct were listed as an output of a rule then the rule would always rerun when the input is newer than the byproduct but the byproduct may never be updated. Ninja solves this problem by offering a 'restat' feature to check whether an output was really modified after running a rule and tracking the fact that it is up to date separately from its timestamp. However, Ninja also stats all dependencies up front and will only restat files that are listed as outputs of rules with the 'restat' option enabled. Therefore an undeclared byproduct that does not exist at the start of the build will be considered missing and the build will fail even if other dependencies would cause the byproduct to be available before its dependents build. CMake works around this limitation by adding 'phony' build rules for custom command dependencies in the build tree that do not have any explicit specification of what produces them. This is not optimal because it prevents Ninja from reporting an error when an input to a rule really is missing. A better approach is to allow projects to explicitly specify the byproducts of their custom commands so that no phony rules are needed for them. In order to work with the non-Ninja generators, the byproducts must be known separately from the outputs. Add a new "BYPRODUCTS" option to the add_custom_command and add_custom_target commands to specify byproducts explicitly. Teach the Ninja generator to specify byproducts as outputs of the custom commands. In the case of POST_BUILD, PRE_LINK, and PRE_BUILD events on targets that link, the byproducts must be specified as outputs of the link rule that runs the commands. Activate 'restat' for such rules so that Ninja knows it needs to check the byproducts, but not for link rules that have no byproducts.
2014-11-14 02:54:52 +03:00
/*restat*/ "$RESTAT",
/*generator*/ false);
2011-11-11 09:00:49 +04:00
}
if (this->TargetNameOut != this->TargetNameReal &&
!this->GetTarget()->IsFrameworkOnApple()) {
2011-11-11 09:00:49 +04:00
std::string cmakeCommand =
this->GetLocalGenerator()->ConvertToOutputFormat(
cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
2011-11-11 09:00:49 +04:00
if (targetType == cmTarget::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);
2011-11-11 09:00:49 +04:00
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);
2011-11-11 09:00:49 +04:00
}
}
std::vector<std::string>
cmNinjaNormalTargetGenerator
::ComputeLinkCmd()
{
std::vector<std::string> 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->GetTarget()->GetType()) {
2011-11-11 09:00:49 +04:00
case cmTarget::STATIC_LIBRARY: {
// We have archive link commands set. First, delete the existing archive.
{
2011-11-11 09:00:49 +04:00
std::string cmakeCommand =
this->GetLocalGenerator()->ConvertToOutputFormat(
cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
linkCmds.push_back(cmakeCommand + " -E remove $TARGET_FILE");
}
2011-11-11 09:00:49 +04:00
// 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);
2011-11-11 09:00:49 +04:00
}
{
std::string linkCmdVar = "CMAKE_";
linkCmdVar += this->TargetLinkLanguage;
linkCmdVar += "_ARCHIVE_FINISH";
const char *linkCmd = mf->GetRequiredDefinition(linkCmdVar);
cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
2011-11-11 09:00:49 +04:00
}
return linkCmds;
}
case cmTarget::SHARED_LIBRARY:
case cmTarget::MODULE_LIBRARY:
case cmTarget::EXECUTABLE:
break;
2011-11-11 09:00:49 +04:00
default:
assert(0 && "Unexpected target type");
}
2012-02-05 05:48:08 +04:00
return std::vector<std::string>();
2011-11-11 09:00:49 +04:00
}
static int calculateCommandLineLengthLimit(int linkRuleLength)
2011-11-11 09:00:49 +04:00
{
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<int>::max()
};
size_t const arrSz = cmArraySize(limits);
int const sz = *std::min_element(limits, limits + arrSz);
if (sz == std::numeric_limits<int>::max())
{
return -1;
}
return sz - linkRuleLength;
}
2011-11-11 09:00:49 +04:00
void cmNinjaNormalTargetGenerator::WriteLinkStatement()
{
cmTarget& target = *this->GetTarget();
2012-10-06 20:30:43 +04:00
cmGeneratorTarget& gt = *this->GetGeneratorTarget();
const std::string cfgName = this->GetConfigName();
std::string targetOutput = ConvertToNinjaPath(
2012-10-06 20:30:43 +04:00
gt.GetFullPath(cfgName));
std::string targetOutputReal = ConvertToNinjaPath(
2012-10-06 20:30:43 +04:00
gt.GetFullPath(cfgName,
/*implib=*/false,
/*realpath=*/true));
std::string targetOutputImplib = ConvertToNinjaPath(
2012-10-06 20:30:43 +04:00
gt.GetFullPath(cfgName,
/*implib=*/true));
if (target.IsAppBundleOnApple())
{
// Create the app bundle
std::string outpath = target.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 (target.IsFrameworkOnApple())
{
// Create the library framework.
this->OSXBundleGenerator->CreateFramework(this->TargetNameOut,
target.GetDirectory(cfgName));
}
else if(target.IsCFBundleOnApple())
{
2012-07-16 18:00:40 +04:00
// Create the core foundation bundle.
this->OSXBundleGenerator->CreateCFBundle(this->TargetNameOut,
target.GetDirectory(cfgName));
}
2011-11-11 09:00:49 +04:00
// Write comments.
cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
const cmTarget::TargetType targetType = target.GetType();
2011-11-11 09:00:49 +04:00
this->GetBuildFileStream()
<< "# Link build statements for "
<< cmTarget::GetTargetTypeName(targetType)
<< " target "
<< this->GetTargetName()
<< "\n\n";
cmNinjaDeps emptyDeps;
cmNinjaVars vars;
// Compute the comment.
std::ostringstream comment;
comment <<
"Link the " << this->GetVisibleTypeName() << " " << targetOutputReal;
2011-11-11 09:00:49 +04:00
// Compute outputs.
cmNinjaDeps outputs;
outputs.push_back(targetOutputReal);
// Compute specific libraries to link with.
cmNinjaDeps explicitDeps = this->GetObjects();
cmNinjaDeps implicitDeps = this->ComputeLinkDeps();
2011-11-11 09:00:49 +04:00
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, cmLocalGenerator::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")
&& target.GetType() == cmTarget::SHARED_LIBRARY)
{
if(target.GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS"))
{
std::string dllname = targetOutput;
std::string name_of_def_file
= target.GetSupportDirectory();
name_of_def_file += "/" + target.GetName();
name_of_def_file += ".def ";
vars["LINK_FLAGS"] += " /DEF:";
vars["LINK_FLAGS"] += this->GetLocalGenerator()
->ConvertToOutputFormat(name_of_def_file.c_str(),
cmLocalGenerator::SHELL);
}
}
2011-11-11 09:00:49 +04:00
this->addPoolNinjaVariable("JOB_POOL_LINK", &target, vars);
2012-03-10 15:19:18 +04:00
this->AddModuleDefinitionFlag(vars["LINK_FLAGS"]);
vars["LINK_FLAGS"] = cmGlobalNinjaGenerator
::EncodeLiteral(vars["LINK_FLAGS"]);
vars["MANIFESTS"] = this->GetManifests();
vars["LINK_PATH"] = frameworkPath + linkPath;
2012-03-10 15:19:18 +04:00
2011-11-11 09:00:49 +04:00
// 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 == cmTarget::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);
Support building shared libraries or modules without soname (#13155) Add a boolean target property NO_SONAME which may be used to disable soname for the specified shared library or module even if the platform supports it. This property should be useful for private shared libraries or various plugins which live in private directories and have not been designed to be found or loaded globally. Replace references to <CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG> and hard-coded -install_name flags with a conditional <SONAME_FLAG> which is expanded to the value of the CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG definition as long as soname supports is enabled for the target in question. Keep expanding CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG in rules in case third party projects still use it. Such projects would not yet use NO_SONAME so the adjacent <TARGET_SONAME> will always be expanded. Make <TARGET_INSTALLNAME_DIR> NO_SONAME aware as well. Since -install_name is soname on OS X, this should not be a problem if this variable is expanded only if soname is enabled. The Ninja generator performs rule variable substitution only once globally per rule to put its own placeholders. Final substitution is performed by ninja at build time. Therefore we cannot conditionally replace the soname placeholders on a per-target basis. Rather than omitting $SONAME from rules.ninja, simply do not write its contents for targets which have NO_SONAME. Since 3 variables are affected by NO_SONAME ($SONAME, $SONAME_FLAG, $INSTALLNAME_DIR), set them only if soname is enabled.
2012-04-22 17:42:55 +04:00
vars["SONAME"] = this->TargetNameSO;
if (targetType == cmTarget::SHARED_LIBRARY)
{
std::string install_dir =
this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(cfgName);
if (!install_dir.empty())
{
vars["INSTALLNAME_DIR"] = localGen.Convert(install_dir,
cmLocalGenerator::NONE,
cmLocalGenerator::SHELL);
}
Support building shared libraries or modules without soname (#13155) Add a boolean target property NO_SONAME which may be used to disable soname for the specified shared library or module even if the platform supports it. This property should be useful for private shared libraries or various plugins which live in private directories and have not been designed to be found or loaded globally. Replace references to <CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG> and hard-coded -install_name flags with a conditional <SONAME_FLAG> which is expanded to the value of the CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG definition as long as soname supports is enabled for the target in question. Keep expanding CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG in rules in case third party projects still use it. Such projects would not yet use NO_SONAME so the adjacent <TARGET_SONAME> will always be expanded. Make <TARGET_INSTALLNAME_DIR> NO_SONAME aware as well. Since -install_name is soname on OS X, this should not be a problem if this variable is expanded only if soname is enabled. The Ninja generator performs rule variable substitution only once globally per rule to put its own placeholders. Final substitution is performed by ninja at build time. Therefore we cannot conditionally replace the soname placeholders on a per-target basis. Rather than omitting $SONAME from rules.ninja, simply do not write its contents for targets which have NO_SONAME. Since 3 variables are affected by NO_SONAME ($SONAME, $SONAME_FLAG, $INSTALLNAME_DIR), set them only if soname is enabled.
2012-04-22 17:42:55 +04:00
}
2011-11-11 09:00:49 +04:00
}
cmNinjaDeps byproducts;
if (!this->TargetNameImport.empty())
{
const std::string impLibPath = localGen.ConvertToOutputFormat(
targetOutputImplib,
cmLocalGenerator::SHELL);
2012-08-22 14:26:56 +04:00
vars["TARGET_IMPLIB"] = impLibPath;
EnsureParentDirectoryExists(impLibPath);
if(target.HasImportLibrary())
{
byproducts.push_back(targetOutputImplib);
}
}
if (!this->SetMsvcTargetPdbVariable(vars))
2012-07-16 16:16:43 +04:00
{
// 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;
2012-07-16 16:16:43 +04:00
}
2012-06-12 06:17:55 +04:00
const std::string objPath = GetTarget()->GetSupportDirectory();
vars["OBJECT_DIR"] = ConvertToNinjaPath(objPath);
EnsureDirectoryExists(objPath);
if (this->GetGlobalGenerator()->IsGCCOnWindows())
2012-06-12 06:17:55 +04:00
{
2012-11-07 20:13:09 +04:00
// 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(), '\\', '/');
2012-06-12 06:17:55 +04:00
}
const std::vector<cmCustomCommand> *cmdLists[3] = {
&target.GetPreBuildCommands(),
&target.GetPreLinkCommands(),
&target.GetPostBuildCommands()
2011-11-11 09:00:49 +04:00
};
std::vector<std::string> preLinkCmdLines, postBuildCmdLines;
std::vector<std::string> *cmdLineLists[3] = {
&preLinkCmdLines,
&preLinkCmdLines,
&postBuildCmdLines
};
for (unsigned i = 0; i != 3; ++i)
{
2011-11-11 09:00:49 +04:00
for (std::vector<cmCustomCommand>::const_iterator
ci = cmdLists[i]->begin();
ci != cmdLists[i]->end(); ++ci)
{
cmCustomCommandGenerator ccg(*ci, cfgName, this->GetLocalGenerator());
localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]);
Add an option for explicit BYPRODUCTS of custom commands (#14963) A common idiom in CMake-based build systems is to have custom commands that generate files not listed explicitly as outputs so that these files do not have to be newer than the inputs. The file modification times of such "byproducts" are updated only when their content changes. Then other build rules can depend on the byproducts explicitly so that their dependents rebuild when the content of the original byproducts really does change. This "undeclared byproduct" approach is necessary for Makefile, VS, and Xcode build tools because if a byproduct were listed as an output of a rule then the rule would always rerun when the input is newer than the byproduct but the byproduct may never be updated. Ninja solves this problem by offering a 'restat' feature to check whether an output was really modified after running a rule and tracking the fact that it is up to date separately from its timestamp. However, Ninja also stats all dependencies up front and will only restat files that are listed as outputs of rules with the 'restat' option enabled. Therefore an undeclared byproduct that does not exist at the start of the build will be considered missing and the build will fail even if other dependencies would cause the byproduct to be available before its dependents build. CMake works around this limitation by adding 'phony' build rules for custom command dependencies in the build tree that do not have any explicit specification of what produces them. This is not optimal because it prevents Ninja from reporting an error when an input to a rule really is missing. A better approach is to allow projects to explicitly specify the byproducts of their custom commands so that no phony rules are needed for them. In order to work with the non-Ninja generators, the byproducts must be known separately from the outputs. Add a new "BYPRODUCTS" option to the add_custom_command and add_custom_target commands to specify byproducts explicitly. Teach the Ninja generator to specify byproducts as outputs of the custom commands. In the case of POST_BUILD, PRE_LINK, and PRE_BUILD events on targets that link, the byproducts must be specified as outputs of the link rule that runs the commands. Activate 'restat' for such rules so that Ninja knows it needs to check the byproducts, but not for link rules that have no byproducts.
2014-11-14 02:54:52 +03:00
std::vector<std::string> const& ccByproducts = ccg.GetByproducts();
std::transform(ccByproducts.begin(), ccByproducts.end(),
std::back_inserter(byproducts), MapToNinjaPath());
}
2011-11-11 09:00:49 +04:00
}
// maybe create .def file from list of objects
if (target.GetType() == cmTarget::SHARED_LIBRARY &&
this->GetMakefile()->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS"))
{
if(target.GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS"))
{
std::string cmakeCommand =
this->GetLocalGenerator()->ConvertToOutputFormat(
cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
std::string dllname = targetOutput;
std::string name_of_def_file
= target.GetSupportDirectory();
name_of_def_file += "/" + target.GetName();
name_of_def_file += ".def";
std::string cmd = cmakeCommand;
cmd += " -E __create_def ";
cmd += this->GetLocalGenerator()
->ConvertToOutputFormat(name_of_def_file.c_str(),
cmLocalGenerator::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(),
cmLocalGenerator::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";
}
}
}
}
2011-11-11 09:00:49 +04:00
// 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(
mf->GetHomeOutputDirectory(),
cmLocalGenerator::SHELL);
2012-08-22 14:26:56 +04:00
preLinkCmdLines.push_back("cd " + homeOutDir);
}
2011-11-11 09:00:49 +04:00
vars["PRE_LINK"] = localGen.BuildCommandLine(preLinkCmdLines);
std::string postBuildCmdLine = localGen.BuildCommandLine(postBuildCmdLines);
2011-11-11 09:00:49 +04:00
cmNinjaVars symlinkVars;
if (targetOutput == targetOutputReal)
{
2011-11-11 09:00:49 +04:00
vars["POST_BUILD"] = postBuildCmdLine;
}
else
{
2011-11-11 09:00:49 +04:00
vars["POST_BUILD"] = ":";
symlinkVars["POST_BUILD"] = postBuildCmdLine;
}
2011-11-11 09:00:49 +04:00
cmGlobalNinjaGenerator& globalGen = *this->GetGlobalGenerator();
int commandLineLengthLimit = 1;
const char* forceRspFile = "CMAKE_NINJA_FORCE_RESPONSE_FILE";
if (!mf->IsDefinitionSet(forceRspFile) &&
cmSystemTools::GetEnv(forceRspFile) == 0)
{
commandLineLengthLimit = calculateCommandLineLengthLimit(
globalGen.GetRuleCmdLength(this->LanguageLinkerRule()));
}
const std::string rspfile =
std::string(cmake::GetCMakeFilesDirectoryPostSlash())
+ target.GetName() + ".rsp";
// Gather order-only dependencies.
cmNinjaDeps orderOnlyDeps;
this->GetLocalGenerator()->AppendTargetDepends(this->GetTarget(),
orderOnlyDeps);
Add an option for explicit BYPRODUCTS of custom commands (#14963) A common idiom in CMake-based build systems is to have custom commands that generate files not listed explicitly as outputs so that these files do not have to be newer than the inputs. The file modification times of such "byproducts" are updated only when their content changes. Then other build rules can depend on the byproducts explicitly so that their dependents rebuild when the content of the original byproducts really does change. This "undeclared byproduct" approach is necessary for Makefile, VS, and Xcode build tools because if a byproduct were listed as an output of a rule then the rule would always rerun when the input is newer than the byproduct but the byproduct may never be updated. Ninja solves this problem by offering a 'restat' feature to check whether an output was really modified after running a rule and tracking the fact that it is up to date separately from its timestamp. However, Ninja also stats all dependencies up front and will only restat files that are listed as outputs of rules with the 'restat' option enabled. Therefore an undeclared byproduct that does not exist at the start of the build will be considered missing and the build will fail even if other dependencies would cause the byproduct to be available before its dependents build. CMake works around this limitation by adding 'phony' build rules for custom command dependencies in the build tree that do not have any explicit specification of what produces them. This is not optimal because it prevents Ninja from reporting an error when an input to a rule really is missing. A better approach is to allow projects to explicitly specify the byproducts of their custom commands so that no phony rules are needed for them. In order to work with the non-Ninja generators, the byproducts must be known separately from the outputs. Add a new "BYPRODUCTS" option to the add_custom_command and add_custom_target commands to specify byproducts explicitly. Teach the Ninja generator to specify byproducts as outputs of the custom commands. In the case of POST_BUILD, PRE_LINK, and PRE_BUILD events on targets that link, the byproducts must be specified as outputs of the link rule that runs the commands. Activate 'restat' for such rules so that Ninja knows it needs to check the byproducts, but not for link rules that have no byproducts.
2014-11-14 02:54:52 +03:00
// 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);
}
2011-11-11 09:00:49 +04:00
// 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 && !target.IsFrameworkOnApple())
{
if (targetType == cmTarget::EXECUTABLE)
{
globalGen.WriteBuild(this->GetBuildFileStream(),
"Create executable symlink " + targetOutput,
"CMAKE_SYMLINK_EXECUTABLE",
cmNinjaDeps(1, targetOutput),
cmNinjaDeps(1, targetOutputReal),
emptyDeps,
emptyDeps,
symlinkVars);
}
else
{
2012-07-06 12:16:45 +04:00
cmNinjaDeps symlinks;
const std::string soName = this->GetTargetFilePath(this->TargetNameSO);
// If one link has to be created.
if (targetOutputReal == soName || targetOutput == soName)
{
symlinkVars["SONAME"] = soName;
}
else
{
symlinkVars["SONAME"] = "";
2012-07-06 12:16:45 +04:00
symlinks.push_back(soName);
}
2012-07-06 12:16:45 +04:00
symlinks.push_back(targetOutput);
globalGen.WriteBuild(this->GetBuildFileStream(),
"Create library symlink " + targetOutput,
"CMAKE_SYMLINK_LIBRARY",
symlinks,
cmNinjaDeps(1, targetOutputReal),
emptyDeps,
emptyDeps,
symlinkVars);
}
2011-11-11 09:00:49 +04:00
}
2011-11-11 09:00:49 +04:00
// Add aliases for the file name and the target name.
globalGen.AddTargetAlias(this->TargetNameOut, &target);
globalGen.AddTargetAlias(this->GetTargetName(), &target);
2011-11-11 09:00:49 +04:00
}
//----------------------------------------------------------------------------
void cmNinjaNormalTargetGenerator::WriteObjectLibStatement()
{
// Write a phony output that depends on all object files.
cmNinjaDeps outputs;
this->GetLocalGenerator()->AppendTargetOutputs(this->GetTarget(), 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->GetTarget());
}