Merge topic 'vs10-source-paths'

d931ce9 VS10: Generate relative source paths when possible (#12570)
b2e7c7a VS11: Do not use source path conversion workaround specific to VS 10
4248132 VS10: Convert paths normally unless forced to relative
c2ba6ba VS10: Simplify vcxproj.filter file generation
4f2d9d2 VS10: Refactor custom commands to use WriteSource
This commit is contained in:
David Cole 2012-05-01 14:09:22 -04:00 committed by CMake Topic Stage
commit 28de11291a
4 changed files with 137 additions and 80 deletions

View File

@ -13,6 +13,7 @@
#include "cmGlobalVisualStudio10Generator.h" #include "cmGlobalVisualStudio10Generator.h"
#include "cmLocalVisualStudio10Generator.h" #include "cmLocalVisualStudio10Generator.h"
#include "cmMakefile.h" #include "cmMakefile.h"
#include "cmSourceFile.h"
#include "cmake.h" #include "cmake.h"
@ -50,6 +51,38 @@ cmLocalGenerator *cmGlobalVisualStudio10Generator::CreateLocalGenerator()
return lg; return lg;
} }
//----------------------------------------------------------------------------
void cmGlobalVisualStudio10Generator::Generate()
{
this->LongestSource = LongestSourcePath();
this->cmGlobalVisualStudio8Generator::Generate();
if(this->LongestSource.Length > 0)
{
cmMakefile* mf = this->LongestSource.Target->GetMakefile();
cmOStringStream e;
e <<
"The binary and/or source directory paths may be too long to generate "
"Visual Studio 10 files for this project. "
"Consider choosing shorter directory names to build this project with "
"Visual Studio 10. "
"A more detailed explanation follows."
"\n"
"There is a bug in the VS 10 IDE that renders property dialog fields "
"blank for files referenced by full path in the project file. "
"However, CMake must reference at least one file by full path:\n"
" " << this->LongestSource.SourceFile->GetFullPath() << "\n"
"This is because some Visual Studio tools would append the relative "
"path to the end of the referencing directory path, as in:\n"
" " << mf->GetCurrentOutputDirectory() << "/"
<< this->LongestSource.SourceRel << "\n"
"and then incorrectly complain that the file does not exist because "
"the path length is too long for some internal buffer or API. "
"To avoid this problem CMake must use a full path for this file "
"which then triggers the VS 10 property dialog bug.";
mf->IssueMessage(cmake::WARNING, e.str().c_str());
}
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmGlobalVisualStudio10Generator void cmGlobalVisualStudio10Generator
::GetDocumentation(cmDocumentationEntry& entry) const ::GetDocumentation(cmDocumentationEntry& entry) const
@ -230,3 +263,18 @@ cmGlobalVisualStudio10Generator
ruleFile += ".rule"; ruleFile += ".rule";
return ruleFile; return ruleFile;
} }
//----------------------------------------------------------------------------
void cmGlobalVisualStudio10Generator::PathTooLong(
cmTarget* target, cmSourceFile* sf, std::string const& sfRel)
{
size_t len = (strlen(target->GetMakefile()->GetCurrentOutputDirectory()) +
1 + sfRel.length());
if(len > this->LongestSource.Length)
{
this->LongestSource.Length = len;
this->LongestSource.Target = target;
this->LongestSource.SourceFile = sf;
this->LongestSource.SourceRel = sfRel;
}
}

View File

@ -46,6 +46,8 @@ public:
///! create the correct local generator ///! create the correct local generator
virtual cmLocalGenerator *CreateLocalGenerator(); virtual cmLocalGenerator *CreateLocalGenerator();
virtual void Generate();
/** /**
* Try to determine system infomation such as shared library * Try to determine system infomation such as shared library
* extension, pthreads, byte order etc. * extension, pthreads, byte order etc.
@ -79,10 +81,22 @@ public:
/** Generate an <output>.rule file path for a given command output. */ /** Generate an <output>.rule file path for a given command output. */
virtual std::string GenerateRuleFile(std::string const& output) const; virtual std::string GenerateRuleFile(std::string const& output) const;
void PathTooLong(cmTarget* target, cmSourceFile* sf,
std::string const& sfRel);
protected: protected:
virtual const char* GetIDEVersion() { return "10.0"; } virtual const char* GetIDEVersion() { return "10.0"; }
std::string PlatformToolset; std::string PlatformToolset;
bool ExpressEdition; bool ExpressEdition;
private:
struct LongestSourcePath
{
LongestSourcePath(): Length(0), Target(0), SourceFile(0) {}
size_t Length;
cmTarget* Target;
cmSourceFile* SourceFile;
std::string SourceRel;
};
LongestSourcePath LongestSource;
}; };
#endif #endif

