diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 28d738a8f..18a786d78 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -13,6 +13,7 @@ #include "cmGlobalVisualStudio10Generator.h" #include "cmLocalVisualStudio10Generator.h" #include "cmMakefile.h" +#include "cmSourceFile.h" #include "cmake.h" @@ -50,6 +51,38 @@ cmLocalGenerator *cmGlobalVisualStudio10Generator::CreateLocalGenerator() 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 ::GetDocumentation(cmDocumentationEntry& entry) const @@ -230,3 +263,18 @@ cmGlobalVisualStudio10Generator ruleFile += ".rule"; 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; + } +} diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index e2cc14ece..423656352 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -46,6 +46,8 @@ public: ///! create the correct local generator virtual cmLocalGenerator *CreateLocalGenerator(); + virtual void Generate(); + /** * Try to determine system infomation such as shared library * extension, pthreads, byte order etc. @@ -79,10 +81,22 @@ public: /** Generate an .rule file path for a given command output. */ virtual std::string GenerateRuleFile(std::string const& output) const; + void PathTooLong(cmTarget* target, cmSourceFile* sf, + std::string const& sfRel); protected: virtual const char* GetIDEVersion() { return "10.0"; } std::string PlatformToolset; 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 diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 9c25b0cd4..9a97ab0e3 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -491,14 +491,9 @@ cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile* source, std::vector *configs = static_cast (this->GlobalGenerator)->GetConfigurations(); - this->WriteString("Makefile->GetCurrentOutputDirectory(), - sourcePath.c_str()); - this->ConvertToWindowsSlash(path); - (*this->BuildFileStream ) << path << "\">\n"; + + this->WriteSource("CustomBuild", source, ">\n"); + for(std::vector::iterator i = configs->begin(); i != configs->end(); ++i) { @@ -541,6 +536,18 @@ cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile* source, this->WriteString("\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) { // first convert all of the slashes @@ -559,13 +566,6 @@ void cmVisualStudio10TargetGenerator::WriteGroups() std::vector classes = this->Target->GetSourceFiles(); std::set groupsUsed; - std::vector clCompile; - std::vector customBuild; - std::vector none; - std::vector headers; - std::vector idls; - std::vector resource; - for(std::vector::const_iterator s = classes.begin(); s != classes.end(); s++) { @@ -574,40 +574,6 @@ void cmVisualStudio10TargetGenerator::WriteGroups() cmSourceGroup& sourceGroup = this->Makefile->FindSourceGroup(source.c_str(), sourceGroups); 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); @@ -629,11 +595,11 @@ void cmVisualStudio10TargetGenerator::WriteGroups() "xmlns=\"http://schemas.microsoft.com/" "developer/msbuild/2003\">\n", 0); - this->WriteGroupSources("ClCompile", clCompile, sourceGroups); - this->WriteGroupSources("ClInclude", headers, sourceGroups); - this->WriteGroupSources("ResourceCompile", resource, sourceGroups); - this->WriteGroupSources("Midl", idls, sourceGroups); - this->WriteGroupSources("CustomBuild", customBuild, sourceGroups); + for(ToolSourceMap::const_iterator ti = this->Tools.begin(); + ti != this->Tools.end(); ++ti) + { + this->WriteGroupSources(ti->first.c_str(), ti->second, sourceGroups); + } // Add object library contents as external objects. std::vector objs; @@ -690,7 +656,6 @@ void cmVisualStudio10TargetGenerator::WriteGroups() this->WriteString("\n", 2); } this->WriteString("\n", 1); - this->WriteGroupSources("None", none, sourceGroups); this->WriteString("\n", 0); // restore stream pointer this->BuildFileStream = save; @@ -750,32 +715,20 @@ cmVisualStudio10TargetGenerator::AddMissingSourceGroups( void cmVisualStudio10TargetGenerator:: WriteGroupSources(const char* name, - std::vector const& sources, + ToolSources const& sources, std::vector& sourceGroups) { this->WriteString("\n", 1); - for(std::vector::const_iterator s = sources.begin(); + for(ToolSources::const_iterator s = sources.begin(); s != sources.end(); ++s) { - cmSourceFile* sf = *s; - if(sf->GetExtension() == "obj") - { - continue; - } + cmSourceFile* sf = s->SourceFile; std::string const& source = sf->GetFullPath(); cmSourceGroup& sourceGroup = this->Makefile->FindSourceGroup(source.c_str(), sourceGroups); const char* filter = sourceGroup.GetFullName(); this->WriteString("<", 2); - std::string path = source; - // 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()); - } + std::string path = this->ConvertPath(source, s->RelativePath); this->ConvertToWindowsSlash(path); (*this->BuildFileStream) << name << " Include=\"" << path; @@ -796,15 +749,47 @@ WriteGroupSources(const char* name, } void cmVisualStudio10TargetGenerator::WriteSource( - const char* tool, cmSourceFile* sf, bool end) + const char* tool, cmSourceFile* sf, const char* end) { - std::string sourceFile = sf->GetFullPath(); - // do not use a relative path here because it means that you - // can not use as long a path to the file. + // Visual Studio tools append relative paths to the current dir, as in: + // + // 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->WriteString("<", 2); (*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( @@ -836,7 +821,7 @@ void cmVisualStudio10TargetGenerator::WriteAllSources() bool cl = strcmp(lang, "C") == 0 || strcmp(lang, "CXX") == 0; bool rc = strcmp(lang, "RC") == 0; 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 if(cl && this->OutputSourceSpecificFlags(*si)) { diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 20a443ba4..2d5ec2a6b 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -43,11 +43,19 @@ public: ); private: + struct ToolSource + { + cmSourceFile* SourceFile; + bool RelativePath; + }; + struct ToolSources: public std::vector {}; + + std::string ConvertPath(std::string const& path, bool forceRelative); void ConvertToWindowsSlash(std::string& s); void WriteString(const char* line, int indentLevel); void WriteProjectConfigurations(); 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 const&); void WriteAllSources(); void WriteDotNetReferences(); @@ -77,8 +85,7 @@ private: void WriteEvents(std::string const& configName); void WriteEvent(const char* name, std::vector & commands, std::string const& configName); - void WriteGroupSources(const char* name, - std::vector const& sources, + void WriteGroupSources(const char* name, ToolSources const& sources, std::vector& ); void AddMissingSourceGroups(std::set& groupsUsed, const std::vector& allGroups); @@ -99,6 +106,9 @@ private: cmGeneratedFileStream* BuildFileStream; cmLocalVisualStudio7Generator* LocalGenerator; std::set SourcesVisited; + + typedef std::map ToolSourceMap; + ToolSourceMap Tools; }; #endif