2006-02-14 18:36:04 +03:00
|
|
|
/*=========================================================================
|
|
|
|
|
|
|
|
Program: CMake - Cross-Platform Makefile Generator
|
|
|
|
Module: $RCSfile$
|
|
|
|
Language: C++
|
|
|
|
Date: $Date$
|
|
|
|
Version: $Revision$
|
|
|
|
|
|
|
|
Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
|
|
|
|
See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
|
|
|
|
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
|
|
PURPOSE. See the above copyright notices for more information.
|
|
|
|
|
|
|
|
=========================================================================*/
|
|
|
|
#include "cmMakefileTargetGenerator.h"
|
|
|
|
|
|
|
|
#include "cmGeneratedFileStream.h"
|
|
|
|
#include "cmGlobalGenerator.h"
|
2006-04-11 19:06:19 +04:00
|
|
|
#include "cmGlobalUnixMakefileGenerator3.h"
|
2006-02-14 18:36:04 +03:00
|
|
|
#include "cmLocalUnixMakefileGenerator3.h"
|
|
|
|
#include "cmMakefile.h"
|
|
|
|
#include "cmSourceFile.h"
|
|
|
|
#include "cmTarget.h"
|
2006-06-14 20:28:32 +04:00
|
|
|
#include "cmake.h"
|
2006-02-14 18:36:04 +03:00
|
|
|
|
|
|
|
#include "cmMakefileExecutableTargetGenerator.h"
|
|
|
|
#include "cmMakefileLibraryTargetGenerator.h"
|
|
|
|
#include "cmMakefileUtilityTargetGenerator.h"
|
|
|
|
|
|
|
|
|
|
|
|
cmMakefileTargetGenerator::cmMakefileTargetGenerator()
|
|
|
|
{
|
|
|
|
this->BuildFileStream = 0;
|
|
|
|
this->InfoFileStream = 0;
|
2006-02-16 00:35:16 +03:00
|
|
|
this->FlagFileStream = 0;
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
cmMakefileTargetGenerator *
|
|
|
|
cmMakefileTargetGenerator::New(cmLocalUnixMakefileGenerator3 *lg,
|
|
|
|
cmStdString tgtName, cmTarget *tgt)
|
|
|
|
{
|
|
|
|
cmMakefileTargetGenerator *result = 0;
|
|
|
|
|
|
|
|
switch (tgt->GetType())
|
|
|
|
{
|
|
|
|
case cmTarget::EXECUTABLE:
|
|
|
|
result = new cmMakefileExecutableTargetGenerator;
|
|
|
|
break;
|
|
|
|
case cmTarget::STATIC_LIBRARY:
|
|
|
|
case cmTarget::SHARED_LIBRARY:
|
|
|
|
case cmTarget::MODULE_LIBRARY:
|
|
|
|
result = new cmMakefileLibraryTargetGenerator;
|
|
|
|
break;
|
|
|
|
case cmTarget::UTILITY:
|
|
|
|
result = new cmMakefileUtilityTargetGenerator;
|
|
|
|
break;
|
|
|
|
default:
|
2006-02-16 21:42:58 +03:00
|
|
|
return result;
|
2006-08-01 19:38:42 +04:00
|
|
|
// break; /* unreachable */
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
result->TargetName = tgtName;
|
|
|
|
result->Target = tgt;
|
|
|
|
result->LocalGenerator = lg;
|
|
|
|
result->GlobalGenerator = lg->GetGlobalGenerator();
|
|
|
|
result->Makefile = lg->GetMakefile();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator::CreateRuleFile()
|
|
|
|
{
|
|
|
|
// Create a directory for this target.
|
2006-07-10 15:52:35 +04:00
|
|
|
this->TargetBuildDirectory =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->GetTargetDirectory(*this->Target);
|
2006-07-10 15:52:35 +04:00
|
|
|
this->TargetBuildDirectoryFull =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->ConvertToFullPath(this->TargetBuildDirectory);
|
|
|
|
cmSystemTools::MakeDirectory(this->TargetBuildDirectoryFull.c_str());
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Construct the rule file name.
|
|
|
|
this->BuildFileName = this->TargetBuildDirectory;
|
|
|
|
this->BuildFileName += "/build.make";
|
|
|
|
this->BuildFileNameFull = this->TargetBuildDirectoryFull;
|
|
|
|
this->BuildFileNameFull += "/build.make";
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-07-11 17:55:27 +04:00
|
|
|
// Construct the rule file name.
|
|
|
|
this->ProgressFileName = this->TargetBuildDirectory;
|
|
|
|
this->ProgressFileName += "/progress.make";
|
|
|
|
this->ProgressFileNameFull = this->TargetBuildDirectoryFull;
|
|
|
|
this->ProgressFileNameFull += "/progress.make";
|
|
|
|
|
|
|
|
// reset the progress count
|
|
|
|
this->NumberOfProgressActions = 0;
|
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Open the rule file. This should be copy-if-different because the
|
|
|
|
// rules may depend on this file itself.
|
2006-07-10 15:52:35 +04:00
|
|
|
this->BuildFileStream =
|
2006-02-14 18:36:04 +03:00
|
|
|
new cmGeneratedFileStream(this->BuildFileNameFull.c_str());
|
|
|
|
this->BuildFileStream->SetCopyIfDifferent(true);
|
|
|
|
if(!this->BuildFileStream)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this->LocalGenerator->WriteDisclaimer(*this->BuildFileStream);
|
|
|
|
this->LocalGenerator->WriteSpecialTargetsTop(*this->BuildFileStream);
|
|
|
|
this->LocalGenerator->WriteMakeVariables(*this->BuildFileStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
2006-06-01 19:45:51 +04:00
|
|
|
void cmMakefileTargetGenerator::WriteTargetBuildRules()
|
2006-02-14 18:36:04 +03:00
|
|
|
{
|
|
|
|
// write the custom commands for this target
|
|
|
|
// Look for files registered for cleaning in this directory.
|
|
|
|
if(const char* additional_clean_files =
|
|
|
|
this->Makefile->GetProperty
|
|
|
|
("ADDITIONAL_MAKE_CLEAN_FILES"))
|
|
|
|
{
|
2006-07-10 15:52:35 +04:00
|
|
|
cmSystemTools::ExpandListArgument(additional_clean_files,
|
2006-02-14 18:36:04 +03:00
|
|
|
this->CleanFiles);
|
2006-07-10 15:52:35 +04:00
|
|
|
}
|
2006-06-01 19:45:51 +04:00
|
|
|
|
|
|
|
// add custom commands to the clean rules?
|
2006-07-10 15:52:35 +04:00
|
|
|
const char* clean_no_custom =
|
2006-06-01 19:45:51 +04:00
|
|
|
this->Makefile->GetProperty("CLEAN_NO_CUSTOM");
|
|
|
|
bool clean = cmSystemTools::IsOff(clean_no_custom);
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-06-01 19:45:51 +04:00
|
|
|
// First generate the object rule files. Save a list of all object
|
|
|
|
// files for this target.
|
|
|
|
const std::vector<cmSourceFile*>& sources = this->Target->GetSourceFiles();
|
|
|
|
for(std::vector<cmSourceFile*>::const_iterator source = sources.begin();
|
|
|
|
source != sources.end(); ++source)
|
|
|
|
{
|
|
|
|
if(cmCustomCommand* cc = (*source)->GetCustomCommand())
|
|
|
|
{
|
|
|
|
this->GenerateCustomRuleFile(*cc);
|
|
|
|
if (clean)
|
|
|
|
{
|
|
|
|
const std::vector<std::string>& outputs = cc->GetOutputs();
|
|
|
|
for(std::vector<std::string>::const_iterator o = outputs.begin();
|
|
|
|
o != outputs.end(); ++o)
|
|
|
|
{
|
|
|
|
this->CleanFiles.push_back
|
|
|
|
(this->Convert(o->c_str(),
|
|
|
|
cmLocalGenerator::START_OUTPUT,
|
|
|
|
cmLocalGenerator::UNCHANGED));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(!(*source)->GetPropertyAsBool("HEADER_FILE_ONLY"))
|
|
|
|
{
|
|
|
|
if(!this->GlobalGenerator->IgnoreFile
|
|
|
|
((*source)->GetSourceExtension().c_str()))
|
|
|
|
{
|
|
|
|
// Generate this object file's rule file.
|
|
|
|
this->WriteObjectRuleFiles(*(*source));
|
|
|
|
}
|
|
|
|
else if((*source)->GetPropertyAsBool("EXTERNAL_OBJECT"))
|
|
|
|
{
|
|
|
|
// This is an external object file. Just add it.
|
|
|
|
this->ExternalObjects.push_back((*source)->GetFullPath());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We only get here if a source file is not an external object
|
|
|
|
// and has an extension that is listed as an ignored file type
|
|
|
|
// for this language. No message or diagnosis should be
|
|
|
|
// given.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator::WriteCommonCodeRules()
|
|
|
|
{
|
|
|
|
// Include the dependencies for the target.
|
|
|
|
std::string dependFileNameFull = this->TargetBuildDirectoryFull;
|
|
|
|
dependFileNameFull += "/depend.make";
|
|
|
|
*this->BuildFileStream
|
|
|
|
<< "# Include any dependencies generated for this target.\n"
|
2006-03-15 19:02:08 +03:00
|
|
|
<< this->LocalGenerator->IncludeDirective << " "
|
2006-02-14 18:36:04 +03:00
|
|
|
<< this->Convert(dependFileNameFull.c_str(),
|
2006-07-11 17:55:27 +04:00
|
|
|
cmLocalGenerator::HOME_OUTPUT,
|
|
|
|
cmLocalGenerator::MAKEFILE)
|
|
|
|
<< "\n\n";
|
|
|
|
|
|
|
|
// Include the progress variables for the target.
|
|
|
|
*this->BuildFileStream
|
|
|
|
<< "# Include the progress variables for this target.\n"
|
|
|
|
<< this->LocalGenerator->IncludeDirective << " "
|
|
|
|
<< this->Convert(this->ProgressFileNameFull.c_str(),
|
|
|
|
cmLocalGenerator::HOME_OUTPUT,
|
|
|
|
cmLocalGenerator::MAKEFILE)
|
2006-02-14 18:36:04 +03:00
|
|
|
<< "\n\n";
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// make sure the depend file exists
|
|
|
|
if (!cmSystemTools::FileExists(dependFileNameFull.c_str()))
|
|
|
|
{
|
|
|
|
// Write an empty dependency file.
|
|
|
|
cmGeneratedFileStream depFileStream(dependFileNameFull.c_str());
|
|
|
|
depFileStream
|
|
|
|
<< "# Empty dependencies file for " << this->Target->GetName() << ".\n"
|
|
|
|
<< "# This may be replaced when dependencies are built." << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open the flags file. This should be copy-if-different because the
|
|
|
|
// rules may depend on this file itself.
|
|
|
|
this->FlagFileNameFull = this->TargetBuildDirectoryFull;
|
|
|
|
this->FlagFileNameFull += "/flags.make";
|
2006-07-10 15:52:35 +04:00
|
|
|
this->FlagFileStream =
|
2006-02-14 18:36:04 +03:00
|
|
|
new cmGeneratedFileStream(this->FlagFileNameFull.c_str());
|
|
|
|
this->FlagFileStream->SetCopyIfDifferent(true);
|
|
|
|
if(!this->FlagFileStream)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this->LocalGenerator->WriteDisclaimer(*this->FlagFileStream);
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Include the flags for the target.
|
|
|
|
*this->BuildFileStream
|
|
|
|
<< "# Include the compile flags for this target's objects.\n"
|
2006-03-15 19:02:08 +03:00
|
|
|
<< this->LocalGenerator->IncludeDirective << " "
|
2006-07-10 15:52:35 +04:00
|
|
|
<< this->Convert(this->FlagFileNameFull.c_str(),
|
|
|
|
cmLocalGenerator::HOME_OUTPUT,
|
2006-02-14 18:36:04 +03:00
|
|
|
cmLocalGenerator::MAKEFILE)
|
|
|
|
<< "\n\n";
|
2006-06-01 19:45:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
|
|
|
|
{
|
2006-02-14 18:36:04 +03:00
|
|
|
// write language flags for target
|
2006-07-10 15:52:35 +04:00
|
|
|
std::map<cmStdString,cmLocalUnixMakefileGenerator3::IntegrityCheckSet>&
|
|
|
|
checkSet =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->GetIntegrityCheckSet()[this->Target->GetName()];
|
2006-07-10 15:52:35 +04:00
|
|
|
for(std::map<cmStdString,
|
2006-02-14 18:36:04 +03:00
|
|
|
cmLocalUnixMakefileGenerator3::IntegrityCheckSet>::const_iterator
|
|
|
|
l = checkSet.begin(); l != checkSet.end(); ++l)
|
|
|
|
{
|
|
|
|
const char *lang = l->first.c_str();
|
|
|
|
std::string flags;
|
|
|
|
// Add the export symbol definition for shared library objects.
|
|
|
|
bool shared = ((this->Target->GetType() == cmTarget::SHARED_LIBRARY) ||
|
|
|
|
(this->Target->GetType() == cmTarget::MODULE_LIBRARY));
|
|
|
|
if(shared)
|
|
|
|
{
|
|
|
|
flags += "-D";
|
2006-07-10 15:52:35 +04:00
|
|
|
if(const char* custom_export_name =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->Target->GetProperty("DEFINE_SYMBOL"))
|
|
|
|
{
|
|
|
|
flags += custom_export_name;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string in = this->Target->GetName();
|
|
|
|
in += "_EXPORTS";
|
|
|
|
flags += cmSystemTools::MakeCindentifier(in.c_str());
|
|
|
|
}
|
|
|
|
}
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Add language-specific flags.
|
2006-02-24 19:13:31 +03:00
|
|
|
this->LocalGenerator
|
|
|
|
->AddLanguageFlags(flags, lang,
|
2006-03-15 19:02:08 +03:00
|
|
|
this->LocalGenerator->ConfigurationName.c_str());
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Add shared-library flags if needed.
|
|
|
|
this->LocalGenerator->AddSharedFlags(flags, lang, shared);
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Add include directory flags.
|
|
|
|
this->LocalGenerator->
|
|
|
|
AppendFlags(flags, this->LocalGenerator->GetIncludeFlags(lang));
|
|
|
|
// Add include directory flags.
|
|
|
|
this->LocalGenerator->
|
|
|
|
AppendFlags(flags,this->GetFrameworkFlags().c_str());
|
|
|
|
|
|
|
|
*this->FlagFileStream << lang << "_FLAGS = " << flags << "\n\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator::WriteObjectRuleFiles(cmSourceFile& source)
|
|
|
|
{
|
|
|
|
// Identify the language of the source file.
|
|
|
|
const char* lang = this->LocalGenerator->GetSourceFileLanguage(source);
|
|
|
|
if(!lang)
|
|
|
|
{
|
2006-04-21 05:54:35 +04:00
|
|
|
// don't know anything about this file so skip it
|
2006-02-14 18:36:04 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the full path name of the object file.
|
|
|
|
std::string objNoTargetDir;
|
2006-07-10 15:52:35 +04:00
|
|
|
std::string obj =
|
|
|
|
this->LocalGenerator->GetObjectFileName(*this->Target, source,
|
2006-05-12 20:29:09 +04:00
|
|
|
&objNoTargetDir);
|
2006-02-14 18:36:04 +03:00
|
|
|
|
|
|
|
// Avoid generating duplicate rules.
|
2006-03-15 19:02:08 +03:00
|
|
|
if(this->ObjectFiles.find(obj) == this->ObjectFiles.end())
|
2006-02-14 18:36:04 +03:00
|
|
|
{
|
2006-03-15 19:02:08 +03:00
|
|
|
this->ObjectFiles.insert(obj);
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cmOStringStream err;
|
|
|
|
err << "Warning: Source file \""
|
|
|
|
<< source.GetSourceName().c_str() << "."
|
|
|
|
<< source.GetSourceExtension().c_str()
|
2006-07-10 15:52:35 +04:00
|
|
|
<< "\" is listed multiple times for target \""
|
2006-02-14 18:36:04 +03:00
|
|
|
<< this->Target->GetName()
|
|
|
|
<< "\".";
|
|
|
|
cmSystemTools::Message(err.str().c_str(), "Warning");
|
|
|
|
return;
|
|
|
|
}
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Create the directory containing the object file. This may be a
|
|
|
|
// subdirectory under the target's directory.
|
|
|
|
std::string dir = cmSystemTools::GetFilenamePath(obj.c_str());
|
|
|
|
cmSystemTools::MakeDirectory
|
|
|
|
(this->LocalGenerator->ConvertToFullPath(dir).c_str());
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Save this in the target's list of object files.
|
2006-03-28 17:54:01 +04:00
|
|
|
if ( source.GetPropertyAsBool("EXTRA_CONTENT") )
|
|
|
|
{
|
|
|
|
this->ExtraContent.insert(obj);
|
|
|
|
}
|
2006-02-14 18:36:04 +03:00
|
|
|
this->Objects.push_back(obj);
|
2006-07-07 00:04:49 +04:00
|
|
|
|
|
|
|
// TODO: Remove
|
2006-07-10 15:52:35 +04:00
|
|
|
//std::string relativeObj
|
|
|
|
//= this->LocalGenerator->GetHomeRelativeOutputPath();
|
2006-07-07 00:04:49 +04:00
|
|
|
//relativeObj += obj;
|
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// we compute some depends when writing the depend.make that we will also
|
|
|
|
// use in the build.make, same with depMakeFile
|
|
|
|
std::vector<std::string> depends;
|
|
|
|
std::string depMakeFile;
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// generate the build rule file
|
|
|
|
this->WriteObjectBuildFile(obj, lang, source, depends);
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// The object file should be checked for dependency integrity.
|
2006-05-12 20:29:09 +04:00
|
|
|
this->LocalGenerator->
|
|
|
|
CheckDependFiles[this->Target->GetName()][lang].insert(&source);
|
2006-02-14 18:36:04 +03:00
|
|
|
// add this to the list of objects for this local generator
|
|
|
|
if(cmSystemTools::FileIsFullPath(objNoTargetDir.c_str()))
|
|
|
|
{
|
|
|
|
objNoTargetDir = cmSystemTools::GetFilenameName(objNoTargetDir);
|
|
|
|
}
|
2006-05-12 20:29:09 +04:00
|
|
|
this->LocalGenerator->LocalObjectFiles[objNoTargetDir].
|
|
|
|
push_back(this->Target);
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
|
|
cmMakefileTargetGenerator
|
|
|
|
::WriteObjectBuildFile(std::string &obj,
|
2006-07-10 15:52:35 +04:00
|
|
|
const char *lang,
|
2006-02-14 18:36:04 +03:00
|
|
|
cmSourceFile& source,
|
|
|
|
std::vector<std::string>& depends)
|
|
|
|
{
|
2006-07-10 15:52:35 +04:00
|
|
|
this->LocalGenerator->AppendRuleDepend(depends,
|
2006-05-12 20:29:09 +04:00
|
|
|
this->FlagFileNameFull.c_str());
|
2006-02-14 18:36:04 +03:00
|
|
|
|
|
|
|
// generate the depend scanning rule
|
|
|
|
this->WriteObjectDependRules(source, depends);
|
|
|
|
|
|
|
|
std::string relativeObj = this->LocalGenerator->GetHomeRelativeOutputPath();
|
2006-07-07 00:04:49 +04:00
|
|
|
if ( source.GetPropertyAsBool("MACOSX_CONTENT") )
|
|
|
|
{
|
|
|
|
relativeObj = "";
|
|
|
|
}
|
2006-02-14 18:36:04 +03:00
|
|
|
relativeObj += obj;
|
|
|
|
if(this->Makefile->GetDefinition("CMAKE_WINDOWS_OBJECT_PATH"))
|
|
|
|
{
|
|
|
|
relativeObj = cmSystemTools::ConvertToOutputPath(relativeObj.c_str());
|
|
|
|
}
|
|
|
|
// Write the build rule.
|
2006-02-20 20:48:56 +03:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Build the set of compiler flags.
|
|
|
|
std::string flags;
|
2006-02-20 20:48:56 +03:00
|
|
|
|
|
|
|
// Add language-specific flags.
|
|
|
|
std::string langFlags = "$(";
|
|
|
|
langFlags += lang;
|
|
|
|
langFlags += "_FLAGS)";
|
|
|
|
this->LocalGenerator->AppendFlags(flags, langFlags.c_str());
|
|
|
|
|
|
|
|
// Add target-specific flags.
|
2006-02-14 18:36:04 +03:00
|
|
|
if(this->Target->GetProperty("COMPILE_FLAGS"))
|
|
|
|
{
|
2006-05-12 20:29:09 +04:00
|
|
|
this->LocalGenerator->AppendFlags
|
|
|
|
(flags, this->Target->GetProperty("COMPILE_FLAGS"));
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add flags from source file properties.
|
|
|
|
if (source.GetProperty("COMPILE_FLAGS"))
|
|
|
|
{
|
2006-05-12 20:29:09 +04:00
|
|
|
this->LocalGenerator->AppendFlags
|
|
|
|
(flags, source.GetProperty("COMPILE_FLAGS"));
|
2006-02-14 18:36:04 +03:00
|
|
|
*this->FlagFileStream << "# Custom flags: "
|
|
|
|
<< relativeObj << "_FLAGS = "
|
|
|
|
<< source.GetProperty("COMPILE_FLAGS")
|
|
|
|
<< "\n"
|
|
|
|
<< "\n";
|
|
|
|
}
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Get the output paths for source and object files.
|
|
|
|
std::string sourceFile = source.GetFullPath();
|
2006-03-15 19:02:08 +03:00
|
|
|
if(this->LocalGenerator->UseRelativePaths)
|
2006-02-14 18:36:04 +03:00
|
|
|
{
|
|
|
|
sourceFile = this->Convert(sourceFile.c_str(),
|
2006-05-12 20:29:09 +04:00
|
|
|
cmLocalGenerator::HOME_OUTPUT);
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
sourceFile = this->Convert(sourceFile.c_str(),
|
2006-05-12 20:29:09 +04:00
|
|
|
cmLocalGenerator::NONE,
|
|
|
|
cmLocalGenerator::SHELL);
|
|
|
|
std::string objectFile = this->Convert(obj.c_str(),
|
|
|
|
cmLocalGenerator::START_OUTPUT,
|
|
|
|
cmLocalGenerator::SHELL);
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Construct the build message.
|
|
|
|
std::vector<std::string> no_commands;
|
|
|
|
std::vector<std::string> commands;
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-05-23 17:11:46 +04:00
|
|
|
// add in a progress call if needed
|
2006-06-12 19:40:31 +04:00
|
|
|
std::string progressDir = this->Makefile->GetHomeOutputDirectory();
|
2006-06-14 20:28:32 +04:00
|
|
|
progressDir += cmake::GetCMakeFilesDirectory();
|
2006-06-12 19:40:31 +04:00
|
|
|
cmOStringStream progCmd;
|
|
|
|
progCmd << "$(CMAKE_COMMAND) -E cmake_progress_report ";
|
|
|
|
progCmd << this->LocalGenerator->Convert(progressDir.c_str(),
|
|
|
|
cmLocalGenerator::FULL,
|
|
|
|
cmLocalGenerator::SHELL);
|
2006-07-11 17:55:27 +04:00
|
|
|
this->NumberOfProgressActions++;
|
|
|
|
progCmd << " $(CMAKE_PROGRESS_"
|
|
|
|
<< this->NumberOfProgressActions
|
|
|
|
<< ")";
|
2006-06-12 19:40:31 +04:00
|
|
|
commands.push_back(progCmd.str());
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-06-12 20:18:59 +04:00
|
|
|
std::string buildEcho = "Building ";
|
|
|
|
buildEcho += lang;
|
|
|
|
buildEcho += " object ";
|
|
|
|
buildEcho += relativeObj;
|
|
|
|
this->LocalGenerator->AppendEcho(commands, buildEcho.c_str(),
|
|
|
|
cmLocalUnixMakefileGenerator3::EchoBuild);
|
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Construct the compile rules.
|
|
|
|
std::string compileRuleVar = "CMAKE_";
|
|
|
|
compileRuleVar += lang;
|
|
|
|
compileRuleVar += "_COMPILE_OBJECT";
|
|
|
|
std::string compileRule =
|
|
|
|
this->Makefile->GetRequiredDefinition(compileRuleVar.c_str());
|
|
|
|
cmSystemTools::ExpandListArgument(compileRule, commands);
|
2006-04-20 00:36:14 +04:00
|
|
|
|
|
|
|
std::string outpath = this->Makefile->GetStartOutputDirectory();
|
|
|
|
outpath += "/";
|
|
|
|
outpath += this->Target->GetName();
|
|
|
|
outpath += ".pdb";
|
|
|
|
outpath = this->Convert(outpath.c_str(), cmLocalGenerator::FULL,
|
|
|
|
cmLocalGenerator::MAKEFILE);
|
2006-02-20 21:42:18 +03:00
|
|
|
cmLocalGenerator::RuleVariables vars;
|
|
|
|
vars.Language = lang;
|
2006-04-20 00:36:14 +04:00
|
|
|
vars.TargetPDB = outpath.c_str();
|
2006-02-20 21:42:18 +03:00
|
|
|
vars.Source = sourceFile.c_str();
|
|
|
|
vars.Object = relativeObj.c_str();
|
2006-04-17 22:01:22 +04:00
|
|
|
std::string objdir = this->LocalGenerator->GetHomeRelativeOutputPath();
|
2006-07-10 15:52:35 +04:00
|
|
|
objdir = this->Convert(objdir.c_str(),
|
2006-04-17 22:01:22 +04:00
|
|
|
cmLocalGenerator::START_OUTPUT,
|
|
|
|
cmLocalGenerator::SHELL);
|
|
|
|
std::string objectDir = cmSystemTools::GetFilenamePath(obj);
|
|
|
|
vars.ObjectDir = objectDir.c_str();
|
2006-02-20 21:42:18 +03:00
|
|
|
vars.Flags = flags.c_str();
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Expand placeholders in the commands.
|
|
|
|
for(std::vector<std::string>::iterator i = commands.begin();
|
|
|
|
i != commands.end(); ++i)
|
|
|
|
{
|
2006-02-20 21:42:18 +03:00
|
|
|
this->LocalGenerator->ExpandRuleVariables(*i, vars);
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make the target dependency scanning rule include cmake-time-known
|
|
|
|
// dependencies. The others are handled by the check-build-system
|
|
|
|
// path.
|
2006-07-10 15:52:35 +04:00
|
|
|
std::string depMark =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->GetRelativeTargetDirectory(*this->Target);
|
|
|
|
depMark += "/depend.make.mark";
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
2006-02-16 00:35:16 +03:00
|
|
|
depMark.c_str(),
|
|
|
|
depends, no_commands, false);
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Write the rule.
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
2006-02-16 00:35:16 +03:00
|
|
|
relativeObj.c_str(),
|
|
|
|
depends, commands, false);
|
2006-02-14 18:36:04 +03:00
|
|
|
|
2006-08-08 07:25:21 +04:00
|
|
|
bool do_preprocess_rules =
|
|
|
|
this->LocalGenerator->GetCreatePreprocessedSourceRules();
|
|
|
|
bool do_assembly_rules =
|
|
|
|
this->LocalGenerator->GetCreateAssemblySourceRules();
|
|
|
|
if(do_preprocess_rules || do_assembly_rules)
|
|
|
|
{
|
|
|
|
std::vector<std::string> force_depends;
|
|
|
|
force_depends.push_back("cmake_force");
|
|
|
|
std::string::size_type dot_pos = relativeObj.rfind(".");
|
|
|
|
std::string relativeObjBase = relativeObj.substr(0, dot_pos);
|
|
|
|
|
|
|
|
if(do_preprocess_rules)
|
|
|
|
{
|
|
|
|
commands.clear();
|
2006-08-09 19:43:35 +04:00
|
|
|
std::string relativeObjI = relativeObjBase + ".i";
|
2006-08-08 07:25:21 +04:00
|
|
|
|
|
|
|
std::string preprocessEcho = "Preprocessing ";
|
|
|
|
preprocessEcho += lang;
|
|
|
|
preprocessEcho += " source to ";
|
2006-08-09 19:43:35 +04:00
|
|
|
preprocessEcho += relativeObjI;
|
2006-08-08 07:25:21 +04:00
|
|
|
this->LocalGenerator->AppendEcho(
|
|
|
|
commands, preprocessEcho.c_str(),
|
|
|
|
cmLocalUnixMakefileGenerator3::EchoBuild
|
|
|
|
);
|
|
|
|
|
|
|
|
std::string preprocessRuleVar = "CMAKE_";
|
|
|
|
preprocessRuleVar += lang;
|
|
|
|
preprocessRuleVar += "_CREATE_PREPROCESSED_SOURCE";
|
|
|
|
if(const char* preprocessRule =
|
|
|
|
this->Makefile->GetDefinition(preprocessRuleVar.c_str()))
|
|
|
|
{
|
|
|
|
cmSystemTools::ExpandListArgument(preprocessRule, commands);
|
|
|
|
|
2006-08-09 19:43:35 +04:00
|
|
|
vars.PreprocessedSource = relativeObjI.c_str();
|
2006-08-08 07:25:21 +04:00
|
|
|
|
|
|
|
// Expand placeholders in the commands.
|
|
|
|
for(std::vector<std::string>::iterator i = commands.begin();
|
|
|
|
i != commands.end(); ++i)
|
|
|
|
{
|
|
|
|
this->LocalGenerator->ExpandRuleVariables(*i, vars);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string cmd = "$(CMAKE_COMMAND) -E cmake_unimplemented_variable ";
|
|
|
|
cmd += preprocessRuleVar;
|
|
|
|
commands.push_back(cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
2006-08-09 19:43:35 +04:00
|
|
|
relativeObjI.c_str(),
|
2006-08-08 07:25:21 +04:00
|
|
|
force_depends, commands, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(do_assembly_rules)
|
|
|
|
{
|
|
|
|
commands.clear();
|
2006-08-09 19:43:35 +04:00
|
|
|
std::string relativeObjS = relativeObjBase + ".s";
|
2006-08-08 07:25:21 +04:00
|
|
|
|
|
|
|
std::string assemblyEcho = "Compiling ";
|
|
|
|
assemblyEcho += lang;
|
|
|
|
assemblyEcho += " source to assembly ";
|
|
|
|
assemblyEcho += relativeObjS;
|
|
|
|
this->LocalGenerator->AppendEcho(
|
|
|
|
commands, assemblyEcho.c_str(),
|
|
|
|
cmLocalUnixMakefileGenerator3::EchoBuild
|
|
|
|
);
|
|
|
|
|
|
|
|
std::string assemblyRuleVar = "CMAKE_";
|
|
|
|
assemblyRuleVar += lang;
|
|
|
|
assemblyRuleVar += "_CREATE_ASSEMBLY_SOURCE";
|
|
|
|
if(const char* assemblyRule =
|
|
|
|
this->Makefile->GetDefinition(assemblyRuleVar.c_str()))
|
|
|
|
{
|
|
|
|
cmSystemTools::ExpandListArgument(assemblyRule, commands);
|
|
|
|
|
|
|
|
vars.AssemblySource = relativeObjS.c_str();
|
|
|
|
|
|
|
|
// Expand placeholders in the commands.
|
|
|
|
for(std::vector<std::string>::iterator i = commands.begin();
|
|
|
|
i != commands.end(); ++i)
|
|
|
|
{
|
|
|
|
this->LocalGenerator->ExpandRuleVariables(*i, vars);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string cmd = "$(CMAKE_COMMAND) -E cmake_unimplemented_variable ";
|
|
|
|
cmd += assemblyRuleVar;
|
|
|
|
commands.push_back(cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
|
|
|
relativeObjS.c_str(),
|
|
|
|
force_depends, commands, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// If the language needs provides-requires mode, create the
|
|
|
|
// corresponding targets.
|
|
|
|
std::string objectRequires = relativeObj;
|
|
|
|
objectRequires += ".requires";
|
|
|
|
std::vector<std::string> p_depends;
|
|
|
|
// always provide an empty requires target
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
2006-07-10 15:52:35 +04:00
|
|
|
objectRequires.c_str(), p_depends,
|
2006-02-16 00:35:16 +03:00
|
|
|
no_commands, true);
|
2006-02-14 18:36:04 +03:00
|
|
|
|
|
|
|
// write a build rule to recursively build what this obj provides
|
|
|
|
std::string objectProvides = relativeObj;
|
|
|
|
objectProvides += ".provides";
|
|
|
|
std::string temp = relativeObj;
|
|
|
|
temp += ".provides.build";
|
|
|
|
std::vector<std::string> r_commands;
|
2006-07-10 15:52:35 +04:00
|
|
|
std::string tgtMakefileName =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->GetRelativeTargetDirectory(*this->Target);
|
|
|
|
tgtMakefileName += "/build.make";
|
|
|
|
r_commands.push_back
|
2006-05-12 20:29:09 +04:00
|
|
|
(this->LocalGenerator->GetRecursiveMakeCall(tgtMakefileName.c_str(),
|
|
|
|
temp.c_str()));
|
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
p_depends.clear();
|
|
|
|
p_depends.push_back(objectRequires);
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
2006-07-10 15:52:35 +04:00
|
|
|
objectProvides.c_str(), p_depends,
|
2006-02-16 00:35:16 +03:00
|
|
|
r_commands, true);
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// write the provides.build rule dependency on the obj file
|
|
|
|
p_depends.clear();
|
|
|
|
p_depends.push_back(relativeObj);
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
2006-02-16 00:35:16 +03:00
|
|
|
temp.c_str(), p_depends, no_commands,
|
|
|
|
true);
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator::WriteTargetRequiresRules()
|
|
|
|
{
|
|
|
|
std::vector<std::string> depends;
|
|
|
|
std::vector<std::string> no_commands;
|
|
|
|
|
|
|
|
// Construct the name of the dependency generation target.
|
2006-07-10 15:52:35 +04:00
|
|
|
std::string depTarget =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->GetRelativeTargetDirectory(*this->Target);
|
|
|
|
depTarget += "/requires";
|
|
|
|
|
|
|
|
// This target drives dependency generation for all object files.
|
|
|
|
std::string relPath = this->LocalGenerator->GetHomeRelativeOutputPath();
|
|
|
|
std::string objTarget;
|
|
|
|
for(std::vector<std::string>::const_iterator obj = this->Objects.begin();
|
|
|
|
obj != this->Objects.end(); ++obj)
|
|
|
|
{
|
|
|
|
objTarget = relPath;
|
|
|
|
objTarget += *obj;
|
|
|
|
objTarget += ".requires";
|
|
|
|
depends.push_back(objTarget);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the rule.
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
2006-02-16 00:35:16 +03:00
|
|
|
depTarget.c_str(),
|
|
|
|
depends, no_commands, true);
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator::WriteTargetCleanRules()
|
|
|
|
{
|
|
|
|
std::vector<std::string> depends;
|
|
|
|
std::vector<std::string> commands;
|
|
|
|
|
|
|
|
// Construct the clean target name.
|
2006-07-10 15:52:35 +04:00
|
|
|
std::string cleanTarget =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->GetRelativeTargetDirectory(*this->Target);
|
|
|
|
cleanTarget += "/clean";
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Construct the clean command.
|
2006-03-09 22:30:35 +03:00
|
|
|
this->LocalGenerator->AppendCleanCommand(commands, this->CleanFiles,
|
|
|
|
*this->Target);
|
2006-05-12 20:29:09 +04:00
|
|
|
this->LocalGenerator->CreateCDCommand
|
|
|
|
(commands,
|
|
|
|
this->Makefile->GetStartOutputDirectory(),
|
|
|
|
this->Makefile->GetHomeOutputDirectory());
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Write the rule.
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
2006-02-16 00:35:16 +03:00
|
|
|
cleanTarget.c_str(),
|
|
|
|
depends, commands, true);
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator::WriteTargetDependRules()
|
|
|
|
{
|
|
|
|
// must write the targets depend info file
|
|
|
|
std::string dir = this->LocalGenerator->GetTargetDirectory(*this->Target);
|
|
|
|
this->InfoFileNameFull = dir;
|
|
|
|
this->InfoFileNameFull += "/DependInfo.cmake";
|
2006-07-10 15:52:35 +04:00
|
|
|
this->InfoFileNameFull =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->ConvertToFullPath(this->InfoFileNameFull);
|
2006-07-10 15:52:35 +04:00
|
|
|
this->InfoFileStream =
|
2006-02-14 18:36:04 +03:00
|
|
|
new cmGeneratedFileStream(this->InfoFileNameFull.c_str());
|
|
|
|
this->InfoFileStream->SetCopyIfDifferent(true);
|
|
|
|
if(!*this->InfoFileStream)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this->LocalGenerator->
|
|
|
|
WriteDependLanguageInfo(*this->InfoFileStream,*this->Target);
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// and now write the rule to use it
|
|
|
|
std::vector<std::string> depends;
|
|
|
|
std::vector<std::string> commands;
|
|
|
|
|
|
|
|
// Construct the name of the dependency generation target.
|
2006-07-10 15:52:35 +04:00
|
|
|
std::string depTarget =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->GetRelativeTargetDirectory(*this->Target);
|
|
|
|
depTarget += "/depend";
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
std::string depMark = depTarget;
|
|
|
|
depMark += ".make.mark";
|
|
|
|
depends.push_back(depMark);
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
2006-02-16 00:35:16 +03:00
|
|
|
depTarget.c_str(),
|
|
|
|
depends, commands, true);
|
2006-02-14 18:36:04 +03:00
|
|
|
depends.clear();
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Write the dependency generation rule.
|
|
|
|
std::string depEcho = "Scanning dependencies of target ";
|
|
|
|
depEcho += this->Target->GetName();
|
2006-03-31 00:39:03 +04:00
|
|
|
this->LocalGenerator->AppendEcho(commands, depEcho.c_str(),
|
|
|
|
cmLocalUnixMakefileGenerator3::EchoDepend);
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Add a command to call CMake to scan dependencies. CMake will
|
|
|
|
// touch the corresponding depends file after scanning dependencies.
|
|
|
|
cmOStringStream depCmd;
|
|
|
|
// TODO: Account for source file properties and directory-level
|
|
|
|
// definitions when scanning for dependencies.
|
2006-04-27 18:41:37 +04:00
|
|
|
#if !defined(_WIN32) || defined(__CYGWIN__)
|
|
|
|
// This platform supports symlinks, so cmSystemTools will translate
|
|
|
|
// paths. Make sure PWD is set to the original name of the home
|
|
|
|
// output directory to help cmSystemTools to create the same
|
|
|
|
// translation table for the dependency scanning process.
|
|
|
|
depCmd << "cd "
|
|
|
|
<< (this->LocalGenerator->Convert(
|
|
|
|
this->Makefile->GetHomeOutputDirectory(),
|
|
|
|
cmLocalGenerator::FULL, cmLocalGenerator::SHELL))
|
|
|
|
<< " && ";
|
|
|
|
#endif
|
2006-05-25 17:47:30 +04:00
|
|
|
// Generate a call this signature:
|
|
|
|
//
|
|
|
|
// cmake -E cmake_depends <generator>
|
|
|
|
// <home-src-dir> <start-src-dir>
|
|
|
|
// <home-out-dir> <start-out-dir>
|
|
|
|
// <dep-info>
|
|
|
|
//
|
|
|
|
// This gives the dependency scanner enough information to recreate
|
|
|
|
// the state of our local generator sufficiently for its needs.
|
|
|
|
depCmd << "$(CMAKE_COMMAND) -E cmake_depends \""
|
2006-02-14 18:36:04 +03:00
|
|
|
<< this->GlobalGenerator->GetName() << "\" "
|
2006-05-25 17:47:30 +04:00
|
|
|
<< this->Convert(this->Makefile->GetHomeDirectory(),
|
|
|
|
cmLocalGenerator::FULL, cmLocalGenerator::SHELL)
|
2006-02-14 18:36:04 +03:00
|
|
|
<< " "
|
2006-05-25 17:47:30 +04:00
|
|
|
<< this->Convert(this->Makefile->GetStartDirectory(),
|
|
|
|
cmLocalGenerator::FULL, cmLocalGenerator::SHELL)
|
|
|
|
<< " "
|
|
|
|
<< this->Convert(this->Makefile->GetHomeOutputDirectory(),
|
|
|
|
cmLocalGenerator::FULL, cmLocalGenerator::SHELL)
|
|
|
|
<< " "
|
|
|
|
<< this->Convert(this->Makefile->GetStartOutputDirectory(),
|
|
|
|
cmLocalGenerator::FULL, cmLocalGenerator::SHELL)
|
2006-02-14 18:36:04 +03:00
|
|
|
<< " "
|
|
|
|
<< this->Convert(this->InfoFileNameFull.c_str(),
|
2006-05-25 17:47:30 +04:00
|
|
|
cmLocalGenerator::FULL, cmLocalGenerator::SHELL);
|
2006-02-14 18:36:04 +03:00
|
|
|
commands.push_back(depCmd.str());
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Write the rule.
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
2006-02-16 00:35:16 +03:00
|
|
|
depMark.c_str(),
|
|
|
|
depends, commands, false);
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator
|
|
|
|
::WriteObjectDependRules(cmSourceFile& source,
|
|
|
|
std::vector<std::string>& depends)
|
|
|
|
{
|
|
|
|
// Create the list of dependencies known at cmake time. These are
|
|
|
|
// shared between the object file and dependency scanning rule.
|
|
|
|
depends.push_back(source.GetFullPath());
|
|
|
|
if(const char* objectDeps = source.GetProperty("OBJECT_DEPENDS"))
|
|
|
|
{
|
|
|
|
std::vector<std::string> deps;
|
|
|
|
cmSystemTools::ExpandListArgument(objectDeps, deps);
|
|
|
|
for(std::vector<std::string>::iterator i = deps.begin();
|
|
|
|
i != deps.end(); ++i)
|
|
|
|
{
|
|
|
|
depends.push_back(i->c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator
|
|
|
|
::GenerateCustomRuleFile(const cmCustomCommand& cc)
|
|
|
|
{
|
|
|
|
// Collect the commands.
|
|
|
|
std::vector<std::string> commands;
|
2006-04-11 19:06:19 +04:00
|
|
|
std::string comment = this->LocalGenerator->ConstructComment(cc);
|
|
|
|
if(!comment.empty())
|
|
|
|
{
|
2006-07-13 22:03:56 +04:00
|
|
|
// add in a progress call if needed
|
|
|
|
std::string progressDir = this->Makefile->GetHomeOutputDirectory();
|
|
|
|
progressDir += cmake::GetCMakeFilesDirectory();
|
|
|
|
cmOStringStream progCmd;
|
|
|
|
progCmd << "$(CMAKE_COMMAND) -E cmake_progress_report ";
|
|
|
|
progCmd << this->LocalGenerator->Convert(progressDir.c_str(),
|
|
|
|
cmLocalGenerator::FULL,
|
|
|
|
cmLocalGenerator::SHELL);
|
|
|
|
this->NumberOfProgressActions++;
|
|
|
|
progCmd << " $(CMAKE_PROGRESS_"
|
|
|
|
<< this->NumberOfProgressActions
|
|
|
|
<< ")";
|
|
|
|
commands.push_back(progCmd.str());
|
2006-04-11 19:06:19 +04:00
|
|
|
this->LocalGenerator
|
|
|
|
->AppendEcho(commands, comment.c_str(),
|
|
|
|
cmLocalUnixMakefileGenerator3::EchoGenerate);
|
|
|
|
}
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->AppendCustomCommand(commands, cc);
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-02-14 18:36:04 +03:00
|
|
|
// Collect the dependencies.
|
|
|
|
std::vector<std::string> depends;
|
|
|
|
this->LocalGenerator->AppendCustomDepend(depends, cc);
|
|
|
|
|
|
|
|
// Write the rule.
|
2006-04-11 19:06:19 +04:00
|
|
|
const std::vector<std::string>& outputs = cc.GetOutputs();
|
|
|
|
std::vector<std::string>::const_iterator o = outputs.begin();
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
|
|
|
o->c_str(), depends, commands,
|
|
|
|
false);
|
|
|
|
|
|
|
|
// If the rule has multiple outputs, add a rule for the extra
|
|
|
|
// outputs to just depend on the first output with no command. Also
|
|
|
|
// register the extra outputs as paired with the first output so
|
|
|
|
// that the check-build-system step will remove the primary output
|
|
|
|
// if any extra outputs are missing, forcing the rule to regenerate
|
|
|
|
// all outputs.
|
|
|
|
depends.clear();
|
|
|
|
depends.push_back(*o);
|
|
|
|
commands.clear();
|
|
|
|
cmGlobalUnixMakefileGenerator3* gg =
|
|
|
|
static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
|
2006-04-13 18:15:48 +04:00
|
|
|
std::string emptyCommand = gg->GetEmptyCommandHack();
|
|
|
|
if(!emptyCommand.empty())
|
|
|
|
{
|
|
|
|
commands.push_back(emptyCommand);
|
|
|
|
}
|
2006-04-11 19:06:19 +04:00
|
|
|
for(++o; o != outputs.end(); ++o)
|
2006-02-14 18:36:04 +03:00
|
|
|
{
|
2006-04-11 19:06:19 +04:00
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
|
|
|
o->c_str(), depends, commands,
|
|
|
|
false);
|
|
|
|
gg->AddMultipleOutputPair(o->c_str(), depends[0].c_str());
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
|
|
cmMakefileTargetGenerator
|
|
|
|
::WriteObjectsVariable(std::string& variableName,
|
|
|
|
std::string& variableNameExternal)
|
|
|
|
{
|
|
|
|
// Write a make variable assignment that lists all objects for the
|
|
|
|
// target.
|
2006-07-10 15:52:35 +04:00
|
|
|
variableName =
|
|
|
|
this->LocalGenerator->CreateMakeVariable(this->Target->GetName(),
|
2006-05-12 20:29:09 +04:00
|
|
|
"_OBJECTS");
|
2006-02-14 18:36:04 +03:00
|
|
|
*this->BuildFileStream
|
|
|
|
<< "# Object files for target " << this->Target->GetName() << "\n"
|
|
|
|
<< variableName.c_str() << " =";
|
|
|
|
std::string object;
|
2006-07-10 15:52:35 +04:00
|
|
|
const char* objName =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->Makefile->GetDefinition("CMAKE_NO_QUOTED_OBJECTS");
|
2006-07-10 15:52:35 +04:00
|
|
|
const char* lineContinue =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->Makefile->GetDefinition("CMAKE_MAKE_LINE_CONTINUE");
|
|
|
|
if(!lineContinue)
|
|
|
|
{
|
|
|
|
lineContinue = "\\";
|
|
|
|
}
|
|
|
|
for(std::vector<std::string>::const_iterator i = this->Objects.begin();
|
|
|
|
i != this->Objects.end(); ++i)
|
|
|
|
{
|
2006-03-28 17:54:01 +04:00
|
|
|
if ( this->ExtraContent.find(i->c_str()) != this->ExtraContent.end() )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2006-02-14 18:36:04 +03:00
|
|
|
*this->BuildFileStream << " " << lineContinue << "\n";
|
|
|
|
if(objName)
|
|
|
|
{
|
2006-07-10 15:52:35 +04:00
|
|
|
*this->BuildFileStream <<
|
|
|
|
this->Convert(i->c_str(), cmLocalGenerator::START_OUTPUT,
|
2006-02-14 18:36:04 +03:00
|
|
|
cmLocalGenerator::MAKEFILE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-07-10 15:52:35 +04:00
|
|
|
*this->BuildFileStream <<
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->ConvertToQuotedOutputPath(i->c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*this->BuildFileStream << "\n";
|
|
|
|
|
|
|
|
// Write a make variable assignment that lists all external objects
|
|
|
|
// for the target.
|
2006-07-10 15:52:35 +04:00
|
|
|
variableNameExternal =
|
2006-05-12 20:29:09 +04:00
|
|
|
this->LocalGenerator->CreateMakeVariable(this->Target->GetName(),
|
|
|
|
"_EXTERNAL_OBJECTS");
|
2006-02-14 18:36:04 +03:00
|
|
|
*this->BuildFileStream
|
|
|
|
<< "\n"
|
2006-07-10 15:52:35 +04:00
|
|
|
<< "# External object files for target "
|
2006-05-12 20:29:09 +04:00
|
|
|
<< this->Target->GetName() << "\n"
|
2006-02-14 18:36:04 +03:00
|
|
|
<< variableNameExternal.c_str() << " =";
|
2006-07-10 15:52:35 +04:00
|
|
|
for(std::vector<std::string>::const_iterator i =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->ExternalObjects.begin();
|
|
|
|
i != this->ExternalObjects.end(); ++i)
|
|
|
|
{
|
|
|
|
object = this->Convert(i->c_str(),cmLocalGenerator::START_OUTPUT);
|
|
|
|
*this->BuildFileStream
|
|
|
|
<< " " << lineContinue << "\n"
|
|
|
|
<< this->Makefile->GetSafeDefinition("CMAKE_OBJECT_NAME");
|
|
|
|
if(objName)
|
|
|
|
{
|
2006-07-10 15:52:35 +04:00
|
|
|
*this->BuildFileStream <<
|
|
|
|
this->Convert(i->c_str(), cmLocalGenerator::START_OUTPUT,
|
2006-05-12 20:29:09 +04:00
|
|
|
cmLocalGenerator::MAKEFILE);
|
2006-02-14 18:36:04 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-07-10 15:52:35 +04:00
|
|
|
*this->BuildFileStream <<
|
2006-02-14 18:36:04 +03:00
|
|
|
this->LocalGenerator->ConvertToQuotedOutputPath(i->c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*this->BuildFileStream << "\n" << "\n";
|
|
|
|
}
|
|
|
|
|
2006-06-16 00:17:11 +04:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
|
|
cmMakefileTargetGenerator
|
|
|
|
::WriteObjectsString(std::string& buildObjs)
|
|
|
|
{
|
|
|
|
std::string object;
|
|
|
|
const char* no_quoted =
|
|
|
|
this->Makefile->GetDefinition("CMAKE_NO_QUOTED_OBJECTS");
|
|
|
|
const char* space = "";
|
|
|
|
for(std::vector<std::string>::const_iterator i = this->Objects.begin();
|
|
|
|
i != this->Objects.end(); ++i)
|
|
|
|
{
|
|
|
|
if ( this->ExtraContent.find(i->c_str()) != this->ExtraContent.end() )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
buildObjs += space;
|
|
|
|
space = " ";
|
|
|
|
if(no_quoted)
|
|
|
|
{
|
|
|
|
buildObjs +=
|
|
|
|
this->Convert(i->c_str(), cmLocalGenerator::START_OUTPUT,
|
|
|
|
cmLocalGenerator::SHELL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buildObjs +=
|
|
|
|
this->LocalGenerator->ConvertToQuotedOutputPath(i->c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(std::vector<std::string>::const_iterator i =
|
|
|
|
this->ExternalObjects.begin();
|
|
|
|
i != this->ExternalObjects.end(); ++i)
|
|
|
|
{
|
|
|
|
buildObjs += space;
|
|
|
|
space = " ";
|
|
|
|
if(no_quoted)
|
|
|
|
{
|
|
|
|
buildObjs +=
|
|
|
|
this->Convert(i->c_str(), cmLocalGenerator::START_OUTPUT,
|
|
|
|
cmLocalGenerator::SHELL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buildObjs +=
|
|
|
|
this->LocalGenerator->ConvertToQuotedOutputPath(i->c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-06-01 19:45:51 +04:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator::WriteTargetDriverRule(const char* main_output,
|
|
|
|
bool relink)
|
|
|
|
{
|
|
|
|
// Compute the name of the driver target.
|
|
|
|
std::string dir = this->Makefile->GetStartOutputDirectory();
|
|
|
|
dir += "/";
|
|
|
|
dir += this->LocalGenerator->GetTargetDirectory(*this->Target);
|
|
|
|
std::string buildTargetRuleName = dir;
|
|
|
|
buildTargetRuleName += relink?"/preinstall":"/build";
|
|
|
|
buildTargetRuleName = this->Convert(buildTargetRuleName.c_str(),
|
|
|
|
cmLocalGenerator::HOME_OUTPUT,
|
|
|
|
cmLocalGenerator::MAKEFILE);
|
|
|
|
|
|
|
|
// Build the list of target outputs to drive.
|
|
|
|
std::vector<std::string> depends;
|
|
|
|
if(main_output)
|
|
|
|
{
|
|
|
|
depends.push_back(main_output);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* comment = 0;
|
|
|
|
if(relink)
|
|
|
|
{
|
|
|
|
// Setup the comment for the preinstall driver.
|
|
|
|
comment = "Rule to relink during preinstall.";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Setup the comment for the main build driver.
|
|
|
|
comment = "Rule to build all files generated by this target.";
|
|
|
|
|
|
|
|
// Make sure all custom command outputs in this target are built.
|
2006-07-10 15:52:35 +04:00
|
|
|
const std::vector<cmSourceFile*>& sources =
|
2006-06-05 22:32:12 +04:00
|
|
|
this->Target->GetSourceFiles();
|
2006-06-01 19:45:51 +04:00
|
|
|
for(std::vector<cmSourceFile*>::const_iterator source = sources.begin();
|
|
|
|
source != sources.end(); ++source)
|
|
|
|
{
|
|
|
|
if(cmCustomCommand* cc = (*source)->GetCustomCommand())
|
|
|
|
{
|
|
|
|
const std::vector<std::string>& outputs = cc->GetOutputs();
|
|
|
|
for(std::vector<std::string>::const_iterator o = outputs.begin();
|
|
|
|
o != outputs.end(); ++o)
|
|
|
|
{
|
|
|
|
depends.push_back(*o);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the driver rule.
|
|
|
|
std::vector<std::string> no_commands;
|
|
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, comment,
|
|
|
|
buildTargetRuleName.c_str(),
|
|
|
|
depends, no_commands, true);
|
|
|
|
}
|
2006-02-14 18:36:04 +03:00
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
std::string cmMakefileTargetGenerator::GetFrameworkFlags()
|
|
|
|
{
|
|
|
|
#ifndef __APPLE__
|
|
|
|
return std::string();
|
|
|
|
#else
|
|
|
|
std::set<cmStdString> emitted;
|
|
|
|
std::vector<std::string> includes;
|
2006-02-15 20:30:01 +03:00
|
|
|
this->LocalGenerator->GetIncludeDirectories(includes);
|
2006-02-14 18:36:04 +03:00
|
|
|
std::vector<std::string>::iterator i;
|
|
|
|
// check all include directories for frameworks as this
|
|
|
|
// will already have added a -F for the framework
|
|
|
|
for(i = includes.begin(); i != includes.end(); ++i)
|
|
|
|
{
|
|
|
|
if(cmSystemTools::IsPathToFramework(i->c_str()))
|
|
|
|
{
|
|
|
|
std::string frameworkDir = *i;
|
|
|
|
frameworkDir += "/../";
|
|
|
|
frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir.c_str());
|
|
|
|
emitted.insert(frameworkDir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string flags;
|
|
|
|
std::vector<std::string>& frameworks = this->Target->GetFrameworks();
|
|
|
|
for(i = frameworks.begin();
|
|
|
|
i != frameworks.end(); ++i)
|
|
|
|
{
|
|
|
|
if(emitted.insert(*i).second)
|
|
|
|
{
|
|
|
|
flags += "-F";
|
|
|
|
flags += this->LocalGenerator->ConvertToOutputForExisting(i->c_str());
|
|
|
|
flags += " ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return flags;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator
|
|
|
|
::AppendTargetDepends(std::vector<std::string>& depends)
|
|
|
|
{
|
|
|
|
// Static libraries never depend on anything for linking.
|
|
|
|
if(this->Target->GetType() == cmTarget::STATIC_LIBRARY)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep track of dependencies already listed.
|
|
|
|
std::set<cmStdString> emitted;
|
|
|
|
|
|
|
|
// A target should not depend on itself.
|
|
|
|
emitted.insert(this->Target->GetName());
|
|
|
|
|
|
|
|
// Loop over all library dependencies.
|
2006-07-10 15:52:35 +04:00
|
|
|
const cmTarget::LinkLibraryVectorType& tlibs =
|
2006-03-15 19:02:08 +03:00
|
|
|
this->Target->GetLinkLibraries();
|
|
|
|
for(cmTarget::LinkLibraryVectorType::const_iterator lib = tlibs.begin();
|
2006-02-14 18:36:04 +03:00
|
|
|
lib != tlibs.end(); ++lib)
|
|
|
|
{
|
|
|
|
// Don't emit the same library twice for this target.
|
|
|
|
if(emitted.insert(lib->first).second)
|
|
|
|
{
|
|
|
|
// Depend only on other CMake targets.
|
2006-07-10 15:52:35 +04:00
|
|
|
if(cmTarget* tgt =
|
2006-02-14 18:36:04 +03:00
|
|
|
this->GlobalGenerator->FindTarget(0, lib->first.c_str()))
|
|
|
|
{
|
|
|
|
if(const char* location =
|
2006-03-15 19:02:08 +03:00
|
|
|
tgt->GetLocation(this->LocalGenerator->ConfigurationName.c_str()))
|
2006-02-14 18:36:04 +03:00
|
|
|
{
|
|
|
|
depends.push_back(location);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void cmMakefileTargetGenerator
|
|
|
|
::CloseFileStreams()
|
|
|
|
{
|
|
|
|
delete this->BuildFileStream;
|
|
|
|
delete this->InfoFileStream;
|
|
|
|
delete this->FlagFileStream;
|
|
|
|
}
|
2006-03-14 22:03:16 +03:00
|
|
|
|
|
|
|
void cmMakefileTargetGenerator::RemoveForbiddenFlags(const char* flagVar,
|
2006-07-10 15:52:35 +04:00
|
|
|
const char* linkLang,
|
2006-03-14 22:03:16 +03:00
|
|
|
std::string& linkFlags)
|
|
|
|
{
|
|
|
|
// check for language flags that are not allowed at link time, and
|
|
|
|
// remove them, -w on darwin for gcc -w -dynamiclib sends -w to libtool
|
|
|
|
// which fails, there may be more]
|
2006-07-10 15:52:35 +04:00
|
|
|
|
2006-03-14 22:03:16 +03:00
|
|
|
std::string removeFlags = "CMAKE_";
|
|
|
|
removeFlags += linkLang;
|
|
|
|
removeFlags += flagVar;
|
2006-07-10 15:52:35 +04:00
|
|
|
std::string removeflags =
|
2006-03-14 22:03:16 +03:00
|
|
|
this->Makefile->GetSafeDefinition(removeFlags.c_str());
|
|
|
|
std::vector<std::string> removeList;
|
|
|
|
cmSystemTools::ExpandListArgument(removeflags, removeList);
|
|
|
|
for(std::vector<std::string>::iterator i = removeList.begin();
|
|
|
|
i != removeList.end(); ++i)
|
|
|
|
{
|
|
|
|
cmSystemTools::ReplaceString(linkFlags, i->c_str(), "");
|
|
|
|
}
|
|
|
|
}
|
2006-07-11 17:55:27 +04:00
|
|
|
|
|
|
|
void cmMakefileTargetGenerator::WriteProgressVariables(unsigned long total,
|
|
|
|
unsigned long ¤t)
|
|
|
|
{
|
2006-07-12 22:15:06 +04:00
|
|
|
cmGeneratedFileStream *progressFileStream =
|
|
|
|
new cmGeneratedFileStream(this->ProgressFileNameFull.c_str());
|
|
|
|
if(!progressFileStream)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-07-11 17:55:27 +04:00
|
|
|
unsigned long num;
|
|
|
|
unsigned long i;
|
|
|
|
for (i = 1; i <= this->NumberOfProgressActions; ++i)
|
|
|
|
{
|
2006-07-12 22:15:06 +04:00
|
|
|
*progressFileStream
|
2006-07-11 17:55:27 +04:00
|
|
|
<< "CMAKE_PROGRESS_" << i << " = ";
|
|
|
|
if (total <= 100)
|
|
|
|
{
|
|
|
|
num = i + current;
|
2006-07-12 22:15:06 +04:00
|
|
|
*progressFileStream << num;
|
2006-07-11 17:55:27 +04:00
|
|
|
this->LocalGenerator->ProgressFiles[this->Target->GetName()]
|
|
|
|
.push_back(num);
|
|
|
|
}
|
|
|
|
else if (((i+current)*100)/total > ((i-1+current)*100)/total)
|
|
|
|
{
|
|
|
|
num = ((i+current)*100)/total;
|
2006-07-12 22:15:06 +04:00
|
|
|
*progressFileStream << num;
|
2006-07-11 17:55:27 +04:00
|
|
|
this->LocalGenerator->ProgressFiles[this->Target->GetName()]
|
|
|
|
.push_back(num);
|
|
|
|
}
|
2006-07-12 22:15:06 +04:00
|
|
|
*progressFileStream << "\n";
|
2006-07-11 17:55:27 +04:00
|
|
|
}
|
|
|
|
current += this->NumberOfProgressActions;
|
2006-07-12 22:15:06 +04:00
|
|
|
delete progressFileStream;
|
2006-07-11 17:55:27 +04:00
|
|
|
}
|
|
|
|
|