View File

@ -491,14 +491,9 @@ cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile* source,
std::vector<std::string> *configs = std::vector<std::string> *configs =
static_cast<cmGlobalVisualStudio7Generator *> static_cast<cmGlobalVisualStudio7Generator *>
(this->GlobalGenerator)->GetConfigurations(); (this->GlobalGenerator)->GetConfigurations();
this->WriteString("<CustomBuild Include=\"", 2);
// custom command have to use relative paths or they do not this->WriteSource("CustomBuild", source, ">\n");
// show up in the GUI
std::string path = cmSystemTools::RelativePath(
this->Makefile->GetCurrentOutputDirectory(),
sourcePath.c_str());
this->ConvertToWindowsSlash(path);
(*this->BuildFileStream ) << path << "\">\n";
for(std::vector<std::string>::iterator i = configs->begin(); for(std::vector<std::string>::iterator i = configs->begin();
i != configs->end(); ++i) i != configs->end(); ++i)
{ {
@ -541,6 +536,18 @@ cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile* source,
this->WriteString("</CustomBuild>\n", 2); this->WriteString("</CustomBuild>\n", 2);
} }
std::string
cmVisualStudio10TargetGenerator::ConvertPath(std::string const& path,
bool forceRelative)
{
return forceRelative
? cmSystemTools::RelativePath(
this->Makefile->GetCurrentOutputDirectory(), path.c_str())
: this->LocalGenerator->Convert(path.c_str(),
cmLocalGenerator::START_OUTPUT,
cmLocalGenerator::UNCHANGED);
}
void cmVisualStudio10TargetGenerator::ConvertToWindowsSlash(std::string& s) void cmVisualStudio10TargetGenerator::ConvertToWindowsSlash(std::string& s)
{ {
// first convert all of the slashes // first convert all of the slashes
@ -559,13 +566,6 @@ void cmVisualStudio10TargetGenerator::WriteGroups()
std::vector<cmSourceFile*> classes = this->Target->GetSourceFiles(); std::vector<cmSourceFile*> classes = this->Target->GetSourceFiles();
std::set<cmSourceGroup*> groupsUsed; std::set<cmSourceGroup*> groupsUsed;
std::vector<cmSourceFile*> clCompile;
std::vector<cmSourceFile*> customBuild;
std::vector<cmSourceFile*> none;
std::vector<cmSourceFile*> headers;
std::vector<cmSourceFile*> idls;
std::vector<cmSourceFile*> resource;
for(std::vector<cmSourceFile*>::const_iterator s = classes.begin(); for(std::vector<cmSourceFile*>::const_iterator s = classes.begin();
s != classes.end(); s++) s != classes.end(); s++)
{ {
@ -574,40 +574,6 @@ void cmVisualStudio10TargetGenerator::WriteGroups()
cmSourceGroup& sourceGroup = cmSourceGroup& sourceGroup =
this->Makefile->FindSourceGroup(source.c_str(), sourceGroups); this->Makefile->FindSourceGroup(source.c_str(), sourceGroups);
groupsUsed.insert(&sourceGroup); groupsUsed.insert(&sourceGroup);
const char* lang = sf->GetLanguage();
bool header = (*s)->GetPropertyAsBool("HEADER_FILE_ONLY")
|| this->GlobalGenerator->IgnoreFile
((*s)->GetExtension().c_str());
std::string ext =
cmSystemTools::LowerCase((*s)->GetExtension());
if(!lang)
{
lang = "None";
}
if(header)
{
headers.push_back(sf);
}
else if(lang[0] == 'C')
{
clCompile.push_back(sf);
}
else if(strcmp(lang, "RC") == 0)
{
resource.push_back(sf);
}
else if(sf->GetCustomCommand())
{
customBuild.push_back(sf);
}
else if(ext == "idl")
{
idls.push_back(sf);
}
else
{
none.push_back(sf);
}
} }
this->AddMissingSourceGroups(groupsUsed, sourceGroups); this->AddMissingSourceGroups(groupsUsed, sourceGroups);
@ -629,11 +595,11 @@ void cmVisualStudio10TargetGenerator::WriteGroups()
"xmlns=\"http://schemas.microsoft.com/" "xmlns=\"http://schemas.microsoft.com/"
"developer/msbuild/2003\">\n", "developer/msbuild/2003\">\n",
0); 0);
this->WriteGroupSources("ClCompile", clCompile, sourceGroups); for(ToolSourceMap::const_iterator ti = this->Tools.begin();
this->WriteGroupSources("ClInclude", headers, sourceGroups); ti != this->Tools.end(); ++ti)
this->WriteGroupSources("ResourceCompile", resource, sourceGroups); {
this->WriteGroupSources("Midl", idls, sourceGroups); this->WriteGroupSources(ti->first.c_str(), ti->second, sourceGroups);
this->WriteGroupSources("CustomBuild", customBuild, sourceGroups); }
// Add object library contents as external objects. // Add object library contents as external objects.
std::vector<std::string> objs; std::vector<std::string> objs;
@ -690,7 +656,6 @@ void cmVisualStudio10TargetGenerator::WriteGroups()
this->WriteString("</Filter>\n", 2); this->WriteString("</Filter>\n", 2);
} }
this->WriteString("</ItemGroup>\n", 1); this->WriteString("</ItemGroup>\n", 1);
this->WriteGroupSources("None", none, sourceGroups);
this->WriteString("</Project>\n", 0); this->WriteString("</Project>\n", 0);
// restore stream pointer // restore stream pointer
this->BuildFileStream = save; this->BuildFileStream = save;
@ -750,32 +715,20 @@ cmVisualStudio10TargetGenerator::AddMissingSourceGroups(
void void
cmVisualStudio10TargetGenerator:: cmVisualStudio10TargetGenerator::
WriteGroupSources(const char* name, WriteGroupSources(const char* name,
std::vector<cmSourceFile*> const& sources, ToolSources const& sources,
std::vector<cmSourceGroup>& sourceGroups) std::vector<cmSourceGroup>& sourceGroups)
{ {
this->WriteString("<ItemGroup>\n", 1); this->WriteString("<ItemGroup>\n", 1);
for(std::vector<cmSourceFile*>::const_iterator s = sources.begin(); for(ToolSources::const_iterator s = sources.begin();
s != sources.end(); ++s) s != sources.end(); ++s)
{ {
cmSourceFile* sf = *s; cmSourceFile* sf = s->SourceFile;
if(sf->GetExtension() == "obj")
{
continue;
}
std::string const& source = sf->GetFullPath(); std::string const& source = sf->GetFullPath();
cmSourceGroup& sourceGroup = cmSourceGroup& sourceGroup =
this->Makefile->FindSourceGroup(source.c_str(), sourceGroups); this->Makefile->FindSourceGroup(source.c_str(), sourceGroups);
const char* filter = sourceGroup.GetFullName(); const char* filter = sourceGroup.GetFullName();
this->WriteString("<", 2); this->WriteString("<", 2);
std::string path = source; std::string path = this->ConvertPath(source, s->RelativePath);
// custom command sources must use relative paths or they will
// not show up in the GUI.
if(sf->GetCustomCommand())
{
path = cmSystemTools::RelativePath(
this->Makefile->GetCurrentOutputDirectory(),
source.c_str());
}
this->ConvertToWindowsSlash(path); this->ConvertToWindowsSlash(path);
(*this->BuildFileStream) << name << " Include=\"" (*this->BuildFileStream) << name << " Include=\""
<< path; << path;
@ -796,15 +749,47 @@ WriteGroupSources(const char* name,
} }
void cmVisualStudio10TargetGenerator::WriteSource( void cmVisualStudio10TargetGenerator::WriteSource(
const char* tool, cmSourceFile* sf, bool end) const char* tool, cmSourceFile* sf, const char* end)
{ {
std::string sourceFile = sf->GetFullPath(); // Visual Studio tools append relative paths to the current dir, as in:
// do not use a relative path here because it means that you //
// can not use as long a path to the file. // c:\path\to\current\dir\..\..\..\relative\path\to\source.c
//
// and fail if this exceeds the maximum allowed path length. Our path
// conversion uses full paths outside the build tree to allow deeper trees.
bool forceRelative = false;
std::string sourceFile = this->ConvertPath(sf->GetFullPath(), false);
if(this->LocalGenerator->GetVersion() == cmLocalVisualStudioGenerator::VS10
&& cmSystemTools::FileIsFullPath(sourceFile.c_str()))
{
// Normal path conversion resulted in a full path. VS 10 (but not 11)
// refuses to show the property page in the IDE for a source file with a
// full path (not starting in a '.' or '/' AFAICT). CMake <= 2.8.4 used a
// relative path but to allow deeper build trees CMake 2.8.[5678] used a
// full path except for custom commands. Custom commands do not work
// without a relative path, but they do not seem to be involved in tools
// with the above behavior. For other sources we now use a relative path
// when the combined path will not be too long so property pages appear.
std::string sourceRel = this->ConvertPath(sf->GetFullPath(), true);
size_t const maxLen = 250;
if(sf->GetCustomCommand() ||
((strlen(this->Makefile->GetCurrentOutputDirectory()) + 1 +
sourceRel.length()) <= maxLen))
{
forceRelative = true;
sourceFile = sourceRel;
}
else
{
this->GlobalGenerator->PathTooLong(this->Target, sf, sourceRel);
}
}
this->ConvertToWindowsSlash(sourceFile); this->ConvertToWindowsSlash(sourceFile);
this->WriteString("<", 2); this->WriteString("<", 2);
(*this->BuildFileStream ) << tool << (*this->BuildFileStream ) << tool <<
" Include=\"" << sourceFile << (end? "\" />\n" : "\" "); " Include=\"" << sourceFile << "\"" << (end? end : " />\n");
ToolSource toolSource = {sf, forceRelative};
this->Tools[tool].push_back(toolSource);
} }
void cmVisualStudio10TargetGenerator::WriteSources( void cmVisualStudio10TargetGenerator::WriteSources(
@ -836,7 +821,7 @@ void cmVisualStudio10TargetGenerator::WriteAllSources()
bool cl = strcmp(lang, "C") == 0 || strcmp(lang, "CXX") == 0; bool cl = strcmp(lang, "C") == 0 || strcmp(lang, "CXX") == 0;
bool rc = strcmp(lang, "RC") == 0; bool rc = strcmp(lang, "RC") == 0;
const char* tool = cl? "ClCompile" : (rc? "ResourceCompile" : "None"); const char* tool = cl? "ClCompile" : (rc? "ResourceCompile" : "None");
this->WriteSource(tool, *si, false); this->WriteSource(tool, *si, " ");
// ouput any flags specific to this source file // ouput any flags specific to this source file
if(cl && this->OutputSourceSpecificFlags(*si)) if(cl && this->OutputSourceSpecificFlags(*si))
{ {

View File

@ -43,11 +43,19 @@ public:
); );
private: private:
struct ToolSource
{
cmSourceFile* SourceFile;
bool RelativePath;
};
struct ToolSources: public std::vector<ToolSource> {};
std::string ConvertPath(std::string const& path, bool forceRelative);
void ConvertToWindowsSlash(std::string& s); void ConvertToWindowsSlash(std::string& s);
void WriteString(const char* line, int indentLevel); void WriteString(const char* line, int indentLevel);
void WriteProjectConfigurations(); void WriteProjectConfigurations();
void WriteProjectConfigurationValues(); void WriteProjectConfigurationValues();
void WriteSource(const char* tool, cmSourceFile* sf, bool end = true); void WriteSource(const char* tool, cmSourceFile* sf, const char* end = 0);
void WriteSources(const char* tool, std::vector<cmSourceFile*> const&); void WriteSources(const char* tool, std::vector<cmSourceFile*> const&);
void WriteAllSources(); void WriteAllSources();
void WriteDotNetReferences(); void WriteDotNetReferences();
@ -77,8 +85,7 @@ private:
void WriteEvents(std::string const& configName); void WriteEvents(std::string const& configName);
void WriteEvent(const char* name, std::vector<cmCustomCommand> & commands, void WriteEvent(const char* name, std::vector<cmCustomCommand> & commands,
std::string const& configName); std::string const& configName);
void WriteGroupSources(const char* name, void WriteGroupSources(const char* name, ToolSources const& sources,
std::vector<cmSourceFile*> const& sources,
std::vector<cmSourceGroup>& ); std::vector<cmSourceGroup>& );
void AddMissingSourceGroups(std::set<cmSourceGroup*>& groupsUsed, void AddMissingSourceGroups(std::set<cmSourceGroup*>& groupsUsed,
const std::vector<cmSourceGroup>& allGroups); const std::vector<cmSourceGroup>& allGroups);
@ -99,6 +106,9 @@ private:
cmGeneratedFileStream* BuildFileStream; cmGeneratedFileStream* BuildFileStream;
cmLocalVisualStudio7Generator* LocalGenerator; cmLocalVisualStudio7Generator* LocalGenerator;
std::set<cmSourceFile*> SourcesVisited; std::set<cmSourceFile*> SourcesVisited;
typedef std::map<cmStdString, ToolSources> ToolSourceMap;
ToolSourceMap Tools;
}; };
#endif #endif