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.

This commit is contained in:
Brad King 2005-02-24 16:04:54 -05:00
parent 9bf0811e34
commit cb48e0c161
6 changed files with 239 additions and 239 deletions

View File

@ -24,6 +24,8 @@
#include <windows.h> #include <windows.h>
#endif #endif
#include <assert.h>
int cmGlobalGenerator::s_TryCompileTimeout = 0; int cmGlobalGenerator::s_TryCompileTimeout = 0;
cmGlobalGenerator::cmGlobalGenerator() cmGlobalGenerator::cmGlobalGenerator()
@ -487,7 +489,10 @@ void cmGlobalGenerator::Configure()
delete m_LocalGenerators[i]; delete m_LocalGenerators[i];
} }
m_LocalGenerators.clear(); m_LocalGenerators.clear();
// Setup relative path generation.
this->ConfigureRelativePaths();
// start with this directory // start with this directory
cmLocalGenerator *lg = this->CreateLocalGenerator(); cmLocalGenerator *lg = this->CreateLocalGenerator();
m_LocalGenerators.push_back(lg); m_LocalGenerators.push_back(lg);
@ -573,6 +578,7 @@ void cmGlobalGenerator::Configure()
} }
} }
// loop through the directories creating cmLocalGenerators and Configure() // loop through the directories creating cmLocalGenerators and Configure()
void cmGlobalGenerator::RecursiveConfigure(cmLocalGenerator *lg, void cmGlobalGenerator::RecursiveConfigure(cmLocalGenerator *lg,
float startProgress, float startProgress,
@ -877,3 +883,133 @@ cmTarget* cmGlobalGenerator::FindTarget(const char* name)
return 0; return 0;
} }
//----------------------------------------------------------------------------
void cmGlobalGenerator::ConfigureRelativePaths()
{
// Identify the longest shared path component between the source
// directory and the build directory.
std::vector<std::string> source;
std::vector<std::string> 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<std::string>& 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<std::string> remote;
cmSystemTools::SplitPath(in_remote, remote);
unsigned int common=0;
while(common < remote.size() &&
common < local.size() &&
cmSystemTools::ComparePath(remote[common].c_str(),
local[common].c_str()))
{
++common;
}
// If 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;
}

View File

@ -129,11 +129,24 @@ public:
const char* GetLanguageOutputExtensionForLanguage(const char* lang); const char* GetLanguageOutputExtensionForLanguage(const char* lang);
///! What is the output extension for a given source file extension. ///! What is the output extension for a given source file extension.
const char* GetLanguageOutputExtensionFromExtension(const char* lang); 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<std::string>& local,
const char* remote);
protected: protected:
// Fill the m_ProjectMap, this must be called after m_LocalGenerators has been populated. // Fill the m_ProjectMap, this must be called after m_LocalGenerators has been populated.
void FillProjectMap(); void FillProjectMap();
bool IsExcluded(cmLocalGenerator* root, cmLocalGenerator* gen); bool IsExcluded(cmLocalGenerator* root, cmLocalGenerator* gen);
void FindMakeProgram(cmMakefile*); void FindMakeProgram(cmMakefile*);
void ConfigureRelativePaths();
bool m_ForceUnixPaths; bool m_ForceUnixPaths;
cmStdString m_FindMakeProgramFile; cmStdString m_FindMakeProgramFile;
@ -157,6 +170,11 @@ private:
std::map<cmStdString, cmStdString> m_LanguageToOutputExtension; std::map<cmStdString, cmStdString> m_LanguageToOutputExtension;
std::map<cmStdString, cmStdString> m_ExtensionToLanguage; std::map<cmStdString, cmStdString> m_ExtensionToLanguage;
std::map<cmStdString, cmStdString> m_LanguageToLinkerPreference; std::map<cmStdString, cmStdString> 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 #endif

View File

@ -51,6 +51,16 @@ void cmLocalGenerator::Configure()
std::string currentStart = m_Makefile->GetStartDirectory(); std::string currentStart = m_Makefile->GetStartDirectory();
currentStart += "/CMakeLists.txt"; currentStart += "/CMakeLists.txt";
m_Makefile->ReadListFile(currentStart.c_str()); 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) void cmLocalGenerator::SetGlobalGenerator(cmGlobalGenerator *gg)
@ -404,83 +414,6 @@ std::string cmLocalGenerator::GetFullTargetName(const char* n,
return name; 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, void cmLocalGenerator::AddCustomCommandToCreateObject(const char* ofname,
const char* lang, const char* lang,
cmSourceFile& source, cmSourceFile& source,
@ -1356,3 +1289,46 @@ cmLocalGenerator::ConstructScript(const cmCustomCommandLines& commandLines,
} }
return script; 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());
}

