From cb48e0c16178738c3d3bafd551f0f3e0b906c6c1 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 24 Feb 2005 16:04:54 -0500 Subject: [PATCH] ENH: Merged implementations of ConvertToRelative*Path methods. The main ConvertToRelativePath method is now in cmGlobalGenerator. It converts paths only if they are at least inside the deepest common directory between the top-level source and build trees. Each cmLocalGenerator instance calls this global method with its own output directory as the "local" argument from which paths are relative. Added separate ConvertToOptionallyRelative path that pays attention to the CMAKE_USE_RELATIVE_PATHS option. --- Source/cmGlobalGenerator.cxx | 138 ++++++++++++++++++++- Source/cmGlobalGenerator.h | 18 +++ Source/cmLocalGenerator.cxx | 130 ++++++++------------ Source/cmLocalGenerator.h | 36 +++++- Source/cmLocalUnixMakefileGenerator2.cxx | 147 +---------------------- Source/cmLocalUnixMakefileGenerator2.h | 9 -- 6 files changed, 239 insertions(+), 239 deletions(-) diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 296a00802..4b8d711ea 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -24,6 +24,8 @@ #include #endif +#include + int cmGlobalGenerator::s_TryCompileTimeout = 0; cmGlobalGenerator::cmGlobalGenerator() @@ -487,7 +489,10 @@ void cmGlobalGenerator::Configure() delete m_LocalGenerators[i]; } m_LocalGenerators.clear(); - + + // Setup relative path generation. + this->ConfigureRelativePaths(); + // start with this directory cmLocalGenerator *lg = this->CreateLocalGenerator(); m_LocalGenerators.push_back(lg); @@ -573,6 +578,7 @@ void cmGlobalGenerator::Configure() } } + // loop through the directories creating cmLocalGenerators and Configure() void cmGlobalGenerator::RecursiveConfigure(cmLocalGenerator *lg, float startProgress, @@ -877,3 +883,133 @@ cmTarget* cmGlobalGenerator::FindTarget(const char* name) return 0; } +//---------------------------------------------------------------------------- +void cmGlobalGenerator::ConfigureRelativePaths() +{ + // Identify the longest shared path component between the source + // directory and the build directory. + std::vector source; + std::vector binary; + cmSystemTools::SplitPath(m_CMakeInstance->GetHomeDirectory(), source); + cmSystemTools::SplitPath(m_CMakeInstance->GetHomeOutputDirectory(), binary); + unsigned int common=0; + while(common < source.size() && common < binary.size() && + cmSystemTools::ComparePath(source[common].c_str(), + binary[common].c_str())) + { + ++common; + } + + // Require more than just the root portion of the path to be in + // common before allowing relative paths. Also disallow relative + // paths if the build tree is a network path. The current working + // directory on Windows cannot be a network path. Therefore + // relative paths cannot work with network paths. + if(common > 1 && source[0] != "//") + { + // Build the minimum prefix required of a path to be converted to + // a relative path. + source.erase(source.begin()+common, source.end()); + m_RelativePathTop = cmSystemTools::JoinPath(source); + } + else + { + // Disable relative paths. + m_RelativePathTop = ""; + } +} + +//---------------------------------------------------------------------------- +std::string +cmGlobalGenerator::ConvertToRelativePath(const std::vector& local, + const char* in_remote) +{ + // 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 or relative paths are disabled + // then just return the path. + if(m_RelativePathTop.size() == 0 || + !cmSystemTools::FileIsFullPath(in_remote)) + { + return in_remote; + } + + // If the path does not begin with the minimum relative path prefix + // then do not convert it. + std::string original = in_remote; + if(original.size() < m_RelativePathTop.size() || + !cmSystemTools::ComparePath( + original.substr(0, m_RelativePathTop.size()).c_str(), + m_RelativePathTop.c_str())) + { + 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].c_str(), + local[common].c_str())) + { + ++common; + } + + // 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; +} + diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 6f4d3b4f3..7832a96d5 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -129,11 +129,24 @@ public: const char* GetLanguageOutputExtensionForLanguage(const char* lang); ///! What is the output extension for a given source file extension. const char* GetLanguageOutputExtensionFromExtension(const char* lang); + + /** + * 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 char* remote); + protected: // Fill the m_ProjectMap, this must be called after m_LocalGenerators has been populated. void FillProjectMap(); bool IsExcluded(cmLocalGenerator* root, cmLocalGenerator* gen); void FindMakeProgram(cmMakefile*); + + void ConfigureRelativePaths(); bool m_ForceUnixPaths; cmStdString m_FindMakeProgramFile; @@ -157,6 +170,11 @@ private: std::map m_LanguageToOutputExtension; std::map m_ExtensionToLanguage; std::map m_LanguageToLinkerPreference; + + // The prefix required of a path to be converted to a relative path. + // No sequence of ../.. will ever go past this path. This is the + // longest common path between the top level source and build trees. + std::string m_RelativePathTop; }; #endif diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index e0d296c13..61f20eca6 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -51,6 +51,16 @@ void cmLocalGenerator::Configure() std::string currentStart = m_Makefile->GetStartDirectory(); currentStart += "/CMakeLists.txt"; m_Makefile->ReadListFile(currentStart.c_str()); + + // Setup the current output directory components for use by + // ConvertToRelativePath. + std::string outdir = + cmSystemTools::CollapseFullPath(m_Makefile->GetCurrentOutputDirectory()); + cmSystemTools::SplitPath(outdir.c_str(), m_CurrentOutputDirectoryComponents); + + // Check whether relative paths should be used for optionally + // relative paths. + m_UseRelativePaths = m_Makefile->IsOn("CMAKE_USE_RELATIVE_PATHS"); } void cmLocalGenerator::SetGlobalGenerator(cmGlobalGenerator *gg) @@ -404,83 +414,6 @@ std::string cmLocalGenerator::GetFullTargetName(const char* n, return name; } - -std::string cmLocalGenerator::ConvertToRelativeOutputPath(const char* p) -{ - if ( !m_Makefile->IsOn("CMAKE_USE_RELATIVE_PATHS") ) - { - return cmSystemTools::ConvertToOutputPath(p); - } - // NOTE, much of this was copied to - // cmGlobalXCodeGenerator::ConvertToRelativeOutputPath - // fixes here should be made there as well. - - // copy to a string class - std::string pathIn = p; - // check to see if the path is already relative, it is - // considered relative if one of the following is true - // - has no / in it at all - // - does not start with / or drive leter : - // - starts with a ".." - if(pathIn.find('/') == pathIn.npos || - (pathIn[0] != '/' && pathIn[1] != ':') || - pathIn.find("..") == 0) - { - return cmSystemTools::ConvertToOutputPath(p); - } - - // do not use relative paths for network build trees - // the network paths do not work - const char* outputDirectory = m_Makefile->GetHomeOutputDirectory(); - if ( outputDirectory && *outputDirectory && *(outputDirectory+1) && - outputDirectory[0] == '/' && outputDirectory[1] == '/' ) - { - return cmSystemTools::ConvertToOutputPath(p); - } - // if the path is double quoted remove the double quotes - if(pathIn.size() && pathIn[0] == '\"') - { - pathIn = pathIn.substr(1, pathIn.size()-2); - } - // The first time this is called - // initialize m_CurrentOutputDirectory to contain - // the full path to the current output directory - // This has to be done here and not in the constructor - // because the output directory is not yet set in the constructor. - if(m_CurrentOutputDirectory.size() == 0) - { - m_CurrentOutputDirectory = cmSystemTools::CollapseFullPath(m_Makefile->GetCurrentOutputDirectory()); - } - // Given that we are in m_CurrentOutputDirectory how to we - // get to pathIn with a relative path, store in ret - std::string ret = cmSystemTools::RelativePath(m_CurrentOutputDirectory.c_str(), pathIn.c_str()); - // If the path is 0 sized make it a . - // this happens when pathIn is the same as m_CurrentOutputDirectory - if(ret.size() == 0) - { - ret = "."; - } - // if there was a trailing / there still is one, and - // if there was not one, there still is not one - if(ret[ret.size()-1] == '/' && - pathIn[pathIn.size()-1] != '/') - { - ret.erase(ret.size()-1, 1); - } - if(ret[ret.size()-1] != '/' && - pathIn[pathIn.size()-1] == '/') - { - ret += "/"; - } - // Now convert the relative path to an output path - ret = cmSystemTools::ConvertToOutputPath(ret.c_str()); - // finally return the path - // at this point it should be relative and in the correct format - // for the native build system. (i.e. \ for windows and / for unix, - // and correct escaping/quoting of spaces in the path - return ret; -} - void cmLocalGenerator::AddCustomCommandToCreateObject(const char* ofname, const char* lang, cmSourceFile& source, @@ -1356,3 +1289,46 @@ cmLocalGenerator::ConstructScript(const cmCustomCommandLines& commandLines, } return script; } + +//---------------------------------------------------------------------------- +std::string cmLocalGenerator::ConvertToRelativePath(const char* remote) +{ + return (m_GlobalGenerator + ->ConvertToRelativePath(m_CurrentOutputDirectoryComponents, + remote)); +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator::ConvertToRelativeOutputPath(const char* remote) +{ + // TODO: Make this behave like its documentation...always convert. + // This involves identifying all calls to it that should be calls to + // the optional one. + return this->ConvertToOptionallyRelativeOutputPath(remote); +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator::ConvertToOptionallyRelativePath(const char* remote) +{ + if(m_UseRelativePaths) + { + return this->ConvertToRelativePath(remote); + } + else + { + return remote; + } +} + +//---------------------------------------------------------------------------- +std::string +cmLocalGenerator::ConvertToOptionallyRelativeOutputPath(const char* remote) +{ + // Convert the path to a relative path. + std::string relative = this->ConvertToOptionallyRelativePath(remote); + + // Now convert it to an output path. + return cmSystemTools::ConvertToOutputPath(relative.c_str()); +} diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index eaa9a8a2f..0932c76eb 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -77,8 +77,35 @@ public: /** Get the full name of the target's file, without path. */ std::string GetFullTargetName(const char* n, const cmTarget& t); - std::string ConvertToRelativeOutputPath(const char* p); - + /** + * Convert the given remote path to a relative path with respect to + * this generator's output directory. The remote path must use + * forward slashes and not already be escaped or quoted. + */ + std::string ConvertToRelativePath(const char* remote); + + /** + * Convert to an output path that is relative to the current output + * directory. The remote path must use forward slashes and not + * already be escaped or quoted. + */ + std::string ConvertToRelativeOutputPath(const char* remote); + + /** + * Calls ConvertToRelativePath conditionally on the cache option + * CMAKE_USE_RELATIVE_PATHS. The remote path must use forward + * slashes and not already be escaped or quoted. + */ + std::string ConvertToOptionallyRelativePath(const char* remote); + + /** + * Convert the given path to an output path that is optionally + * relative based on the cache option CMAKE_USE_RELATIVE_PATHS. The + * remote path must use forward slashes and not already be escaped + * or quoted. + */ + std::string ConvertToOptionallyRelativeOutputPath(const char* remote); + // flag to determine if this project should be included in a parent project bool GetExcludeAll() { @@ -156,10 +183,7 @@ protected: // members used for relative path function ConvertToMakefilePath std::string m_RelativePathToSourceDir; std::string m_RelativePathToBinaryDir; - std::string m_CurrentOutputDirectory; - std::string m_HomeOutputDirectory; - std::string m_HomeDirectory; - std::string m_HomeOutputDirectoryNoSlash; + std::vector m_CurrentOutputDirectoryComponents; bool m_ExcludeFromAll; cmLocalGenerator* m_Parent; std::map m_LanguageToIncludeFlags; diff --git a/Source/cmLocalUnixMakefileGenerator2.cxx b/Source/cmLocalUnixMakefileGenerator2.cxx index 5eec9f702..2a2aeaecb 100644 --- a/Source/cmLocalUnixMakefileGenerator2.cxx +++ b/Source/cmLocalUnixMakefileGenerator2.cxx @@ -33,8 +33,6 @@ #include // auto_ptr #include -#include - // TODO: Convert makefile name to a runtime switch. #define CMLUMG_MAKEFILE_NAME "Makefile" @@ -1998,6 +1996,7 @@ cmLocalUnixMakefileGenerator2 for(std::vector::const_iterator i = objects.begin(); i != objects.end(); ++i) { + // TODO: Make sure we don't escape spaces and quote. ruleFileStream << " \\\n" << "\"" << this->ConvertToRelativeOutputPath(i->c_str()) << "\""; @@ -2308,94 +2307,6 @@ cmLocalUnixMakefileGenerator2 return dir; } -//---------------------------------------------------------------------------- -std::string -cmLocalUnixMakefileGenerator2::ConvertToRelativePath(const char* p) -{ - // The path should never be quoted. - assert(p[0] != '\"'); - - // If the path is already relative or relative paths are disabled - // then just return the path. - if(m_RelativePathTop.size() == 0 || !cmSystemTools::FileIsFullPath(p)) - { - return p; - } - - // If the path does not begin with the minimum relative path prefix - // then do not convert it. - std::string original = p; - if(original.size() < m_RelativePathTop.size() || - !this->ComparePath(original.substr(0, m_RelativePathTop.size()).c_str(), - m_RelativePathTop.c_str())) - { - return p; - } - - // Identify the longest shared path component between the given path - // and the current output directory. - std::vector path; - cmSystemTools::SplitPath(p, path); - unsigned int common=0; - while(common < path.size() && - common < m_CurrentOutputDirectoryComponents.size() && - this->ComparePath(path[common].c_str(), - m_CurrentOutputDirectoryComponents[common].c_str())) - { - ++common; - } - - // If the entire path is in common then just return a ".". - if(common == path.size() && - common == m_CurrentOutputDirectoryComponents.size()) - { - return "."; - } - - // If the entire path is in common except for a trailing slash then - // just return a "./". - if(common+1 == path.size() && path[common].size() == 0 && - common == m_CurrentOutputDirectoryComponents.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 m_CurrentOutputDirectoryComponents will never be - // empty because m_CurrentOutputDirectory does not have a trailing - // slash. - for(unsigned int i=common; i < m_CurrentOutputDirectoryComponents.size(); ++i) - { - relative += ".."; - if(i < m_CurrentOutputDirectoryComponents.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 < path.size(); ++i) - { - if(relative.size() > 0) - { - relative += "/"; - } - relative += path[i]; - } - - // Finally return the path. - return relative; -} - //---------------------------------------------------------------------------- std::string cmLocalUnixMakefileGenerator2::ConvertToRelativeOutputPath(const char* p) @@ -2424,52 +2335,6 @@ void cmLocalUnixMakefileGenerator2::ConfigureOutputPaths() m_ExecutableOutputPath = exeOut; this->FormatOutputPath(m_ExecutableOutputPath, "EXECUTABLE"); } - - // Setup fully collapsed paths. - m_CurrentOutputDirectory = - cmSystemTools::CollapseFullPath(m_Makefile->GetCurrentOutputDirectory()); - m_HomeOutputDirectory = - cmSystemTools::CollapseFullPath(m_Makefile->GetHomeOutputDirectory()); - m_HomeDirectory = - cmSystemTools::CollapseFullPath(m_Makefile->GetHomeDirectory()); - - // Identify the longest shared path component between the source - // directory and the build directory. - std::vector source; - std::vector binary; - cmSystemTools::SplitPath(m_HomeDirectory.c_str(), source); - cmSystemTools::SplitPath(m_HomeOutputDirectory.c_str(), binary); - unsigned int common=0; - while(common < source.size() && common < binary.size() && - this->ComparePath(source[common].c_str(), binary[common].c_str())) - { - ++common; - } - - // Require more than just the root portion of the path to be in - // common before allowing relative paths. Also disallow relative - // paths if the build tree is a network path. The current working - // directory on Windows cannot be a network path. Therefore - // relative paths cannot work with network paths. - if(common > 1 && source[0] != "//") - { - // Build the minimum prefix required of a path to be converted to - // a relative path. - m_RelativePathTop = source[0]; - for(unsigned int i=1; i < common; ++i) - { - if(i > 1) - { - m_RelativePathTop += "/"; - } - m_RelativePathTop += source[i]; - } - - // Split the current output directory now to save time when - // converting paths. - cmSystemTools::SplitPath(m_CurrentOutputDirectory.c_str(), - m_CurrentOutputDirectoryComponents); - } } //---------------------------------------------------------------------------- @@ -2503,16 +2368,6 @@ void cmLocalUnixMakefileGenerator2::FormatOutputPath(std::string& path, } } -//---------------------------------------------------------------------------- -bool cmLocalUnixMakefileGenerator2::ComparePath(const char* c1, const char* c2) -{ -#if defined(_WIN32) || defined(__APPLE__) - return cmSystemTools::Strucmp(c1, c2) == 0; -#else - return strcmp(c1, c2) == 0; -#endif -} - //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 diff --git a/Source/cmLocalUnixMakefileGenerator2.h b/Source/cmLocalUnixMakefileGenerator2.h index efa03cd23..90997f180 100644 --- a/Source/cmLocalUnixMakefileGenerator2.h +++ b/Source/cmLocalUnixMakefileGenerator2.h @@ -198,11 +198,9 @@ protected: const cmSourceFile& source); const char* GetSourceFileLanguage(const cmSourceFile& source); std::string ConvertToFullPath(const std::string& localPath); - std::string ConvertToRelativePath(const char* p); std::string ConvertToRelativeOutputPath(const char* p); void ConfigureOutputPaths(); void FormatOutputPath(std::string& path, const char* name); - bool ComparePath(const char* c1, const char* c2); void AppendTargetDepends(std::vector& depends, const cmTarget& target); @@ -277,13 +275,6 @@ private: // Set of object file names that will be built in this directory. std::set m_ObjectFiles; - - // The prefix required of a path to be converted to a relative path. - // No sequence of ../.. will ever go past this path. - std::string m_RelativePathTop; - - // The pre-split current output directory. - std::vector m_CurrentOutputDirectoryComponents; }; #endif