diff options
author | Brad King <brad.king@kitware.com> | 2005-01-04 22:41:19 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2005-01-04 22:41:19 (GMT) |
commit | 79c67a6dfa0f864734859a0ab08fe7c22bcfbf24 (patch) | |
tree | b12973e300f98c19a6aa9f51caa7b296356c4138 /Source/cmLocalUnixMakefileGenerator2.cxx | |
parent | 524769aebc2356b89a021fc9229fb7ed548a46a9 (diff) | |
download | CMake-79c67a6dfa0f864734859a0ab08fe7c22bcfbf24.zip CMake-79c67a6dfa0f864734859a0ab08fe7c22bcfbf24.tar.gz CMake-79c67a6dfa0f864734859a0ab08fe7c22bcfbf24.tar.bz2 |
ENH: Added relative path support (mostly done). Many paths are written relative even if CMAKE_USE_RELATIVE_PATHS is not on just to keep makefiles short.
Diffstat (limited to 'Source/cmLocalUnixMakefileGenerator2.cxx')
-rw-r--r-- | Source/cmLocalUnixMakefileGenerator2.cxx | 357 |
1 files changed, 291 insertions, 66 deletions
diff --git a/Source/cmLocalUnixMakefileGenerator2.cxx b/Source/cmLocalUnixMakefileGenerator2.cxx index db4e5ed..9168fd2 100644 --- a/Source/cmLocalUnixMakefileGenerator2.cxx +++ b/Source/cmLocalUnixMakefileGenerator2.cxx @@ -33,6 +33,7 @@ // TODO: Add "help" target. // TODO: Add install targets to m_InstallTargets list. +// TODO: Identify remaining relative path violations. //---------------------------------------------------------------------------- cmLocalUnixMakefileGenerator2::cmLocalUnixMakefileGenerator2() @@ -194,28 +195,37 @@ void cmLocalUnixMakefileGenerator2::GenerateCMakefile() lfiles.end()); lfiles.erase(new_end, lfiles.end()); + // Build the path to the cache file. + std::string cache = m_Makefile->GetHomeOutputDirectory(); + cache += "/CMakeCache.txt"; + // Save the list to the cmake file. cmakefileStream << "# The corresponding makefile\n" - << "# \"" << makefileName << "\"\n" + << "# \"" << this->ConvertToRelativePath(makefileName.c_str()).c_str() << "\"\n" << "# was generated from the following files:\n" << "SET(CMAKE_MAKEFILE_DEPENDS\n" - << " \"" << m_Makefile->GetHomeOutputDirectory() << "/CMakeCache.txt\"\n"; + << " \"" << this->ConvertToRelativePath(cache.c_str()).c_str() << "\"\n"; for(std::vector<std::string>::const_iterator i = lfiles.begin(); i != lfiles.end(); ++i) { cmakefileStream - << " \"" << i->c_str() << "\"\n"; + << " \"" << this->ConvertToRelativePath(i->c_str()).c_str() + << "\"\n"; } cmakefileStream << " )\n\n"; + // Build the path to the cache check file. + std::string check = m_Makefile->GetHomeOutputDirectory(); + check += "/cmake.check_cache"; + // Set the corresponding makefile in the cmake file. cmakefileStream << "# The corresponding makefile is:\n" << "SET(CMAKE_MAKEFILE_OUTPUTS\n" - << " \"" << makefileName.c_str() << "\"\n" - << " \"" << m_Makefile->GetHomeOutputDirectory() << "/cmake.check_cache\"\n" + << " \"" << this->ConvertToRelativePath(makefileName.c_str()).c_str() << "\"\n" + << " \"" << this->ConvertToRelativePath(check.c_str()).c_str() << "\"\n" << " )\n\n"; // Set the set of files to check for dependency integrity. @@ -226,7 +236,7 @@ void cmLocalUnixMakefileGenerator2::GenerateCMakefile() i != m_CheckDependFiles.end(); ++i) { cmakefileStream - << " \"" << i->c_str() << "\"\n"; + << " \"" << this->ConvertToRelativePath(i->c_str()).c_str() << "\"\n"; } cmakefileStream << " )\n"; @@ -400,7 +410,7 @@ cmLocalUnixMakefileGenerator2 for(std::vector<std::string>::iterator i = deps.begin(); i != deps.end(); ++i) { - depends.push_back(this->ConvertToRelativeOutputPath(i->c_str())); + depends.push_back(i->c_str()); } } depends.push_back(ruleFileName); @@ -811,11 +821,14 @@ cmLocalUnixMakefileGenerator2 m_Makefile->GetRequiredDefinition("CMAKE_COMMAND")); makefileStream << "# The CMake executable.\n" - << "CMAKE_COMMAND = " << cmakecommand.c_str() << "\n" + << "CMAKE_COMMAND = " + << this->ConvertToRelativeOutputPath(cmakecommand.c_str()).c_str() << "\n" << "\n"; makefileStream << "# The command to remove a file.\n" - << "RM = " << cmakecommand.c_str() << " -E remove -f\n" + << "RM = " + << this->ConvertToRelativeOutputPath(cmakecommand.c_str()).c_str() + << " -E remove -f\n" << "\n"; if(m_Makefile->GetDefinition("CMAKE_EDIT_COMMAND")) @@ -1241,18 +1254,18 @@ cmLocalUnixMakefileGenerator2 void cmLocalUnixMakefileGenerator2 ::WriteConvenienceRules(std::ostream& ruleFileStream, const cmTarget& target, - const char* targetFullPath) + const char* targetOutPath) { // Add a rule to build the target by name. std::string localName = this->GetFullTargetName(target.GetName(), target); localName = this->ConvertToRelativeOutputPath(localName.c_str()); - this->WriteConvenienceRule(ruleFileStream, targetFullPath, + this->WriteConvenienceRule(ruleFileStream, targetOutPath, localName.c_str()); // Add a target with the canonical name (no prefix, suffix or path). if(localName != target.GetName()) { - this->WriteConvenienceRule(ruleFileStream, targetFullPath, + this->WriteConvenienceRule(ruleFileStream, targetOutPath, target.GetName()); } } @@ -1337,7 +1350,6 @@ cmLocalUnixMakefileGenerator2 depends.push_back(ruleFileName); // Construct the full path to the executable that will be generated. - // TODO: Try to convert this to use a relative path. std::string targetFullPath = m_ExecutableOutputPath; if(targetFullPath.length() == 0) { @@ -1354,7 +1366,10 @@ cmLocalUnixMakefileGenerator2 #endif targetFullPath += target.GetName(); targetFullPath += cmSystemTools::GetExecutableExtension(); - targetFullPath = this->ConvertToRelativeOutputPath(targetFullPath.c_str()); + + // Convert to the output path to use in constructing commands. + std::string targetOutPath = + this->ConvertToRelativeOutputPath(targetFullPath.c_str()); // Get the language to use for linking this executable. const char* linkLanguage = @@ -1421,7 +1436,7 @@ cmLocalUnixMakefileGenerator2 this->ExpandRuleVariables(*i, linkLanguage, objs.c_str(), - targetFullPath.c_str(), + targetOutPath.c_str(), linklibs.str().c_str(), 0, 0, @@ -1436,17 +1451,17 @@ cmLocalUnixMakefileGenerator2 std::string buildEcho = "Linking "; buildEcho += linkLanguage; buildEcho += " executable "; - buildEcho += this->ConvertToRelativeOutputPath(targetFullPath.c_str()); + buildEcho += targetOutPath; buildEcho += "..."; this->WriteMakeRule(ruleFileStream, 0, buildEcho.c_str(), targetFullPath.c_str(), depends, commands); // Write convenience targets. - this->WriteConvenienceRules(ruleFileStream, target, targetFullPath.c_str()); + this->WriteConvenienceRules(ruleFileStream, target, targetOutPath.c_str()); // Write clean target. std::vector<std::string> cleanFiles; - cleanFiles.push_back(this->ConvertToRelativeOutputPath(targetFullPath.c_str())); + cleanFiles.push_back(targetOutPath.c_str()); cleanFiles.push_back(objs); this->WriteTargetCleanRule(ruleFileStream, target, cleanFiles); @@ -1565,10 +1580,13 @@ cmLocalUnixMakefileGenerator2 // Add a dependency on the rule file itself. depends.push_back(ruleFileName); + // Create set of linking flags. const char* linkLanguage = target.GetLinkerLanguage(this->GetGlobalGenerator()); std::string linkFlags; this->AppendFlags(linkFlags, extraFlags); + + // Construct the name of the library. std::string targetName; std::string targetNameSO; std::string targetNameReal; @@ -1577,61 +1595,42 @@ cmLocalUnixMakefileGenerator2 targetName, targetNameSO, targetNameReal, targetNameBase); - std::string outpath; - std::string outdir; - if(m_UseRelativePaths) - { - outdir = this->ConvertToRelativeOutputPath(m_LibraryOutputPath.c_str()); - } - else - { - outdir = m_LibraryOutputPath; - } - if(!m_WindowsShell && m_UseRelativePaths && outdir.size()) - { - outpath = "\"`cd "; - } - outpath += outdir; - if(!m_WindowsShell && m_UseRelativePaths && outdir.size()) - { - outpath += ";pwd`\"/"; - } - if(outdir.size() == 0 && m_UseRelativePaths && !m_WindowsShell) + // Construct the full path version of the names. + std::string outpath = m_LibraryOutputPath; + if(outpath.length() == 0) { - outpath = "\"`pwd`\"/"; + outpath = m_Makefile->GetStartOutputDirectory(); + outpath += "/"; } - // The full path versions of the names. std::string targetFullPath = outpath + targetName; std::string targetFullPathSO = outpath + targetNameSO; std::string targetFullPathReal = outpath + targetNameReal; std::string targetFullPathBase = outpath + targetNameBase; - // If not using relative paths then the output path needs to be - // converted here - if(!m_UseRelativePaths) - { - targetFullPath = this->ConvertToRelativeOutputPath(targetFullPath.c_str()); - targetFullPathSO = this->ConvertToRelativeOutputPath(targetFullPathSO.c_str()); - targetFullPathReal = this->ConvertToRelativeOutputPath(targetFullPathReal.c_str()); - targetFullPathBase = this->ConvertToRelativeOutputPath(targetFullPathBase.c_str()); - } + + // Construct the output path version of the names for use in command + // arguments. + std::string targetOutPath = this->ConvertToRelativeOutputPath(targetFullPath.c_str()); + std::string targetOutPathSO = this->ConvertToRelativeOutputPath(targetFullPathSO.c_str()); + std::string targetOutPathReal = this->ConvertToRelativeOutputPath(targetFullPathReal.c_str()); + std::string targetOutPathBase = this->ConvertToRelativeOutputPath(targetFullPathBase.c_str()); // Add a command to remove any existing files for this library. std::vector<std::string> cleanFiles; std::string remove = "$(CMAKE_COMMAND) -E remove -f "; - remove += targetFullPathReal; - cleanFiles.push_back(targetFullPathReal); - if(targetFullPathSO != targetFullPathReal) + remove += targetOutPathReal; + cleanFiles.push_back(targetOutPathReal); + if(targetOutPathSO != targetOutPathReal) { remove += " "; - remove += targetFullPathSO; - cleanFiles.push_back(targetFullPathSO); + remove += targetOutPathSO; + cleanFiles.push_back(targetOutPathSO); } - if(targetFullPath != targetFullPathSO && - targetFullPath != targetFullPathReal) + if(targetOutPath != targetOutPathSO && + targetOutPath != targetOutPathReal) { remove += " "; - remove += targetFullPath; - cleanFiles.push_back(targetFullPath); + remove += targetOutPath; + cleanFiles.push_back(targetOutPath); } commands.push_back(remove); @@ -1642,14 +1641,14 @@ cmLocalUnixMakefileGenerator2 cmSystemTools::ExpandListArgument(linkRule, commands); // Add a rule to create necessary symlinks for the library. - if(targetFullPath != targetFullPathReal) + if(targetOutPath != targetOutPathReal) { std::string symlink = "$(CMAKE_COMMAND) -E cmake_symlink_library "; - symlink += targetFullPathReal; + symlink += targetOutPathReal; symlink += " "; - symlink += targetFullPathSO; + symlink += targetOutPathSO; symlink += " "; - symlink += targetFullPath; + symlink += targetOutPath; commands.push_back(symlink); } @@ -1674,13 +1673,18 @@ cmLocalUnixMakefileGenerator2 for(std::vector<std::string>::iterator i = commands.begin(); i != commands.end(); ++i) { + // TODO: Fix target output paths to use "cd...;pwd" idiom to pass + // a full path to the linker. This should be done by identifying + // a relative path and stripping the directory part off to put in + // this format. This rule is the only place that this idiom is + // needed. this->ExpandRuleVariables(*i, linkLanguage, objs.c_str(), - targetFullPathReal.c_str(), + targetOutPathReal.c_str(), linklibs.str().c_str(), 0, 0, 0, objsQuoted.c_str(), - targetFullPathBase.c_str(), + targetOutPathBase.c_str(), targetNameSO.c_str(), linkFlags.c_str()); } @@ -1699,13 +1703,13 @@ cmLocalUnixMakefileGenerator2 default: buildEcho += " library "; break; } - buildEcho += this->ConvertToRelativeOutputPath(targetFullPath.c_str()); + buildEcho += targetOutPath.c_str(); buildEcho += "..."; this->WriteMakeRule(ruleFileStream, 0, buildEcho.c_str(), targetFullPath.c_str(), depends, commands); // Write convenience targets. - this->WriteConvenienceRules(ruleFileStream, target, targetFullPath.c_str()); + this->WriteConvenienceRules(ruleFileStream, target, targetOutPath.c_str()); // Write clean target. cleanFiles.push_back(objs); @@ -1959,6 +1963,227 @@ cmLocalUnixMakefileGenerator2 } //---------------------------------------------------------------------------- +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<std::string> path; + this->SplitFullPath(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()) + { + 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) + { + 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) +{ + // Convert the path to a relative path. + std::string relative = this->ConvertToRelativePath(p); + + // Now convert it to an output path. + return cmSystemTools::ConvertToOutputPath(relative.c_str()); +} + +//---------------------------------------------------------------------------- +void +cmLocalUnixMakefileGenerator2::ConfigureOutputPaths() +{ + // Call superclass version first. + this->cmLocalUnixMakefileGenerator::ConfigureOutputPaths(); + + // 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<std::string> source; + std::vector<std::string> binary; + this->SplitFullPath(m_HomeDirectory.c_str(), source); + this->SplitFullPath(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. + this->SplitFullPath(m_CurrentOutputDirectory.c_str(), + m_CurrentOutputDirectoryComponents); + } +} + +//---------------------------------------------------------------------------- +void +cmLocalUnixMakefileGenerator2 +::SplitFullPath(const char* p, std::vector<std::string>& components) +{ + // The path is split into its basic components. This starts with a + // root ("/" for UNIX, "c:/" for Windows, "//" for Network) and is + // followed by the directory names. If there is a trailing slash + // then the last component is the empty string. The components can + // be recombined as "c[0]c[1]/c[2]/.../c[n]". + assert(cmSystemTools::FileIsFullPath(p)); + + // Identify the root component. + const char* c = p; + if(c[0] == '/' && c[1] == '/') + { + // Network path. + components.push_back("//"); + c += 2; + } + else if(c[0] == '/') + { + // Unix path. + components.push_back("/"); + c += 1; + } + else if(c[0] && c[1] == ':' && c[2] == '/') + { + // Windows path. + std::string root = "_:/"; + root[0] = c[0]; + components.push_back(root); + c += 3; + } + else + { + // Already a relative path. + cmSystemTools::Error("SplitFullPath called with path ", p); + return; + } + + // Parse the remaining components. + const char* first = c; + const char* last = first; + for(;*last; ++last) + { + if(*last == '/') + { + // End of a component. Save it. + components.push_back(std::string(first, last-first)); + first = last+1; + } + } + // Save the last component unless there were no components. + if(last != c) + { + components.push_back(std::string(first, last-first)); + } +} + +//---------------------------------------------------------------------------- +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::AddLanguageFlags(std::string& flags, const char* lang) { |