diff options
Diffstat (limited to 'Source/cmLocalGenerator.cxx')
-rw-r--r-- | Source/cmLocalGenerator.cxx | 3278 |
1 files changed, 3278 insertions, 0 deletions
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx new file mode 100644 index 000000000..0cfb36bc3 --- /dev/null +++ b/Source/cmLocalGenerator.cxx @@ -0,0 +1,3278 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + 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 "cmLocalGenerator.h" + +#include "cmComputeLinkInformation.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmInstallGenerator.h" +#include "cmInstallFilesGenerator.h" +#include "cmInstallScriptGenerator.h" +#include "cmInstallTargetGenerator.h" +#include "cmMakefile.h" +#include "cmSourceFile.h" +#include "cmTest.h" +#include "cmTestGenerator.h" +#include "cmVersion.h" +#include "cmake.h" + +#if defined(CMAKE_BUILD_WITH_CMAKE) +# define CM_LG_ENCODE_OBJECT_NAMES +# include <cmsys/MD5.h> +#endif + +#include <cmsys/System.h> + +#include <ctype.h> // for isalpha + +#include <assert.h> + +#if defined(__HAIKU__) +#include <StorageKit.h> +#endif + +cmLocalGenerator::cmLocalGenerator() +{ + this->Makefile = 0; // moved to after set on global + this->Parent = 0; + this->WindowsShell = false; + this->WindowsVSIDE = false; + this->WatcomWMake = false; + this->MinGWMake = false; + this->NMake = false; + this->MSYSShell = false; + this->LinkScriptShell = false; + this->IgnoreLibPrefix = false; + this->UseRelativePaths = false; + this->Configured = false; + this->EmitUniversalBinaryFlags = true; + this->IsMakefileGenerator = false; + this->RelativePathsConfigured = false; + this->PathConversionsSetup = false; + this->BackwardsCompatibility = 0; + this->BackwardsCompatibilityFinal = false; +} + +cmLocalGenerator::~cmLocalGenerator() +{ + delete this->Makefile; +} + +//---------------------------------------------------------------------------- +class cmLocalGeneratorCurrent +{ + cmGlobalGenerator* GG; + cmLocalGenerator* LG; +public: + cmLocalGeneratorCurrent(cmLocalGenerator* lg) + { + this->GG = lg->GetGlobalGenerator(); + this->LG = this->GG->GetCurrentLocalGenerator(); + this->GG->SetCurrentLocalGenerator(lg); + } + ~cmLocalGeneratorCurrent() + { + this->GG->SetCurrentLocalGenerator(this->LG); + } +}; + +//---------------------------------------------------------------------------- +void cmLocalGenerator::Configure() +{ + // Manage the global generator's current local generator. + cmLocalGeneratorCurrent clg(this); + static_cast<void>(clg); + + // make sure the CMakeFiles dir is there + std::string filesDir = this->Makefile->GetStartOutputDirectory(); + filesDir += cmake::GetCMakeFilesDirectory(); + cmSystemTools::MakeDirectory(filesDir.c_str()); + + // find & read the list file + this->ReadInputFile(); + + // at the end of the ReadListFile handle any old style subdirs + // first get all the subdirectories + std::vector<cmLocalGenerator *> subdirs = this->GetChildren(); + + // for each subdir recurse + std::vector<cmLocalGenerator *>::iterator sdi = subdirs.begin(); + for (; sdi != subdirs.end(); ++sdi) + { + if (!(*sdi)->Configured) + { + this->Makefile->ConfigureSubDirectory(*sdi); + } + } + + // Check whether relative paths should be used for optionally + // relative paths. + this->UseRelativePaths = this->Makefile->IsOn("CMAKE_USE_RELATIVE_PATHS"); + + this->ComputeObjectMaxPath(); + + this->Configured = true; +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::ComputeObjectMaxPath() +{ + // Choose a maximum object file name length. +#if defined(_WIN32) || defined(__CYGWIN__) + this->ObjectPathMax = 250; +#else + this->ObjectPathMax = 1000; +#endif + const char* plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX"); + if(plen && *plen) + { + unsigned int pmax; + if(sscanf(plen, "%u", &pmax) == 1) + { + if(pmax >= 128) + { + this->ObjectPathMax = pmax; + } + else + { + cmOStringStream w; + w << "CMAKE_OBJECT_PATH_MAX is set to " << pmax + << ", which is less than the minimum of 128. " + << "The value will be ignored."; + this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str()); + } + } + else + { + cmOStringStream w; + w << "CMAKE_OBJECT_PATH_MAX is set to \"" << plen + << "\", which fails to parse as a positive integer. " + << "The value will be ignored."; + this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str()); + } + } + this->ObjectMaxPathViolations.clear(); +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::ReadInputFile() +{ + // Look for the CMakeLists.txt file. + std::string currentStart = this->Makefile->GetStartDirectory(); + currentStart += "/CMakeLists.txt"; + if(cmSystemTools::FileExists(currentStart.c_str(), true)) + { + this->Makefile->ReadListFile(currentStart.c_str()); + return; + } + + if(!this->Parent) + { + return; + } + + // The file is missing. Check policy CMP0014. + cmMakefile* mf = this->Parent->GetMakefile(); + cmOStringStream e; + e << "The source directory\n" + << " " << this->Makefile->GetStartDirectory() << "\n" + << "does not contain a CMakeLists.txt file."; + switch (mf->GetPolicyStatus(cmPolicies::CMP0014)) + { + case cmPolicies::WARN: + // Print the warning. + e << "\n" + << "CMake does not support this case but it used " + << "to work accidentally and is being allowed for " + << "compatibility." + << "\n" + << mf->GetPolicies()->GetPolicyWarning(cmPolicies::CMP0014); + mf->IssueMessage(cmake::AUTHOR_WARNING, e.str()); + case cmPolicies::OLD: + // OLD behavior does not warn. + return; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + e << "\n" + << mf->GetPolicies()->GetRequiredPolicyError(cmPolicies::CMP0014); + case cmPolicies::NEW: + // NEW behavior prints the error. + mf->IssueMessage(cmake::FATAL_ERROR, e.str()); + break; + } +} + +void cmLocalGenerator::SetupPathConversions() +{ + // Setup the current output directory components for use by + // Convert + std::string outdir; + outdir = + cmSystemTools::CollapseFullPath(this->Makefile->GetHomeDirectory()); + cmSystemTools::SplitPath(outdir.c_str(), this->HomeDirectoryComponents); + outdir = + cmSystemTools::CollapseFullPath(this->Makefile->GetStartDirectory()); + cmSystemTools::SplitPath(outdir.c_str(), this->StartDirectoryComponents); + + outdir = cmSystemTools::CollapseFullPath + (this->Makefile->GetHomeOutputDirectory()); + cmSystemTools::SplitPath(outdir.c_str(), + this->HomeOutputDirectoryComponents); + + outdir = cmSystemTools::CollapseFullPath + (this->Makefile->GetStartOutputDirectory()); + cmSystemTools::SplitPath(outdir.c_str(), + this->StartOutputDirectoryComponents); +} + + +void cmLocalGenerator::SetGlobalGenerator(cmGlobalGenerator *gg) +{ + this->GlobalGenerator = gg; + this->Makefile = new cmMakefile; + this->Makefile->SetLocalGenerator(this); + + // setup the home directories + this->Makefile->GetProperties().SetCMakeInstance(gg->GetCMakeInstance()); + this->Makefile->SetHomeDirectory( + gg->GetCMakeInstance()->GetHomeDirectory()); + this->Makefile->SetHomeOutputDirectory( + gg->GetCMakeInstance()->GetHomeOutputDirectory()); +} + +void cmLocalGenerator::ConfigureFinalPass() +{ + this->Makefile->ConfigureFinalPass(); +} + +void cmLocalGenerator::TraceDependencies() +{ + // Generate the rule files for each target. + cmTargets& targets = this->Makefile->GetTargets(); + for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t) + { + const char* projectFilename = 0; + if (this->IsMakefileGenerator == false) // only use of this variable + { + projectFilename = t->second.GetName(); + } + t->second.TraceDependencies(projectFilename); + } +} + +void cmLocalGenerator::GenerateTestFiles() +{ + if ( !this->Makefile->IsOn("CMAKE_TESTING_ENABLED") ) + { + return; + } + + // Compute the set of configurations. + std::vector<std::string> configurationTypes; + const char* config = + this->Makefile->GetConfigurations(configurationTypes, false); + + std::string file = this->Makefile->GetStartOutputDirectory(); + file += "/"; + file += "CTestTestfile.cmake"; + + cmGeneratedFileStream fout(file.c_str()); + fout.SetCopyIfDifferent(true); + + fout << "# CMake generated Testfile for " << std::endl + << "# Source directory: " + << this->Makefile->GetStartDirectory() << std::endl + << "# Build directory: " + << this->Makefile->GetStartOutputDirectory() << std::endl + << "# " << std::endl + << "# This file includes the relevent testing commands " + << "required for " << std::endl + << "# testing this directory and lists subdirectories to " + << "be tested as well." << std::endl; + + const char* testIncludeFile = + this->Makefile->GetProperty("TEST_INCLUDE_FILE"); + if ( testIncludeFile ) + { + fout << "INCLUDE(\"" << testIncludeFile << "\")" << std::endl; + } + + // Ask each test generator to write its code. + std::vector<cmTestGenerator*> const& + testers = this->Makefile->GetTestGenerators(); + for(std::vector<cmTestGenerator*>::const_iterator gi = testers.begin(); + gi != testers.end(); ++gi) + { + (*gi)->Generate(fout, config, configurationTypes); + } + if ( this->Children.size()) + { + size_t i; + for(i = 0; i < this->Children.size(); ++i) + { + fout << "SUBDIRS("; + std::string outP = + this->Children[i]->GetMakefile()->GetStartOutputDirectory(); + fout << this->Convert(outP.c_str(),START_OUTPUT); + fout << ")" << std::endl; + } + } +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::GenerateInstallRules() +{ + // Compute the install prefix. + const char* prefix = this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX"); +#if defined(_WIN32) && !defined(__CYGWIN__) + std::string prefix_win32; + if(!prefix) + { + if(!cmSystemTools::GetEnv("SystemDrive", prefix_win32)) + { + prefix_win32 = "C:"; + } + const char* project_name = this->Makefile->GetDefinition("PROJECT_NAME"); + if(project_name && project_name[0]) + { + prefix_win32 += "/Program Files/"; + prefix_win32 += project_name; + } + else + { + prefix_win32 += "/InstalledCMakeProject"; + } + prefix = prefix_win32.c_str(); + } +#elif defined(__HAIKU__) + if (!prefix) + { + BPath dir; + if (find_directory(B_COMMON_DIRECTORY, &dir) == B_OK) + { + prefix = dir.Path(); + } + else + { + prefix = "/boot/common"; + } + } +#else + if (!prefix) + { + prefix = "/usr/local"; + } +#endif + + // Compute the set of configurations. + std::vector<std::string> configurationTypes; + const char* config = + this->Makefile->GetConfigurations(configurationTypes, false); + + // Choose a default install configuration. + const char* default_config = config; + const char* default_order[] = {"RELEASE", "MINSIZEREL", + "RELWITHDEBINFO", "DEBUG", 0}; + for(const char** c = default_order; *c && !default_config; ++c) + { + for(std::vector<std::string>::iterator i = configurationTypes.begin(); + i != configurationTypes.end(); ++i) + { + if(cmSystemTools::UpperCase(*i) == *c) + { + default_config = i->c_str(); + } + } + } + if(!default_config && !configurationTypes.empty()) + { + default_config = configurationTypes[0].c_str(); + } + if(!default_config) + { + default_config = "Release"; + } + + // Create the install script file. + std::string file = this->Makefile->GetStartOutputDirectory(); + std::string homedir = this->Makefile->GetHomeOutputDirectory(); + std::string currdir = this->Makefile->GetCurrentOutputDirectory(); + cmSystemTools::ConvertToUnixSlashes(file); + cmSystemTools::ConvertToUnixSlashes(homedir); + cmSystemTools::ConvertToUnixSlashes(currdir); + int toplevel_install = 0; + if ( currdir == homedir ) + { + toplevel_install = 1; + } + file += "/cmake_install.cmake"; + cmGeneratedFileStream fout(file.c_str()); + fout.SetCopyIfDifferent(true); + + // Write the header. + fout << "# Install script for directory: " + << this->Makefile->GetCurrentDirectory() << std::endl << std::endl; + fout << "# Set the install prefix" << std::endl + << "IF(NOT DEFINED CMAKE_INSTALL_PREFIX)" << std::endl + << " SET(CMAKE_INSTALL_PREFIX \"" << prefix << "\")" << std::endl + << "ENDIF(NOT DEFINED CMAKE_INSTALL_PREFIX)" << std::endl + << "STRING(REGEX REPLACE \"/$\" \"\" CMAKE_INSTALL_PREFIX " + << "\"${CMAKE_INSTALL_PREFIX}\")" << std::endl + << std::endl; + + // Write support code for generating per-configuration install rules. + fout << + "# Set the install configuration name.\n" + "IF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)\n" + " IF(BUILD_TYPE)\n" + " STRING(REGEX REPLACE \"^[^A-Za-z0-9_]+\" \"\"\n" + " CMAKE_INSTALL_CONFIG_NAME \"${BUILD_TYPE}\")\n" + " ELSE(BUILD_TYPE)\n" + " SET(CMAKE_INSTALL_CONFIG_NAME \"" << default_config << "\")\n" + " ENDIF(BUILD_TYPE)\n" + " MESSAGE(STATUS \"Install configuration: " + "\\\"${CMAKE_INSTALL_CONFIG_NAME}\\\"\")\n" + "ENDIF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)\n" + "\n"; + + // Write support code for dealing with component-specific installs. + fout << + "# Set the component getting installed.\n" + "IF(NOT CMAKE_INSTALL_COMPONENT)\n" + " IF(COMPONENT)\n" + " MESSAGE(STATUS \"Install component: \\\"${COMPONENT}\\\"\")\n" + " SET(CMAKE_INSTALL_COMPONENT \"${COMPONENT}\")\n" + " ELSE(COMPONENT)\n" + " SET(CMAKE_INSTALL_COMPONENT)\n" + " ENDIF(COMPONENT)\n" + "ENDIF(NOT CMAKE_INSTALL_COMPONENT)\n" + "\n"; + + // Copy user-specified install options to the install code. + if(const char* so_no_exe = + this->Makefile->GetDefinition("CMAKE_INSTALL_SO_NO_EXE")) + { + fout << + "# Install shared libraries without execute permission?\n" + "IF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n" + " SET(CMAKE_INSTALL_SO_NO_EXE \"" << so_no_exe << "\")\n" + "ENDIF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n" + "\n"; + } + + // Ask each install generator to write its code. + std::vector<cmInstallGenerator*> const& installers = + this->Makefile->GetInstallGenerators(); + for(std::vector<cmInstallGenerator*>::const_iterator + gi = installers.begin(); + gi != installers.end(); ++gi) + { + (*gi)->Generate(fout, config, configurationTypes); + } + + // Write rules from old-style specification stored in targets. + this->GenerateTargetInstallRules(fout, config, configurationTypes); + + // Include install scripts from subdirectories. + if(!this->Children.empty()) + { + fout << "IF(NOT CMAKE_INSTALL_LOCAL_ONLY)\n"; + fout << " # Include the install script for each subdirectory.\n"; + for(std::vector<cmLocalGenerator*>::const_iterator + ci = this->Children.begin(); ci != this->Children.end(); ++ci) + { + if(!(*ci)->GetMakefile()->GetPropertyAsBool("EXCLUDE_FROM_ALL")) + { + std::string odir = (*ci)->GetMakefile()->GetStartOutputDirectory(); + cmSystemTools::ConvertToUnixSlashes(odir); + fout << " INCLUDE(\"" << odir.c_str() + << "/cmake_install.cmake\")" << std::endl; + } + } + fout << "\n"; + fout << "ENDIF(NOT CMAKE_INSTALL_LOCAL_ONLY)\n\n"; + } + + // Record the install manifest. + if ( toplevel_install ) + { + fout << + "IF(CMAKE_INSTALL_COMPONENT)\n" + " SET(CMAKE_INSTALL_MANIFEST \"install_manifest_" + "${CMAKE_INSTALL_COMPONENT}.txt\")\n" + "ELSE(CMAKE_INSTALL_COMPONENT)\n" + " SET(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n" + "ENDIF(CMAKE_INSTALL_COMPONENT)\n\n"; + fout + << "FILE(WRITE \"" + << homedir.c_str() << "/${CMAKE_INSTALL_MANIFEST}\" " + << "\"\")" << std::endl; + fout + << "FOREACH(file ${CMAKE_INSTALL_MANIFEST_FILES})" << std::endl + << " FILE(APPEND \"" + << homedir.c_str() << "/${CMAKE_INSTALL_MANIFEST}\" " + << "\"${file}\\n\")" << std::endl + << "ENDFOREACH(file)" << std::endl; + } +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::GenerateTargetManifest() +{ + // Collect the set of configuration types. + std::vector<std::string> configNames; + this->Makefile->GetConfigurations(configNames); + + // Add our targets to the manifest for each configuration. + cmTargets& targets = this->Makefile->GetTargets(); + for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t) + { + cmTarget& target = t->second; + if(configNames.empty()) + { + target.GenerateTargetManifest(0); + } + else + { + for(std::vector<std::string>::iterator ci = configNames.begin(); + ci != configNames.end(); ++ci) + { + const char* config = ci->c_str(); + target.GenerateTargetManifest(config); + } + } + } +} + +void cmLocalGenerator::AddCustomCommandToCreateObject(const char* ofname, + const char* lang, + cmSourceFile& source, + cmTarget& target) +{ + std::string objectDir = cmSystemTools::GetFilenamePath(std::string(ofname)); + objectDir = this->Convert(objectDir.c_str(),START_OUTPUT,SHELL); + std::string objectFile = this->Convert(ofname,START_OUTPUT,SHELL); + std::string sourceFile = + this->Convert(source.GetFullPath().c_str(),START_OUTPUT,SHELL,true); + std::string varString = "CMAKE_"; + varString += lang; + varString += "_COMPILE_OBJECT"; + std::vector<std::string> rules; + rules.push_back(this->Makefile->GetRequiredDefinition(varString.c_str())); + varString = "CMAKE_"; + varString += lang; + varString += "_FLAGS"; + std::string flags; + flags += this->Makefile->GetSafeDefinition(varString.c_str()); + flags += " "; + { + std::vector<std::string> includes; + this->GetIncludeDirectories(includes, &target, lang); + flags += this->GetIncludeFlags(includes, lang); + } + flags += this->Makefile->GetDefineFlags(); + + // Construct the command lines. + cmCustomCommandLines commandLines; + std::vector<std::string> commands; + cmSystemTools::ExpandList(rules, commands); + cmLocalGenerator::RuleVariables vars; + vars.Language = lang; + vars.Source = sourceFile.c_str(); + vars.Object = objectFile.c_str(); + vars.ObjectDir = objectDir.c_str(); + vars.Flags = flags.c_str(); + for(std::vector<std::string>::iterator i = commands.begin(); + i != commands.end(); ++i) + { + // Expand the full command line string. + this->ExpandRuleVariables(*i, vars); + + // Parse the string to get the custom command line. + cmCustomCommandLine commandLine; + std::vector<cmStdString> cmd = cmSystemTools::ParseArguments(i->c_str()); + for(std::vector<cmStdString>::iterator a = cmd.begin(); + a != cmd.end(); ++a) + { + commandLine.push_back(*a); + } + + // Store this command line. + commandLines.push_back(commandLine); + } + + // Check for extra object-file dependencies. + std::vector<std::string> depends; + const char* additionalDeps = source.GetProperty("OBJECT_DEPENDS"); + if(additionalDeps) + { + cmSystemTools::ExpandListArgument(additionalDeps, depends); + } + + // Generate a meaningful comment for the command. + std::string comment = "Building "; + comment += lang; + comment += " object "; + comment += this->Convert(ofname, START_OUTPUT); + + // Add the custom command to build the object file. + this->Makefile->AddCustomCommandToOutput( + ofname, + depends, + source.GetFullPath().c_str(), + commandLines, + comment.c_str(), + this->Makefile->GetStartOutputDirectory() + ); +} + +void cmLocalGenerator::AddBuildTargetRule(const char* llang, cmTarget& target) +{ + cmStdString objs; + std::vector<std::string> objVector; + // Add all the sources outputs to the depends of the target + std::vector<cmSourceFile*> const& classes = target.GetSourceFiles(); + for(std::vector<cmSourceFile*>::const_iterator i = classes.begin(); + i != classes.end(); ++i) + { + cmSourceFile* sf = *i; + if(!sf->GetCustomCommand() && + !sf->GetPropertyAsBool("HEADER_FILE_ONLY") && + !sf->GetPropertyAsBool("EXTERNAL_OBJECT")) + { + std::string dir_max; + dir_max += this->Makefile->GetCurrentOutputDirectory(); + dir_max += "/"; + std::string obj = this->GetObjectFileNameWithoutTarget(*sf, dir_max); + if(!obj.empty()) + { + std::string ofname = this->Makefile->GetCurrentOutputDirectory(); + ofname += "/"; + ofname += obj; + objVector.push_back(ofname); + this->AddCustomCommandToCreateObject(ofname.c_str(), + llang, *(*i), target); + objs += this->Convert(ofname.c_str(),START_OUTPUT,MAKEFILE); + objs += " "; + } + } + } + std::string createRule = "CMAKE_"; + createRule += llang; + createRule += target.GetCreateRuleVariable(); + std::string targetName = target.GetFullName(); + // Executable : + // Shared Library: + // Static Library: + // Shared Module: + std::string linkLibs; // should be set + std::string flags; // should be set + std::string linkFlags; // should be set + this->GetTargetFlags(linkLibs, flags, linkFlags, target); + cmLocalGenerator::RuleVariables vars; + vars.Language = llang; + vars.Objects = objs.c_str(); + vars.ObjectDir = "."; + vars.Target = targetName.c_str(); + vars.LinkLibraries = linkLibs.c_str(); + vars.Flags = flags.c_str(); + vars.LinkFlags = linkFlags.c_str(); + + std::string langFlags; + this->AddLanguageFlags(langFlags, llang, 0); + this->AddArchitectureFlags(langFlags, &target, llang, 0); + vars.LanguageCompileFlags = langFlags.c_str(); + + cmCustomCommandLines commandLines; + std::vector<std::string> rules; + rules.push_back(this->Makefile->GetRequiredDefinition(createRule.c_str())); + std::vector<std::string> commands; + cmSystemTools::ExpandList(rules, commands); + for(std::vector<std::string>::iterator i = commands.begin(); + i != commands.end(); ++i) + { + // Expand the full command line string. + this->ExpandRuleVariables(*i, vars); + // Parse the string to get the custom command line. + cmCustomCommandLine commandLine; + std::vector<cmStdString> cmd = cmSystemTools::ParseArguments(i->c_str()); + for(std::vector<cmStdString>::iterator a = cmd.begin(); + a != cmd.end(); ++a) + { + commandLine.push_back(*a); + } + + // Store this command line. + commandLines.push_back(commandLine); + } + std::string targetFullPath = target.GetFullPath(); + // Generate a meaningful comment for the command. + std::string comment = "Linking "; + comment += llang; + comment += " target "; + comment += this->Convert(targetFullPath.c_str(), START_OUTPUT); + this->Makefile->AddCustomCommandToOutput( + targetFullPath.c_str(), + objVector, + 0, + commandLines, + comment.c_str(), + this->Makefile->GetStartOutputDirectory() + ); + target.AddSourceFile + (this->Makefile->GetSource(targetFullPath.c_str())); +} + + +void cmLocalGenerator +::CreateCustomTargetsAndCommands(std::set<cmStdString> const& lang) +{ + cmTargets &tgts = this->Makefile->GetTargets(); + for(cmTargets::iterator l = tgts.begin(); + l != tgts.end(); l++) + { + cmTarget& target = l->second; + switch(target.GetType()) + { + case cmTarget::STATIC_LIBRARY: + case cmTarget::SHARED_LIBRARY: + case cmTarget::MODULE_LIBRARY: + case cmTarget::EXECUTABLE: + { + const char* llang = target.GetLinkerLanguage(); + if(!llang) + { + cmSystemTools::Error + ("CMake can not determine linker language for target:", + target.GetName()); + return; + } + // if the language is not in the set lang then create custom + // commands to build the target + if(lang.count(llang) == 0) + { + this->AddBuildTargetRule(llang, target); + } + } + break; + default: + break; + } + } +} + +// List of variables that are replaced when +// rules are expanced. These variables are +// replaced in the form <var> with GetSafeDefinition(var). +// ${LANG} is replaced in the variable first with all enabled +// languages. +static const char* ruleReplaceVars[] = +{ + "CMAKE_${LANG}_COMPILER", + "CMAKE_SHARED_LIBRARY_CREATE_${LANG}_FLAGS", + "CMAKE_SHARED_MODULE_CREATE_${LANG}_FLAGS", + "CMAKE_SHARED_MODULE_${LANG}_FLAGS", + "CMAKE_SHARED_LIBRARY_${LANG}_FLAGS", + "CMAKE_${LANG}_LINK_FLAGS", + "CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG", + "CMAKE_${LANG}_ARCHIVE", + "CMAKE_AR", + "CMAKE_CURRENT_SOURCE_DIR", + "CMAKE_CURRENT_BINARY_DIR", + "CMAKE_RANLIB", + "CMAKE_LINKER", + 0 +}; + +std::string +cmLocalGenerator::ExpandRuleVariable(std::string const& variable, + const RuleVariables& replaceValues) +{ + if(replaceValues.LinkFlags) + { + if(variable == "LINK_FLAGS") + { + return replaceValues.LinkFlags; + } + } + if(replaceValues.Flags) + { + if(variable == "FLAGS") + { + return replaceValues.Flags; + } + } + + if(replaceValues.Source) + { + if(variable == "SOURCE") + { + return replaceValues.Source; + } + } + if(replaceValues.PreprocessedSource) + { + if(variable == "PREPROCESSED_SOURCE") + { + return replaceValues.PreprocessedSource; + } + } + if(replaceValues.AssemblySource) + { + if(variable == "ASSEMBLY_SOURCE") + { + return replaceValues.AssemblySource; + } + } + if(replaceValues.Object) + { + if(variable == "OBJECT") + { + return replaceValues.Object; + } + } + if(replaceValues.ObjectDir) + { + if(variable == "OBJECT_DIR") + { + return replaceValues.ObjectDir; + } + } + if(replaceValues.Objects) + { + if(variable == "OBJECTS") + { + return replaceValues.Objects; + } + } + if(replaceValues.ObjectsQuoted) + { + if(variable == "OBJECTS_QUOTED") + { + return replaceValues.ObjectsQuoted; + } + } + if(replaceValues.Defines && variable == "DEFINES") + { + return replaceValues.Defines; + } + if(replaceValues.TargetPDB ) + { + if(variable == "TARGET_PDB") + { + return replaceValues.TargetPDB; + } + } + if(replaceValues.DependencyFile ) + { + if(variable == "DEP_FILE") + { + return replaceValues.DependencyFile; + } + } + + if(replaceValues.Target) + { + if(variable == "TARGET_QUOTED") + { + std::string targetQuoted = replaceValues.Target; + if(targetQuoted.size() && targetQuoted[0] != '\"') + { + targetQuoted = '\"'; + targetQuoted += replaceValues.Target; + targetQuoted += '\"'; + } + return targetQuoted; + } + if(variable == "TARGET_UNQUOTED") + { + std::string unquoted = replaceValues.Target; + std::string::size_type sz = unquoted.size(); + if(sz > 2 && unquoted[0] == '\"' && unquoted[sz-1] == '\"') + { + unquoted = unquoted.substr(1, sz-2); + } + return unquoted; + } + if(replaceValues.LanguageCompileFlags) + { + if(variable == "LANGUAGE_COMPILE_FLAGS") + { + return replaceValues.LanguageCompileFlags; + } + } + if(replaceValues.Target) + { + if(variable == "TARGET") + { + return replaceValues.Target; + } + } + if(variable == "TARGET_IMPLIB") + { + return this->TargetImplib; + } + if(variable == "TARGET_VERSION_MAJOR") + { + if(replaceValues.TargetVersionMajor) + { + return replaceValues.TargetVersionMajor; + } + else + { + return "0"; + } + } + if(variable == "TARGET_VERSION_MINOR") + { + if(replaceValues.TargetVersionMinor) + { + return replaceValues.TargetVersionMinor; + } + else + { + return "0"; + } + } + if(replaceValues.Target) + { + if(variable == "TARGET_BASE") + { + // Strip the last extension off the target name. + std::string targetBase = replaceValues.Target; + std::string::size_type pos = targetBase.rfind("."); + if(pos != targetBase.npos) + { + return targetBase.substr(0, pos); + } + else + { + return targetBase; + } + } + } + } + if(variable == "TARGET_SONAME" || variable == "SONAME_FLAG" || + variable == "TARGET_INSTALLNAME_DIR") + { + // All these variables depend on TargetSOName + if(replaceValues.TargetSOName) + { + if(variable == "TARGET_SONAME") + { + return replaceValues.TargetSOName; + } + if(variable == "SONAME_FLAG" && replaceValues.SONameFlag) + { + return replaceValues.SONameFlag; + } + if(replaceValues.TargetInstallNameDir && + variable == "TARGET_INSTALLNAME_DIR") + { + return replaceValues.TargetInstallNameDir; + } + } + return ""; + } + if(replaceValues.LinkLibraries) + { + if(variable == "LINK_LIBRARIES") + { + return replaceValues.LinkLibraries; + } + } + if(replaceValues.Language) + { + if(variable == "LANGUAGE") + { + return replaceValues.Language; + } + } + if(replaceValues.CMTarget) + { + if(variable == "TARGET_NAME") + { + return replaceValues.CMTarget->GetName(); + } + if(variable == "TARGET_TYPE") + { + return cmTarget::GetTargetTypeName(replaceValues.CMTarget->GetType()); + } + } + if(replaceValues.Output) + { + if(variable == "OUTPUT") + { + return replaceValues.Output; + } + } + if(variable == "CMAKE_COMMAND") + { + const char* cmcommand = + this->GetMakefile()->GetDefinition("CMAKE_COMMAND"); + return this->Convert(cmcommand, FULL, SHELL); + } + std::vector<std::string> enabledLanguages; + this->GlobalGenerator->GetEnabledLanguages(enabledLanguages); + // loop over language specific replace variables + int pos = 0; + while(ruleReplaceVars[pos]) + { + for(std::vector<std::string>::iterator i = enabledLanguages.begin(); + i != enabledLanguages.end(); ++i) + { + const char* lang = i->c_str(); + std::string actualReplace = ruleReplaceVars[pos]; + // If this is the compiler then look for the extra variable + // _COMPILER_ARG1 which must be the first argument to the compiler + const char* compilerArg1 = 0; + if(actualReplace == "CMAKE_${LANG}_COMPILER") + { + std::string arg1 = actualReplace + "_ARG1"; + cmSystemTools::ReplaceString(arg1, "${LANG}", lang); + compilerArg1 = this->Makefile->GetDefinition(arg1.c_str()); + } + if(actualReplace.find("${LANG}") != actualReplace.npos) + { + cmSystemTools::ReplaceString(actualReplace, "${LANG}", lang); + } + if(actualReplace == variable) + { + std::string replace = + this->Makefile->GetSafeDefinition(variable.c_str()); + // if the variable is not a FLAG then treat it like a path + if(variable.find("_FLAG") == variable.npos) + { + std::string ret = this->ConvertToOutputForExisting(replace.c_str()); + // if there is a required first argument to the compiler add it + // to the compiler string + if(compilerArg1) + { + ret += " "; + ret += compilerArg1; + } + return ret; + } + return replace; + } + } + pos++; + } + return variable; +} + + +void +cmLocalGenerator::ExpandRuleVariables(std::string& s, + const RuleVariables& replaceValues) +{ + std::vector<std::string> enabledLanguages; + this->GlobalGenerator->GetEnabledLanguages(enabledLanguages); + this->InsertRuleLauncher(s, replaceValues.CMTarget, + replaceValues.RuleLauncher); + std::string::size_type start = s.find('<'); + // no variables to expand + if(start == s.npos) + { + return; + } + std::string::size_type pos = 0; + std::string expandedInput; + while(start != s.npos && start < s.size()-2) + { + std::string::size_type end = s.find('>', start); + // if we find a < with no > we are done + if(end == s.npos) + { + return; + } + char c = s[start+1]; + // if the next char after the < is not A-Za-z then + // skip it and try to find the next < in the string + if(!isalpha(c)) + { + start = s.find('<', start+1); + } + else + { + // extract the var + std::string var = s.substr(start+1, end - start-1); + std::string replace = this->ExpandRuleVariable(var, + replaceValues); + expandedInput += s.substr(pos, start-pos); + expandedInput += replace; + // move to next one + start = s.find('<', start+var.size()+2); + pos = end+1; + } + } + // add the rest of the input + expandedInput += s.substr(pos, s.size()-pos); + s = expandedInput; +} + +//---------------------------------------------------------------------------- +const char* cmLocalGenerator::GetRuleLauncher(cmTarget* target, + const char* prop) +{ + if(target) + { + return target->GetProperty(prop); + } + else + { + return this->Makefile->GetProperty(prop); + } +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::InsertRuleLauncher(std::string& s, cmTarget* target, + const char* prop) +{ + if(const char* val = this->GetRuleLauncher(target, prop)) + { + cmOStringStream wrapped; + wrapped << val << " " << s; + s = wrapped.str(); + } +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator::ConvertToOutputForExistingCommon(const char* remote, + std::string const& result) +{ + // If this is a windows shell, the result has a space, and the path + // already exists, we can use a short-path to reference it without a + // space. + if(this->WindowsShell && result.find(' ') != result.npos && + cmSystemTools::FileExists(remote)) + { + std::string tmp; + if(cmSystemTools::GetShortPath(remote, tmp)) + { + return this->Convert(tmp.c_str(), NONE, SHELL, true); + } + } + + // Otherwise, leave it unchanged. + return result; +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator::ConvertToOutputForExisting(const char* remote, + RelativeRoot local) +{ + // Perform standard conversion. + std::string result = this->Convert(remote, local, SHELL, true); + + // Consider short-path. + return this->ConvertToOutputForExistingCommon(remote, result); +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator::ConvertToOutputForExisting(RelativeRoot remote, + const char* local) +{ + // Perform standard conversion. + std::string result = this->Convert(remote, local, SHELL, true); + + // Consider short-path. + const char* remotePath = this->GetRelativeRootPath(remote); + return this->ConvertToOutputForExistingCommon(remotePath, result); +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator::ConvertToIncludeReference(std::string const& path) +{ + return this->ConvertToOutputForExisting(path.c_str()); +} + +//---------------------------------------------------------------------------- +std::string cmLocalGenerator::GetIncludeFlags( + const std::vector<std::string> &includes, + const char* lang, bool forResponseFile) +{ + if(!lang) + { + return ""; + } + + cmOStringStream includeFlags; + + std::string flagVar = "CMAKE_INCLUDE_FLAG_"; + flagVar += lang; + const char* includeFlag = + this->Makefile->GetSafeDefinition(flagVar.c_str()); + flagVar = "CMAKE_INCLUDE_FLAG_SEP_"; + flagVar += lang; + const char* sep = this->Makefile->GetDefinition(flagVar.c_str()); + bool quotePaths = false; + if(this->Makefile->GetDefinition("CMAKE_QUOTE_INCLUDE_PATHS")) + { + quotePaths = true; + } + bool repeatFlag = true; + // should the include flag be repeated like ie. -IA -IB + if(!sep) + { + sep = " "; + } + else + { + // if there is a separator then the flag is not repeated but is only + // given once i.e. -classpath a:b:c + repeatFlag = false; + } + + // Support special system include flag if it is available and the + // normal flag is repeated for each directory. + std::string sysFlagVar = "CMAKE_INCLUDE_SYSTEM_FLAG_"; + sysFlagVar += lang; + const char* sysIncludeFlag = 0; + if(repeatFlag) + { + sysIncludeFlag = this->Makefile->GetDefinition(sysFlagVar.c_str()); + } + + bool flagUsed = false; + std::set<cmStdString> emitted; +#ifdef __APPLE__ + emitted.insert("/System/Library/Frameworks"); +#endif + std::vector<std::string>::const_iterator i; + for(i = includes.begin(); i != includes.end(); ++i) + { + if(this->Makefile->IsOn("APPLE") + && cmSystemTools::IsPathToFramework(i->c_str())) + { + std::string frameworkDir = *i; + frameworkDir += "/../"; + frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir.c_str()); + if(emitted.insert(frameworkDir).second) + { + OutputFormat format = forResponseFile? RESPONSE : SHELL; + includeFlags + << "-F" << this->Convert(frameworkDir.c_str(), + START_OUTPUT, format, true) + << " "; + } + continue; + } + + std::string include = *i; + if(!flagUsed || repeatFlag) + { + if(sysIncludeFlag && + this->Makefile->IsSystemIncludeDirectory(i->c_str())) + { + includeFlags << sysIncludeFlag; + } + else + { + includeFlags << includeFlag; + } + flagUsed = true; + } + std::string includePath; + if(forResponseFile) + { + includePath = this->Convert(i->c_str(), START_OUTPUT, + RESPONSE, true); + } + else + { + includePath = this->ConvertToIncludeReference(*i); + } + if(quotePaths && includePath.size() && includePath[0] != '\"') + { + includeFlags << "\""; + } + includeFlags << includePath; + if(quotePaths && includePath.size() && includePath[0] != '\"') + { + includeFlags << "\""; + } + includeFlags << sep; + } + std::string flags = includeFlags.str(); + // remove trailing separators + if((sep[0] != ' ') && flags.size()>0 && flags[flags.size()-1] == sep[0]) + { + flags[flags.size()-1] = ' '; + } + return flags; +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, + cmTarget* target, + const char* lang) +{ + // Need to decide whether to automatically include the source and + // binary directories at the beginning of the include path. + bool includeSourceDir = false; + bool includeBinaryDir = false; + + // When automatic include directories are requested for a build then + // include the source and binary directories at the beginning of the + // include path to approximate include file behavior for an + // in-source build. This does not account for the case of a source + // file in a subdirectory of the current source directory but we + // cannot fix this because not all native build tools support + // per-source-file include paths. + if(this->Makefile->IsOn("CMAKE_INCLUDE_CURRENT_DIR")) + { + includeSourceDir = true; + includeBinaryDir = true; + } + + // CMake versions below 2.0 would add the source tree to the -I path + // automatically. Preserve compatibility. + if(this->NeedBackwardsCompatibility(1,9)) + { + includeSourceDir = true; + } + + // Hack for VTK 4.0 - 4.4 which depend on the old behavior but do + // not set the backwards compatibility level automatically. + const char* vtkSourceDir = + this->Makefile->GetDefinition("VTK_SOURCE_DIR"); + if(vtkSourceDir) + { + const char* vtk_major = + this->Makefile->GetDefinition("VTK_MAJOR_VERSION"); + const char* vtk_minor = + this->Makefile->GetDefinition("VTK_MINOR_VERSION"); + vtk_major = vtk_major? vtk_major : "4"; + vtk_minor = vtk_minor? vtk_minor : "4"; + int vmajor = 0; + int vminor = 0; + if(sscanf(vtk_major, "%d", &vmajor) && + sscanf(vtk_minor, "%d", &vminor) && vmajor == 4 && vminor <= 4) + { + includeSourceDir = true; + } + } + + // Do not repeat an include path. + std::set<cmStdString> emitted; + + // Store the automatic include paths. + if(includeBinaryDir) + { + if(emitted.find( + this->Makefile->GetStartOutputDirectory()) == emitted.end()) + { + dirs.push_back(this->Makefile->GetStartOutputDirectory()); + emitted.insert(this->Makefile->GetStartOutputDirectory()); + } + } + if(includeSourceDir) + { + if(emitted.find(this->Makefile->GetStartDirectory()) == emitted.end()) + { + dirs.push_back(this->Makefile->GetStartDirectory()); + emitted.insert(this->Makefile->GetStartDirectory()); + } + } + + // Load implicit include directories for this language. + std::string impDirVar = "CMAKE_"; + impDirVar += lang; + impDirVar += "_IMPLICIT_INCLUDE_DIRECTORIES"; + if(const char* value = this->Makefile->GetDefinition(impDirVar.c_str())) + { + std::vector<std::string> impDirVec; + cmSystemTools::ExpandListArgument(value, impDirVec); + for(std::vector<std::string>::const_iterator i = impDirVec.begin(); + i != impDirVec.end(); ++i) + { + emitted.insert(*i); + } + } + + // Get the target-specific include directories. + std::vector<std::string> includes; + if(target) + { + includes = target->GetIncludeDirectories(); + } + + // Support putting all the in-project include directories first if + // it is requested by the project. + if(this->Makefile->IsOn("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE")) + { + const char* topSourceDir = this->Makefile->GetHomeDirectory(); + const char* topBinaryDir = this->Makefile->GetHomeOutputDirectory(); + for(std::vector<std::string>::const_iterator i = includes.begin(); + i != includes.end(); ++i) + { + // Emit this directory only if it is a subdirectory of the + // top-level source or binary tree. + if(cmSystemTools::ComparePath(i->c_str(), topSourceDir) || + cmSystemTools::ComparePath(i->c_str(), topBinaryDir) || + cmSystemTools::IsSubDirectory(i->c_str(), topSourceDir) || + cmSystemTools::IsSubDirectory(i->c_str(), topBinaryDir)) + { + if(emitted.insert(*i).second) + { + dirs.push_back(*i); + } + } + } + } + + // Construct the final ordered include directory list. + for(std::vector<std::string>::const_iterator i = includes.begin(); + i != includes.end(); ++i) + { + if(emitted.insert(*i).second) + { + dirs.push_back(*i); + } + } +} + +void cmLocalGenerator::GetTargetFlags(std::string& linkLibs, + std::string& flags, + std::string& linkFlags, + cmTarget& target) +{ + std::string buildType = + this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"); + buildType = cmSystemTools::UpperCase(buildType); + const char* libraryLinkVariable = + "CMAKE_SHARED_LINKER_FLAGS"; // default to shared library + + switch(target.GetType()) + { + case cmTarget::STATIC_LIBRARY: + { + const char* targetLinkFlags = + target.GetProperty("STATIC_LIBRARY_FLAGS"); + if(targetLinkFlags) + { + linkFlags += targetLinkFlags; + linkFlags += " "; + } + if(!buildType.empty()) + { + std::string build = "STATIC_LIBRARY_FLAGS_"; + build += buildType; + targetLinkFlags = target.GetProperty(build.c_str()); + if(targetLinkFlags) + { + linkFlags += targetLinkFlags; + linkFlags += " "; + } + } + } + break; + case cmTarget::MODULE_LIBRARY: + libraryLinkVariable = "CMAKE_MODULE_LINKER_FLAGS"; + case cmTarget::SHARED_LIBRARY: + { + linkFlags = this->Makefile->GetSafeDefinition(libraryLinkVariable); + linkFlags += " "; + if(!buildType.empty()) + { + std::string build = libraryLinkVariable; + build += "_"; + build += buildType; + linkFlags += this->Makefile->GetSafeDefinition(build.c_str()); + linkFlags += " "; + } + if(this->Makefile->IsOn("WIN32") && + !(this->Makefile->IsOn("CYGWIN") || this->Makefile->IsOn("MINGW"))) + { + const std::vector<cmSourceFile*>& sources = target.GetSourceFiles(); + for(std::vector<cmSourceFile*>::const_iterator i = sources.begin(); + i != sources.end(); ++i) + { + cmSourceFile* sf = *i; + if(sf->GetExtension() == "def") + { + linkFlags += + this->Makefile->GetSafeDefinition("CMAKE_LINK_DEF_FILE_FLAG"); + linkFlags += this->Convert(sf->GetFullPath().c_str(), + FULL, SHELL); + linkFlags += " "; + } + } + } + const char* targetLinkFlags = target.GetProperty("LINK_FLAGS"); + if(targetLinkFlags) + { + linkFlags += targetLinkFlags; + linkFlags += " "; + } + if(!buildType.empty()) + { + std::string configLinkFlags = "LINK_FLAGS_"; + configLinkFlags += buildType; + targetLinkFlags = target.GetProperty(configLinkFlags.c_str()); + if(targetLinkFlags) + { + linkFlags += targetLinkFlags; + linkFlags += " "; + } + } + cmOStringStream linklibsStr; + this->OutputLinkLibraries(linklibsStr, target, false); + linkLibs = linklibsStr.str(); + } + break; + case cmTarget::EXECUTABLE: + { + linkFlags += + this->Makefile->GetSafeDefinition("CMAKE_EXE_LINKER_FLAGS"); + linkFlags += " "; + if(!buildType.empty()) + { + std::string build = "CMAKE_EXE_LINKER_FLAGS_"; + build += buildType; + linkFlags += this->Makefile->GetSafeDefinition(build.c_str()); + linkFlags += " "; + } + const char* linkLanguage = target.GetLinkerLanguage(); + if(!linkLanguage) + { + cmSystemTools::Error + ("CMake can not determine linker language for target:", + target.GetName()); + return; + } + this->AddLanguageFlags(flags, linkLanguage, buildType.c_str()); + cmOStringStream linklibs; + this->OutputLinkLibraries(linklibs, target, false); + linkLibs = linklibs.str(); + if(cmSystemTools::IsOn + (this->Makefile->GetDefinition("BUILD_SHARED_LIBS"))) + { + std::string sFlagVar = std::string("CMAKE_SHARED_BUILD_") + + linkLanguage + std::string("_FLAGS"); + linkFlags += this->Makefile->GetSafeDefinition(sFlagVar.c_str()); + linkFlags += " "; + } + if ( target.GetPropertyAsBool("WIN32_EXECUTABLE") ) + { + linkFlags += + this->Makefile->GetSafeDefinition("CMAKE_CREATE_WIN32_EXE"); + linkFlags += " "; + } + else + { + linkFlags += + this->Makefile->GetSafeDefinition("CMAKE_CREATE_CONSOLE_EXE"); + linkFlags += " "; + } + if (target.IsExecutableWithExports()) + { + std::string exportFlagVar = "CMAKE_EXE_EXPORTS_"; + exportFlagVar += linkLanguage; + exportFlagVar += "_FLAG"; + + linkFlags += + this->Makefile->GetSafeDefinition(exportFlagVar.c_str()); + linkFlags += " "; + } + const char* targetLinkFlags = target.GetProperty("LINK_FLAGS"); + if(targetLinkFlags) + { + linkFlags += targetLinkFlags; + linkFlags += " "; + } + if(!buildType.empty()) + { + std::string configLinkFlags = "LINK_FLAGS_"; + configLinkFlags += buildType; + targetLinkFlags = target.GetProperty(configLinkFlags.c_str()); + if(targetLinkFlags) + { + linkFlags += targetLinkFlags; + linkFlags += " "; + } + } + } + break; + default: + break; + } +} + +std::string cmLocalGenerator::ConvertToLinkReference(std::string const& lib) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + // Work-ardound command line parsing limitations in MSVC 6.0 and + // Watcom. + if(this->Makefile->IsOn("MSVC60") || this->Makefile->IsOn("WATCOM")) + { + // Search for the last space. + std::string::size_type pos = lib.rfind(' '); + if(pos != lib.npos) + { + // Find the slash after the last space, if any. + pos = lib.find('/', pos); + + // Convert the portion of the path with a space to a short path. + std::string sp; + if(cmSystemTools::GetShortPath(lib.substr(0, pos).c_str(), sp)) + { + // Append the rest of the path with no space. + sp += lib.substr(pos); + + // Convert to an output path. + return this->Convert(sp.c_str(), NONE, SHELL); + } + } + } +#endif + + // Normal behavior. + return this->Convert(lib.c_str(), START_OUTPUT, SHELL); +} + +/** + * Output the linking rules on a command line. For executables, + * targetLibrary should be a NULL pointer. For libraries, it should point + * to the name of the library. This will not link a library against itself. + */ +void cmLocalGenerator::OutputLinkLibraries(std::ostream& fout, + cmTarget& tgt, + bool relink) +{ + const char* config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE"); + cmComputeLinkInformation* pcli = tgt.GetLinkInformation(config); + if(!pcli) + { + return; + } + cmComputeLinkInformation& cli = *pcli; + + // Collect library linking flags command line options. + std::string linkLibs; + + const char* linkLanguage = cli.GetLinkLanguage(); + + std::string libPathFlag = + this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG"); + std::string libPathTerminator = + this->Makefile->GetSafeDefinition("CMAKE_LIBRARY_PATH_TERMINATOR"); + + // Flags to link an executable to shared libraries. + std::string linkFlagsVar = "CMAKE_SHARED_LIBRARY_LINK_"; + linkFlagsVar += linkLanguage; + linkFlagsVar += "_FLAGS"; + if( tgt.GetType() == cmTarget::EXECUTABLE ) + { + linkLibs = this->Makefile->GetSafeDefinition(linkFlagsVar.c_str()); + linkLibs += " "; + } + + // Append the framework search path flags. + std::vector<std::string> const& fwDirs = cli.GetFrameworkPaths(); + for(std::vector<std::string>::const_iterator fdi = fwDirs.begin(); + fdi != fwDirs.end(); ++fdi) + { + linkLibs += "-F"; + linkLibs += this->Convert(fdi->c_str(), NONE, SHELL, false); + linkLibs += " "; + } + + // Append the library search path flags. + std::vector<std::string> const& libDirs = cli.GetDirectories(); + for(std::vector<std::string>::const_iterator libDir = libDirs.begin(); + libDir != libDirs.end(); ++libDir) + { + std::string libpath = this->ConvertToOutputForExisting(libDir->c_str()); + linkLibs += libPathFlag; + linkLibs += libpath; + linkLibs += libPathTerminator; + linkLibs += " "; + } + + // Append the link items. + typedef cmComputeLinkInformation::ItemVector ItemVector; + ItemVector const& items = cli.GetItems(); + for(ItemVector::const_iterator li = items.begin(); li != items.end(); ++li) + { + if(li->IsPath) + { + linkLibs += this->ConvertToLinkReference(li->Value); + } + else + { + linkLibs += li->Value; + } + linkLibs += " "; + } + + // Write the library flags to the build rule. + fout << linkLibs; + + // Get the RPATH entries. + std::vector<std::string> runtimeDirs; + cli.GetRPath(runtimeDirs, relink); + + // Check what kind of rpath flags to use. + if(cli.GetRuntimeSep().empty()) + { + // Each rpath entry gets its own option ("-R a -R b -R c") + std::string rpath; + for(std::vector<std::string>::iterator ri = runtimeDirs.begin(); + ri != runtimeDirs.end(); ++ri) + { + rpath += cli.GetRuntimeFlag(); + rpath += this->Convert(ri->c_str(), NONE, SHELL, false); + rpath += " "; + } + fout << rpath; + } + else + { + // All rpath entries are combined ("-Wl,-rpath,a:b:c"). + std::string rpath = cli.GetRPathString(relink); + + // Store the rpath option in the stream. + if(!rpath.empty()) + { + fout << cli.GetRuntimeFlag(); + fout << this->EscapeForShell(rpath.c_str(), true); + fout << " "; + } + } + + // Add the linker runtime search path if any. + std::string rpath_link = cli.GetRPathLinkString(); + if(!cli.GetRPathLinkFlag().empty() && !rpath_link.empty()) + { + fout << cli.GetRPathLinkFlag(); + fout << this->EscapeForShell(rpath_link.c_str(), true); + fout << " "; + } + + // Add standard libraries for this language. + std::string standardLibsVar = "CMAKE_"; + standardLibsVar += cli.GetLinkLanguage(); + standardLibsVar += "_STANDARD_LIBRARIES"; + if(const char* stdLibs = + this->Makefile->GetDefinition(standardLibsVar.c_str())) + { + fout << stdLibs << " "; + } +} + + +//---------------------------------------------------------------------------- +void cmLocalGenerator::AddArchitectureFlags(std::string& flags, + cmTarget* target, + const char *lang, + const char* config) +{ + // Only add Mac OS X specific flags on Darwin platforms (OSX and iphone): + if(!this->Makefile->IsOn("APPLE")) + { + return; + } + + if(this->EmitUniversalBinaryFlags) + { + std::vector<std::string> archs; + target->GetAppleArchs(config, archs); + const char* sysroot = + this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT"); + const char* sysrootDefault = + this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT_DEFAULT"); + const char* deploymentTarget = + this->Makefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET"); + std::string isysrootVar = std::string("CMAKE_") + lang + "_HAS_ISYSROOT"; + bool hasIsysroot = this->Makefile->IsOn(isysrootVar.c_str()); + std::string deploymentTargetFlagVar = + std::string("CMAKE_") + lang + "_OSX_DEPLOYMENT_TARGET_FLAG"; + const char* deploymentTargetFlag = + this->Makefile->GetDefinition(deploymentTargetFlagVar.c_str()); + bool flagsUsed = false; + if(!archs.empty() && sysroot && lang && (lang[0] =='C' || lang[0] == 'F')) + { + // if there is more than one arch add the -arch and + // -isysroot flags, or if there is one arch flag, but + // it is not the default -arch flag for the system, then + // add it. Otherwize do not add -arch and -isysroot + if(archs[0] != "") + { + for( std::vector<std::string>::iterator i = archs.begin(); + i != archs.end(); ++i) + { + flags += " -arch "; + flags += *i; + } + if(hasIsysroot) + { + flags += " -isysroot "; + flags += sysroot; + } + flagsUsed = true; + } + } + + if(!flagsUsed && sysroot && sysrootDefault && + strcmp(sysroot, sysrootDefault) != 0 && hasIsysroot) + { + flags += " -isysroot "; + flags += sysroot; + } + + if (deploymentTargetFlag && *deploymentTargetFlag && + deploymentTarget && *deploymentTarget) + { + flags += " "; + flags += deploymentTargetFlag; + flags += deploymentTarget; + } + } +} + + +//---------------------------------------------------------------------------- +void cmLocalGenerator::AddLanguageFlags(std::string& flags, + const char* lang, + const char* config) +{ + // Add language-specific flags. + std::string flagsVar = "CMAKE_"; + flagsVar += lang; + flagsVar += "_FLAGS"; + this->AddConfigVariableFlags(flags, flagsVar.c_str(), config); +} + +//---------------------------------------------------------------------------- +bool cmLocalGenerator::GetRealDependency(const char* inName, + const char* config, + std::string& dep) +{ + // Older CMake code may specify the dependency using the target + // output file rather than the target name. Such code would have + // been written before there was support for target properties that + // modify the name so stripping down to just the file name should + // produce the target name in this case. + std::string name = cmSystemTools::GetFilenameName(inName); + if(cmSystemTools::GetFilenameLastExtension(name) == ".exe") + { + name = cmSystemTools::GetFilenameWithoutLastExtension(name); + } + + // Look for a CMake target with the given name. + if(cmTarget* target = this->Makefile->FindTargetToUse(name.c_str())) + { + // make sure it is not just a coincidence that the target name + // found is part of the inName + if(cmSystemTools::FileIsFullPath(inName)) + { + std::string tLocation; + if(target->GetType() >= cmTarget::EXECUTABLE && + target->GetType() <= cmTarget::MODULE_LIBRARY) + { + tLocation = target->GetLocation(config); + tLocation = cmSystemTools::GetFilenamePath(tLocation); + tLocation = cmSystemTools::CollapseFullPath(tLocation.c_str()); + } + std::string depLocation = cmSystemTools::GetFilenamePath( + std::string(inName)); + depLocation = cmSystemTools::CollapseFullPath(depLocation.c_str()); + if(depLocation != tLocation) + { + // it is a full path to a depend that has the same name + // as a target but is in a different location so do not use + // the target as the depend + dep = inName; + return true; + } + } + switch (target->GetType()) + { + case cmTarget::EXECUTABLE: + case cmTarget::STATIC_LIBRARY: + case cmTarget::SHARED_LIBRARY: + case cmTarget::MODULE_LIBRARY: + case cmTarget::UNKNOWN_LIBRARY: + dep = target->GetLocation(config); + return true; + case cmTarget::OBJECT_LIBRARY: + // An object library has no single file on which to depend. + // This was listed to get the target-level dependency. + return false; + case cmTarget::UTILITY: + case cmTarget::GLOBAL_TARGET: + // A utility target has no file on which to depend. This was listed + // only to get the target-level dependency. + return false; + } + } + + // The name was not that of a CMake target. It must name a file. + if(cmSystemTools::FileIsFullPath(inName)) + { + // This is a full path. Return it as given. + dep = inName; + return true; + } + + // Check for a source file in this directory that matches the + // dependency. + if(cmSourceFile* sf = this->Makefile->GetSource(inName)) + { + dep = sf->GetFullPath(); + return true; + } + + // Treat the name as relative to the source directory in which it + // was given. + dep = this->Makefile->GetCurrentDirectory(); + dep += "/"; + dep += inName; + return true; +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::AddSharedFlags(std::string& flags, + const char* lang, + bool shared) +{ + std::string flagsVar; + + // Add flags for dealing with shared libraries for this language. + if(shared) + { + flagsVar = "CMAKE_SHARED_LIBRARY_"; + flagsVar += lang; + flagsVar += "_FLAGS"; + this->AppendFlags(flags, this->Makefile->GetDefinition(flagsVar.c_str())); + } +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::AddCMP0018Flags(std::string &flags, cmTarget* target, + std::string const& lang) +{ + int targetType = target->GetType(); + + bool shared = ((targetType == cmTarget::SHARED_LIBRARY) || + (targetType == cmTarget::MODULE_LIBRARY)); + + if (this->GetShouldUseOldFlags(shared, lang)) + { + this->AddSharedFlags(flags, lang.c_str(), shared); + } + else + { + // Add position independendent flags, if needed. + if (target->GetPropertyAsBool("POSITION_INDEPENDENT_CODE")) + { + this->AddPositionIndependentFlags(flags, lang, targetType); + } + if (shared) + { + this->AppendFeatureOptions(flags, lang.c_str(), "DLL"); + } + } +} + +//---------------------------------------------------------------------------- +bool cmLocalGenerator::GetShouldUseOldFlags(bool shared, + const std::string &lang) const +{ + std::string originalFlags = + this->GlobalGenerator->GetSharedLibFlagsForLanguage(lang); + if (shared) + { + std::string flagsVar = "CMAKE_SHARED_LIBRARY_"; + flagsVar += lang; + flagsVar += "_FLAGS"; + const char* flags = + this->Makefile->GetSafeDefinition(flagsVar.c_str()); + + if (flags && flags != originalFlags) + { + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0018)) + { + case cmPolicies::WARN: + { + cmOStringStream e; + e << "Variable " << flagsVar << " has been modified. CMake " + "will ignore the POSITION_INDEPENDENT_CODE target property for " + "shared libraries and will use the " << flagsVar << " variable " + "instead. This may cause errors if the original content of " + << flagsVar << " was removed.\n" + << this->Makefile->GetPolicies()->GetPolicyWarning( + cmPolicies::CMP0018); + + this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, e.str()); + // fall through to OLD behaviour + } + case cmPolicies::OLD: + return true; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + default: + return false; + } + } + } + return false; +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::AddPositionIndependentFlags(std::string& flags, + std::string const& lang, + int targetType) +{ + const char* picFlags = 0; + + if(targetType == cmTarget::EXECUTABLE) + { + std::string flagsVar = "CMAKE_"; + flagsVar += lang; + flagsVar += "_COMPILE_OPTIONS_PIE"; + picFlags = this->Makefile->GetSafeDefinition(flagsVar.c_str()); + } + if (!picFlags) + { + std::string flagsVar = "CMAKE_"; + flagsVar += lang; + flagsVar += "_COMPILE_OPTIONS_PIC"; + picFlags = this->Makefile->GetSafeDefinition(flagsVar.c_str()); + } + if (picFlags) + { + std::vector<std::string> options; + cmSystemTools::ExpandListArgument(picFlags, options); + for(std::vector<std::string>::const_iterator oi = options.begin(); + oi != options.end(); ++oi) + { + this->AppendFlags(flags, this->EscapeForShell(oi->c_str()).c_str()); + } + } +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::AddConfigVariableFlags(std::string& flags, + const char* var, + const char* config) +{ + // Add the flags from the variable itself. + std::string flagsVar = var; + this->AppendFlags(flags, this->Makefile->GetDefinition(flagsVar.c_str())); + // Add the flags from the build-type specific variable. + if(config && *config) + { + flagsVar += "_"; + flagsVar += cmSystemTools::UpperCase(config); + this->AppendFlags(flags, this->Makefile->GetDefinition(flagsVar.c_str())); + } +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::AppendFlags(std::string& flags, + const char* newFlags) +{ + if(newFlags && *newFlags) + { + std::string newf = newFlags; + if(flags.size()) + { + flags += " "; + } + flags += newFlags; + } +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::AppendDefines(std::string& defines, + const char* defines_list, + const char* lang) +{ + // Short-circuit if there are no definitions. + if(!defines_list) + { + return; + } + + // Expand the list of definitions. + std::vector<std::string> defines_vec; + cmSystemTools::ExpandListArgument(defines_list, defines_vec); + + // Short-circuit if there are no definitions. + if(defines_vec.empty()) + { + return; + } + + // Lookup the define flag for the current language. + std::string dflag = "-D"; + if(lang) + { + std::string defineFlagVar = "CMAKE_"; + defineFlagVar += lang; + defineFlagVar += "_DEFINE_FLAG"; + const char* df = this->Makefile->GetDefinition(defineFlagVar.c_str()); + if(df && *df) + { + dflag = df; + } + } + + // Add each definition to the command line with appropriate escapes. + const char* dsep = defines.empty()? "" : " "; + for(std::vector<std::string>::const_iterator di = defines_vec.begin(); + di != defines_vec.end(); ++di) + { + // Skip unsupported definitions. + if(!this->CheckDefinition(*di)) + { + continue; + } + + // Separate from previous definitions. + defines += dsep; + dsep = " "; + + // Append the definition with proper escaping. + defines += dflag; + if(this->WatcomWMake) + { + // The Watcom compiler does its own command line parsing instead + // of using the windows shell rules. Definitions are one of + // -DNAME + // -DNAME=<cpp-token> + // -DNAME="c-string with spaces and other characters(?@#$)" + // + // Watcom will properly parse each of these cases from the + // command line without any escapes. However we still have to + // get the '$' and '#' characters through WMake as '$$' and + // '$#'. + for(const char* c = di->c_str(); *c; ++c) + { + if(*c == '$' || *c == '#') + { + defines += '$'; + } + defines += *c; + } + } + else + { + // Make the definition appear properly on the command line. Use + // -DNAME="value" instead of -D"NAME=value" to help VS6 parser. + std::string::size_type eq = di->find("="); + defines += di->substr(0, eq); + if(eq != di->npos) + { + defines += "="; + defines += this->EscapeForShell(di->c_str() + eq + 1, true); + } + } + } +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::AppendFeatureOptions( + std::string& flags, const char* lang, const char* feature) +{ + std::string optVar = "CMAKE_"; + optVar += lang; + optVar += "_COMPILE_OPTIONS_"; + optVar += feature; + if(const char* optionList = this->Makefile->GetDefinition(optVar.c_str())) + { + std::vector<std::string> options; + cmSystemTools::ExpandListArgument(optionList, options); + for(std::vector<std::string>::const_iterator oi = options.begin(); + oi != options.end(); ++oi) + { + this->AppendFlags(flags, this->EscapeForShell(oi->c_str()).c_str()); + } + } +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator::ConstructComment(const cmCustomCommand& cc, + const char* default_comment) +{ + // Check for a comment provided with the command. + if(cc.GetComment()) + { + return cc.GetComment(); + } + + // Construct a reasonable default comment if possible. + if(!cc.GetOutputs().empty()) + { + std::string comment; + comment = "Generating "; + const char* sep = ""; + for(std::vector<std::string>::const_iterator o = cc.GetOutputs().begin(); + o != cc.GetOutputs().end(); ++o) + { + comment += sep; + comment += this->Convert(o->c_str(), cmLocalGenerator::START_OUTPUT); + sep = ", "; + } + return comment; + } + + // Otherwise use the provided default. + return default_comment; +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator::ConvertToOptionallyRelativeOutputPath(const char* remote) +{ + return this->Convert(remote, START_OUTPUT, SHELL, true); +} + +//---------------------------------------------------------------------------- +const char* cmLocalGenerator::GetRelativeRootPath(RelativeRoot relroot) +{ + switch (relroot) + { + case HOME: return this->Makefile->GetHomeDirectory(); + case START: return this->Makefile->GetStartDirectory(); + case HOME_OUTPUT: return this->Makefile->GetHomeOutputDirectory(); + case START_OUTPUT: return this->Makefile->GetStartOutputDirectory(); + default: break; + } + return 0; +} + +//---------------------------------------------------------------------------- +std::string cmLocalGenerator::Convert(const char* source, + RelativeRoot relative, + OutputFormat output, + bool optional) +{ + // Make sure the relative path conversion components are set. + if(!this->PathConversionsSetup) + { + this->SetupPathConversions(); + this->PathConversionsSetup = true; + } + + // Convert the path to a relative path. + std::string result = source; + + if (!optional || this->UseRelativePaths) + { + switch (relative) + { + case HOME: + //result = cmSystemTools::CollapseFullPath(result.c_str()); + result = this->ConvertToRelativePath(this->HomeDirectoryComponents, + result.c_str()); + break; + case START: + //result = cmSystemTools::CollapseFullPath(result.c_str()); + result = this->ConvertToRelativePath(this->StartDirectoryComponents, + result.c_str()); + break; + case HOME_OUTPUT: + //result = cmSystemTools::CollapseFullPath(result.c_str()); + result = + this->ConvertToRelativePath(this->HomeOutputDirectoryComponents, + result.c_str()); + break; + case START_OUTPUT: + //result = cmSystemTools::CollapseFullPath(result.c_str()); + result = + this->ConvertToRelativePath(this->StartOutputDirectoryComponents, + result.c_str()); + break; + case FULL: + result = cmSystemTools::CollapseFullPath(result.c_str()); + break; + case NONE: + break; + } + } + return this->ConvertToOutputFormat(result.c_str(), output); +} + +//---------------------------------------------------------------------------- +std::string cmLocalGenerator::ConvertToOutputFormat(const char* source, + OutputFormat output) +{ + std::string result = source; + // Convert it to an output path. + if (output == MAKEFILE) + { + result = cmSystemTools::ConvertToOutputPath(result.c_str()); + } + else if( output == SHELL) + { + // For the MSYS shell convert drive letters to posix paths, so + // that c:/some/path becomes /c/some/path. This is needed to + // avoid problems with the shell path translation. + if(this->MSYSShell && !this->LinkScriptShell) + { + if(result.size() > 2 && result[1] == ':') + { + result[1] = result[0]; + result[0] = '/'; + } + } + if(this->WindowsShell) + { + std::string::size_type pos = 0; + while((pos = result.find('/', pos)) != std::string::npos) + { + result[pos] = '\\'; + pos++; + } + } + result = this->EscapeForShell(result.c_str(), true, false); + } + else if(output == RESPONSE) + { + result = this->EscapeForShell(result.c_str(), false, false); + } + return result; +} + +//---------------------------------------------------------------------------- +std::string cmLocalGenerator::Convert(RelativeRoot remote, + const char* local, + OutputFormat output, + bool optional) +{ + const char* remotePath = this->GetRelativeRootPath(remote); + + // The relative root must have a path (i.e. not FULL or NONE) + assert(remotePath != 0); + + if(local && (!optional || this->UseRelativePaths)) + { + std::vector<std::string> components; + cmSystemTools::SplitPath(local, components); + std::string result = this->ConvertToRelativePath(components, remotePath); + return this->ConvertToOutputFormat(result.c_str(), output); + } + else + { + return this->ConvertToOutputFormat(remotePath, output); + } +} + +//---------------------------------------------------------------------------- +std::string cmLocalGenerator::FindRelativePathTopSource() +{ + // Relative path conversion within a single tree managed by CMake is + // safe. We can use our parent relative path top if and only if + // this is a subdirectory of that top. + if(cmLocalGenerator* parent = this->GetParent()) + { + std::string parentTop = parent->FindRelativePathTopSource(); + if(cmSystemTools::IsSubDirectory( + this->Makefile->GetStartDirectory(), parentTop.c_str())) + { + return parentTop; + } + } + + // Otherwise this directory itself is the new top. + return this->Makefile->GetStartDirectory(); +} + +//---------------------------------------------------------------------------- +std::string cmLocalGenerator::FindRelativePathTopBinary() +{ + // Relative path conversion within a single tree managed by CMake is + // safe. We can use our parent relative path top if and only if + // this is a subdirectory of that top. + if(cmLocalGenerator* parent = this->GetParent()) + { + std::string parentTop = parent->FindRelativePathTopBinary(); + if(cmSystemTools::IsSubDirectory( + this->Makefile->GetStartOutputDirectory(), parentTop.c_str())) + { + return parentTop; + } + } + + // Otherwise this directory itself is the new top. + return this->Makefile->GetStartOutputDirectory(); +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::ConfigureRelativePaths() +{ + // Relative path conversion inside the source tree is not used to + // construct relative paths passed to build tools so it is safe to + // even when the source is a network path. + std::string source = this->FindRelativePathTopSource(); + this->RelativePathTopSource = source; + + // The current working directory on Windows cannot be a network + // path. Therefore relative paths cannot work when the binary tree + // is a network path. + std::string binary = this->FindRelativePathTopBinary(); + if(binary.size() < 2 || binary.substr(0, 2) != "//") + { + this->RelativePathTopBinary = binary; + } + else + { + this->RelativePathTopBinary = ""; + } +} + +//---------------------------------------------------------------------------- +static bool cmLocalGeneratorNotAbove(const char* a, const char* b) +{ + return (cmSystemTools::ComparePath(a, b) || + cmSystemTools::IsSubDirectory(a, b)); +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator::ConvertToRelativePath(const std::vector<std::string>& local, + const char* in_remote, bool force) +{ + // The path should never be quoted. + assert(in_remote[0] != '\"'); + + // The local path should never have a trailing slash. + assert(local.size() > 0 && !(local[local.size()-1] == "")); + + // If the path is already relative then just return the path. + if(!cmSystemTools::FileIsFullPath(in_remote)) + { + return in_remote; + } + + // Make sure relative path conversion is configured. + if(!this->RelativePathsConfigured) + { + this->ConfigureRelativePaths(); + this->RelativePathsConfigured = true; + } + + if(!force) + { + // Skip conversion if the path and local are not both in the source + // or both in the binary tree. + std::string local_path = cmSystemTools::JoinPath(local); + if(!((cmLocalGeneratorNotAbove(local_path.c_str(), + this->RelativePathTopBinary.c_str()) && + cmLocalGeneratorNotAbove(in_remote, + this->RelativePathTopBinary.c_str())) || + (cmLocalGeneratorNotAbove(local_path.c_str(), + this->RelativePathTopSource.c_str()) && + cmLocalGeneratorNotAbove(in_remote, + this->RelativePathTopSource.c_str())))) + { + return in_remote; + } + } + + // Identify the longest shared path component between the remote + // path and the local path. + std::vector<std::string> remote; + cmSystemTools::SplitPath(in_remote, remote); + unsigned int common=0; + while(common < remote.size() && + common < local.size() && + cmSystemTools::ComparePath(remote[common].c_str(), + local[common].c_str())) + { + ++common; + } + + // If no part of the path is in common then return the full path. + if(common == 0) + { + return in_remote; + } + + // If the entire path is in common then just return a ".". + if(common == remote.size() && + common == local.size()) + { + return "."; + } + + // If the entire path is in common except for a trailing slash then + // just return a "./". + if(common+1 == remote.size() && + remote[common].size() == 0 && + common == local.size()) + { + return "./"; + } + + // Construct the relative path. + std::string relative; + + // First add enough ../ to get up to the level of the shared portion + // of the path. Leave off the trailing slash. Note that the last + // component of local will never be empty because local should never + // have a trailing slash. + for(unsigned int i=common; i < local.size(); ++i) + { + relative += ".."; + if(i < local.size()-1) + { + relative += "/"; + } + } + + // Now add the portion of the destination path that is not included + // in the shared portion of the path. Add a slash the first time + // only if there was already something in the path. If there was a + // trailing slash in the input then the last iteration of the loop + // will add a slash followed by an empty string which will preserve + // the trailing slash in the output. + for(unsigned int i=common; i < remote.size(); ++i) + { + if(relative.size() > 0) + { + relative += "/"; + } + relative += remote[i]; + } + + // Finally return the path. + return relative; +} + +//---------------------------------------------------------------------------- +void +cmLocalGenerator +::GenerateTargetInstallRules( + std::ostream& os, const char* config, + std::vector<std::string> const& configurationTypes) +{ + // Convert the old-style install specification from each target to + // an install generator and run it. + cmTargets& tgts = this->Makefile->GetTargets(); + for(cmTargets::iterator l = tgts.begin(); l != tgts.end(); ++l) + { + // Include the user-specified pre-install script for this target. + if(const char* preinstall = l->second.GetProperty("PRE_INSTALL_SCRIPT")) + { + cmInstallScriptGenerator g(preinstall, false, 0); + g.Generate(os, config, configurationTypes); + } + + // Install this target if a destination is given. + if(l->second.GetInstallPath() != "") + { + // Compute the full install destination. Note that converting + // to unix slashes also removes any trailing slash. + // We also skip over the leading slash given by the user. + std::string destination = l->second.GetInstallPath().substr(1); + cmSystemTools::ConvertToUnixSlashes(destination); + if(destination.empty()) + { + destination = "."; + } + + // Generate the proper install generator for this target type. + switch(l->second.GetType()) + { + case cmTarget::EXECUTABLE: + case cmTarget::STATIC_LIBRARY: + case cmTarget::MODULE_LIBRARY: + { + // Use a target install generator. + cmInstallTargetGenerator g(l->second, destination.c_str(), false); + g.Generate(os, config, configurationTypes); + } + break; + case cmTarget::SHARED_LIBRARY: + { +#if defined(_WIN32) || defined(__CYGWIN__) + // Special code to handle DLL. Install the import library + // to the normal destination and the DLL to the runtime + // destination. + cmInstallTargetGenerator g1(l->second, destination.c_str(), true); + g1.Generate(os, config, configurationTypes); + // We also skip over the leading slash given by the user. + destination = l->second.GetRuntimeInstallPath().substr(1); + cmSystemTools::ConvertToUnixSlashes(destination); + cmInstallTargetGenerator g2(l->second, destination.c_str(), false); + g2.Generate(os, config, configurationTypes); +#else + // Use a target install generator. + cmInstallTargetGenerator g(l->second, destination.c_str(), false); + g.Generate(os, config, configurationTypes); +#endif + } + break; + default: + break; + } + } + + // Include the user-specified post-install script for this target. + if(const char* postinstall = l->second.GetProperty("POST_INSTALL_SCRIPT")) + { + cmInstallScriptGenerator g(postinstall, false, 0); + g.Generate(os, config, configurationTypes); + } + } +} + +#if defined(CM_LG_ENCODE_OBJECT_NAMES) +static std::string cmLocalGeneratorMD5(const char* input) +{ + char md5out[32]; + cmsysMD5* md5 = cmsysMD5_New(); + cmsysMD5_Initialize(md5); + cmsysMD5_Append(md5, reinterpret_cast<unsigned char const*>(input), -1); + cmsysMD5_FinalizeHex(md5, md5out); + cmsysMD5_Delete(md5); + return std::string(md5out, 32); +} + +static bool +cmLocalGeneratorShortenObjectName(std::string& objName, + std::string::size_type max_len) +{ + // Replace the beginning of the path portion of the object name with + // its own md5 sum. + std::string::size_type pos = objName.find('/', objName.size()-max_len+32); + if(pos != objName.npos) + { + std::string md5name = cmLocalGeneratorMD5(objName.substr(0, pos).c_str()); + md5name += objName.substr(pos); + objName = md5name; + + // The object name is now short enough. + return true; + } + else + { + // The object name could not be shortened enough. + return false; + } +} + +static +bool cmLocalGeneratorCheckObjectName(std::string& objName, + std::string::size_type dir_len, + std::string::size_type max_total_len) +{ + // Enforce the maximum file name length if possible. + std::string::size_type max_obj_len = max_total_len; + if(dir_len < max_total_len) + { + max_obj_len = max_total_len - dir_len; + if(objName.size() > max_obj_len) + { + // The current object file name is too long. Try to shorten it. + return cmLocalGeneratorShortenObjectName(objName, max_obj_len); + } + else + { + // The object file name is short enough. + return true; + } + } + else + { + // The build directory in which the object will be stored is + // already too deep. + return false; + } +} +#endif + +//---------------------------------------------------------------------------- +std::string& +cmLocalGenerator +::CreateSafeUniqueObjectFileName(const char* sin, + std::string const& dir_max) +{ + // Look for an existing mapped name for this object file. + std::map<cmStdString,cmStdString>::iterator it = + this->UniqueObjectNamesMap.find(sin); + + // If no entry exists create one. + if(it == this->UniqueObjectNamesMap.end()) + { + // Start with the original name. + std::string ssin = sin; + + // Avoid full paths by removing leading slashes. + std::string::size_type pos = 0; + for(;pos < ssin.size() && ssin[pos] == '/'; ++pos) + { + } + ssin = ssin.substr(pos); + + // Avoid full paths by removing colons. + cmSystemTools::ReplaceString(ssin, ":", "_"); + + // Avoid relative paths that go up the tree. + cmSystemTools::ReplaceString(ssin, "../", "__/"); + + // Avoid spaces. + cmSystemTools::ReplaceString(ssin, " ", "_"); + + // Mangle the name if necessary. + if(this->Makefile->IsOn("CMAKE_MANGLE_OBJECT_FILE_NAMES")) + { + bool done; + int cc = 0; + char rpstr[100]; + sprintf(rpstr, "_p_"); + cmSystemTools::ReplaceString(ssin, "+", rpstr); + std::string sssin = sin; + do + { + done = true; + for ( it = this->UniqueObjectNamesMap.begin(); + it != this->UniqueObjectNamesMap.end(); + ++ it ) + { + if ( it->second == ssin ) + { + done = false; + } + } + if ( done ) + { + break; + } + sssin = ssin; + cmSystemTools::ReplaceString(ssin, "_p_", rpstr); + sprintf(rpstr, "_p%d_", cc++); + } + while ( !done ); + } + +#if defined(CM_LG_ENCODE_OBJECT_NAMES) + if(!cmLocalGeneratorCheckObjectName(ssin, dir_max.size(), + this->ObjectPathMax)) + { + // Warn if this is the first time the path has been seen. + if(this->ObjectMaxPathViolations.insert(dir_max).second) + { + cmOStringStream m; + m << "The object file directory\n" + << " " << dir_max << "\n" + << "has " << dir_max.size() << " characters. " + << "The maximum full path to an object file is " + << this->ObjectPathMax << " characters " + << "(see CMAKE_OBJECT_PATH_MAX). " + << "Object file\n" + << " " << ssin << "\n" + << "cannot be safely placed under this directory. " + << "The build may not work correctly."; + this->Makefile->IssueMessage(cmake::WARNING, m.str()); + } + } +#else + (void)dir_max; +#endif + + // Insert the newly mapped object file name. + std::map<cmStdString, cmStdString>::value_type e(sin, ssin); + it = this->UniqueObjectNamesMap.insert(e).first; + } + + // Return the map entry. + return it->second; +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator +::GetObjectFileNameWithoutTarget(const cmSourceFile& source, + std::string const& dir_max, + bool* hasSourceExtension) +{ + // Construct the object file name using the full path to the source + // file which is its only unique identification. + const char* fullPath = source.GetFullPath().c_str(); + + // Try referencing the source relative to the source tree. + std::string relFromSource = this->Convert(fullPath, START); + assert(!relFromSource.empty()); + bool relSource = !cmSystemTools::FileIsFullPath(relFromSource.c_str()); + bool subSource = relSource && relFromSource[0] != '.'; + + // Try referencing the source relative to the binary tree. + std::string relFromBinary = this->Convert(fullPath, START_OUTPUT); + assert(!relFromBinary.empty()); + bool relBinary = !cmSystemTools::FileIsFullPath(relFromBinary.c_str()); + bool subBinary = relBinary && relFromBinary[0] != '.'; + + // Select a nice-looking reference to the source file to construct + // the object file name. + std::string objectName; + if((relSource && !relBinary) || (subSource && !subBinary)) + { + objectName = relFromSource; + } + else if((relBinary && !relSource) || (subBinary && !subSource)) + { + objectName = relFromBinary; + } + else if(relFromBinary.length() < relFromSource.length()) + { + objectName = relFromBinary; + } + else + { + objectName = relFromSource; + } + + // if it is still a full path check for the try compile case + // try compile never have in source sources, and should not + // have conflicting source file names in the same target + if(cmSystemTools::FileIsFullPath(objectName.c_str())) + { + if(this->GetGlobalGenerator()->GetCMakeInstance()->GetIsInTryCompile()) + { + objectName = cmSystemTools::GetFilenameName(source.GetFullPath()); + } + } + + // Replace the original source file extension with the object file + // extension. + bool keptSourceExtension = true; + if(!source.GetPropertyAsBool("KEEP_EXTENSION")) + { + // Decide whether this language wants to replace the source + // extension with the object extension. For CMake 2.4 + // compatibility do this by default. + bool replaceExt = this->NeedBackwardsCompatibility(2, 4); + if(!replaceExt) + { + if(const char* lang = source.GetLanguage()) + { + std::string repVar = "CMAKE_"; + repVar += lang; + repVar += "_OUTPUT_EXTENSION_REPLACE"; + replaceExt = this->Makefile->IsOn(repVar.c_str()); + } + } + + // Remove the source extension if it is to be replaced. + if(replaceExt) + { + keptSourceExtension = false; + std::string::size_type dot_pos = objectName.rfind("."); + if(dot_pos != std::string::npos) + { + objectName = objectName.substr(0, dot_pos); + } + } + + // Store the new extension. + objectName += + this->GlobalGenerator->GetLanguageOutputExtension(source); + } + if(hasSourceExtension) + { + *hasSourceExtension = keptSourceExtension; + } + + // Convert to a safe name. + return this->CreateSafeUniqueObjectFileName(objectName.c_str(), dir_max); +} + +//---------------------------------------------------------------------------- +const char* +cmLocalGenerator +::GetSourceFileLanguage(const cmSourceFile& source) +{ + return source.GetLanguage(); +} + +//---------------------------------------------------------------------------- +std::string cmLocalGenerator::EscapeForShellOldStyle(const char* str) +{ + std::string result; +#if defined(_WIN32) && !defined(__CYGWIN__) + // if there are spaces + std::string temp = str; + if (temp.find(" ") != std::string::npos && + temp.find("\"")==std::string::npos) + { + result = "\""; + result += str; + result += "\""; + return result; + } + return str; +#else + for(const char* ch = str; *ch != '\0'; ++ch) + { + if(*ch == ' ') + { + result += '\\'; + } + result += *ch; + } + return result; +#endif +} + +//---------------------------------------------------------------------------- +static bool cmLocalGeneratorIsShellOperator(const char* str) +{ + if(strcmp(str, "<") == 0 || + strcmp(str, ">") == 0 || + strcmp(str, "<<") == 0 || + strcmp(str, ">>") == 0 || + strcmp(str, "|") == 0 || + strcmp(str, "||") == 0 || + strcmp(str, "&&") == 0 || + strcmp(str, "&>") == 0 || + strcmp(str, "1>") == 0 || + strcmp(str, "2>") == 0 || + strcmp(str, "2>&1") == 0 || + strcmp(str, "1>&2") == 0) + { + return true; + } + return false; +} + +//---------------------------------------------------------------------------- +std::string cmLocalGenerator::EscapeForShell(const char* str, bool makeVars, + bool forEcho) +{ + // Do not escape shell operators. + if(cmLocalGeneratorIsShellOperator(str)) + { + return str; + } + + // Compute the flags for the target shell environment. + int flags = 0; + if(this->WindowsVSIDE) + { + flags |= cmsysSystem_Shell_Flag_VSIDE; + } + else if(!this->LinkScriptShell) + { + flags |= cmsysSystem_Shell_Flag_Make; + } + if(makeVars) + { + flags |= cmsysSystem_Shell_Flag_AllowMakeVariables; + } + if(forEcho) + { + flags |= cmsysSystem_Shell_Flag_EchoWindows; + } + if(this->WatcomWMake) + { + flags |= cmsysSystem_Shell_Flag_WatcomWMake; + } + if(this->MinGWMake) + { + flags |= cmsysSystem_Shell_Flag_MinGWMake; + } + if(this->NMake) + { + flags |= cmsysSystem_Shell_Flag_NMake; + } + + // Compute the buffer size needed. + int size = (this->WindowsShell ? + cmsysSystem_Shell_GetArgumentSizeForWindows(str, flags) : + cmsysSystem_Shell_GetArgumentSizeForUnix(str, flags)); + + // Compute the shell argument itself. + std::vector<char> arg(size); + if(this->WindowsShell) + { + cmsysSystem_Shell_GetArgumentForWindows(str, &arg[0], flags); + } + else + { + cmsysSystem_Shell_GetArgumentForUnix(str, &arg[0], flags); + } + return std::string(&arg[0]); +} + +//---------------------------------------------------------------------------- +std::string cmLocalGenerator::EscapeForCMake(const char* str) +{ + // Always double-quote the argument to take care of most escapes. + std::string result = "\""; + for(const char* c = str; *c; ++c) + { + if(*c == '"') + { + // Escape the double quote to avoid ending the argument. + result += "\\\""; + } + else if(*c == '$') + { + // Escape the dollar to avoid expanding variables. + result += "\\$"; + } + else if(*c == '\\') + { + // Escape the backslash to avoid other escapes. + result += "\\\\"; + } + else + { + // Other characters will be parsed correctly. + result += *c; + } + } + result += "\""; + return result; +} + +//---------------------------------------------------------------------------- +cmLocalGenerator::FortranFormat +cmLocalGenerator::GetFortranFormat(const char* value) +{ + FortranFormat format = FortranFormatNone; + if(value && *value) + { + std::vector<std::string> fmt; + cmSystemTools::ExpandListArgument(value, fmt); + for(std::vector<std::string>::iterator fi = fmt.begin(); + fi != fmt.end(); ++fi) + { + if(*fi == "FIXED") + { + format = FortranFormatFixed; + } + if(*fi == "FREE") + { + format = FortranFormatFree; + } + } + } + return format; +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator::GetTargetDirectory(cmTarget const&) const +{ + cmSystemTools::Error("GetTargetDirectory" + " called on cmLocalGenerator"); + return ""; +} + +//---------------------------------------------------------------------------- +unsigned int cmLocalGenerator::GetBackwardsCompatibility() +{ + // The computed version may change until the project is fully + // configured. + if(!this->BackwardsCompatibilityFinal) + { + unsigned int major = 0; + unsigned int minor = 0; + unsigned int patch = 0; + if(const char* value + = this->Makefile->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY")) + { + switch(sscanf(value, "%u.%u.%u", &major, &minor, &patch)) + { + case 2: patch = 0; break; + case 1: minor = 0; patch = 0; break; + default: break; + } + } + this->BackwardsCompatibility = CMake_VERSION_ENCODE(major, minor, patch); + this->BackwardsCompatibilityFinal = this->Configured; + } + + return this->BackwardsCompatibility; +} + +//---------------------------------------------------------------------------- +bool cmLocalGenerator::NeedBackwardsCompatibility(unsigned int major, + unsigned int minor, + unsigned int patch) +{ + // Check the policy to decide whether to pay attention to this + // variable. + switch(this->Makefile->GetPolicyStatus(cmPolicies::CMP0001)) + { + case cmPolicies::WARN: + // WARN is just OLD without warning because user code does not + // always affect whether this check is done. + case cmPolicies::OLD: + // Old behavior is to check the variable. + break; + case cmPolicies::NEW: + // New behavior is to ignore the variable. + return false; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + // This will never be the case because the only way to require + // the setting is to require the user to specify version policy + // 2.6 or higher. Once we add that requirement then this whole + // method can be removed anyway. + return false; + } + + // Compatibility is needed if CMAKE_BACKWARDS_COMPATIBILITY is set + // equal to or lower than the given version. + unsigned int actual_compat = this->GetBackwardsCompatibility(); + return (actual_compat && + actual_compat <= CMake_VERSION_ENCODE(major, minor, patch)); +} + +//---------------------------------------------------------------------------- +bool cmLocalGenerator::CheckDefinition(std::string const& define) const +{ + // Many compilers do not support -DNAME(arg)=sdf so we disable it. + bool function_style = false; + for(const char* c = define.c_str(); *c && *c != '='; ++c) + { + if(*c == '(') + { + function_style = true; + break; + } + } + if(function_style) + { + cmOStringStream e; + e << "WARNING: Function-style preprocessor definitions may not be " + << "passed on the compiler command line because many compilers " + << "do not support it.\n" + << "CMake is dropping a preprocessor definition: " << define << "\n" + << "Consider defining the macro in a (configured) header file.\n"; + cmSystemTools::Message(e.str().c_str()); + return false; + } + + // Many compilers do not support # in the value so we disable it. + if(define.find_first_of("#") != define.npos) + { + cmOStringStream e; + e << "WARNING: Preprocessor definitions containing '#' may not be " + << "passed on the compiler command line because many compilers " + << "do not support it.\n" + << "CMake is dropping a preprocessor definition: " << define << "\n" + << "Consider defining the macro in a (configured) header file.\n"; + cmSystemTools::Message(e.str().c_str()); + return false; + } + + // Assume it is supported. + return true; +} + +//---------------------------------------------------------------------------- +static void cmLGInfoProp(cmMakefile* mf, cmTarget* target, const char* prop) +{ + if(const char* val = target->GetProperty(prop)) + { + mf->AddDefinition(prop, val); + } +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::GenerateAppleInfoPList(cmTarget* target, + const char* targetName, + const char* fname) +{ + // Find the Info.plist template. + const char* in = target->GetProperty("MACOSX_BUNDLE_INFO_PLIST"); + std::string inFile = (in && *in)? in : "MacOSXBundleInfo.plist.in"; + if(!cmSystemTools::FileIsFullPath(inFile.c_str())) + { + std::string inMod = this->Makefile->GetModulesFile(inFile.c_str()); + if(!inMod.empty()) + { + inFile = inMod; + } + } + if(!cmSystemTools::FileExists(inFile.c_str(), true)) + { + cmOStringStream e; + e << "Target " << target->GetName() << " Info.plist template \"" + << inFile << "\" could not be found."; + cmSystemTools::Error(e.str().c_str()); + return; + } + + // Convert target properties to variables in an isolated makefile + // scope to configure the file. If properties are set they will + // override user make variables. If not the configuration will fall + // back to the directory-level values set by the user. + cmMakefile* mf = this->Makefile; + mf->PushScope(); + mf->AddDefinition("MACOSX_BUNDLE_EXECUTABLE_NAME", targetName); + cmLGInfoProp(mf, target, "MACOSX_BUNDLE_INFO_STRING"); + cmLGInfoProp(mf, target, "MACOSX_BUNDLE_ICON_FILE"); + cmLGInfoProp(mf, target, "MACOSX_BUNDLE_GUI_IDENTIFIER"); + cmLGInfoProp(mf, target, "MACOSX_BUNDLE_LONG_VERSION_STRING"); + cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_NAME"); + cmLGInfoProp(mf, target, "MACOSX_BUNDLE_SHORT_VERSION_STRING"); + cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_VERSION"); + cmLGInfoProp(mf, target, "MACOSX_BUNDLE_COPYRIGHT"); + mf->ConfigureFile(inFile.c_str(), fname, false, false, false); + mf->PopScope(); +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::GenerateFrameworkInfoPList(cmTarget* target, + const char* targetName, + const char* fname) +{ + // Find the Info.plist template. + const char* in = target->GetProperty("MACOSX_FRAMEWORK_INFO_PLIST"); + std::string inFile = (in && *in)? in : "MacOSXFrameworkInfo.plist.in"; + if(!cmSystemTools::FileIsFullPath(inFile.c_str())) + { + std::string inMod = this->Makefile->GetModulesFile(inFile.c_str()); + if(!inMod.empty()) + { + inFile = inMod; + } + } + if(!cmSystemTools::FileExists(inFile.c_str(), true)) + { + cmOStringStream e; + e << "Target " << target->GetName() << " Info.plist template \"" + << inFile << "\" could not be found."; + cmSystemTools::Error(e.str().c_str()); + return; + } + + // Convert target properties to variables in an isolated makefile + // scope to configure the file. If properties are set they will + // override user make variables. If not the configuration will fall + // back to the directory-level values set by the user. + cmMakefile* mf = this->Makefile; + mf->PushScope(); + mf->AddDefinition("MACOSX_FRAMEWORK_NAME", targetName); + cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_ICON_FILE"); + cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_IDENTIFIER"); + cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_SHORT_VERSION_STRING"); + cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_VERSION"); + mf->ConfigureFile(inFile.c_str(), fname, false, false, false); + mf->PopScope(); +} |