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