diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index a7adb5125..b434327ae 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -299,6 +299,8 @@ set(SRCS cmMakefileUtilityTargetGenerator.cxx cmOSXBundleGenerator.cxx cmOSXBundleGenerator.h + cmOutputConverter.cxx + cmOutputConverter.h cmNewLineStyle.h cmNewLineStyle.cxx cmOrderDirectories.cxx diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index fe71d0fb4..f8d1bee83 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -46,7 +46,7 @@ cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmLocalGenerator* parent, cmState::Snapshot snapshot) - : StateSnapshot(snapshot) + : cmOutputConverter(snapshot), StateSnapshot(snapshot) { assert(snapshot.IsValid()); this->GlobalGenerator = gg; @@ -58,7 +58,6 @@ cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, this->Makefile = new cmMakefile(this); - this->LinkScriptShell = false; this->EmitUniversalBinaryFlags = true; this->BackwardsCompatibility = 0; this->BackwardsCompatibilityFinal = false; @@ -1095,58 +1094,6 @@ void cmLocalGenerator::InsertRuleLauncher(std::string& s, cmTarget* target, } } -//---------------------------------------------------------------------------- -std::string -cmLocalGenerator::ConvertToOutputForExistingCommon(const std::string& remote, - std::string const& result, - OutputFormat format) -{ - // 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->GetState()->UseWindowsShell() && result.find(' ') != result.npos && - cmSystemTools::FileExists(remote.c_str())) - { - std::string tmp; - if(cmSystemTools::GetShortPath(remote, tmp)) - { - return this->ConvertToOutputFormat(tmp, format); - } - } - - // Otherwise, leave it unchanged. - return result; -} - -//---------------------------------------------------------------------------- -std::string -cmLocalGenerator::ConvertToOutputForExisting(const std::string& remote, - RelativeRoot local, - OutputFormat format) -{ - static_cast(local); - - // Perform standard conversion. - std::string result = this->ConvertToOutputFormat(remote, format); - - // Consider short-path. - return this->ConvertToOutputForExistingCommon(remote, result, format); -} - -//---------------------------------------------------------------------------- -std::string -cmLocalGenerator::ConvertToOutputForExisting(RelativeRoot remote, - const std::string& local, - OutputFormat format) -{ - // Perform standard conversion. - std::string result = this->Convert(remote, local, format, true); - - // Consider short-path. - const char* remotePath = this->GetRelativeRootPath(remote); - return this->ConvertToOutputForExistingCommon(remotePath, result, format); -} - //---------------------------------------------------------------------------- std::string cmLocalGenerator::ConvertToIncludeReference(std::string const& path, @@ -2603,225 +2550,6 @@ cmLocalGenerator::ConstructComment(cmCustomCommandGenerator const& ccg, return default_comment; } -//---------------------------------------------------------------------------- -const char* cmLocalGenerator::GetRelativeRootPath(RelativeRoot relroot) -{ - switch (relroot) - { - case HOME: return this->GetState()->GetSourceDirectory(); - case START: return this->StateSnapshot.GetCurrentSourceDirectory(); - case HOME_OUTPUT: return this->GetState()->GetBinaryDirectory(); - case START_OUTPUT: return this->StateSnapshot.GetCurrentBinaryDirectory(); - default: break; - } - return 0; -} - -//---------------------------------------------------------------------------- -std::string cmLocalGenerator::Convert(const std::string& source, - RelativeRoot relative, - OutputFormat output) -{ - std::string result = source; - - switch (relative) - { - case HOME: - result = this->ConvertToRelativePath( - this->GetState()->GetSourceDirectoryComponents(), result); - break; - case START: - result = this->ConvertToRelativePath( - this->StateSnapshot.GetCurrentSourceDirectoryComponents(), result); - break; - case HOME_OUTPUT: - result = this->ConvertToRelativePath( - this->GetState()->GetBinaryDirectoryComponents(), result); - break; - case START_OUTPUT: - result = this->ConvertToRelativePath( - this->StateSnapshot.GetCurrentBinaryDirectoryComponents(), result); - break; - case FULL: - result = cmSystemTools::CollapseFullPath(result); - break; - case NONE: - break; - - } - return this->ConvertToOutputFormat(result, output); -} - -//---------------------------------------------------------------------------- -std::string cmLocalGenerator::ConvertToOutputFormat(const std::string& source, - OutputFormat output) -{ - std::string result = source; - // Convert it to an output path. - if (output == MAKERULE) - { - result = cmSystemTools::ConvertToOutputPath(result.c_str()); - } - else if(output == SHELL || output == WATCOMQUOTE) - { - // 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->GetState()->UseMSYSShell() && !this->LinkScriptShell) - { - if(result.size() > 2 && result[1] == ':') - { - result[1] = result[0]; - result[0] = '/'; - } - } - if(this->GetState()->UseWindowsShell()) - { - std::replace(result.begin(), result.end(), '/', '\\'); - } - result = this->EscapeForShell(result, true, false, output == WATCOMQUOTE); - } - else if(output == RESPONSE) - { - result = this->EscapeForShell(result, false, false, false); - } - return result; -} - -//---------------------------------------------------------------------------- -std::string cmLocalGenerator::Convert(RelativeRoot remote, - const std::string& 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.empty() && !optional) - { - std::vector components; - cmSystemTools::SplitPath(local, components); - std::string result = this->ConvertToRelativePath(components, remotePath); - return this->ConvertToOutputFormat(result, output); - } - else - { - return this->ConvertToOutputFormat(remotePath, output); - } -} - -//---------------------------------------------------------------------------- -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& local, - const std::string& 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.empty() && !(local[local.size()-1] == "")); - - // If the path is already relative then just return the path. - if(!cmSystemTools::FileIsFullPath(in_remote.c_str())) - { - return in_remote; - } - - 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->StateSnapshot.GetRelativePathTopBinary()) && - cmLocalGeneratorNotAbove(in_remote.c_str(), - this->StateSnapshot.GetRelativePathTopBinary())) || - (cmLocalGeneratorNotAbove(local_path.c_str(), - this->StateSnapshot.GetRelativePathTopSource()) && - cmLocalGeneratorNotAbove(in_remote.c_str(), - this->StateSnapshot.GetRelativePathTopSource())))) - { - return in_remote; - } - } - - // Identify the longest shared path component between the remote - // path and the local path. - std::vector remote; - cmSystemTools::SplitPath(in_remote, remote); - unsigned int common=0; - while(common < remote.size() && - common < local.size() && - cmSystemTools::ComparePath(remote[common], - local[common])) - { - ++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].empty() && - 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. - - if(!relative.empty() && !remote.empty()) - { - relative += "/"; - } - relative += cmJoin(cmRange(remote).advance(common), "/"); - - // Finally return the path. - return relative; -} - //---------------------------------------------------------------------------- class cmInstallTargetGeneratorLocal: public cmInstallTargetGenerator { @@ -3214,150 +2942,6 @@ cmLocalGenerator return source.GetLanguage(); } -//---------------------------------------------------------------------------- -static bool cmLocalGeneratorIsShellOperator(const std::string& str) -{ - static std::set shellOperators; - if(shellOperators.empty()) - { - shellOperators.insert("<"); - shellOperators.insert(">"); - shellOperators.insert("<<"); - shellOperators.insert(">>"); - shellOperators.insert("|"); - shellOperators.insert("||"); - shellOperators.insert("&&"); - shellOperators.insert("&>"); - shellOperators.insert("1>"); - shellOperators.insert("2>"); - shellOperators.insert("2>&1"); - shellOperators.insert("1>&2"); - } - return shellOperators.count(str) > 0; -} - -//---------------------------------------------------------------------------- -std::string cmLocalGenerator::EscapeForShell(const std::string& str, - bool makeVars, - bool forEcho, - bool useWatcomQuote) -{ - // Do not escape shell operators. - if(cmLocalGeneratorIsShellOperator(str)) - { - return str; - } - - // Compute the flags for the target shell environment. - int flags = 0; - if(this->GetState()->UseWindowsVSIDE()) - { - 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(useWatcomQuote) - { - flags |= cmsysSystem_Shell_Flag_WatcomQuote; - } - if(this->GetState()->UseWatcomWMake()) - { - flags |= cmsysSystem_Shell_Flag_WatcomWMake; - } - if(this->GetState()->UseMinGWMake()) - { - flags |= cmsysSystem_Shell_Flag_MinGWMake; - } - if(this->GetState()->UseNMake()) - { - flags |= cmsysSystem_Shell_Flag_NMake; - } - - // Compute the buffer size needed. - int size = (this->GetState()->UseWindowsShell() ? - cmsysSystem_Shell_GetArgumentSizeForWindows(str.c_str(), flags) : - cmsysSystem_Shell_GetArgumentSizeForUnix(str.c_str(), flags)); - - // Compute the shell argument itself. - std::vector arg(size); - if(this->GetState()->UseWindowsShell()) - { - cmsysSystem_Shell_GetArgumentForWindows(str.c_str(), &arg[0], flags); - } - else - { - cmsysSystem_Shell_GetArgumentForUnix(str.c_str(), &arg[0], flags); - } - return std::string(&arg[0]); -} - -//---------------------------------------------------------------------------- -std::string cmLocalGenerator::EscapeForCMake(const std::string& str) -{ - // Always double-quote the argument to take care of most escapes. - std::string result = "\""; - for(const char* c = str.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 fmt; - cmSystemTools::ExpandListArgument(value, fmt); - for(std::vector::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 diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 1359dd6ba..583159f05 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -14,6 +14,7 @@ #include "cmStandardIncludes.h" #include "cmState.h" +#include "cmOutputConverter.h" class cmMakefile; class cmGlobalGenerator; @@ -31,7 +32,7 @@ class cmCustomCommandGenerator; * platforms. This class should never be constructed directly. A * GlobalGenerator will create it and invoke the appropriate commands on it. */ -class cmLocalGenerator +class cmLocalGenerator : public cmOutputConverter { public: cmLocalGenerator(cmGlobalGenerator* gg, cmLocalGenerator* parent, @@ -90,35 +91,6 @@ public: cmState* GetState() const; cmState::Snapshot GetStateSnapshot() const; - /** - * Convert something to something else. This is a centralized conversion - * routine used by the generators to handle relative paths and the like. - * The flags determine what is actually done. - * - * relative: treat the argument as a directory and convert it to make it - * relative or full or unchanged. If relative (HOME, START etc) then that - * specifies what it should be relative to. - * - * output: make the result suitable for output to a... - * - * optional: should any relative path operation be controlled by the rel - * path setting - */ - enum RelativeRoot { NONE, FULL, HOME, START, HOME_OUTPUT, START_OUTPUT }; - enum OutputFormat { UNCHANGED, MAKERULE, SHELL, WATCOMQUOTE, RESPONSE }; - std::string ConvertToOutputFormat(const std::string& source, - OutputFormat output); - std::string Convert(const std::string& remote, RelativeRoot local, - OutputFormat output = UNCHANGED); - std::string Convert(RelativeRoot remote, const std::string& local, - OutputFormat output = UNCHANGED, - bool optional = false); - - /** - * Get path for the specified relative root. - */ - const char* GetRelativeRootPath(RelativeRoot relroot); - ///! set/get the parent generator cmLocalGenerator* GetParent() const {return this->Parent;} @@ -194,17 +166,6 @@ public: bool GetRealDependency(const std::string& name, const std::string& config, std::string& dep); - ///! for existing files convert to output path and short path if spaces - std::string ConvertToOutputForExisting(const std::string& remote, - RelativeRoot local = START_OUTPUT, - OutputFormat format = SHELL); - - /** For existing path identified by RelativeRoot convert to output - path and short path if spaces. */ - std::string ConvertToOutputForExisting(RelativeRoot remote, - const std::string& local = "", - OutputFormat format = SHELL); - virtual std::string ConvertToIncludeReference(std::string const& path, OutputFormat format = SHELL, bool forceFullPaths = false); @@ -276,40 +237,6 @@ public: const char* FilterPrefix; }; - /** Set whether to treat conversions to SHELL as a link script shell. */ - void SetLinkScriptShell(bool b) { this->LinkScriptShell = b; } - - /** Escape the given string to be used as a command line argument in - the native build system shell. Optionally allow the build - system to replace make variable references. Optionally adjust - escapes for the special case of passing to the native echo - command. */ - std::string EscapeForShell(const std::string& str, bool makeVars = false, - bool forEcho = false, - bool useWatcomQuote = false); - - /** Escape the given string as an argument in a CMake script. */ - static std::string EscapeForCMake(const std::string& str); - - enum FortranFormat - { - FortranFormatNone, - FortranFormatFixed, - FortranFormatFree - }; - FortranFormat GetFortranFormat(const char* value); - - /** - * Convert the given remote path to a relative path with respect to - * the given local path. The local path must be given in component - * form (see SystemTools::SplitPath) without a trailing slash. The - * remote path must use forward slashes and not already be escaped - * or quoted. - */ - std::string ConvertToRelativePath(const std::vector& local, - const std::string& remote, - bool force=false); - /** * Get the relative path from the generator output directory to a * per-target support directory. @@ -442,7 +369,6 @@ protected: std::set WarnCMP0063; - bool LinkScriptShell; bool EmitUniversalBinaryFlags; // Hack for ExpandRuleVariable until object-oriented version is @@ -452,10 +378,6 @@ protected: cmIML_INT_uint64_t BackwardsCompatibility; bool BackwardsCompatibilityFinal; private: - std::string ConvertToOutputForExistingCommon(const std::string& remote, - std::string const& result, - OutputFormat format); - void AddSharedFlags(std::string& flags, const std::string& lang, bool shared); bool GetShouldUseOldFlags(bool shared, const std::string &lang) const; diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx new file mode 100644 index 000000000..db73a3438 --- /dev/null +++ b/Source/cmOutputConverter.cxx @@ -0,0 +1,447 @@ +/*============================================================================ + 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 "cmOutputConverter.h" + +#include "cmAlgorithms.h" +#include "cmake.h" + +#include + +#include + +cmOutputConverter::cmOutputConverter(cmState::Snapshot snapshot) + : StateSnapshot(snapshot), LinkScriptShell(false) +{ +} + +//---------------------------------------------------------------------------- +std::string +cmOutputConverter::ConvertToOutputForExistingCommon(const std::string& remote, + std::string const& result, + OutputFormat format) +{ + // 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->GetState()->UseWindowsShell() && result.find(' ') != result.npos && + cmSystemTools::FileExists(remote.c_str())) + { + std::string tmp; + if(cmSystemTools::GetShortPath(remote, tmp)) + { + return this->ConvertToOutputFormat(tmp, format); + } + } + + // Otherwise, leave it unchanged. + return result; +} + +//---------------------------------------------------------------------------- +std::string +cmOutputConverter::ConvertToOutputForExisting(const std::string& remote, + RelativeRoot local, + OutputFormat format) +{ + static_cast(local); + + // Perform standard conversion. + std::string result = this->ConvertToOutputFormat(remote, format); + + // Consider short-path. + return this->ConvertToOutputForExistingCommon(remote, result, format); +} + +//---------------------------------------------------------------------------- +std::string +cmOutputConverter::ConvertToOutputForExisting(RelativeRoot remote, + const std::string& local, + OutputFormat format) +{ + // Perform standard conversion. + std::string result = this->Convert(remote, local, format, true); + + // Consider short-path. + const char* remotePath = this->GetRelativeRootPath(remote); + return this->ConvertToOutputForExistingCommon(remotePath, result, format); +} + +//---------------------------------------------------------------------------- +const char* cmOutputConverter::GetRelativeRootPath(RelativeRoot relroot) +{ + switch (relroot) + { + case HOME: return this->GetState()->GetSourceDirectory(); + case START: return this->StateSnapshot.GetCurrentSourceDirectory(); + case HOME_OUTPUT: return this->GetState()->GetBinaryDirectory(); + case START_OUTPUT: return this->StateSnapshot.GetCurrentBinaryDirectory(); + default: break; + } + return 0; +} + +std::string cmOutputConverter::Convert(const std::string& source, + RelativeRoot relative, + OutputFormat output) +{ + // Convert the path to a relative path. + std::string result = source; + + switch (relative) + { + case HOME: + result = this->ConvertToRelativePath( + this->GetState()->GetSourceDirectoryComponents(), result); + break; + case START: + result = this->ConvertToRelativePath( + this->StateSnapshot.GetCurrentSourceDirectoryComponents(), result); + break; + case HOME_OUTPUT: + result = this->ConvertToRelativePath( + this->GetState()->GetBinaryDirectoryComponents(), result); + break; + case START_OUTPUT: + result = this->ConvertToRelativePath( + this->StateSnapshot.GetCurrentBinaryDirectoryComponents(), result); + break; + case FULL: + result = cmSystemTools::CollapseFullPath(result); + break; + case NONE: + break; + } + return this->ConvertToOutputFormat(result, output); +} + +//---------------------------------------------------------------------------- +std::string cmOutputConverter::ConvertToOutputFormat(const std::string& source, + OutputFormat output) +{ + std::string result = source; + // Convert it to an output path. + if (output == MAKERULE) + { + result = cmSystemTools::ConvertToOutputPath(result.c_str()); + } + else if(output == SHELL || output == WATCOMQUOTE) + { + // 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->GetState()->UseMSYSShell() && !this->LinkScriptShell) + { + if(result.size() > 2 && result[1] == ':') + { + result[1] = result[0]; + result[0] = '/'; + } + } + if(this->GetState()->UseWindowsShell()) + { + std::replace(result.begin(), result.end(), '/', '\\'); + } + result = this->EscapeForShell(result, true, false, output == WATCOMQUOTE); + } + else if(output == RESPONSE) + { + result = this->EscapeForShell(result, false, false, false); + } + return result; +} + +//---------------------------------------------------------------------------- +std::string cmOutputConverter::Convert(RelativeRoot remote, + const std::string& 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.empty() && !optional) + { + std::vector components; + cmSystemTools::SplitPath(local, components); + std::string result = this->ConvertToRelativePath(components, remotePath); + return this->ConvertToOutputFormat(result, output); + } + + return this->ConvertToOutputFormat(remotePath, output); +} + +//---------------------------------------------------------------------------- +static bool cmOutputConverterNotAbove(const char* a, const char* b) +{ + return (cmSystemTools::ComparePath(a, b) || + cmSystemTools::IsSubDirectory(a, b)); +} + +//---------------------------------------------------------------------------- +std::string +cmOutputConverter::ConvertToRelativePath(const std::vector& local, + const std::string& 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.empty() && !(local[local.size()-1] == "")); + + // If the path is already relative then just return the path. + if(!cmSystemTools::FileIsFullPath(in_remote.c_str())) + { + return in_remote; + } + + 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(!((cmOutputConverterNotAbove(local_path.c_str(), + this->StateSnapshot.GetRelativePathTopBinary()) && + cmOutputConverterNotAbove(in_remote.c_str(), + this->StateSnapshot.GetRelativePathTopBinary())) || + (cmOutputConverterNotAbove(local_path.c_str(), + this->StateSnapshot.GetRelativePathTopSource()) && + cmOutputConverterNotAbove(in_remote.c_str(), + this->StateSnapshot.GetRelativePathTopSource())))) + { + return in_remote; + } + } + + // Identify the longest shared path component between the remote + // path and the local path. + std::vector remote; + cmSystemTools::SplitPath(in_remote, remote); + unsigned int common=0; + while(common < remote.size() && + common < local.size() && + cmSystemTools::ComparePath(remote[common], + local[common])) + { + ++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].empty() && + 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. + + if(!relative.empty() && !remote.empty()) + { + relative += "/"; + } + relative += cmJoin(cmRange(remote).advance(common), "/"); + + // Finally return the path. + return relative; +} + +//---------------------------------------------------------------------------- +static bool cmOutputConverterIsShellOperator(const std::string& str) +{ + static std::set shellOperators; + if(shellOperators.empty()) + { + shellOperators.insert("<"); + shellOperators.insert(">"); + shellOperators.insert("<<"); + shellOperators.insert(">>"); + shellOperators.insert("|"); + shellOperators.insert("||"); + shellOperators.insert("&&"); + shellOperators.insert("&>"); + shellOperators.insert("1>"); + shellOperators.insert("2>"); + shellOperators.insert("2>&1"); + shellOperators.insert("1>&2"); + } + return shellOperators.count(str) > 0; +} + +//---------------------------------------------------------------------------- +std::string cmOutputConverter::EscapeForShell(const std::string& str, + bool makeVars, + bool forEcho, + bool useWatcomQuote) +{ + // Do not escape shell operators. + if(cmOutputConverterIsShellOperator(str)) + { + return str; + } + + // Compute the flags for the target shell environment. + int flags = 0; + if(this->GetState()->UseWindowsVSIDE()) + { + 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(useWatcomQuote) + { + flags |= cmsysSystem_Shell_Flag_WatcomQuote; + } + if(this->GetState()->UseWatcomWMake()) + { + flags |= cmsysSystem_Shell_Flag_WatcomWMake; + } + if(this->GetState()->UseMinGWMake()) + { + flags |= cmsysSystem_Shell_Flag_MinGWMake; + } + if(this->GetState()->UseNMake()) + { + flags |= cmsysSystem_Shell_Flag_NMake; + } + + // Compute the buffer size needed. + int size = (this->GetState()->UseWindowsShell() ? + cmsysSystem_Shell_GetArgumentSizeForWindows(str.c_str(), flags) : + cmsysSystem_Shell_GetArgumentSizeForUnix(str.c_str(), flags)); + + // Compute the shell argument itself. + std::vector arg(size); + if(this->GetState()->UseWindowsShell()) + { + cmsysSystem_Shell_GetArgumentForWindows(str.c_str(), &arg[0], flags); + } + else + { + cmsysSystem_Shell_GetArgumentForUnix(str.c_str(), &arg[0], flags); + } + return std::string(&arg[0]); +} + +//---------------------------------------------------------------------------- +std::string cmOutputConverter::EscapeForCMake(const std::string& str) +{ + // Always double-quote the argument to take care of most escapes. + std::string result = "\""; + for(const char* c = str.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; +} + +//---------------------------------------------------------------------------- +cmOutputConverter::FortranFormat +cmOutputConverter::GetFortranFormat(const char* value) +{ + FortranFormat format = FortranFormatNone; + if(value && *value) + { + std::vector fmt; + cmSystemTools::ExpandListArgument(value, fmt); + for(std::vector::iterator fi = fmt.begin(); + fi != fmt.end(); ++fi) + { + if(*fi == "FIXED") + { + format = FortranFormatFixed; + } + if(*fi == "FREE") + { + format = FortranFormatFree; + } + } + } + return format; +} + +void cmOutputConverter::SetLinkScriptShell(bool linkScriptShell) +{ + this->LinkScriptShell = linkScriptShell; +} + +cmState* cmOutputConverter::GetState() const +{ + return this->StateSnapshot.GetState(); +} diff --git a/Source/cmOutputConverter.h b/Source/cmOutputConverter.h new file mode 100644 index 000000000..1d3f8c75f --- /dev/null +++ b/Source/cmOutputConverter.h @@ -0,0 +1,106 @@ +/*============================================================================ + 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. +============================================================================*/ +#ifndef cmOutputConverter_h +#define cmOutputConverter_h + +#include "cmStandardIncludes.h" + +#include "cmGlobalGenerator.h" +#include "cmState.h" + +class cmOutputConverter +{ +public: + cmOutputConverter(cmState::Snapshot snapshot); + + /** + * Convert something to something else. This is a centralized conversion + * routine used by the generators to handle relative paths and the like. + * The flags determine what is actually done. + * + * relative: treat the argument as a directory and convert it to make it + * relative or full or unchanged. If relative (HOME, START etc) then that + * specifies what it should be relative to. + * + * output: make the result suitable for output to a... + * + * optional: should any relative path operation be controlled by the rel + * path setting + */ + enum RelativeRoot { NONE, FULL, HOME, START, HOME_OUTPUT, START_OUTPUT }; + enum OutputFormat { UNCHANGED, MAKERULE, SHELL, WATCOMQUOTE, RESPONSE }; + std::string ConvertToOutputFormat(const std::string& source, + OutputFormat output); + std::string Convert(const std::string& remote, RelativeRoot local, + OutputFormat output = UNCHANGED); + std::string Convert(RelativeRoot remote, const std::string& local, + OutputFormat output = UNCHANGED, + bool optional = false); + + /** + * Get path for the specified relative root. + */ + const char* GetRelativeRootPath(RelativeRoot relroot); + + ///! for existing files convert to output path and short path if spaces + std::string ConvertToOutputForExisting(const std::string& remote, + RelativeRoot local = START_OUTPUT, + OutputFormat format = SHELL); + + /** For existing path identified by RelativeRoot convert to output + path and short path if spaces. */ + std::string ConvertToOutputForExisting(RelativeRoot remote, + const std::string& local = "", + OutputFormat format = SHELL); + + void SetLinkScriptShell(bool linkScriptShell); + + std::string EscapeForShell(const std::string& str, + bool makeVars = false, + bool forEcho = false, + bool useWatcomQuote = false); + + static std::string EscapeForCMake(const std::string& str); + + enum FortranFormat + { + FortranFormatNone, + FortranFormatFixed, + FortranFormatFree + }; + static FortranFormat GetFortranFormat(const char* value); + + /** + * Convert the given remote path to a relative path with respect to + * the given local path. The local path must be given in component + * form (see SystemTools::SplitPath) without a trailing slash. The + * remote path must use forward slashes and not already be escaped + * or quoted. + */ + std::string ConvertToRelativePath(const std::vector& local, + const std::string& in_remote, + bool force = false); + +private: + cmState* GetState() const; + + std::string ConvertToOutputForExistingCommon(const std::string& remote, + std::string const& result, + OutputFormat format); + +private: + cmState::Snapshot StateSnapshot; + + bool LinkScriptShell; +}; + +#endif diff --git a/bootstrap b/bootstrap index 14046eeec..98cdcd0a7 100755 --- a/bootstrap +++ b/bootstrap @@ -299,6 +299,7 @@ CMAKE_CXX_SOURCES="\ cmMakefileLibraryTargetGenerator \ cmMakefileTargetGenerator \ cmMakefileUtilityTargetGenerator \ + cmOutputConverter \ cmOSXBundleGenerator \ cmNewLineStyle \ cmBootstrapCommands1 \