View File

@ -77,8 +77,35 @@ public:
/** Get the full name of the target's file, without path. */ /** Get the full name of the target's file, without path. */
std::string GetFullTargetName(const char* n, const cmTarget& t); 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 // flag to determine if this project should be included in a parent project
bool GetExcludeAll() bool GetExcludeAll()
{ {
@ -156,10 +183,7 @@ protected:
// members used for relative path function ConvertToMakefilePath // members used for relative path function ConvertToMakefilePath
std::string m_RelativePathToSourceDir; std::string m_RelativePathToSourceDir;
std::string m_RelativePathToBinaryDir; std::string m_RelativePathToBinaryDir;
std::string m_CurrentOutputDirectory; std::vector<std::string> m_CurrentOutputDirectoryComponents;
std::string m_HomeOutputDirectory;
std::string m_HomeDirectory;
std::string m_HomeOutputDirectoryNoSlash;
bool m_ExcludeFromAll; bool m_ExcludeFromAll;
cmLocalGenerator* m_Parent; cmLocalGenerator* m_Parent;
std::map<cmStdString, cmStdString> m_LanguageToIncludeFlags; std::map<cmStdString, cmStdString> m_LanguageToIncludeFlags;

View File

@ -33,8 +33,6 @@
#include <memory> // auto_ptr #include <memory> // auto_ptr
#include <queue> #include <queue>
#include <assert.h>
// TODO: Convert makefile name to a runtime switch. // TODO: Convert makefile name to a runtime switch.
#define CMLUMG_MAKEFILE_NAME "Makefile" #define CMLUMG_MAKEFILE_NAME "Makefile"
@ -1998,6 +1996,7 @@ cmLocalUnixMakefileGenerator2
for(std::vector<std::string>::const_iterator i = objects.begin(); for(std::vector<std::string>::const_iterator i = objects.begin();
i != objects.end(); ++i) i != objects.end(); ++i)
{ {
// TODO: Make sure we don't escape spaces and quote.
ruleFileStream ruleFileStream
<< " \\\n" << " \\\n"
<< "\"" << this->ConvertToRelativeOutputPath(i->c_str()) << "\""; << "\"" << this->ConvertToRelativeOutputPath(i->c_str()) << "\"";
@ -2308,94 +2307,6 @@ cmLocalUnixMakefileGenerator2
return dir; 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<std::string> 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 std::string
cmLocalUnixMakefileGenerator2::ConvertToRelativeOutputPath(const char* p) cmLocalUnixMakefileGenerator2::ConvertToRelativeOutputPath(const char* p)
@ -2424,52 +2335,6 @@ void cmLocalUnixMakefileGenerator2::ConfigureOutputPaths()
m_ExecutableOutputPath = exeOut; m_ExecutableOutputPath = exeOut;
this->FormatOutputPath(m_ExecutableOutputPath, "EXECUTABLE"); 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<std::string> source;
std::vector<std::string> 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 void
cmLocalUnixMakefileGenerator2 cmLocalUnixMakefileGenerator2

View File

@ -198,11 +198,9 @@ protected:
const cmSourceFile& source); const cmSourceFile& source);
const char* GetSourceFileLanguage(const cmSourceFile& source); const char* GetSourceFileLanguage(const cmSourceFile& source);
std::string ConvertToFullPath(const std::string& localPath); std::string ConvertToFullPath(const std::string& localPath);
std::string ConvertToRelativePath(const char* p);
std::string ConvertToRelativeOutputPath(const char* p); std::string ConvertToRelativeOutputPath(const char* p);
void ConfigureOutputPaths(); void ConfigureOutputPaths();
void FormatOutputPath(std::string& path, const char* name); void FormatOutputPath(std::string& path, const char* name);
bool ComparePath(const char* c1, const char* c2);
void AppendTargetDepends(std::vector<std::string>& depends, void AppendTargetDepends(std::vector<std::string>& depends,
const cmTarget& target); const cmTarget& target);
@ -277,13 +275,6 @@ private:
// Set of object file names that will be built in this directory. // Set of object file names that will be built in this directory.
std::set<cmStdString> m_ObjectFiles; std::set<cmStdString> 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<std::string> m_CurrentOutputDirectoryComponents;
}; };
#endif #endif